1.spring核心结构
Core:容器是Spring框架最核心的部分,管理Bean的创建(IOC)、配置(DI)和管理(Context),所有的Spring模块都构建于核心容器之上。
Aop:Spring应用系统中开发切面的基础,代码解耦,增强复用。
Data Access:Spring的JDBC和DAO模块封装大量样板代码,使得数据库代码变得简洁,也可避免数据库资源释放失败引发的问题。由JDBC、Transactions(AOP模块支持)、ORM、OXM 和 JMS等模块组成。
Web:提供了SpringMVC框架给Web应⽤,还提供了多种构建和其它应⽤交互的远程调⽤⽅ 案。 SpringMVC框架在Web层提升了应⽤的松耦合⽔平。
Test 为了使得开发者能够很⽅便的进⾏测试,Spring提供了测试模块以致⼒于Spring应⽤的测试。 通过该模块,Spring为使⽤Servlet、JNDI等编写单元测试提供了⼀系列的mock对象实现。
1.1 IOC
IOC容器帮助实例化对象并管理对象的生命周期。
控制反转:对象创建的权利交给给IOC容器。
IOC:解决对象之间的耦合问题。
1.2 依赖注入和控制反转的区别
角度不一样,控制反转是指对象实例化及其管理的权利交给了IOC容器
依赖注入:站在容器的角度,容器会把对象依赖的其他对象注入
1.3 AOP
解决代码重复问题,面向切面编程
在不改变原有业务逻辑代码外,增强横切逻辑代码,根本上解耦合
2.基础知识
2.1 bean.xml和BeanFactory
2.2 BeanFactory与ApplicationContext区别
BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的高级接口,比
BeanFactory要拥有更多的功能,比如说国际化支持和资源访问(xml,java配置类)等等
2.3 启动IOC容器
2.3.1 Java环境下启动IoC容器
ClassPathXmlApplicationContext:从类的根路径下加载配置文件(推荐使用)
FileSystemXmlApplicationContext:从磁盘路径上加载配置文件
AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
2.3.2 Web环境下启动IoC容器
从xml启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--配置Spring ioc容器的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<!--使用监听器启动Spring的IOC容器-->
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
</web-app>
从配置类启动容器
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--告诉ContextloaderListener知道我们使用注解的方式启动ioc容器-->
<context-param>
<param-name>contextClass</param-name>
<param-
value>org.springframework.web.context.support.AnnotationConfigWebAppli
cationContext</param-value>
</context-param><!--配置启动类的全限定类名-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.lagou.edu.SpringConfig</param-value>
</context-param>
<!--使用监听器启动Spring的IOC容器-->
<listener>
<listener-
class>org.springframework.web.context.ContextLoaderListener</listener-
class>
</listener>
</web-app>
2.4 纯xml配置
2.4.1 实例化Bean的三种方式
使用无参构造函数
在默认情况下,它会通过反射调用无参构造函数来创建对象。如果类中没有无参构造函数,将创建
失败。
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>
使用静态方法创建
在实际开发中,我们使用的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创
建的过程 中会做很多额外的操作。此时会提供一个创建对象的方法,恰好这个方法是static修饰的
方法,即是此种情 况。
<!--使用静态方法创建对象的配置方式-->
<bean id="userService" class="com.lagou.factory.BeanFactory"
factory-method="getTransferService"></bean>
用实例化方法创建
<!--使用实例方法创建对象的配置方式-->
<bean id="beanFactory"
class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory" factory-
method="getTransferService"></bean>
2.4.2 Bean标签属性
scope作用域
singleton:单例,随容器销毁生命周期结束,默认作用域
prototype:每次使用时创建,不随容器生命周期销毁,待垃圾回收时回收
factory-bean属性
用于指定创建当前bean对象的工厂bean的唯一标识。当指定了此属性之后,
class属性失效。
init-method属性
用于指定bean对象的初始化方法,此方法会在bean对象装配后调用。必须是
一个无参方法。
destory-method属性
用于指定bean对象的销毁方法,此方法会在bean对象销毁前执行。它只
能为scope是singleton时起作用。
2.4.3 依赖注入xml配置
构造器注入constructor-arg
在使用构造函数注入时,涉及的标签是 constructor-arg ,该标签有如下属性:
name:用于给构造函数中指定名称的参数赋值。
index:用于给构造函数中指定索引位置的参数赋值。
value:用于指定基本类型或者String类型的数据。
ref:用于指定其他Bean类型的数据。写的是其他bean的唯一标识。
set方法注入
在使用set方法注入时,需要使用 property 标签,该标签属性如下:
name:指定注入时调用的set方法名称。(注:不包含set这三个字母,druid连接池指定属性名称)
value:指定注入的数据。它支持基本类型和String类型。
ref:指定注入的数据。它支持其他bean类型。写的是其他bean的唯一标识。
2.5 xml+注解相结合
2.5.1 xml中标签与注解的对应(IoC)
| xml形式 | 对应的注解形式 |
|---|---|
| 标签 | @Component("accountDao"),注解加在类上bean的id属性内容直接配置在注解后面如果不配置,默认定义个这个bean的id为类的类名首字母小写;另外,针对分层代码开发提供了@Componenet的三种别名@Controller、@Service、@Repository分别用于控制层类、服务层类、dao层类的bean定义,这四个注解的用法完全一样,只是为了更清晰的区分而已 |
| 标签的 | scope属@Scope("prototype"),默认单例,注解加在类上性 |
| 标签的init-method属性 | @PostConstruct,注解加在方法上,该方法就是初始化后调用的方法 |
| 标签destory-method属性 | @PreDestory,注解加在方法上,该方法就是销毁前调用的方法 |
2.5.2 依赖注入的注解实现方式
@Autowired(推荐使用)
@Autowired为Spring提供的注解,需要导入包
org.springframework.beans.factory.annotation.Autowired。
@Autowired采取的策略为按照类型注入。
public class TransferServiceImpl {
@Autowired
private AccountDao accountDao;
}
public class TransferServiceImpl {
@Autowired
@Qualifier(name="jdbcAccountDaoImpl")
private AccountDao accountDao;
}
public class TransferService {
@Resource
private AccountDao accountDao;
@Resource(name="studentDao")
private StudentDao studentDao;
@Resource(type="TeacherDao")
private TeacherDao teacherDao;
@Resource(name="manDao",type="ManDao")
private ManDao manDao;
}
2.6 纯注解模式
改造xm+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解:
@Configuration 注解,表名当前类是一个配置类
@ComponentScan 注解,替代 context:component-scan@PropertySource,引入外部属性配置文件
@Import 引入其他配置类
@Value 对变量赋值,可以直接赋值,也可以使用 ${} 读取资源配置文件中的信息
@Bean 将方法返回对象加入 SpringIOC 容器
2.7 SpringIOC高级特性
2.7.1 Lazy-init延迟加载
ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean 提前进行实例化。提前实例化是指ApplicationContext 实例会创建并配置所有的singletonbean。
<!--lazy-init默认为false,表示在spring启动时,立刻进行实例化。-->
<bean id="testBean" class="cn.lagou.LazyBean" />
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第一次向容器
通过 getBean 索取 bean 时实例化的。
如果一个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不
会实例化bean,而是调用 getBean 方法实例化的。
Lazy加载的优点:
- 开启延迟加载一定程度提高容器启动和运转性能
- 对于不常使用的Bean 设置延迟加载,这样偶尔使用的时候再加载,不必要从一开始该 Bean 就占用资源
2.7.2 Factory-Bean
BeanFactory接口是容器的顶级接口,定义了容器的一些基础行为,负责生产和管理Bean的一个工厂,
具体使用它下面的子接口类型,比如ApplicationContext;此处我们重点分析FactoryBean
Spring中Bean有两种,一种是普通Bean,一种是工厂Bean(FactoryBean),FactoryBean可以生成
某一个类型的Bean实例(返回给我们),也就是说我们可以借助于它自定义Bean的创建过程。
//可以让我们自定义Bean的创建过程(完成复杂Bean的定义)
public interface FactoryBean<T> {
@Nullable
// 返回FactoryBean创建的Bean实例,如果isSingleton返回true,则该实例会放到Spring容器
//的单例对象缓存池中Map
T getObject() throws Exception;
@Nullable
// 返回FactoryBean创建的Bean类型
Class<?> getObjectType();
// 返回作用域是否单例
default boolean isSingleton() {
return true;
}
}
Company类
public class Company {
private String name;
private String address;
private int scale;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getScale() {
return scale;
}
public void setScale(int scale) {
this.scale = scale;
}
@Override
public String toString() {
return "Company{" +
"name='" + name + ''' +
", address='" + address + ''' +
", scale=" + scale +
'}';
}
}
CompanyFactoryBean类
public class CompanyFactoryBean implements FactoryBean<Company> {
private String companyInfo; // 公司名称,地址,规模
public void setCompanyInfo(String companyInfo) {
this.companyInfo = companyInfo;
}
@Override
public Company getObject() throws Exception {
// 模拟创建复杂对象Company
Company company = new Company();
String[] strings = companyInfo.split(",");
company.setName(strings[0]);
company.setAddress(strings[1]);
company.setScale(Integer.parseInt(strings[2]));
return company;
}
@Override
public Class<?> getObjectType() {
return Company.class;
}
@Override
public boolean isSingleton() {
return true;
}
}
xml配置
<bean id="companyBean" class="com.lagou.edu.factory.CompanyFactoryBean">
<property name="companyInfo" value="拉勾,中关村,500"/>
</bean>
2.7.3 后置处理器
Spring提供了两种后处理bean的扩展接口: BeanPostProcessor 和BeanFactoryPostProcessor,两者在使用上是有所区别的。
工厂初始化(BeanFactory)->Bean对象
在beanFactory初始化之后可以用beanFactoryPostProcessor进行后置处理的一些事情
在bean对象实例化后(并不是整个生命周期MethodParameter),可以用beanPostProcessor进行后置处理
BeanPostProcessor
BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean。
BeanFactoryPostProcessor
BeanFactory级别的处理,是针对整个Bean的工厂进行处理,典型应用:PropertyPlaceholderConfigurer
BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为一个 JavaBean,
这个JavaBean 就是 BeanDefinition
注意:调用 BeanFactoryPostProcessor 方法时,这时候bean还没有实例化,此时 bean 刚被解析成
BeanDefinition对象
3.源码分析
3.1 BeanFactory
3.2 Spring Ioc容器初始化主流程
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 第一步:刷新前的预处理
prepareRefresh();
/*
第二步:
获取BeanFactory;默认实现是DefaultListableBeanFactory
加载BeanDefition 并注册到 BeanDefitionRegistry
*/ConfigurableListableBeanFactory beanFactory =
obtainFreshBeanFactory();
// 第三步:BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加
载器等)
prepareBeanFactory(beanFactory);
try {
// 第四步:BeanFactory准备工作完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
// 第五步:实例化并调用实现了BeanFactoryPostProcessor接口的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执
行
registerBeanPostProcessors(beanFactory);
// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
// 第八步:初始化事件派发器
initApplicationEventMulticaster();
// 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
onRefresh();
// 第十步:注册应用的监听器。就是注册实现了ApplicationListener接口的监听器
bean
registerListeners();
/*
第十一步:
初始化所有剩下的非懒加载的单例bean
初始化创建非懒加载方式的单例Bean实例(未设置属性)
填充属性
初始化方法调用(比如调用afterPropertiesSet方法、init-method方法)
调用BeanPostProcessor(后置处理器)对实例bean进行后置处
*/
finishBeanFactoryInitialization(beanFactory);
/*
第十二步:
完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事
件
(ContextRefreshedEvent)
*/
finishRefresh();
}......
3.2 Spring Bean生命周期
如上图所示,Bean 的生命周期还是比较复杂的,下面来对上图每一个步骤做文字描述:
- Spring启动,查找并加载需要被Spring管理的bean,进行Bean的实例化
- Bean实例化后对将Bean的引入和值注入到Bean的属性中
- 如果Bean实现了BeanNameAware接口的话,Spring将Bean的Id传递给setBeanName()方法
- 如果Bean实现了BeanFactoryAware接口的话,Spring将调用setBeanFactory()方法,将BeanFactory容器实例传入
- 如果Bean实现了ApplicationContextAware接口的话,Spring将调用Bean的setApplicationContext()方法,将bean所在应用上下文引用传入进来
- 如果Bean实现了BeanPostProcessor接口,Spring就将调用他们的postProcessBeforeInitialization()方法。
- 如果Bean 实现了InitializingBean接口,Spring将调用他们的afterPropertiesSet()方法。类似的,如果bean使用init-method声明了初始化方法,该方法也会被调用
- 如果Bean 实现了BeanPostProcessor接口,Spring就将调用他们的postProcessAfterInitialization()方法。
- 此时,Bean已经准备就绪,可以被应用程序使用了。他们将一直驻留在应用上下文中,直到应用上下文被销毁。
- 如果bean实现了DisposableBean接口,Spring将调用它的destory()接口方法,同样,如果bean使用了destory-method 声明销毁方法,该方法也会被调用。
3.4 Spring解决循环依赖
首先,Spring内部维护了三个Map,也就是我们通常说的三级缓存。
在Spring的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个Map:
- singletonObjects 它是我们最熟悉的朋友,俗称“单例池”“容器”,缓存创建完成单例Bean的地方。
- singletonFactories 映射创建Bean的原始工厂
- earlySingletonObjects 映射Bean的早期引用,也就是说在这个Map里的Bean不是完整的,甚至还不能称之为“Bean”,只是一个Instance.
- spring尝试通过ApplicationContext.getBean()方法获取A对象的实例,由于spring容器中还没有A对象[getSingleton(A) = null],因此spring会创建A对象【spring创建一个bean分为三步:1. 实例化bean、 2. 给bean注入属性、3. 初始化bean】。并将A对象的半成品【未注入属性,未初始化】保存在三级缓存中[addSingletonFactory(A)]。
- 然后为A对象注入属性B,通过getBean(B)从spring容器中尝试获取B对象,由于spring容器还没有B对象,会创建B对象。
-
创建 B 的半成品对象,并保存在三级缓存中[addSingletonFactory(B)]。
-
然后为B对象注入属性A,通过getBean(A)从三级缓存中获取A的半成品对象的引用,将A从三级缓存移入二级缓存。并将它做为属性注入B对象
-
初始化B对象后,返回B对象。将B对象保存到一级缓存中
- 最后将返回的B对象做为属性注入A对象,初始化A,并将A对象保存在一级缓存中。
4.Spring涉及的设计模式
3.1 工厂模式
我们都知道是把创建对象交给工厂,以此来降低类与类之间的耦合。工厂模式在Spring中的应用非常广泛,这里举的例子是ApplicationContext和BeanFactory,这也是Spring的IOC容器的基础。
首先看BeanFactory,这是最底层的接口。
public interface BeanFactory {
Object getBean(String name) throws BeansException;
T getBean(String name, @Nullable Class requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
T getBean(Class requiredType) throws BeansException;
T getBean(Class requiredType, Object... args) throws BeansException;
//省略...
}
4.2 单例模式
在系统中,有很多对象我们都只需要一个,比如线程池、Spring的上下文对象,日志对象等等。
单例模式的好处在于:
- 对一些重量级的对象,省略了创建对象花费的时间,减少了系统的开销,
- 可以减少new操作的次数,减少了GC线程回收内存的压力。
实际上,在Spring中的Bean默认的作用域就是singleton(单例)的。
public Object getSingleton(String beanName, ObjectFactory<?> singletonFactory) {
Assert.notNull(beanName, "Bean name must not be null");
synchronized (this.singletonObjects) {
//检查缓存中是否有实例,如果缓存中有实例,直接返回
Object singletonObject = this.singletonObjects.get(beanName);
if (singletonObject == null) {
//省略...
try {
//通过singletonFactory获取单例
singletonObject = singletonFactory.getObject();
newSingleton = true;
}
//省略...
if (newSingleton) {
addSingleton(beanName, singletonObject);
}
}
//返回实例
return singletonObject;
}
}
protected void addSingleton(String beanName, Object singletonObject) {
synchronized (this.singletonObjects) {
this.singletonObjects.put(beanName, singletonObject);
this.singletonFactories.remove(beanName);
this.earlySingletonObjects.remove(beanName);
this.registeredSingletons.add(beanName);
}
}
4.3 策略模式
4.4 代理模式
Spring AOP主要是基于动态代理实现的,如果要代理的类,实现了某个接口,则使用JDK动态代理,如果没有实现接口则使用Cglib动态代理。