官网地址:https://github.com/spring-projects/spring-framework
中文文档:https://github.com/DocsHome/spring-docs/blob/master/SUMMARY.md
家族清单:https://spring.io/projects
本文内容主要摘自中文文档中内容,详细内容可参考中文文档
一、搭建Spring项目
1、第一个spring项目(Xml方式)
1.1、引入pom文件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
1.2、添加bean对应类
public class TestService {
public void sayHello() {
System.out.println("你好");
}
}
1.3、在resources下添加services.xml
<?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="testService" class="service.TestService">
</bean>
</beans>
1.4、写main方法
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("services.xml");
TestService bean = context.getBean(TestService.class);
bean.sayHello();
}
1.5、查看运行效果
你好
Process finished with exit code 0
2、第一个spring项目(JavaConfig方式)
2.1、引入pom文件
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
2.2、添加bean对应类
public class ConfigTestService {
public void sayHello() {
System.out.println("config:你好");
}
}
2.3、添加config对应类
@Configuration
public class MyConfig {
@Bean
public ConfigTestService configTestService() {
return new ConfigTestService();
}
}
2.4、写main方法
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
ConfigTestService bean = context.getBean(ConfigTestService.class);
bean.sayHello();
}
2.5、查看运行效果
config:你好
Process finished with exit code 0
3、第一个spring项目(JavaConfig+xml组合方式)
3.1、修改main方法
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
new XmlBeanDefinitionReader(context).loadBeanDefinitions("services.xml");
context.register(MyConfig.class);
context.refresh();
ConfigTestService bean = context.getBean(ConfigTestService.class);
bean.sayHello();
TestService bean2 = context.getBean(TestService.class);
bean2.sayHello();
}
3.2、查看运行效果
config:你好
你好
Process finished with exit code 0
二、Spring详细功能使用
选择BeanFactory还是ApplicationContext?
Feature | BeanFactory | ApplicationContext |
---|---|---|
Bean Bean实例化/装配 | Yes | Yes |
集成的生命周期管理r | No | Yes |
自动注册 BeanPostProcessor | No | Yes |
自动注册 BeanFactoryPostProcessor | No | Yes |
便利的 MessageSource 访问 (国际化) | No | Yes |
内置ApplicationEvent 发布机制 | No | Yes |
1、Bean相关
1.1、为Bean起别名
每个bean都有一个或多个标识符,这些标识符在容器托管时必须是唯一的。bean通常只有一个标识符,但如果需要到的标识不止一个时,可以考虑使用别名
xml方式:
<bean name="aaa,bbb,ccc" />
或者
<alias name="testService" alias="toName"/>
javaConfig方式:
@Bean(name = {"a", "b", "c"})
1.2、实例化Bean
bean定义基本上就是用来创建一个或多个对象的配置,当需要bean的时候,容器会查找配置并且根据bean定义封装的元数据来创建(或获取)实际对象。
1.2.1 无参构造函数
xml方式:
<bean id="exampleBean" class="examples.ExampleBean"/>
<bean name="anotherExample" class="examples.ExampleBeanTwo"/>
或javaConfig:
@Bean
1.2.2、静态工厂创建Bean
xml方式:
<bean id="clientService" class="service.ClientService" factory-method="createInstance"/>
1.2.3、实例工厂创建Bean
xml方式:
<bean id="serviceLocator" class="examples.DefaultServiceLocator"/>
<bean id="clientService" factory-bean="serviceLocator" factory-method="createClientServiceInstance"/>
1.3、依赖注入
依赖注入主要使用两种方式,一种是基于构造函数的注入,另一种的基于Setter方法的依赖注入。
1.3.1 构造函数注入
<bean id="thingOne" class="x.y.ThingOne">
<constructor-arg ref="thingTwo"/>
<constructor-arg type="java.lang.String" value="42"/>
<constructor-arg index="2" value="7500000"/>
</bean>
1.3.2 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>
1.3.3 depends-on属性
依赖另一个bean
<bean id="beanOne" class="ExampleBean" depends-on="manager"/>
1.3.4 懒加载
<bean id="lazy" class="com.something.ExpensiveToCreateBean" lazy-init="true"/>
1.3.5 自动装配
模式 | 说明 |
---|---|
no | (默认) 不自动装配。Bean引用必须由 ref 元素定义,对于比较大的项目的部署,不建议修改默认的配置 ,因为明确指定协作者可以提供更好的控制和清晰度。在某种程度上,它记录了系统的结构。 |
byName | 按属性名称自动装配。 Spring查找与需要自动装配的属性同名的bean。 例如,如果bean配置为根据名字装配,他包含 的属性名字为master(即,它具有setMaster(…)方法),则Spring会查找名为 master 的bean定义并使用它来设置属性。 |
byType | 如果需要自动装配的属性的类型在容器中只存在一个的话,他允许自动装配。如果存在多个,则抛出致命异常,这表示您不能对该bean使用byType自动装配。 如果没有匹配的bean,则不会发生任何事情(未设置该属性)。 |
constructor | 类似于byType,但应用于构造函数参数。 如果容器中没有一个Bean的类型和构造函数参数类型一致的话,则会引发致命错误。 |
1.3.6 作用域
作用域 | 描述 |
---|---|
singleton | (默认) 每一Spring IOC容器都拥有唯一的实例对象。 |
prototype | 一个Bean定义可以创建任意多个实例对象. |
request | 将单个bean定义范围限定为单个HTTP请求的生命周期。 也就是说,每个HTTP请求都有自己的bean实例,它是在单个bean定义的后面创建的。 只有基于Web的Spring ApplicationContext的才可用。 |
session | 将单个bean定义范围限定为HTTP Session的生命周期。 只有基于Web的Spring ApplicationContext的才可用。 |
application | 将单个bean定义范围限定为ServletContext的生命周期。 只有基于Web的Spring ApplicationContext的才可用。 |
websocket | 将单个bean定义范围限定为 WebSocket的生命周期。 只有基于Web的Spring ApplicationContext的才可用。 |
<bean scope="singleton"/>
<bean scope="prototype"/>
...
@RequestScope
@SessionScope
@ApplicationScope
...
1.4、 Bean生命周期
1.4.1 实现InitializingBean 和 DisposableBean 回调接口
1.4.2 自定义 init() 和 destroy() 方法
1.4.3 @PostConstruct 和 @PreDestroy 注解
1.4.4 实现Lifecycle接口:需显示调用ApplicationContext的start方法
1.4.5 实现SmartLifecycle 接口
1.4.6 实现Aware
2、 注解
2.1、@Required
@Required注解适用于bean属性setter方法
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Required
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
2.2、@Autowired
可以在构造器上使用@Autowired注解:
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
@Autowired注解应用于“传统”setter方法
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Autowired
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
还可以将注解应用于具有任意名称和多个参数的方法
public class MovieRecommender {
private MovieCatalog movieCatalog;
private CustomerPreferenceDao customerPreferenceDao;
@Autowired
public void prepare(MovieCatalog movieCatalog,
CustomerPreferenceDao customerPreferenceDao) {
this.movieCatalog = movieCatalog;
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
还可以将@Autowired应用于字段,甚至可以和构造函数混合使用
public class MovieRecommender {
private final CustomerPreferenceDao customerPreferenceDao;
@Autowired
private MovieCatalog movieCatalog;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao) {
this.customerPreferenceDao = customerPreferenceDao;
}
// ...
}
2.3、@Primary
如果存在多个候选者且另一个bean只需要一个特定类型的bean依赖时,使用标记有@Primary注解的Bean就是自动注入的值
@Configuration
public class MovieConfiguration {
@Bean
@Primary
public MovieCatalog firstMovieCatalog() { ... }
@Bean
public MovieCatalog secondMovieCatalog() { ... }
// ...
}
2.4、@Qualifier
筛选指定值的bean
public class MovieRecommender {
@Autowired
@Qualifier("main")
private MovieCatalog movieCatalog;
// ...
}
2.5、@Resource
根据名字注入
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Resource
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
}
2.6、@Component
@Repository, @Service, 或 @Controller是@Component的特殊化
2.7、@ComponentScan
<context:component-scan base-package="org.example"/>
@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig {
...
}
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
...
}
2.8、@Scope
2.9、@Inject
同@Autowired
2.10、@Named
@Named 和 @ManagedBean注解: 标准与 @Component 注解相同
@javax.inject.Named 或 javax.annotation.ManagedBean可以使用下面的方式来替代@Component注解:
import javax.inject.Inject;
import javax.inject.Named;
@Named("movieListener") // @ManagedBean("movieListener") could be used as well
public class SimpleMovieLister {
private MovieFinder movieFinder;
@Inject
public void setMovieFinder(MovieFinder movieFinder) {
this.movieFinder = movieFinder;
}
// ...
}
2.11、@Description
Bean 的描述
@Configuration
public class AppConfig {
@Bean
@Description("Provides a basic example of a bean")
public Thing thing() {
return new Thing();
}
}
2.12、@Import
就像在Spring XML文件中使用元素来帮助模块化配置一样,@Import 注解允许从另一个配置类加载@Bean定义
@Configuration
public class ConfigA {
@Bean
public A a() {
return new A();
}
}
@Configuration
@Import(ConfigA.class)
public class ConfigB {
@Bean
public B b() {
return new B();
}
}
2.13、@PropertySource
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
Spring的组件模型元素 vs JSR-330 变量
Spring | javax.inject.* | javax.inject restrictions / comments |
---|---|---|
@Autowired | @Inject | @Inject 没有’required’属性。 可以与Java 8的 Optional一起使用。 |
@Component | @Named / @ManagedBean | JSR-330不提供可组合模型,只是一种识别命名组件的方法。 |
@Scope(“singleton”) | @Singleton | JSR-330的默认作用域就像Spring的prototype。 但是,为了使其与Spring的一般默认值保持一致,默认情况下,Spring容器中声明的JSR-330 bean是一个 singleton。 为了使用除 singleton之外的范围,您应该使用Spring的@Scope注解。 javax.inject还提供了@Scope注解。 然而,这个仅用于创建自己的注解。 |
@Qualifier | @Qualifier / @Named | javax.inject.Qualifier 只是用于构建自定义限定符的元注解。 可以通过javax.inject.Named创建与Spring中@Qualifier一样的限定符。 |
@Value | - | 无 |
@Required | - | 无 |
@Lazy | - | 无 |
ObjectFactory | Provider | javax.inject.Provider avax.inject.Provider是Spring的ObjectFactory的直接替代品, 仅仅使用简短的get()方法即可。 它也可以与Spring的@Autowired结合使用,也可以与非注解的构造函数和setter方法结合使用。 |
3、事件
Spring提供的标准事件::
事件 | 说明 |
---|---|
ContextRefreshedEvent | 初始化或刷新ApplicationContext时发布(例如,通过使用ConfigurableApplicationContext 接口上的refresh() 方法)。 这里,“initialized”意味着加载所有bean,检测并激活bean的后置处理器,预先实例化单例,并且可以使用ApplicationContext对象。 只要上下文尚未关闭,只要所选的ApplicationContext实际支持这种“热”刷新,就可以多次触发刷新。 例如,XmlWebApplicationContext支持热刷新,但GenericApplicationContext 不支持。 |
ContextStartedEvent | 通过使用ConfigurableApplicationContext接口上的start()方法启动ApplicationContext 时发布。 通常,此信号用于在显式停止后重新启动Bean,但它也可用于启动尚未为自动启动配置的组件(例如,在初始化时尚未启动的组件)。 |
ContextStoppedEvent | 通过使用ConfigurableApplicationContext接口上的close() 方法停止ApplicationContext时发布。 这里,“已停止”表示所有生命周期bean都会收到明确的停止信号。 可以通过start()调用重新启动已停止的上下文。 |
ContextClosedEvent | 通过使用ConfigurableApplicationContext接口上的close()方法关闭ApplicationContext时发布。 这里, “关闭” 意味着所有单例bean都被销毁。 封闭的环境达到其寿命终结。 它无法刷新或重新启动。 |
RequestHandledEvent | 一个特定于Web的事件,告诉所有bean已经为HTTP请求提供服务。 请求完成后发布此事件。 此事件仅适用于使用Spring的DispatcherServlet的Web应用程序。 |
3.1、定义事件
public class BlackListEvent extends ApplicationEvent {
private final String address;
private final String content;
public BlackListEvent(Object source, String address, String content) {
super(source);
this.address = address;
this.content = content;
}
// accessor and other methods...
}
3.2、发布事件
public class EmailService implements ApplicationEventPublisherAware {
private List<String> blackList;
private ApplicationEventPublisher publisher;
public void setBlackList(List<String> blackList) {
this.blackList = blackList;
}
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void sendEmail(String address, String content) {
if (blackList.contains(address)) {
publisher.publishEvent(new BlackListEvent(this, address, content));
return;
}
// send email...
}
}
3.3、监听事件
public class BlackListNotifier implements ApplicationListener<BlackListEvent> {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
public void onApplicationEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
3.4、基于注解的事件监听器
public class BlackListNotifier {
private String notificationAddress;
public void setNotificationAddress(String notificationAddress) {
this.notificationAddress = notificationAddress;
}
@EventListener
public void processBlackListEvent(BlackListEvent event) {
// notify appropriate parties via notificationAddress...
}
}
@EventListener({ContextStartedEvent.class, ContextRefreshedEvent.class})
public void handleContextStart() {
...
}
@EventListener(condition = "#blEvent.content == 'my-event'")
public void processBlackListEvent(BlackListEvent blEvent) {
// notify appropriate parties via notificationAddress...
}