Spring Aop实现对目标对象的代理,主要有两种方式:Jdk代理和Cglib代理。这两种代理的区别在于,Jdk代理与目标类都会实现同一个接口,并且在代理类中会调用目标类中被代理的方法,调用者实际调用的则是代理类的方法,通过这种方式我们就可以在代理类中织入切面逻辑;Jdk代理存在的问题在于目标类被代理的方法必须实现某个接口,Cglib代理则是为了解决这个问题而存在的,其实现代理的方式是通过为目标类动态生成一个子类,通过在子类中织入相应逻辑来达到织入代理逻辑的目的。
关于Jdk代理和Cglib代理,其优缺点主要在于:
- Jdk代理生成的代理类只有一个,因而其编译速度是非常快的;而由于被代理的目标类是动态传入代理类中的,Jdk代理的执行效率相对来说低一点,这也是Jdk代理被称为动态代理的原因;
- Cglib代理需要为每个目标类生成相应的子类,因而在实际运行过程中,其可能会生成非常多的子类,过多的子类始终不是太好的,因为这影响了虚拟机编译类的效率;但由于在调用过程中,代理类的方法是已经静态编译生成了的,因而Cglib代理的执行效率相对来说高一些。
本文主要讲解Spring Aop是如何通过Cglib代理实现将切面逻辑织入目标类的。
1. AopProxy织入对象生成
前面我们讲过,Spring Aop织入切面逻辑的入口方法是AbstractAutoProxyCreator.createProxy()方法,如下是该方法的源码:
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
@Nullable Object[] specificInterceptors, TargetSource targetSource) {
// 如果当前beanFactory实现了ConfigurableListableBeanFactory接口,则将需要被代理的
// 对象暴露出来
if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory)
this.beanFactory, beanName, beanClass);
}
// 创建代理工厂
ProxyFactory proxyFactory = new ProxyFactory();
// 复制proxyTargetClass,exposeProxy等属性
proxyFactory.copyFrom(this);
// 如果当前设置了不使用Cglib代理目标类,则判断目标类是否设置了preserveTargetClass属性,
// 如果设置了,则还是强制使用Cglib代理目标类;如果没有设置,则判断目标类是否实现了相关接口,
// 没有设置,则还是使用Cglib代理。需要注意的是Spring默认使用的是Jdk代理来织入切面逻辑。
if (!proxyFactory.isProxyTargetClass()) {
// 判断目标类是否设置了preserveTargetClass属性
if (shouldProxyTargetClass(beanClass, beanName)) {
proxyFactory.setProxyTargetClass(true);
} else {
// 判断目标类是否实现了相关接口
evaluateProxyInterfaces(beanClass, proxyFactory);
}
}
// 将需要织入的切面逻辑都转换为Advisor对象
Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
proxyFactory.addAdvisors(advisors);
proxyFactory.setTargetSource(targetSource);
// 提供的hook方法,供子类实现以实现对代理工厂的定制
customizeProxyFactory(proxyFactory);
proxyFactory.setFrozen(this.freezeProxy);
// 当前判断逻辑默认返回false,子类可进行重写,对于AnnotationAwareAspectJAutoProxyCreator,
// 其重写了该方法返回true,因为其已经对获取到的Advisor进行了过滤,后面不需要在对目标类进行重新
// 匹配了
if (advisorsPreFiltered()) {
proxyFactory.setPreFiltered(true);
}
// 生成代理类
return proxyFactory.getProxy(getProxyClassLoader());
}
可以看到,在生成代理类之前,主要做了两件事:①判断使用Jdk代理还是Cglib代理;②设置相关的属性。这里我们继续看最后的ProxyFactory.getProxy()方法:
public Object getProxy(@Nullable ClassLoader classLoader) {
// 首先获取AopProxy对象,其主要有两个实现:JdkDynamicAopProxy和ObjenesisCglibAopProxy,
// 分别用于Jdk和Cglib代理类的生成,其getProxy()方法则用于获取具体的代理对象
return createAopProxy().getProxy(classLoader);
}
上面的createAopProxy()方法可以理解为一个工厂方法,返回值是一个AopProxy类型的对象,其内部根据具体的条件生成相应的子类对象,即JdkDynamicAopProxy和ObjenesisCglibAopProxy。后面则通过调用AopProxy.getProxy()方法获取代理过的对象。如下是createAopProxy()方法的实现逻辑:
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// 判断当前类是否需要进行运行时优化,或者是指定了使用Cglib代理的方式,再或者是目标类没有用户提供的
// 相关接口,则使用Cglib代理实现代理逻辑的织入
if (config.isOptimize() || config.isProxyTargetClass() ||
hasNoUserSuppliedProxyInterfaces(config)) {
Class<?> targetClass = config.getTargetClass();
if (targetClass == null) {
throw new AopConfigException("TargetSource cannot determine target class: "
+ "Either an interface or a target is required for proxy creation.");
}
// 如果被代理的类是一个接口,或者被代理的类是使用Jdk代理生成的类,此时还是使用Jdk代理
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
return new JdkDynamicAopProxy(config);
}
// 返回Cglib代理织入类对象
return new ObjenesisCglibAopProxy(config);
} else {
// 返回Jdk代理织入类对象
return new JdkDynamicAopProxy(config);
}
}
这里可以看到,本文需要讲解的Cglib代理逻辑的织入就在ObjenesisCglibAopProxy.getProxy()方法中。
2. 代理逻辑的织入
关于代理逻辑的织入,其实现主体还是通过Enhancer来实现,即通过需要织入的Advisor列表,生成Callback对象,并将其设置到Enhancer对象中,最后通过Enhancer生成目标对象。如下是AopProxy.getProxy()方法的源码:
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isDebugEnabled()) {
logger.debug("Creating CGLIB proxy: target source is "
+ this.advised.getTargetSource());
}
try {
Class<?> rootClass = this.advised.getTargetClass();
Assert.state(rootClass != null,
"Target class must be available for creating a CGLIB proxy");
// 判断当前类是否是已经通过Cglib代理生成的类,如果是的,则获取其原始父类,
// 并将其接口设置到需要代理的接口中
Class<?> proxySuperClass = rootClass;
if (ClassUtils.isCglibProxyClass(rootClass)) {
// 获取父类
proxySuperClass = rootClass.getSuperclass();
// 获取父类实现的接口,并将其设置到需要代理的接口中
Class<?>[] additionalInterfaces = rootClass.getInterfaces();
for (Class<?> additionalInterface : additionalInterfaces) {
this.advised.addInterface(additionalInterface);
}
}
// 对目标类进行检查,主要检查点有三个:
// 1. 目标方法不能使用final修饰;
// 2. 目标方法不能是private类型的;
// 3. 目标方法不能是包访问权限的;
// 这三个点满足任何一个,当前方法就不能被代理,此时该方法就会被略过
validateClassIfNecessary(proxySuperClass, classLoader);
// 创建Enhancer对象,并且设置ClassLoader
Enhancer enhancer = createEnhancer();
if (classLoader != null) {
enhancer.setClassLoader(classLoader);
if (classLoader instanceof SmartClassLoader &&
((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
enhancer.setUseCache(false);
}
}
enhancer.setSuperclass(proxySuperClass);
// 这里AopProxyUtils.completeProxiedInterfaces()方法的主要目的是为要生成的代理类
// 增加SpringProxy,Advised,DecoratingProxy三个需要实现的接口。这里三个接口的作用如下:
// 1. SpringProxy:是一个空接口,用于标记当前生成的代理类是Spring生成的代理类;
// 2. Advised:Spring生成代理类所使用的属性都保存在该接口中,
// 包括Advisor,Advice和其他相关属性;
// 3. DecoratingProxy:该接口用于获取当前代理对象所代理的目标对象的Class类型。
enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
enhancer.setStrategy(new
ClassLoaderAwareUndeclaredThrowableStrategy(classLoader));
// 获取当前需要织入到代理类中的逻辑
Callback[] callbacks = getCallbacks(rootClass);
Class<?>[] types = new Class<?>[callbacks.length];
for (int x = 0; x < types.length; x++) {
types[x] = callbacks[x].getClass();
}
// 设置代理类中各个方法将要使用的切面逻辑,这里ProxyCallbackFilter.accept()方法返回
// 的整型值正好一一对应上面Callback数组中各个切面逻辑的下标,也就是说这里的CallbackFilter
// 的作用正好指定了代理类中各个方法将要使用Callback数组中的哪个或哪几个切面逻辑
enhancer.setCallbackFilter(new ProxyCallbackFilter(
this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap,
this.fixedInterceptorOffset));
enhancer.setCallbackTypes(types);
// 生成代理对象
return createProxyClassAndInstance(enhancer, callbacks);
} catch (CodeGenerationException | IllegalArgumentException ex) {
throw new AopConfigException("Could not generate CGLIB subclass of class ["
+ this.advised.getTargetClass() + "]: Common causes of this problem "
+ "include using a final class or a non-visible class", ex);
} catch (Throwable ex) {
throw new AopConfigException("Unexpected AOP exception", ex);
}
}
可以看到,这里的AopProxy.getProxy()方法就是生成代理对象的主干逻辑。上面的逻辑中主要有两个部分需要重点讲解:①如果获取Callback数组;②CallbackFilter的作用。关于第一点,我们后面会进行重点讲解,至于第二点,这里我们需要理解的就是CallbackFilter.accept()方法接收一个Method类型的参数,该参数也即当前要生成的代理逻辑的方法,这里的accept()方法将返回目标当前要织入代理逻辑的方法所需要使用的切面逻辑,也即Callback对象在Callback数组中的下标。关于CallbackFilter的使用原理,读者可以阅读实战CGLib系列之proxy篇(二):回调过滤CallbackFilter这篇文章。下面我们继续阅读getCallbacks()的源码:
private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
boolean exposeProxy = this.advised.isExposeProxy();
boolean isFrozen = this.advised.isFrozen();
boolean isStatic = this.advised.getTargetSource().isStatic();
// 用户自定义的代理逻辑的主要织入类
Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
Callback targetInterceptor;
// 判断如果要暴露代理对象,如果是,则使用AopContext设置将代理对象设置到ThreadLocal中
// 用户则可以通过AopContext获取目标对象
if (exposeProxy) {
// 判断被代理的对象是否是静态的,如果是静态的,则将目标对象缓存起来,每次都使用该对象即可,
// 如果目标对象是动态的,则在DynamicUnadvisedExposedInterceptor中每次都生成一个新的
// 目标对象,以织入后面的代理逻辑
targetInterceptor = isStatic ?
new StaticUnadvisedExposedInterceptor(
this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource());
} else {
// 下面两个类与上面两个的唯一区别就在于是否使用AopContext暴露生成的代理对象
targetInterceptor = isStatic ?
new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
new DynamicUnadvisedInterceptor(this.advised.getTargetSource());
}
// 当前Callback用于一般的不用背代理的方法,这些方法
Callback targetDispatcher = isStatic ?
new StaticDispatcher(this.advised.getTargetSource().getTarget())
: new SerializableNoOp();
// 将获取到的callback组装为一个数组
Callback[] mainCallbacks = new Callback[] {
aopInterceptor, // 用户自己定义的拦截器
targetInterceptor, // 根据条件是否暴露代理对象的拦截器
new SerializableNoOp(), // 不做任何操作的拦截器
targetDispatcher, this.advisedDispatcher, // 用于存储Advised对象的分发器
new EqualsInterceptor(this.advised), // 针对equals方法调用的拦截器
new HashCodeInterceptor(this.advised) // 针对hashcode方法调用的拦截器
};
Callback[] callbacks;
// 如果目标对象是静态的,也即可以缓存的,并且切面逻辑的调用链是固定的,
// 则对目标对象和整个调用链进行缓存
if (isStatic && isFrozen) {
Method[] methods = rootClass.getMethods();
Callback[] fixedCallbacks = new Callback[methods.length];
this.fixedInterceptorMap = new HashMap<>(methods.length);
for (int x = 0; x < methods.length; x++) {
// 获取目标对象的切面逻辑
List<Object> chain =
this.advised.getInterceptorsAndDynamicInterceptionAdvice(
methods[x], rootClass);
fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
chain, this.advised.getTargetSource().getTarget(),
this.advised.getTargetClass());
// 对调用链进行缓存
this.fixedInterceptorMap.put(methods[x].toString(), x);
}
// 将生成的静态调用链存入Callback数组中
callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length,
fixedCallbacks.length);
// 这里fixedInterceptorOffset记录了当前静态的调用链的切面逻辑的起始位置,
// 这里记录的用处在于后面使用CallbackFilter的时候,如果发现是静态的调用链,
// 则直接通过该参数获取相应的调用链,而直接略过了前面的动态调用链
this.fixedInterceptorOffset = mainCallbacks.length;
} else {
callbacks = mainCallbacks;
}
return callbacks;
}
这里的getCallbacks()方法主要做了三件事:①获取目标对象的动态调用链;②判断是否设置了exposeProxy属性,如果设置了,则生成一个可以暴露代理对象的Callback对象,否则生成一个不做任何处理直接调用目标对象的Callback对象;③判断目标对象是否是静态的,并且当前的切面逻辑是否是固定的,如果是,则将目标对象和调用链进行缓存,以便后续直接调用。这里需要说明的一个点在于第三点,因为在判断目标对象为静态对象,并且调用链是固定的时候,会将目标对象和调用链进行缓存,并且封装到指定的Callback对象中。这里读者可能会疑问为什么动态调用链和静态调用链都进行了缓存,这和前面讲解的CallbackFilter是息息相关的,因为上述代码最后使用fixedInterceptorOffset记录了当前静态调用链在数组中存储的位置,我们前面也讲了,Enhancer可以通过CallbackFilter返回的整数值来动态的指定从当前对象Callback数组中的第几个环绕逻辑开始织入,这里就会使用到fixedInterceptorOffset。从上述代码中可以看出,用户自定义的调用链是在DynamicAdvisedInterceptor中生成的(关于静态调用链的生成实际上是同样的逻辑,只不过静态调用链会被缓存),这里我们看看DynamicAdvisedInterceptor的实现源码:
public Object intercept(Object proxy, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
Object oldProxy = null;
boolean setProxyContext = false;
Object target = null;
// 通过TargetSource获取目标对象
TargetSource targetSource = this.advised.getTargetSource();
try {
// 判断如果需要暴露代理对象,则将当前代理对象设置到ThreadLocal中
if (this.advised.exposeProxy) {
oldProxy = AopContext.setCurrentProxy(proxy);
setProxyContext = true;
}
target = targetSource.getTarget();
Class<?> targetClass = (target != null ? target.getClass() : null);
// 获取目标对象切面逻辑的环绕链
List<Object> chain = this.advised
.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
Object retVal;
if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) {
// 对参数进行处理,以使其与目标方法的参数类型一致,尤其对于数组类型,
// 会单独处理其数据类型与实际类型一致
Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
// 因为没有切面逻辑需要织入,这里直接调用目标方法
retVal = methodProxy.invoke(target, argsToUse);
} else {
// 通过生成的调用链,对目标方法进行环绕调用
retVal = new CglibMethodInvocation(proxy, target, method,
args, targetClass, chain, methodProxy).proceed();
}
// 对返回值进行处理,如果返回值就是当前目标对象,那么将代理生成的代理对象返回;
// 如果返回值为空,并且返回值类型是非void的基本数据类型,则抛出异常;
// 如果上述两个条件都不符合,则直接将生成的返回值返回
retVal = processReturnType(proxy, target, method, retVal);
return retVal;
} finally {
// 如果目标对象不是静态的,则调用TargetSource.releaseTarget()方法释放目标对象
if (target != null && !targetSource.isStatic()) {
targetSource.releaseTarget(target);
}
// 将代理对象设置为前面(外层逻辑)调用设置的对象,以防止暴露出来的代理对象不一致
if (setProxyContext) {
AopContext.setCurrentProxy(oldProxy);
}
}
}
这里intercept()方法里主要逻辑有两点:①为目标对象生成切面逻辑调用链;②通过切面逻辑对目标对象进行环绕,并且进行调用。关于这两点,我们都会进行讲解,这里我们首先看看Cglib是如何生成调用链的,如下是getInterceptorsAndDynamicInterceptionAdvice()方法最终调用的源码,中间略过了部分比较简单的调用:
public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
Advised config, Method method, @Nullable Class<?> targetClass) {
List<Object> interceptorList = new ArrayList<>(config.getAdvisors().length);
Class<?> actualClass = (targetClass != null ?
targetClass : method.getDeclaringClass());
// 判断切面逻辑中是否有IntroductionAdvisor类型的Advisor
boolean hasIntroductions = hasMatchingIntroductions(config, actualClass);
AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
for (Advisor advisor : config.getAdvisors()) {
if (advisor instanceof PointcutAdvisor) {
PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
// 这里判断切面逻辑的调用链是否提前进行过过滤,如果进行过,则不再进行目标方法的匹配,
// 如果没有,则再进行一次匹配。这里我们使用的AnnotationAwareAspectJAutoProxyCreator
// 在生成切面逻辑的时候就已经进行了过滤,因而这里返回的是true,本文最开始也对这里进行了讲解
if (config.isPreFiltered() || pointcutAdvisor.getPointcut()
.getClassFilter().matches(actualClass)) {
// 将Advisor对象转换为MethodInterceptor数组
MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
// 这里进行匹配的时候,首先会检查是否为IntroductionAwareMethodMatcher类型的
// Matcher,如果是,则调用其定义的matches()方法进行匹配,如果不是,则直接调用
// 当前切面的matches()方法进行匹配。这里由于前面进行匹配时可能存在部分在静态匹配时
// 无法确认的方法匹配结果,因而这里调用是必要的,而对于能够确认的匹配逻辑,这里调用
// 也是非常迅速的,因为前面已经对匹配结果进行了缓存
if (MethodMatchers.matches(mm, method, actualClass, hasIntroductions)) {
// 判断如果是动态匹配,则使用InterceptorAndDynamicMethodMatcher对其进行封装
if (mm.isRuntime()) {
for (MethodInterceptor interceptor : interceptors) {
interceptorList.add(
new InterceptorAndDynamicMethodMatcher(interceptor, mm));
}
} else {
// 如果是静态匹配,则直接将调用链返回
interceptorList.addAll(Arrays.asList(interceptors));
}
}
}
} else if (advisor instanceof IntroductionAdvisor) {
IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
// 判断如果为IntroductionAdvisor类型的Advisor,则将调用链封装为Interceptor数组
if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
} else {
// 这里是提供的使用自定义的转换器对Advisor进行转换的逻辑,因为getInterceptors()方法中
// 会使用相应的Adapter对目标Advisor进行匹配,如果能匹配上,通过其getInterceptor()方法
// 将自定义的Advice转换为MethodInterceptor对象
Interceptor[] interceptors = registry.getInterceptors(advisor);
interceptorList.addAll(Arrays.asList(interceptors));
}
}
return interceptorList;
}
这里获取调用链的逻辑其实比较简单,其最终的目的就是将Advisor数组一个一个的封装为Interceptor对象。在进行Advisor封装的时候,这里分为了三种类型:
- 如果目标切面逻辑是一般的切面逻辑,即PointcutAdvisor,则会在运行时对目标方法进行动态匹配,因为前面可能存在还不能确认的是否应该应用切面逻辑的方法;
- 如果切面逻辑是IntroductionAdvisor的,则将其封装为Interceptor类型的数组;
- 如果以上两个都不是,说明切面逻辑可能是用户自定义的切面逻辑,这里就通过注册的AdvisorAdapter进行匹配,如果某个Adapter能够支持当前Advisor的转换,则调用其getInterceptor()方法将Advisor转换为MethodInterceptor返回。
下面我们看看Cglib是如何通过生成的切面调用链将目标对象进行环绕的。前面我们讲了,将切面逻辑进行织入的逻辑在CglibMethodInvocation中,实际上其调用逻辑在其proceed()方法中,这里我们直接看该方法的源码:
public Object proceed() throws Throwable {
// 这里currentInterceptorIndex记录了当前调用链中正在调用的Intercepor的下标,该数值初始为-1
if (this.currentInterceptorIndex ==
this.interceptorsAndDynamicMethodMatchers.size() - 1) {
// 如果调用链为空,则直接调用目标方法
return invokeJoinpoint();
}
// 获取下一个需要织入的Interceptor逻辑
Object interceptorOrInterceptionAdvice =
this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
InterceptorAndDynamicMethodMatcher dm =
(InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
// 对动态的方法进行匹配,如果匹配成功,才进行调用,否则直接进行下一个Interceptor的调用
if (dm.methodMatcher.matches(this.method, this.targetClass, this.arguments)) {
return dm.interceptor.invoke(this);
} else {
return proceed();
}
} else {
// 如果不需要进行动态匹配,则直接进行下一步的调用
return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
}
}
这里proceed()方法的逻辑比较简单,其使用一个索引记录了当前正在调用的Interceptor在调用链中的位置,并且依次对调用链进行调用,从而实现将切面逻辑织入目标对象的目的。这里最终对目标对象的调用的逻辑在invokeJoinpoint()方法中。
3. 小结
本文首先讲解Spring是如何通过配置的参数来选择使用哪种代理方式的,然后重点讲解了Spring Aop是如何使用Cglib代理实现代理逻辑的织入的。
本文来自云栖社区合作伙伴“开源中国”
本文作者:王练