7.2 动态AOP自定义标签
public class AopNamespaceHandler extends NamespaceHandlerSupport {/*** Register the {@link BeanDefinitionParser BeanDefinitionParsers} for the* '{@code config}', '{@code spring-configured}', '{@code aspectj-autoproxy}'* and '{@code scoped-proxy}' tags.*/@Overridepublic void init() {// In 2.0 XSD as well as in 2.5+ XSDsregisterBeanDefinitionParser("config", new ConfigBeanDefinitionParser());registerBeanDefinitionParser("aspectj-autoproxy", new AspectJAutoProxyBeanDefinitionParser());registerBeanDefinitionDecorator("scoped-proxy", new ScopedProxyBeanDefinitionDecorator());// Only in 2.0 XSD: moved to context namespace in 2.5+registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser());}}
在解析配置文件的时候,一旦遇到aspectj-autoproxy注解时是使用解析器 AspectJAutoProxyBeanDefinitionParser进行解析。
7.2.1 注册AspectJAnnotationAutoProxyCreator
所有解析器,是对BeanDefinitionParser接口的统一实现,入口都是从parse函数开始的。
AspectJAutoProxyBeanDefinitionParserpublic BeanDefinition parse(Element element, ParserContext parserContext) {//注册 AspectJAnnotationAutoProxyCreatorAopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element);//对于注解中子类的处理extendBeanDefinition(element, parserContext);return null;}public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary(ParserContext parserContext, Element sourceElement) {BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext.getRegistry(), parserContext.extractSource(sourceElement));//对于proxy-target-class以及expose-proxy的属性的处理useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement);//注册并通知,便于监听器做进一步处理registerComponentIfNecessary(beanDefinition, parserContext);}private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, @Nullable Object source) {Assert.notNull(registry, "BeanDefinitionRegistry must not be null");//如果已经存在了自动代理创建器 且存在的自动代理创建器与现在的不一致,那么 需要根据优先级来判断到底需要使用哪个if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition apcDefinition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);if (!cls.getName().equals(apcDefinition.getBeanClassName())) {int currentPriority = findPriorityForClass(apcDefinition.getBeanClassName());int requiredPriority = findPriorityForClass(cls);if (currentPriority < requiredPriority) {//改变bean最重要的就是改变bean所对应的className属性apcDefinition.setBeanClassName(cls.getName());}}//如果已经存在自动代理创建器与将要创建的一致,那么无须再次创建。return null;}RootBeanDefinition beanDefinition = new RootBeanDefinition(cls);beanDefinition.setSource(source);beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE);beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition);return beanDefinition;}
2 处理proxy-target-class以及 expose-proxy 属性
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, @Nullable Element sourceElement) {if (sourceElement != null) {//对于 proxy-target-class 的处理boolean proxyTargetClass = Boolean.parseBoolean(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE));if (proxyTargetClass) {AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);}//对于 expose-proxy 的处理boolean exposeProxy = Boolean.parseBoolean(sourceElement.getAttribute(EXPOSE_PROXY_ATTRIBUTE));if (exposeProxy) {AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);}}}public static void forceAutoProxyCreatorToUseClassProxying(BeanDefinitionRegistry registry) {if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);definition.getPropertyValues().add("proxyTargetClass", Boolean.TRUE);}}public static void forceAutoProxyCreatorToExposeProxy(BeanDefinitionRegistry registry) {if (registry.containsBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME)) {BeanDefinition definition = registry.getBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME);definition.getPropertyValues().add("exposeProxy", Boolean.TRUE);}}
- proxy-target-class :Spring AOP部分部分使用JDK动态代理或者CGLIB来为目标对象创建代理(建议尽量使用JDK动态代理)。如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。若该目标对象没有实现任何接口,则创建一个CGLIB代理。如果需要强制使用CGLIB,需要考虑以下两个问题
- 无法通知final方法,因为他们不能重写
- 需要将cglib二进制发行包放在classpath下面。
与之相比,JDK本身提供了动态代理,强制使用CGLIB代理需要将proxy-target-class设置为true。
而在实际使用的过程中你会发现细节问题的差别。
- JDK动态代理:其代理对象必须是某个接口的实现,他是通过在运行期间创建一个接口的实现类来完成对目标对象的代理。
- CGLIB代理:其原理类似于JDK动态代理,只是它在运行期间代理对象是针对目标类扩展的子类,CGLIB是高效的代码生成包,底层是依靠ASM(开源的java字节码编辑类库)操作字节码实现的,性能比JDK强。
- expose-proxy:有时候目标对象内部的自我调用无法实施切面中的增强。
例如Transactional注解中的问题,AopContext.currentProxy()
public void hello() {System.out.println("hello");TestBean self = (TestBean) AopContext.currentProxy(); // 获取当前代理self.hi();}public void hi() {System.out.println("hi");}
7.3 创建AOP代理
AnnotationAwareAspectJAutoProxyCreator的类层次结构如下
AnnotationAwareAspectJAutoProxyCreator实现了BeanPostProcessor接口,而实现BeanPostProcessor后,当Spring加载这个Bean会在实例化前调用其postProcessAfterInitialization方法,而我们的AOP逻辑分析也由此开始。
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {//根据给定的bean的class和name出一个key,格式:beanClassName_beanNameObject cacheKey = getCacheKey(bean.getClass(), beanName);if (this.earlyProxyReferences.remove(cacheKey) != bean) {//如果它适合被代理,则需要封装指定beanreturn wrapIfNecessary(bean, beanName, cacheKey);}}return bean;}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;}//给定的bean类是否代表一个基础设施类,基础设施类不应代理,或者配置了指定bean需要自动代理if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}//如果存在增强方法则创建代理Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);if (specificInterceptors != DO_NOT_PROXY) {this.advisedBeans.put(cacheKey, Boolean.TRUE);//创建代理Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;}this.advisedBeans.put(cacheKey, Boolean.FALSE);return bean;}
