Spring中Autowired注解,Resource注解和xml default-autowire工作方式异同

前面说到了关于在xml中有提供default-autowire的配置信息,从spring 2.5开始,spring又提供了一个Autowired以及javaEE中标准的Resource注释,都好像可以实现类似的自动注入。那么是不是每个都实现同样的方式呢,这里面的几个配置到底有哪些异同点。哪个更全,哪个更优先,这些都需要对spring的内部原理有详细的了解才可以进行了解。
在以下文章时,首先有几个概念需要列出:
字段名称:即fieldName,这个即propertyDescriper的getPropertyName返回信息。
setter名称:即方法setter除set之外的名称,如setAbc,则名称为abc,这里的abc不一定和fieldName相同。
参数名称:即在参数中所定义的参数的名称,如setAbc(Abc a123)。这里的参数名称就是a123。
本文所使用spring版本为spring3.0.2。

处理类和处理顺序异同

default-autowire是在xml中进行配置的,而这个配置从spring初始就提供了。而Autowired注解,则是从2.5自支持以java1.5之后才出现的,这就必然导致对相应的处理以及逻辑是不同的。那么每个方式的处理顺序是怎样的呢,从我写的文章:Spring中获取一个bean的流程-2.也可以由下面的代码得出:

		if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME ||
				mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
			MutablePropertyValues newPvs = new MutablePropertyValues(pvs);

			// Add property values based on autowire by name if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_NAME) {
				autowireByName(beanName, mbd, bw, newPvs);
			}

			// Add property values based on autowire by type if applicable.
			if (mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_BY_TYPE) {
				autowireByType(beanName, mbd, bw, newPvs);
			}

			pvs = newPvs;
		}
......
			for (BeanPostProcessor bp : getBeanPostProcessors()) {
				if (bp instanceof InstantiationAwareBeanPostProcessor) {
					InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
					pvs = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);

以上代码来源于类AbstractAutowireCapableBeanFactory的populateBean方法。从上可以看出,spring首先处理在bean定义上的autowire属性,然后再处理后面的InstantiationAwareBeanPostProcessor类。首先bean定义上的autowire属性,可以来自于<bean>定义时autowire属性,也可以来自于整个xml定义中<beans>节点中的default-autowire属性。

那么@Autowired注解和@Resource注解在哪儿处理呢,看上面的代码,有个InstantiationAwareBeanPostProcessor类,如果你仔细查看里面的实现,你可以发现里面即为处理相应注解类的实现。而这些注解类,只要在xml中启用了<context:annotation-config/>,即可以开启这些类了。而我们的Autowired注解,由AutowiredAnnotationBeanPostProcessor来进行处理,而Resource类,则由CommonAnnotationBeanPostProcessor进行处理。

继续阅读“Spring中Autowired注解,Resource注解和xml default-autowire工作方式异同”

在Mybatis-spring中由于默认Autowired导致不能配置多个数据源的问题分析及解决

在使用Mybatis中,通常使用接口来表示一个Sql Mapper的接口以及相对应的xml实现,而在spring的配置文件中,通常会使用MapperScannerConfigurer来达到批量扫描以及简化spring bean接口配置的目的,以直接让mybatis的各个接口直接成为spring的bean组件。那么,一个通常的spring配置文件如下所示:

	<bean id="datasource" class="org.springframework.jdbc.datasource.SimpleDriverDataSource"/>

	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" p:dataSource-ref="datasource"/>
	<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg index="0" ref="sqlSessionFactory"/>
	</bean>
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"
		  p:dataSource-ref="datasource"/>
	<bean  class="org.mybatis.spring.mapper.MapperScannerConfigurer"
		  p:basePackage="mapper"/>

上面的配置分别对应于一个基本的mybatis最需要的信息。值得需要注意的是,在配置MapperScannerConfigurer时,这里并没有指定sqlSessionFactoryName以及sqlTemplateName。在没有指定的情况下,spring就会指定默认的查找规则进行查询,如分别查找到默认的sqlSessionFactory实现和sqlSessionTemplate实现,并注入到MapperFactoryBean中。

这种配置方式,在一个数据源时,没有问题,但是在如果存在多个数据源时,上面的配置就存在问题了。在多个数据源时,如果配置不正确,或者配置的步骤不正确,将直接产生莫名奇妙的问题。而这个问题的产生,不在于开发人员,即不在于程序员本身,而在于spring,或来自于mybatis-spring,在其内部画蛇添足的注解,将导致整个多数据源配置完全不能工作。

继续阅读“在Mybatis-spring中由于默认Autowired导致不能配置多个数据源的问题分析及解决”

Spring中获取一个bean的流程-2

上篇,进入第4节,实质性地创建一个Bean信息。

4 doCreateBean

4.1 首先创建一个原始对象的包装对象,即BeanWrapper,这通过对bean进行包装而来,如下代码所示:

instanceWrapper = createBeanInstance(beanName, mbd, args);

在这个createBeanInstance方法中,首先会创建原始对象,这就有几个策略。如下所示:
如果存在工厂方法,即在xml中定义了factory-method,就会调用工厂方法进行创建,代码如下所示:

if (mbd.getFactoryMethodName() != null) {
			return instantiateUsingFactoryMethod(beanName, mbd, args);
		}

如果已经确认或者解析了候选构造方法,则调用此方法进行创建

if (resolved) {
			if (autowireNecessary) {
				return autowireConstructor(beanName, mbd, null, null);
			}
			else {
				return instantiateBean(beanName, mbd);
			}
		}

如果存在SmartInstantiationAwareBeanPostProcessor并且对象解析出指定的bean Class有候选的构造方法,如在构造方法上追加@Autowired的话,会优化使用这个构造方法。
最后,采用默认的构造策略进行对象创建,如调用Class.newInstance()方法创建。

继续阅读“Spring中获取一个bean的流程-2”

Spring中获取一个bean的流程-1

本文从ClasspathXmlApplication进行入手,未考虑针对于Annotation的处理,仅从xml方向对getBean进行代码追踪,以形成一个完整的链条。以下代码从取得一个singleton对象入手进行分析。

1 获取Bean信息

1.1 首先这里会调用AbstractApplicationContext.getBean(String name),这个方法直接会转向1.2
1.2 进入方法AbstractBeanFactory.getBean(String name),这个方法会直接进入1.3
1.3 进入方法AbstractBeanFactory.doGetBean(final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)。这个方法是获取bean的主要入口。此方法的详细叙述在第2节

2 doGetBean信息

2.1 首先这里会将传入的beanName进行转化。代码如下所示:

		final String beanName = transformedBeanName(name);
protected String transformedBeanName(String name) {
		return canonicalName(BeanFactoryUtils.transformedBeanName(name));
	}

转化有两个步骤,首先处理beanName为&XXX的格式,这里表示要取指定name的factoryBean。在这里先把&符号取消,先获取bean再处理。然后,针对bean的alias机制,这里传入的参数可能是一个bean别名,那么我们先获取这个bean的主要id,只需要根据id值取bean就可以了。

继续阅读“Spring中获取一个bean的流程-1”

Spring中循环引用的处理-3

上文中对调用点A和调用点B的调用方法进行了陈述。接下来,针对创建bean的不同顺序对调用点和调用方法进行分析。

在正常的情况下,调用顺序如下:以下有无,表示是否持有对指定Bean的引用

  singletonFactories earlySingletonObjects singletonObjects
getSingleton(beanName, true)
doCreateBean(beanName,mdb,args)
getSingleton(beanName, true);
addSingleton(beanName, singletonObject)

但是出现循环引用之后呢,就会出现这种情况:

  singletonFactories earlySingletonObjects singletonObjects
getSingleton(A, true); A无B无 A无B无 A无B无
doCreateBean(A,mdb,args) A有B无 A无B无 A无B无
       
populateBean(A, mbd, instanceWrapper) 解析B……  
getSingleton(B, true) A有B无 A无B无 A无B无
doCreateBean(B,mdb,args) A有B有 A无B无 A无B无
populateBean(B, mbd, instanceWrapper)由B准备解析A……  
getSingleton(A, true) A无B有 A有B无 A无B无
完成populateBean(B, mbd, instanceWrapper)解析……  
addSingleton(B, singletonObject) A无B无 A有B无 A无B有
完成populateBean(A, mbd, instanceWrapper)  
       
A- = initializeBean(beanName, exposedObject, mbd)在initializeBean之后A变为A-  
getSingleton(A, false);验证  
addSingleton(A, singletonObject)    ……  

在上面这个过程中,在对A进行验证时,就会从earlySingletonObjects中取得一个A,但是这个A和后面的A-可能不是同一个对象,这是因为有了beanPostProcessor存在,它可以改变bean的最终值,比如对原始bean进行封装,代理等。在这个过程中,出现了3个对象A,A-,B,而B中所持有的A对象为原始的A。如果这里的A和A-不是同一个对象,即产生了beanA有了beanB的引用,但beanB并没有beanA的引用,而是另一个beanA的引用。这肯定不满足条件。

继续阅读“Spring中循环引用的处理-3”

Spring中循环引用的处理-2

上文中对涉及到循环引用的3个方法作了陈述。

在方法1中,对象信息对beanFactory的形式被放入singletonFactories中,这时earlySingletonObjects中肯定没有此对象(因为remove)。

在方法2中,在一定条件下(allowEarlyReference为true)的条件下,对象从singleFactories中的objectFactory中被取出来,同时remove掉,被放入earlySingletonObjects中。这时,earlySingletonObjects就持有对象信息了;当然,如果allowEarlyReference为false的情况下,且earlySingletonObjects本身就没有持有对象的情况下,肯定不会将对象从objectFactory中取出来的。这个很重要,因为后面将根据此信息进行循环引用处理。

在方法3中,对象被加入到singletonObjects中,同时singletonFactories和earlySingletonObjects中都remove掉持有的对象(不管持有与否),这就表示在之前的处理中,这只相当于一个临时容器,处理完毕之后都会remove掉。

继续阅读“Spring中循环引用的处理-2”