The interface org.springframework.context.ApplicationContext
represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the aforementioned beans. The container gets its instructions on what objects to instantiate, configure, and assemble by reading configuration metadata. The configuration metadata is represented in XML, Java annotations, or Java code. It allows you to express the objects that compose your application and the rich interdependencies between such objects.
Several implementations of the ApplicationContext
interface are supplied out-of-the-box with Spring. In standalone applications it is common to create an instance ofClassPathXmlApplicationContext
or FileSystemXmlApplicationContext
. While XML has been the traditional format for defining configuration metadata you can instruct the container to use Java annotations or code as the metadata format by providing a small amount of XML configuration to declaratively enable support for these additional metadata formats.
For information about using other forms of metadata with the Spring container, see:
Annotation-based configuration: Spring 2.5 introduced support for annotation-based configuration metadata.
Java-based configuration: Starting with Spring 3.0, many features provided by the Spring JavaConfig project became part of the core Spring Framework. Thus you can define beans external to your application classes by using Java rather than XML files. To use these new features, see the@Configuration
,@Bean, @Import
and@DependsOn
annotations.
The following example shows the basic structure of XML-based configuration metadata:
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
id="..." class="...">
id="..." class="...">
A Spring IoC container manages one or more beans. These beans are created with the configuration metadata that you supply to the container, for example, in the form of XML
definitions.
Within the container itself, these bean definitions are represented as BeanDefinition
objects, which contain (among other information) the following metadata:
A package-qualified class name: typically the actual implementation class of the bean being defined.
Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
References to other beans that are needed for the bean to do its work; these references are also calledcollaborators ordependencies.
Other configuration settings to set in the newly created object, for example, the number of connections to use in a bean that manages a connection pool, or the size limit of the pool.
This metadata translates to a set of properties that make up each bean definition.
Property | Explained in... |
---|---|
class | Section 5.3.2, “Instantiating beans” |
name | Section 5.3.1, “Naming beans” |
scope | Section 5.5, “Bean scopes” |
constructor arguments | Section 5.4.1, “Dependency injection” |
properties | Section 5.4.1, “Dependency injection” |
autowiring mode | Section 5.4.5, “Autowiring collaborators” |
lazy-initialization mode | Section 5.4.4, “Lazy-initialized beans” |
initialization method | the section called “Initialization callbacks” |
destruction method | the section called “Destruction callbacks” |
In addition to bean definitions that contain information on how to create a specific bean, theApplicationContext
implementations also permit the registration of existing objects that are created outside the container, by users. This is done by accessing the ApplicationContext's BeanFactory via the methodgetBeanFactory()
which returns the BeanFactory implementationDefaultListableBeanFactory
.DefaultListableBeanFactory
supports this registration through the methodsregisterSingleton(..)
and registerBeanDefinition(..)
. However, typical applications work solely with beans defined through metadata bean definitions.
When you create a bean definition, you create a recipe for creating actual instances of the class defined by that bean definition. The idea that a bean definition is a recipe is important, because it means that, as with a class, you can create many object instances from a single recipe.
You can control not only the various dependencies and configuration values that are to be plugged into an object that is created from a particular bean definition, but also thescope of the objects created from a particular bean definition. This approach is powerful and flexible in that you canchoose the scope of the objects you create through configuration instead of having to bake in the scope of an object at the Java class level. Beans can be defined to be deployed in one of a number of scopes: out of the box, the Spring Framework supports five scopes, three of which are available only if you use a web-awareApplicationContext
.
The following scopes are supported out of the box. You can also create a custom scope.
Scope | Description |
---|---|
singleton |
(Default) Scopes a single bean definition to a single object instance per Spring IoC container. |
prototype |
Scopes a single bean definition to any number of object instances. |
request |
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 |
session |
Scopes a single bean definition to the lifecycle of an HTTP |
global session |
Scopes a single bean definition to the lifecycle of a global HTTP |
Thread-scoped beans | |
---|---|
As of Spring 3.0, a thread scope is available, but is not registered by default. For more information, see the documentation forSimpleThreadScope. For instructions on how to register this or any other custom scope, seethe section called “Using a custom scope”. |
When you use singleton-scoped beans with dependencies on prototype beans, be aware thatdependencies are resolved at instantiation time. Thus if you dependency-inject a prototype-scoped bean into a singleton-scoped bean, a new prototype bean is instantiated and then dependency-injected into the singleton bean. The prototype instance is the sole instance that is ever supplied to the singleton-scoped bean.
However, suppose you want the singleton-scoped bean to acquire a new instance of the prototype-scoped bean repeatedly at runtime. You cannot dependency-inject a prototype-scoped bean into your singleton bean, because that injection occurs onlyonce, when the Spring container is instantiating the singleton bean and resolving and injecting its dependencies. If you need a new instance of a prototype bean at runtime more than once, seeSection 5.4.6, “Method injection”
The request
, session
, andglobal session
scopes are only available if you use a web-aware SpringApplicationContext
implementation (such as XmlWebApplicationContext
). If you use these scopes with regular Spring IoC containers such as theClassPathXmlApplicationContext
, you get an IllegalStateException
complaining about an unknown bean scope.
To support the scoping of beans at the request
, session
, and global session
levels (web-scoped beans), some minor initial configuration is required before you define your beans. (This initial setup isnot required for the standard scopes, singleton and prototype.)
How you accomplish this initial setup depends on your particular Servlet environment..
If you access scoped beans within Spring Web MVC, in effect, within a request that is processed by the SpringDispatcherServlet
, or DispatcherPortlet
, then no special setup is necessary:DispatcherServlet
and DispatcherPortlet
already expose all relevant state.
If you use a Servlet 2.4+ web container, with requests processed outside of Spring's DispatcherServlet (for example, when using JSF or Struts), you need to add the followingjavax.servlet.ServletRequestListener
to the declarations in your web applicationsweb.xml
file:
... org.springframework.web.context.request.RequestContextListener ...
If you use an older web container (Servlet 2.3), use the provided javax.servlet.Filter
implementation. The following snippet of XML configuration must be included in theweb.xml
file of your web application if you want to access web-scoped beans in requests outside of Spring's DispatcherServlet on a Servlet 2.3 container. (The filter mapping depends on the surrounding web application configuration, so you must change it as appropriate.)
.. requestContextFilter org.springframework.web.filter.RequestContextFilter requestContextFilter /* ...
DispatcherServlet
, RequestContextListener
andRequestContextFilter
all do exactly the same thing, namely bind the HTTP request object to theThread
that is servicing that request. This makes beans that are request- and session-scoped available further down the call chain.
The Spring IoC container manages not only the instantiation of your objects (beans), but also the wiring up of collaborators (or dependencies). If you want to inject (for example) an HTTP request scoped bean into another bean, you must inject an AOP proxy in place of the scoped bean. That is, you need to inject a proxy object that exposes the same public interface as the scoped object but that can also retrieve the real, target object from the relevant scope (for example, an HTTP request) and delegate method calls onto the real object.
Note | |
---|---|
You do not need to use the |
The configuration in the following example is only one line, but it is important to understand the“why” as well as the “how” behind it.
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">
id="userPreferences" class="com.foo.UserPreferences" scope="session">
id="userService" class="com.foo.SimpleUserService">
name="userPreferences" ref="userPreferences"/>
To create such a proxy, you insert a child
element into a scoped bean definition. Seethe section called “Choosing the type of proxy to create” and Appendix E, XML Schema-based configuration.) Why do definitions of beans scoped at therequest
, session
, globalSession
and custom-scope levels require the
element ? Let's examine the following singleton bean definition and contrast it with what you need to define for the aforementioned scopes. (The followinguserPreferences
bean definition as it stands is incomplete.)
id="userPreferences" class="com.foo.UserPreferences" scope="session"/>
id="userManager" class="com.foo.UserManager">
name="userPreferences" ref="userPreferences"/>
In the preceding example, the singleton bean userManager
is injected with a reference to the HTTPSession
-scoped bean userPreferences
. The salient point here is that theuserManager
bean is a singleton: it will be instantiatedexactly once per container, and its dependencies (in this case only one, theuserPreferences
bean) are also injected only once. This means that theuserManager
bean will only operate on the exact sameuserPreferences
object, that is, the one that it was originally injected with.
This is not the behavior you want when injecting a shorter-lived scoped bean into a longer-lived scoped bean, for example injecting an HTTPSession
-scoped collaborating bean as a dependency into singleton bean. Rather, you need a singleuserManager
object, and for the lifetime of an HTTP Session
, you need a userPreferences
object that is specific to said HTTPSession
. Thus the container creates an object that exposes the exact same public interface as theUserPreferences
class (ideally an object that is a UserPreferences
instance) which can fetch the realUserPreferences
object from the scoping mechanism (HTTP request,Session
, etc.). The container injects this proxy object into theuserManager
bean, which is unaware that this UserPreferences
reference is a proxy. In this example, when a UserManager
instance invokes a method on the dependency-injected UserPreferences
object, it actually is invoking a method on the proxy. The proxy then fetches the realUserPreferences
object from (in this case) the HTTPSession
, and delegates the method invocation onto the retrieved realUserPreferences
object.
Thus you need the following, correct and complete, configuration when injectingrequest-
, session-
, andglobalSession-scoped
beans into collaborating objects:
id="userPreferences" class="com.foo.UserPreferences" scope="session">
id="userManager" class="com.foo.UserManager">
name="userPreferences" ref="userPreferences"/>
By default, when the Spring container creates a proxy for a bean that is marked up with the
element, a CGLIB-based class proxy is created.
Note: CGLIB proxies only intercept public method calls! Do not call non-public methods on such a proxy; they will not be delegated to the scoped target object.
Alternatively, you can configure the Spring container to create standard JDK interface-based proxies for such scoped beans, by specifyingfalse
for the value of the proxy-target-class
attribute of the
element. Using JDK interface-based proxies means that you do not need additional libraries in your application classpath to effect such proxying. However, it also means that the class of the scoped bean must implement at least one interface, and that all collaborators into which the scoped bean is injected must reference the bean through one of its interfaces.
id="userPreferences" class="com.foo.DefaultUserPreferences" scope="session">
proxy-target-class="false"
/>
id="userManager" class="com.foo.UserManager">
name="userPreferences" ref="userPreferences"/>
For more detailed information about choosing class-based or interface-based proxying, seeSection 9.6, “Proxying mechanisms”.
Spring AOP uses either JDK dynamic proxies or CGLIB to create the proxy for a given target object. (JDK dynamic proxies are preferred whenever you have a choice).
If the target object to be proxied implements at least one interface then a JDK dynamic proxy will be used. All of the interfaces implemented by the target type will be proxied. If the target object does not implement any interfaces then a CGLIB proxy will be created.
If you want to force the use of CGLIB proxying (for example, to proxy every method defined for the target object, not just those implemented by its interfaces) you can do so. However, there are some issues to consider:
final
methods cannot be advised, as they cannot be overridden.
As of Spring 3.2, it is no longer necessary to add CGLIB to your project classpath, as CGLIB classes are repackaged under org.springframework and included directly in the spring-core JAR. This means that CGLIB-based proxy support 'just works' in the same way that JDK dynamic proxies always have.
The constructor of your proxied object will be called twice. This is a natural consequence of the CGLIB proxy model whereby a subclass is generated for each proxied object. For each proxied instance, two objects are created: the actual proxied object and an instance of the subclass that implements the advice. This behavior is not exhibited when using JDK proxies. Usually, calling the constructor of the proxied type twice, is not an issue, as there are usually only assignments taking place and no real logic is implemented in the constructor.
To force the use of CGLIB proxies set the value of theproxy-target-class
attribute of the
element to true:
proxy-target-class="true">
To force CGLIB proxying when using the @AspectJ autoproxy support, set the 'proxy-target-class'
attribute of the
element totrue
:
proxy-target-class="true"/>
Note | |
---|---|
Multiple To be clear: using ' |
Spring AOP is proxy-based. It is vitally important that you grasp the semantics of what that last statement actually means before you write your own aspects or use any of the Spring AOP-based aspects supplied with the Spring Framework.
Consider first the scenario where you have a plain-vanilla, un-proxied, nothing-special-about-it, straight object reference, as illustrated by the following code snippet.
public class SimplePojo implements Pojo { public void foo() { // this next method invocation is a direct call on the 'this' reference this.bar(); } public void bar() { // some logic... } }
If you invoke a method on an object reference, the method is invoked directly on that object reference, as can be seen below.
public class Main { public static void main(String[] args) { Pojo pojo = new SimplePojo(); // this is a direct method call on the 'pojo' reference pojo.foo(); } }
Things change slightly when the reference that client code has is a proxy. Consider the following diagram and code snippet.
public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new SimplePojo()); factory.addInterface(Pojo.class); factory.addAdvice(new RetryAdvice()); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); } }
The key thing to understand here is that the client code inside the main(..)
of the Main
class has a reference to the proxy. This means that method calls on that object reference will be calls on the proxy, and as such the proxy will be able to delegate to all of the interceptors (advice) that are relevant to that particular method call. However, once the call has finally reached the target object, the SimplePojo
reference in this case, any method calls that it may make on itself, such asthis.bar()
or this.foo()
, are going to be invoked against thethis
reference, andnot the proxy. This has important implications. It means that self-invocation isnot going to result in the advice associated with a method invocation getting a chance to execute.
Okay, so what is to be done about this? The best approach (the term best is used loosely here) is to refactor your code such that the self-invocation does not happen. For sure, this does entail some work on your part, but it is the best, least-invasive approach. The next approach is absolutely horrendous, and I am almost reticent to point it out precisely because it is so horrendous. You can (choke!) totally tie the logic within your class to Spring AOP by doing this:
public class SimplePojo implements Pojo { public void foo() { // this works, but... gah! ((Pojo) AopContext.currentProxy()).bar(); } public void bar() { // some logic... } }
This totally couples your code to Spring AOP, and it makes the class itself aware of the fact that it is being used in an AOP context, which flies in the face of AOP. It also requires some additional configuration when the proxy is being created:
public class Main { public static void main(String[] args) { ProxyFactory factory = new ProxyFactory(new SimplePojo()); factory.adddInterface(Pojo.class); factory.addAdvice(new RetryAdvice()); factory.setExposeProxy(true); Pojo pojo = (Pojo) factory.getProxy(); // this is a method call on the proxy! pojo.foo(); } }
Finally, it must be noted that AspectJ does not have this self-invocation issue because it is not a proxy-based AOP framework.
As of Spring 2.0, the bean scoping mechanism is extensible. You can define your own scopes, or even redefine existing scopes, although the latter is considered bad practice and you cannot override the built-in singleton
and prototype
scopes.
自定义scope
To integrate your custom scope(s) into the Spring container, you need to implement the org.springframework.beans.factory.config.Scope
interface, which is described in this section. For an idea of how to implement your own scopes, see the Scope
implementations that are supplied with the Spring Framework itself and the Scope Javadoc, which explains the methods you need to implement in more detail.
The Scope
interface has four methods to get objects from the scope, remove them from the scope, and allow them to be destroyed.
The following method returns the object from the underlying scope. The session scope implementation, for example, returns the session-scoped bean (and if it does not exist, the method returns a new instance of the bean, after having bound it to the session for future reference).
Object get(String name, ObjectFactory objectFactory)
The following method removes the object from the underlying scope. The session scope implementation for example, removes the session-scoped bean from the underlying session. The object should be returned, but you can return null if the object with the specified name is not found.
Object remove(String name)
The following method registers the callbacks the scope should execute when it is destroyed or when the specified object in the scope is destroyed. Refer to the Javadoc or a Spring scope implementation for more information on destruction callbacks.
void registerDestructionCallback(String name, Runnable destructionCallback)
The following method obtains the conversation identifier for the underlying scope. This identifier is different for each scope. For a session scoped implementation, this identifier can be the session identifier.
String getConversationId()
After you write and test one or more custom Scope
implementations, you need to make the Spring container aware of your new scope(s). The following method is the central method to register a new Scope
with the Spring container:
void registerScope(String scopeName, Scope scope);
This method is declared on the ConfigurableBeanFactory
interface, which is available on most of the concrete ApplicationContext
implementations that ship with Spring via the BeanFactory property.
The first argument to the registerScope(..)
method is the unique name associated with a scope; examples of such names in the Spring container itself are singleton
and prototype
. The second argument to the registerScope(..)
method is an actual instance of the custom Scope
implementation that you wish to register and use.
Suppose that you write your custom Scope
implementation, and then register it as below.
Note | |
---|---|
The example below uses |
Scope threadScope = new SimpleThreadScope(); beanFactory.registerScope("thread", threadScope);
You then create bean definitions that adhere to the scoping rules of your custom Scope
:
id="..." class="..." scope="thread">
With a custom Scope
implementation, you are not limited to programmatic registration of the scope. You can also do the Scope
registration declaratively, using the CustomScopeConfigurer
class:
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">
class="org.springframework.beans.factory.config.CustomScopeConfigurer">
name="scopes">
key="thread">
class="org.springframework.context.support.SimpleThreadScope"/>
id="bar" class="x.y.Bar" scope="thread">
name="name" value="Rick"/>
id="foo" class="x.y.Foo">
name="bar" ref="bar"/>
Note | |
---|---|
When you place |
To interact with the container's management of the bean lifecycle, you can implement the Spring InitializingBean
and DisposableBean
interfaces. The container calls afterPropertiesSet()
for the former and destroy()
for the latter to allow the bean to perform certain actions upon initialization and destruction of your beans.
Tip | |
---|---|
The JSR-250 If you don't want to use the JSR-250 annotations but you are still looking to remove coupling consider the use of init-method and destroy-method object definition metadata. |
Internally, the Spring Framework uses BeanPostProcessor
implementations to process any callback interfaces it can find and call the appropriate methods. If you need custom features or other lifecycle behavior Spring does not offer out-of-the-box, you can implement a BeanPostProcessor
yourself. For more information, see Section 5.8, “Container Extension Points”.
In addition to the initialization and destruction callbacks, Spring-managed objects may also implement the Lifecycle
interface so that those objects can participate in the startup and shutdown process as driven by the container's own lifecycle.
The lifecycle callback interfaces are described in this section.
实例初始化后定义初始方法
The org.springframework.beans.factory.InitializingBean
interface allows a bean to perform initialization work after all necessary properties on the bean have been set by the container. The InitializingBean
interface specifies a single method:
void afterPropertiesSet() throws Exception;
It is recommended that you do not use the InitializingBean
interface because it unnecessarily couples the code to Spring. Alternatively, use the @PostConstruct
annotation or specify a POJO initialization method. In the case of XML-based configuration metadata, you use the init-method
attribute to specify the name of the method that has a void no-argument signature. For example, the following definition:
id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
public class ExampleBean { public void init() { // do some initialization work } }
...is exactly the same as...
id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements InitializingBean { public void afterPropertiesSet() { // do some initialization work } }
... but does not couple the code to Spring.
实例回收后调用的方法
Implementing the org.springframework.beans.factory.DisposableBean
interface allows a bean to get a callback when the container containing it is destroyed. The DisposableBean
interface specifies a single method:
void destroy() throws Exception;
It is recommended that you do not use the DisposableBean
callback interface because it unnecessarily couples the code to Spring. Alternatively, use the @PreDestroy
annotation or specify a generic method that is supported by bean definitions. With XML-based configuration metadata, you use the destroy-method
attribute on the
. For example, the following definition:
id="exampleInitBean" class="examples.ExampleBean" destroy-method="cleanup"/>
public class ExampleBean { public void cleanup() { // do some destruction work (like releasing pooled connections) } }
...is exactly the same as...
id="exampleInitBean" class="examples.AnotherExampleBean"/>
public class AnotherExampleBean implements DisposableBean { public void destroy() { // do some destruction work (like releasing pooled connections) } }
... but does not couple the code to Spring.