IoC is also known as dependency injection (DI). It is a process whereby objects define their dependencies (that is, the other objects they work with) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method.
IoC也称为依赖注入(DI)。这是一个过程,在此过程中,对象仅通过构造参数(即构造器注入)
,工厂方法的参数
或在对象被构造之后设置对象属性值(即setter注入,或称Field注入)
或从工厂方法返回实例
。
org.springframework.beans
和 org.springframework.context
两个包是Spring IoC容器的基础。 BeanFactory
接口有能力提供管理管理任何对象的配置机制。 ApplicationContext
是 BeanFactory
的子类。提供以下能力:
WebApplicationContext
总之, BeanFactory
提供了 配置框架(Configuration framework
和 基础功能(basic functionality)
, ApplictionContext
添加了更多企业功能。 ApplicationContext
是 BeanFactory
的完整超集。
在Spring框架中,Bean是由Spring IoC容器实例化,组装以及其他管理的对象。
The org.springframework.context.ApplicationContext
interface represents the Spring IoC container and is responsible for instantiating, configuring, and assembling the 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 lets you express the objects that compose your application and the rich interdependencies between those objects.
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlLzAvMjAyMC9wbmcvMTA1ODQ4LzE1OTUxNjAxMDg1ODAtODdjMmNjMjUtNTkyMy00MjU0LTk3MGUtODYyYzhmMzAyZWY1LnBuZw?x-oss-process=image/format,png#align=left&display=inline&height=296&margin=[object Object]&originHeight=296&originWidth=498&size=0&status=done&style=none&width=498)
如上图所示,配置元信息可以让IoC容器在你的应用中实例化、配置和组装对象。
Java configuration typically uses @Bean
-annotated methods within a @Configuration
class.
You can use Spring’s integration with AspectJ to configure objects that have been created outside the control of an IoC container.See.
The location path or paths supplied to an ApplicationContext
constructor are resource strings that let the container load configuration metadata from a variety of external resources, such as the local file system, the Java CLASSPATH
, and so on.
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
通过,可以使用
../
表示父目录,但是不推荐这么做。通常使用classpath:/config/services.xml
进行配置。
// create and configure beans
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
// retrieve configured instance
PetStoreService service = context.getBean("petStore", PetStoreService.class);
// use configured instance
List<String> userList = service.getUsernameList();
GenericApplicationContext
是最灵活的变体,代码如下:
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
可以在同一 ApplicationContext
上混合和匹配此类读取器代理,从各种配置文件读取Bean元信息。
在容器内部,这些Bean元数据是通过 BeanDefinition
对象表示。其中包含以下数据:
Property | Explained in… |
---|---|
Class | Instantiating Beans |
Name | Naming Beans |
Scope | Bean Scopes |
Constructor arguments | Dependency Injection |
Properties | Dependency Injection |
Autowiring mode | Autowiring Collaborators |
Lazy initialization mode | Lazy-initialized Beans |
Initialization method | Initialization Callbacks |
Destruction method | Destruction Callbacks |
DefaultListableBeanFactory
supports this registration through the registerSingleton(..)
and registerBeanDefinition(..)
methods. However, typical applications work solely with beans defined through regular bean definition metadata.
Bean metadata and manually supplied singleton instances need to be registered as early as possible, in order for the container to properly reason about them during autowiring and other introspection steps. While overriding existing metadata and existing singleton instances is supported to some degree, the registration of new beans at runtime (concurrently with live access to the factory) is not officially supported and may lead to concurrent access exceptions, inconsistent state in the bean container, or both.
Bean元信息需要尽早注册,以便容器在自动装配和其他自省步骤期间对它们进行适当的推理。虽然在某种程序上支持覆盖现有的元信息和已存在的单例Bean,但在运行时注册新的Bean(与工厂的实时访问同时进行) 不受官方支持,有可能出现并发访问异常、bean容器中状态不一致或者两者都有。
<alias name="fromName" alias="toName"/>
You can use the Class
property in one of two ways:
Typically, to specify the bean class to be constructed in the case where the container itself directly creates the bean by calling its constructor reflectively, somewhat equivalent to Java code with the new
operator.
To specify the actual class containing the static
factory method that is invoked to create the object, in the less common case where the container invokes a static
factory method on a class to create the bean. The object type returned from the invocation of the static
factory method may be the same class or another class entirely.
Inner class names
If you want to configure a bean definition for astatic
nested class, you have to use the binary name of the nested class.
For example, if you have a class calledSomeThing
in thecom.example
package, and thisSomeThing
class has astatic
nested class calledOtherThing
, the value of theclass
attribute on a bean definition would becom.example.SomeThing$OtherThing
.
Notice the use of the$
character in the name to separate the nested class name from the outer class name.
When you create a bean by the constructor approach, all normal classes are usable by and compatible with Spring.
<bean id="clientService"
class="examples.ClientService"
factory-method="createInstance"/>
public class ClientService {
private static ClientService clientService = new ClientService();
private ClientService() {}
public static ClientService createInstance() {
return clientService;
}
}
找出特定Bean的实际运行时类型的推荐方法是通过 BeanFactory#getType
调用指定Bean名称。这个接口考虑了所有可能存在的情况,并返回了针对相同Bean名称调用返回的对象的类型。
Dependency injection (DI) is a process whereby objects define their dependencies (that is, the other objects with which they work) only through constructor arguments, arguments to a factory method, or properties that are set on the object instance after it is constructed or returned from a factory method. The container then injects those dependencies when it creates the bean. This process is fundamentally the inverse (hence the name, Inversion of Control) of the bean itself controlling the instantiation or location of its dependencies on its own by using direct construction of classes or the Service Locator pattern.
使用 DI 原则的代码更加整洁,并且当对象具有其依赖关系时,解耦更加有效。对象不查找其依赖项,也不知道依赖项的位置或类。因此,您的类变得更容易测试,特别是当依赖关系位于接口或抽象基类上时,这允许在单元测试中使用存根或模拟实现。
DI 有两个主要的变体: 基于构造函数的依赖注入和基于 setter 的依赖注入。
基于构造函数的 DI 是通过容器调用具有许多参数的构造函数来实现的,每个参数表示一个依赖项。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on a MovieFinder
private MovieFinder movieFinder;
// a constructor so that the Spring container can inject a MovieFinder
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
Constructor argument resolution matching occurs by using the argument’s type.
构造函数参数解析是以参数的类型为依据进行匹配。
对于使用type属性显示指定构造函数参数类型,则容器可以对简单类型使用类型匹配。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg type="int" value="7500000"/>
<constructor-arg type="java.lang.String" value="42"/>
bean>
可以使用 index
属性显示指定构造函数参数的索引。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg index="0" value="7500000"/>
<constructor-arg index="1" value="42"/>
bean>
可以使用参数名消除值岐义。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
bean>
为了实现这个功能,必须在编译代码时启用调试标志(debug flag),以便Spring可以从构造函数中查找参数名称。如果你不想使用调试标签编译代码,可以使用注解 @constructorproperties
显示地命名构造函数参数。
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
在调用无参数构造函数或无参静态工厂方法实例化Bean之后,容器通过setter方法实现对Bean的依赖注入。
public class SimpleMovieLister {
// the SimpleMovieLister has a dependency on the MovieFinder
private MovieFinder movieFinder;
// a setter method so that the Spring container can inject a MovieFinder
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// business logic that actually uses the injected MovieFinder is omitted...
}
你可以通过 BeanDefinition
配置依赖,并通过PropertyEditor
实例将属性从一种格式转换为另一种格式。
Since you can mix constructor-based and setter-based DI, it is a good rule of thumb to use constructors for mandatory dependencies and setter methods or configuration methods for optional dependencies. Note that use of the @Required annotation on a setter method can be used to make the property be a required dependency; however, constructor injection with programmatic validation of arguments is preferable.
由于可以混合使用基于构造器注入和基于setter注入,因此,对于强制依赖项使用构造器注入,而对于可选依赖推荐使用setter注入或配置方法。可以在setter方法上使用注解@Required
让可选依赖成为强制依赖。但是,最好使用带参的程序验证的构造器注入。
The Spring team generally advocates constructor injection, as it lets you implement application components as immutable objects and ensures that required dependencies are not null. Furthermore, constructor-injected components are always returned to the client (calling) code in a fully initialized state. As a side note, a large number of constructor arguments is a bad code smell, implying that the class likely has too many responsibilities and should be refactored to better address proper separation of concerns.
Spring团队提倡构造器注入,因为它允许用户将应用组件实现为不可变对象并且确保依赖非空。构造器注入的组件始终以完全实例化的状态返回给客户端(调用的)代码。附带说明,大量的构造函数参数不是一种好的代码,有可能这个类承担太多的职责,应该进行重构以更好的解决关注点分离问题。
Setter injection should primarily only be used for optional dependencies that can be assigned reasonable default values within the class. Otherwise, not-null checks must be performed everywhere the code uses the dependency. One benefit of setter injection is that setter methods make objects of that class amenable to reconfiguration or re-injection later. Management through JMX MBeans is therefore a compelling use case for setter injection.
Setter注入应该主要用于可选的依赖,这些依赖可以在类中被分配合理的默认值。否则,必须在代码使用依赖项的所有地方执行非空检查。setter注入有一个好处是setter方法使该类的对象容易在以后重新配置或注入。因此,通过JMX mBean进行管理是一个令人注目的案例。
容器执行Bean依赖解析如下:
ApplicationContext
。可以通过 XML
、 Java Code
、 注解
。int
, long
, String
, boolean
。Spring容器在创建容器时会校验每个Bean的配置。当容器被创建时,单例Bean和预实例化(pre-instantiated)的Bean也会被创建。
如果主要使用基于构造器注入,则有可能创建不可解析的循环依赖场景。
如果A类需要通过构造函数注入的 b 类实例,而 b 类需要通过构造函数注入的 a 类实例。如果将 bean 配置为类 a 和类 b 相互注入,那么 Spring IoC 容器在运行时检测到这个循环引用,并抛出 BeanCurrentlyInCreationException。
一个可能的解决方案是改为setter注入。尽管不推荐使用setter注入。
<bean id="exampleBean" class="examples.ExampleBean">
<property name="beanOne">
<ref bean="anotherExampleBean"/>
property>
<property name="beanTwo" ref="yetAnotherBean"/>
<property name="integerProperty" value="1"/>
bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public void setBeanOne(AnotherBean beanOne) {
this.beanOne = beanOne;
}
public void setBeanTwo(YetAnotherBean beanTwo) {
this.beanTwo = beanTwo;
}
public void setIntegerProperty(int i) {
this.i = i;
}
}
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg>
<ref bean="anotherExampleBean"/>
constructor-arg>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg type="int" value="1"/>
bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
private AnotherBean beanOne;
private YetAnotherBean beanTwo;
private int i;
public ExampleBean(
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
this.beanOne = anotherBean;
this.beanTwo = yetAnotherBean;
this.i = i;
}
}
静态工厂方法实际与构造函数完全相同。
你可以通过 xml
配置 java.util.Properties
实例。
<bean id="mappings"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
value>
property>
bean>
Spring容器将
元素内的文本使用 PropertyEditor
机制转换为 java.util.Properties
实例。这是一个方便的快捷方式。
引用父容器对象
<bean id="accountService" class="com.something.SimpleAccountService">
bean>
<bean id="accountService"
property>
bean>
内部Bean定义不需要定义 ID
或 name
。如果指定,容器则不使用此值作为标识符。容器在创建时忽略 scope
标志,因为内部bean总是匿名的且总是由外部bean创建。
与Java中的集合相对应。,
<bean id="moreComplexObject" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">[email protected]prop>
<prop key="support">[email protected]prop>
<prop key="development">[email protected]prop>
props>
property>
<property name="someList">
<list>
<value>a list element followed by a referencevalue>
<ref bean="myDataSource" />
list>
property>
<property name="someMap">
<map>
<entry key="an entry" value="just some string"/>
map>
property>
<property name="someSet">
<set>
<value>just some stringvalue>
<ref bean="myDataSource" />
set>
property>
bean>
Spring可以让子元素从父元素继承和覆盖值。
Java5
支持泛型。Spring框架通过反射获取泛型信息。因此可以将字符串转换对应的类型。
<beans>
<bean id="something" class="x.y.SomeClass">
<property name="accounts">
<map>
<entry key="one" value="9.99"/>
<entry key="two" value="2.75"/>
<entry key="six" value="3.99"/>
map>
property>
bean>
beans>
public class SomeClass {
private Map<String, Float> accounts;
public void setAccounts(Map<String, Float> accounts) {
this.accounts = accounts;
}
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="john-classic" class="com.example.Person">
<property name="name" value="John Doe"/>
<property name="spouse" ref="jane"/>
</bean>
<bean name="john-modern"
class="com.example.Person"
p:name="John Doe"
p:spouse-ref="jane"/>
<bean name="jane" class="com.example.Person">
<property name="name" value="Jane Doe"/>
</bean>
</beans>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="beanTwo" class="x.y.ThingTwo"/>
<bean id="beanThree" class="x.y.ThingThree"/>
<!-- traditional declaration with optional argument names -->
<bean id="beanOne" class="x.y.ThingOne">
<constructor-arg name="thingTwo" ref="beanTwo"/>
<constructor-arg name="thingThree" ref="beanThree"/>
<constructor-arg name="email" value="[email protected]"/>
</bean>
<!-- c-namespace declaration with argument names -->
<bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
c:thingThree-ref="beanThree" c:email="[email protected]"/>
</beans>
表明某个Bean在实例化之前需要依赖其他Bean才能完成实例化过程。
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />
默认情况下, ApplictionContext
对单例和Spring默认初始化Bean都采用饿汉式初始化。这是因为初始化过程可以早点发现错误并解决。当前,也可以对Bean指定懒加载方式。通过 lazy-init
指定
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
<bean name="not.lazy" class="com.something.AnotherBean"/>
延迟初始化的bean被注入到其他地方的单例Bean中,这个单例bean不是延迟初始化。
自动装配有以下优点:
自动装配有四种模式:
| no
默认值。Bean引用需要使用 ref 元素定义。对于大型系统,不推荐更改默认配置,因为显示指定协作者可以提供更好的控制和清晰度。在某种程序上,它记录了一个系统的结构 |
---|
byName |
byType |
constructor |
你有如下几种选择:
autowire-candidate
为 false
来避免为Bean定义进行自动装配primary
In most application scenarios, most beans in the container are singletons. When a singleton bean needs to collaborate with another singleton bean or a non-singleton bean needs to collaborate with another non-singleton bean, you typically handle the dependency by defining one bean as a property of the other. A problem arises when the bean lifecycles are different. Suppose singleton bean A needs to use non-singleton (prototype) bean B, perhaps on each method invocation on A. The container creates the singleton bean A only once, and thus only gets one opportunity to set the properties. The container cannot provide bean A with a new instance of bean B every time one is needed.
在大多数应用场景中,容器中的大多数bean是单例的。 当一个单例Bean需要与另一个单例Bean协作或一个非单例Bean需要与另一个非单例Bean协作时,通常可以通过将一个Bean定义为另一个Bean的属性来处理依赖关系。 当bean的生命周期不同时会出现问题。 假设单例bean A可能需要使用非单例(原型)bean B,也许是在A的每个方法调用上使用的。容器仅创建一次单例bean A,因此只有一次机会来设置属性。 每次需要一个容器时,容器都无法为bean A提供一个新的bean B实例。
A solution is to forego some inversion of control. You can make bean A aware of the container by implementing theApplicationContextAware
interface, and by making agetBean("B")
call to the container ask for (a typically new) bean B instance every time bean A needs it. The following example shows this approach:
一种解决方式是放弃控制反转。通过实现ApplicationContextAware
接口,并通过对容器进行getBean("B")
调用来使Bean A每次请求(通常是新的)Bean B实例。示例如下:
// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;
// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
public class CommandManager implements ApplicationContextAware {
private ApplicationContext applicationContext;
public Object process(Map commandState) {
// grab a new instance of the appropriate Command
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
protected Command createCommand() {
// notice the Spring API dependency!
return this.applicationContext.getBean("command", Command.class);
}
public void setApplicationContext(
ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
前面内容并不可取,因为业务代码耦合Spring框架。
方法注入是Spring IoC容器高级特性。
查找方法注入是容器重写容器所管理的Beans的方法以及返回容器中另一个命名的Bean的能力。查找通常涉及 原型
。Spring框架通过使用 CGLIB
字节码生成的能力动态生成覆盖该方法的子类,从而实现此方法的注入。
<bean id="myCommand" class="com.clarencezero.lookupmethodinject.Command" scope="prototype">
<property name="state" value="1" />
bean>
<bean id="commandManager" class="com.clarencezero.lookupmethodinject.CommandManager">
<lookup-method name="createCommand" bean="myCommand"/>
bean>
该查找方法不能与工厂方法一起工作,特别是不能与配置类中的 @Bean
方法一起工作,因为在这种情况下,容器不负责创建实例,因此不能动态创建运行时生成的子类。
package fiona.apple;
// no more Spring imports!
public abstract class CommandManager {
public Object process(Object commandState) {
// grab a new instance of the appropriate Command interface
Command command = createCommand();
// set the state on the (hopefully brand new) Command instance
command.setState(commandState);
return command.execute();
}
// okay... but where is the implementation of this method?
protected abstract Command createCommand();
}
如果该方法是抽象的,则动态生成的子类实现该方法。
com.clarencezero.lookupmethodinject.CommandManager$$EnhancerBySpringCGLIB$$5ad7b9ae@3cd1f1c8
com.clarencezero.lookupmethodinject.CommandManager$$EnhancerBySpringCGLIB$$5ad7b9ae@3cd1f1c8
com.clarencezero.lookupmethodinject.Command@3a4afd8d
com.clarencezero.lookupmethodinject.Command@3a4afd8d
com.clarencezero.lookupmethodinject.Command@3a4afd8d
在基于注解中,可以使用 @Lookup
声明一个查找方法
public abstract class CommandManager {
public Object process(Object commandState) {
Command command = createCommand();
command.setState(commandState);
return command.execute();
}
@Lookup("myCommand")
protected abstract Command createCommand();
}
访问不同作用域的目标 bean 的另一种方法是 ObjectFactory/Provider
注入点。
您可能还会发现 ServiceLocatorFactoryBean
(在 org.springframework.beans.factory.config 包中)非常有用。
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 control the scope of the objects created from a particular bean definition. This approach is powerful and flexible, because you can choose 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. The Spring Framework supports six scopes, four of which are available only if you use a web-aware
ApplicationContext
. You can also create a custom scope.
Spring框架支持六种类型作用域。其中四个作用域只有在web的上下文才用到。
singleton | (默认值)为每个 Spring IoC 容器将一个 bean 定义作用于一个对象实例 |
---|---|
prototype | 将单个 bean 定义作用于任意数量的对象实例。 |
request | 将单个 bean 定义作用于单个 HTTP 请求的生命周期。也就是说,每个 HTTP 请求都有自己的 bean 实例,这个 bean 是从单个 bean 定义后面创建的。仅在感知 web 的 Spring application 上下文中有效。 |
session | 将单个 bean 定义作用于 HTTP 会话的生命周期。仅在web 的 Spring application 上下文中有效。 |
application | Scopes a single bean definition to the lifecycle of a ServletContext . Only valid in the context of a web-aware Spring ApplicationContext . |
将单个 bean 定义作用于 ServletContext 的生命周期。仅在web 的application 上下文中有效。 | |
websocket | |
将一个 bean 定义作为 WebSocket 生命周期的范围。仅在 web 的 Spring application 上下文中有效。 |
有且只有一个。对于这个命名的Bean后续请求和引用都返回缓存中的对象。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlLzAvMjAyMC9wbmcvMTA1ODQ4LzE1OTUzMzE2ODcwNTItZDlhNjcyNGUtNWE5ZC00YTQ1LWI2MzAtNmY1MTc3ZjEzMmI0LnBuZw?x-oss-process=image/format,png#align=left&display=inline&height=398&margin=[object Object]&originHeight=398&originWidth=800&size=0&status=done&style=none&width=800)
Spring的 singleton
概念不同于设计模式定义的 单例模式
。设计模式对对象的作用域进行硬编码(hard-codes),每个Classloader只能创建特定类实例。Spring的单例则描述为容器和每个Bean实例。这意味着,如果在Spring定义一个单例,那么这个容器将创建有且仅有一个实例。单例是Spring默认作用域。
每次创建都会创建新的实例对象。通常,应该为所有有状态的bean使用原型,为无状态bean使用单例。
![](https://imgconvert.csdnimg.cn/aHR0cHM6Ly9jZG4ubmxhcmsuY29tL3l1cXVlLzAvMjAyMC9wbmcvMTA1ODQ4LzE1OTUzMzIyMTQ5NTktMDRlOGQxMTUtN2FhMC00NzQ3LWFlZmMtNmUyY2EyZTA5NzIzLnBuZw?x-oss-process=image/format,png#align=left&display=inline&height=397&margin=[object Object]&originHeight=397&originWidth=800&size=0&status=done&style=none&width=800)
与其他范围不同,Spring不会管理原型Bean的生命周期。容器实例化、配置并组装成一个原型对象,然后将其交给客户端,而不再进一步记录原型实例。初始化生命周期回调方法无论什么类型作用域都会被调用,但在原型模式化,销毁生命周期回调则不会调用。用户必须手动清理原型对象,并释放资源。 要让Spring容器释放原型Bean所持有的资源,可以尝试使用定义的Bean后置处理器
。
如果直接注入,则原型Bean将会是仅有的一个。如果你希望单例Bean在运行时重复获取原型Bean的新实例对象,不能通过依赖注入,因为注入只会出现一次。如果需要每次获取原型Bean,参照方法注入。
这些域只用做 web
环境。如果与 ClassPathXmlApplicationContext
一起使用,则会抛出 IllegalStateException
异常。
为了支持在请求、会话、应用程序和 websocket 级别(web 范围内的 bean)对 bean 进行范围限定,在定义 bean 之前需要进行一些小的初始配置。(标准作用域: 单例和原型不需要这种初始设置)
如何完成这个初始设置取决于特定的 Servlet 环境。 如果您在 Spring Web MVC 中访问作用域 bean,实际上,在 Spring DispatcherServlet 处理的请求中,不需要特殊设置。DispatcherServlet 已经暴露所有相关的状态。
如果使用 Servlet2.5
规范,并在Spring的 DispatcherServlet
容器之外处理请求(如JSF、Struts),则需要注册org.springframework.web.context.request.RequestContextListener
ServletRequestListener
。对于 Servlet3.0
,可以通过WebApplicationInitializer
接口以编程方式完成。
DispatcherServlet
、 RequestContextListener
和 RequestContextFilter
都做完全相同的事情,即将 HTTP 请求对象绑定到服务该请求的 Thread。这使得请求范围和会话范围的 bean 在调用链的下一级可用。
<bean id="loginAction" class="com.something.LoginAction" scope="request"/>
为每个HTTP请求创建新的 LoginAction
实例。可以使用注解 @RequestScope
交组件指派给request作用域。
@RequestScope
@Component
public class LoginAction {
// ...
}
<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>
UserPreferences
有效地限定在HTTP Session级别。
@SessionScope
@Component
public class UserPreferences {
// ...
}
<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>
AppPreferences
的作用级别是 ServletContext
。有点类似Spring的单例Bean,但两者不相同。
@ApplicationScope
@Component
public class AppPreferences {
// ...
}
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 of a longer-lived scope, you may choose to 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 (such as an HTTP request) and delegate method calls onto the real object.
Also, scoped proxies are not the only way to access beans from shorter scopes in a lifecycle-safe fashion. You may also declare your injection point (that is, the constructor or setter argument or autowired field) as ObjectFactory, allowing for a getObject() call to retrieve the current instance on demand every time it is needed — without holding on to the instance or storing it separately.
As an extended variant, you may declare ObjectProvider, which delivers several additional access variants, including getIfAvailable and getIfUnique.
容器通过代码创建一个对象,该对象与 UserPreferences
类具有完全相同的公共接口,该对象可以从作用域中获取真正的 UserPreferences
(具有正确的作用域行为)的UserPreferences
对象。而userManager并不清楚这是一个代理对象。
当注入 request-
和 session
作用域对象给合作者时,需要如正确的配置:
<bean id="userPreferences" class="com.something.UserPreferences" scope="session">
<aop:scoped-proxy/>
bean>
<bean id="userManager" class="com.something.UserManager">
<property name="userPreferences" ref="userPreferences"/>
bean>
默认情况下,当Spring容器为用
元素标记的bean创建代理时,将创建一个基于 cglib
的代理类。
代理只拦截
public
调用,不要调用非public
方法,它们不会被委派给实际作用域的目标对象。
您可以配置 Spring 容器,通过为 < aop: scoped-proxy/> 元素的 proxy-target-class 属性值指定 false,为这种作用域 bean 创建标准的基于 JDK 接口的代理。使用基于 JDK 接口的代理意味着在应用程序类路径中不需要额外的库来影响这种代理。然而,这也意味着作用域 bean 的类必须实现至少一个接口,并且注入作用域 bean 的所有协作者必须通过其中一个接口引用 bean。
<bean id="userPreferences" class="com.stuff.DefaultUserPreferences" scope="session">
<aop:scoped-proxy proxy-target-class="false"/>
bean>
Bean的作用域机制是可扩展的。用户可以定义自己的作用域,甚至可以重新定义现有的作用域,但后者被认为是不好的实践,而且不能覆盖内置的单例和原型作用域。
你需要实现 org.springframework.beans.factory.config.Scope
接口,该接口有四个方法用于从作用域中获取对象、从作用域中移除对象、销毁对象。
使用Spring容器注册一个新的作用域方法,这个方法是由ConfigurableBeanFactory
接口声明,可以从 ApplicationContext
应用上下文中获取 BeanFactory
属性获得。
void registerScope(String scopeName, Scope scope);
Spring提供了许多接口,你可以使用它们来自定义Bean的性质。归类如下:
Lifecycle Callbacks
(生命周期回调)ApplicationContextAware
和 BeanNameAware
Aware
接口要与 bean 生命周期的容器管理交互,您可以实现 Spring InitializingBean
和 DisposableBean
接口。容器为前者调用 afterPropertiesSet()
,为后者调用 destroy()
,让 bean 在初始化和销毁 bean 时执行某些操作。
JSR-250@PostConstruct 和@predestroy 注释通常被认为是在现代 Spring 应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的 bean 没有耦合到特定于 spring 的接口。
在Spring内部框架中使用 BeanPostProcessor
实现来处理它能够找到并调用适当方法的任何回调接口。如果用户需要自定义特性或其他Spring默认不提供的生命周期行为,也可以自己实现 BeanPostProcessor
。除了初始化和销毁回调之外,Spring管理的对象还可以实现 Lifecycle
接口,以便这些对象能够参与启动和关闭过程,这是由容器自身的生命周期驱动的。
org.springframework.beans.factory.InitializingBean
该接口允许在 对象属性注入之后
进行初始化工作。该接口指定了一个方法:
void afterPropertiesSet() throws Exception;
不建议使用该接口,因为会与代码进行耦合。建议使用JSR-250规范的注解 @PostConstruct
指定对象初始化方法。也可以基于XML配置 init-method
指定初始化方法名称。如下:
<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>
实现org.springframework.beans.factory.DisposableBean
接口。
void destroy() throws Exception;
并不推荐使用,建议使用JSR-250规范的注解 @PreDestory
可以通过约定的方式定义初始化方法和销毁方法,通常使用 init()、initializa()、dispose()
等名称编写方法。理想情况下,这种生命周期回调方法在项目中是标准化的,这样所有开发人员都使用相同的方法名称,并确保一致性。
你可以配置Spring容器去查找(look)每个bean上的命名初始化和销毁回调方法。这样就避免为每个bean都要配置 init-method
属性。
<beans default-init-method="init">
<bean id="blogService" class="com.something.DefaultBlogService">
<property name="blogDao" ref="blogDao" />
</bean>
</beans>
Spring容器保证Bean中所有依赖注入完成之后立即进行初始化回调。因此,也意味着AOP拦截器并未作用此刻的Bean上面。首先需要完全实例化一个Bean,然后再对Bean应用AOP拦截。因此,将拦截器应用到初始化回调方法可能会导致不一致的情况(出现未知异常或状态)。
Hence, it would be inconsistent to apply the interceptors to the
init
method, because doing so would couple the lifecycle of the target bean to its proxy or interceptors and leave strange semantics when your code interacts directly with the raw target bean.
在Spring2.5中,控制Bean生命周期行为有三个选项
The InitializingBean
and DisposableBean
callback interfaces
Custom init()
and destroy()
methods
The @PostConstruct
and @PreDestroy
annotations. You can combine these mechanisms to control a given bean.
初始化生命回调顺序
@PostConstruct
InitializingBean
的方法 atferPropertiesSet()
回调init()
方法销毁回调顺序
@PreDestory
DisposableBean
的方法 destory()
回调destory()
方法Lifecycle 接口为任何具有自己的生命周期需求的对象定义基本方法(例如启动和停止某个后台流程) :
public interface Lifecycle {
void start();
void stop();
boolean isRunning();
}
任何Spring管理的对象都可以实现 Lifecycle
接口。当 ApplicatonContext
容器本身接收到启动和停止信号(如在运行时停止/重新启动等场景) 时,它将这些调用传递给在上下文中定义的所有 Lifecycle
接口实现。通过委派给 LifecycleProcessor
来处理实现逻辑。
Note that the regular org.springframework.context.Lifecycle interface is a plain contract for explicit start and stop notifications and does not imply auto-startup at context refresh time. For fine-grained control over auto-startup of a specific bean (including startup phases), consider implementing org.springframework.context.SmartLifecycle instead.
Also, please note that stop notifications are not guaranteed to come before destruction. On regular shutdown, all Lifecycle beans first receive a stop notification before the general destruction callbacks are being propagated. However, on hot refresh during a context’s lifetime or on stopped refresh attempts, only destroy methods are called.
如果两个对象之间存在依赖关系,但是有时直接依赖关系是未知的。你可能知道某个对象要早于另一个对象开始,在这种情况下, SmarkLifecycle
接口定义了另一个选项 getPhase()
,表示分阶段:
public interface Phased {
int getPhase();
}
public interface SmartLifecycle extends Lifecycle, Phased {
boolean isAutoStartup();
void stop(Runnable callback);
}
最低阶段对象(lowest phase)首先开始。当停止时,按照相反的顺序。因此,实现 SmartLifecycle
并且 getPhase()
方法返回 Integer.MIN_VALUE
的对象将首先启动,最后停止。
Spring基于web的
ApplicationContext
已经有默认实现,可以在相关web应用程序关闭时优雅地关闭Spring IoC容器。
如果用在非Web环境,需要向JVM注册一个 shutdown hook
。这样做可以确保优雅地关闭单例Bean上的相关 destory()
方法,从而释放宝贵的资源。如下所示:
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public final class Boot {
public static void main(final String[] args) throws Exception {
ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
// add a shutdown hook for the above context...
ctx.registerShutdownHook();
// app runs here...
// main method exits, hook is called prior to the app shutting down...
}
}
ApplicationContextAware
接口如下:
public interface ApplicationContextAware {
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
Bean可以通过实现此接口,得到 ApplicationContext
对象,并通过强制类型转换为该接口的已知子类(如 ConfigurableApplicationContext
,它暴露了额外的功能)。但是这样会与Spring框架耦合且不符合DI风格。 ApplicationContext
其他方法提供了对 文件资源的访问(access to file resources)
、 发布应用事件(publicshing application event)
、 访问MessageSource(access MessageSource)
。
自动装配(Autowring)
是获得 ApplicationContext
引用的另一种选择。
org.springframework.beans.factory.BeanNameAware
提供了对其关联对象定义中的名称的信息。
public interface BeanNameAware {
void setBeanName(String name) throws BeansException;
}
Name 姓名 | Injected Dependency | Explained in… |
---|---|---|
ApplicationContextAware |
Declaring ApplicationContext . |
ApplicationContextAware and BeanNameAware |
ApplicationEventPublisherAware |
Event publisher of the enclosing ApplicationContext . |
Additional Capabilities of the ApplicationContext |
BeanClassLoaderAware |
Class loader used to load the bean classes. | Instantiating Beans |
BeanFactoryAware |
Declaring BeanFactory . |
ApplicationContextAware and BeanNameAware |
BeanNameAware |
Name of the declaring bean. | ApplicationContextAware and BeanNameAware |
LoadTimeWeaverAware |
Defined weaver for processing class definition at load time. | Load-time Weaving with AspectJ in the Spring Framework |
MessageSourceAware |
Configured strategy for resolving messages (with support for parametrization and internationalization). | Additional Capabilities of the ApplicationContext |
NotificationPublisherAware |
Spring JMX notification publisher. | Notifications |
ResourceLoaderAware |
Configured loader for low-level access to resources. | Resources |
ServletConfigAware |
Current ServletConfig the container runs in. Valid only in a web-aware Spring ApplicationContext . |
Spring MVC |
ServletContextAware |
Current ServletContext the container runs in. Valid only in a web-aware Spring ApplicationContext . |
Spring MVC |
Bean元信息可以包含许多配置信息,包括 构造函数参数、属性值和特定于容器的信息,如初始化方法、静态工厂方法名称
等。子Bean元信息从父Bean元信息中继承配置信息,并且可以重写/覆盖/添加配置信息。实际上,这是一种模板模式。XML如下配置,注意 parent
属性值
<bean id="inheritedTestBean" abstract="true"
class="org.springframework.beans.TestBean">
<property name="name" value="parent"/>
<property name="age" value="1"/>
</bean>
<bean id="inheritsWithDifferentClass"
class="org.springframework.beans.DerivedTestBean"
parent="inheritedTestBean" init-method="initialize">
<property name="name" value="override"/>
<!-- the age property value of 1 will be inherited from parent -->
</bean>
BeanPostProcessor
自定义BeansBeanPostProcessor
接口定义了回调方法,可以实现该方法提供自己的(或覆盖容器默认实现)实例化逻辑、依赖解析逻辑等等。如果想要在容器完成实例化、配置和初始化Bean之后实现一些自定义逻辑,可以插入一个或多个自定义BeanPostProcessor
实现。
您可以配置多个 BeanPostProcessor 实例,并且可以通过设置 order 属性来控制这些 BeanPostProcessor 实例的运行顺序。
BeanPostProcessor instances are scoped per-container. This is relevant only if you use container hierarchies. If you define a BeanPostProcessor in one container, it post-processes only the beans in that container. In other words, beans that are defined in one container are not post-processed by a BeanPostProcessor defined in another container, even if both containers are part of the same hierarchy.
即使具体相同的层次结构,每个容器的BeanPostProcessor
还是各司其职,各不相关。
To change the actual bean definition (that is, the blueprint that defines the bean), you instead need to use a BeanFactoryPostProcessor, as described in Customizing Configuration Metadata with a BeanFactoryPostProcessor.
在改变实际的bean元信息,你需要使用BeanFactoryPostProcessor
。
public interface BeanPostProcessor {
/**
* Apply this {@code BeanPostProcessor} to the given new bean instance before any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* The default implementation returns the given {@code bean} as-is.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
*/
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
/**
* Apply this {@code BeanPostProcessor} to the given new bean instance after any bean
* initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
* or a custom init-method). The bean will already be populated with property values.
* The returned bean instance may be a wrapper around the original.
* In case of a FactoryBean, this callback will be invoked for both the FactoryBean
* instance and the objects created by the FactoryBean (as of Spring 2.0). The
* post-processor can decide whether to apply to either the FactoryBean or created
* objects or both through corresponding {@code bean instanceof FactoryBean} checks.
*
This callback will also be invoked after a short-circuiting triggered by a
* {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
* in contrast to all other {@code BeanPostProcessor} callbacks.
*
The default implementation returns the given {@code bean} as-is.
* @param bean the new bean instance
* @param beanName the name of the bean
* @return the bean instance to use, either the original or a wrapped one;
* if {@code null}, no subsequent BeanPostProcessors will be invoked
* @throws org.springframework.beans.BeansException in case of errors
* @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
* @see org.springframework.beans.factory.FactoryBean
*/
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
The
org.springframework.beans.factory.config.BeanPostProcessor
interface consists of exactly two callback methods. When such a class is registered as a post-processor with the container, for each bean instance that is created by the container, the post-processor gets a callback from the container both before container initialization methods (such asInitializingBean.afterPropertiesSet()
or any declaredinit
method) are called, and after any bean initialization callbacks. The post-processor can take any action with the bean instance, including ignoring the callback completely. A bean post-processor typically checks for callback interfaces, or it may wrap a bean with a proxy. Some Spring AOP infrastructure classes are implemented as bean post-processors in order to provide proxy-wrapping logic.
org.springframework.beans.factory.config.BeanPostProcessor接口由两个回调方法构成。当向容器注册一个容器回调,对于每个由容器创建的实例对象,在bean初始化回调方法之前(如 afterPropertiesSet
或 init
方法)获取容器回调( postProcessBeforeInitialization
)和初始化回调方法之后获取容器回调( postProcessAfterInitialization
)。容器后置处理器可以对bean实例做任何操作,包含完全忽略回调方法。一个Bean后置处理器通常用来检查回调接口,或者用代理包装一个Bean对象。一些Spring AOP的基础结构类通过实现 bean post-processors
提供代理包装逻辑。
Note that, when declaring a
BeanPostProcessor
by using an@Bean
factory method on a configuration class, the return type of the factory method should be the implementation class itself or at least theorg.springframework.beans.factory.config.BeanPostProcessor
interface, clearly indicating the post-processor nature of that bean. Otherwise, theApplicationContext
cannot autodetect it by type before fully creating it. Since aBeanPostProcessor
needs to be instantiated early in order to apply to the initialization of other beans in the context, this early type detection is critical.
当使用@Bean
工厂方法声明的BeanPostProcessor
时,工厂方法返回的就该是实现类本身,至少是BeanPostProcessor
接口,可以清晰表示这个Bean的后置处理特性。否则,ApplicationContext 无法在完全创建它之前通过类型自动检测它。
可以通过编程的方式注册。ConfigurableBeanFactory#addBeanPostProcessor方法
。编程的方式不会遵守Ordered
接口。在这里,注册的顺序决定了执行的顺序,且通过编程方式注册的后置处理器先于
基于自动检测。
This can be useful when you need to evaluate conditional logic before registration or even for copying bean post processors across contexts in a hierarchy.
实现 BeanPostProcessor
如看看的类是特殊的,容器对他们会做不同的处理。所有直接引BeanPostProcessor
的实例和Beans在启动的时候会被实例化,作为ApplicationContext特殊启动阶段的一部分。接着,排序注册所有BeanPostProcessor
实例并将他们应用于容器的所有其他的Beans。因为AOP实现BeanPostProcessor
,所以不会对BeanPostProcessor
实例或直接引用BeanPostProcessor
的bean进行自动代理。
示例 : 定制一个BeanPostProcessor
,在容器创建每个bean时调用每个bean的 toString()
方法,并将生成的字符串打印到控制台。
package scripting;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
@ComponentScan("com.clarencezero.springioc")
public class LifeCycleCallbackDemo {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
context.register(LifeCycleCallbackDemo.class);
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
beanFactory.addBeanPostProcessor(new InstantiationTracingBeanPostProcessor());
context.refresh();
}
}
BeanFactoryPostProcessor
与 BeanPostProcessor
主要区别在于 BeanFactoryPostProcessor
对Bean元数据进行操作,也就是说,Spring IoC容器允许 BeanFactoryPostProcessor
读取配置元数据,并有可能在容器实例化除BeanFactoryPostProcessor实例以外的任何bean之前更改它。
可以配置多个BeanFactoryPostProcessor
实例且通过设置Order属性控制运行顺序。
If you want to change the actual bean instances (that is, the objects that are created from the configuration metadata), then you instead need to use a BeanPostProcessor. While it is technically possible to work with bean instances within a BeanFactoryPostProcessor (for example, by using BeanFactory.getBean()), doing so causes premature bean instantiation, violating the standard container lifecycle. This may cause negative side effects, such as bypassing bean post processing.
如果想要更改实际的Bean实际(由配置元信息创建),则需要使用BeanPostProcessor。虽然在技术上可以使用BeanFactoryPostProcessor进行实例化(比如使用BeanFactory.getBean()),但这样做会导致Bean提前实例化,违反标准的容器生命周期,可能导致如线索bean的后置处理等负面作用。
Also, BeanFactoryPostProcessor instances are scoped per-container. This is only relevant if you use container hierarchies. If you define a BeanFactoryPostProcessor in one container, it is applied only to the bean definitions in that container. Bean definitions in one container are not post-processed by BeanFactoryPostProcessor instances in another container, even if both containers are part of the same hierarchy.
此外,BeanFactoryPostProcessor作用域为每个容器。与其他容器不相关。
Spring包含了许多预定义的BeanFactoryPostProcessor,如PropertyOverrideConfigurer、PropertySourcesPlaceholderConfigurer。也可以使用自定义BeanFactoryPostProcessor,如注册自定义属性编辑器。
You can use the
PropertySourcesPlaceholderConfigurer
to externalize property values from a bean definition in a separate file by using the standard JavaProperties
format. Doing so enables the person deploying an application to customize environment-specific properties, such as database URLs and passwords, without the complexity or risk of modifying the main XML definition file or files for the container.
The
PropertyOverrideConfigurer
, another bean factory post-processor, resembles thePropertySourcesPlaceholderConfigurer
, but unlike the latter, the original definitions can have default values or no values at all for bean properties. If an overridingProperties
file does not have an entry for a certain bean property, the default context definition is used.
You can implement the
org.springframework.beans.factory.FactoryBean
interface for objects that are themselves factories.
您可以为本身就是工厂的对象实现org.springframework.beans.factory.FactoryBean接口。
FactoryBean
接口是Spring IoC容器的实例化逻辑的扩展点。如果你有复杂的初始化逻辑,使用Java代码描述比XML描述更好。你可以创建自定义的FactoryBean,在该类中编写复杂的初始化逻辑,然后将自定义的FactoryBean插入容器中。
FactoryBean
接口有以下三种方式:
Object getObject()
: Returns an instance of the object this factory creates. The instance can possibly be shared, depending on whether this factory returns singletons or prototypes.boolean isSingleton()
: Returns true
if this FactoryBean
returns singletons or false
otherwise.Class getObjectType()
: Returns the object type returned by the getObject()
method or null
if the type is not known in advance.通过在getBean("&mybean)
方法获取FactoryBean本身。
与 BeanPostProcessor
后置处理器结合使用是扩展Spring IoC容器常用的方法。
Spring IoC隐式注册的后置处理器有
- AutowiredAnnotationBeanPostProcessor
- CommonAnnotationBeanPostProcessor
- PersistenceAnnotationBeanPostProcessor
- RequiredAnnotationBeanPostProcessor
应用 setter
方法
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
如果在容器中通过依赖查找找不到合适的bean,则会抛出异常。从而避免以后出现空指针情况。自Spring5.1废弃。
可以使用 JSR 330的@inject 注释来代替本节中包含的示例中 Spring 的@autowired 注释。
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
As of Spring Framework 4.3, an @Autowired annotation on such a constructor is no longer necessary if the target bean defines only one constructor to begin with. However, if several constructors are available and there is no primary/default constructor, at least one of the constructors must be annotated with @Autowired in order to instruct the container which one to use. See the discussion on constructor resolution for details.
public class SimpleMovieLister {
private MovieFinder movieFinder;
// 作用在setter方法上
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
Only one constructor of any given bean class may declare @Autowired with the required attribute set to true, indicating the constructor to autowire when used as a Spring bean. As a consequence, if the required attribute is left at its default value true, only a single constructor may be annotated with @Autowired. If multiple constructors declare the annotation, they will all have to declare required=false in order to be considered as candidates for autowiring (analogous to autowire=constructor in XML). The constructor with the greatest number of dependencies that can be satisfied by matching beans in the Spring container will be chosen. If none of the candidates can be satisfied, then a primary/default constructor (if present) will be used. Similarly, if a class declares multiple constructors but none of them is annotated with @Autowired, then a primary/default constructor (if present) will be used. If a class only declares a single constructor to begin with, it will always be used, even if not annotated. Note that an annotated constructor does not have to be public.
任何给定bean类的构造函数都只能声明@Autowired,并将必填属性设置为true,这表示在用作Spring bean时可以自动装配的构造函数。因此,如果必填属性保留为其默认值true,则只能使用@Autowired注释单个构造函数。如果多个构造函数声明了注释,则它们都必须声明required = false才能被视为自动装配的候选对象(类似于XML中的autowire = constructor)。将选择通过匹配Spring容器中的bean可以满足的依赖关系数量最多的构造函数。如果没有一个候选者满意,则将使用主/默认构造函数(如果存在)。同样,如果一个类声明了多个构造函数,但都没有使用@Autowired进行注释,则将使用主/默认构造函数(如果存在)。如果一个类仅声明一个单一的构造函数开始,即使没有注释,也将始终使用它。请注意,带注释的构造函数不必是公共的。
The required attribute of @Autowired is recommended over the deprecated @Required annotation on setter methods. Setting the required attribute to false indicates that the property is not required for autowiring purposes, and the property is ignored if it cannot be autowired. @Required, on the other hand, is stronger in that it enforces the property to be set by any means supported by the container, and if no value is defined, a corresponding exception is raised.
建议在setter方法上使用@Autowired的required属性,而不建议使用已废弃的@Required注释。 将required属性设置为false表示该属性对于自动装配不是必需的,并且如果无法自动装配该属性,则将其忽略。 从另一方面讲,@Required更为强大,因为它可以通过容器支持的任何方式来强制设置属性,并且如果未定义任何值,则会引发相应的异常。
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(Optional<MovieFinder> movieFinder) {
...
}
}
当然,也可以使用 @Nullable
public class SimpleMovieLister {
@Autowired
public void setMovieFinder(@Nullable MovieFinder movieFinder) {
...
}
}
你可以通过 @Autowire
注入以下所熟知的依赖
这些接口的扩展(如ConfigurableApplicationContext和ResourcePatternResolver)等。
The
@Autowired, @Inject, @Value, and @Resource
annotations are handled by SpringBeanPostProcessor
implementations. This means that you cannot apply these annotations within your own BeanPostProcessor or BeanFactoryPostProcessor types (if any). These types must be ‘wired up’ explicitly by using XML or a Spring @Bean method.
@ Autowired,@ Inject,@ Value和@Resource批注由Spring BeanPostProcessor实现处理。 这意味着您不能在自己的BeanPostProcessor或BeanFactoryPostProcessor类型(如果有)中应用这些注释。 必须使用XML或Spring @Bean方法显式“连接”这些类型。
由于按类型注入可能会有多个候选Bean,所以通常需要对选择过程有更多的控制。 @Primary
表示优先选择特定的Bean。
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
当对选择过程进行更多的控制时,可以使用注解 @Qualifiers
,通过将限定符与特定参数关联起来,从而缩小类型匹配范围。
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean class="example.SimpleMovieCatalog">
<qualifier value="main"/>
bean>
<bean class="example.SimpleMovieCatalog">
<qualifier value="action"/>
bean>
<bean id="movieRecommender" class="example.MovieRecommender"/>
beans>
For a fallback match, the bean name is considered a default qualifier value. Thus, you can define the bean with an id of main instead of the nested qualifier element, leading to the same matching result. However, although you can use this convention to refer to specific beans by name, @Autowired is fundamentally about type-driven injection with optional semantic qualifiers. This means that qualifier values, even with the bean name fallback, always have narrowing semantics within the set of type matches. They do not semantically express a reference to a unique bean id. Good qualifier values are main or EMEA or persistent, expressing characteristics of a specific component that are independent from the bean id, which may be auto-generated in case of an anonymous bean definition such as the one in the preceding example.
对于后备匹配,bean名称被视为默认的限定符值。 因此,可以使用id为main而不是嵌套的qualifier元素定义bean,从而得到相同的匹配结果。 但是,尽管您可以使用此约定按名称引用特定的bean,但@Autowired基本上是关于带有可选语义限定符的类型驱动的注入。 这意味着,即使带有Bean名称后退的限定符值,在类型匹配集中也始终具有狭窄的语义。 它们没有在语义上表示对唯一bean id的引用。 好的限定符值是主要的或EMEA的或持久的,表示特定组件的特征,这些特征独立于bean id,在使用匿名bean定义(例如上例中的定义)的情况下,可以自动生成这些特征。
Letting qualifier values select against target bean names, within the type-matching candidates, does not require a @Qualifier annotation at the injection point. If there is no other resolution indicator (such as a qualifier or a primary marker), for a non-unique dependency situation, Spring matches the injection point name (that is, the field name or parameter name) against the target bean names and choose the same-named candidate, if any.
在类型匹配的候选对象中,让限定符值针对目标Bean名称进行选择,在注入点不需要@Qualifier注释。 如果没有其他解析度指示符(例如限定词或主标记),则对于非唯一依赖性情况,Spring将注入点名称(即字段名称或参数名称)与目标Bean名称进行匹配,然后选择 同名候选人(如果有)。
That said, if you intend to express annotation-driven injection by name, do not primarily use @Autowired, even if it is capable of selecting by bean name among type-matching candidates. Instead, use the JSR-250 @Resource annotation, which is semantically defined to identify a specific target component by its unique name, with the declared type being irrelevant for the matching process. @Autowired has rather different semantics: After selecting candidate beans by type, the specified String qualifier value is considered within those type-selected candidates only (for example, matching an account qualifier against beans marked with the same qualifier label).
就是说,如果您打算按名称表示注释驱动的注入,则即使它能够在类型匹配的候选对象中按bean名称进行选择,也不要主要使用@Autowired。 相反,请使用JSR-250
@Resource
注释,该注释在语义上定义为通过其唯一名称标识特定目标组件,而声明的类型与匹配过程无关。 @Autowired具有不同的语义:按类型选择候选bean之后,仅在那些类型选择的候选对象中考虑指定的String限定符值(例如,将帐户限定符与标记有相同限定符标签的bean进行匹配)。
For beans that are themselves defined as a collection, Map, or array type, @Resource is a fine solution, referring to the specific collection or array bean by unique name. That said, as of 4.3, collection, you can match Map, and array types through Spring’s @Autowired type matching algorithm as well, as long as the element type information is preserved in @Bean return type signatures or collection inheritance hierarchies. In this case, you can use qualifier values to select among same-typed collections, as outlined in the previous paragraph.
对于本身定义为集合,映射或数组类型的bean,@Resource是一个很好的解决方案,它通过唯一的名称引用特定的集合或数组bean。 也就是说,从4.3版本开始,只要元素类型信息保留在@Bean返回类型签名或集合继承层次结构中,就可以通过Spring的@Autowired类型匹配算法来匹配Map和数组类型。 在这种情况下,您可以使用限定符值在同类型的集合中进行选择,如上一段所述。
@Autowired适用于字段,构造函数和多参数方法,从而允许在参数级别缩小限定符注释的范围。 相反,只有具有单个参数的字段和bean属性设置器方法才支持@Resource。 因此,如果注入目标是构造函数或多参数方法,则应坚持使用限定符。你可以创建自定义限定符:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
除了 @Qualifier
注释之外,还可以使用Java泛型类型作为一种隐式的限定形式。
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier
public @interface Genre {
String value();
}
// Inject all Store beans as long as they have an generic
// Store beans will not appear in this list
@Autowired
private List<Store<Integer>> s;
Customautowireconfigureer 是一个 BeanFactoryPostProcessor,它允许您注册自己的自定义限定符注释类型,即使它们没有注释 Spring 的@qualifier 注释。下面的示例演示如何使用 CustomAutowireConfigurer:
<bean id="customAutowireConfigurer"
class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>example.CustomQualifiervalue>
set>
property>
bean>
AutowireCandidateResolver
通过以下方式确定自动装配候选者:
autowire-candidate
value of each bean definitiondefault-autowire-candidates
patterns available on the
element@Qualifier
annotations and any custom annotations registered with the CustomAutowireConfigurer
当多个Bean符合候选项时,主要看是 @Primary
是否为true。
Spring框架支持 JSR-250
@Resource
注入,用于字段或setter方法。@Resource
通过将name属性值解释为要注入的bean名称。如下所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource(name="myMovieFinder")
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
@Value
通常注入外部化属性。
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("${catalog.name}") String catalog) {
this.catalog = catalog;
}
}
如果你想对不存在的属性值保持严格控制,可以声明 propertysourcesplaceholderconfigurebean
,示例如下:
@Configuration
public class AppConfig {
//必须是static方法
@Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
如果属性配置解析失败,则Spring初始化失败。
Spring BeanPostProcessor 在场景后面使用 ConversionService
来处理将@value 中的 String 值转换为目标类型的过程。如果你想为你自己的定制类型提供转换支持,你可以提供你自己的 ConversionService bean 实例,如下面的例子所示:
@Configuration
public class AppConfig {
@Bean
public ConversionService conversionService() {
DefaultFormattingConversionService conversionService = new DefaultFormattingConversionService();
conversionService.addConverter(new MyCustomConverter());
return conversionService;
}
}
当@value 包含一个 SpEL 表达式时,该值将在运行时动态计算,如下面的示例所示:
@Component
public class MovieRecommender {
private final String catalog;
public MovieRecommender(@Value("#{systemProperties['user.catalog'] + 'Catalog' }") String catalog) {
this.catalog = catalog;
}
}
SpEL 还支持使用更复杂的数据结构:
@Component
public class MovieRecommender {
private final Map<String, Integer> countOfMoviesPerCatalog;
public MovieRecommender(
@Value("#{{'Thriller': 100, 'Comedy': 300}}") Map<String, Integer> countOfMoviesPerCatalog) {
this.countOfMoviesPerCatalog = countOfMoviesPerCatalog;
}
}
CommonAnnotationBeanPostProcessor
不仅识别@Resource注解,还识别JSR-250生命周期注解: javax.annotation.PostConstruct
和javax.annotation.PreDestroy
。
if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {
RootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);
def.setSource(source);
beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));
}
public class CachingMovieLister {
@PostConstruct
public void populateMovieCache() {
// populates the movie cache upon initialization...
}
@PreDestroy
public void clearMovieCache() {
// clears the movie cache upon destruction...
}
}
@Component
是Spring管理的组件的通用类型, @Repository、@Service、@Controller
是对 @Component
的领域细分,分别用于持有层、服务层、表示层。虽然说可以直接使用 @Component
,但是如果对组件进行细分注解,那么类会更适合工具进行处理或与切面相关。也可能在Spring框架版本中承载额外的语义。
Spring提供的许多注解都可以在您自己的代码中用作元注解。元注解是一个可以应用于另一个注解的注解。例如,前面提到的@service 注解使用@component 进行了元注解,如下面的示例所示:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
// ...
}
组合注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {
/**
* Alias for {@link Scope#proxyMode}.
* Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
*/
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
Spring可以自动检测类以及注册相应的元信息。比如:
@Service
public class SimpleMovieLister {
private MovieFinder movieFinder;
public SimpleMovieLister(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
要自动检测这些类并注册相应的 bean,需要将@componentscan 添加到@configuration 类中。如果使用XML,则需要隐式启用
j。
On JDK 9’s module path (Jigsaw), Spring’s classpath scanning generally works as expected. However, make sure that your component classes are exported in your module-info descriptors. If you expect Spring to invoke non-public members of your classes, make sure that they are ‘opened’ (that is, that they use an opens declaration instead of an exports declaration in your module-info descriptor).
此外,在使用 component-scan 元素时,AutowiredAnnotationBeanPostProcessor 和 CommonAnnotationBeanPostProcessor 都隐式地包含在内。
| annotation (default) | org.example.SomeAnnotation
| An annotation to be present or meta-present at the type level in target components.
在目标组件的类型级别显示或元显示的注释。 |
---|
assignable |
目标组件可分配给(扩展或实现)的类(或接口)。 |
aspectj |
由目标组件匹配的 AspectJ 类型表达式。 |
regex |
由目标组件的类名匹配的正则表达式。 |
custom |
Org.springframework.core.type. TypeFilter 接口的自定义实现。 |
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
常规 Spring 组件中的@bean 方法与 Spring @Configuration
类中的@bean 方法处理方式不同。不同之处在于, @component 类没有通过 CGLIB 进行增强
,以拦截方法和字段的调用。CGLIB 代理是通过调用@configuration 类中的@bean 方法中的方法或字段来创建对协作对象的 bean 元数据引用的方法。这些方法不是用普通的 Java 语义调用的,而是通过容器来提供通常的生命周期管理和 Spring bean 的代理,甚至在通过编程调用@bean 方法引用其他 bean 时也是如此。相比之下,在普通的@component 类中调用@bean 方法中的方法或字段具有标准的 Java 语义,不应用特殊的 CGLIB 处理或其他约束。
你可以将@Bean的方法声明为静态方法,而不需要将它们的包含配置类作为实例来创建。在定义后置处理器的baans(如BeanFactoryPostProcessor or BeanPostProcessor类型)时,这是非常有意义的,因为这些Beans在容器生命周期就已初始化,并且应该避免在那个时候触发配置的其他部分。
由于技术限制,静态方法永远不会被容器获取。CGLIB只能重写非静态方法。
当一个组件作为扫描过程的一部分被自动检测时,它的 bean 名称由该扫描程序已知的 BeanNameGenerator 策略生成。
当你不满足默认的命名策略,你可以提供自定义bean命令策略。首先,实现BeanNameGenerator接口,并确保包含默认的无参构造函数。
@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {
// ...
}
@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
若要给自定义作用域提供自定久策略而不是依赖注解方法,你可以实现ScopeMetadataResolver接口并确保包含一个默认的无参构造器。
@Configuration
@ComponentScan(basePackages = "org.example", scopeResolver = MyScopeResolver.class)
public class AppConfig {
// ...
}
When using certain non-singleton scopes, it may be necessary to generate proxies for the scoped objects. The reasoning is described in Scoped Beans as Dependencies. For this purpose, a scoped-proxy attribute is available on the component-scan element. The three possible values are:
no
,interfaces
, andtargetClass
. For example, the following configuration results in standard JDK dynamic proxies:
@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
// ...
}
@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {
// ...
}
若要生成索引,请向每个模块添加一个附加依赖项,该模块包含组件扫描指令的目标组件。下面的例子展示了如何使用 Maven:
<dependencies>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-context-indexerartifactId>
<version>5.3.0-SNAPSHOTversion>
<optional>trueoptional>
dependency>
dependencies>
该过程生成一个 META-INF/spring.components 文件,该文件包含在 jar 文件中。
需要添加额外的依赖
<dependency>
<groupId>javax.injectgroupId>
<artifactId>javax.injectartifactId>
<version>1version>
dependency>
可以使用 @javax.inject.Inject
替换 @Autowired
。
import javax.inject.Inject;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
public void listMovies() {
this.movieFinder.findMovies(...);
// ...
}
}
如果想要注入使用限定名,应该使用 @Named
注解。
import javax.inject.Inject;
import javax.inject.Named;
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
可以使用 @javax.inject.Named
或 javax.annotation.ManagedBean
替换 @Autowired
。
import javax.inject.Inject;
import javax.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
Spring | javax.inject.* Javax.inject. * | javax.inject restrictions / comments |
---|---|---|
@Autowired | @Inject | @Inject has no ‘required’ attribute. Can be used with Java 8’s Optional instead. |
@Component | @Named / @ManagedBean | JSR-330 does not provide a composable model, only a way to identify named components. |
@Scope(“singleton”) | @Singleton | JSR-330的默认作用域类似于 Spring 的原型。但是,为了使它与 Spring 的常规默认值保持一致,在 Spring 容器中声明的 JSR-330 bean 默认是单例模式。为了使用单例以外的范围,您应该使用 Spring 的@scope 注释。Inject 还提供了@scope 注释。尽管如此,这个注释只用于创建自己的注释。 |
@Qualifier | @Qualifier / @Named | Javax 注射。Qualifier 只是用于构建自定义限定符的元注释。可以通过 javax.inject 关联 Concrete String 限定符(比如 Spring 的@qualifier 和一个值)。名字。 |
@Value | - | no equivalent |
@Required | - | no equivalent |
@Lazy | - | no equivalent |
ObjectFactory | Provider | provider 是 Spring 的 ObjectFactory 的直接替代品,只是具有较短的 get ()方法名。它也可以与 Spring 的@autowired 组合使用,或者与没有注释的构造函数和 setter 方法组合使用。 |
如何在Java代码中使用注解来配置Spring容器。
Spring最新配置的核心是 @Configuration
和 @Bean
两个注解。
@Bean注解用于指示方法实例化,配置和初始化要由Spring IoC容器管理的新对象。@Bean扮演了与XML中
一样的角色。但是通常它与@Configuration
配合使用。
用@Configuration注释类表示该类的主要目的是作为Bean定义的来源。 此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义Bean间的依赖关系。 最简单的@Configuration类的内容如下:
@Configuration
public class AppConfig {
@Bean
public MyService myService() {
return new MyServiceImpl();
}
}
如果在未使用@Configuration注释的类中声明@Bean方法,则将它们称为以“精简”模式进行处理。
与 full@Configuration
不同,lite@Bean 方法不能声明 bean 间的依赖关系。这里最直接的影响是CGLIB不会代理该对象。
在常见的场景中,@Bean方法需要声明在@Configuration类中,确保始终使用full模式。这样可以保证跨方法引用会重定向到容器生命周期管理。
public static void main(String[] args) {
ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.register(AppConfig.class, OtherConfig.class);
ctx.register(AdditionalConfig.class);
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
myService.doStuff();
}
@Configuration
@ComponentScan(basePackages = "com.acme")
public class AppConfig {
...
}
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("com.acme");
ctx.refresh();
MyService myService = ctx.getBean(MyService.class);
}
当你需要配置Spring ContextLoaderListener servlet监听器、Spring MVC DispatcherServlet 时,可以使用此实现。
@Bean和XML中的一样,同时支持 init-method
、 destory-method
、 autowiring
等。
@Configuration
public class AppConfig {
@Bean
public TransferServiceImpl transferService() {
return new TransferServiceImpl();
}
}
使用@bean 注解定义的任何类都支持常规的生命周期回调,并且可以使用 JSR-250中的@postconstruct 和@predestroy 注解。
还完全支持常规的 Spring 生命周期回调。如果 bean 实现 InitializingBean、 DisposableBean 或 Lifecycle,则容器将调用它们各自的方法。
还完全支持 *Aware
接口的标准集(如 BeanFactoryAware、 BeanNameAware、 MessageSourceAware、 ApplicationContextAware 等)。
@ bean 注解支持指定任意初始化和销毁回调方法,非常类似于 Spring XML 在 bean 元素上的 init-method 和 destroy-method 属性,如下面的示例所示:
@Configuration
public class AppConfig {
@Bean(initMethod = "init")
public BeanOne beanOne() {
return new BeanOne();
}
@Bean(destroyMethod = "cleanup")
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
指定bean的作用域。Spring默认作用域为单例。
@Configuration
public class AppConfig {
@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
public DataSource dataSource() {
// instantiate, configure and return DataSource bean...
}
}
@Configuration
public class AppConfig {
@Bean
public BeanOne beanOne() {
return new BeanOne(beanTwo());
}
@Bean
public BeanTwo beanTwo() {
return new BeanTwo();
}
}
这是很少使用到的高级特性。当一个单例对象被注入了原型对象,使用查找方法注入会非常有用。
@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
AsyncCommand command = new AsyncCommand();
// inject dependencies here as required
return command;
}
@Bean
public CommandManager commandManager() {
// return new anonymous implementation of CommandManager with createCommand()
// overridden to return a new prototype Command object
return new CommandManager() {
protected Command createCommand() {
return asyncCommand();
}
}
}
类似XML的
标签来帮助模块化配置。还允许从另一个配置类中加载@Bean定义。
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}