容器概览

配置元数据(装配方式)

XML配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. https://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="..." class="...">
  7. <!-- collaborators and configuration for this bean go here -->
  8. </bean>
  9. <bean id="..." class="...">
  10. <!-- collaborators and configuration for this bean go here -->
  11. </bean>
  12. <!-- more bean definitions go here -->
  13. </beans>

组合XML文件

  1. <beans>
  2. <import resource="services.xml"/>
  3. <import resource="resources/messageSource.xml"/>
  4. <import resource="/resources/themeSource.xml"/>
  5. <bean id="bean1" class="..."/>
  6. <bean id="bean2" class="..."/>
  7. </beans>

注解

@ComponentScan @Component @Service @Repository @Controller

JavaConfig

@Configuration @Import @Bean on method

实例化与使用容器

ClassPathXmlApplicationContext

  1. ApplicationContext context = new ClassPathXmlApplicationContext("services.xml", "daos.xml");

GenericApplicationContext

  1. GenericApplicationContext context = new GenericApplicationContext();
  2. new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml", "daos.xml");
  3. context.refresh();

XmlWebApplicationContext

一般由Web容器初始化来初始化,常见的XML配置

  1. <context-param>
  2. <param-name>contextConfigLocation</param-name>
  3. <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
  4. </context-param>
  5. <listener>
  6. <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  7. </listener>

Bean概览

定义Bean

BeanDefinition对象主要包含如下的几部分属性

属性 描述
Class 用来实例化一个Bean
Name 用来指定一个Bean的名称,用Id或Name或别名来描述
Scope 用来限定Bean的作用域
Constructor Argument 用来实现依赖注入
Properties 用来实现依赖注入
Autowiring mode 依赖注入模式,如byName
Lazy Initialization mode 延迟初始化模式
Initialization mode 初始化模式
Destruction mode Bean销毁处理

命名Bean

id 最常用 name:id的别名,可指定多个,用逗号分隔 alias:跨多个系统间调用,可以定义一个别名

示例:

  1. <alias name="myApp-dataSource" alias="subsystemA-dataSource"/>

实例化Bean

通过构造方法

注引用一个内部类作为Bean,使用外部类加$符号,标识一个嵌套类

  1. <bean id="helloSpring" name="aliasForId" class="com.example.start.springdemo.spring.HelloSpring" >
  2. <property name="innerSpring">
  3. <bean class="com.example.start.springdemo.spring.HelloSpring$HelloInnerSpring"/>
  4. </property>
  5. </bean>

通过静态工厂方法

  1. <bean id="clientService"
  2. class="examples.ClientService"
  3. factory-method="createInstance"/>
  4. 对应Class
  5. public class ClientService {
  6. private static ClientService clientService = new ClientService();
  7. private ClientService() {}
  8. public static ClientService createInstance() {
  9. return clientService;
  10. }
  11. }

通过实例工厂方法

  1. <!-- the factory bean, which contains a method called createInstance() -->
  2. <bean id="serviceLocator" class="examples.DefaultServiceLocator">
  3. <!-- inject any dependencies required by this locator bean -->
  4. </bean>
  5. <!-- the bean to be created via the factory bean -->
  6. <bean id="clientService"
  7. factory-bean="serviceLocator"
  8. factory-method="createClientServiceInstance"/>
  9. public class DefaultServiceLocator {
  10. private static ClientService clientService = new ClientServiceImpl();
  11. public ClientService createClientServiceInstance() {
  12. return clientService;
  13. }
  14. }

工厂类可以指定多个工厂方法来创建Bean

  1. <bean id="serviceLocator" class="examples.DefaultServiceLocator">
  2. <!-- inject any dependencies required by this locator bean -->
  3. </bean>
  4. <bean id="clientService"
  5. factory-bean="serviceLocator"
  6. factory-method="createClientServiceInstance"/>
  7. <bean id="accountService"
  8. factory-bean="serviceLocator"
  9. factory-method="createAccountServiceInstance"/>
  10. public class DefaultServiceLocator {
  11. private static ClientService clientService = new ClientServiceImpl();
  12. private static AccountService accountService = new AccountServiceImpl();
  13. public ClientService createClientServiceInstance() {
  14. return clientService;
  15. }
  16. public AccountService createAccountServiceInstance() {
  17. return accountService;
  18. }
  19. }

通过FactoryBean接口

FactoryBean是Spring提供的一种扩展点,用来自定义负责的Bean初始化逻辑。通过Bean名称(如myBean)调用getBean时,返回getObject方法生成的Bean,并缓存起来。如果要获取FactoryBean本身的实例,则使用&myBean,示例如下:

  1. public class ClientServiceFactoryBean implements FactoryBean<ClientService> {
  2. @Override
  3. public ClientService getObject() throws Exception {
  4. return new ClientService();
  5. }
  6. @Override
  7. public boolean isSingleton() {
  8. return true;
  9. }
  10. @Override
  11. public Class<?> getObjectType() {
  12. return null;
  13. }
  14. }
  15. XML配置
  16. <bean id="clientServiceFactoryBean" class="com.example.start.springdemo.spring.ClientServiceFactoryBean" />
  17. 使用如下:
  18. 由于是单例,client1 client2是同一个实例
  19. ClientService client1 = (ClientService)context.getBean("clientServiceFactoryBean");
  20. ClientService client2 = (ClientService)context.getBean("clientServiceFactoryBean");
  21. 使用&Bean名称
  22. ClientServiceFactoryBean fb = (ClientServiceFactoryBean)context.getBean("&clientServiceFactoryBean");

依赖

依赖注入

构造方法注入

基本使用

  1. public class SimpleMovieLister {
  2. // the SimpleMovieLister has a dependency on a MovieFinder
  3. private MovieFinder movieFinder;
  4. // a constructor so that the Spring container can inject a MovieFinder
  5. public SimpleMovieLister(MovieFinder movieFinder) {
  6. this.movieFinder = movieFinder;
  7. }
  8. // business logic that actually uses the injected MovieFinder is omitted...
  9. }

容器调用构造方法来完成注入

构造参数解析

构造参数很明确的场景,无模糊参数类型

  1. package x.y;
  2. public class ThingOne {
  3. public ThingOne(ThingTwo thingTwo, ThingThree thingThree) {
  4. // ...
  5. }
  6. }
  7. <beans>
  8. <bean id="beanOne" class="x.y.ThingOne">
  9. <constructor-arg ref="beanTwo"/>
  10. <constructor-arg ref="beanThree"/>
  11. </bean>
  12. <bean id="beanTwo" class="x.y.ThingTwo"/>
  13. <bean id="beanThree" class="x.y.ThingThree"/>
  14. </beans>

构造参数类型匹配

  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <constructor-arg type="int" value="7500000"/>
  3. <constructor-arg type="java.lang.String" value="42"/>
  4. </bean>

指定构造参数下标索引(默认从0开始)

  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <constructor-arg type="int" value="7500000"/>
  3. <constructor-arg type="java.lang.String" value="42"/>
  4. </bean>

指定构造参数名称

  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <constructor-arg name="years" value="7500000"/>
  3. <constructor-arg name="ultimateAnswer" value="42"/>
  4. </bean>

Setter方法注入

容器调用无参构造方法或非静态方法初始化一个Bean后,再调用setter方法注入依赖

  1. public class SimpleMovieLister {
  2. // the SimpleMovieLister has a dependency on the MovieFinder
  3. private MovieFinder movieFinder;
  4. // a setter method so that the Spring container can inject a MovieFinder
  5. public void setMovieFinder(MovieFinder movieFinder) {
  6. this.movieFinder = movieFinder;
  7. }
  8. // business logic that actually uses the injected MovieFinder is omitted...
  9. }

构造方法注入或Setter方法注入?

  • 构造方法注入要求被注入的Bean必须存在,不存在则抛异常,可以做到提前检查,而不用等到运行时
  • Spring 团队推荐使用构造方法注入,方便调用方法可以通过new直接构造对象,便于做单元测试等。如果有多个被注入的Bean,需求考虑是否职责不清晰,是否需要重构,分离关注点
  • Setter方法注入不校验bean是否为null,需要考虑默认值

循环依赖解决方案

使用构造方法注入方式产生的循环依赖,Spring IoC会抛BeanCurrentlyInCreationException,解决方案是采用Setter方法注入。

注Spring内部采用三级Bean缓存的机制解决循环注入,只适用与Setter方法注入场景

依赖注入示例

Case1: Setter方法

  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <!-- setter injection using the nested ref element -->
  3. <property name="beanOne">
  4. <ref bean="anotherExampleBean"/>
  5. </property>
  6. <!-- setter injection using the neater ref attribute -->
  7. <property name="beanTwo" ref="yetAnotherBean"/>
  8. <property name="integerProperty" value="1"/>
  9. </bean>
  10. <bean id="anotherExampleBean" class="examples.AnotherBean"/>
  11. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
  12. public class ExampleBean {
  13. private AnotherBean beanOne;
  14. private YetAnotherBean beanTwo;
  15. private int i;
  16. public void setBeanOne(AnotherBean beanOne) {
  17. this.beanOne = beanOne;
  18. }
  19. public void setBeanTwo(YetAnotherBean beanTwo) {
  20. this.beanTwo = beanTwo;
  21. }
  22. public void setIntegerProperty(int i) {
  23. this.i = i;
  24. }
  25. }

Case2: 构造方法

  1. <bean id="exampleBean" class="examples.ExampleBean">
  2. <!-- constructor injection using the nested ref element -->
  3. <constructor-arg>
  4. <ref bean="anotherExampleBean"/>
  5. </constructor-arg>
  6. <!-- constructor injection using the neater ref attribute -->
  7. <constructor-arg ref="yetAnotherBean"/>
  8. <constructor-arg type="int" value="1"/>
  9. </bean>
  10. <bean id="anotherExampleBean" class="examples.AnotherBean"/>
  11. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
  12. public class ExampleBean {
  13. private AnotherBean beanOne;
  14. private YetAnotherBean beanTwo;
  15. private int i;
  16. public ExampleBean(
  17. AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
  18. this.beanOne = anotherBean;
  19. this.beanTwo = yetAnotherBean;
  20. this.i = i;
  21. }
  22. }

Case3: 静态工厂方法

  1. <bean id="exampleBean" class="examples.ExampleBean" factory-method="createInstance">
  2. <constructor-arg ref="anotherExampleBean"/>
  3. <constructor-arg ref="yetAnotherBean"/>
  4. <constructor-arg value="1"/>
  5. </bean>
  6. <bean id="anotherExampleBean" class="examples.AnotherBean"/>
  7. <bean id="yetAnotherBean" class="examples.YetAnotherBean"/>
  8. public class ExampleBean {
  9. // a private constructor
  10. private ExampleBean(...) {
  11. ...
  12. }
  13. // a static factory method; the arguments to this method can be
  14. // considered the dependencies of the bean that is returned,
  15. // regardless of how those arguments are actually used.
  16. public static ExampleBean createInstance (
  17. AnotherBean anotherBean, YetAnotherBean yetAnotherBean, int i) {
  18. ExampleBean eb = new ExampleBean (...);
  19. // some other operations...
  20. return eb;
  21. }
  22. }

依赖配置详细说明

配置Properties

  1. <bean id="mappings"
  2. class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
  3. <!-- typed as a java.util.Properties -->
  4. <property name="properties">
  5. <value>
  6. jdbc.driver.className=com.mysql.jdbc.Driver
  7. jdbc.url=jdbc:mysql://localhost:3306/mydb
  8. </value>
  9. </property>
  10. <property name="location" value="classpath*:application.properties"/>
  11. </bean>

idref元素引用String Bean

  1. <bean id="theTargetBean" class="..."/>
  2. <bean id="theClientBean" class="...">
  3. <property name="targetName">
  4. <idref bean="theTargetBean"/>
  5. </property>
  6. </bean>
  7. 等同于:
  8. <bean id="theTargetBean" class="..." />
  9. <bean id="client" class="...">
  10. <property name="targetName" value="theTargetBean"/>
  11. </bean>

idref 可以提前校验引用的bean是否存在,xsd4.0后不允许使用idref local标签, 可使用idref bean

引用其他Bean

  1. <ref bean="someBean"/>
  2. 引用parent bean
  3. <!-- in the child (descendant) context -->
  4. <bean id="accountService" <!-- bean name is the same as the parent bean -->
  5. class="org.springframework.aop.framework.ProxyFactoryBean">
  6. <property name="target">
  7. <ref parent="accountService"/> <!-- notice how we refer to the parent bean -->
  8. </property>
  9. <!-- insert other configuration and dependencies as required here -->
  10. </bean>

不推荐使用ref local -> ref bean

内部Bean

  1. <bean id="outer" class="...">
  2. <!-- instead of using a reference to a target bean, simply define the target bean inline -->
  3. <property name="target">
  4. <bean class="com.example.Person"> <!-- this is the inner bean -->
  5. <property name="name" value="Fiona Apple"/>
  6. <property name="age" value="25"/>
  7. </bean>
  8. </property>
  9. </bean>

内部Bean不需要id或name,即使指定了,Spring也不会作为注入标识符。

Collection

The <list/>, <set/>, <map/>, and <props/> elements set the properties and arguments of the Java Collection types List, Set, Map, and Properties, respectively. The following example shows how to use them:

  1. <bean id="moreComplexObject" class="example.ComplexObject">
  2. <!-- results in a setAdminEmails(java.util.Properties) call -->
  3. <property name="adminEmails">
  4. <props>
  5. <prop key="administrator">administrator@example.org</prop>
  6. <prop key="support">support@example.org</prop>
  7. <prop key="development">development@example.org</prop>
  8. </props>
  9. </property>
  10. <!-- results in a setSomeList(java.util.List) call -->
  11. <property name="someList">
  12. <list>
  13. <value>a list element followed by a reference</value>
  14. <ref bean="myDataSource" />
  15. </list>
  16. </property>
  17. <!-- results in a setSomeMap(java.util.Map) call -->
  18. <property name="someMap">
  19. <map>
  20. <entry key="an entry" value="just some string"/>
  21. <entry key ="a ref" value-ref="myDataSource"/>
  22. </map>
  23. </property>
  24. <!-- results in a setSomeSet(java.util.Set) call -->
  25. <property name="someSet">
  26. <set>
  27. <value>just some string</value>
  28. <ref bean="myDataSource" />
  29. </set>
  30. </property>
  31. </bean>

强类型Collection
Spring默认采用类型转换

  1. public class SomeClass {
  2. private Map<String, Float> accounts;
  3. public void setAccounts(Map<String, Float> accounts) {
  4. this.accounts = accounts;
  5. }
  6. }
  7. <beans>
  8. <bean id="something" class="x.y.SomeClass">
  9. <property name="accounts">
  10. <map>
  11. <entry key="one" value="9.99"/>
  12. <entry key="two" value="2.75"/>
  13. <entry key="six" value="3.99"/>
  14. </map>
  15. </property>
  16. </bean>
  17. </beans>

Null and Empty String Value

  1. <bean class="ExampleBean">
  2. <property name="email" value=""/>
  3. </bean>
  4. ==>
  5. exampleBean.setEmail("");
  6. 处理null
  7. <bean class="ExampleBean">
  8. <property name="email">
  9. <null/>
  10. </property>
  11. </bean>
  12. ==>
  13. exampleBean.setEmail(null);

XML 标签简写: p or c

p 命名空间简写property c 命名空间简写constructor-arg

使用depends-on

明确强制让依赖的Bean先初始化

  1. <bean id="beanOne" class="ExampleBean" depends-on="manager,accountDao">
  2. <property name="manager" ref="manager" />
  3. </bean>
  4. <bean id="manager" class="ManagerBean" />
  5. <bean id="accountDao" class="x.y.jdbc.JdbcAccountDao" />

Lazy初始化Bean

Spring 默认为false, 表示容器启动时即主动初始化单例Bean

The effective default is “false”.

  1. <bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
  2. <bean name="not.lazy" class="com.something.AnotherBean"/>

自动注入Bean

自动注入模式

模式 解释
no 默认,表示不使用自动注入,采用显示的ref描述。用以明确指定依赖结构
byName 根据Bean的名称自动注入(在容器中查找bean name)
byType 根据Bean的类型自动注入(在容器中查找类型),如果type 不存在,则抛异常

示例说明:

  1. 定义Bean
  2. <bean id="clientService" class="com.example.start.springdemo.spring.ClientService" factory-method="createInstance"/>
  3. <bean id="helloSpring" name="aliasForId" class="com.example.start.springdemo.spring.HelloSpring"/>
  4. public class HelloSpring {
  5. private ClientService clientService;
  6. /**
  7. * Setter method for property <tt>clientService</tt>.
  8. *
  9. * @param clientService value to be assigned to property clientService
  10. */
  11. public void setClientService(ClientService clientService) {
  12. this.clientService = clientService;
  13. }
  14. }

不使用依赖注入:默认值为no
default-autowire=”default”,内部不自动注入
image.png
default-autowire=”byName”, 内部自动通过Setter方法注入
image.png

Method Injection(方法注入)

通过感知容器(耦合)显示从容器中获取Bean. 实现ApplicationContextAware接口,调用getBean

  1. // a class that uses a stateful Command-style class to perform some processing
  2. package fiona.apple;
  3. // Spring-API imports
  4. import org.springframework.beans.BeansException;
  5. import org.springframework.context.ApplicationContext;
  6. import org.springframework.context.ApplicationContextAware;
  7. public class CommandManager implements ApplicationContextAware {
  8. private ApplicationContext applicationContext;
  9. public Object process(Map commandState) {
  10. // grab a new instance of the appropriate Command
  11. Command command = createCommand();
  12. // set the state on the (hopefully brand new) Command instance
  13. command.setState(commandState);
  14. return command.execute();
  15. }
  16. protected Command createCommand() {
  17. // notice the Spring API dependency!
  18. return this.applicationContext.getBean("command", Command.class);
  19. }
  20. public void setApplicationContext(
  21. ApplicationContext applicationContext) throws BeansException {
  22. this.applicationContext = applicationContext;
  23. }
  24. }

Bean作用域

Scope(作用域)
描述
singleton(单例) 默认,Spring只会创建一个Bean实例
prototype (原型) 创建多个Bean实例
request (请求) Web应用,对应Http Request
session (会话) Web应用,对应Http Session
applicaiton (应用) Web应用,对应Servlet Context
websocket Websocket

单例作用域

定义方式

  1. <bean id="accountService" class="com.something.DefaultAccountService"/>
  2. <!-- the following is equivalent, though redundant (singleton scope is the default) -->
  3. <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>

image.png

原型作用域

通常来说,原型作用域用于有状态的bean, 被注入到不同的bean里,会新产生一个实例。

关键点: 容器调用getBean返回一个新的实例

定义方式如下:

  1. <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>

image.png

注入不同的Dao实例

自定义Bean的性质(Nature of Bean)

生命周期回调(Lifecycle Callback)

Spring API 提供的InitializingBean与DisposableBean,分别提供afterPropertiesSet()与destroy()方法来定义初始化或清理Bean的特殊行为 如果不想耦合Spring内部的接口,可以使用JSR-250 定义的注解@PostConstruct @PreDestroy 如果不想使用@PostConstruct @PreDestroy,又不想与Spring耦合,可以使用init-method, destroy-method

Spring内部都是采用BeanPostProcessor机制来处理Bean 生命周期回调

Initialization Callback

1) 使用InitializingBean, Spring 不推荐使用,因为没有必要耦合Spring,推荐使用@PostConstruct
2) 如果是xml配置,可采用init-method(无参数)
3) 如果是Java Config, 可采用@Bean#initMethod

Destruction Callback

1) 使用DisposableBean
2) 使用destroy-method 或@Bean#destroyMethod
3) 使用@PreDestroy

多种方法的处理流程

Multiple lifecycle mechanisms configured for the same bean, with different initialization methods, are called as follows:

  1. Methods annotated with @PostConstruct
  2. afterPropertiesSet() as defined by the InitializingBean callback interface
  3. A custom configured init() method

Destroy methods are called in the same order:

  1. Methods annotated with @PreDestroy
  2. destroy() as defined by the DisposableBean callback interface
  3. A custom configured destroy() method

容器启动或关闭回调

使用LifecycleProcessor

IoC容器优雅关闭

  1. import org.springframework.context.ConfigurableApplicationContext;
  2. import org.springframework.context.support.ClassPathXmlApplicationContext;
  3. public final class Boot {
  4. public static void main(final String[] args) throws Exception {
  5. ConfigurableApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
  6. // add a shutdown hook for the above context...
  7. ctx.registerShutdownHook();
  8. // app runs here...
  9. // main method exits, hook is called prior to the app shutting down...
  10. }
  11. }

感知容器(Aware)

常用的接口: ApplicationContextAware and BeanNameAware 额外可能需要关注: ApplicationEventPublisherAware

Bean继承

  1. <bean id="inheritedTestBean" abstract="true"
  2. class="org.springframework.beans.TestBean">
  3. <property name="name" value="parent"/>
  4. <property name="age" value="1"/>
  5. </bean>
  6. <bean id="inheritsWithDifferentClass"
  7. class="org.springframework.beans.DerivedTestBean"
  8. parent="inheritedTestBean" init-method="initialize">
  9. <property name="name" value="override"/>
  10. <!-- the age property value of 1 will be inherited from parent -->
  11. </bean>
  12. 不需要实例化Parent Bean
  13. <bean id="inheritedTestBeanWithoutClass" abstract="true">
  14. <property name="name" value="parent"/>
  15. <property name="age" value="1"/>
  16. </bean>
  17. <bean id="inheritsWithClass" class="org.springframework.beans.DerivedTestBean"
  18. parent="inheritedTestBeanWithoutClass" init-method="initialize">
  19. <property name="name" value="override"/>
  20. <!-- age will inherit the value of 1 from the parent bean definition-->
  21. </bean>

自定义扩展点

使用BeanPostProcessor

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. spring 会针对post-processor bean在容器启动时初始化,作特殊处理。bean post-prosessor 作用域容器里的bean, 在bean作初始化阶段之前(init-method,initializingbean,postconstruct)或初始化之后。

HelloWorldBeanPostProcessor

  1. public class HelloWorldBeanPostProcessor implements BeanPostProcessor {
  2. @Override
  3. public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  4. System.out.println("before init. beanName:" + beanName + ", bean:" + bean);
  5. return bean;
  6. }
  7. @Override
  8. public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  9. System.out.println("Bean '" + beanName + "' created : " + bean.toString());
  10. return bean;
  11. }
  12. }
  13. <bean class="com.example.start.springdemo.spring.HelloWorldBeanPostProcessor"/>

处理日志:

  1. before init. beanName:helloSpring, bean:com.example.start.springdemo.spring.HelloSpring@36a5cabc
  2. ###########HelloSpringBean Init##########
  3. Bean 'helloSpring' created : com.example.start.springdemo.spring.HelloSpring@36a5cabc

BeanFactoryPostProcessor(配置Bean定义元数据)

Spring includes a number of predefined bean factory post-processors, such as PropertyOverrideConfigurer and PropertySourcesPlaceholderConfigurer. You can also use a custom BeanFactoryPostProcessor — for example, to register custom property editors.

类名或属性替换PropertySourcesPlaceholderConfigurer

  1. <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
  2. <property name="locations" value="classpath:com/something/jdbc.properties"/>
  3. </bean>
  4. <bean id="dataSource" destroy-method="close"
  5. class="org.apache.commons.dbcp.BasicDataSource">
  6. <property name="driverClassName" value="${jdbc.driverClassName}"/>
  7. <property name="url" value="${jdbc.url}"/>
  8. <property name="username" value="${jdbc.username}"/>
  9. <property name="password" value="${jdbc.password}"/>
  10. </bean>

还可采用context标签用法:

多个文件用逗号分隔

选择一个运行时具体的类:

  1. <bean class="org.springframework.beans.factory.config.PropertySourcesPlaceholderConfigurer">
  2. <property name="locations">
  3. <value>classpath:com/something/strategy.properties</value>
  4. </property>
  5. <property name="properties">
  6. <value>custom.strategy.class=com.something.DefaultStrategy</value>
  7. </property>
  8. </bean>
  9. <bean id="serviceStrategy" class="${custom.strategy.class}"/>

属性覆盖PropertyOverrideConfigurer

自定义初始化逻辑FactoryBean

see org.springframework.beans.factory.FactoryBean

基于注解的容器配置

注解配置还是XML配置好?要看情况。注解表达更简洁,XML擅长组织Bean装配(wiring)或不用重新编译源代码。

启用注解

(上述标签会隐式注册几个post-processor. (The implicitly registered post-processors include AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor, PersistenceAnnotationBeanPostProcessor, and the aforementioned RequiredAnnotationBeanPostProcessor.) 注: context:annotaion-config只会在相同的application context里查找Bean

使用@Required

表示必须存在的Bean, 注: Spring 5.1以后已废弃该注解.使用构造注入

使用@Autowired

指定required=false,表示不强校验bean 是否存在

构造方法注入

  1. public class MovieRecommender {
  2. private final CustomerPreferenceDao customerPreferenceDao;
  3. @Autowired
  4. public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
  5. this.customerPreferenceDao = customerPreferenceDao;
  6. }
  7. // ...
  8. }

Setter注入

  1. public class MovieRecommender {
  2. private final CustomerPreferenceDao customerPreferenceDao;
  3. @Autowired
  4. public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
  5. this.customerPreferenceDao = customerPreferenceDao;
  6. }
  7. // ...
  8. }

任意方法注入

  1. public class MovieRecommender {
  2. private MovieCatalog movieCatalog;
  3. private CustomerPreferenceDao customerPreferenceDao;
  4. @Autowired
  5. public void prepare(MovieCatalog movieCatalog,
  6. CustomerPreferenceDao customerPreferenceDao) {
  7. this.movieCatalog = movieCatalog;
  8. this.customerPreferenceDao = customerPreferenceDao;
  9. }
  10. // ...
  11. }

注入字段或混合使用

  1. public class MovieRecommender {
  2. private final CustomerPreferenceDao customerPreferenceDao;
  3. @Autowired
  4. private MovieCatalog movieCatalog;
  5. @Autowired
  6. public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
  7. this.customerPreferenceDao = customerPreferenceDao;
  8. }
  9. // ...
  10. }

注入集合或数组或Map

  1. @Autowired
  2. private MovieCatalog[] movieCatalogs;
  3. @Autowired
  4. private List<MovieCatalog> movieCatalogs;
  5. private Set<MovieCatalog> movieCatalogs;
  6. @Autowired
  7. public void setMovieCatalogs(Set<MovieCatalog> movieCatalogs) {
  8. this.movieCatalogs = movieCatalogs;
  9. }
  10. // key is beanName
  11. private Map<String, MovieCatalog> movieCatalogs;
  12. @Autowired
  13. public void setMovieCatalogs(Map<String, MovieCatalog> movieCatalogs) {
  14. this.movieCatalogs = movieCatalogs;
  15. }

注入ApplicationContext,BeanFactory等

You can also use @Autowired for interfaces that are well-known resolvable dependencies: BeanFactory, ApplicationContext, Environment, ResourceLoader, ApplicationEventPublisher, and MessageSource

  1. @Autowired
  2. private ApplicationContext context;

Tune with @Primary or @Qualifier

@Autowired @Primary

泛型注入

  1. @Autowired
  2. private Store<String> s1; // <String> qualifier, injects the stringStore bean
  3. @Autowired
  4. private Store<Integer> s2; // <Integer> qualifier, injects the integerStore bean

使用@Resource注入

Resource为JSF-250提供的,用于注入字段,setter方法, 可用name指定具体的bean。若未指定name, 默认取字段名称或Setter方法的属性名称。see CommonAnnotationBeanPostProcessor.

先基于name查找,再根据type查找

  1. public class SimpleMovieLister {
  2. private MovieFinder movieFinder;
  3. @Resource(name="myMovieFinder")
  4. public void setMovieFinder(MovieFinder movieFinder) {
  5. this.movieFinder = movieFinder;
  6. }
  7. }

使用@Value

主要用来注入外部属性

  1. @Configuration
  2. @PropertySource("classpath:application.properties")
  3. public class AppConfig { }
  4. @Component
  5. public class MovieRecommender {
  6. private final String catalog;
  7. public MovieRecommender(@Value("${catalog.name:defaultValue}") String catalog) {
  8. this.catalog = catalog;
  9. }
  10. }
  11. application.properties
  12. catalog.name=MovieCatalog

如果占位符字段不存在,可以使用默认值。

特殊场景,可自定义ConversionService

使用@PostConstrct and @PreDestroy

原理见 CommonAnnotationBeanPostProcessor

  1. public class CachingMovieLister {
  2. @PostConstruct
  3. public void populateMovieCache() {
  4. // populates the movie cache upon initialization...
  5. }
  6. @PreDestroy
  7. public void clearMovieCache() {
  8. // clears the movie cache upon destruction...
  9. }
  10. }

类路径扫描并管理Component

@Component @Service @Repository @Controller

自动扫描

The use of implicitly enables the functionality of . There is usually no need to include the element when using .

  1. @Configuration
  2. @ComponentScan(basePackages = "org.example")
  3. public class AppConfig {
  4. // ...
  5. }

自动扫描机制由如下两个processor实现: AutowiredAnnotationBeanPostProcessor and CommonAnnotationBeanPostProcessor

配置扫描策略:

  1. JavaConfig
  2. @Configuration
  3. @ComponentScan(basePackages = "org.example",
  4. includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
  5. excludeFilters = @Filter(Repository.class))
  6. public class AppConfig {
  7. ...
  8. }
  9. XML方式
  10. <beans>
  11. <context:component-scan base-package="org.example">
  12. <context:include-filter type="regex"
  13. expression=".*Stub.*Repository"/>
  14. <context:exclude-filter type="annotation"
  15. expression="org.springframework.stereotype.Repository"/>
  16. </context:component-scan>
  17. </beans>

命名Component

  1. @Service("myMovieLister")
  2. public class SimpleMovieLister {
  3. // ...
  4. }

如果没有指定名称,默认使用BeanNameGenerator来生成bean name.默认为小写首字母的类全称

使用JSR330标准注解

@Inject and @Named

  1. import javax.inject.Inject;
  2. public class SimpleMovieLister {
  3. private MovieFinder movieFinder;
  4. @Inject
  5. public void setMovieFinder(@Named("main") MovieFinder movieFinder) {
  6. this.movieFinder = movieFinder;
  7. }
  8. }

Java Config 注解配置Bean

基本概览

@Bean like @Confirguration like

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public MyService myService() {
  5. return new MyServiceImpl();
  6. }
  7. }

注解配置容器AnnotationConfigApplicationContext

ClassPathXmlApplicationContext 也能兼容@Configuration @Bean注解 AnnotationConfigApplicationContext能处理所有的注解,如Component,JSR-330注解

  1. public static void main(String[] args) {
  2. ApplicationContext ctx =
  3. new AnnotationConfigApplicationContext("com.example.start.springdemo.spring");
  4. System.out.println(Arrays.toString(ctx.getBeanDefinitionNames()));
  5. }

registerClass(?)

  1. public static void main(String[] args) {
  2. AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
  3. ctx.register(AppConfig.class, OtherConfig.class);
  4. ctx.register(AdditionalConfig.class);
  5. ctx.refresh();
  6. MyService myService = ctx.getBean(MyService.class);
  7. myService.doStuff();
  8. }

注解扫描

  1. @Configuration
  2. @ComponentScan(basePackages = "com.acme")
  3. public class AppConfig {
  4. ...
  5. }

Config+Autowired

  1. @Configuration
  2. public class ServiceConfig {
  3. @Autowired
  4. private AccountRepository accountRepository;
  5. @Bean
  6. public TransferService transferService() {
  7. return new TransferServiceImpl(accountRepository);
  8. }
  9. }
  10. @Configuration
  11. public class RepositoryConfig {
  12. private final DataSource dataSource;
  13. public RepositoryConfig(DataSource dataSource) {
  14. this.dataSource = dataSource;
  15. }
  16. @Bean
  17. public AccountRepository accountRepository() {
  18. return new JdbcAccountRepository(dataSource);
  19. }
  20. }
  21. @Configuration
  22. @Import({ServiceConfig.class, RepositoryConfig.class})
  23. public class SystemTestConfig {
  24. @Bean
  25. public DataSource dataSource() {
  26. // return new DataSource
  27. }
  28. }
  29. public static void main(String[] args) {
  30. ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
  31. // everything wires up across configuration classes...
  32. TransferService transferService = ctx.getBean(TransferService.class);
  33. transferService.transfer(100.00, "A123", "C456");
  34. }

JavaConfig+XML配置

  1. @Configuration
  2. public class AppConfig {
  3. @Autowired
  4. private DataSource dataSource;
  5. @Bean
  6. public AccountRepository accountRepository() {
  7. return new JdbcAccountRepository(dataSource);
  8. }
  9. @Bean
  10. public TransferService transferService() {
  11. return new TransferService(accountRepository());
  12. }
  13. }
  14. system-test-config.xml
  15. <beans>
  16. <!-- enable processing of annotations such as @Autowired and @Configuration -->
  17. <context:annotation-config/>
  18. <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
  19. <bean class="com.acme.AppConfig"/>
  20. <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  21. <property name="url" value="${jdbc.url}"/>
  22. <property name="username" value="${jdbc.username}"/>
  23. <property name="password" value="${jdbc.password}"/>
  24. </bean>
  25. </beans>
  26. public static void main(String[] args) {
  27. ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
  28. TransferService transferService = ctx.getBean(TransferService.class);
  29. // ...
  30. }

In system-test-config.xml file, the AppConfig does not declare an id element. While it would be acceptable to do so, it is unnecessary, given that no other bean ever refers to it, and it is unlikely to be explicitly fetched from the container by name. Similarly, the DataSource bean is only ever autowired by type, so an explicit bean id is not strictly required.

Using to pick up @Configuration classes

  1. <beans>
  2. <!-- picks up and registers AppConfig as a bean definition -->
  3. <context:component-scan base-package="com.acme"/>
  4. <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
  5. <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  6. <property name="url" value="${jdbc.url}"/>
  7. <property name="username" value="${jdbc.username}"/>
  8. <property name="password" value="${jdbc.password}"/>
  9. </bean>
  10. </beans>

@Configuration Class-centric Use of XML with @ImportResource

  1. @Configuration
  2. @ImportResource("classpath:/com/acme/properties-config.xml")
  3. public class AppConfig {
  4. @Value("${jdbc.url}")
  5. private String url;
  6. @Value("${jdbc.username}")
  7. private String username;
  8. @Value("${jdbc.password}")
  9. private String password;
  10. @Bean
  11. public DataSource dataSource() {
  12. return new DriverManagerDataSource(url, username, password);
  13. }
  14. }
  15. properties-config.xml
  16. <beans>
  17. <context:property-placeholder location="classpath:/com/acme/jdbc.properties"/>
  18. </beans>
  19. main
  20. public static void main(String[] args) {
  21. ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
  22. TransferService transferService = ctx.getBean(TransferService.class);
  23. // ...
  24. }

环境抽象

注册LoadTimeWeaver

ApplicationContext附加能力

国际化MessageSource

优先使用 org.springframework.context.support.ReloadableResourceBundleMessageSource

  1. <beans>
  2. <!-- this MessageSource is being used in a web application -->
  3. <bean id="messageSource" class="org.springframework.context.support.ResourceBundleMessageSource">
  4. <property name="basename" value="exceptions"/>
  5. </bean>
  6. <!-- lets inject the above MessageSource into this POJO -->
  7. <bean id="example" class="com.something.Example">
  8. <property name="messages" ref="messageSource"/>
  9. </bean>
  10. </beans>
  11. 使用
  12. public class Example {
  13. private MessageSource messages;
  14. public void setMessages(MessageSource messages) {
  15. this.messages = messages;
  16. }
  17. public void execute() {
  18. String message = this.messages.getMessage("argument.required",
  19. new Object [] {"userDao"}, "Required", Locale.ENGLISH);
  20. System.out.println(message);
  21. }
  22. }

容器事件

  1. public class EmailService implements ApplicationEventPublisherAware {
  2. private List<String> blackList;
  3. private ApplicationEventPublisher publisher;
  4. public void setBlackList(List<String> blackList) {
  5. this.blackList = blackList;
  6. }
  7. public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
  8. this.publisher = publisher;
  9. }
  10. public void sendEmail(String address, String content) {
  11. if (blackList.contains(address)) {
  12. publisher.publishEvent(new BlackListEvent(this, address, content));
  13. return;
  14. }
  15. // send email...
  16. }
  17. }

BeanFactory

GenericApplicationContext and its subclass AnnotationConfigApplicationContext as the common implementations for custom bootstrapping. These are the primary entry points to Spring’s core container for all common purposes: loading of configuration files, triggering a classpath scan, programmatically registering bean definitions and annotated classes, and (as of 5.0) registering functional bean definitions.

  1. DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
  2. // populate the factory with bean definitions
  3. // now register any needed BeanPostProcessor instances
  4. factory.addBeanPostProcessor(new AutowiredAnnotationBeanPostProcessor());
  5. factory.addBeanPostProcessor(new MyBeanPostProcessor());
  6. // now start using the factory

BeanFactory使用PostProcess

  1. DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
  2. XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
  3. reader.loadBeanDefinitions(new FileSystemResource("beans.xml"));
  4. // bring in some property values from a Properties file
  5. PropertySourcesPlaceholderConfigurer cfg = new PropertySourcesPlaceholderConfigurer();
  6. cfg.setLocation(new FileSystemResource("jdbc.properties"));
  7. // now actually do the replacement
  8. cfg.postProcessBeanFactory(factory);