我们在前文中已经介绍了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 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
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 +
'}';
}
}
被代理的类
即目标类, 对被代理的类中的方法进行增强
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
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;
}
}
使用代理
启动类中指定代理目标并执行。
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 函数,执行的结果如下:
[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方法
@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方法返回的索引从这个集合中返回对应的拦截增强器执行增强操作
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;
}
可以结合调试,方便理解

参考文章
https://pdai.tech/md/spring/spring-x-framework-aop-source-3.html
677

被折叠的 条评论
为什么被折叠?



