Spring framework中的beans
1.概述
- bean其实就是各个类实例化后的对象,即objects
- spring framework的IOC容器所管理的基本单元就是bean
- spring的IOC容器管理bean的实例化、依赖关系配置过程、bean组装过程(依据依赖关系进行组装)
- 使用spring的IOC容器管理beans,有三种配置beans之间的依赖关系的方法,分别是XML-based configuration、annotion-based configuration以及Java-based configuration,但是无论是这三种方法中的哪一种,都涉及到bean的以下属性(参见本文的第2小节内容或者官网reference document的第7.3节)
- IOC容器所管理的bean是有生命周期的,可以通过bean definition使得不同bean有不同的生命周期。默认情况下bean的生命周期是singleton的,与singleton相对应的是protype。除了singleton和protype这两个常用scope之外,IOC容器还支持session、request、application...等scope,开发者甚至可以自定义bean scope。定义不同scope的bean之间的依赖关系时,要注意不要出错,譬如一个singleton-scoped bean要依赖一个session scoped bean,就得定义session scope bean代理<aop:scoped-proxy/>,如果不定义代理,那么singleton scoped bean所使用的始终是同一个session scoped bean实例。
- 可以定义IOC容器在实例化bean或者销毁bean之前或之后的行为,有多种方法:
- (不常用)方法一:去编写bean相关的类时,可以使得bean类实现 Spring的
InitializingBean
andDisposableBean
接口,通过重写这些接口的afterPropertiesSet()
anddestroy()
函数,使得IOC容器在实例化相应bean之前或者销毁相应bean之后可以执行afterPropertiesSet()
anddestroy()函数中所定义的特定的行为。
- 方法二(基于xml-based configuration):基于xml-based configuration,使用bean的init-method和destroy-method属性
- 方法三(基于annotion configuration):使用
@PreDestroy和
- 方法四(基于Java based configuration)
- (不常用)方法一:去编写bean相关的类时,可以使得bean类实现 Spring的
2.附录
2.1附录一,bean scope
- 参考资料:官网reference documentation如下章节
- 学习心得:
- 概述:Table 7.3. Bean scopes
Scope Description (Default) Scopes a single bean definition to a single object instance per Spring IoC container.
Scopes a single bean definition to any number of object instances.
Scopes a single bean definition to the lifecycle of a single HTTP request; that is, each HTTP request has its own instance of a bean created off the back of a single bean definition. Only valid in the context of a web-aware Spring
ApplicationContext
.Scopes a single bean definition to the lifecycle of an HTTP
Session
. Only valid in the context of a web-aware SpringApplicationContext
.Scopes a single bean definition to the lifecycle of a global HTTP
Session
. Typically only valid when used in a Portlet context. Only valid in the context of a web-aware SpringApplicationContext
.Scopes a single bean definition to the lifecycle of a
ServletContext
. Only valid in the context of a web-aware SpringApplicationContext
.Scopes a single bean definition to the lifecycle of a
WebSocket
. Only valid in the context of a web-aware SpringApplicationContext
.-
关于singleton:
- 声明一个bean是singleton的,意味着在一个IOC容器中只能有该bean的一个实例对象(object),该容器中所有引用到该bean的位置所得到的都是该class的同一个实例(object)
- xml-based configuration中要按照如下方式配置,该bean就是singleton的:
<bean id="accountService" class="com.foo.DefaultAccountService"/> <!--上述配置代码和下面的配置代码等效,因为spring framework中xml-based configuration默认情况下bean都是singleton的,不用再显式指定 -->
<bean id="accountService" class="com.foo.DefaultAccountService" scope="singleton"/>
-
关于protype:
- 和singleton相对,如果把一个bean的范围设置为protype的,则意味着每次调用该bean对象时,都会重新生成一个该对象的实例。譬如其他bean依赖此protype-bean时,或者使用applicationContext.getBean()获取该protype-bean时,IOC容器都会创建一个新的bean实例。
- 怎么样定义一个bean是protype的,xml-based configuration如下:
<bean id="accountService" class="com.foo.DefaultAccountService" scope="prototype"/>
- 值得注意的一点是:IOC容器不管理protype-bean的销毁工作,所以client应该编写相应的代码,保证protype-bean被用完之后执行销毁操作,释放protype-bean所占用的资源
- 关于Request, session, global session, application, and WebSocket scopes
-
综述:bean的这些范围 are only available if you use a web-aware Spring
ApplicationContext
implementation (such asXmlWebApplicationContext
).- If you use these scopes with regular Spring IoC containers such as the
ClassPathXmlApplicationContext
, anIllegalStateException
will be thrown complaining about an unknown bean scope. - 如果想要使用上述这些scope,首先要进行相关的配置,譬如在web.xml文件中配置RequestContextListener等。(具体配置方法参见官方reference documentation的“”7.5.4 小节中的Initial web configuration部分”的内容)。如果你的project中使用spring framework中的spring web MVC模块,那么这些配置过程可以省略,因为spring web mvc模块中的dispatcherServlet已经对上述
request
,session
,globalSession
,application
, andwebsocket
等bean scope做出了关联,所以不再需要开发者作出相应的配置,但是如果你的工程中集成的是Struts2等三方框架,就需要按照相应教程先配置相关scope,否则如果不配置这些scope就直接使用,是会抛出异常的
- If you use these scopes with regular Spring IoC containers such as the
-
关于request scope bean
- 概述:将一个bean配置成request scope bean意味着每次HTTP request都会产生该bean的一个新的实例。 When the request completes processing, the bean that is scoped to the request is discarded【丢弃】.
- 例如:The Spring container creates a new instance of the
LoginAction
bean by using theloginAction
bean definition for each and every HTTP request. - xml-based configuration,配置一个bean的scope为request
<bean id="loginAction" class="com.foo.LoginAction" scope="request"/>
-
关于session scope bean
- 概述:将一个bean配置成session scope,意味着
- 例如:The Spring container creates a new instance of the
UserPreferences
bean by using theuserPreferences
bean definition for the lifetime of a single HTTPSession
.xml-based configuration,配置一个bean的scope为session -
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
-
关于globalSession scope bean(applies only in the context of portlet-based web applications.)
概述:将一个bean配置成globalSession scope,意味着
- The
globalSession
scope is similar to the standard HTTPSession
scope (described above), and applies only in the context of portlet-based web applications. - 例如:
- xml-based configuration,配置一个bean的scope为globalSession
<bean id="userPreferences" class="com.foo.UserPreferences" scope="globalSession"/>
关于application scope bean
-
综述:bean的这些范围 are only available if you use a web-aware Spring
概述:将一个bean配置成session scope,意味着该bean的生命周期是整个web application,相当于原码开发中ServletContext中的一个attribute的生存周期
- 例如:The Spring container creates a new instance of the
AppPreferences
bean by using theappPreferences
bean definition once for the entire web application. That is, theappPreferences
bean is scoped at theServletContext
level, stored as a regularServletContext
attribute. This is somewhat similar to a Spring singleton bean but differs in two important ways: It is a singleton perServletContext
, not per Spring 'ApplicationContext' (for which there may be several in any given web application), and it is actually exposed and therefore visible as aServletContext
attribute. - xml-based configuration,配置一个bean的scope为application
<bean id="appPreferences" class="com.foo.AppPreferences" scope="application"/>
关于websocket scope bean
-
如何配置两个不同scope的bean之间的依赖关系?
情境描述:injecting a shorter-lived scoped bean into a longer-lived scoped bean,即当一个bean依赖另外一个和他自己的scope不同的bean时(依赖一个比自己生命周期更短的bean时),要使用“代理”的方式进行依赖注入(例如xml-based configuration中使用<aop:scoped-proxy/>标签标示所依赖的bean是代理bean)
-
例子:xml-based configuration
- 将一个HTTP Session-scoped bean作为依赖注入到a singleton-scoped 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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- an HTTP Session-scoped bean exposed as a proxy -->
<bean id="userPreferences" class="com.foo.UserPreferences" scope="session">
<!-- instructs the container to proxy the surrounding bean -->
<!--方法一:声明为CGLIB proxies.
CGLIB proxies only intercept public method calls! Do not call non-public methods on such a proxy; they will not be delegated to the actual scoped target object.-->
<aop:scoped-proxy/><!--方法二:声明为standard JDK interface-based proxies
However, it also means that the class of the scoped bean must implement at least one in terface, and that all collaborators into which the scoped bean is injected must refere nce the bean through one of its interfaces..-->
<aop:scoped-proxy proxy-target-class="false"/></bean> <!-- a singleton-scoped bean injected with a proxy to the above bean -->
<bean id="userService" class="com.foo.SimpleUserService">
<!-- a reference to the proxied userPreferences bean -->
<property name="userPreferences" ref="userPreferences"/>
</bean>
</beans>
- 将一个HTTP Session-scoped bean作为依赖注入到a singleton-scoped bean中
-
Why do definitions of beans scoped at the
request
,session
,globalSession
and custom-scope levels require the<aop:scoped-proxy/>
element?- 如果上述例子中在IOC容器中定义session-scoped bean时没有使用<aop:scoped-proxy/>标签,那么就会出错,为什么呢?因为userService是一个singleton-scoped bean,该bean只在IOC容器初始化时被创建一次,他的依赖也是在IOC容器被初始化时被注入的,所以如果不用<aop:scoped-proxy/>标签声明session-scoped bean userPreference,那么后面每次调用userService时所使用的都是同一个userPreference实例,而不是每个新的会话注入一个新的userPreference实例.而如果你给a shorter-lived scoped bean 添加了<aop:scoped-proxy/>标签,那么IOC容器就知道要更新相应的实例into a longer-lived scoped bean。比如session-scope bean如果添加了<aop:scoped-proxy/>标签再注入到singleton-scoped bean中,那么每次新的会话更新时,就会更新singleton scoped bean中的session-scoped bean
- For more detailed information about choosing class-based or interface-based proxying, see Section 11.6, “Proxying mechanisms”.
-
可以自定义scope
- 概述:除了上述普通scope以及session、request等scope之外,开发者还可以自定义scope,也可以override上述已有scope
- 如何实现自定义scope?
- implement the
org.springframework.beans.factory.config.Scope
interface,、 - 实现上述接口的成员函数,如下所述:
Object get(String name, ObjectFactory objectFactory)//获取一个实例
Object remove(String name)//删除一个实例
void registerDestructionCallback(String name, Runnable destructionCallback)//销毁一个实例时将会调用这个函数
String getConversationId()//对于session-scoped bean而言,这个函数返回的是sessionId,对于request-scoped bean而言,是requestId
- implement the
-
实现了自定义的scope之后,如何使用自定义的scope?
-
step1,注册你自定义的scope,让IOC容器能够识别它,有两种方法,
方法一:使用Java程序注册自定义scope;
-
void registerScope(String scopeName, Scope scope);
-
example1,
Scope threadScope = new SimpleThreadScope();//SimpleThreadScope()是自定义scope类
beanFactory.registerScope("thread", threadScope);//tread是IOC容器中类似于singleton、protype、request、、、之类的名字,treadScope是你自定义的scope类的一个对象
-
- 方法二,在xml文件中注册自定义scope
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd"> <bean class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="thread">
<bean class="org.springframework.context.support.SimpleThreadScope"/>
</entry>
</map>
</property>
</bean> <bean id="bar" class="x.y.Bar" scope="session">
<property name="name" value="Rick"/>
<aop:scoped-proxy/>
</bean> <bean id="foo" class="x.y.Foo">
<property name="bar" ref="bar"/>
</bean> </beans>
-
step2,使用自定义scope定义IOC容器中的bean
<bean id="..." class="..." scope="thread">
-
2.2附录二,bean 的一些特性(nature)
2.2.1自定义bean被实例化之前以及destroy之后的行为
概述:
- 你的bean可以实现 the Spring
InitializingBean
andDisposableBean
interfaces. 重写这些接口的afterPropertiesSet()
和destroy()函数。 - 只要实现了上述接口并且实现了上述相应的函数,IOC容器在管理bean的时候就可以allow the bean to perform certain actions upon initialization【初始化】 and destruction of your beans.
- 除了上述通过实现相应接口来定义bean的init和destroy之前的行为的方法之外,还可以通过其他方法定义bean被初始化之前以及destroy之前的行为,比如xml-based configuration中通过bean标签的init-method/destroy-method属性来定义,或者annotion-based configuration中通过
@PostConstruct、
@PreDestroy来定义,再或者Java-based configuration中通过@Bean(
destroyMethod
=""initMethod=""
) - 可以通过上述四种方法定义每个bean各自的callback函数,也可以为整个IOC容器定义默认的init和destroy行为.例如,xml-based configuration中使用<beans default-init-method="" default-destroy-method=“”/>来定义该IOC容器中所有bean的默认init和destroy方法。且bean标签中的init-method以及destroy-method可以覆盖beans标签中的设置。
- 如果同时使用了多种方法去定义某个bean的init和destroy行为,那么将会按照如下顺序执行相应的init和destroy行为:
-
Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:
- Methods annotated with
@PostConstruct
-
afterPropertiesSet()
as defined by theInitializingBean
callback interface - A custom configured
init()
method
Destroy methods are called in the same order:
- Methods annotated with
@PreDestroy
-
destroy()
as defined by theDisposableBean
callback interface - A custom configured
destroy()
method
- Methods annotated with
-
- 你的bean可以实现 the Spring
- 方法一,使bean代码继承spring framework中
InitializingBean
andDisposableBean
interfaces. 重写这些接口的afterPropertiesSet()
和destroy()函数。example1, allows a bean to perform initialization work after all necessary properties on the bean have been set by the container. -
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() {
// do some initialization work
} }example2,allows a bean to perform initialization work after all necessary properties on the bean have been set by the container.
<bean id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() {
// do some initialization work
} } - 方法二,xml-based configuration中通过bean标签的init-method/destroy-method属性来定义。 example1,you use the
init-method
attribute to specify the name of the method that has a void no-argument signature.<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean { public void init() {
// do some initialization work
} }example2,
<bean id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup() {
// do some destruction work (like releasing pooled connections)
} } - 方法三,annotion-based configuration中通过
@PostConstruct、
@PreDestroy来定义
- 方法四,者Java-based configuration中通过@Bean(
destroyMethod
=""initMethod=""
) - 整个IOC容器的所有beans的默认init和destroy行为:可以使用<beans default-init-method="" default-destroy-method=“”/> 来定义整个IOC容器中的所有bean的init和destroy行为。这样定义了之后,IOC容器在实例化该容器的某个bean或者destroy该容器中的某个bean时,如果该bean拥有<beans>标签中default-init-method或者default-destroy-method属性所定义的成员方法,则相应bean中相应的成员方法被执行。
- example1,
public class DefaultBlogService implements BlogService { private BlogDao blogDao; public void setBlogDao(BlogDao blogDao) {
this.blogDao = blogDao;
} // this is (unsurprisingly) the initialization callback method
public void init() {
if (this.blogDao == null) {
throw new IllegalStateException("The [blogDao] property must be set.");
}
} }public class ExampleBean { public void init() {
// do some initialization work
} }<beans default-init-method="init"> <bean id="blogService" class="com.foo.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
<bean id="exampleInitBean" class="examples.ExampleBean"/></beans>
- example1,