【SpringBoot框架篇】3.优化代码,让代码更简洁高效

文章目录

  • 简介
  • lombok插件
    • idea安装lombok插件
    • 在pom.xml文件中引入依赖
    • 实体类注解
      • @Getter
      • @Setter
      • @ToString
      • @Data
      • @Slf4j
  • 优化toString方法
    • 引入依赖
    • 定义一个基类
    • 继承基类
  • 定义全局异常处理类
    • @ControllerAdvice
    • @ExceptionHandler
    • 应用如下
  • 优化接口日志打印
    • AOP描述
    • 引入依赖
    • 相关注解描述
    • 应用实在
      • 编写切面类代码
      • 编写测试的web接口
      • 模拟postman调用接口
      • 运行结果如下
  • 项目配套代码

简介

该文章主要教大家如何去除重复性的代码,减少代码的行数

lombok插件

Lombok是一个可以通过简单的注解形式来帮助我们简化消除一些必须有但显得很臃肿的Java代码的工具,通过使用对应的注解,可以在编译源码的时候生成对应的方法。

idea安装lombok插件

【SpringBoot框架篇】3.优化代码,让代码更简洁高效_第1张图片
【SpringBoot框架篇】3.优化代码,让代码更简洁高效_第2张图片
下载完成 重新下idea就能生效了

在pom.xml文件中引入依赖

   
       org.projectlombok
       lombok
  

实体类注解

@Getter

@Getter可以自动生成get方法

@Setter

@Setter可以自动生成set方法

@ToString

@ToString可以重写toString方法,打印对象拥有的属性信息

如果不重写toString方法,实体类对象的toString方法只会输出对象内存地址信息,
在这里插入图片描述
一般我们会手动重写toString方法,把对象的属性信息打印出来,代码如下,使用@ToString注解,下面的代码就可以删掉了。

    @Override
    public String toString(){
            return "User{" +
                    "username='" + username + '\'' +
                    ", password='" + password + '\'' +
                    ", age='" + age + '\'' +
                    '}';
    }

@Data

@Data注解包含@Getter,@Setter,@ToString这3个注解的功能
我一般都用@Data注解
代码应用如下

@Data
public class User  {

    public User(){ }

    public User(String username,String password,Integer age){
        this.setUsername(username);
        this.setPassword(password);
        this.setAge(age);
        System.out.println(this);
    }

    private String username;
    private String password;
    private Integer age;

    public static void main(String[] args) {
        new User("Dominick Li","123456",25);
    }
}

运行main函数结果如下
在这里插入图片描述
我们可以查看编译后的源码文件【SpringBoot框架篇】3.优化代码,让代码更简洁高效_第3张图片

@Slf4j

@Slf4j可以去除log对象的声明
我们一般在需要打印日志的类里都要写下面代码

public class LogTest {
    
    public static final Logger logger= LoggerFactory.getLogger(LogTest.class);
    public static void main(String[] args) {
        logger.info("打印info级别日志");
    }
}

优化后的代码如下

@Slf4j
public class LogTest {
    public static void main(String[] args) {
        log.info("打印info级别日志");
    }
}

优化toString方法

如果项目不允许使用lombok插件,那我们可以通过使用commons.lang3包下的ToStringBuilder类实现重写toString方法

引入依赖

  
       org.apache.commons
       commons-lang3
       3.7
 

定义一个基类

通常我们会在基类定义一些通用属性,如记录的id,创建时间,修改时间
我们把重写toString的方法在基类中定义

public  class BaseModel implements Serializable {

    private static final long serialVersionUID = 2863256929817929825L;
    private Integer id;
    private Date createTime;
    private Date lastmodifiedTime;
	//省略 get set方法
    /**
     * 把对象转换成json字符
     * 默认toString方法打印的是对象内存地址
     */
    @Override
    public String toString() {
        return ToStringBuilder.reflectionToString(this, ToStringStyle.SHORT_PREFIX_STYLE);
    }
}

ToStringStyle的样式有很多种,大家可以自己试试别的样式,如JSON_STYLE。
【SpringBoot框架篇】3.优化代码,让代码更简洁高效_第4张图片

继承基类

在构造方法中打印了当前对象信息

public class Role  extends BaseModel{

    public Role(){}

    public Role(String roleName, Date createDate){
        this.roleName=roleName;
        this.setCreateTime(createDate);
        System.out.println(this);
    }
    private String roleName;
    public String getRoleName() {
        return roleName;
    }
    public void setRoleName(String roleName) {
        this.roleName = roleName;
    }

    public static void main(String[] args) {
        new Role("admin",new Date());
    }

}

运行结果如下
在这里插入图片描述

定义全局异常处理类

@ControllerAdvice

使用@ControllerAdvice注解接收全局信息

@ExceptionHandler

可以在@ExceptionHandler注解中指定处理的异常类

应用如下

如果接口代码中没有try catch 异常的话,Exception类型的异常信息则会被ExceptionHndler方法处理

@ControllerAdvice
@Slf4j
public class GlobalExceptionController {
    private static final String ERROR_MESSAGE = "系统内部错误,请联系管理员!";

    @ExceptionHandler(value = Exception.class)
    @ResponseBody
    public String ExceptionHndler(Exception e) {
        if (e instanceof FileNotFoundException) {
            //可以自定义根据不同的异常类型,做一些代码处理
        }
        log.error("global error:{}", e);
        return ERROR_MESSAGE;
    }
}

优化接口日志打印

我们可以通过aop来打印接口请求参数,返回结果,处理时间等信息.

AOP描述

AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

引入依赖

   
       1.9.5
   
       
           org.aspectj
           aspectjrt
           ${aspectj.version}
       

       
           org.aspectj
           aspectjweaver
           ${aspectj.version}
       

相关注解描述

注解 功能描述
@Aspect 作用是把当前类标识为一个切面供容器读取
@Pointcut Pointcut是植入Advice的触发条件。每个Pointcut的定义包括2部分,一是表达式,二是方法签名。方法签名必须是 public及void型。可以将Pointcut中的方法看作是一个被Advice引用的助记符,因为表达式不直观,因此我们可以通过方法签名的方式为 此表达式命名。因此Pointcut中的方法只需要方法签名,而不需要在方法体内编写实际代码。
@Around 环绕增强,相当于MethodInterceptor
@AfterReturning 后置增强,相当于AfterReturningAdvice,方法正常退出时执行
@Before 标识一个前置增强方法,相当于BeforeAdvice的功能,相似功能的还有
@AfterThrowing 异常抛出增强,相当于ThrowsAdvice
@After final增强,不管是抛出异常或者正常退出都会执行

本文用到了@Aspect ,@Pointcut,@Around,@AfterThrowing|这4个注解.

应用实在

编写切面类代码

编写一个切面类,然后通关@Pointcut注解指定要切入的方法
下面代码中用到了lombok插件,如果你没有使用的话,需要手动声明get set方法,还有log对象

@Aspect
@Slf4j
@Component
public class LogPointcut {

    /**
     * 对controller包下的所有类里的方法进行代理
     */
    @Pointcut("execution(public * com.ljm.boot.simplifycode.controller.*.*(..)))")
    public  void requestServer(){

    }

    /**
     * 环绕增强通知
     */
    @Around("requestServer()")
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        long start = System.currentTimeMillis();
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        Object result = proceedingJoinPoint.proceed();
        RequestInfo requestInfo = new RequestInfo();
        requestInfo.setIp(request.getRemoteAddr());
        requestInfo.setUrl(request.getRequestURL().toString());
        requestInfo.setHttpMethod(request.getMethod());
        requestInfo.setClassMethod(String.format("%s.%s", proceedingJoinPoint.getSignature().getDeclaringTypeName(),
                proceedingJoinPoint.getSignature().getName()));
        requestInfo.setRequestParams(getRequestParamsByProceedingJoinPoint(proceedingJoinPoint));
        requestInfo.setResult(result);
        requestInfo.setTimeCost(System.currentTimeMillis() - start);
        log.info("Request Info      : {}", requestInfo);
        return result;
    }

    /**
     * 异常抛出增强
     */
    @AfterThrowing(pointcut = "requestServer()", throwing = "e")
    public void doAfterThrow(JoinPoint joinPoint, RuntimeException e) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        RequestErrorInfo requestErrorInfo = new RequestErrorInfo();
        requestErrorInfo.setIp(request.getRemoteAddr());
        requestErrorInfo.setUrl(request.getRequestURL().toString());
        requestErrorInfo.setHttpMethod(request.getMethod());
        requestErrorInfo.setClassMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
                joinPoint.getSignature().getName()));
        requestErrorInfo.setRequestParams(getRequestParamsByJoinPoint(joinPoint));
        requestErrorInfo.setException(e);
        log.info("Error Request Info      : {}", requestErrorInfo);
    }

    /**
     * 获取入参
     * @param proceedingJoinPoint
     * */
    private Map<String, Object> getRequestParamsByProceedingJoinPoint(ProceedingJoinPoint proceedingJoinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature)proceedingJoinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = proceedingJoinPoint.getArgs();
        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> getRequestParamsByJoinPoint(JoinPoint joinPoint) {
        //参数名
        String[] paramNames = ((MethodSignature)joinPoint.getSignature()).getParameterNames();
        //参数值
        Object[] paramValues = joinPoint.getArgs();
        return buildRequestParam(paramNames, paramValues);
    }

    private Map<String, Object> buildRequestParam(String[] paramNames, Object[] paramValues) {
        Map<String, Object> requestParams = new HashMap<>(paramNames.length);
        for (int i = 0; i < paramNames.length; i++) {
            Object value = paramValues[i];
            //如果是文件对象
            if (value instanceof MultipartFile) {
                MultipartFile file = (MultipartFile) value;
                value = file.getOriginalFilename();  //获取文件名
            }
            requestParams.put(paramNames[i], value);
        }
        return requestParams;
    }
    
    @Data
    public class RequestInfo implements Serializable {
        private String ip; //ip地址
        private String url; //请求路径
        private String httpMethod; //方法类型
        private String classMethod; //类和方法名称
        private Object requestParams; //请求参数
        private Object result; //返回的结果
        private Long timeCost; //执行时长
    }

    @Data
    public class RequestErrorInfo implements Serializable  {
        private String ip;
        private String url;
        private String httpMethod;
        private String classMethod;
        private Object requestParams;
        private RuntimeException exception; //异常类型
    }
}

编写测试的web接口

@RestController
public class TestController {
    @RequestMapping("/login")
    public String  login(@RequestBody User user){
        if(user.getUsername().equals("admin") && user.getPassword().equals("123")){
            return "登录成功";
        }
        return "登录失败";
    }
}

模拟postman调用接口

public class TestPrint {
    public static void main(String[] args) {
        RestTemplate restTemplate=new RestTemplate();
        HashMap param=new HashMap();
        param.put("username","admin");
        param.put("password","123");
        String result=restTemplate.postForObject("http://localhost:8003/login",param,String.class);
        System.out.println(result);
    }
}

运行结果如下

在这里插入图片描述
日志的完整json格式数据如下

RequestInfo: 
LogPointcut.RequestInfo(
ip=127.0.0.1,
url=http: //localhost:8003/login,
httpMethod=POST,
classMethod=com.ljm.boot.simplifycode.controller.TestController.login,
requestParams={
  user=User(username=admin,
  password=123,
  age=null)
},
result=登录成功,
timeCost=7
)

项目配套代码

github地址
要是觉得我写的对你有点帮助的话,麻烦在github上帮我点 Star

【SpringBoot框架篇】其它文章如下,后续会继续更新。

  • 1.搭建第一个springboot项目
  • 2.Thymeleaf模板引擎实战
  • 3.优化代码,让代码更简洁高效
  • 4.集成jta-atomikos实现分布式事务
  • 5.分布式锁的实现方式
  • 6.docker部署,并挂载配置文件到宿主机上面
  • 7.项目发布到生产环境
  • 8.搭建自己的spring-boot-starter
  • 9.dobbo入门实战
  • 10.API接口限流实战
  • 11.Spring Data Jpa实战
  • 12.使用druid的monitor工具查看sql执行性能
  • 13.使用springboot admin对springboot应用进行监控
  • 14.mybatis-plus实战

你可能感兴趣的:(springBoot)