1. 日志功能

1.1 基本方式实现

1.1.1 导入初始工程

导入maven项目

1.1.2 添加日志功能

日志类
@Component
public class Logger {

    public void m1() {
        System.out.println("方法执行之前" + LocalDateTime.now());
    }

    public void m2() {
        System.out.println("方法运行结束" + LocalDateTime.now());
    }

    public void m3() {
        System.out.println("方法出现异常" + LocalDateTime.now());
    }
}

业务层
@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private Logger logger;
    @Override
    public List<Dept> findAll() {
        logger.m1();
        try {
            System.out.println("findAll 查询列表");
        }catch (Exception e){
            logger.m3();
        }
        logger.m2();
        return new ArrayList<>();
    }
 }

1.1.3 测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DeptServiceTest {

    @Autowired
    private DeptService deptService;

    @Test
    public void test1() {
        deptService.findAll();
    }

测试结果
方法执行之前2023-04-20T09:05:54.409563800
findAll 查询列表
方法运行结束2023-04-20T09:05:54.413553100

1.1.4 分析代码问题

代码复用性低:日志代码在每个方法都要书写一遍
代码耦合性高:业务代码和日志代码耦合在了一起

1.1.5 解决方法

使用动态代理

1.2 jdk动态代理

动态代理: 目标对象(EmpServiceImpl)+增强逻辑(Logger)=代理对象
基于jdk动态代理产生的代理对象与被代理对象是兄弟关系,二者实现同一接口
jdk代理对象必须有接口

1.2.1 复制工程

将之前有问题的项目复制一份,在此基础上修改

1.2.2 准备目标类

目标类是指要被代理的类

@Service
public class DeptServiceImpl implements DeptService {
    @Override
    public List<Dept> findAll() {
        System.out.println("findAll 查询列表");
        return new ArrayList<>();
    }

    @Override
    public void save(Dept dept) {
        System.out.println("save  保存成功");
    }

    @Override
    public Dept findById(Integer id) {
        System.out.println("findById 根据id查询");
        return new Dept();
    }
}

1.2.3 准备增强类

增强类指的是要给被代理类添加的功能

@Component
public class Logger {

    public void m1() {
        System.out.println("方法执行之前" + LocalDateTime.now());
    }

    public void m2() {
        System.out.println("方法运行结束" + LocalDateTime.now());
    }

    public void m3() {
        System.out.println("方法出现异常" + LocalDateTime.now());
    }
}

1.2.4 创建代理对象

在测试类中,使用jdk技术创建代理对象,然后调用方法

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DeptServiceTest {
    //准备目标对象
    @Autowired
    private DeptService deptService;
    //增强逻辑对象
    @Autowired
    private Logger logger;

    @Test
    public void test1() {
    //获取方法,编写增强逻辑
        InvocationHandler invocationHandler = new InvocationHandler() {

            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //前置增强
                logger.m1();
                Object result = null;
                try {
                //接受返回值
                    result = method.invoke(deptService, args);
                } catch (IllegalAccessException e) {
                    logger.m3();
                }
                //后置增强
                logger.m2();
                return result;
            }
        };
        //获取代理对象(强转成和目标对象在同一接口下)
        DeptService proxyInstance = (DeptService) Proxy.newProxyInstance(
                deptService.getClass().getClassLoader(),//类加载器(与目标方法一致)
                deptService.getClass().getInterfaces(),//接口(与目标方法处于同一接口下)
                invocationHandler);//增强逻辑
        //代理对象调用方法
        proxyInstance.findAll();
    }
}

测试结果
方法执行之前2023-04-20T09:40:52.372474300
findAll 查询列表
方法运行结束2023-04-20T09:40:52.376464300

1.3 CGLIB动态代理

基于Cglib动态代理产生的代理对象与被代理对象是父子关系,代理对象是被代理对象的儿子

1.3.1 复制工程

将jdk代理复制一份,在此基础上进行修改

1.3.2 删除接口

删除DeptService接口,并删除类中对接口的实现

1.3.3 创建代理对象

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class DeptServiceTest {
    //目标对象
    @Autowired
    private DeptServiceImpl deptServiceimpl;

    //增强逻辑对象
    @Autowired
    private Logger logger;

    @Test
    public void test1() {
        //注意: 这个InvocationHandler是org.springframework.cglib.proxy.InvocationHandler提供的
        InvocationHandler invocationHandler = new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] args) throws Throwable {
                logger.m1();
                Object result = null;
                try {
                    result = method.invoke(deptServiceimpl, args);
                } catch (Exception e) {
                    logger.m3();
                }
                logger.m2();
                return result;
            }
        };

        //创建代理对象
        DeptServiceImpl proxyInstance  = (DeptServiceImpl) Enhancer.create(
                DeptServiceImpl.class,
                invocationHandler);
        //调用带对象方法
        proxyInstance.findAll();
    }
}

1.3.4 小结

首先明确在创建代理实现类时,jdk的速度要高于cglib,所以选择的时候:
当被代理类有接口的时候,使用jdk动态代理;
当被代理类没有接口的时候,使用cglib动态代理

1.4 总结

当核心业务(保存)和增强业务(日志)同时出现时,我们可以在开发时对他们分别开发,运行时再组装在一起(使用动态代理的方式)。
这样做的好处是:

  1. 逻辑清晰:开发核心业务的时候,不必关注增强业务的代码
  2. 代码复用性高:增强代码不用重复书写

这就是一种 AOP ( 面向切面编程 ) 的思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。
我的总结: 开发阶段分别开发,运行阶段组装运行

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐