当前位置: 代码迷 >> 综合 >> SpringDoc阅读(Chapter1)-Spring IoC容器
  详细解决方案

SpringDoc阅读(Chapter1)-Spring IoC容器

热度:19   发布时间:2024-01-31 04:59:36.0

SpringDoc阅读之Chapter1-Spring IoC容器

1.1 Spring IoC 容器、Beans相关介绍

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 的子类。提供以下能力:

  • 与Spring AOP轻松集成
  • 国际化消息处理
  • 事件发布
  • 应用层特定的上下文,比如Web应用程序使用 WebApplicationContext

总之, BeanFactory 提供了 配置框架(Configuration framework 和 基础功能(basic functionality) , ApplictionContext 添加了更多企业功能。 ApplicationContext 是 BeanFactory 的完整超集。
在Spring框架中,Bean是由Spring IoC容器实例化,组装以及其他管理的对象。

1.2 容器概览

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)

1.2.1 配置元信息(Configuration Metadata)

如上图所示,配置元信息可以让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.

1.2.2 实例化容器(Instantiating a Container)

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 进行配置。

1.2.3 容器使用

// 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元信息。

1.3 Bean预览

在容器内部,这些Bean元数据是通过 BeanDefinition 对象表示。其中包含以下数据:

  • 包限定类名
  • Bean行为配置元素,用于声明Bean在容器中的行为(作用域、生命周期回调等)
  • 依赖其他Bean才能完成工作。这些引用也称为协作者(collaborators)或依赖项(dependencies)
  • 要在新创建的对象中设置其他配置
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"/>

1.3.2 实例化Beans

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 a static nested class, you have to use the binary name of the nested class.
For example, if you have a class called SomeThing in the com.example package, and this SomeThing class has a static nested class called OtherThing, the value of the class attribute on a bean definition would be com.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.

Instantiation with a Static Factory Method

<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运行时类型

找出特定Bean的实际运行时类型的推荐方法是通过 BeanFactory#getType 调用指定Bean名称。这个接口考虑了所有可能存在的情况,并返回了针对相同Bean名称调用返回的对象的类型。

1.4 依赖

1.4.1 依赖注入(Dependency Injection)

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 MovieFinderprivate MovieFinder movieFinder;// a constructor so that the Spring container can inject a MovieFinderpublic 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;}
}

基于setter的依赖注入

在调用无参数构造函数或无参静态工厂方法实例化Bean之后,容器通过setter方法实现对Bean的依赖注入。

public class SimpleMovieLister {// the SimpleMovieLister has a dependency on the MovieFinderprivate MovieFinder movieFinder;// a setter method so that the Spring container can inject a MovieFinderpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// business logic that actually uses the injected MovieFinder is omitted...
}

你可以通过 BeanDefinition 配置依赖,并通过PropertyEditor 实例将属性从一种格式转换为另一种格式。

基于构造器注入还是基于Setter注入

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依赖解析如下:

  • 使用用来描述所有Bean的配置元信息(configuration metadata)创建并初始化ApplicationContext 。可以通过 XML 、 Java Code 、 注解 。
  • 对于每个Bean,其依赖关系以属性、构造函数参数或静态工厂方法的参数的形式表示。在实际创建Bean时,这些依赖项会被提供给Bean。
  • 每个属性或构造函数参数都是要设置值的实际实际定义,或者对容器中的另一个Bean的引用。
  • 作为值的每个属性或构造函数参数都将从其指定的格式转换为该属性或构造参数的实际类型。默认情况下,Spring可以将字符串格式提供的值转换为所有内置类型,如intlongStringboolean

Spring容器在创建容器时会校验每个Bean的配置。当容器被创建时,单例Bean和预实例化(pre-instantiated)的Bean也会被创建。

循环依赖(Circular dependencies)

如果主要使用基于构造器注入,则有可能创建不可解析的循环依赖场景。
如果A类需要通过构造函数注入的 b 类实例,而 b 类需要通过构造函数注入的 a 类实例。如果将 bean 配置为类 a 和类 b 相互注入,那么 Spring IoC 容器在运行时检测到这个循环引用,并抛出 BeanCurrentlyInCreationException。
一个可能的解决方案是改为setter注入。尽管不推荐使用setter注入。

setter依赖注入示例

<bean id="exampleBean" class="examples.ExampleBean"><!-- setter injection using the nested ref element --><property name="beanOne"><ref bean="anotherExampleBean"/></property><!-- setter injection using the neater ref attribute --><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 injection using the nested ref element --><constructor-arg><ref bean="anotherExampleBean"/></constructor-arg><!-- constructor injection using the neater ref attribute --><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;}
}

静态工厂方法实际与构造函数完全相同。

1.4.2 依赖和详细配置

你可以通过 xml 配置 java.util.Properties 实例。

<bean id="mappings"class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer"><!-- typed as a java.util.Properties --><property name="properties"><value>jdbc.driver.className=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3306/mydb</value></property>
</bean>

Spring容器将 <value/> 元素内的文本使用 PropertyEditor 机制转换为 java.util.Properties 实例。这是一个方便的快捷方式。
引用父容器对象

<!-- in the parent context -->
<bean id="accountService" class="com.something.SimpleAccountService"><!-- insert dependencies as required as here -->
</bean>
<!-- in the child (descendant) context -->
<bean id="accountService" <!-- bean name is the same as the parent bean -->class="org.springframework.aop.framework.ProxyFactoryBean"><property name="target"><ref parent="accountService"/> <!-- notice how we refer to the parent bean --></property><!-- insert other configuration and dependencies as required here -->
</bean>

内部Bean

内部Bean定义不需要定义 ID 或 name 。如果指定,容器则不使用此值作为标识符。容器在创建时忽略 scope 标志,因为内部bean总是匿名的且总是由外部bean创建。

集合

<list/>, <set/>, <map/>, and <props/> 与Java中的集合相对应。

<bean id="moreComplexObject" class="example.ComplexObject"><!-- results in a setAdminEmails(java.util.Properties) call --><property name="adminEmails"><props><prop key="administrator">administrator@example.org</prop><prop key="support">support@example.org</prop><prop key="development">development@example.org</prop></props></property><!-- results in a setSomeList(java.util.List) call --><property name="someList"><list><value>a list element followed by a reference</value><ref bean="myDataSource" /></list></property><!-- results in a setSomeMap(java.util.Map) call --><property name="someMap"><map><entry key="an entry" value="just some string"/><entry key ="a ref" value-ref="myDataSource"/></map></property><!-- results in a setSomeSet(java.util.Set) call --><property name="someSet"><set><value>just some string</value><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;}
}

带P、C命名空间

<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/beanshttps://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/beanshttps://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="something@somewhere.com"/></bean><!-- c-namespace declaration with argument names --><bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
</beans>

1.4.3 Depends-On

表明某个Bean在实例化之前需要依赖其他Bean才能完成实例化过程。

<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
<bean id="manager" class="ManagerBean" />

1.4.4 Bean延迟初始化

默认情况下, 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不是延迟初始化。

1.4.5 自动装配(Autowiring Collaborators)

自动装配有以下优点:

  • 自动装配可以显著减少构造函数参数。
  • 自动装配可以根据对象的发展自动更新。

自动装配有四种模式:

| no

默认值。Bean引用需要使用 ref 元素定义。对于大型系统,不推荐更改默认配置,因为显示指定协作者可以提供更好的控制和清晰度。在某种程序上,它记录了一个系统的结构
byName
byType
constructor

自动装配局限性和缺点

  • 属性和构造器参数设置中的显式依赖项始终会覆盖自动装配。不能自动装配简单属性,如原语(primitives)、Strings和class(以及这种简单属性的数组)
  • 自动装配不如显式装配精确
  • 对于可能从Spring容器中生成文档工具。连接信息可能不可用
  • 容器内的多个bean定义可能与要自动装配的setter方法或构造函数参数指定的类型匹配。 对于数组,集合或Map实例,这不一定是问题。 但是,对于需要单个值的依赖项,不会任意解决此歧义。 如果没有唯一的bean定义可用,则会引发异常。

你有如下几种选择:

  • 放弃自动装配
  • 设置 autowire-candidate 为 false 来避免为Bean定义进行自动装配
  • 设置Bean的主属性为 primary
  • 基于注解实现更细粒度的控制

1.4.6 方法注入

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 the ApplicationContextAware interface, and by making a getBean("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 CommandCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.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容器高级特性。

查找方法注入(Lookup Method Injection )

查找方法注入是容器重写容器所管理的Beans的方法以及返回容器中另一个命名的Bean的能力。查找通常涉及 原型 。Spring框架通过使用 CGLIB 字节码生成的能力动态生成覆盖该方法的子类,从而实现此方法的注入。

	<bean id="myCommand" class="com.clarencezero.lookupmethodinject.Command" scope="prototype"><property name="state" value="1" /><!-- inject dependencies here as required --></bean><!-- commandProcessor uses statefulCommandHelper --><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 interfaceCommand command = createCommand();// set the state on the (hopefully brand new) Command instancecommand.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 包中)非常有用。

1.5 Bean Scopes

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 上下文中有效。

1.5.1 单例

有且只有一个。对于这个命名的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默认作用域。

1.5.2 原型

每次创建都会创建新的实例对象。通常,应该为所有有状态的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后置处理器 。

1.5.3 单例Bean依赖原型Bean

如果直接注入,则原型Bean将会是仅有的一个。如果你希望单例Bean在运行时重复获取原型Bean的新实例对象,不能通过依赖注入,因为注入只会出现一次。如果需要每次获取原型Bean,参照方法注入。

1.5.4 Request、Session、Applicaton、WebSocket Scopes

这些域只用做 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 在调用链的下一级可用。

Request scope

<bean id="loginAction" class="com.something.LoginAction" scope="request"/>

为每个HTTP请求创建新的 LoginAction 实例。可以使用注解 @RequestScope 交组件指派给request作用域。

@RequestScope
@Component
public class LoginAction {// ...
}

Session Scope

<bean id="userPreferences" class="com.something.UserPreferences" scope="session"/>

UserPreferences 有效地限定在HTTP Session级别。

@SessionScope
@Component
public class UserPreferences {// ...
}

Application Scope

<bean id="appPreferences" class="com.something.AppPreferences" scope="application"/>

AppPreferences 的作用级别是 ServletContext 。有点类似Spring的单例Bean,但两者不相同。

@ApplicationScope
@Component
public class AppPreferences {// ...
}

Scoped Beans as Dependencies

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容器为用 <aop:scoped-proxy/> 元素标记的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>

1.5.5 定制作用域

Bean的作用域机制是可扩展的。用户可以定义自己的作用域,甚至可以重新定义现有的作用域,但后者被认为是不好的实践,而且不能覆盖内置的单例和原型作用域。

创建自定义范围

你需要实现 org.springframework.beans.factory.config.Scope 接口,该接口有四个方法用于从作用域中获取对象、从作用域中移除对象、销毁对象。

使用自定义作用域

使用Spring容器注册一个新的作用域方法,这个方法是由ConfigurableBeanFactory 接口声明,可以从 ApplicationContext 应用上下文中获取 BeanFactory 属性获得。

void registerScope(String scopeName, Scope scope);

1.6 自定义Bean的性质(Customizing the Nature of a Bean)

Spring提供了许多接口,你可以使用它们来自定义Bean的性质。归类如下:

  • Lifecycle Callbacks (生命周期回调)
  • ApplicationContextAware 和 BeanNameAware
  • 其他 Aware 接口

1.6.1 Lifecycle Callbacks 生命周期回调

要与 bean 生命周期的容器管理交互,您可以实现 Spring InitializingBean  和 DisposableBean  接口。容器为前者调用 afterPropertiesSet()  ,为后者调用 destroy()  ,让 bean 在初始化和销毁 bean 时执行某些操作。

JSR-250@PostConstruct 和@predestroy 注释通常被认为是在现代 Spring 应用程序中接收生命周期回调的最佳实践。使用这些注释意味着您的 bean 没有耦合到特定于 spring 的接口。

在Spring内部框架中使用 BeanPostProcessor 实现来处理它能够找到并调用适当方法的任何回调接口。如果用户需要自定义特性或其他Spring默认不提供的生命周期行为,也可以自己实现 BeanPostProcessor 。除了初始化和销毁回调之外,Spring管理的对象还可以实现 Lifecycle 接口,以便这些对象能够参与启动和关闭过程,这是由容器自身的生命周期驱动的。

初始化回调(Initialization Callbacks)

org.springframework.beans.factory.InitializingBean 该接口允许在 对象属性注入之后 进行初始化工作。该接口指定了一个方法:

void afterPropertiesSet() throws Exception;

不建议使用该接口,因为会与代码进行耦合。建议使用JSR-250规范的注解 @PostConstruct 指定对象初始化方法。也可以基于XML配置 init-method 指定初始化方法名称。如下:

<bean id="exampleInitBean" class="examples.ExampleBean" init-method="init"/>

销毁回调(Destruction Callbacks)

实现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.

Combining Lifecycle Mechanisms

在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() 回调
    • ③通过XML自定义 init() 方法
  • 销毁回调顺序

    • ①注解 @PreDestory
    • ②实现接口 DisposableBean 的方法 destory()  回调
    • ③通过XML自定义 destory() 方法

启动和关闭回调(Startup and Shutdown 回调

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 的对象将首先启动,最后停止。

在非Web应用程序中优雅关闭Spring IoC容器

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...}
}

1.6.2 ApplicationContextAware和BeanNameAware

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;
}

1.6.3 其他Aware接口

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

1.7 Bean元信息继承(Bean Definition Inheritance)

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>

1.8 容器扩展点

1.8.1使用 BeanPostProcessor 自定义Beans

BeanPostProcessor 接口定义了回调方法,可以实现该方法提供自己的(或覆盖容器默认实现)实例化逻辑、依赖解析逻辑等等。如果想要在容器完成实例化、配置和初始化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 <i>before</i> 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.* <p>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*/@Nullabledefault Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}/*** Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> 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.* <p>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.* <p>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.* <p>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*/@Nullabledefault 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 as InitializingBean.afterPropertiesSet() or any declared init 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 the org.springframework.beans.factory.config.BeanPostProcessor interface, clearly indicating the post-processor nature of that bean. Otherwise, the ApplicationContext cannot autodetect it by type before fully creating it. Since a BeanPostProcessor 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实例与AOP自动代理

实现 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-ispublic 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();}
}

1.8.2 自定义配置元数据 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,如注册自定义属性编辑器。

BeanFactoryPostProcessor

You can use the PropertySourcesPlaceholderConfigurer to externalize property values from a bean definition in a separate file by using the standard Java Properties 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.

PropertyOverrideConfigurer

The PropertyOverrideConfigurer, another bean factory post-processor, resembles the PropertySourcesPlaceholderConfigurer, but unlike the latter, the original definitions can have default values or no values at all for bean properties. If an overriding Properties file does not have an entry for a certain bean property, the default context definition is used.

1.8.3 自定义实例化逻辑(FactoryBean)

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本身。

1.9 基于注解的容器配置

BeanPostProcessor 后置处理器结合使用是扩展Spring IoC容器常用的方法。

Spring IoC隐式注册的后置处理器有

  • AutowiredAnnotationBeanPostProcessor
  • CommonAnnotationBeanPostProcessor
  • PersistenceAnnotationBeanPostProcessor
  • RequiredAnnotationBeanPostProcessor

1.9.1 @Required

应用 setter 方法

public class SimpleMovieLister {private MovieFinder movieFinder;@Requiredpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// ...
}

如果在容器中通过依赖查找找不到合适的bean,则会抛出异常。从而避免以后出现空指针情况。自Spring5.1废弃。

1.9.2 @Autowired

可以使用 JSR 330的@inject 注释来代替本节中包含的示例中 Spring 的@autowired 注释。

public class MovieRecommender {private final CustomerPreferenceDao customerPreferenceDao;@Autowiredpublic 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方法上@Autowiredpublic 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 {@Autowiredpublic void setMovieFinder(Optional<MovieFinder> movieFinder) {...}
}

当然,也可以使用 @Nullable

public class SimpleMovieLister {@Autowiredpublic void setMovieFinder(@Nullable MovieFinder movieFinder) {...}
}

你可以通过 @Autowire 注入以下所熟知的依赖

  • BeanFactory
  • ApplicationContext
  • Environment
  • ResourceLoader
  • ApplicationEventPublisher
  • MessageSource

这些接口的扩展(如ConfigurableApplicationContext和ResourcePatternResolver)等。

The @Autowired, @Inject, @Value, and @Resource  annotations are handled by Spring BeanPostProcessor  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方法显式“连接”这些类型。

1.9.3 @Primary

由于按类型注入可能会有多个候选Bean,所以通常需要对选择过程有更多的控制。 @Primary 表示优先选择特定的Bean。

@Configuration
public class MovieConfiguration {@Bean@Primarypublic MovieCatalog firstMovieCatalog() { ... }@Beanpublic MovieCatalog secondMovieCatalog() { ... }// ...
}

1.9.4 @Qualifiers

当对选择过程进行更多的控制时,可以使用注解 @Qualifiers ,通过将限定符与特定参数关联起来,从而缩小类型匹配范围。

public class MovieRecommender {@Autowired@Qualifier("main")private MovieCatalog movieCatalog;// ...
}
<?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:context="http://www.springframework.org/schema/context"xsi:schemaLocation="http://www.springframework.org/schema/beanshttps://www.springframework.org/schema/beans/spring-beans.xsdhttp://www.springframework.org/schema/contexthttps://www.springframework.org/schema/context/spring-context.xsd"><context:annotation-config/><bean class="example.SimpleMovieCatalog"><qualifier value="main"/> <!-- inject any dependencies required by this bean --></bean><bean class="example.SimpleMovieCatalog"><qualifier value="action"/> <!-- inject any dependencies required by this bean --></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();
}

1.9.5 泛型自动装配限定符

除了 @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 <Integer> generic
// Store<String> beans will not appear in this list
@Autowired
private List<Store<Integer>> s;

1.9.6 CustomAutowireConfigurer

Customautowireconfigureer 是一个 BeanFactoryPostProcessor,它允许您注册自己的自定义限定符注释类型,即使它们没有注释 Spring 的@qualifier 注释。下面的示例演示如何使用 CustomAutowireConfigurer:

<bean id="customAutowireConfigurer"class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer"><property name="customQualifierTypes"><set><value>example.CustomQualifier</value></set></property>
</bean>

AutowireCandidateResolver 通过以下方式确定自动装配候选者:

  • The autowire-candidate value of each bean definition
  • Any default-autowire-candidates patterns available on the <beans/> element
  • The presence of @Qualifier annotations and any custom annotations registered with the CustomAutowireConfigurer

当多个Bean符合候选项时,主要看是 @Primary 是否为true。

1.9.7 @Resource

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;}
}

1.9.8 @Value

@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方法@Beanpublic static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {return new PropertySourcesPlaceholderConfigurer();}
}

如果属性配置解析失败,则Spring初始化失败。
Spring BeanPostProcessor 在场景后面使用 ConversionService 来处理将@value 中的 String 值转换为目标类型的过程。如果你想为你自己的定制类型提供转换支持,你可以提供你自己的 ConversionService bean 实例,如下面的例子所示:

@Configuration
public class AppConfig {@Beanpublic 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;}
}

1.9.9 @PostConstruct和@PreDestroy

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 {@PostConstructpublic void populateMovieCache() {// populates the movie cache upon initialization...}@PreDestroypublic void clearMovieCache() {// clears the movie cache upon destruction...}
}

1.10 类路径扫描以及组件管理

1.10.1 @Component and Further Stereotype Annotations

@Component 是Spring管理的组件的通用类型, @Repository、@Service、@Controller 是对 @Component  的领域细分,分别用于持有层、服务层、表示层。虽然说可以直接使用 @Component ,但是如果对组件进行细分注解,那么类会更适合工具进行处理或与切面相关。也可能在Spring框架版本中承载额外的语义。

1.10.2 使用元注解以及组合注解

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}.* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.*/@AliasFor(annotation = Scope.class)ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;}

1.10.3 自动检测类以及注册Bean元信息

Spring可以自动检测类以及注册相应的元信息。比如:

@Service
public class SimpleMovieLister {private MovieFinder movieFinder;public SimpleMovieLister(MovieFinder movieFinder) {this.movieFinder = movieFinder;}
}

要自动检测这些类并注册相应的 bean,需要将@componentscan 添加到@configuration 类中。如果使用XML,则需要隐式启用 <context:annotation-config> 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 都隐式地包含在内。

1.10.4 使用过滤器自定义扫描

| 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 {...
}

1.10.5 在组件中定义Bean元信息

常规 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只能重写非静态方法。

1.10.6 命名自动检测的组件

当一个组件作为扫描过程的一部分被自动检测时,它的 bean 名称由该扫描程序已知的 BeanNameGenerator 策略生成。
当你不满足默认的命名策略,你可以提供自定义bean命令策略。首先,实现BeanNameGenerator接口,并确保包含默认的无参构造函数。

@Configuration
@ComponentScan(basePackages = "org.example", nameGenerator = MyNameGenerator.class)
public class AppConfig {// ...
}

1.10.7 为自动检测组件提供作用域 @Scoped

@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: nointerfaces, and targetClass. For example, the following configuration results in standard JDK dynamic proxies:

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {// ...
}

1.10.8 @Qualifier

@Component
@Qualifier("Action")
public class ActionMovieCatalog implements MovieCatalog {// ...
}

1.10.9 生成候选组件索引

若要生成索引,请向每个模块添加一个附加依赖项,该模块包含组件扫描指令的目标组件。下面的例子展示了如何使用 Maven:

<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context-indexer</artifactId><version>5.3.0-SNAPSHOT</version><optional>true</optional></dependency>
</dependencies>

该过程生成一个 META-INF/spring.components 文件,该文件包含在 jar 文件中。

1.11 使用JSR 330标准注解

需要添加额外的依赖

<dependency><groupId>javax.inject</groupId><artifactId>javax.inject</artifactId><version>1</version>
</dependency>

1.11.1 依赖注入 @Inject以及@Named

可以使用 @javax.inject.Inject 替换 @Autowired 。

import javax.inject.Inject;public class SimpleMovieLister {private MovieFinder movieFinder;@Injectpublic 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;@Injectpublic void setMovieFinder(@Named("main") MovieFinder movieFinder) {this.movieFinder = movieFinder;}// ...
}

1.11.2 @ManagedBean

可以使用 @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;@Injectpublic void setMovieFinder(MovieFinder movieFinder) {this.movieFinder = movieFinder;}// ...
}

1.11.3 JSR-330标准局限性

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 方法组合使用。

1.12 基于Java容器配置

如何在Java代码中使用注解来配置Spring容器。

1.12.1 @Bean、@Configuration基本概念

Spring最新配置的核心是 @Configuration 和 @Bean 两个注解。
@Bean注解用于指示方法实例化,配置和初始化要由Spring IoC容器管理的新对象。@Bean扮演了与XML中 <bean/> 一样的角色。但是通常它与@Configuration 配合使用。
用@Configuration注释类表示该类的主要目的是作为Bean定义的来源。 此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义Bean间的依赖关系。 最简单的@Configuration类的内容如下:

@Configuration
public class AppConfig {@Beanpublic MyService myService() {return new MyServiceImpl();}
}

如果在未使用@Configuration注释的类中声明@Bean方法,则将它们称为以“精简”模式进行处理。
与 full @Configuration  不同,lite@Bean 方法不能声明 bean 间的依赖关系。这里最直接的影响是CGLIB不会代理该对象。
在常见的场景中,@Bean方法需要声明在@Configuration类中,确保始终使用full模式。这样可以保证跨方法引用会重定向到容器生命周期管理。

1.12.2 使用AnnotationConfigApplicationContext实例化容器

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);
}

对Web的支持 AnnotationConfigWebApplicationContext

当你需要配置Spring ContextLoaderListener servlet监听器、Spring MVC DispatcherServlet 时,可以使用此实现。

1.12.3 @Bean

@Bean和XML中的一样,同时支持 init-method 、 destory-method 、 autowiring 等。

@Configuration
public class AppConfig {@Beanpublic 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();}
}

@Scope

指定bean的作用域。Spring默认作用域为单例。

Bean的别名

@Configuration
public class AppConfig {@Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})public DataSource dataSource() {// instantiate, configure and return DataSource bean...}
}

注入Bean之间存有依赖关系

@Configuration
public class AppConfig {@Beanpublic BeanOne beanOne() {return new BeanOne(beanTwo());}@Beanpublic BeanTwo beanTwo() {return new BeanTwo();}
}

查找方法注入

这是很少使用到的高级特性。当一个单例对象被注入了原型对象,使用查找方法注入会非常有用。

@Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {AsyncCommand command = new AsyncCommand();// inject dependencies here as requiredreturn command;
}@Bean
public CommandManager commandManager() {// return new anonymous implementation of CommandManager with createCommand()// overridden to return a new prototype Command objectreturn new CommandManager() {protected Command createCommand() {return asyncCommand();}}
}

1.12.5 组成基于Java的配置

使用@import注解

类似XML的 <import/> 标签来帮助模块化配置。还允许从另一个配置类中加载@Bean定义。

@Configuration
public class ConfigA {@Beanpublic A a() {return new A();}
}@Configuration
@Import(ConfigA.class)
public class ConfigB {@Beanpublic B b() {return new B();}
}
  相关解决方案