引入
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理。
要了解动态代理是如何工作的,首先需要了解
- 什么是代理模式?
- 什么是动态代理?
- 什么是Cglib?
- SpringAOP和Cglib是什么关系?
动态代理要解决什么问题?
什么是代理?
代理模式(Proxy pattern): 为另一个对象提供一个替身或占位符以控制对这个对象的访问

举个简单的例子:
我(client)如果要买(doOperation)房,可以找中介(proxy)买房,中介直接和卖方(target)买房。中介和卖方都实现买卖(doOperation)的操作。中介就是代理(proxy)。
什么是动态代理?
动态代理就是,在程序运行期,创建目标对象的代理对象,并对目标对象中的方法进行功能性增强的一种技术。
在生成代理对象的过程中,目标对象不变,代理对象中的方法是目标对象方法的增强方法。可以理解为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。

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

- 最顶层是字节码,字节码相关的知识请参考 JVM基础 - 类字节码详解
- ASM是操作字节码的工具
- cglib基于ASM字节码工具操作字节码(即动态生成代理,对方法进行增强)
- SpringAOP基于cglib进行封装,实现cglib方式的动态代理
Cglib代理的案例
这里我们写一个使用cglib的简单例子。@pdai
pom包依赖
引入cglib的依赖包
<?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>tech-pdai-spring-demos</artifactId><groupId>tech.pdai</groupId><version>1.0-SNAPSHOT</version></parent><modelVersion>4.0.0</modelVersion><artifactId>007-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
package tech.pdai.springframework.entity;/*** @author pdai*/ 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;}@Overridepublic String toString() {return "User{" +"name='" + name + '\'' +", age=" + age +'}';} }
被代理的类
即目标类, 对被代理的类中的方法进行增强
package tech.pdai.springframework.service;import java.util.Collections; import java.util.List;import tech.pdai.springframework.entity.User;/*** @author pdai*/ public class UserServiceImpl {/*** find user list.** @return user list*/public List<User> findUserList() {return Collections.singletonList(new User("pdai", 18));}/*** add user*/public void addUser() {// do something}}
cglib代理
cglib代理类,需要实现MethodInterceptor接口,并指定代理目标类target
package tech.pdai.springframework.proxy;import java.lang.reflect.Method;import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy;/*** This class is for proxy demo.** @author pdai*/ 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();}// 实现回调方法@Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// log - before methodSystem.out.println("[before] execute method: " + method.getName());// call methodObject result = proxy.invokeSuper(obj, args);// log - after methodSystem.out.println("[after] execute method: " + method.getName() + ", return value: " + result);return null;} }
使用代理
启动类中指定代理目标并执行。
package tech.pdai.springframework;import tech.pdai.springframework.proxy.UserLogProxy; import tech.pdai.springframework.service.UserServiceImpl;/*** Cglib proxy demo.** @author pdai*/ public class ProxyDemo {/*** main interface.** @param args args*/public static void main(String[] args) {// proxyUserServiceImpl userService = (UserServiceImpl) new UserLogProxy().getUserLogProxy(new UserServiceImpl());// call methodsuserService.findUserList();userService.addUser();} }
简单测试
我们启动上述类main 函数,执行的结果如下:
[before] execute method: findUserList [after] execute method: findUserList, return value: [User{name='pdai', age=18}] [before] execute method: addUser [after] execute method: addUser, return value: null
Cglib代理的流程
我们把上述Demo的主要流程画出来,你便能很快理解
更多细节:
- 在上图中,我们可以通过在Enhancer中配置更多的参数来控制代理的行为,比如如果只希望增强这个类中的一个方法(而不是所有方法),那就增加callbackFilter来对目标类中方法进行过滤;Enhancer可以有更多的参数类配置其行为,不过我们在学习上述主要的流程就够了。
- final方法为什么不能被代理?很显然final方法没法被子类覆盖,当然不能代理了。
- Mockito为什么不能mock静态方法?因为mockito也是基于cglib动态代理来实现的,static方法也不能被子类覆盖,所以显然不能mock。但PowerMock可以mock静态方法,因为它直接在bytecode上工作,更多可以看 Mockito单元测试 。(pdai: 通了没?是不是so easy...)
SpringAOP中Cglib代理的实现
SpringAOP封装了cglib,通过其进行动态代理的创建。
我们看下CglibAopProxy的getProxy方法
@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();}// 上节说到的filterenhancer.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() failedthrow new AopConfigException("Unexpected AOP exception", ex);} }
获取callback的方法如下,提几个理解的要点吧,具体读者在学习的时候建议把我的例子跑一下,然后打一个断点进行理解。
rootClass advised exposeProxy DynamicAdvisedInterceptor targetInterceptor
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 optimizednew SerializableNoOp(), // no override for methods mapped to thistargetDispatcher, 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; }
可以结合调试,方便理解

示例源码
https://github.com/realpdai/tech-pdai-spring-demos