Spring框架是由于软件开发的复杂性而创建的。Spring使用的是基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅仅限于服务器端的开发。从简单性、可测试性和松耦合性角度而言,绝大部分Java应用都可以从Spring中受益。
Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。
学习Spring时,IOC和DI的特性大家铭记于心。
那么到底什么是控制反转呢?控制反转,反转的是什么?
其实说白了,就是将对象的维护权交给Spring容器去管理,反转的是对象的生命周期。
开发者无需手动创建对象,而是交给Spring管理,需要对象时,从Spring的容器中拿就行了。
之前一直讲针对接口编程,而非针对实现。
让Spring管理对象可以在程序上很好的解耦。
例如:接口和实现类,当我们手动new实现类时,程序就依赖于实现类了。
我们应该在容器中配置具体的实现类,让Spring去帮我们维护对象,而不是在程序中硬编码。
依赖注入就是控制反转的一种技术实现,只是Spring觉得这个名字描述的更恰当而已。
什么是依赖?
Web项目中采用分层的思想:控制层、业务层、持久层。
Controller需要依赖于Service,Service依赖于Dao。
我们声明的永远是接口,并没有实例化实现类。
就是通过@Autowired让Spring去帮我们注入依赖对象的。
大型项目中,类大多数是一起协同工作,类之间会相互依赖,Spring会帮我们自动注入依赖的对象,而不用开发者手动实例化对象,这就是依赖注入。
面向切面弥补了面向对象的不足。
当我们要在不修改原代码情况下,对原有的系统做增强时,就可以利用AOP来实现。
分离业务代码和非业务代码。
例如:我们需要打印方法运行的开始和结束日志时。
Spring 容器是 Spring 框架的核心。容器将创建对象,把它们连接在一起,配置它们,并管理他们的整个生命周期从创建到销毁。
这是一个最简单的容器,主要的功能是为依赖注入 (DI) 提供支持。
常用的就是XmlBeanFactory,新版本的Spring已经将其废弃。
BeanFactory 创建时,不会立刻创建Bean实例,只有在getBean()时才会创建,延迟了对象的实例化。
节省了系统的资源,系统资源不紧张的情况下,建议使用ApplicationContext。
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("spring/beanFactory.xml"));
Person person = (Person) beanFactory.getBean("PersonBean");
ApplicationContext是Spring中较高级的容器。
ApplicationContext 包含 BeanFactory 所有的功能,并在其基础上针对企业做了一些加强。
常用的ApplicationContext实现:
ClassPathXmlApplicationContext
常用。从 CLASSPATH 中加载 bean 配置文件。
FileSystemXmlApplicationContext
不常用。给出绝对路径,从磁盘加载配置文件。
如果配置的bean的scope是单例的,那么ApplicationContext容器创建时,会直接实例化所有对象。
如果配置是多例的,则会每次getBean()时才实例化对象,且返回的都是新对象。
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/beanFactory.xml");
Person person = (Person) applicationContext.getBean("PersonBean");
属性 | 描述 |
---|---|
class | Bean的全限定类名 |
name/id | Bean的唯一标识符,通过getBean(name)获取 |
scope | 对象作用域,单例多例等 |
constructor-arg | 通过构造函数注入 |
autowire | 自动装配 |
properties | 通过setter方法注入 |
lazy-init | 是否延迟实例化 |
init-method | 实例化后调用的方法 |
destroy-method | 销毁后调用的方法 |
<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="PersonBean" class="com.ch.entity.Person" scope="singleton" lazy-init="true" init-method="created" destroy-method="destroy">
<property name="name">
<value>小狗钱钱value>
property>
bean>
beans>
Spring对于bean默认的scope是单例(singleton)的。
header 1 | header 2 |
---|---|
singleton | 单例的 |
prototype | 多例的,每次获取都会创建新对象 |
request | 作用域限于每次Http请求 |
session | 同一个Session共享一个bean |
global-session | 仅适用于WebApplicationContext环境 |
注:这里单例和设计模式中的单例模式是两回事。单例指的是对象在Spring容器中,只会存在一个对象,但是客户端仍然可以手动的创建对象实例。
init-method和destroy-method分别会在对象创建时和销毁时触发。
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/beanFactory.xml");
Person person = (Person) applicationContext.getBean("PersonBean");
System.out.println(person);
//关闭IOC容器
applicationContext.registerShutdownHook();
输出如下:
Person初始化
Person创建完毕
Person{
name='小狗钱钱'}
Person销毁
Spring允许在实例化对象前后对bean进行额外的处理。
BeanPostProcessor 接口定义回调方法,通过实现它,并注入到Spring容器,Spring会在实例化对象前后调用增强方法。
public class PersonPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object o, String s) throws BeansException {
System.out.println("before");
return o;
}
@Override
public Object postProcessAfterInitialization(Object o, String s) throws BeansException {
System.out.println("after");
return o;
}
}
<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="PersonBean" class="com.ch.entity.Person" scope="singleton" lazy-init="true" init-method="created" destroy-method="destroy">
<property name="name">
<value>小狗钱钱value>
property>
bean>
<bean class="com.ch.entity.PersonPostProcessor" />
beans>
Bean的配置可以继承,子Bean可以继承父Bean的配置。
需要注意的是Spring中Bean的继承和Java的继承毫无关系,Bean继承只是为了简化同样的配置,减少重复配置。
@Data
public class Man {
private String name;
private int age;
}
<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="PersonBean" class="com.ch.entity.Person" scope="singleton" lazy-init="true">
<property name="name">
<value>小狗钱钱value>
property>
bean>
<bean class="com.ch.entity.PersonPostProcessor" />
<bean id="manBean" class="com.ch.entity.Man" parent="PersonBean">
<property name="age">
<value>18value>
property>
bean>
beans>
manBean会继承父类的name属性配置:“小狗钱钱”。
一个复杂的程序通常由很多类组成,类与类之间可能存在依赖关系。
Spring可以帮我们完成依赖对象的注入。
通过Spring来管理依赖对象,而不是硬编码写在Java代码中,可以很好的解耦。
注入的方式有两种:构造器和setter方法。
public class Inner {
public void inner(){
System.out.println("inner");
}
}
@Data
public class Outer {
private Inner inner;
public Outer(Inner inner) {
this.inner = inner;
}
}
<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="outer" class="com.ch.entity.Outer">
<constructor-arg name="inner" ref="inner" />
bean>
<bean id="inner" class="com.ch.entity.Inner"/>
beans>
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/DI.xml");
Outer outer = (Outer) applicationContext.getBean("outer");
outer.getInner().inner();
输出如下:
inner
<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="outer" class="com.ch.entity.Outer">
<property name="inner" ref="inner"/>
bean>
<bean id="inner" class="com.ch.entity.Inner"/>
beans>
除了注入单个对象外,有时还需注入集合。
@Data
public class Outer {
private List list;
private Set set;
private Map map;
private Properties properties;
}
<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="outer" class="com.ch.entity.Outer">
<property name="list">
<list>
<value>adminvalue>
<value>rootvalue>
list>
property>
<property name="set">
<set>
<value>adminvalue>
<value>rootvalue>
set>
property>
<property name="map">
<map>
<entry key="name" value="admin"/>
<entry key="age" value="18"/>
map>
property>
<property name="properties">
<props>
<prop key="name">adminprop>
<prop key="age">18prop>
props>
property>
bean>
beans>
AbstractApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring/collection.xml");
Outer outer = (Outer) applicationContext.getBean("outer");
System.out.println(outer.getList());
System.out.println(outer.getSet());
System.out.println(outer.getMap());
System.out.println(outer.getProperties());
输出如下:
[admin, root]
[admin, root]
{name=admin, age=18}
{age=18, name=admin}
除了通过构造器和setter手动的注入对象外,Spring还支持自动装配。
自动装配的方式有两种:byName和byType。
byName通过bean的name去自动装配,byType通过bean的类型去装配。
当接口有多个实现类时,通过byType装配会报错,因为Spring不知道该装配哪一个实现类。
@Data
public class AutoWire {
private Inner inner;
}
<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="autoWire" class="com.ch.entity.AutoWire" autowire="byType"/>
<bean id="inner" class="com.ch.entity.Inner"/>
beans>
Spring2.5开始支持使用注解来注入对象,简化了xml的配置。
通过对象类型自动注入–byType。
@Data
public class AnnotationWire {
@Autowired
private Inner inner;
}
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config/>
<bean id="autoWire" class="com.ch.entity.AnnotationWire"/>
<bean id="inner" class="com.ch.entity.Inner"/>
beans>
同一个类型我们有时可能会在Spring中配置多个对象。
@Autowired是通过类型来进行装配的,当同一个类型扫描到多个对象时,这时Spring就不知道该注入哪一个了。
可以通过@Qualifier制定bean的name或id,来指定装配的具体bean。
@Qualifier("inner1")
@Qualifier("inner2")
Spring还使用基于 JSR-250 注解。
如果觉得使用@Autowired+@Qualifier麻烦,那么可以使用@Resource。
@Data
public class AnnotationWire {
@Resource(name = "inner")
private Inner inner;
}
目前为止,所有的bean配置都是基于xml文件的,大型项目往往会滥用xml文件,导致不好管理。
除了xml文件配置容器外,Spring还支持Java代码来配置。
带有 @Configuration 的注解类表示这个类可以使用 Spring IOC 容器作为 bean 定义的来源。
@Bean 注解告诉 Spring 将返回对象注册到当前应用程序上下文中的bean。
@Configuration
public class MyConfig {
@Bean
public Inner inner(){
return new Inner();
}
}
@Test
public void annotationConfigApplicationContext(){
ApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
Inner bean = applicationContext.getBean(Inner.class);
bean.inner();
}
输出如下:
inner
当有多个@Configuration配置类时,我们可以在主配置类上 加@Import注解,将其他配置类引入进来。
@Configuration
@Import(ConfigA.class)
public class ConfigB {
}
指定Bean的作用域。
@Bean
@Scope("prototype")
public Inner inner(){
return new Inner();
}
Spring 的核心是 ApplicationContext,它负责管理Beans。
当加载Bean时,ApplicationContext会发布某些类型事件,当Bean实现了ApplicationListener接口,那么当ApplicationEvent发布到ApplicationContext上时,该bean就会被通知。
我没有看源码,但是我猜测用到的应该是 “观察者模式”。
事件 | 描述 |
---|---|
ContextRefreshedEvent | 容器初始化或刷新时,该事件被发布。 |
ContextStartedEvent | 容器启动时,该事件被发布。 |
ContextStoppedEvent | 容器停止时,该事件被发布。 |
ContextClosedEvent | 容器关闭时,该事件被发布。 |
RequestHandledEvent | web-specific 事件 |
public class RefreshedEvent implements ApplicationListener<ContextRefreshedEvent> {
public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) {
System.out.println("contextRefreshedEvent");
}
}
public class StartedEvent implements ApplicationListener<ContextStartedEvent> {
public void onApplicationEvent(ContextStartedEvent contextStartedEvent) {
System.out.println("contextStartedEvent");
}
}
public class StoppedEvent implements ApplicationListener<ContextStoppedEvent> {
public void onApplicationEvent(ContextStoppedEvent contextStoppedEvent) {
System.out.println("contextStoppedEvent");
}
}
public class ClosedEvent implements ApplicationListener<ContextClosedEvent> {
public void onApplicationEvent(ContextClosedEvent contextClosedEvent) {
System.out.println("ContextClosedEvent");
}
}
@Configuration
public class MyConfig {
@Bean
public Inner inner(){
return new Inner();
}
@Bean
public ApplicationListener start(){
return new StartedEvent();
}
@Bean
public ApplicationListener stop(){
return new StoppedEvent();
}
@Bean
public ApplicationListener refresh(){
return new RefreshedEvent();
}
@Bean
public ApplicationListener close(){
return new ClosedEvent();
}
}
@Test
public void annotationConfigApplicationContext(){
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
applicationContext.start();
Inner bean = applicationContext.getBean(Inner.class);
bean.inner();
applicationContext.stop();
applicationContext.close();
}
输出如下:
contextRefreshedEvent
contextStartedEvent
contextStoppedEvent
inner
ContextClosedEvent
自定义的事件比较复杂。
需要自己实现事件处理器、事件发布。
public class CustomEvent extends ApplicationEvent {
public CustomEvent(Object source) {
super(source);
}
}
public class CustomEventHandler implements ApplicationListener<CustomEvent> {
public void onApplicationEvent(CustomEvent customEvent) {
System.out.println("customEvent 自定义事件");
}
}
public class CustomEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
this.publisher = applicationEventPublisher;
}
public void publish() {
CustomEvent ce = new CustomEvent(this);
publisher.publishEvent(ce);
}
}
@Configuration
public class CustomEventConfig {
@Bean
public ApplicationListener eventHandler(){
return new CustomEventHandler();
}
@Bean
public ApplicationEventPublisherAware eventPublisher(){
return new CustomEventPublisher();
}
}
@Test
public void customEvent(){
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(CustomEventConfig.class);
CustomEventPublisher bean = applicationContext.getBean(CustomEventPublisher.class);
bean.publish();
}
输出如下:
customEvent 自定义事件