SpringAop的认识 (二) 之 日志开发(上)

我们现在要给程序中的所有用户接口和订单的接口全部进行订单增强日志处理?

会定义一个 Log日志类:

日志场景需求的作用

  • 可以协助我们排除执行方法耗时的问题
  • 这样可以针对性的优化和处理执行方法耗时的问题

日志的pojo

package com.kuangstudy.first;

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

@Data
@NoArgsConstructor
@AllArgsConstructor
@ToString
public class Logs {
    private Integer id;
    private String classname;
    private String method;
    private String time;
    private String params;
}

日志处理service

package com.kuangstudy.first;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class LogService {

    public void saveLog(Logs logs) {
        log.info("你保存日志是:{}", logs);
    }
}

开发日志保存的记录功能

我们一般的正常思维和开发流程是上面样子,如下:

用户服务需要记录日志如下:

package com.kuangstudy.first;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.ToString;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class UserService {


    @Autowired
    private LogService logService;

    /**
     * 用户注册
     *
     * @param user
     */
    public void saveUser(User user) {
        log.info("用户注册....");

        // 记录日志
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.UserService");
        logs.setMethod("saveUser");
        logs.setId(1);
        logs.setParams(user.toString());
        logs.setTime("100ms");
        logService.saveLog(logs);
    }

    /**
     * 修改用户
     *
     * @param user
     */
    public void updateUser(User user) {
        log.info("修改用户....");

        // 记录日志
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.UserService");
        logs.setMethod("updateUser");
        logs.setId(1);
        logs.setParams(user.toString());
        logs.setTime("100ms");
        logService.saveLog(logs);
    }


    /**
     * 删除用户
     *
     * @param userId
     */
    public void delUser(Integer userId) {
        log.info("删除用户....{}", userId);

        // 记录日志
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.UserService");
        logs.setMethod("delUser");
        logs.setId(1);
        logs.setParams(userId + "");
        logs.setTime("100ms");
        logService.saveLog(logs);
    }

}

订单服务记录日志

package com.kuangstudy.first;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class OrderService {

    @Autowired
    private LogService logService;

    /**
     * 订单注册
     *
     * @param order
     */
    public void saveOrder(Order order) {
        log.info("订单注册....");

        // 记录日志
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.OrderService");
        logs.setMethod("saveOrder");
        logs.setId(1);
        logs.setParams(order.toString());
        logs.setTime("100ms");
        logService.saveLog(logs);
    }

    /**
     * 修改订单
     *
     * @param order
     */
    public void updateOrder(Order order) {
        log.info("修改订单....");

        // 记录日志
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.OrderService");
        logs.setMethod("updateOrder");
        logs.setId(1);
        logs.setParams(order.toString());
        logs.setTime("100ms");
        logService.saveLog(logs);
    }


    /**
     * 删除订单
     *
     * @param orderId
     */
    public void delOrder(Integer orderId) {
        log.info("删除订单....{}", orderId);

        // 记录日志
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.OrderService");
        logs.setMethod("delOrder");
        logs.setId(1);
        logs.setParams(orderId.toString());
        logs.setTime("100ms");
        logService.saveLog(logs);
    }

}

执行测试

package com.kuangstudy;

import com.kuangstudy.first.Order;
import com.kuangstudy.first.OrderService;
import com.kuangstudy.first.User;
import com.kuangstudy.first.UserService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest
public class SpringbootAopApplicationTests {

    @Autowired
    private UserService userService;
    @Autowired
    private OrderService orderService;


    @Test
    void contextLoads() {
        // 1:用户注册
        userService.saveUser(new User());
        // 2: 用户修改
        userService.updateUser(new User());
        // 3: 用户的删除
        userService.delUser(1);
    }

    @Test
    public void testOrder() {
        // 1:用户注册
        orderService.saveOrder(new Order());
        // 2: 用户修改
        orderService.updateOrder(new Order());
        // 3: 用户的删除
        orderService.delOrder(1);
    }

}

执行结果

【KSD - CONSOLE】 2021-12-21 22:33:38:208 [main] [INFO ] com.kuangstudy.first.UserService saveUser 33 - 用户注册....
【KSD - CONSOLE】 2021-12-21 22:33:38:208 [main] [INFO ] com.kuangstudy.first.LogService saveLog 19 - 你保存日志是:Logs(id=1, classname=com.kuangstudy.service.UserService, method=saveUser, time=100ms, params=User(userId=null, nickname=null, password=null))
【KSD - CONSOLE】 2021-12-21 22:33:38:210 [main] [INFO ] com.kuangstudy.first.UserService updateUser 51 - 修改用户....
【KSD - CONSOLE】 2021-12-21 22:33:38:211 [main] [INFO ] com.kuangstudy.first.LogService saveLog 19 - 你保存日志是:Logs(id=1, classname=com.kuangstudy.service.UserService, method=updateUser, time=100ms, params=User(userId=null, nickname=null, password=null))
【KSD - CONSOLE】 2021-12-21 22:33:38:211 [main] [INFO ] com.kuangstudy.first.UserService delUser 70 - 删除用户....1
【KSD - CONSOLE】 2021-12-21 22:33:38:211 [main] [INFO ] com.kuangstudy.first.LogService saveLog 19 - 你保存日志是:Logs(id=1, classname=com.kuangstudy.service.UserService, method=delUser, time=100ms, params=1)


订单执行的结果

【KSD - CONSOLE】 2021-12-21 22:34:38:089 [main] [INFO ] com.kuangstudy.first.OrderService saveOrder 28 - 订单注册....
【KSD - CONSOLE】 2021-12-21 22:34:38:090 [main] [INFO ] com.kuangstudy.first.LogService saveLog 19 - 你保存日志是:Logs(id=1, classname=com.kuangstudy.service.OrderService, method=saveOrder, time=100ms, params=Order(userId=null, orderId=null, price=null, orderTradeNo=null))
【KSD - CONSOLE】 2021-12-21 22:34:38:096 [main] [INFO ] com.kuangstudy.first.OrderService updateOrder 46 - 修改订单....
【KSD - CONSOLE】 2021-12-21 22:34:38:096 [main] [INFO ] com.kuangstudy.first.LogService saveLog 19 - 你保存日志是:Logs(id=1, classname=com.kuangstudy.service.OrderService, method=updateOrder, time=100ms, params=Order(userId=null, orderId=null, price=null, orderTradeNo=null))
【KSD - CONSOLE】 2021-12-21 22:34:38:097 [main] [INFO ] com.kuangstudy.first.OrderService delOrder 65 - 删除订单....1
【KSD - CONSOLE】 2021-12-21 22:34:38:097 [main] [INFO ] com.kuangstudy.first.LogService saveLog 19 - 你保存日志是:Logs(id=1, classname=com.kuangstudy.service.OrderService, method=delOrder, time=100ms, params=1)


从上面的代码中认知又哪些?

1、通过上面一个非常典型案例就给一些做日志增强处理的时候,我们发现都是 对象执行方法额外做一些事情而这些个事情,你又还得不得不做。

2、日志逻辑的代码是紧耦合,它污染了一些业务逻辑。而且不便于后续的维护。

3、现在上面的整个日志都会拦截,灵活吗? 如果我要考虑到有些方法要拦截,又方法又不拦截怎么处理?比如后续的限流,权限拦截

05、上面的问题的解决方案

  • AOP

06、上面的代码我们可以通过一些方案去优化呢?

01、方法封装

01、日志方法的重载

package com.kuangstudy.first;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
public class LogService {

    public void saveLog(Logs logs) {
        log.info("你保存日志是:{}", logs);
    }

    public void saveLog(String classname,String method,String time,String params) {
        Logs logs = new Logs();
        logs.setClassname("com.kuangstudy.service.OrderService");
        logs.setMethod("updateOrder");
        logs.setId(1);
        logs.setParams(params);
        logs.setTime("100ms");
        log.info("你保存日志是:{}", logs);
    }
}

用户注册方法进行修改

    /**
     * 用户注册
     *
     * @param user
     */
    public void saveUser(User user) {
        log.info("用户注册....");
        // 记录日志
        logService.saveLog("com.kuangstudy.service.UserService","saveUser","100ms",user.toString());
    }

其他的以此类推

02:问题

并没用解决根本性的问题,这个肯定抛弃的。

拦截器(springmvc的拦截器)

核心思想:jdk动态代理 必须 实现接口:HandlerInterceptor

为什么拦截器一定实现一个接口:HandlerInterceptor ,是因为jdk动态代理必须要要有一个接口,才能创建代理对象

定义拦截器

LogInterceptor实现HandlerInterceptor覆盖三个方法。然后把需要处理的日志在handler进行处理。

  • 注册拦截器
  • 配置拦截去路由

拦截器存在问题

  • 和容器(servlet)有关的业务(request,response,freemarker)

  • 粒度太粗了。因为拦截器是根据路由(RequestMapping)来决定,你只能拦截controller层,不能拦截和处理service层,dao层也就是controller以外的都没处理。

    @PostMaiing("/save/userorder")
    public void saveUserOrder(){// --- 3s ~5s
       // 保存订单
       orderServicee.saveOrder(); // 需要记录日志 限流 100ms
       // 用户积分增加
       userService.updateUserjifen();//这个不需要 不做 800ms
    }
    
  • 逻辑处理不方便。太笨重了。

    • 如果出现异常呢(这个问题呢?)
    • 在方法执行中,
    • 在方法执行后
    • 执行结果之后
  • 如果又多个处理,你必须定义多个拦截器,其实粒度太大了。配置和维护都灾难。

addRegisterInterceptor(new LogInterceptor())
.addPatterns("/user/**","/order/**");

addRegisterInterceptor(new PersmissionInterceptor())
.addPatterns("/user/save","/user/update","/order/makeorder");

jdk动态代理、cglib代码

使用aop的底层机制比如:jdk动态代理去实现或者cglib代理去实现。是没问题。但是面临如下问题:

面临的问题:

  • 精细的控制必须自己去编写,
    • 比如:条件判断,哪些方法要进,哪些方法不进。必须自己控制
    • 如果多个代理 代理对象的,代理类创建也必须自己定义(切面,连接点,切点,织入,通知增强,全部要自己去封装)
    • 多代理类怎么执行顺序是如何保证。你也需要自己控制
  • 你必须对springioc的底层机制,和生命周期要非常的熟悉。因为你一定动态代理无论你是用jdk动态代理实现还是cglib代理实现,它们目标都是要把目标对象转化成:代理对象。
  • 因为只有代理对象执行方法,才会进行到代理类(切面)去做逻辑增强处理。(我们处理)

你可能感兴趣的:(spring,java,spring)