天天看點

spring4.0源碼分析之注解━━━Annotation

       Annotation比起xml是各有優勢,Annotation用起來就是簡潔,少了大量的配置檔案。配置檔案則很容易的就看出類之間的關聯,看xml配置檔案即可。但是如果用Annotation,還得看java代碼。至于選擇使用什麼就看自己實際的情況而定。這裡講解spring在用Annotation的時候,是怎麼讀取Annotation,又像IOC中注入BeanDefinition的。

       如果是用Annotation,則隻需下面一行即可:

<!-- 激活spring的注解 ,有了下面一行,這行可以省略
		<context:annotation-config />
-->
<context:component-scan base-package="com.zzx.study" />
           
  • 怎麼讀取xml配置檔案

    我們直接看spring-context-4.0.0.M2.jar檔案下的META-INF目錄下的spring.handlers檔案有如下一行,至于怎麼到這行,可看DefaultNamespaceHandlerResolver類的解析:

http\://www.springframework.org/schema/context=org.springframework.context.config.ContextNamespaceHandler
           

   即如果在xml配置檔案中,如果命名空間為http://www.springframework.org/schema/context則使用org.springframework.context.config.ContextNamespaceHandler這個類來解析,ContextNamespaceHandler類有init,是初始化針對不同的标簽用不同的類來解析。例如上面的component-scan,則使用ComponentScanBeanDefinitionParser類解析。具體怎麼到這些類類解析xml,可參考另外一篇博文http://992012.iteye.com/blog/1921633

@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類中使用到了ASM來獲得Annotation,而不是使用java的反射。原因是java的反射效率相對ASM第三方位元組碼庫來說低一點。是以使用到了ASM。如果想看ASM,可到這個官網上檢視http://asm.ow2.org/。ComponentScanBeanDefinitionParser的parse方法如下:

@Override
	public BeanDefinition parse(Element element, ParserContext parserContext) {
		String[] basePackages = StringUtils.tokenizeToStringArray(element.getAttribute(BASE_PACKAGE_ATTRIBUTE),
				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);

		// Actually scan for bean definitions and register them.
		ClassPathBeanDefinitionScanner scanner = configureScanner(parserContext, element);
//通過scanner找到符合要求的Annotation,通過ASM第三方庫
		
Set<BeanDefinitionHolder> beanDefinitions = scanner.doScan(basePackages);
//向IOC容器注入BeanDefinition

		registerComponents(parserContext.getReaderContext(), beanDefinitions, element);

		return null;
	}
           

     在ClassPathBeanDefinitionScanner的doScan方法中,會調用到ClassPathScanningCandidateComponentProvider中的findCandidateComponents方法

public Set<BeanDefinition> findCandidateComponents(String basePackage) {
		Set<BeanDefinition> candidates = new LinkedHashSet<BeanDefinition>();
		try {
			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 {
//通過ASM建立中繼資料reader
						MetadataReader metadataReader = this.metadataReaderFactory.getMetadataReader(resource);
//根據reader判斷是否BeanDefinition			
			if (isCandidateComponent(metadataReader)) {
//這裡建立了ScannedGenericBeanDefinition 
							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;
	}
           

   如果是使用了Service,Repository同意是可以被spring認識到的,因為這兩個類的類同樣也有中繼資料@Component。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {

	/**
	 * The value may indicate a suggestion for a logical component name,
	 * to be turned into a Spring bean in case of an autodetected component.
	 * @return the suggested component name, if any
	 */
	String value() default "";

}
           

    這裡順帶講解一下ASM。

@Test
	public void test3() throws IOException{
		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
		resolveBasePackage("com.zzx.study.spring") + "/" + "**/*.class";
		PathMatchingResourcePatternResolver  pathResolver = new PathMatchingResourcePatternResolver();
		Resource[] rr = pathResolver.getResources(packageSearchPath);
		for(Resource r:rr){
			if(r.getFilename().equals("UserDao.class")){
				System.out.println(r.getFilename());
				AnnotationMetadataReadingVisitor visitor = new AnnotationMetadataReadingVisitor(Thread.currentThread().getContextClassLoader());
				InputStream is = new BufferedInputStream(r.getInputStream());
				
ClassReader classReader;
				try {
//建立ASM的ClassReader
					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: " + r, ex);
				}
				finally {
					is.close();
				}
//調用reader的接受方法,這個方法實際就是解析class位元組碼的實作,這裡使用了Visitor模式
				classReader.accept(visitor, ClassReader.SKIP_DEBUG);
				for(String annotationType:visitor.getAnnotationTypes()){
					System.out.println(annotationType);
				}
				System.out.println("hasAnnotation:"+visitor.hasAnnotation(Component.class.getName()));
				System.out.println("hasMetaAnnotation:"+visitor.hasMetaAnnotation(Component.class.getName()));
			}
			}
	}
           

  使用者可以實作自己的visitor,也就是具體的通路者了。

public class MyOwnClassVisitor extends ClassVisitor {

	@Override
	public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
		//解析過程中如果有annotation就會調用到我這個具體通路者的方法了,下面的Field和method類似
		System.out.println("visitAnnotation:"+desc);
		return super.visitAnnotation(desc, visible);
	}

	@Override
	public FieldVisitor visitField(int access, String name, String desc,
			String signature, Object value) {
		System.out.println("visitField:"+name);
		return super.visitField(access, name, desc, signature, value);
	}

	@Override
	public MethodVisitor visitMethod(int access, String name, String desc,
			String signature, String[] exceptions) {
		System.out.println("visitMethod:"+name);
		return super.visitMethod(access, name, desc, signature, exceptions);
	}

	public MyOwnClassVisitor(int api) {
		super(api);
	}

	public static void main(String[] args) throws IOException {
		MyOwnClassVisitor visitor = new MyOwnClassVisitor(
				SpringAsmInfo.ASM_VERSION);
		//建立reader
		ClassReader reader=new ClassReader("com.zzx.study.spring.dao.UserDao");
//		調用accept解析位元組碼,第一個參數傳使用者具體的通路者
		reader.accept(visitor, ClassReader.SKIP_DEBUG);
		System.out.println(reader.getSuperName());
	}
	

}
           

  如果想了解visitor模式,可參考網上資料 http://blog.csdn.net/chenjie19891104/article/details/6393770