Spring进阶 - Spring AOP实现原理(三)Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理。
要了解动态代理是如何工作的,首先需要了解
- 什么是代理模式?
- 什么是动态代理?
- 什么是Cglib?
- SpringAOP和Cglib是什么关系?
动态代理要解决什么问题?
什么是代理?
代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问。从代码为例,就是我们调用的是目标对象的方法,但实际执行的是代理对象的方法,很奇怪,明明没调用代理对象,它非要执行。这是因为目标对象被代理了,一定会这样执行。

再举一个生活中的例子,你(调用方)去买房,你根本分不清他是中介(代理对象)还是房东(真实对象)。
什么是动态代理?
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,有一种技术可以动态拦截目标对象,在目标对象前后增加其它逻辑,这种技术就是动态代理技术。

什么是Cglib? SpringAOP和Cglib是什么关系?
Cglib是一个强大的、高性能的代码生成包,它广泛被许多AOP框架使用,为他们提供方法的拦截。

- 最底层是字节码
- ASM是操作字节码的工具
- cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)
- SpringAOP基于cglib进行封装,实现cglib方式的动态代理
Cglib代理的案例
不借助 Spring AOP 框架,用 Cglib 一样可以实现代理,只是没有切面编程范式,就没有切入点、通知等功能,很不灵活,实现如下:
pom包依赖
引入cglib的依赖包
▼xml复制代码<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <parent> <artifactId>com-fency-spring-demos</artifactId> <groupId>com.fency</groupId> <version>1.0-SNAPSHOT</version> </parent> <modelVersion>4.0.0</modelVersion> <artifactId>spring-framework-demo-aop-proxy-cglib</artifactId> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> </dependencies> </project>
定义实体
User
▼java复制代码public class User { /** * user's name. */ private String name; /** * user's age. */ private int age; /** * init. * * @param name name * @param age age */ public User(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + '}'; } }
被代理的类
即目标类, 对被代理的类中的方法进行增强
▼java复制代码public class UserServiceImpl { /** * find user list. * * @return user list */ public List<User> findUserList() { return Collections.singletonList(new User("fency", 18)); } /** * add user */ public void addUser() { // do something } }
cglib代理
cglib代理类,需要实现MethodInterceptor接口,并指定代理目标类target
▼java复制代码public class UserLogProxy implements MethodInterceptor { /** * 业务类对象,供代理方法中进行真正的业务方法调用 */ private Object target; public Object getUserLogProxy(Object target) { //给业务对象赋值 this.target = target; //创建加强器,用来创建动态代理类 Enhancer enhancer = new Enhancer(); //为加强器指定要代理的业务类(即:为下面生成的代理类指定父类) enhancer.setSuperclass(this.target.getClass()); //设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦截 enhancer.setCallback(this); // 创建动态代理类对象并返回 return enhancer.create(); } // 实现回调方法 @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { // log - before method System.out.println("[before] execute method: " + method.getName()); // call method Object result = proxy.invokeSuper(obj, args); // log - after method System.out.println("[after] execute method: " + method.getName() + ", return value: " + result); return null; } }
使用代理
启动类中指定代理目标并执行。
▼java复制代码public class ProxyDemo { /** * main interface. * * @param args args */ public static void main(String[] args) { // proxy UserServiceImpl userService = (UserServiceImpl) new UserLogProxy().getUserLogProxy(new UserServiceImpl()); // call methods userService.findUserList(); userService.addUser(); } }
简单测试
我们启动上述类main 函数,执行的结果如下:
▼bash复制代码[before] execute method: findUserList [after] execute method: findUserList, return value: [User{name='fency', age=18}] [before] execute method: addUser [after] execute method: addUser, return value: null
Cglib代理的流程
我们把上述Demo的主要流程画出来,你便能很快理解

SpringAOP中Cglib代理的实现
SpringAOP封装了cglib,通过其进行动态代理的创建。
CglibAopProxy 是 Spring 框架的一个工具类,作用是统一管理和执行CGLIB代理逻辑。
Spring 6.x 没有 CglibAopProxy ,取而代之的是 DefaultAopProxyFactory ,本文仅学习 CglibAopProxy 源码。
我们看下CglibAopProxy的getProxy方法
▼java复制代码@Override public Object getProxy() { return getProxy(null); } @Override public Object getProxy(@Nullable ClassLoader classLoader) { if (logger.isTraceEnabled()) { logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource()); } try { Class<?> rootClass = this.advised.getTargetClass(); Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy"); // 上面流程图中的目标类 Class<?> proxySuperClass = rootClass; if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) { proxySuperClass = rootClass.getSuperclass(); Class<?>[] additionalInterfaces = rootClass.getInterfaces(); for (Class<?> additionalInterface : additionalInterfaces) { this.advised.addInterface(additionalInterface); } } // Validate the class, writing log messages as necessary. validateClassIfNecessary(proxySuperClass, classLoader); // 重点看这里,就是上图的enhancer,设置各种参数来构建 Enhancer enhancer = createEnhancer(); if (classLoader != null) { enhancer.setClassLoader(classLoader); if (classLoader instanceof SmartClassLoader && ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) { enhancer.setUseCache(false); } } enhancer.setSuperclass(proxySuperClass); enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised)); enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE); enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader)); // 设置callback回调接口,即方法的增强点 Callback[] callbacks = getCallbacks(rootClass); Class<?>[] types = new Class<?>[callbacks.length]; for (int x = 0; x < types.length; x++) { types[x] = callbacks[x].getClass(); } // 上节说到的filter enhancer.setCallbackFilter(new ProxyCallbackFilter( this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset)); enhancer.setCallbackTypes(types); // 重点:创建proxy和其实例 return createProxyClassAndInstance(enhancer, callbacks); } catch (CodeGenerationException | IllegalArgumentException ex) { throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() + ": Common causes of this problem include using a final class or a non-visible class", ex); } catch (Throwable ex) { // TargetSource.getTarget() failed throw new AopConfigException("Unexpected AOP exception", ex); } }
重点关注 getCallbacks(rootClass) 方法,下面给出源码。
提几个下面这段源码的关键点:
rootClass: 即目标代理类advised: 包含上文中我们获取到的advisor增强器的集合exposeProxy: 在xml配置文件中配置的,作用就是如果在事务A中使用了代理,事务A调用了目标类的方法a,在方法a中又调用目标类的方法b,方法a,b同时都是要被增强的方法,如果不配置exposeProxy属性,方法b的增强将会失效,如果配置exposeProxy,方法b在方法a的执行中也会被增强了DynamicAdvisedInterceptor: 拦截器将advised(包含上文中我们获取到的advisor增强器)构建配置的AOP的callback(第一个callback)targetInterceptor: xml配置的optimize属性使用的(第二个callback)- 最后连同其它5个默认的Interceptor 返回作为cglib的拦截器链,之后通过CallbackFilter的accpet方法返回的索引从这个集合中返回对应的拦截增强器执行增强操作
▼java复制代码private Callback[] getCallbacks(Class<?> rootClass) throws Exception { // Parameters used for optimization choices... boolean exposeProxy = this.advised.isExposeProxy(); boolean isFrozen = this.advised.isFrozen(); boolean isStatic = this.advised.getTargetSource().isStatic(); // Choose an "aop" interceptor (used for AOP calls). Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised); // Choose a "straight to target" interceptor. (used for calls that are // unadvised but can return this). May be required to expose the proxy. Callback targetInterceptor; if (exposeProxy) { targetInterceptor = (isStatic ? new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource())); } else { targetInterceptor = (isStatic ? new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) : new DynamicUnadvisedInterceptor(this.advised.getTargetSource())); } // Choose a "direct to target" dispatcher (used for // unadvised calls to static targets that cannot return this). Callback targetDispatcher = (isStatic ? new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp()); Callback[] mainCallbacks = new Callback[] { aopInterceptor, // targetInterceptor, // invoke target without considering advice, if optimized new SerializableNoOp(), // no override for methods mapped to this targetDispatcher, this.advisedDispatcher, new EqualsInterceptor(this.advised), new HashCodeInterceptor(this.advised) }; Callback[] callbacks; // If the target is a static one and the advice chain is frozen, // then we can make some optimizations by sending the AOP calls // direct to the target using the fixed chain for that method. if (isStatic && isFrozen) { Method[] methods = rootClass.getMethods(); Callback[] fixedCallbacks = new Callback[methods.length]; this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length); // TODO: small memory optimization here (can skip creation for methods with no advice) for (int x = 0; x < methods.length; x++) { Method method = methods[x]; List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass); fixedCallbacks[x] = new FixedChainStaticTargetInterceptor( chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass()); this.fixedInterceptorMap.put(method, x); } // Now copy both the callbacks from mainCallbacks // and fixedCallbacks into the callbacks array. callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length]; System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length); System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length); this.fixedInterceptorOffset = mainCallbacks.length; } else { callbacks = mainCallbacks; } return callbacks; }
可以结合调试,方便理解


