Spring源码分析——AOP
Spring源码分析——AOP
寻找 aop:aspectj-autoproxy 注解对应的解析器
要使用SpringAop的功能,需要在XMl配置文件中配置 <aop:aspectj-autoproxy/>注解,这个注解便是开启spring的aop功能的开关,所以他就是分析aop功能原理的入口
org.springframework.aop.config.AopNamespaceHandler#init
@Override public void init() { // In 2.0 XSD as well as in 2.5+ XSDs registerBeanDefinitionParser("config", new ConfigBeanDefinitionParser()); /** * 注册自定义的BeanDefinition解析器,用于自定义 "节点解析成BeanDefinition" 的流程 * AspectJAutoProxyBeanDefinitionParser主要的功能就 * 是将AnnotationAwareAspectJAutoProxyCreator注册到Spring容器中, * 把bean交给Spring去托管 */ registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser()); registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());
<span class="token comment">// Only in 2.0 XSD: moved to context namespace in 2.5+</span> <span class="token function">registerBeanDefinitionParser</span><span class="token punctuation">(</span><span class="token string">"spring-configured"</span><span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">SpringConfiguredBeanDefinitionParser</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
可以看到,我们只要配置了<aop:aspectj-autoproxy/>注解,其实就会想spring容器中注册一个BeanDefinition解析器 AspectJAutoProxyBeanDefinitionParser,BeanDefinition解析器的功能就是想spring容器中解析注册BeanDefinition的,然后spring就会根据已有的BeanDefinition取实例化bean
了解AspectJAutoProxyBeanDefinitionParser对应的行为
org.springframework.aop.config.AspectJAutoProxyBeanDefinitionParser#parse
@Override
@Nullable
public BeanDefinition parse(Element element, ParserContext parserContext) {
//注册 AnnotationAwareAspectJAutoProxyCreator 对应的的BeanDefinition
AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);
extendBeanDefinition(element, parserContext);
return null;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
这里调用了AopNamespaceUtils的静态方法registerAspectJAnnotationAutoProxyCreatorIfNecessary
org.springframework.aop.config.AopNamespaceUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) {
BeanDefinition beanDefinition <span class="token operator">=</span> AopConfigUtils<span class="token punctuation">.</span><span class="token function">registerAspectJAnnotationAutoProxyCreatorIfNecessary</span><span class="token punctuation">(</span> parserContext<span class="token punctuation">.</span><span class="token function">getRegistry</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> parserContext<span class="token punctuation">.</span><span class="token function">extractSource</span><span class="token punctuation">(</span>sourceElement<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">useClassProxyingIfNecessary</span><span class="token punctuation">(</span>parserContext<span class="token punctuation">.</span><span class="token function">getRegistry</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> sourceElement<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">registerComponentIfNecessary</span><span class="token punctuation">(</span>beanDefinition<span class="token punctuation">,</span> parserContext<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
可以看到,这里又接着调用另外一个静态方法的AopConfigUtils的registerAspectJAnnotationAutoProxyCreatorIfNecessary方法
org.springframework.aop.config.AopConfigUtils#registerAspectJAnnotationAutoProxyCreatorIfNecessary(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
@Nullable public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary( BeanDefinitionRegistry registry, @Nullable Object source) {
<span class="token keyword">return</span> <span class="token function">registerOrEscalateApcAsRequired</span><span class="token punctuation">(</span>AnnotationAwareAspectJAutoProxyCreator<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">,</span> registry<span class="token punctuation">,</span> source<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
org.springframework.aop.config.AopConfigUtils#registerOrEscalateApcAsRequired
@Nullable private static BeanDefinition registerOrEscalateApcAsRequired( Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {
Assert<span class="token punctuation">.</span><span class="token function">notNull</span><span class="token punctuation">(</span>registry<span class="token punctuation">,</span> <span class="token string">"BeanDefinitionRegistry must not be null"</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>registry<span class="token punctuation">.</span><span class="token function">containsBeanDefinition</span><span class="token punctuation">(</span>AUTO_PROXY_CREATOR_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> BeanDefinition apcDefinition <span class="token operator">=</span> registry<span class="token punctuation">.</span><span class="token function">getBeanDefinition</span><span class="token punctuation">(</span>AUTO_PROXY_CREATOR_BEAN_NAME<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>cls<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">equals</span><span class="token punctuation">(</span>apcDefinition<span class="token punctuation">.</span><span class="token function">getBeanClassName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">int</span> currentPriority <span class="token operator">=</span> <span class="token function">findPriorityForClass</span><span class="token punctuation">(</span>apcDefinition<span class="token punctuation">.</span><span class="token function">getBeanClassName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">int</span> requiredPriority <span class="token operator">=</span> <span class="token function">findPriorityForClass</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>currentPriority <span class="token operator"><</span> requiredPriority<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> apcDefinition<span class="token punctuation">.</span><span class="token function">setBeanClassName</span><span class="token punctuation">(</span>cls<span class="token punctuation">.</span><span class="token function">getName</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> null<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">/*创建AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinition */</span> RootBeanDefinition beanDefinition <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">RootBeanDefinition</span><span class="token punctuation">(</span>cls<span class="token punctuation">)</span><span class="token punctuation">;</span> beanDefinition<span class="token punctuation">.</span><span class="token function">setSource</span><span class="token punctuation">(</span>source<span class="token punctuation">)</span><span class="token punctuation">;</span> beanDefinition<span class="token punctuation">.</span><span class="token function">getPropertyValues</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">add</span><span class="token punctuation">(</span><span class="token string">"order"</span><span class="token punctuation">,</span> Ordered<span class="token punctuation">.</span>HIGHEST_PRECEDENCE<span class="token punctuation">)</span><span class="token punctuation">;</span> beanDefinition<span class="token punctuation">.</span><span class="token function">setRole</span><span class="token punctuation">(</span>BeanDefinition<span class="token punctuation">.</span>ROLE_INFRASTRUCTURE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/*注册AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinition*/</span> registry<span class="token punctuation">.</span><span class="token function">registerBeanDefinition</span><span class="token punctuation">(</span>AUTO_PROXY_CREATOR_BEAN_NAME<span class="token punctuation">,</span> beanDefinition<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> beanDefinition<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
到这一步,就可以看到创建AnnotationAwareAspectJAutoProxyCreator对应的BeanDefinition并且注册该beanDefinition的处理,然后AnnotationAwareAspectJAutoProxyCreator的BeanDefinition就交给了spring容器管理了
分析AnnotationAwareAspectJAutoProxyCreator主要行为
AnnotationAwareAspectJAutoProxyCreator是AbstractAutoProxyCreator的子孙类,而AbstractAutoProxyCreator又实现了接口SmartInstantiationAwareBeanPostProcessor,
SmartInstantiationAwareBeanPostProcessor接口继承了InstantiationAwareBeanPostProcessor,而InstantiationAwareBeanPostProcessor又间接继承了BeanPostProcessor,所以可以认为AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor,因此它是一个bean后置处理器
而bean后置处理器的方法会在bean进行实例化回调的这个阶段被执行
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessAfterInitialization
/**
* 如果bean被子类标识为要代理的bean,则使用配置的拦截器创建代理。
* 这个方法时重写BeanPostProcesser接口的方法
* 会在Bean完成初始化回调方法调用执行
* @see #getAdvicesAndAdvisorsForBean
*/
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
if (bean != null) {
Object cacheKey = getCacheKey(bean.getClass(), beanName);
/* 判断之前是否代理过 */
if (this.earlyProxyReferences.remove(cacheKey) != bean) {
/* 在bean初始化之后对生产出的bean进行包装 */
return wrapIfNecessary(bean, beanName, cacheKey);
}
}
return bean;
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
postProcessAfterInitialization方法会在进行完bean的初始化回调方法后被执行,这里判断之前是否代理过,如果不是,就调用wrapIfNecessary进行代理
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#wrapIfNecessary
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) { if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) { return bean; } if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) { return bean; } if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) { this.advisedBeans.put(cacheKey, Boolean.FALSE); return bean; }
<span class="token comment">// 如果该类有advice则创建proxy.</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> specificInterceptors <span class="token operator">=</span> <span class="token function">getAdvicesAndAdvisorsForBean</span><span class="token punctuation">(</span>bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>specificInterceptors <span class="token operator">!=</span> DO_NOT_PROXY<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> Boolean<span class="token punctuation">.</span>TRUE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 创建代理对象 */</span> Object proxy <span class="token operator">=</span> <span class="token function">createProxy</span><span class="token punctuation">(</span> bean<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> specificInterceptors<span class="token punctuation">,</span> <span class="token keyword">new</span> <span class="token class-name">SingletonTargetSource</span><span class="token punctuation">(</span>bean<span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">this</span><span class="token punctuation">.</span>proxyTypes<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> proxy<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">/* 返回代理对象 */</span> <span class="token keyword">return</span> proxy<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">this</span><span class="token punctuation">.</span>advisedBeans<span class="token punctuation">.</span><span class="token function">put</span><span class="token punctuation">(</span>cacheKey<span class="token punctuation">,</span> Boolean<span class="token punctuation">.</span>FALSE<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">return</span> bean<span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
所以可以这么理解,一旦我们开启了aop的注解,那么在spring中创建的每个bean,在进行bean后置处理器的postProcessAfterInitialization回调的时候,都会被拿去校验是否方法作为作为joinPoint被某个切面的pointCut所切中,是的话就要进行代理返回代理对象。
创建proxy过程分析
上面如果判断要进行代理对象的创建时,就会通过createProxy创建代理对象,把该bean重新包装为代理对象proxy
org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#createProxy
protected Object createProxy(Class<?> beanClass, @Nullable String beanName, @Nullable Object[] specificInterceptors, TargetSource targetSource) {
<span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory <span class="token keyword">instanceof</span> <span class="token class-name">ConfigurableListableBeanFactory</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> AutoProxyUtils<span class="token punctuation">.</span><span class="token function">exposeTargetClass</span><span class="token punctuation">(</span><span class="token punctuation">(</span>ConfigurableListableBeanFactory<span class="token punctuation">)</span> <span class="token keyword">this</span><span class="token punctuation">.</span>beanFactory<span class="token punctuation">,</span> beanName<span class="token punctuation">,</span> beanClass<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的</span> ProxyFactory proxyFactory <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ProxyFactory</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> proxyFactory<span class="token punctuation">.</span><span class="token function">copyFrom</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span>proxyFactory<span class="token punctuation">.</span><span class="token function">isProxyTargetClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">shouldProxyTargetClass</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">,</span> beanName<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> proxyFactory<span class="token punctuation">.</span><span class="token function">setProxyTargetClass</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token function">evaluateProxyInterfaces</span><span class="token punctuation">(</span>beanClass<span class="token punctuation">,</span> proxyFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token comment">// 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中</span> Advisor<span class="token punctuation">[</span><span class="token punctuation">]</span> advisors <span class="token operator">=</span> <span class="token function">buildAdvisors</span><span class="token punctuation">(</span>beanName<span class="token punctuation">,</span> specificInterceptors<span class="token punctuation">)</span><span class="token punctuation">;</span> proxyFactory<span class="token punctuation">.</span><span class="token function">addAdvisors</span><span class="token punctuation">(</span>advisors<span class="token punctuation">)</span><span class="token punctuation">;</span> proxyFactory<span class="token punctuation">.</span><span class="token function">setTargetSource</span><span class="token punctuation">(</span>targetSource<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token function">customizeProxyFactory</span><span class="token punctuation">(</span>proxyFactory<span class="token punctuation">)</span><span class="token punctuation">;</span> proxyFactory<span class="token punctuation">.</span><span class="token function">setFrozen</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>freezeProxy<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token function">advisorsPreFiltered</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> proxyFactory<span class="token punctuation">.</span><span class="token function">setPreFiltered</span><span class="token punctuation">(</span><span class="token boolean">true</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 3.调用getProxy获取bean对应的proxy</span> <span class="token keyword">return</span> proxyFactory<span class="token punctuation">.</span><span class="token function">getProxy</span><span class="token punctuation">(</span><span class="token function">getProxyClassLoader</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
上面方法主要分三步
- 创建proxyFactory,用于创建代理对象
- 将当前bean适合的advice封装为Advisor类,然后添加到ProxyFactory中
- 调用getProxy获取bean对应的proxy
org.springframework.aop.framework.ProxyFactory#getProxy(java.lang.ClassLoader)
public Object getProxy(@Nullable ClassLoader classLoader) {
return createAopProxy().getProxy(classLoader);
}
- 1
- 2
- 3
org.springframework.aop.framework.ProxyCreatorSupport#createAopProxy
/**
* Subclasses should call this to get a new AOP proxy. They should <b>not</b>
* create an AOP proxy with {@code this} as an argument.
*
* createAopProxy()方法就是决定究竟创建何种类型的proxy JDKProxy or CGLIBProxy
*/
protected final synchronized AopProxy createAopProxy() {
if (!this.active) {
activate();
}
// 关键方法createAopProxy()
return getAopProxyFactory().createAopProxy(this);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
createAopProxy就会进行创建代理类的工作,后面就会通过这个代理类获取代理对象
org.springframework.aop.framework.DefaultAopProxyFactory#createAopProxy
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
// config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
// config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
// hasNoUserSuppliedProxyInterfaces()是否存在代理接口
if (!IN_NATIVE_IMAGE &&
(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.");
}
if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
// 2.如果目标类是接口或者是代理类,则直接使用JDKproxy
return new JdkDynamicAopProxy(config);
}
// 3.其他情况则使用CGLIBproxy
return new ObjenesisCglibAopProxy(config);
}
else {
return new JdkDynamicAopProxy(config);
}
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
可以看到上面有两种情况
- 如果要代理的对象是接口或者实现了某接口,则是由jdk动态代理
- 普通类则使用CGLIB
我们看一下jdk动态代理的处理
JdkDynamicAopProxy.getProxy()方法
org.springframework.aop.framework.JdkDynamicAopProxy#getProxy(java.lang.ClassLoader)
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
if (logger.isTraceEnabled()) {
logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
}
Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
// JDK proxy 动态代理的标准用法
return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
可以看到上面是jdk动态代理获取代理对象的标准用法
然后JdkDynamicAopProxy本身实现了Jdk动态代理要求实现的InvocationHandler
接口,所有肯定有Invoke方法
final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
- 1
invoke()方法
以上的代码模式可以很明确的看出来,使用了JDK动态代理模式,真正的方法执行在invoke()方法里
org.springframework.aop.framework.JdkDynamicAopProxy#invoke
@Override @Nullable public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object oldProxy = null; boolean setProxyContext = false;
TargetSource targetSource <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>advised<span class="token punctuation">.</span>targetSource<span class="token punctuation">;</span> Object target <span class="token operator">=</span> null<span class="token punctuation">;</span> <span class="token keyword">try</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>equalsDefined <span class="token operator">&&</span> AopUtils<span class="token punctuation">.</span><span class="token function">isEqualsMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// The target does not implement the equals(Object) method itself.</span> <span class="token keyword">return</span> <span class="token function">equals</span><span class="token punctuation">(</span>args<span class="token punctuation">[</span><span class="token number">0</span><span class="token punctuation">]</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>hashCodeDefined <span class="token operator">&&</span> AopUtils<span class="token punctuation">.</span><span class="token function">isHashCodeMethod</span><span class="token punctuation">(</span>method<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// The target does not implement the hashCode() method itself.</span> <span class="token keyword">return</span> <span class="token function">hashCode</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">getDeclaringClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">==</span> DecoratingProxy<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// There is only getDecoratedClass() declared -> dispatch to proxy config.</span> <span class="token keyword">return</span> AopProxyUtils<span class="token punctuation">.</span><span class="token function">ultimateTargetClass</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>advised<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token operator">!</span><span class="token keyword">this</span><span class="token punctuation">.</span>advised<span class="token punctuation">.</span>opaque <span class="token operator">&&</span> method<span class="token punctuation">.</span><span class="token function">getDeclaringClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isInterface</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">&&</span> method<span class="token punctuation">.</span><span class="token function">getDeclaringClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">isAssignableFrom</span><span class="token punctuation">(</span>Advised<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Service invocations on ProxyConfig with the proxy config...</span> <span class="token keyword">return</span> AopUtils<span class="token punctuation">.</span><span class="token function">invokeJoinpointUsingReflection</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>advised<span class="token punctuation">,</span> method<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> Object retVal<span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>advised<span class="token punctuation">.</span>exposeProxy<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Make invocation available if necessary.</span> oldProxy <span class="token operator">=</span> AopContext<span class="token punctuation">.</span><span class="token function">setCurrentProxy</span><span class="token punctuation">(</span>proxy<span class="token punctuation">)</span><span class="token punctuation">;</span> setProxyContext <span class="token operator">=</span> <span class="token boolean">true</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Get as late as possible to minimize the time we "own" the target,</span> <span class="token comment">// in case it comes from a pool.</span> target <span class="token operator">=</span> targetSource<span class="token punctuation">.</span><span class="token function">getTarget</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> targetClass <span class="token operator">=</span> <span class="token punctuation">(</span>target <span class="token operator">!=</span> null <span class="token operator">?</span> target<span class="token punctuation">.</span><span class="token function">getClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span> <span class="token operator">:</span> null<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// 2.获取当前bean被拦截方法链表</span> List<span class="token generics function"><span class="token punctuation"><</span>Object<span class="token punctuation">></span></span> chain <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>advised<span class="token punctuation">.</span><span class="token function">getInterceptorsAndDynamicInterceptionAdvice</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> targetClass<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Check whether we have any advice. If we don't, we can fallback on direct</span> <span class="token comment">// reflective invocation of the target, and avoid creating a MethodInvocation.</span> <span class="token comment">// 3.如果为空,则直接调用target的method</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>chain<span class="token punctuation">.</span><span class="token function">isEmpty</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// We can skip creating a MethodInvocation: just invoke the target directly</span> <span class="token comment">// Note that the final invoker must be an InvokerInterceptor so we know it does</span> <span class="token comment">// nothing but a reflective operation on the target, and no hot swapping or fancy proxying.</span> Object<span class="token punctuation">[</span><span class="token punctuation">]</span> argsToUse <span class="token operator">=</span> AopProxyUtils<span class="token punctuation">.</span><span class="token function">adaptArgumentsIfNecessary</span><span class="token punctuation">(</span>method<span class="token punctuation">,</span> args<span class="token punctuation">)</span><span class="token punctuation">;</span> retVal <span class="token operator">=</span> AopUtils<span class="token punctuation">.</span><span class="token function">invokeJoinpointUsingReflection</span><span class="token punctuation">(</span>target<span class="token punctuation">,</span> method<span class="token punctuation">,</span> argsToUse<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// 4.不为空,则逐一调用chain中的每一个拦截方法的proceed</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// We need to create a method invocation...</span> MethodInvocation invocation <span class="token operator">=</span> <span class="token keyword">new</span> <span class="token class-name">ReflectiveMethodInvocation</span><span class="token punctuation">(</span>proxy<span class="token punctuation">,</span> target<span class="token punctuation">,</span> method<span class="token punctuation">,</span> args<span class="token punctuation">,</span> targetClass<span class="token punctuation">,</span> chain<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token comment">// Proceed to the joinpoint through the interceptor chain.</span> retVal <span class="token operator">=</span> invocation<span class="token punctuation">.</span><span class="token function">proceed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token comment">// Massage return value if necessary.</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> returnType <span class="token operator">=</span> method<span class="token punctuation">.</span><span class="token function">getReturnType</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>retVal <span class="token operator">!=</span> null <span class="token operator">&&</span> retVal <span class="token operator">==</span> target <span class="token operator">&&</span> returnType <span class="token operator">!=</span> Object<span class="token punctuation">.</span><span class="token keyword">class</span> <span class="token operator">&&</span> returnType<span class="token punctuation">.</span><span class="token function">isInstance</span><span class="token punctuation">(</span>proxy<span class="token punctuation">)</span> <span class="token operator">&&</span> <span class="token operator">!</span>RawTargetAccess<span class="token punctuation">.</span><span class="token keyword">class</span><span class="token punctuation">.</span><span class="token function">isAssignableFrom</span><span class="token punctuation">(</span>method<span class="token punctuation">.</span><span class="token function">getDeclaringClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Special case: it returned "this" and the return type of the method</span> <span class="token comment">// is type-compatible. Note that we can't help if the target sets</span> <span class="token comment">// a reference to itself in another returned object.</span> retVal <span class="token operator">=</span> proxy<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>retVal <span class="token operator">==</span> null <span class="token operator">&&</span> returnType <span class="token operator">!=</span> Void<span class="token punctuation">.</span>TYPE <span class="token operator">&&</span> returnType<span class="token punctuation">.</span><span class="token function">isPrimitive</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">throw</span> <span class="token keyword">new</span> <span class="token class-name">AopInvocationException</span><span class="token punctuation">(</span> <span class="token string">"Null return value from advice does not match primitive return type for: "</span> <span class="token operator">+</span> method<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">return</span> retVal<span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">finally</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">if</span> <span class="token punctuation">(</span>target <span class="token operator">!=</span> null <span class="token operator">&&</span> <span class="token operator">!</span>targetSource<span class="token punctuation">.</span><span class="token function">isStatic</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Must have come from TargetSource.</span> targetSource<span class="token punctuation">.</span><span class="token function">releaseTarget</span><span class="token punctuation">(</span>target<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>setProxyContext<span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Restore old proxy.</span> AopContext<span class="token punctuation">.</span><span class="token function">setCurrentProxy</span><span class="token punctuation">(</span>oldProxy<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
- 32
- 33
- 34
- 35
- 36
- 37
- 38
- 39
- 40
- 41
- 42
- 43
- 44
- 45
- 46
- 47
- 48
- 49
- 50
- 51
- 52
- 53
- 54
- 55
- 56
- 57
- 58
- 59
- 60
- 61
- 62
- 63
- 64
- 65
- 66
- 67
- 68
- 69
- 70
- 71
- 72
- 73
- 74
- 75
- 76
- 77
- 78
- 79
- 80
- 81
- 82
- 83
- 84
- 85
- 86
- 87
- 88
- 89
- 90
- 91
org.springframework.aop.framework.ReflectiveMethodInvocation#proceed
@Override @Nullable public Object proceed() throws Throwable { // We start with an index of -1 and increment early. if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) { return invokeJoinpoint(); }
Object interceptorOrInterceptionAdvice <span class="token operator">=</span> <span class="token keyword">this</span><span class="token punctuation">.</span>interceptorsAndDynamicMethodMatchers<span class="token punctuation">.</span><span class="token function">get</span><span class="token punctuation">(</span><span class="token operator">++</span><span class="token keyword">this</span><span class="token punctuation">.</span>currentInterceptorIndex<span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>interceptorOrInterceptionAdvice <span class="token keyword">instanceof</span> <span class="token class-name">InterceptorAndDynamicMethodMatcher</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Evaluate dynamic method matcher here: static part will already have</span> <span class="token comment">// been evaluated and found to match.</span> InterceptorAndDynamicMethodMatcher dm <span class="token operator">=</span> <span class="token punctuation">(</span>InterceptorAndDynamicMethodMatcher<span class="token punctuation">)</span> interceptorOrInterceptionAdvice<span class="token punctuation">;</span> Class<span class="token operator"><</span><span class="token operator">?</span><span class="token operator">></span> targetClass <span class="token operator">=</span> <span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>targetClass <span class="token operator">!=</span> null <span class="token operator">?</span> <span class="token keyword">this</span><span class="token punctuation">.</span>targetClass <span class="token operator">:</span> <span class="token keyword">this</span><span class="token punctuation">.</span>method<span class="token punctuation">.</span><span class="token function">getDeclaringClass</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token keyword">if</span> <span class="token punctuation">(</span>dm<span class="token punctuation">.</span>methodMatcher<span class="token punctuation">.</span><span class="token function">matches</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">.</span>method<span class="token punctuation">,</span> targetClass<span class="token punctuation">,</span> <span class="token keyword">this</span><span class="token punctuation">.</span>arguments<span class="token punctuation">)</span><span class="token punctuation">)</span> <span class="token punctuation">{<!-- --></span> <span class="token keyword">return</span> dm<span class="token punctuation">.</span>interceptor<span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// Dynamic matching failed.</span> <span class="token comment">// Skip this interceptor and invoke the next in the chain.</span> <span class="token keyword">return</span> <span class="token function">proceed</span><span class="token punctuation">(</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span> <span class="token keyword">else</span> <span class="token punctuation">{<!-- --></span> <span class="token comment">// It's an interceptor, so we just invoke it: The pointcut will have</span> <span class="token comment">// been evaluated statically before this object was constructed.</span> <span class="token keyword">return</span> <span class="token punctuation">(</span><span class="token punctuation">(</span>MethodInterceptor<span class="token punctuation">)</span> interceptorOrInterceptionAdvice<span class="token punctuation">)</span><span class="token punctuation">.</span><span class="token function">invoke</span><span class="token punctuation">(</span><span class="token keyword">this</span><span class="token punctuation">)</span><span class="token punctuation">;</span> <span class="token punctuation">}</span> <span class="token punctuation">}</span>
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 29
- 30
- 31
依次遍历拦截器链的每个元素,然后调用其实现,将真正调用工作委托给各个增强器
总结
所以当我们开启了Aop的注解以后,就会执行以下步骤
- AspectJAutoProxyBeanDefinitionParser 注册 AnnotationAwareAspectJAutoProxyCreator
- 再执行bean后置处理器的after方法回调时,判断该bean是否有advice
- 如果有则把bean包装为proxy对象返回
- 以后调用bean的方法时,通过proxy来调用,proxy依次调用增强器的相关方法,实现方法的切入