该文章是我读Spring英文官方文档的读书笔记,方便以后快速的回忆文档里讲述的内容,而不用再去读一遍官方文档。
文章内容精简掉了官方文档的一些比较浅显易懂的用法,一半是翻译,然后加入部分自己的理解,可以使读者快速的了解Spring提供了些什么功能,以及在什么情况下使用。
该文章只适合有一定Spring使用经验的人,因为博客内容不会讲解某一个功能具体怎么使用,而是只列出Spring有某某功能,以及简单的使用示例。
基于的Spring文档是Spring framework的core模块,版本5.0.6。
官方网站https://docs.spring.io/spring/docs/5.0.6.RELEASE/spring-framework-reference/core.html#spring-core
下载路径http://repo.springsource.org/libs-release-local/org/springframework/spring/5.0.6.RELEASE/
-
- 1.1 Introduction
- 1.2 Container overview
- 1.3 Bean overview
- 1.4 Dependencies
- 1.5 Bean scopes bean作用域
- 1.6 Customizing the nature of a bean 定制bean的特性
- 1.7 Bean definition inheritance (Bean definition继承)
- 1.8. Container Extension Points 容器扩展点
- 1.9. Annotation-based container configuration 基于注解配置容器
- 1.10. Classpath scanning and managed components 扫描类路径和管理component
- 1.10.2. Meta-annotations 元注解
- 1.10.3. Automatically detecting classes and registering bean definitions 自动检测类和注册bean definition
- 1.10.4. Using filters to customize scanning 使用filter实现自定义扫描组件
- 1.10.5. Defining bean metadata within components 使用component定义bean元数据
- 1.10.6. Naming autodetected components 为自动检测的组件命名
- 1.10.7. Providing a scope for autodetected components 为自动检测的组件提供scope
- 1.10.9. Generating an index of candidate components 为component生成index
- 1.11. Using JSR 330 Standard Annotations 使用JSR330的标准注解
- 1.12. Java-based container configuration 基于Java代码的容器配置
- 1.12.1. Basic concepts: @Bean and @Configuration
- Full @Configuration vs ‘lite’ @Bean mode? 完整的@Configuration VS 简化的@Bean模式
- 1.12.2. Instantiating the Spring container using AnnotationConfigApplicationContext
- 1.13. Environment abstraction 抽象Environment
- 1.14. Registering a LoadTimeWeaver 注册LoadTimeWeaver
- 1.15. Additional capabilities of the ApplicationContext ApplicationContext的以为额外的能力
1.1 Introduction
- org.springframework.beans 和 org.springframework.context 是spring ioc容器的基础。
- ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 是ApplicationContext的开箱即用的实现。
1.2 Container overview
- 基于xml的配置方法,将bean元素放在元素内。而java configuration的配置方法,在有@Configuration的类里使用@Bean注解。
- 基本的XML_based ocnfiguration metadata
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
<!-- more bean definitions go here -->
</beans>
- 使用如下代码来加载来自CLASSPATH的配置文件,启动spring ApplicationContext。
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");
- 在xml配置文件中,使用import来加载其他配置文件
<beans>
<import resource="services.xml"/>
<import resource="resources/messageSource.xml"/>
<import resource="/resources/themeSource.xml"/>
<bean id="bean1" class="..."/>
<bean id="bean2" class="..."/>
</beans>
- 使用容器
// 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和reader delegates,可以更灵活的配置容器。
GenericApplicationContext context = new GenericApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
context.refresh();
1.3 Bean overview
spring ioc container 管理一个或多个bean,bean来自configuration metadata,比如xml的标签。Spring使用BeanDefinition来定义bean,其包含以下元数据
- A package-qualified class name: typically the actual implementation class of the bean being defined.
- Bean behavioral configuration elements, which state how the bean should behave in the container (scope, lifecycle callbacks, and so forth).
- References to other beans that are needed for the bean to do its work; these references are also called collaborators or dependencies.
- Other configuration settings to set in the newly created object, for example, the number of connections to use in a bean that manages a connection pool, or the size limit of the pool.
用表格表示BeanDefinition的properties。Explained in 列表示该属性的详细介绍的章节名
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 |
* ApplicationContext的实现还允许注册在容器外创建的对象,可以通过调用ApplicationContext的getBeanFactory() 获得BeanFactory的一个实现类DefaultListableBeanFactory,该实现类通过registerSingleton(..) 和 registerBeanDefinition(..)注册。
1.3.1 Naming beans
- 在XML-based configuration metadata中,使用id和/或name为bean命名。一个bean只能有一个id,但是可以有多个name作为别名。使用name属性指定别名是,用逗号;分号或者空格将多个别名分隔。如果没有为bean指定id或name,name容器会自动分配一个唯一的标识符。
- 有时候在bean定义的地方设置所以别名并不能满足所有情况,这时可以使用元素,在任何地方设置别名。
<alias name="fromName" alias="toName"/>
1.3.2 实例化bean
- 一般情况下,在xml配置文件中,容器使用元素的class属性来实例化bean,但是也可以使用factory method来实例化bean。
Class属性有以下两种用处:
- 代表性的用处是,容器使用反射生成该属性指定的类的实例。
- 指明包含静态工厂方法所在的类。
- 使用反射,调用constructor实例化bean
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
- 使用静态工厂方法实例化bean。其中class属性指的是包含工厂方法的类,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。这种情况下class属性为空,factory-bean属性指向工厂方法所在的bean的引用。
<!-- the factory bean, which contains a method called createInstance() -->
<bean id="serviceLocator" class="examples.DefaultServiceLocator">
<!-- inject any dependencies required by this locator bean -->
</bean>
<!-- the bean to be created via the factory bean -->
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
public class DefaultServiceLocator {
private static ClientService clientService = new ClientServiceImpl();
public ClientService createClientServiceInstance() {
return clientService;
}
}
1.4 Dependencies
1.4.1 Dependency injection
基于构造函数的依赖注入
如果容器能够推断出相应的参数,则不用指定参数类型或者index,如下:
package x.y;
public class Foo {
public Foo(Bar bar, Baz baz) {
// ...
} }
<beans>
<bean id="foo" class="x.y.Foo">
<constructor-arg ref="bar"/>
<constructor-arg ref="baz"/>
</bean>
<bean id="bar" class="x.y.Bar"/>
<bean id="baz" class="x.y.Baz"/>
</beans>
如果不能推断出相应参数,则需要指定,如下:
package examples;
public class ExampleBean {
// Number of years to calculate the Ultimate Answer
private int years;
// The Answer to Life, the Universe, and Everything
private String ultimateAnswer;
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
} }
- 可以指定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
- 也可以指定参数name。使用这种方式,在编译代码时,必须指定debug flag为enabled,这样spring才能找到构造函数的正确参数名。如果不能指定debug flag为enabled,那么可以使用@ConstructorProperties JDK annotation来指定构造函数。
<bean id="exampleBean" class="examples.ExampleBean">
<constructor-arg name="years" value="7500000"/>
<constructor-arg name="ultimateAnswer" value="42"/>
</bean>
package examples;
public class ExampleBean {
// Fields omitted
@ConstructorProperties({
"years", "ultimateAnswer"})
public ExampleBean(int years, String ultimateAnswer) {
this.years = years;
this.ultimateAnswer = ultimateAnswer;
}
}
基于setter的依赖注入
就是使用set方法注入
使用构造器注入还是setter注入?
对于强制要求的依赖,使用构造器注入,对于可选的依赖,使用setter注入。
在setter注入上添加@Required注解,也可以强制注入依赖。
dependency resolution process
- 使用构造器注入,有可能会存在循环依赖的问题。
- spring容器会发现循环依赖,或者依赖的bean不存在的情况,并抛出异常。
- spring会在尽可能晚的时候加载bean,对于一些需要在使用时才初始化的bean,如果存在加载异常,并不会在spring启动的时候发现,只会在使用的时候发现问题。
- 加载bean默认是pre-instantiated的,就是启动容器的时候就初始化bean。也可以设置为需要使用到的时候才加载。
依赖注入的例子
xml的例子,通过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"/>
通过构造器注入
<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"/>
通过静态工厂方法注入
<bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
<constructor-arg ref="anotherExampleBean"/>
<constructor-arg ref="yetAnotherBean"/>
<constructor-arg value="1"/>
</bean>
<bean id="anotherExampleBean" class="examples.AnotherBean"/>
<bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
} }
1.4.2 依赖和配置的细节
直接类型,原始类型、String等
- 元素的value属性
public class ExampleBean {
// a private constructor
private ExampleBean(...) {
...
}
// a static factory method; the arguments to this method can be
// considered the dependencies of the bean that is returned,
// regardless of how those arguments are actually used.
public static ExampleBean createInstance (
AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
ExampleBean eb = new ExampleBean (...);
// some other operations...
return eb;
} }
- 也可以使用p-namespace
<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
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="myDataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close"
p:driverClassName="com.mysql.jdbc.Driver"
p:url="jdbc:mysql://localhost:3306/mydb"
p:username="root"
p:password="masterkaoli"/>
</beans>
- 也可以使用java.util.Properties实例。spring会通过PropertyEditor,将元素里的文字转换为java.util.Properties实例。
<bean id="mappings"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<!-- typed as a java.util.Properties -->
<property name="properties">
<value>
jdbc.driver.className=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mydb
</value>
</property>
</bean>
对其他bean的引用
- 使用在 或者使用ref属性,可以引用bean
<ref bean="someBean"/>
- 使用parent属性,指定引用父容器的bean
<!-- in the parent context -->
<bean id="accountService" class="com.foo.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 id="outer" class="...">
<!-- instead of using a reference to a target bean, simply define the target bean
inline -->
<property name="target">
<bean class="com.example.Person"> <!-- this is the inner bean -->
<property name="name" value="Fiona Apple"/>
<property name="age" value="25"/>
</bean>
</property>
</bean>
集合
<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>
集合合并,当子类继承父类的集合属性时,可以继承父类已有的集合的值。merge=true
指明了继承父类的集合值。
<beans>
<bean id="parent" abstract="true" class="example.ComplexObject">
<property name="adminEmails">
<props>
<prop key="administrator">administrator@example.com</prop>
<prop key="support">support@example.com</prop>
</props>
</property>
</bean>
<bean id="child" parent="parent">
<property name="adminEmails">
<!-- the merge is specified on the child collection definition -->
<props merge</