我们知道,使用面积对象编程(OOP) 有一些弊端,当需要为多个不具有继承关系的对象引入同一个公共的行为时,例如日志,安全检测等,我们只有在每个对象引用公共的行为,这样程序中能产生大量的重复代码,程序就不便于维护了,所以就有了一个对面向对象编程的补充,即面向方面编程(AOP),AOP所关注的就是方向是横向的,不同于 OOP 的纵向。
Spring 中提供了 AOP 的实现,但是低版本 Spring中定义一个切面是比较麻烦的,需要实现特定的接口,并进行一些较为复杂的配置,低版本Spring AOP的配置是被批评最多的地方,Spring 听取了这一方法的批评声音,并下定决心彻底改变这一现状,在 Spring 2.0中,Spring AOP己经焕然一新,你可以使用@AspectJ注解非常容易 的定义一个切面,不需要实现任何的接口。
Spring 2.0采用@AspectJ 注解对 POJO 进行标注,从而定义一个包含切点信息和增强横切逻辑 的切面,Spring 2.0可以将这个切面织入到匹配的目标 Bean中,@AspectJ 注解使用 AspectJ 切点表达式语法进行切点定义,可以通过切点函数,运算符,通配符等高级功能进行切点定义,拥有强大的连接点的能力,我们先来直观的浏览一下 Spring AOP 的功能吧。
1.创建用于拦截的 bean
在实际工作中,此 bean可能是满足业务需要的核心逻辑,例如test 方法中可能会封装着某个核心业务,但是如果我们想在 test 前后加入日志来跟踪调试,如果直接修改源码并不符合面向对象的设计方法,而且随意改动原来代码会造成一定的风险,还好接下来的 Spring帮我们做到了这一点。
@Service public class TestBean { public void test() { System.out.println("test"); } }
2.创建 Advisor
Spring 中摒弃了最繁杂的配置方式而采用了@AspectJ 注解对 POJO 进行标注,使得AOP 的工作大大简化,例如,在 AspectJTest 类中,我们要做的就是在所有的类中的 test 方法执行前打印beforeTest,而在所有的类的 test 方法执行后打印 afterTest,同时又使用环绕的方式在所有的类中的方法执行前后再分别打印出 before1和 after1。
@Aspect @Configuration public class AspectJTest { @Pointcut("execution(* com.spring_1_100.test_61_70.test64.*.*(..))") public void test() { } @Before("test()") public void beforeTest() { System.out.println("beforeTest"); } @After("test()") public void afterTest() { System.out.println("afterTest()"); } @Around("test()") public Object aroundTest(ProceedingJoinPoint p) { System.out.println("before1"); Object o = null; try { o = p.proceed(); } catch (Throwable throwable) { throwable.printStackTrace(); } System.out.println("after1"); return o; } }
3.创建配置文件
xml 是 Spring 的基础,尽管 Spring 一再简化配置,并且大有使用注解取代 xml 配置之势,但是无论如何,至少现在的XML 还是 Spring 的基础,要在 Spring中开启 AOP 的功能还需要在配置文件中声明的。
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:aspectj-autoproxy ></aop:aspectj-autoproxy> <context:component-scan base-package="com.spring_1_100.test_61_70.test64"></context:component-scan> </beans>
测试:
public static void main(String[] args) { ApplicationContext ac = new ClassPathXmlApplicationContext("classpath:spring_1_100/config_61_70/spring64.xml"); TestBean testBean = ac.getBean(TestBean.class); testBean.test(); }
结果输出:
Spring 实现了对所有的类的 test 方法进行增强,使辅助功能可以独立于核心业务之外,方便程序的扩展和解耦。
那么 Spring 究竟是如何实现 AOP 的呢?首先,我们知道,Spring 是否支持注解的 AOP 是由一个配置文件控制,也就是<aop:aspectj-autoproxy/>,当在配置文件中声明了这句配置的时候,Spring 就会支持注解 AOP,那么我们的分析就从这句开始。由于基于注解的 AOP 的整个实现过程非常的复杂,今天我们不分析多的,只分析配置文件的加载和 BeanDefinition 的创建过程。
DefaultBeanDefinitionDocumentReader.java
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { // Bean 的定义的文档对象使用了Spring默认的命名空间 if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); //获取 bean 定义的文档对象根元素的所有子节点 for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { //获取文档节点的 XML 的节点 Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { //开始默认解析,如 bean,beans标签 parseDefaultElement(ele, delegate); } else { //对自定义的命名空间进行解析,如aspectj-autoproxy,config 等 delegate.parseCustomElement(ele); } } } } else { //使用自定义解析规则解析文档的根节点,如 myuser delegate.parseCustomElement(root); } }
BeanDefinitionParserDelegate.java
public BeanDefinition parseCustomElement(Element ele) { return parseCustomElement(ele, null); }
public BeanDefinition parseCustomElement(Element ele, BeanDefinition containingBd) { //根据命名空间获取 aspectj-autoproxy标签的uri ,http://www.springframework.org/schema/aop String namespaceUri = getNamespaceURI(ele); //根据uri从当前classpath*中获取到AopNamespaceHandler处理器 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler == null) { error("Unable to locate Spring NamespaceHandler for XML schema namespace [" + namespaceUri + "]", ele); return null; } return handler.parse(ele, new ParserContext(this.readerContext, this, containingBd)); }
首先对于aspectj-autoproxy 标签的解析,aspectj-autoproxy标签对应的名称空间是http://www.springframework.org/schema/aop,根据 uri从当前 classpath*获取到处理器AopNamespaceHandler,再调用处理器init()方法,初始化aspectj-autoproxy标签对应的解析器AspectJAutoProxyBeanDefinitionParser
NamespaceHandlerSupport.java
public BeanDefinition parse(Element element, ParserContext parserContext) { //获取到aspectj-autoproxy的解析器AspectJAutoProxyBeanDefinitionParser return findParserForElement(element, parserContext).parse(element, parserContext); }
aspectj-autoproxy 是由AspectJAutoProxyBeanDefinitionParser来解析的,因此,进入AspectJAutoProxyBeanDefinitionParser类。
AspectJAutoProxyBeanDefinitionParser.java
public BeanDefinition parse(Element element, ParserContext parserContext) { //注册AnnotationAwareAspectJAutoProxyCreator AopNamespaceUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(parserContext, element); //对注解中的子类的处理 extendBeanDefinition(element, parserContext); return null; }
AopNamespaceUtils.java
public static void registerAspectJAnnotationAutoProxyCreatorIfNecessary( ParserContext parserContext, Element sourceElement) { //注册或升级 AutoProxyCreator定义beanName 为 org.springframework.aop.config.internalAutoProxyCreator的 BeanDefinition BeanDefinition beanDefinition = AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary( parserContext.getRegistry(), parserContext.extractSource(sourceElement)); //对于 proxy-target-class 以及 expose-proxy 属性的处理 useClassProxyingIfNecessary(parserContext.getRegistry(), sourceElement); //注册组件通知,便于监听器进一步处理,其中 beanDefinition的 className 为 AnnotationAwareAspectJAutoProxyCreator registerComponentIfNecessary(beanDefinition, parserContext); }
在registerAspectJAnnotationAutoProxyCreatorIfNecessar方法中主要完成3件事情,基本上每一行代码都是一个完整的逻辑。
1.注册或者升级 AnnotationAwareAspectJAutoProxyCreator
对于 AOP 的实现,基本上都是靠 AnnotationAwareAspectJAutoProxyCreator去完成,它可以根据@Pointcut 注解定义的切点来自动代理相匹配的 bean,但是为了配置简便,Spring 使用了自定义配置来帮助我们自动注册 AnnotationAwareAspectJAutoProxyCreator,其注册过程就是在这里实现的。
AopConfigUtils.java
public static BeanDefinition registerAspectJAnnotationAutoProxyCreatorIfNecessary(BeanDefinitionRegistry registry, Object source) { return registerOrEscalateApcAsRequired(AnnotationAwareAspectJAutoProxyCreator.class, registry, source); } private static BeanDefinition registerOrEscalateApcAsRequired(Class<?> cls, BeanDefinitionRegistry registry, Object source) { Assert.notNull(registry, "BeanDefinitionRegistry must not be null"); //如果己经存在了自动代理创建器且存在自动代理创建器与现在的不一致,那么需要根据优先级来判断到底需要使用哪个 //AUTO_PROXY_CREATOR_BEAN_NAME ="org.springframework.aop.config.internalAutoProxyCreator"; 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); //添加order属性 beanDefinition.getPropertyValues().add("order", Ordered.HIGHEST_PRECEDENCE); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册 org.springframework.aop.config.internalAutoProxyCreator的beanDefinition registry.registerBeanDefinition(AUTO_PROXY_CREATOR_BEAN_NAME, beanDefinition); return beanDefinition; }
以上代码中实现了 AnnotationAwareAspectJAutoProxyCreator 类的功能,同时这里还涉及一个优先级的问题,如果己经存在了自动代理创建器,而且存在的自动代理创建器与现在的不一致,那么需要根据优先级来判断到底需要使用哪个。
2.处理 proxy-target-class以及 expose-proxy 属性
useClassProxyingIfNecessary()方法实现了proxy-target-class 属性以及 expose-proxy 属性的处理
AopNamespaceUtils.java
private static void useClassProxyingIfNecessary(BeanDefinitionRegistry registry, Element sourceElement) { if (sourceElement != null) { // 如果用户配置了 proxyTargetClass 属性 boolean proxyTargetClass = Boolean.valueOf(sourceElement.getAttribute(PROXY_TARGET_CLASS_ATTRIBUTE)); // 添加 proxyTargetClass 为true的属性到propertyValues中 if (proxyTargetClass) { AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry); } // 添加 exposeProxy 为true的属性到propertyValues中 boolean exposeProxy = Boolean.valueOf(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); } } 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 代理,例如希望代理目标对象所有的方法,而不只是实现自接口的方法,那也可以,但是需要考虑以下两个问题。
a)无法通知(advise)Final 方法,因为它们不能被覆写。
b)你需要将 CGLIB 二进制发行包放在 classpath下面。
与之相比,jdk 本身就提供了动态代理,强制使用 CGLIB 代理需要将aop:config的 proxy-target-class 属性设置为 true 。
<aop:config proxy-target-class=“true”/> …</aop-config>
当需要使用 CGLIB 代理和@AspectJ 自动代理支持,可以按照以下方式设置<aop:aspectj-autoproxy>的 proxy-target-class 属性。
<aop:aspectj-autoproxy proxy-target-class=“true”/>
而实现使用的过程中才会发现细节问题的差别,The devial is in the details。
- JDK动态代理,其代理对象必需是某个接口的实现,它是通过在运行期间创建一个接口的实现来完成对目标对象的代理 。
- CGLIB 代理 :实现原理类似于 JDK 动态代理,只是它在运行期间生成的代理对象是针对目标类扩展的子类,CGLIB 代理是高效的代码生成包,底层是依靠 ASM(开源的 Java 字节码编辑类库)操作字节码实现的,性能比 JDK 强。
- expose-proxy : 有时候目标对象内部自我调用将无法实施切面增强,如下示例:
public interface AService{
public void a ();
public void b ();
}
@Service
public class AServiceImpl implement AService{
@Transactionl(Propagation=Propagation.REQUIRED)
public void a (){
this.b();
}
@Transaction(propagation=Propagation.RQEUIRES_NEW)
public void b (){
}
}
此处的 this 指向目标对象,因此调用 this.b()将不会被执行的 b 事务切面,即不会执行事务增强,因此 b 方法的事务定义@Transaction(propagation=Propagation.RQEUIRES_NEW)将不会实施,为了解决这个问题,我们可以这样做。
<aop:aspectj-autoproxy expose-proxy=“true”/>
然后将以上的代码中的 this.b() 修改为((AService)AopContext.currentProxy()).b() 即可,通过以上的修改便可以完成对对 a 和 b方法同时实现增强。
最后注册组件并通知,便于监听器做进一步的处理,这里就不再一一赘述了。
程序执行到这里己经将aspectj-autoproxy 标签解析完成,下面来看关于切面的其他的标签该如何解析。
component-scan标签获取Handler 和aspectj-autoproxy 类似,下面直接来分析component-scan标签对应的ContextNamespaceHandler。
ContextNamespaceHandler.java
public class ContextNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); } }
因此,是调用ComponentScanBeanDefinitionParser的parse方法来解析BeanDefinition的。
ComponentScanBeanDefinitionParser.java
public BeanDefinition parse(Element element, ParserContext parserContext) { String basePackage = element.getAttribute(BASE_PACKAGE_ATTRIBUTE); basePackage = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(basePackage); String[] basePackages = StringUtils.tokenizeToStringArray(basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS); // 扫描BeanDefinition并注册他们 ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element); Set beanDefinitions = scanner.doScan(basePackages); registerComponents(parserContext.getReaderContext(), beanDefinitions, element); return null; }
ClassPathBeanDefinitionScanner.java
protected Set doScan(String... basePackages) { Assert.notEmpty(basePackages, "At least one base package must be specified"); //创建bean定义的holder对象用于保存扫描后生成的bean定义对象 // 创建一个集合,存入扫描到的Bean 定义的封装类 Set beanDefinitions = new LinkedHashSet(); //循环我们的包路径集合 // 遍历扫描所给定的包 for (String basePackage : basePackages) { // 找到候选的 @Component // 调用父类的 ClassPathScanningCandidateComponentProvider 的 findCandidateComponents 方法 // 扫描给定类路径的,获取符合条件的Bean的定义 // 类路径的Bean定义扫描 ClassPathBeanDefinitionScanner 主要通过 findCandidateComponents() 方法调用其父类 ClassPathScanningCandidateComponentProvider // 来扫描获取给定包及其子包的类 Set candidates = findCandidateComponents(basePackage); // 遍历扫描得到的Bean for (BeanDefinition candidate : candidates) { // 获取Bean定义类中的@Scope注解的值,即获取Bean的作用域 ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate); // 为Bean设置注解配置的作用域 candidate.setScope(scopeMetadata.getScopeName()); //设置我们的beanName,为Bean生成名称 String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry); // 处理@AutoWired相关的 // 如果扫描到Bean不是Spring的注解Bean,则为Bean设置默认值 // 设置Bean的自动依赖注入装配属性等 if (candidate instanceof AbstractBeanDefinition) { postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName); } //处理jsr250相关的组件,如果扫描到的Bean是Spring的注解的Bean,则处理其通用的注解 if (candidate instanceof AnnotatedBeanDefinition) { // 处理注解Bean中通过的注解,在分析注解Bean定义类读取器时已经分析过了 AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate); } //把我们解析出来的组件bean定义注册到Spring IoC容器中,根据Bean名称检查指定的Bean是否需要在容器注册,或者是否是容器中 // 有冲突。 if (checkCandidate(beanName, candidate)) { BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName); // 根据注解中的配置的作用域,为Bean的应用的代理模式 definitionHolder = AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry); beanDefinitions.add(definitionHolder); //注册到Spring IoC容器中,向容器注册扫描到的Bean registerBeanDefinition(definitionHolder, this.registry); } } } return beanDefinitions; }
findCandidateComponents这个方法,在之前的博客中也解析过,只不过今天这个例子中更加复杂,因此再重复解析一遍。
ClassPathScanningCandidateComponentProvider.java
public Set<BeanDefinition> findCandidateComponents(String basePackage) { Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>(); try { // classpath*:com/spring_1_100/test_61_70/test64/**/*.class 获取包下所有的class文件 String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + resolveBasePackage(basePackage) + "/" + this.resourcePattern; Resource[] resources = this.resourcePatternResolver.getResources(packageSearchPath); boolean traceEnabled = logger.isTraceEnabled(); boolean debugEnabled = logger.isDebugEnabled(); for (Resource resource : resources) { if (traceEnabled) { logger.trace("Scanning " + resource); } if (resource.isReadable()) { try { //获取注解相关的元数据 MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource); if (isCandidateComponent(metadataReader)) { ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader); sbd.setResource(resource); sbd.setSource(resource); if (isCandidateComponent(sbd)) { if (debugEnabled) { logger.debug("Identified candidate component class: " + resource); } candidates.add(sbd); } else { if (debugEnabled) {` logger.debug("Ignored because not a concrete top-level class: " + resource); } } } else { if (traceEnabled) { logger.trace("Ignored because not matching any filter: " + resource); } } } catch (Throwable ex) { throw new BeanDefinitionStoreException( "Failed to read candidate component class: " + resource, ex); } } else { if (traceEnabled) { logger.trace("Ignored because not readable: " + resource); } } } } catch (IOException ex) { throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex); } return candidates; }
CachingMetadataReaderFactory.java
public MetadataReader getMetadataReader(Resource resource) throws IOException { //如果禁用缓存 if (getCacheLimit() <= 0) { return super.getMetadataReader(resource); } synchronized (this.metadataReaderCache) { MetadataReader metadataReader = this.metadataReaderCache.get(resource); if (metadataReader == null) { // 获取类的元数据 metadataReader = super.getMetadataReader(resource); this.metadataReaderCache.put(resource, metadataReader); } return metadataReader; } }
SimpleMetadataReaderFactory.java
public MetadataReader getMetadataReader(Resource resource) throws IOException { return new SimpleMetadataReader(resource, this.resourceLoader.getClassLoader()); }
SimpleMetadataReader.java
SimpleMetadataReader(Resource resource, ClassLoader classLoader) throws IOException { InputStream is = new BufferedInputStream(resource.getInputStream()); ClassReader classReader; try { //初始化常量池 classReader = new ClassReader(is); } catch (IllegalArgumentException ex) { throw new NestedIOException("ASM ClassReader failed to parse class file - " + "probably due to a new Java class file version that isn't supported yet: " + resource, ex); } finally { is.close(); } AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(classLoader); classReader.accept(visitor, ClassReader.SKIP_DEBUG); this.annotationMetadata = visitor; this.classMetadata = visitor; this.resource = resource; }
public void accept(final ClassVisitor classVisitor, final Attribute[] attrs, final int flags) { int u = header; // current offset in the class file char[] c = new char[maxStringLength]; // buffer used to read strings Context context = new Context(); context.attrs = attrs; context.flags = flags; context.buffer = c; // reads the class declaration int access = readUnsignedShort(u); String name = readClass(u + 2, c); String superClass = readClass(u + 4, c); String[] interfaces = new String[readUnsignedShort(u + 6)]; u += 8; for (int i = 0; i < interfaces.length; ++i) { interfaces[i] = readClass(u, c); u += 2; } // reads the class attributes String signature = null; String sourceFile = null; String sourceDebug = null; String enclosingOwner = null; String enclosingName = null; String enclosingDesc = null; int anns = 0; int ianns = 0; int tanns = 0; int itanns = 0; int innerClasses = 0; Attribute attributes = null; u = getAttributes(); for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); // tests are sorted in decreasing frequency order // (based on frequencies observed on typical classes) if ("SourceFile".equals(attrName)) { sourceFile = readUTF8(u + 8, c); } else if ("InnerClasses".equals(attrName)) { innerClasses = u + 8; } else if ("EnclosingMethod".equals(attrName)) { enclosingOwner = readClass(u + 8, c); int item = readUnsignedShort(u + 10); if (item != 0) { enclosingName = readUTF8(items[item], c); enclosingDesc = readUTF8(items[item] + 2, c); } } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = u + 8; } else if ("Deprecated".equals(attrName)) { access |= Opcodes.ACC_DEPRECATED; } else if ("Synthetic".equals(attrName)) { access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if ("SourceDebugExtension".equals(attrName)) { int len = readInt(u + 4); sourceDebug = readUTF(u + 8, len, new char[len]); } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = u + 8; } else if ("BootstrapMethods".equals(attrName)) { int[] bootstrapMethods = new int[readUnsignedShort(u + 8)]; for (int j = 0, v = u + 10; j < bootstrapMethods.length; j++) { bootstrapMethods[j] = v; v += 2 + readUnsignedShort(v + 2) << 1; } context.bootstrapMethods = bootstrapMethods; } else { Attribute attr = readAttribute(attrs, attrName, u + 8, readInt(u + 4), c, -1, null); if (attr != null) { attr.next = attributes; attributes = attr; } } u += 6 + readInt(u + 4); } // visits the class declaration classVisitor.visit(readInt(items[1] - 7), access, name, signature, superClass, interfaces); // visits the source and debug info if ((flags & SKIP_DEBUG) == 0 && (sourceFile != null || sourceDebug != null)) { classVisitor.visitSource(sourceFile, sourceDebug); } if (enclosingOwner != null) { classVisitor.visitOuterClass(enclosingOwner, enclosingName, enclosingDesc); } if (ANNOTATIONS && anns != 0) { //访问类中的所有注解,i 为类中的所有注解个数 for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), true)); } } if (ANNOTATIONS && ianns != 0) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, classVisitor.visitAnnotation(readUTF8(v, c), false)); } } if (ANNOTATIONS && tanns != 0) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, classVisitor.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } } if (ANNOTATIONS && itanns != 0) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, classVisitor.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } } // visits the attributes while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; classVisitor.visitAttribute(attributes); attributes = attr; } // visits the inner classes if (innerClasses != 0) { int v = innerClasses + 2; for (int i = readUnsignedShort(innerClasses); i > 0; --i) { classVisitor.visitInnerClass(readClass(v, c), readClass(v + 2, c), readUTF8(v + 4, c), readUnsignedShort(v + 6)); v += 8; } } // visits the fields and methods u = header + 10 + 2 * interfaces.length; for (int i = readUnsignedShort(u - 2); i > 0; --i) { u = readField(classVisitor, context, u); } u += 2 //i 类中的方法个数,读取类中的所有的方法 for (int i = readUnsignedShort(u - 2); i > 0; --i) { u = readMethod(classVisitor, context, u); } // visits the end of the class classVisitor.visitEnd(); }
从下图中得到,从字节码中解析出类的注解有两个
ClassReader.java
private int readMethod(final ClassVisitor classVisitor, final Context context, int u) { char[] c = context.buffer; context.access = readUnsignedShort(u); context.name = readUTF8(u + 2, c); context.desc = readUTF8(u + 4, c); u += 6; // reads the method attributes int code = 0; int exception = 0; String[] exceptions = null; String signature = null; int methodParameters = 0; int anns = 0; int ianns = 0; int tanns = 0; int itanns = 0; int dann = 0; int mpanns = 0; int impanns = 0; int firstAttribute = u; Attribute attributes = null; for (int i = readUnsignedShort(u); i > 0; --i) { String attrName = readUTF8(u + 2, c); // tests are sorted in decreasing frequency order // (based on frequencies observed on typical classes) if ("Code".equals(attrName)) { if ((context.flags & SKIP_CODE) == 0) { code = u + 8; } } else if ("Exceptions".equals(attrName)) { exceptions = new String[readUnsignedShort(u + 8)]; exception = u + 10; for (int j = 0; j < exceptions.length; ++j) { exceptions[j] = readClass(exception, c); exception += 2; } } else if (SIGNATURES && "Signature".equals(attrName)) { signature = readUTF8(u + 8, c); } else if ("Deprecated".equals(attrName)) { context.access |= Opcodes.ACC_DEPRECATED; } else if (ANNOTATIONS && "RuntimeVisibleAnnotations".equals(attrName)) { anns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleTypeAnnotations".equals(attrName)) { tanns = u + 8; } else if (ANNOTATIONS && "AnnotationDefault".equals(attrName)) { dann = u + 8; } else if ("Synthetic".equals(attrName)) { context.access |= Opcodes.ACC_SYNTHETIC | ClassWriter.ACC_SYNTHETIC_ATTRIBUTE; } else if (ANNOTATIONS && "RuntimeInvisibleAnnotations".equals(attrName)) { ianns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleTypeAnnotations".equals(attrName)) { itanns = u + 8; } else if (ANNOTATIONS && "RuntimeVisibleParameterAnnotations".equals(attrName)) { mpanns = u + 8; } else if (ANNOTATIONS && "RuntimeInvisibleParameterAnnotations".equals(attrName)) { impanns = u + 8; } else if ("MethodParameters".equals(attrName)) { methodParameters = u + 8; } else { Attribute attr = readAttribute(context.attrs, attrName, u + 8, readInt(u + 4), c, -1, null); if (attr != null) { attr.next = attributes; attributes = attr; } } u += 6 + readInt(u + 4); } u += 2; // visits the method declaration MethodVisitor mv = classVisitor.visitMethod(context.access, context.name, context.desc, signature, exceptions); if (mv == null) { return u; } if (WRITER && mv instanceof MethodWriter) { MethodWriter mw = (MethodWriter) mv; if (mw.cw.cr == this && (signature != null ? signature.equals(mw.signature) : mw.signature == null)) { boolean sameExceptions = false; if (exceptions == null) { sameExceptions = mw.exceptionCount == 0; } else if (exceptions.length == mw.exceptionCount) { sameExceptions = true; for (int j = exceptions.length - 1; j >= 0; --j) { exception -= 2; if (mw.exceptions[j] != readUnsignedShort(exception)) { sameExceptions = false; break; } } } if (sameExceptions) { mw.classReaderOffset = firstAttribute; mw.classReaderLength = u - firstAttribute; return u; } } } // visit the method parameters if (methodParameters != 0) { for (int i = b[methodParameters] & 0xFF, v = methodParameters + 1; i > 0; --i, v = v + 4) { mv.visitParameter(readUTF8(v, c), readUnsignedShort(v + 2)); } } // visits the method annotations if (ANNOTATIONS && dann != 0) { AnnotationVisitor dv = mv.visitAnnotationDefault(); readAnnotationValue(dann, c, null, dv); if (dv != null) { dv.visitEnd(); } } if (ANNOTATIONS && anns != 0) { for (int i = readUnsignedShort(anns), v = anns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, mv.visitAnnotation(readUTF8(v, c), true)); } } if (ANNOTATIONS && ianns != 0) { for (int i = readUnsignedShort(ianns), v = ianns + 2; i > 0; --i) { v = readAnnotationValues(v + 2, c, true, mv.visitAnnotation(readUTF8(v, c), false)); } } if (ANNOTATIONS && tanns != 0) { for (int i = readUnsignedShort(tanns), v = tanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, mv.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), true)); } } if (ANNOTATIONS && itanns != 0) { for (int i = readUnsignedShort(itanns), v = itanns + 2; i > 0; --i) { v = readAnnotationTarget(context, v); v = readAnnotationValues(v + 2, c, true, mv.visitTypeAnnotation(context.typeRef, context.typePath, readUTF8(v, c), false)); } } if (ANNOTATIONS && mpanns != 0) { readParameterAnnotations(mv, context, mpanns, true); } if (ANNOTATIONS && impanns != 0) { readParameterAnnotations(mv, context, impanns, false); } // visits the method attributes while (attributes != null) { Attribute attr = attributes.next; attributes.next = null; mv.visitAttribute(attributes); attributes = attr; } // visits the method code if (code != 0) { mv.visitCode(); readCode(mv, context, code); } // visits the end of the method mv.visitEnd(); return u; }
AnnotationMetadataReadingVisitor.java
public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { // Skip bridge methods - we're only interested in original annotation-defining user methods. // On JDK 8, we'd otherwise run into double detection of the same annotated method... if ((access & Opcodes.ACC_BRIDGE) != 0) { return super.visitMethod(access, name, desc, signature, exceptions); } //创建一个方法访问器 return new MethodMetadataReadingVisitor(name, access, getClassName(), Type.getReturnType(desc).getClassName(), this.classLoader, this.methodMetadataSet); }
MethodMetadataReadingVisitor.java
public MethodMetadataReadingVisitor(String methodName, int access, String declaringClassName, String returnTypeName, ClassLoader classLoader, Set methodMetadataSet) { super(SpringAsmInfo.ASM_VERSION); this.methodName = methodName; this.access = access; this.declaringClassName = declaringClassName; this.returnTypeName = returnTypeName; this.classLoader = classLoader; //每一次将类的methodMetadataSet保存到当前方法访问器的methodMetadataSet属性中 this.methodMetadataSet = methodMetadataSet; } public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { String className = Type.getType(desc).getClassName(); //将当前方法的MethodMetadataReadingVisitor属性对象保存到类的methodMetadataSet属性中 this.methodMetadataSet.add(this); return new AnnotationAttributesReadingVisitor( className, this.attributesMap, this.metaAnnotationMap, this.classLoader); }
AnnotationMetadataReadingVisitor.java
public AnnotationVisitor visitAnnotation(final String desc, boolean visible) { //将从常量池中获取到的字符串转化成Spring对应的注解名称 String className = Type.getType(desc).getClassName(); //将类的注解名称加入到annotationSet中 this.annotationSet.add(className); return new AnnotationAttributesReadingVisitor( className, this.attributesMap, this.metaAnnotationMap, this.classLoader); }
ClassReader.java
private int readAnnotationValues(int v, final char[] buf, final boolean named, final AnnotationVisitor av) { //从字节码中读取类注解上配置的属性及值保存到attributes中 // @Service("userService") ,会将那么注解@Service上配置的参数个数为1 int i = readUnsignedShort(v); v += 2; if (named) { for (; i > 0; --i) { // 并且将常量池中的"userService"保存到this对象的attributes属性中 v = readAnnotationValue(v + 2, buf, readUTF8(v, buf), av); } } else { for (; i > 0; --i) { v = readAnnotationValue(v, buf, null, av); } } if (av != null) { // 保存类当前注解及祖先注解默认属性 av.visitEnd(); } return v; }
RecursiveAnnotationAttributesVisitor.java
public final void visitEnd() { try { Class<?> annotationClass = this.classLoader.loadClass(this.annotationType); doVisitEnd(annotationClass); } catch (ClassNotFoundException ex) { logger.debug("Failed to class-load type while reading annotation metadata. " + "This is a non-fatal error, but certain annotation metadata may be unavailable.", ex); } }
AnnotationAttributesReadingVisitor.java
public void doVisitEnd(Class<?> annotationClass) { //当前注解的所有的默认值属性保存到attibutes中 super.doVisitEnd(annotationClass); List<AnnotationAttributes> attributes = this.attributesMap.get(this.annotationType); if (attributes == null) { this.attributesMap.add(this.annotationType, this.attributes); } else { attributes.add(0, this.attributes); } Set<String> metaAnnotationTypeNames = new LinkedHashSet<String>(); Annotation[] metaAnnotations = AnnotationUtils.getAnnotations(annotationClass); if (!ObjectUtils.isEmpty(metaAnnotations)) { for (Annotation metaAnnotation : metaAnnotations) { if (!AnnotationUtils.isInJavaLangAnnotationPackage(metaAnnotation)) { recursivelyCollectMetaAnnotations(metaAnnotationTypeNames, metaAnnotation); } } } if (this.metaAnnotationMap != null) { //当前注解的所有祖先注解的名称保存到metaAnnotationMap对象中 this.metaAnnotationMap.put(annotationClass.getName(), metaAnnotationTypeNames); } } private void recursivelyCollectMetaAnnotations(Set<String> visited, Annotation annotation) { String annotationName = annotation.annotationType().getName(); //非java.lang.annotion开头的注解,并加入到属性visited中 if (!AnnotationUtils.isInJavaLangAnnotationPackage(annotation) && visited.add(annotationName)) { //注解类型必需是public if (Modifier.isPublic(annotation.annotationType().getModifiers())) { //将注解的所有属性键值对保存到AnnotationAttributes中 this.attributesMap.add(annotationName, AnnotationUtils.getAnnotationAttributes(annotation, false, true)); for (Annotation metaMetaAnnotation : annotation.annotationType().getAnnotations()) { //递归查找 recursivelyCollectMetaAnnotations(visited, metaMetaAnnotation); } } } }
程序运行到这里,己经将类注解,以及方法注解,保存到类访问器相关属性中。从字节码中得到如下的对象
接下来,就是看这个bean有没有相关注解,如下
static void processCommonDefinitionAnnotations(AnnotatedBeanDefinition abd, AnnotatedTypeMetadata metadata) { //定义 lazy 属性 if (metadata.isAnnotated(Lazy.class.getName())) { abd.setLazyInit(attributesFor(metadata, Lazy.class).getBoolean("value")); } else if (abd.getMetadata() != metadata && abd.getMetadata().isAnnotated(Lazy.class.getName())) { abd.setLazyInit(attributesFor(abd.getMetadata(), Lazy.class).getBoolean("value")); } //定义 Primary 属性 if (metadata.isAnnotated(Primary.class.getName())) { abd.setPrimary(true); } //定义 depend-on 属性 if (metadata.isAnnotated(DependsOn.class.getName())) { abd.setDependsOn(attributesFor(metadata, DependsOn.class).getStringArray("value")); } if (abd instanceof AbstractBeanDefinition) { AbstractBeanDefinition absBd = (AbstractBeanDefinition) abd; //定义 role 属性 if (metadata.isAnnotated(Role.class.getName())) { absBd.setRole(attributesFor(metadata, Role.class).getNumber("value").intValue()); } //定义 description 属性 if (metadata.isAnnotated(Description.class.getName())) { absBd.setDescription(attributesFor(metadata, Description.class).getString("value")); } } }
其实,这篇博客和Spring源码深度解析(郝佳)-学习-源码解析-基于注解bean定义(一) 大同小异,只是增加了一点当注解中配置了注解方法的解析。
过程:
1)parseBeanDefinitions(): 解析 bean 的定义 1)parseDefaultElement(): 默认标签解析 ,如 bean ,beans 2)parseCustomElement():自定义的命名空间解析,如 aspectj-autoprxoy,component-scan 等。 1)getNamespaceURI():获取命名空间 uri 2)getNamespaceHandlerResolver.resolve(namespaceUri) : 获取自定义标签的NamespaceHandler,并调用其 init()方法,将标签解析器初始化。 3)parse():解析开始 1)findParserForElement():根据标签查找解析器 2)parse():解析元素 1)AspectJAutoProxyBeanDefinitionParser.parse(): aspectj-autoproxy标签解析。 1)registerAspectJAnnotationAutoProxyCreatorIfNecessary():注册AnnotationAwareAspectJAutoProxyCreator 1)registerAspectJAnnotationAutoProxyCreatorIfNecessary():注册或升级 AutoProxyCreator定义beanName 为 org.springframework.aop.config.internalAutoProxyCreator的 BeanDefinition 2)useClassProxyingIfNecessary():对于 proxy-target-class 以及 expose-proxy 属性的处理 1)forceAutoProxyCreatorToUseClassProxying():添加 proxyTargetClass 为true的属性到propertyValues中 2)forceAutoProxyCreatorToExposeProxy():添加 exposeProxy 为true的属性到propertyValues中 3)registerComponentIfNecessary():注册组件通知,便于监听器进一步处理,其中 beanDefinition的 className 为 AnnotationAwareAspectJAutoProxyCreator 2)extendBeanDefinition():对注解中的子类的处理 2)ComponentScanBeanDefinitionParser.parse(): component-scan 解析 1)doScan():扫描所有的包 1)findCandidateComponents():根据包路径扫描并获取所有的 BeanDefinition。 1)getResources():获取到包下的所有的 resource 2)getMetadataReader(resource):获取到resource相关注解的元数据metadataReader 1)new SimpleMetadataReader(resource, classLoader()): 创建简单的元数据 1)new ClassReader():初始化字节码常量池 2)accept(): 从类字节码中访问类注解,方法注解,及属性 1)readAnnotationValues():读取类注解属性 2)readMethod():读取方法及方法注解 3)new ScannedGenericBeanDefinition(metadataReader):创建 beanDefinition 2)resolveScopeMetadata(): 获取Bean定义类中的@Scope注解的值,即获取Bean的作用域 3)generateBeanName():获取 beanName 4)postProcessBeanDefinition():设置 bean 的自动依赖注入相关配置 5)processCommonDefinitionAnnotations():处理注解Bean中通过的注解 1)setLazyInit():设置 lazy 2)setPrimary():设置 Primary 3)setDependsOn():设置DependsOn 4)setRole():设置 Role 5)setDescription():设置 Description 属性 6)registerBeanDefinition():注册 bean
今天的博客只分析注解Bean 的解析及定义,关于如何实现切面,后面的博客再来详述。
本文的github地址是
https://github.com/quyixiao/spring_tiny/tree/master/src/main/java/com/spring_1_100/test_61_70/test64