谈谈Spring的IoC之注解扫描

问题

??IoC是Inversion of Control的缩写,翻译过来即“控制反转”。IoC可以说是Spring的灵魂,想要读懂Spring,必先读懂IoC。不过有时候硬着头皮直接看源码,就像雾里看花,并不能一窥真谛。想要理解Spring的IoC,不如反过来思考一下,如果我们自己去实现IoC,需要考虑什么?遵循依赖大于配置的原则,我们这里只讨论注解式的控制反转容器,XML配置的方式暂不考虑。由此我想到下面几个问题:

  1. 注解扫描
  2. 实例化
  3. 循环依赖
  4. 泛型注入

本文的重点,就是围绕这几个问题,从spring的源码入手,解答心中的疑惑。

注解扫描

??我们从一个ApplicationContext示例开始启动Spring:

1234567891011121314151617181920212223242526
 * 这是IoC的测试类 */public class  {    public static void main(String[] args) {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IoCTest.class);        MyService service = context.getBean(MyService.class);        System.out.println(service.getService());    }}

 * 测试注入Bean的接口 */public interface MyService {    String getService();}

 * 测试的Bean */

public class ServiceA implements MyService {    public String getService() {        return "serviceA";    }}

运行IoCTest的main,发现报错NoSuchBeanDefinitionException。因为IoCTest少了一个注解扫描的环节,我们加上一个@ComponentScan就可以运行并打印了:

1234567891011
 * 这是IoC的测试类 */@ComponentScanpublic class  {    public static void main(String[] args) {        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(IoCTest.class);        MyService service = context.getBean(MyService.class);        System.out.println(service.getService());    }}

我们直接由AnnotationConfigApplicationContext入手:

12345
public AnnotationConfigApplicationContext(Class<?>... annotatedClasses) {   this();   register(annotatedClasses);   refresh();}

register()这里是注册IoCTest,我们主要看一下refresh(),这里refresh()方法由AbstractApplicationContext实现:

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263
public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {	// Prepare this context for refreshing.	prepareRefresh();

	// Tell the subclass to refresh the internal bean factory.	ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

	// Prepare the bean factory for use in this context.	prepareBeanFactory(beanFactory);

	try {		// Allows post-processing of the bean factory in context subclasses.		postProcessBeanFactory(beanFactory);

		// Invoke factory processors registered as beans in the context.		invokeBeanFactoryPostProcessors(beanFactory);

		// Register bean processors that intercept bean creation.		registerBeanPostProcessors(beanFactory);

		// Initialize message source for this context.		initMessageSource();

		// Initialize event multicaster for this context.		initApplicationEventMulticaster();

		// Initialize other special beans in specific context subclasses.		onRefresh();

		// Check for listener beans and register them.		registerListeners();

		// Instantiate all remaining (non-lazy-init) singletons.		finishBeanFactoryInitialization(beanFactory);

		// Last step: publish corresponding event.		finishRefresh();	}

	catch (BeansException ex) {		if (logger.isWarnEnabled()) {			logger.warn("Exception encountered during context initialization - " +					"cancelling refresh attempt: " + ex);		}

		// Destroy already created singletons to avoid dangling resources.		destroyBeans();

		// Reset 'active' flag.		cancelRefresh(ex);

		// Propagate exception to caller.		throw ex;	}

	finally {		// Reset common introspection caches in Spring's core, since we		// might not ever need metadata for singleton beans anymore...		resetCommonCaches();	}}}

refresh()有一堆方法,不过还好Spring每一个方法的注释都很清晰。由于我们的重点在于分析注解扫描,所以我们主要看一下invokeBeanFactoryPostProcessors(beanFactory)

12345678910
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {	PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());

	// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime	// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)	if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {		beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));		beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));	}}

调用PostProcessorRegistrationDelegate执行BeanFactory的PostProcessors:

123456789101112131415161718192021
public static void invokeBeanFactoryPostProcessors(	ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {	Set<String> processedBeans = new HashSet<>();    if (beanFactory instanceof BeanDefinitionRegistry) {    	// ...上面省略了一部分代码...    	List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();    	String[] postProcessorNames =    			beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);    	for (String ppName : postProcessorNames) {    		if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {    			currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));    			processedBeans.add(ppName);    		}    	}    	sortPostProcessors(currentRegistryProcessors, beanFactory);    	registryProcessors.addAll(currentRegistryProcessors);    	invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);    	// ...省略下面一部分代码...   }   // ...限于篇幅省略下面的代码...}

BeanFactory中获取BeanDefinitionRegistryPostProcessor的实例,而BeanDefinitionRegistryPostProcessor是一个接口,他只有一个实现ConfigurationClassPostProcessor,即这里currentRegistryProcessors中存放了一个ConfigurationClassPostProcessor的实例对象,看一下invokeBeanDefinitionRegistryPostProcessors方法:

123456789
* Invoke the given BeanDefinitionRegistryPostProcessor beans.*/private static void invokeBeanDefinitionRegistryPostProcessors(Collection<? extends BeanDefinitionRegistryPostProcessor> postProcessors, BeanDefinitionRegistry registry) {  for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) {  postProcessor.postProcessBeanDefinitionRegistry(registry);  }}

只做了一件事,调用BeanDefinitionRegistryPostProcessorpostProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)方法,现在我们来看一下ConfigurationClassPostProcessor的实现:

123456789101112131415161718
 * Derive further bean definitions from the configuration classes in the registry. */@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {	int registryId = System.identityHashCode(registry);	if (this.registriesPostProcessed.contains(registryId)) {		throw new IllegalStateException(				"postProcessBeanDefinitionRegistry already called on this post-processor against " + registry);	}	if (this.factoriesPostProcessed.contains(registryId)) {		throw new IllegalStateException(				"postProcessBeanFactory already called on this post-processor against " + registry);	}	this.registriesPostProcessed.add(registryId);

	processConfigBeanDefinitions(registry);}

继续看这里的processConfigBeanDefinitions(BeanDefinitionRegistry registry)

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {	// ...省略上面一部分代码...	// Parse each @Configuration class	ConfigurationClassParser parser = new ConfigurationClassParser(			this.metadataReaderFactory, this.problemReporter, this.environment,			this.resourceLoader, this.componentScanBeanNameGenerator, registry);	// 这里的configCandidates是BeanDefinitionHolder集合	Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);	Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());	do {		parser.parse(candidates);		parser.validate();

		Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());		configClasses.removeAll(alreadyParsed);

		// Read the model and create bean definitions based on its content		if (this.reader == null) {			this.reader = new ConfigurationClassBeanDefinitionReader(					registry, this.sourceExtractor, this.resourceLoader, this.environment,					this.importBeanNameGenerator, parser.getImportRegistry());		}		this.reader.loadBeanDefinitions(configClasses);		alreadyParsed.addAll(configClasses);

		candidates.clear();		if (registry.getBeanDefinitionCount() > candidateNames.length) {			String[] newCandidateNames = registry.getBeanDefinitionNames();			Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));			Set<String> alreadyParsedClasses = new HashSet<>();			for (ConfigurationClass configurationClass : alreadyParsed) {				alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());			}			for (String candidateName : newCandidateNames) {				if (!oldCandidateNames.contains(candidateName)) {					BeanDefinition bd = registry.getBeanDefinition(candidateName);					if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) 大专栏  谈谈Spring的IoC之注解扫描 &&							!alreadyParsedClasses.contains(bd.getBeanClassName())) {						candidates.add(new BeanDefinitionHolder(bd, candidateName));					}				}			}			candidateNames = newCandidateNames;		}	}	while (!candidates.isEmpty());	// ...省略下面一部分代码...}

candidates在这里其实是IoCTest的BeanDefinitionHolder集合。继续看ConfigurationClassParser的parse()方法:

12345678910111213141516171819202122232425
public void parse(Set<BeanDefinitionHolder> configCandidates) {	for (BeanDefinitionHolder holder : configCandidates) {		BeanDefinition bd = holder.getBeanDefinition();		try {			if (bd instanceof AnnotatedBeanDefinition) {				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());			}			else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());			}			else {				parse(bd.getBeanClassName(), holder.getBeanName());			}		}		catch (BeanDefinitionStoreException ex) {			throw ex;		}		catch (Throwable ex) {			throw new BeanDefinitionStoreException(					"Failed to parse configuration class [" + bd.getBeanClassName() + "]", ex);		}	}

	this.deferredImportSelectorHandler.process();}

最终parse会调用:

1234567891011
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {	// ...省略上面一部分代码...	// Recursively process the configuration class and its superclass hierarchy.	SourceClass sourceClass = asSourceClass(configClass);	do {		sourceClass = doProcessConfigurationClass(configClass, sourceClass);	}	while (sourceClass != null);

	this.configurationClasses.put(configClass, configClass);}

继续看doProcessConfigurationClass()

1234567891011121314151617181920212223242526
protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)		throws IOException {	// ...省略上部分代码...	// Process any @ComponentScan annotations	Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(			sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);	if (!componentScans.isEmpty() &&			!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {		for (AnnotationAttributes componentScan : componentScans) {			// The config class is annotated with @ComponentScan -> perform the scan immediately			Set<BeanDefinitionHolder> scannedBeanDefinitions =					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());			// Check the set of scanned definitions for any further config classes and parse recursively if needed			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();				if (bdCand == null) {					bdCand = holder.getBeanDefinition();				}				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {					parse(bdCand.getBeanClassName(), holder.getBeanName());				}			}		}	}	// ...省略下部分代码...}

至此终于进入我们的正题,注解扫描的主角this.componentScanParserComponentScanAnnotationParser登场。继续看ComponentScanAnnotationParserparse(AnnotationAttributes componentScan, final String declaringClass)

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758
public Set<BeanDefinitionHolder> parse(AnnotationAttributes componentScan, final String declaringClass) {	ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(this.registry,			componentScan.getBoolean("useDefaultFilters"), this.environment, this.resourceLoader);

	Class<? extends BeanNameGenerator> generatorClass = componentScan.getClass("nameGenerator");	boolean useInheritedGenerator = (BeanNameGenerator.class == generatorClass);	scanner.setBeanNameGenerator(useInheritedGenerator ? this.beanNameGenerator :			BeanUtils.instantiateClass(generatorClass));

	ScopedProxyMode scopedProxyMode = componentScan.getEnum("scopedProxy");	if (scopedProxyMode != ScopedProxyMode.DEFAULT) {		scanner.setScopedProxyMode(scopedProxyMode);	}	else {		Class<? extends ScopeMetadataResolver> resolverClass = componentScan.getClass("scopeResolver");		scanner.setScopeMetadataResolver(BeanUtils.instantiateClass(resolverClass));	}

	scanner.setResourcePattern(componentScan.getString("resourcePattern"));

	for (AnnotationAttributes filter : componentScan.getAnnotationArray("includeFilters")) {		for (TypeFilter typeFilter : typeFiltersFor(filter)) {			scanner.addIncludeFilter(typeFilter);		}	}	for (AnnotationAttributes filter : componentScan.getAnnotationArray("excludeFilters")) {		for (TypeFilter typeFilter : typeFiltersFor(filter)) {			scanner.addExcludeFilter(typeFilter);		}	}

	boolean lazyInit = componentScan.getBoolean("lazyInit");	if (lazyInit) {		scanner.getBeanDefinitionDefaults().setLazyInit(true);	}

	Set<String> basePackages = new LinkedHashSet<>();	String[] basePackagesArray = componentScan.getStringArray("basePackages");	for (String pkg : basePackagesArray) {		String[] tokenized = StringUtils.tokenizeToStringArray(this.environment.resolvePlaceholders(pkg),				ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);		Collections.addAll(basePackages, tokenized);	}	for (Class<?> clazz : componentScan.getClassArray("basePackageClasses")) {		basePackages.add(ClassUtils.getPackageName(clazz));	}	if (basePackages.isEmpty()) {		basePackages.add(ClassUtils.getPackageName(declaringClass));	}

	scanner.addExcludeFilter(new AbstractTypeHierarchyTraversingFilter(false, false) {		@Override		protected boolean matchClassName(String className) {			return declaringClass.equals(className);		}	});	return scanner.doScan(StringUtils.toStringArray(basePackages));}

declaringClass在这个例子中的入参即spring.ioc.test.IoCTest,装配basePackages后使用ClassPathBeanDefinitionScannerdoScan(String… basePackages)进行扫描:

1234567891011121314151617181920212223242526
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {		Assert.notEmpty(basePackages, "At least one base package must be specified");		Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();		for (String basePackage : basePackages) {			Set<BeanDefinition> candidates = findCandidateComponents(basePackage);			for (BeanDefinition candidate : candidates) {				ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);				candidate.setScope(scopeMetadata.getScopeName());				String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);				if (candidate instanceof AbstractBeanDefinition) {					postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);				}				if (candidate instanceof AnnotatedBeanDefinition) {					AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);				}				if (checkCandidate(beanName, candidate)) {					BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);					definitionHolder =							AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);					beanDefinitions.add(definitionHolder);					registerBeanDefinition(definitionHolder, this.registry);				}			}		}		return beanDefinitions;	}

终于到了最后一步,findCandidateComponents(String basePackage),来看下Spring如何根据basePackages来扫描组装BeanDefinition

12345678
public Set<BeanDefinition> findCandidateComponents(String basePackage) {	if (this.componentsIndex != null && indexSupportsIncludeFilters()) {		return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);	}	else {		return scanCandidateComponents(basePackage);	}}

在这个示例中,this.componentsIndex为null,进入scanCandidateComponents(String basePackage)

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {	Set<BeanDefinition> candidates = new LinkedHashSet<>();	try {		String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +				resolveBasePackage(basePackage) + '/' + this.resourcePattern;		Resource[] resources = getResourcePatternResolver().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 = getMetadataReaderFactory().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;}

加载packageSearchPath(**classpath:spring/ioc/test/**/.class**)下所有的class文件,并通过isCandidateComponent(MetadataReader metadataReader)进行过滤,上面的测试例子中只有MyService符合条件加入candidates集合。

原文地址:https://www.cnblogs.com/lijianming180/p/12041167.html

时间: 2024-10-30 09:51:55

谈谈Spring的IoC之注解扫描的相关文章

七 Spring的IOC的注解方式

Spring的IOC的注解方式入门 创建web项目,引入相应的jar包 除了IOC的6个包,还需要AOP的包 引入Spring配置文件 创建applicationContext.xml 引入约束:使用注解开发引入context约束 file:///D:/Hibernate/Spring/spring-framework-4.2.4.RELEASE/docs/spring-framework-reference/html/xsd-configuration.html 创建接口和实现类: 配置Spr

(转)java之Spring(IOC)注解装配Bean详解

在这里我们要详细说明一下利用Annotation-注解来装配Bean. 因为如果你学会了注解,你就再也不愿意去手动配置xml文件了,下面就看看Annotation的魅力所在吧. 先来看看之前的bean注解装配例子: package com.eco.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import

Spring第二天:Spring的IOC的注解方式、Spring的AOP开发(XML)

注解方式 如下: 开启组件扫描--> 结果同上. 如果设置值: 新建service 注入dao 进行测试  注入Dao. 但要注意 autowired是按照类型注入的 不管dao的名字是啥 哪怕是userDao22222  只要它实现了userDao接口  那么就可以直接注入userDao. 要想按名称注入 就要用Qualifier一起使用 如上图 这时名字就必须一致(userDao22222) ,也可以直接使用Resource注解. 下面举例:整合开发: 还要关闭包扫描(因为类都交给xml管理

spring注解与springMVC注解扫描的问题

在将spring与springMVC结合使用时,当我们使用注解的时候,一般都是在spring配置文件中配置注解扫描dao层.service层的包,在springMVC配置文件中配置注解扫描controller,自己在练习spring+SpringMVC+mybatis的项目时对这种做法一知半解,所以在练习项目的时候在实践中对自己的一些想法进行了验证. 一般的配置 spring配置文件中 <?xml version="1.0" encoding="UTF-8"?

spring中aop的注解实现方式简单实例

上篇中我们讲到spring的xml实现,这里我们讲讲使用注解如何实现aop呢.前面已经讲过aop的简单理解了,这里就不在赘述了. 注解方式实现aop我们主要分为如下几个步骤(自己整理的,有更好的方法的话,欢迎交流[email protected]): 1.在切面类(为切点服务的类)前用@Aspect注释修饰,声明为一个切面类. 2.用@Pointcut注释声明一个切点,目的是为了告诉切面,谁是它的服务对象.(此注释修饰的方法的方法体为空,不需要写功能比如 public void say(){};

Spring框架 IOC注解

Spring框架的IOC之注解方式的快速入门        1. 步骤一:导入注解开发所有需要的jar包        * 引入IOC容器必须的6个jar包        * 多引入一个:Spring框架的AOP的jar包,spring-aop的jar包        2. 步骤二:创建对应的包结构,编写Java的类        * UserService            -- 接口        * UserServiceImpl        -- 具体的实现类        3.

Spring IOC/DI/注解

一.定义:Spring 是一个开源的控制反转(Inversion of Control,IoC/DI)和面向切面(AOP)的容器框架,它的主要目的是简化企业开发 二.实例化Spring容器: 方法一:在类路径下寻找配置文件来实例化容器 1 ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"}); 方法二:在文件系统路径下寻找配置文件来实例化容器 1 Applicatio

spring 的IoC注解的配置信息

Spring的注解Ioc的配置 注解一共分为四类: 1.创建对象 2.注入数据 3.改变作用范围 4.和生命周期相关 1.创建对象 xml方式创建对象 相对于xml配置就是:<bean id=" " class=" "></bean> id 为要创建对象的唯一标识 class 对象的全限定类名 注解创建创建对象 @component() 组件的意思 作用:把当前类的对象存入IoC容器中(写在要创建的对象的类上面) 参数: value 指定获取

Spring的IOC注解开发入门1

基本知识点如下: 引入注解约束,配置组件扫描 类上的注解: @Conponent  @Controller @Service @Repository 普通属性的注解   @value 对象属性的注解    @Resource   @Autowired  @Qualifier Bean生命周期,初始化与销毁: @PostConstruct @PreDestroy Bean作用范围:@Scope("prototype")  , 默认是singleton 1.创建web项目引入jar包 除了