JavaWeb学习日记(十二)案例

一、准备工作

 

开发规范-Restful: 

开发规范-统一响应结果:

二、部门管理-查询

前端发送请求后,请求会到Controller中的对应方法,随后调用service来获取数据,在service中调用了Mapper接口中的方法来查询全部部门信息,mapper中则执行对应sql语句来调取数据并且将获取到的数据封装为对应类型返回给service,service又返回给controller,再由controller将数据返回给前端。

三、部门管理-删除

Controller中设置对应方法:

调用的Servcice接口以及实现类:

调用的Mapper接口:

四、部门管理-新增

前端在请求的时候传递的json格式的请求参数在服务端如何接受:通过一个实体类来接受json参数,需要在实体类前增加一个注解:@RequestBody

补充基础属性:添加部门时页面只传输的json数据只包含部门的名称,除此以外的创建时间等信息需要自己补全

DeptController

Dept接口

Dept实现类

DeptMapper

五、员工管理-分页查询

基础分页查询:

SQL语句

接口规范:

controller:

service接口+实现类:

mapper:

PageHelper分页插件:

Service接口+实现类

分页查询(查询条件):

SQL语句

XML映射文件+动态SQL:

六、员工管理-删除员工

SQL语句:

接口文档:

Controller:

EmpService接口+实现类:

XML映射文件+动态SQL:

七、员工管理-新增员工

接口文档:

八、文件上传

文件上传介绍:

服务器端接受到文件时,该文件是临时文件,不进行存储则会在请求结束后自动删除

本地存储:

阿里云OSS:

 

api文档接口:

九、修改员工-查询回显

接口文档:

十、修改员工-根据id更新

文档接口:

SQL语句:

 十一、配置文件

后续可以直接在配置文件中修改对应数据,而修改properties文件不需要重新编译

配置格式:

配置文件注解:@ConfigurationProperties:

 十二、基础登录功能

登录功能:

接口文档:

登录校验:

思想:

会话技术:

方案一:Cookie 

请求服务器的时候,可以自行设置一个Cookie用来存储相关信息,比如当前登录的用户的用户名或者ID,服务器端在给客户端响应数据的时候,会自动的将这个Cookie响应给客户端,客户端在接受时,会自动将其存储在本地,而在后续客户端向服务器的每一次请求时,也会自动的将浏览器本地存储的Cookie携带到客户端,而在服务器端就可以获取到该Cookie,通过判断该Cookie的值是否存在,如果不存在,则表示这个客户端之前没有登录过,存在则代表这个客户端之前登录过

方案二:Session

Session就是加密过的Cookie,本质上还是通过Cookie实现的

方案三:令牌技术

本质是一个字符串,通过浏览器发送请求,如登录请求时,服务器端会生成一个令牌并在响应时一同发送给客户端,客户端在接受时,需要将令牌存储起来,可以在Cookie中,也可在其他的地方,在后续其他请求时,都需要携带这个令牌,通过检测令牌,判断客户端是否登录过,如果是在同一次会话的多次请求之间,需要共享数据,则可以将共享的数据存储在令牌中。 

JWT令牌:

package com.itheima;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;

//@SpringBootTest
class TliasWebManagementApplicationTests {

    @Test
    public void testUuid(){
        for(int i=0;i<1000;i++){
            String uuid= UUID.randomUUID().toString();
            System.out.println(uuid);
        }
    }

    /*测试JWT生成*/
    @Test
    public void testGenJwt(){
        Map<String,Object> claims=new HashMap<>();
        claims.put("id",1);
        claims.put("name","tom");
        String jwt=Jwts.builder()
                .signWith(SignatureAlgorithm.HS256,"itheima")//设置签名算法
                .setClaims(claims)//设置自定义数据  Map对象
                .setExpiration(new Date(System.currentTimeMillis()+3600*1000))//设置有效期 Date对象 当前时间+1小时(毫秒值)
                .compact();
        System.out.println(jwt);
    }
    /*解析JWT*/
    @Test
    public void testParseJwt(){
        Claims claims=Jwts.parser()
                .setSigningKey("itheima")
                .parseClaimsJws("eyJhbGciOiJIUzI1NiJ9.eyJuYW1lIjoidG9tIiwiaWQiOjEsImV4cCI6MTc0MDYzMDExMX0._Iwxv6qbaIsr-DDm3rrmHF0i2PQo77G-8UBDtEeq5b4")
                .getBody();
        System.out.println(claims); 
        //{name=tom, id=1, exp=1740630111}
    }

}

登录校验-JWT令牌 :

api接口文档:

校验令牌-过滤器(Filter):

快速入门:

  

执行流程:

拦截路径:

过滤器链:

登录校验Filter:

package com.itheima.filter;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;

import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@Slf4j
@WebFilter(urlPatterns = "/*")
public class LoginCheckFilter implements Filter {

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
        Filter.super.init(filterConfig);
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req=(HttpServletRequest) request;
        HttpServletResponse resp=(HttpServletResponse) response;

        //1.获取请求url
        String url = req.getRequestURI().toString();
        log.info("请求的url:{}",url);

        //2.判断请求url中是否包含login 如果是则代表登录操作,放行
        if(url.contains("login")){
            log.info("这是登录操作..放行");
            chain.doFilter(request,response);
            return;  //终止代码
        }

        //3.如果不是,则获取请求头中的令牌
        String jwt = req.getHeader("token");  // token 代表请求头的名字

        //4.判断令牌是否存在,如果不存在则返回错误结果(未登录)
        if(!StringUtils.hasLength(jwt)){//判断是否有长度,没长度则代表空串
            log.info("请求头token为空,返回未登录的信息");
            Result error=Result.error("NOT_LOGIN"); // 拿到了返回的结果,需要将其转为json格式的返回值 (之前Controller中是自动转换)
            String notLogin = JSONObject.toJSONString(error);//调用工具类中的方法:将对象转化为json格式的字符串
            resp.getWriter().write(notLogin);
            return; //终止程序
        }
        //5.存在则解析token,如果解析失败,则返回错误结果(未登录)
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//解析失败
            e.printStackTrace();
            log.info("解析令牌失败,返回错误信息");
            Result error=Result.error("NOT_LOGIN"); // 拿到了返回的结果,需要将其转为json格式的返回值 (之前Controller中是自动转换)
            String notLogin = JSONObject.toJSONString(error);//调用工具类中的方法:将对象转化为json格式的字符串
            resp.getWriter().write(notLogin);
            return; //终止程序
        }

        //6.解析成功,放行
        log.info("令牌合法,放行");
        chain.doFilter(request,response);
    }

    @Override
    public void destroy() {
        Filter.super.destroy();
    }
}

新增依赖: (用于将字符串数据转为json格式)

拦截器Interceptor:

快速入门:

返回值 true 则放行  返回值false 不放行

拦截路径:

执行流程:

package com.itheima.interceptor;

import com.alibaba.fastjson.JSONObject;
import com.itheima.pojo.Result;
import com.itheima.utils.JwtUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@Component
@Slf4j
public class LoginCheckInterceptor implements HandlerInterceptor {
    @Override  //在目标资源方法运行前运行,返回true-放行,返回false-不放行
    public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object handler) throws Exception {
        System.out.println("preHandle...");
        /*return true;*/

        //1.获取请求url
        String url = req.getRequestURI().toString();
        log.info("请求的url:{}",url);

        //2.判断请求url中是否包含login 如果是则代表登录操作,放行
        if(url.contains("login")){
            log.info("这是登录操作..放行");

            return true;  //终止代码
        }

        //3.如果不是,则获取请求头中的令牌
        String jwt = req.getHeader("token");  // token 代表请求头的名字

        //4.判断令牌是否存在,如果不存在则返回错误结果(未登录)
        if(!StringUtils.hasLength(jwt)){//判断是否有长度,没长度则代表空串
            log.info("请求头token为空,返回未登录的信息");
            Result error=Result.error("NOT_LOGIN"); // 拿到了返回的结果,需要将其转为json格式的返回值 (之前Controller中是自动转换)
            String notLogin = JSONObject.toJSONString(error);//调用工具类中的方法:将对象转化为json格式的字符串
            resp.getWriter().write(notLogin);
            return false; //终止程序
        }
        //5.存在则解析token,如果解析失败,则返回错误结果(未登录)
        try {
            JwtUtils.parseJWT(jwt);
        } catch (Exception e) {//解析失败
            e.printStackTrace();
            log.info("解析令牌失败,返回错误信息");
            Result error=Result.error("NOT_LOGIN"); // 拿到了返回的结果,需要将其转为json格式的返回值 (之前Controller中是自动转换)
            String notLogin = JSONObject.toJSONString(error);//调用工具类中的方法:将对象转化为json格式的字符串
            resp.getWriter().write(notLogin);
            return false; //终止程序
        }

        //6.解析成功,放行
        log.info("令牌合法,放行");
        return true;
    }

    @Override //在目标资源方法运行后运行
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

        System.out.println("postHandle...");
    }

    @Override //在视图渲染完毕后运行,最后运行
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion...");
    }
}

拦截器和过滤器 代码区别:放行和不放行的表示方式不同,一个是通过返回值,一个是调用doFilter方法

十三、异常处理

全局异常处理器:

十四、事物管理

spring事物管理:

模拟异常,则会出现部门删除,但是对应员工数据仍然存在的情况,此时需要使用事务来进行处理

yml配置文件中增加

事务进阶:

事务传播行为:

设置REQUIRES_NEW :无论delete方法中的事务是否出现问题,都会创建一个新的事物来实现日志记录

十五、AOP

快速入门:

package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

@Component
@Aspect  //AOP类
@Slf4j
public class TimeAspect {

    @Around("execution(* com.itheima.service.*.*(..))") //切入点表达式
    // excution(返回值类型  包名.类名/接口名.方法名(..))
    public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {
        //1.记录开始时间
        long begin=System.currentTimeMillis(); //获取当前时间的毫秒值

        //2.调用原始方法运行
        Object result=joinPoint.proceed();

        //3.记录结束时间,计算执行耗时
        long end=System.currentTimeMillis();

        long cost=begin-end;
        log.info(joinPoint.getSignature()+"方法执行耗时:{}ms",cost);

        return result;
    }
}

AOP核心概念:

AOP执行流程:

进行AOP程序的开发,运行的就不再是原始的对象,而是基于目标对象所产生的代理对象,也就是增强后的代理对象(所谓增强就是原方法的基础上,增加了通知部分增加的方法)

AOP进阶:

通知类型:

package com.itheima.aop;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class MyAspect {
    /*抽取切入点表达式*/
    @Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
    private void pt(){} //修饰词为private 无法在其他切面类文件中调用
    @Before("pt()")
    public void before(){
        log.info("before ...");
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("around before ...");

        //调用目标对象的原始方法执行
        Object result=joinPoint.proceed();

        log.info("around after ...");

        return result;
    }

    @After("pt()")
    public void after(){
        log.info("after ....");
    }

    @AfterReturning("pt()")
    public void afterReturning(){
        log.info("afterReturning ...");
    }

    @AfterThrowing("pt()")
    public void afterThrowing(){
        log.info("afterThrowing ...");
    }
}

 

通知顺序:

切入点表达式:

execution:

———————————————————————————————————————————

@annotation:

连接点:

package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
public class MyAspect8 {

    @Pointcut("execution(* com.itheima.service.DeptService.*(..))")
    private void pt(){}

    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info("MyAspect8 .. before ..");
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("MyAspect8 .. around before ..");

        //1.获取 目标对象类名
        String classname= joinPoint.getTarget().getClass().toString();
        log.info("目标对象类名:{}",classname);

        //2.获取 目标方法名
        String methodname=joinPoint.getSignature().getName();
        log.info("目标方法名:{}",methodname);

        //3.获取 目标方法运行时传入的参数
        Object[] args=joinPoint.getArgs();
        log.info("目标方法运行传入的参数:{}",args);

        //4.放行 目标方法执行
        Object result=joinPoint.proceed();

        //5.获取 目标方法运行的返回值
        log.info("返回值: {}",result);

        log.info("MyAspect8 .. around after ..");

        return result;
    }
}

AOP案例:

package com.itheima.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)  //标识生效时间
@Target(ElementType.METHOD) //标识方法
public @interface MyLog {
}
package com.itheima.aop;

import com.alibaba.fastjson.JSONObject;
import com.itheima.mapper.OperateLogMapper;
import com.itheima.pojo.DeptLog;
import com.itheima.pojo.OperateLog;
import com.itheima.utils.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.Arrays;

@Slf4j
@Aspect
@Component
public class MyAspect {

    @Autowired
    private OperateLogMapper operateLogMapper;
    @Autowired
    private HttpServletRequest request;
    //在 Spring Boot 的应用程序中使用 @Autowired 来注入 HttpServletRequest 对象时,它实际上是从请求的上下文中获取对象,而不是从 Spring 容器中获取。
    @Pointcut("@annotation(com.itheima.anno.MyLog)")
    private void pt(){};

    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info("around before ...");

        //获取操作人的id  当前员工的ID
        //获取请求头中的jwt令牌
        //解析jwt令牌信息
        String jwt=request.getHeader("token");
        Claims claims = JwtUtils.parseJWT(jwt); //其实是一个Map集合
        Integer id = (Integer) claims.get("id");

        //操作时间
        LocalDateTime operateTime = LocalDateTime.now();

        //操作类名
        String className = joinPoint.getTarget().getClass().toString();

        //操作方法名
        String methodName = joinPoint.getSignature().getName();

        //操作方法参数
        Object[] args = joinPoint.getArgs();
        String methodParams = Arrays.toString(args);


        Long begin=System.currentTimeMillis();

        //调用目标对象的原始方法执行
        Object result=joinPoint.proceed();

        Long end= System.currentTimeMillis();


        //方法返回值
        String returnValue = JSONObject.toJSONString(result);

        //操作耗时
        Long costTime=begin-end;

        //操作日志-记录
        OperateLog operateLog=new OperateLog(null,id,operateTime,className,methodName,methodParams,returnValue,costTime);

        operateLogMapper.insert(operateLog);

        log.info("AOP记录操作日志:{}",operateLog);
        return result;
    }
}
package com.itheima.mapper;

import com.itheima.pojo.OperateLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OperateLogMapper {

    //插入日志数据
    @Insert("insert into tlias.operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
            "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
    public void insert(OperateLog log);

}
package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; //ID
    private Integer operateUser; //操作人ID
    private LocalDateTime operateTime; //操作时间
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Long costTime; //操作耗时
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值