在使用Ibatis的时候,如果某个sql的定义出现在引用sql的定义之后的话,笨笨的ibatis是会报错的。。这让用惯了spring的人会感到烦躁,为什么ibatis不能和spring一样,做到xml定义的时候与顺序无关。。。但是 spring 真的能够做到完全与bean定义的顺序无关么?下面的代码,会让我们警醒下:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-autowire="byName">
<bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetA" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
<bean id="targetA" class="reference.A"></bean>
<bean id="targetB" class="reference.B"></bean>
<bean id="beforeAdvice" class="reference.BeforeAdvice" />
<bean id="beforeAdvisor" class="reference.BeforeAdvisor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetB" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
</beans>
一个简单的循环引用 + ProxyFactoryBean 定义。
启动spring的代码:
public class Main
{
public static void main(String[] args) throws IOException
{
ApplicationContext ac = new ClassPathXmlApplicationContext("/files/reference.xml");
InterfaceA a = (InterfaceA) ac.getBean("a");
a.ok();
}
}
接着就是悲剧的error:
Caused by: org.springframework.beans.factory.BeanCurrentlyInCreationException: Error creating bean with name 'a': org.springframework.beans.factory.FactoryBeanNotInitializedException: Cannot determine target class for proxy
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:147)
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:109)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1387)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:244)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:189)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireByName(AbstractAutowireCapableBeanFactory.java:1085)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1035)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:511)
... 39 more
好,那么我们换下bean定义的顺序吧:
<bean id="targetA" class="reference.A"></bean>
<bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetA" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
<bean id="targetB" class="reference.B"></bean>
<bean id="beforeAdvice" class="reference.BeforeAdvice" />
<bean id="beforeAdvisor" class="reference.BeforeAdvisor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="targetB" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
再run一次:
2010-7-4 23:09:09 org.springframework.context.support.AbstractApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@32fb4f: startup date [Sun Jul 04 23:09:09 CST 2010]; root of context hierarchy
2010-7-4 23:09:09 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [files/reference.xml]
2010-7-4 23:09:09 org.springframework.beans.factory.support.DefaultListableBeanFactory preInstantiateSingletons
信息: Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@1d7ad1c: defining beans [targetA,a,targetB,beforeAdvice,beforeAdvisor,b]; root of factory hierarchy
xxx
$Proxy1
成功了。。。。。
duo xi die ? 这是为什么呢?
这里的问题确实是出在bean定义的顺序上面。applicationContext 在refresh的时候,是按照bean定义的顺序来加载singleton bean 的(除开BeanPostProcessor,BeanPostProcessor 在singleton加载之前被bean化)。如果仅仅只是加载普通的Bean或者普通的FactoryBean的话,Spring已经通过某种方式很好的解决了singleton循环依赖的问题。
导致这个问题的原因,是由2个原因叠加产生的:
1 FactoryBean的getObject(),需要开发者自己去做处理。如果是new 个对象,那么这个对象就享受不到spring 的IOC 的好处,因为它脱离了spring 容器的管辖。 而ProxyFactoryBean 的 getObject() 在获取代理对象的时候,会对target的属性有依赖,如果target的某些值为空,会抛错。(这里的target 通指 targetName,target)
2 spring 再 bean化 bean的时候,是分为2步的,第一步可以简单的认为new个对象出来,第二步为这个new出来的对象设置属性。就是去容器里面把这个bean需要的其他bean给注入进来。在第2步的时候,注入给其他的bean的bean 可能没有被完全bean化,很有可能只是完成了第一步的bean,还是个“半成品”。但是在整个容器初始化结束的时候,这些“半成品”bean会被变成“合格品”。
1 + 2 ,恩,就是ProxyFactoryBean在getObject()的时候,依赖了一个“半成品”,结果就悲剧了。
private synchronized Object getSingletonInstance() {
if (this.singletonInstance == null) {
this.targetSource = freshTargetSource();
if (this.autodetectInterfaces && getProxiedInterfaces().length == 0 && !isProxyTargetClass()) {
// Rely on AOP infrastructure to tell us what interfaces to proxy.
Class targetClass = getTargetClass();
if (targetClass == null) {
throw new FactoryBeanNotInitializedException("Cannot determine target class for proxy");
}
setInterfaces(ClassUtils.getAllInterfacesForClass(targetClass, this.proxyClassLoader));
}
// Initialize the shared singleton instance.
super.setFrozen(this.freezeProxy);
this.singletonInstance = getProxy(createAopProxy());
}
return this.singletonInstance;
}
这里的targetSource 是ProxyFactoryBean 在设置target属性的时候被设置进去的,很可惜的是,这个时候的 target 指向的Bean 还在创建中,无法走到调用setTarget() 这一步。所以,这个时候的targetSource 就是个 EMPTY_TARGET_SOURCE,它返回的targetClass 就是null. 所以就失败了。。。。
原因找到了,那么怎么解决呢?
解决也有2个方法,就是针对导致问题的2个原因作出处理么?
A方案:
针对 ProxyFactoryBean 在getObject的时候,一定要求 targetSource的targetClass不为空,那么我就让他fresh下吧。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.0.xsd"
default-autowire="byName">
<bean id="a" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="targetA" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
<bean id="targetA" class="reference.A"></bean>
<bean id="targetB" class="reference.B"></bean>
<bean id="beforeAdvice" class="reference.BeforeAdvice" />
<bean id="beforeAdvisor" class="reference.BeforeAdvisor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="b" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="targetName" value="targetB" />
<property name="interceptorNames">
<list>
<value>beforeAdvisor</value>
</list>
</property>
</bean>
</beans>
把 target,换成targetName 就可以了
B方案:
因为spring 在解决循环依赖的时候,化了个好大好大的圈,以至于我们需要的某个属性迟迟还没有set进来。
那么我们可以把这个大大的圈分成N个小小的圈吗。。
既然spring初始化singleton bean是从xml文件第一个bean开始的(除开BeanPostProcessor),那么我们就好好利用下这个第一个Bean么。。就把bean定义的位置给换下吧。。
spring 还是和bean定义的顺序有关的。希望大家在使用spring的时候,有所警醒。这个几率的问题不是很大,在使用ProxyFactoryBean 的时候最好使用 targetName 属性。另外,在使用FactoryBean的时候,需要借鉴下ProxyFactoryBean所导致的问题,对于某些属性依赖的话,不要太相信spring会在那个时候给你设置进去。
分享到:
相关推荐
1、<bean>标签主要用来进行Bean定义; 2、alias用于定义Bean别名的; 3、import用于导入其他配置文件的Bean定义,这是为了加载多个配置文件,当然也可以把这些配置文件构造为一个数组(new String[] {“config1.xml...
3.6. bean定义的继承 3.7. 容器扩展点 3.7.1. 用BeanPostProcessor定制bean 3.7.1.1. 使用BeanPostProcessor的Hello World示例 3.7.1.2. RequiredAnnotationBeanPostProcessor示例 3.7.2. 用...
【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3 【第六章】 AOP 之 6.1 AOP基础 ——跟我学spring3 【第六章】 AOP 之 6.2 AOP的HelloWorld ——跟我学spring3 【第六章】 AOP 之 6.3 基于...
这会将bean定义的范围限定为每个Spring IoC容器一个实例(默认)。 原型 这将单个bean定义的范围限定为具有任意数量的对象实例。 要求 这将bean定义的范围限定为HTTP请求。 仅在可感知网络的Spring ...
在这里,我们分别定义了一个MessageSender对象(messageSenderImpl)和一个Before Advice对象(logBeforeAdvice),并定义了一个 org.springframework.aop.framework.ProxyFactoryBean对象(messageSender),...
4.11.1 使用Java类提供Bean定义信息 4.11.2 使用基于Java类的配置信息启动Spring容器 4.12 不同配置方式比较 4.13 小结 第5章 Spring容器高级主题 5.1 Spring容器技术内幕 5.1.1 内部工作机制 5.1.2 BeanDefinition ...
面试题包括以下十九部分:Java 基础、容器、多线程、反射、对象拷贝、Java Web 模块、异常、网络、设计模式、Spring/Spring MVC、Spring Boot/Spring Cloud、Hibernate、Mybatis、RabbitMQ、Kafka、Zookeeper、MySql...
—— 5.1 概述 5.2 SpEL基础5.3 SpEL语法5.4在Bean定义中使用EL6.1 AOP基础6.2 AOP的HelloWorld6.3 基于Schema的AOP6.4 基于@AspectJ的AOP 6.5 AspectJ切入点语法详解6.6 通知参数6.7 通知顺序6.8 切面实例化模型
1.17 【第五章】Spring表达式语言 之 5.4在Bean定义中使用EL—跟我学spring3 . . . . . . . . . . . . . . . .197 1.18 【第六章】 AOP 之 6.1 AOP基础 ——跟我学spring3 . . . . . . . . . . . . . . . . . . . ....
注意:使用注解的方式,最终通知和后置通知顺序换了,建议使用环绕通知 注解 配置 声明式事务管理 PlatFormTransactionManager:平台事务管理器:定义了commit/rollback Mybatis/jdbc:...
4.11.1 使用Java类提供Bean定义信息 4.11.2 使用基于Java类的配置信息启动Spring容器 4.12 不同配置方式比较 4.13 小结 第5章 Spring容器高级主题 5.1 Spring容器技术内幕 5.1.1 内部工作机制 5.1.2 BeanDefinition ...
Spring 模块构建在核心容器之上,核心容器定义了创建、配置和管理 bean 的方式,如图 1 所示。 组成 Spring 框架的每个模块(或组件)都可以单独存在,或者与其他一个或多个模块联合实现。每个模块的功能如下: ☆...
Spring Bean配置 18.4.6. LDAP属性和自定义UserDetails 19. JSP标签库 19.1. 声明Taglib 19.2. authorize标签 19.3. authentication 标签 19.4. accesscontrollist 标签 20. Java认证和授权服务(JAAS)供应...
Spring Bean 配置 19.4.6. LDAP 属性和自定义UserDetails 20. JSP 标签库 20.1. 声明Taglib 20.2. authorize 标签 20.3. authentication 标签 20.4. accesscontrollist 标签 21. Java 认证和授权服务...
第3~9行定义了一个数据源,其实现类是apache的BasicDataSource,第11~25行定义了Hibernate的会话工厂,会话工厂类用Spring提供的LocalSessionFactoryBean维护,它注入了数据源和资源映射文件,此外还通过一些键值...
Spring Bean配置 19.4.6. LDAP属性和自定义UserDetails 20. JSP标签库 20.1. 声明Taglib 20.2. authorize 标签 20.3. authentication 标签 20.4. accesscontrollist 标签 21. Java认证和授权服务...
其实还有更简单的方法,而且是更好的方法,使用合理描述参数和SQL语句返回值的接口(比如IUserOperation.class),这样现在就可以至此那个更简单,更安全的代码,没有容易发生的字符串文字和转换的错误.下面是详细...
3.意识到弹簧-让bean获取spring容器的服务 BeanNameAware可以获取容器中bean的名称 ApplicationContextAware当前的applicationContext,这也可以调用容器的服务 4. Bean的自动装配 byName根据属性名称自动装配-set...
select执行顺序? 答:from where group by having select order by Collection和Collections的区别? 答:Collection是集合类的父类,继承它的主要由set和list Collections是针对集合类的帮助类,它提供了一...
Spring容器中的bean定义冲突问题 同id的bean,后定义的覆盖先定义的 导入配置文件可以理解为将导入的配置文件复制粘贴到对应位置 导入配置文件的顺序与位置不同可能会导致最终程序运行结果不同