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

1.简介

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

2.lombok插件

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

2.1.idea安装lombok插件

在这里插入图片描述
在这里插入图片描述
下载完成 重新下idea就能生效了

2.2.在pom.xml文件中引入依赖

   <dependency>
       <groupId>org.projectlombok</groupId>
       <artifactId>lombok</artifactId>
  </dependency>

2.3.实体类注解

2.3.1.@Getter

@Getter可以自动生成get方法

2.3.2.@Setter

@Setter可以自动生成set方法

2.3.3.@ToString

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

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

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

2.3.4.@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函数结果如下
在这里插入图片描述
我们可以查看编译后的源码文件在这里插入图片描述

2.3.5.@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级别日志");
    }
}

3.优化toString方法

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

3.1.引入依赖

  <dependency>
       <groupId>org.apache.commons</groupId>
       <artifactId>commons-lang3</artifactId>
       <version>3.7</version>
 </dependency>

3.2.定义一个基类

通常我们会在基类定义一些通用属性,如记录的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。
在这里插入图片描述

3.3.继承基类

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

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());
    }

}

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

4.定义全局异常处理类

4.1.@ControllerAdvice

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

4.2.@ExceptionHandler

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

4.3.应用如下

如果接口代码中没有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;
    }
}

5.优化接口日志打印

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

5.1.AOP描述

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

5.2.引入依赖

   <properties>
       <aspectj.version>1.9.5</aspectj.version>
   </properties>
       <dependency>
           <groupId>org.aspectj</groupId>
           <artifactId>aspectjrt</artifactId>
           <version>${aspectj.version}</version>
       </dependency>

       <dependency>
           <groupId>org.aspectj</groupId>
           <artifactId>aspectjweaver</artifactId>
           <version>${aspectj.version}</version>
       </dependency>

5.3.相关注解描述

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

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

5.4.应用实在

5.4.1.编写切面类代码

编写一个切面类,然后通关@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; //异常类型
    }
}

5.4.2.编写测试的web接口

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

5.4.3.模拟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);
    }
}

5.4.4.运行结果如下

在这里插入图片描述
日志的完整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
)

6.项目配套代码

gitee代码地址

创作不易,要是觉得我写的对你有点帮助的话,麻烦在gitee上帮我点下 Star

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

  • 7
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皓亮君

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值