如果有朋友 遇到Spring疑难杂症,小弟愿意帮忙分析及提出解决方案。
通过spring注入FactoryBean时可能会遇到找不到依赖的异常“Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException:”,但是很多朋友会说明明我配置了,怎么找不到呢?或者如果直接从Spring容器去getBean是能拿到的,为什么注入不行呢?所以此处给大家分析一下原因,希望解决一些朋友的的疑惑。
假设:
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" class="org.springframework.aop.framework.ProxyFactoryBean" p:interfaces="com.myapp.aop.introduce.Monitorable" p:target-ref="forumServiceTarget" p:interceptorNames="pmonitor" p:proxyTargetClass="true" />
@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 class="a" 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-class="com.sishuok.es.common.repository.support.SimpleBaseRepositoryFactoryBean" 注册自己的FactoryBean,
3、其内部getObjectType为:
public Class<? extends T> getObjectType() { return (Class<? extends T>) (null == repositoryInterface ? Repository.class : repositoryInterface); }
但是此时repositoryInterface并没有注入,参见第【9】步,所以返回的是Repository.class;
4、因此会注入失败。
解决方案和第一个一样:
1、使用depends-on,如<bean class="userRealm" 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。
---------------------------------------------------分割线------------------------------------------------
所以总的结论是:
如上是整个注入FactoryBean的代码分析,即FactoryBean在实例化/依赖注入时做了特殊处理,所以会造成问题:
1、在容器初始化时,如果FactoryBean是单例Bean,默认只实例化、依赖注入和初始化FactoryBean,不会自动调用getObject返回具体Bean;
2、如果A依赖的FactoryBean B还没有创建,那么执行依赖注入时:只执行FactoryBean B的实例化,不执行执行依赖注入和初始化。
总的解决方案是:
- 使用depends-on,如<bean class="userRealm" depends-on="userRepository"/>,这样会在实例化userRealm时,先实例化userRepository,因为获取依赖时使用getBean(dependsOnBean);,即走的是完成流程,也不会有问题;可以参见UserService的历史版本;(在UserRealm或UserService上都行,这个没有影响)
- 注入ApplicationContext,然后手工ctx.getBean("userRepository", UserRepository.class); 这样会造成FactoryBean调用getObject返回具体Bean,也是没问题的;
相关推荐
在实际项目中,我们可能需要在`FactoryBean`中处理更多的逻辑,比如依赖注入、属性设置等。可以通过在`getObject()`方法中使用`ApplicationContext`来获取其他bean并注入到新创建的对象中。 总的来说,`FactoryBean...
下面我们将逐步分析`FactoryBean`的相关测试代码: 1. **配置文件(spring.xml)** 在Spring的XML配置文件中,我们可以声明一个`FactoryBean`。例如,假设我们有一个名为`MyFactoryBean`的实现类,它继承了`...
本篇文章将探讨如何结合`FactoryBean`与Jmock库来实现动态Mock类的注入,以便于进行单元测试。 首先,我们需要了解`FactoryBean`的基本用法。`FactoryBean`的`getObject()`方法负责返回一个由工厂生产的对象,而...
通过这种方式,开发者可以更加灵活地控制对象的生命周期和行为,特别是在需要对某些服务进行特殊处理或者扩展时,`FactoryBean`提供了一种强大的解决方案。在实际开发中,`FactoryBean`常被用来创建复杂的对象,例如...
综上所述,通过实现`FactoryBean`接口,我们可以深度定制Spring容器中Bean的创建过程,这对于解决特定场景下的对象创建问题提供了强大的解决方案。在SSH笔记中,通过`FactoryBean`的实践,可以更好地理解和掌握...
在Spring框架中,FactoryBean是一个非常重要的概念,它允许我们自定义对象的创建方式,提供了...通过学习这部分内容,开发者能够更好地理解Spring IoC容器的工作原理,并能灵活运用FactoryBean解决复杂对象创建的问题。
FactoryBean:我们在使用Spring过程中一般都是使用基本的的配置,在Spring配置中,还有一种特殊的FactoryBean,这种bean,可以动态的帮我们创建我们需要的bean,如: ProxyFactoryBean,通用的用于获得AOP代理的工厂bean。...
为了保护这些信息不被未授权的用户访问,实施核心配置项的加密解决方案至关重要。本文将深入探讨一种基于Java的加密配置方法,通过分析`CryptPropertiesFactoryBean.java`这个文件来理解如何在源码级别实现这一功能...
Spring 中的 BeanFactory 和 FactoryBean BeanFactory 是 Spring 框架中的核心组件之一,负责管理 Bean 的生命周期,包括实例化、配置和注入对象之间的关系。它是 Spring IOC(控制反转)容器的核心组件,提供了...
在`@Bean`和`Spring的factoryBean`注入的情况下,`factoryBean`允许我们通过自定义工厂方法创建bean。`@Bean`相当于一个简化版的工厂bean,因为它直接在配置类中定义了bean的创建逻辑。而传统的`factoryBean`则是...
通过实现`FactoryBean`,我们可以控制Bean的实例化过程,从而实现更灵活的依赖注入。 首先,让我们来看一下`FactoryBean`的基本使用。在上述示例中,我们创建了一个名为`Car`的简单类,它代表我们要创建的对象。...
* BeanFactory提供了Bean的实例化、管理和依赖注入等功能,而FactoryBean可以生成其他Bean的实例。 * BeanFactory是Singleton的,而FactoryBean可以是Singleton也可以是Prototype。 在实际应用中,我们通常使用...
1.1.2 解决方案 1 1.1.3 工作原理 3 1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 ...
1.1.2 解决方案 1 1.1.3 工作原理 3 1.2 配置Spring IoC容器中的Bean 4 1.2.1 问题 4 1.2.2 解决方案 4 1.2.3 工作原理 4 1.3 调用构造程序创建Bean 14 1.3.1 问题 14 1.3.2 解决方案 14 1.3.3 ...
通过理解`FactoryBean`的工作原理和应用场景,开发者能够更好地定制Spring容器的行为,解决复杂对象创建和管理的问题。`factory-bean-demo`项目则是一个很好的学习资源,可以深入理解并实践`FactoryBean`的用法。
Spring 中 FactoryBean 中的 getObject() 方法实例解析 Spring 框架中 FactoryBean 是一个非常重要的概念,它提供了一种创建和管理 Bean 的机制。在 Spring 中,FactoryBean 是一个特殊的 Bean,它可以创建其他 ...
在Spring框架中,`FactoryBean`是一个至关重要的接口,它为Spring容器提供了创建对象的自定义逻辑。`FactoryBean`的使用使得我们可以控制对象的创建过程,甚至可以在对象被Spring管理的同时,添加额外的初始化或者...
在Spring框架中,属性注入是一种核心特性,它允许开发者在不编写硬编码依赖关系的代码情况下,将对象的依赖从内部解耦出来。当我们遇到一些特殊的对象,如由工厂方法创建,而非简单地通过`new`关键字实例化时,...
Spring中的FactoryBean代码示例 在Spring框架中,FactoryBean是一种特殊的Bean,它可以生成其他Bean的实例。今天我们来了解一下FactoryBean的实现和使用。 首先,让我们从SessionFactory说起。在使用SSH集成开发时...
Spring 通过 FactoryBean 配置 Bean 在 Spring 框架中,FactoryBean 是一种特殊的 Bean,它可以实现复杂的 Bean 实例化逻辑。通过 FactoryBean,我们可以将复杂的实例化逻辑封装起来,使得 Bean 的配置更加灵活和...