文章目录
- 1. 日志功能
- 1.1 基本方式实现
- 1.1.1 导入初始工程
- 1.1.2 添加日志功能
- 1.1.3 测试
- 1.1.4 分析代码问题
- 1.1.5 解决方法
- 1.2 jdk动态代理
- 1.2.1 复制工程
- 1.2.2 准备目标类
- 1.2.3 准备增强类
- 1.2.4 创建代理对象
- 1.3 CGLIB动态代理
- 1.3.1 复制工程
- 1.3.2 删除接口
- 1.3.3 创建代理对象
- 1.3.4 小结
- 1.4 总结
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 总结
当核心业务(保存)和增强业务(日志)同时出现时,我们可以在开发时对他们分别开发,运行时再组装在一起(使用动态代理的方式)。
这样做的好处是:
- 逻辑清晰:开发核心业务的时候,不必关注增强业务的代码
- 代码复用性高:增强代码不用重复书写
这就是一种 AOP ( 面向切面编程 ) 的思想,它的目的就是在不修改源代码的基础上,对原有功能进行增强。
我的总结: 开发阶段分别开发,运行阶段组装运行