第三章、Spring AOP实现原理
一、Spring AOP实现原理
Spring基于代理模式实现功能动态扩展,包含两种形式:
目标类拥有接口,通过JDK动态代理实现功能扩展。
目标类没有接口,通过CGLib组件实现功能扩展。
1、代理模式
代理模式通过代理对象对原对象的实现功能扩展。
2、静态代理
静态代理是指必须手动创建代理类的代理模式使用方式。
打开IDEA创建新的maven工程,在com.ql.spring.service包下创建UserService接口
package com.ql.spring.service;
public interface UserService {
public void createUser();
}
然后同包下创建该接口的实现类UserServiceImpl
package com.ql.spring.service;
public class UserServiceImpl implements UserService{
@Override
public void createUser() {
System.out.println("执行创建用户业务逻辑");
}
}
然后同包下创建代理类UserServiceProxy
package com.ql.spring.service;
import java.text.SimpleDateFormat;
import java.util.Date;
public class UserServiceProxy implements UserService{
//持有委托类的对象
private UserService userService;
public UserServiceProxy(UserService userService){
this.userService = userService;
}
@Override
public void createUser() {
System.out.println("======="+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"===========");
userService.createUser();
}
}
然后在com.ql.spring包下创建入口类Application ,并运行
package com.ql.spring;
import com.ql.spring.service.UserService;
import com.ql.spring.service.UserServiceImpl;
import com.ql.spring.service.UserServiceProxy;
public class Application {
public static void main(String[] args) {
UserService userService = new UserServiceProxy(new UserServiceImpl());
userService.createUser();
}
}
/*
=======2022-07-04 23:27:22 421===========
执行创建用户业务逻辑
*/
代理类的构造方法中,参数建议使用持有一个接口的某个实现类,而不是具体实现类。原因是作为代理模式是可以嵌套使用的,每次构造方法里传入实现接口的其他代理类就可以了,从而实现多层嵌套。
在com.ql.spring.service包下再创建一个代理类UserServiceProxy1
package com.ql.spring.service;
import java.text.SimpleDateFormat;
import java.util.Date;
public class UserServiceProxy1 implements UserService{
//持有委托类的对象
private UserService userService;
public UserServiceProxy1(UserService userService){
this.userService = userService;
}
@Override
public void createUser() {
userService.createUser();
System.out.println("=========后置扩展功能=========");
}
}
然后修改入口类Application
package com.ql.spring;
import com.ql.spring.service.UserService;
import com.ql.spring.service.UserServiceImpl;
import com.ql.spring.service.UserServiceProxy;
import com.ql.spring.service.UserServiceProxy1;
public class Application {
public static void main(String[] args) {
UserService userService = new UserServiceProxy1(new UserServiceProxy(new UserServiceImpl()));
userService.createUser();
}
}
/*
=======2022-07-04 23:32:05 461===========
执行创建用户业务逻辑
=========后置扩展功能=========
*/
3、JDK动态代理
动态代理,必须实现接口才可以运行。
JDK动态代理具体实现原理:
- 通过实现 InvocationHandler 接⼝创建⾃⼰的调⽤处理器;
- 通过为 Proxy 类指定 ClassLoader 对象和⼀组 interface 来创建动态代理;
- 通过反射机制获取动态代理类的构造函数,其唯⼀参数类型就是调⽤处理器接⼝类型;
- 通过构造函数创建动态代理类实例,构造时调⽤处理器对象作为参数参⼊;
JDK 动态代理是⾯向接⼝的代理模式,Spring 通过 Java 的反射机制⽣产被代理接⼝的新的匿名实现类,重写了其中 AOP 的增强⽅法。
在com.ql.spring.service包下创建EmployeeService和他的实现类EmployeeServiceImpl
package com.ql.spring.service;
public interface EmployeeService {
public void createEmployee();
}
package com.ql.spring.service;
public class EmployeeServiceImpl implements EmployeeService{
@Override
public void createEmployee() {
System.out.println("执行创建员工业务逻辑");
}
}
在同包下创建动态代理实现类ProxyInvocationHandler
package com.ql.spring.service;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* InvocationHandler是JDK提供的反射类,用于在JDK动态代理中对目标方法进行增强
* InvocationHandler实现类与切面类的环绕通知类似
*/
public class ProxyInvocationHandler implements InvocationHandler {
private Object target;//目标对象
private ProxyInvocationHandler(Object target){
this.target = target;
}
/**
* 在invoke()方法对目标方法进行增强
* @param proxy 代理类对象
* @param method 目标方法对象
* @param args 目标方法实参
* @return 目标方法运行后返回值
* @throws Throwable 目标方法抛出的异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("========"+new SimpleDateFormat("yyyy-MM-dd HH:mm:ss SSS").format(new Date()) +"========");
Object ret = method.invoke(target, args);//调用目标方法,类似环绕通知的ProceedingJoinPoint.proceed()
return ret;
}
public static void main(String[] args) {
UserService userService = new UserServiceImpl();
ProxyInvocationHandler invocationHandler = new ProxyInvocationHandler(userService);
//动态创建代理类
UserService userServiceProxy = (UserService) Proxy.newProxyInstance(userService.getClass().getClassLoader(),
userService.getClass().getInterfaces(),
invocationHandler);
userServiceProxy.createUser();
EmployeeService employeeService = new EmployeeServiceImpl();
EmployeeService employeeServiceProxy = (EmployeeService) Proxy.newProxyInstance(employeeService.getClass().getClassLoader(),
employeeService.getClass().getInterfaces(),
new ProxyInvocationHandler(employeeService));
employeeServiceProxy.createEmployee();
}
}
/*
========2022-07-05 08:52:16 166========
执行创建用户业务逻辑
========2022-07-05 08:52:16 169========
执行创建员工业务逻辑
*/
4、CGLib实现代理类
CGLib是运行时字节码增强技术。(Code Generation Library)
Spring AOP扩展无接口类使用CGLib
AOP会运行时生成目标继承类字节码的方式进行行为扩展。
利⽤ ASM 开源包,对代理对象类的 class ⽂件加载进来,通过修改其字节码⽣成⼦类来处理。
两者对⽐:
JDK 动态代理是⾯向接⼝的。
CGLib 动态代理是通过字节码底层继承要代理类来实现,因此如果被代理类被 final 关键字所修饰,会失败。
如果要被代理的对象是个实现类,那么 Spring 会使⽤ JDK 动态代理来完成操作(Spirng 默认采⽤ JDK 动态代理实现机制);
如果要被代理的对象不是个实现类,那么 Spring 会强制使⽤ CGLib 来实现动态代理。