首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 软件管理 > 软件架构设计 >

流入FactoryBean失败分析+解决方案

2013-07-04 
注入FactoryBean失败分析+解决方案A {@Autowired B b} B implements FactoryBean {}假设我们有两个类:1、

注入FactoryBean失败分析+解决方案
A { @Autowired B b;} B implements FactoryBean {}

假设我们有两个类:

1、都是单例Bean;

2、A依赖于B; B是一个FactoryBean;

3、A先于B加载,否则就没有问题了。

?

分析:

1、容器启动时默认会预初始化单例Bean,初始化顺序是无序的,因为在Spring容器内部使用Map存储Bean定义;当然也可以开启如lazy-init,不过还是无序。

?

1.1、比如DefaultListableBeanFactory,使用preInstantiateSingletons方法进行预初始化单例Bean;如果是ClasspathXmlApplicationContext会在其如refresh时调用此方法进行预初始化单例Bean;

1.2、如果是FactoryBean,并且(非SmartFactoryBean且eagerInit=false),那么默认只实例化FactoryBean,不会调用getObject去获取其具体的Bean;如下所示?

if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {if (isFactoryBean(beanName)) {final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);boolean isEagerInit;if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {public Boolean run() {return ((SmartFactoryBean<?>) factory).isEagerInit();}}, getAccessControlContext());}else {isEagerInit = (factory instanceof SmartFactoryBean &&((SmartFactoryBean<?>) factory).isEagerInit());}if (isEagerInit) {getBean(beanName);}}else {getBean(beanName);}}

如果是SmartFactoryBean且是EagerInit(true),那么调用getBean得到FactoryBean对应的具体Bean,即调用FactoryBean.getObject获取;?否则只实例化FactoryBean,不会返回具体的Bean; 此处需要注意的是:FactoryBean会完成实例化、依赖注入、初始化整个逻辑,而不是后边咱们提到的只调用部分逻辑。

?

2、当容器实例化A后,开始注入B;因为我们通过@Autowired注入B;所以Spring使用的是AutowiredAnnotationBeanPostProcessor注入:

具体可参考我之前写的《Spring开闭原则的表现-BeanPostProcessor扩展点》。

?

3、AutowiredAnnotationBeanPostProcessor使用内部的AutowiredFieldElement进行注入,具体调用了beanFactory的如下代码:?

value = beanFactory.resolveDependency(descriptor, beanName, autowiredBeanNames, typeConverter);

?

4、 在我们的场景中会使用resolveDependency中的如下代码(部分)去查找候选Bean:?

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (matchingBeans.isEmpty()) {if (descriptor.isRequired()) {raiseNoSuchBeanDefinitionException(type, "", descriptor);}return null;}if (matchingBeans.size() > 1) {String primaryBeanName = determinePrimaryCandidate(matchingBeans, descriptor);if (primaryBeanName == null) {throw new NoUniqueBeanDefinitionException(type, matchingBeans.keySet());}if (autowiredBeanNames != null) {autowiredBeanNames.add(primaryBeanName);}return matchingBeans.get(primaryBeanName);}// We have exactly one match.Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();if (autowiredBeanNames != null) {autowiredBeanNames.add(entry.getKey());}return entry.getValue();}

大家可以看到,使用findAutowireCandidates去发现候选Bean:

?

1、如果没有找到,抛出之前说的没有找到Bean异常;

2、如果发现多个,但是需要一个,抛出发现多于一个Bean的异常;

3、否则注入一个。

?

5、此时需要去分析findAutowireCandidates方法,在此方法内其使用如下代码去查找候选Bean的名字,而且递归查找父BeanFactory中的:?

String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());

6、findAutowireCandidates委托给如下代码去查找:?

String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);

7、接着委托给如下代码 接着去查找匹配的Bean名字:?

try {RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// Only check bean definition if it is complete.if (!mbd.isAbstract() && (allowEagerInit ||((mbd.hasBeanClass() || !mbd.isLazyInit() || this.allowEagerClassLoading)) &&!requiresEagerInitForType(mbd.getFactoryBeanName()))) {// In case of FactoryBean, match object created by FactoryBean.boolean isFactoryBean = isFactoryBean(beanName, mbd);boolean matchFound = (allowEagerInit || !isFactoryBean || containsSingleton(beanName)) &&(includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type);if (!matchFound && isFactoryBean) {// In case of FactoryBean, try to match FactoryBean instance itself next.beanName = FACTORY_BEAN_PREFIX + beanName;matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);}if (matchFound) {result.add(beanName);}}}

因为我们的B是一个FactoryBean,而且B还未实例化,所以走:?

if (!matchFound && isFactoryBean) {// In case of FactoryBean, try to match FactoryBean instance itself next.beanName = FACTORY_BEAN_PREFIX + beanName;matchFound = (includeNonSingletons || mbd.isSingleton()) && isTypeMatch(beanName, type);}

8、此时具体要看isTypeMatch方法了:代码比较多,此时我只贴将执行的代码片段:?

// Check bean class whether we're dealing with a FactoryBean.if (FactoryBean.class.isAssignableFrom(beanClass)) {if (!BeanFactoryUtils.isFactoryDereference(name)) {// If it's a FactoryBean, we want to look at what it creates, not the factory class.Class<?> type = getTypeForFactoryBean(beanName, mbd);return (type != null && typeToMatch.isAssignableFrom(type));}else {return typeToMatch.isAssignableFrom(beanClass);}}

此处会调用Class<?> type = getTypeForFactoryBean(beanName, mbd); 去获取FactoryBean的类型。getTypeForFactoryBean方法的核心代码如下所示:?

FactoryBean<?> fb = (mbd.isSingleton() ?getSingletonFactoryBeanForTypeCheck(beanName, mbd) :getNonSingletonFactoryBeanForTypeCheck(beanName, mbd));if (fb != null) {// Try to obtain the FactoryBean's object type from this early stage of the instance.objectType.value = getTypeForFactoryBean(fb);if (objectType.value != null) {return objectType.value;}}// No type found - fall back to full creation of the FactoryBean instance.return super.getTypeForFactoryBean(beanName, mbd);

?

9、如果是单例Bean,执行getSingletonFactoryBeanForTypeCheck(beanName, mbd)获取FactoryBean:?

private FactoryBean getSingletonFactoryBeanForTypeCheck(String beanName, RootBeanDefinition mbd) {synchronized (getSingletonMutex()) {BeanWrapper bw = this.factoryBeanInstanceCache.get(beanName);if (bw != null) {return (FactoryBean) bw.getWrappedInstance();}if (isSingletonCurrentlyInCreation(beanName)) {return null;}Object instance = null;try {// Mark this bean as currently in creation, even if just partially.beforeSingletonCreation(beanName);// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.instance = resolveBeforeInstantiation(beanName, mbd);if (instance == null) {bw = createBeanInstance(beanName, mbd, null);instance = bw.getWrappedInstance();}}finally {// Finished partial creation of this bean.afterSingletonCreation(beanName);}FactoryBean fb = getFactoryBean(beanName, instance);if (bw != null) {this.factoryBeanInstanceCache.put(beanName, bw);}return fb;}}

从如上代码可以看到的是,只执行实例化,没有执行依赖注入和初始化。??

?

10、接着调用objectType.value = getTypeForFactoryBean(fb);获取FactoryBean包装的具体类型:?

return factoryBean.getObjectType();

?即通过getObjectType获取具体的类型。

??

结论

如上是整个注入FactoryBean的代码分析,即FactoryBean在实例化/依赖注入时做了特殊处理,所以会造成问题:

1、在容器初始化时,如果FactoryBean是单例Bean,默认只实例化、依赖注入和初始化FactoryBean,不会自动调用getObject返回具体Bean;

2、如果A依赖的FactoryBean B还没有创建,那么执行依赖注入时:只执行FactoryBean B的实例化,不执行执行依赖注入和初始化。?

?

---------------------------------------------------分割线------------------------------------------------?

接着看一下ProxyFactoryBean可能会遇到的问题。

?

问题1:ProxyBean

请参考《Spring 3.2.2 AOP引入方式集成测试的问题》?

    <context:component-scan base-package="com.myapp.aop.introduce" />    <bean id="forumService" p:target-ref="forumServiceTarget"        p:interceptorNames="pmonitor" p:proxyTarget/>
@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration(locations = { "/aop/introduce/applicationContext.xml" })public class ForumServiceTest{    @Autowired    @Qualifier(value = "forumService")    ForumService forumService;    @Test    public void test() {        forumService.removeForum(10);        forumService.removeTopic(1022);        Monitorable moniterable = (Monitorable)forumService;        moniterable.setMonitorActive(true);        forumService.removeForum(10);        forumService.removeTopic(1022);     }}

抛出org.springframework.beans.factory.NoSuchBeanDefinitionException的异常,指向forumService不存在

?

分析:??

1、首先此处spring容器会加载配置文件并先完成初始化,此时会实例化、依赖注入及初始化forumService这个ProxyFactoryBean(即是FactoryBean);即符合之前提到的结论中的【1】;所以此时forumService这个FactoryBean已经初始化完成了;但因为没有调用getObject 所以还未实例化具体的Bean;

2、当进行ForumServiceTest的依赖注入时(ForumService forumService;),此时会按照如上提到的步骤执行,当执行到第【9】步时,因为之前已经完成了ProxyFactoryBean的初始化,所以此时直接返回factoryBeanInstanceCache中的FactoryBean;

3、接着会调用其getObjectType得到具体Bean的类型:?

public Class<?> getObjectType() {synchronized (this) {if (this.singletonInstance != null) {return this.singletonInstance.getClass();}}Class[] ifcs = getProxiedInterfaces();if (ifcs.length == 1) {return ifcs[0];}else if (ifcs.length > 1) {return createCompositeInterface(ifcs);}else if (this.targetName != null && this.beanFactory != null) {return this.beanFactory.getType(this.targetName);}else {return getTargetClass();}}

3.1、首先判断singletonInstance是否已经创建了,此时因为没有调用getObject,所以还是null;

?

3.2、因此到了此步骤,getProxiedInterfaces会得到之前在配置文件中注入的“com.myapp.aop.introduce.Monitorable”,因此会返回这个;

?

所以spring容器断定你的Bean类型是“com.myapp.aop.introduce.Monitorable”,因此和你的ForumService不兼容,因此赋值是失败的。

?

解决方案:

1、使用depends-on,如<bean depends-on="forumService"/>,这样会在实例化a时,先实例化forumService,因为获取依赖时使用getBean(dependsOnBean);,即走的是完成流程,也不会有问题;

2、注入ApplicationContext,然后手工ctx.getBean("forumService", ForumService.class);? 这样会造成FactoryBean调用getObject返回具体Bean,即ForumService的代理,也是没问题的;

3、再写一个ProxyFactoryBean,实现SmartFactoryBean,且getEarlyInit()方法返回true也是可以解决这个问题的,不过比较麻烦,直接使用1/2即可。

?

---------------------------------------------------分割线------------------------------------------------?

接下来分析下?spring data jpa+shiro Realm时的问题吧,这个估计用过的都会遇到这个问题。

比如我写的:UserRealm依赖UserService,UserService依赖UserRepository接口。

?

1、此处假设先实例化UserRealm,此时会去查找并实例化UserService依赖,然后UserService接着去查找UserRepository依赖;

2、UserRealm查找依赖UserService没有问题,但是UserService查找UserRepository会有问题;

?

分析:

1、假设spring data jpa配置是spring-config.xml:?

    <jpa:repositories            base-package="com.sishuok.es.**.repository"            entity-manager-factory-ref="entityManagerFactory"            transaction-manager-ref="transactionManager">    </jpa:repositories>

那么spring data jpa 会在容器启动时使用org.springframework.data.jpa.repository.config.JpaRepositoryNameSpaceHandler去扫描com.sishuok.es.**.repository包下的所有继承org.springframework.data.repository.Repository的接口;这个大家可以去看下org.springframework.data.jpa.repository.config.JpaRepositoryNameSpaceHandler实现;

?

?

2、扫描后默认使用org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean,当然也可以在jpa:repositories中使用factory-name="code">public Class<? extends T> getObjectType() {return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface);}

?但是此时repositoryInterface并没有注入,参见第【9】步,所以返回的是Repository.class;

?

4、因此会注入失败。

?

?

解决方案和第一个一样:

1、使用depends-on,如<bean depends-on="userRepository"/>,这样会在实例化userRealm时,先实例化userRepository,因为获取依赖时使用getBean(dependsOnBean);,即走的是完成流程,也不会有问题;可以参见UserService的历史版本;(在UserRealm或UserService上都行,这个没有影响)

2、注入ApplicationContext,然后手工ctx.getBean("userRepository", UserRepository.class);? 这样会造成FactoryBean调用getObject返回具体Bean,也是没问题的;

?

因为我的UserRealm可能依赖的比较多,所以直接:ctx.getBeansOfType(SimpleBaseRepositoryFactoryBean.class);?可以解决依赖多个的问题,不需要每次加新的后加depends-on。

注入ApplicationContext,然后手工ctx.getBean("userRepository", UserRepository.class);? 这样会造成FactoryBean调用getObject返回具体Bean,也是没问题的;

?

?

   
必须的 
必须的
我非常希望你们有问题。哈哈  我现在都没有搞懂spring注解是怎么工作的,spring是怎么通过@去di的? 我现在都没有搞懂spring注解是怎么工作的,spring是怎么通过@去di的?

你可以参考我之前写的《Spring开闭原则的表现-BeanPostProcessor的扩展点》
http://jinnianshilongnian.iteye.com/blog/1489787
http://jinnianshilongnian.iteye.com/blog/1492424

是通过BeanPostProcessor

热点排行