简介
Spring是一个轻量级的控制反转(IOC)和面向切面编程(AOP)的框架。
组成
IOC
简单概述
IOC能降低耦合性(防止牵一发而动全身),举个例子:在没有使用spring之前,每个对象在使用它的合作对象时,都需要new一个对象,这个合作对象是由自己创建出来的,主动权在自己手上,自己需要哪个对象就去创建哪个对象,这样的话对象之间的就产生了依赖(假如A依赖于B),有很强的耦合性,使用了spring之后就不一样了,spring创建好B对象,然后存储到一个容器里,当A需要B时,Spring就从容器里取出B,然后交给A使用,至于spring怎么去创建那个对象,就不是A所关心的了,所以控制反转就是将创建对象的控制权反转,以前创建对象是由自己把控的,现在交由IOC去管理,IOC容器就是一个专门用来创建对象的工厂,你要什么对象,他就给你对象,而有了IOC容器,依赖关系就变了,原先A依赖于B的关系没有了,现在他依赖于IOC容器,通过IOC容器去关联这些对象类,对象与对象之间就通过IOC容器进行联系;
在Spring中是实现控制反转的是IOC容器,其实现方法就是依赖注入(DI);
依赖注入
依赖:bean对象的创建依赖于容器;
注入:bean对象的所有属性,由容器注入;
Spring名词解释
1、BeanDefinition
BeanDefinition 是一个接口,他里面定义好多set方法,保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、设置作用域、这个 Bean 依赖了哪些 Bean 等等。
为什么要存储到一个BeanDefinition中,而不是一个类中?
因为Class无法完成bean的抽象,比如bean的作用域,bean的注入模型,bean是否是懒加载等等信息,Class是无法抽象出来的,故而需要一个BeanDefinition类来抽象这些信息,以便于spring能够完美的实例化一个bean.
作用:
Spring在扫描读取到类的信息后,首先他会实例化一个BeanDefinition对象,然后调用这个对象的各种set方法进行存储信息;每当扫描到一个符合规则的类,spring都会实例化一个BeanDefinition对象,然后根据类的类名生成一个bean名字(遵循spring规则的驼峰形式,例如MyService类就会转换成myService),最后Spring会把BeanDefinition对象和生成的beanName放到一个Map(BeanDefinitionMap)中,其中key存储的就是beanName,value存储的就是BeanDefinition对象。
public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement {
// 我们可以看到,默认只提供 sington 和 prototype 两种,
// 还有 request, session, globalSession, application, websocket 这几种,
// 不过,它们属于基于 web 的扩展。
String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;
String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;
// 比较不重要,直接跳过吧
int ROLE_APPLICATION = 0;
int ROLE_SUPPORT = 1;
int ROLE_INFRASTRUCTURE = 2;
// 设置父 Bean,这里涉及到 bean 继承,不是 java 继承。请参见附录的详细介绍
// 一句话就是:继承父 Bean 的配置信息而已
void setParentName(String parentName);
// 获取父 Bean
String getParentName();
// 设置 Bean 的类名称,将来是要通过反射来生成实例的
void setBeanClassName(String beanClassName);
// 获取 Bean 的类名称
String getBeanClassName();
// 设置 bean 的 scope
void setScope(String scope);
String getScope();
// 设置是否懒加载
void setLazyInit(boolean lazyInit);
boolean isLazyInit();
// 设置该 Bean 依赖的所有的 Bean,注意,这里的依赖不是指属性依赖(如 @Autowire 标记的),
// 是 depends-on="" 属性设置的值。
void setDependsOn(String... dependsOn);
// 返回该 Bean 的所有依赖
String[] getDependsOn();
// 设置该 Bean 是否可以注入到其他 Bean 中,只对根据类型注入有效,
// 如果根据名称注入,即使这边设置了 false,也是可以的
void setAutowireCandidate(boolean autowireCandidate);
// 该 Bean 是否可以注入到其他 Bean 中
boolean isAutowireCandidate();
// 主要的。同一接口的多个实现,如果不指定名字的话,Spring 会优先选择设置 primary 为 true 的 bean
void setPrimary(boolean primary);
// 是否是 primary 的
boolean isPrimary();
// 如果该 Bean 采用工厂方法生成,指定工厂名称。对工厂不熟悉的读者,请参加附录
// 一句话就是:有些实例不是用反射生成的,而是用工厂模式生成的
void setFactoryBeanName(String factoryBeanName);
// 获取工厂名称
String getFactoryBeanName();
// 指定工厂类中的 工厂方法名称
void setFactoryMethodName(String factoryMethodName);
// 获取工厂类中的 工厂方法名称
String getFactoryMethodName();
// 构造器参数
ConstructorArgumentValues getConstructorArgumentValues();
// Bean 中的属性值,后面给 bean 注入属性值的时候会说到
MutablePropertyValues getPropertyValues();
// 是否 singleton
boolean isSingleton();
// 是否 prototype
boolean isPrototype();
// 如果这个 Bean 是被设置为 abstract,那么不能实例化,
// 常用于作为 父bean 用于继承,其实也很少用......
boolean isAbstract();
int getRole();
String getDescription();
String getResourceDescription();
BeanDefinition getOriginatingBeanDefinition();
}
2、BeanFactoryPostProcessor和BeanPostProcessor区别
2.1、BeanFactoryPostProcessor(Bean工厂后置处理器)
public interface BeanFactoryPostProcessor {
//这里的参数代表的是一个bean工厂
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
为什么说参数ConfigurableListableBeanFactory是bean工厂,因为他的父类就是BeanFactory。
可以插手beanFactory的生命周期
在spring的代码级别是用一个接口来表示BeanFactoryPostProcessor,他是针对bean容器的,只要实现这个接口便是一个bean工厂后置处理器了,就会被spring容器加载;
他是Spring提供的一个扩展点,他具体的扩展工作就是可以干预bean工厂的初始化过程,它的实现类可以在当前BeanFactory初始化(spring容器加载bean定义文件)后,bean实例化之前修改bean的定义属性,达到影响之后实例化bean的效果。;
简单来说就是我们new一个对象(实例化)他是干预不了的,但是new出来的对象里面的属性的填充、修改我们可以通过bean后置处理器来干预,而且BeanFactoryPostProcessor是一个接口,它里面只有一个方法postProcessorBeanFactory,而这个方法里面需要传递的一个参数也是Bean工厂,在我们执行这个方法的时候相当于把实例化的beanFactory对象传递过来了,所以我们自然可以对这个beanFactory为所欲为了。
例如我上面红色圈中的方法getBeanDefinition,我们可以根据此方法,找到我们定义bean的BeanDefinition对象,然后我们可以对定义的属性进行修改了。
当然它的一个执行顺序是先执行Spring内部实现了后置处理器的类,在执行你自己写的实现了该后置处理器的类
spring内部的扫描就是实现了Bean工厂处理器,例如@Component等一系列注解都能被扫描到,他有一个类ConfigurationClassPostProcessor实现了Bean工厂处理器,这个类里面就实现了包的扫描。
2.2、BeanPostProcessor(Bean后置处理器)
如果我们想在Spring容器中完成bean实例化、配置以及其他初始化方法前后(例如InitializingBean的afterPropertiesSet()方法或自定义的init方法)要添加一些自己逻辑处理。我们需要定义一个或多个BeanPostProcessor接口实现类,然后注册到Spring IoC容器中。
注意:接口中两个方法不能返回null,如果返回null那么在后续初始化方法将报空指针异常或者通过getBean()方法获取不到bena实例对象 因为后置处理器从Spring IoC容器中取出bean实例对象没有再次放回IoC容器中
bean级别的处理,针对某个具体的bean进行处理
//提供了两个方法,初始化之前和初始化之后执行。
//第一个参数是每个bean的实例,第二个参数是每个bean的name或者id属性的值,可以通过第二个参数确定我们处理的是哪一个Bean
public interface BeanPostProcessor {
//实例化、依赖注入完毕,在调用显示的初始化之前完成一些定制的初始化任务
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
//实例化、依赖注入、初始化完毕时执行
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
3、BeanFactory和FactoryBean的区别
3.1、BeanFactory
BeanFactory是IOC容器的核心接口,是IOC容器的基础实现,负责生产和管理bean,他提供了一系列方法,例如getBean(获取bean),isSingleton(判断是否是单例)、getAliases(获取别名)等方法;
BeanFactory的职责包括:实例化、定位、配置应用程序中的对象及建立这些对象间的依赖。BeanFactory只是个接口,并不是IOC容器的具体实现,但是Spring容器给出了很多种实现,如 DefaultListableBeanFactory、XmlBeanFactory、ApplicationContext等,他们都继承了BeanFactory,再在其基础上进行了扩展,我们可以通过不同的BeanFactory,对bean进行解析、创建等。
public interface BeanFactory {
/**
FACTORY_BEAN_PREFIX 在Spring当中,有一个叫做FactoryBean的接口,这个类有一个getObject() 这样的方法。这个方法会返回一个对象实例。
对于这个接口的实现类而言,通过BeanFactory的getBean()返回的Bean,到底是实现类本身的实例,还是getObject()的返回实例 就在于有没有前缀。
也就是说&符号是获取FactoryBean还是FactoryBean的createBean创建的实例.如果有&则获取FactoryBean;否则获取createBean创建的实例.
**/
String FACTORY_BEAN_PREFIX = "&";
//获取Bean
Object getBean(String name) throws BeansException;
<T> T getBean(String name, Class<T> requiredType) throws BeansException;
<T> T getBean(Class<T> requiredType) throws BeansException;
Object getBean(String name, Object... args) throws BeansException;
//判断是否包含bean
boolean containsBean(String name);
//判断是否是单例
boolean isSingleton(String name) throws NoSuchBeanDefinitionException;
//判断是否是原型
boolean isPrototype(String name) throws NoSuchBeanDefinitionException;
//判断bean类型
boolean isTypeMatch(String name, Class<?> targetType) throws NoSuchBeanDefinitionException;
//获取bean 的类型
Class<?> getType(String name) throws NoSuchBeanDefinitionException;
//获取别名
String[] getAliases(String name);
}
3.2、FactoryBean
FactoryBean就是创建自定义Bean的一种方式,用于复杂Bean的创建,因为一般情况下,Spring是通过反射机制利用
他可以简化xml配置,隐藏一些细节,如果一个类有很多的属性,例如姓名、年龄、班级名称等信息,我们想通过Spring对这些属性值进行注入,按照传统的方式要在配置文件中书写大量属性配置,造成配置文件臃肿,那么这时可以考虑使用FactoryBean来简化配置,在这个接口方法的getObject方法中定义这些配置,
当调用getBean(“student”) 时,Spring通过反射机制发现StudentFactoryBean实现了FactoryBean的接口,这时Spring容器就调用接口方法StudentFactoryBean#getObject()方法返回。如下代码:
他源码里提供了三个方法,作用分别是获取实际对象、类型、判断是否是单例;
public interface FactoryBean<T> {
//返回实际对象
T getObject() throws Exception;
//返回实际类型
Class<?> getObjectType();
//判断是否是单例
boolean isSingleton();
}
@Data
public class Student {
/** 姓名 */
private String name;
/** 年龄 */
private int age;
/** 班级名称 */
private String className;
}
//实现FactoryBean,泛型为Student
public class StudentFactoryBean implements FactoryBean<Student> {
private String studentInfo;
@Override
public Student getObject() throws Exception {
if (this.studentInfo == null) {
throw new IllegalArgumentException("'studentInfo' is required");
}
String[] splitStudentInfo = studentInfo.split(",");
if (null == splitStudentInfo || splitStudentInfo.length != 3) {
throw new IllegalArgumentException("'studentInfo' config error");
}
Student student = new Student();
student.setName(splitStudentInfo[0]);
student.setAge(Integer.valueOf(splitStudentInfo[1]));
student.setClassName(splitStudentInfo[2]);
return student;
}
@Override
public Class<?> getObjectType() {
return Student.class;
}
public void setStudentInfo(String studentInfo) {
this.studentInfo = studentInfo;
}
}
//然后把StudentFactoryBean注入
<bean id="student" class="com..StudentFactoryBean" p:studentInfo="张三,25,A班级"/>
//测试
@Test
public void testStudentFactoryBean() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("day.xml");
//输出Student{name='张三', age=25, className='A班级'}
System.out.println(applicationContext.getBean("student"));
//输出org.springframework.beans.StudentFactoryBean@1ae369b
System.out.println(applicationContext.getBean("&student"));
}
//这样我们就实现了通过BeanFactory接口达到了简化配置文件的作用。
getBean(“student”)返回的Student类的实例;//com.demo.student
getBean("&student")返回的是StudentFactoryBean实例,即工厂bean其本身;//com.demo.StudentFactoryBean
4、afterPropertiesSet方法、init-method、BeanPostProcessor区别
4.1、InitializingBean
spring的**InitializingBean**为bean提供了定义初始化方法的方式。InitializingBean是一个接口,只包含一个方法:**afterPropertiesSet()**
public interface InitializingBean
{
public abstract void afterPropertiesSet() throws Exception;
}
4.2、init-method
init-method方法,初始化bean的时候执行,可以针对某个具体的bean进行配置。init-method需要在applicationContext.xml配置文档中bean的定义里头写明。
<bean id="testInitMethod" class="com.TestInitMethod" init-method="init"></bean>
Spring要求init-method是一个无参数的方法,如果init-method指定的方法中有参数,那么Spring将会抛出java.lang.NoSuchMethodException
init-method指定的方法可以是public、protected以及private的,并且方法也可以是final的。
init-method指定的方法可以是声明为抛出异常的,就像这样:
final protected void init() throws Exception{
System.out.println("init method...");
if(true) throw new Exception("init exception");
}
如果在init-method方法中抛出了异常,那么Spring将中止这个Bean的后续处理,并且抛出一个org.springframework.beans.factory.BeanCreationException异常。
4.3、BeanPostProcessor
BeanPostProcessor,针对所有Spring上下文中所有的bean,可以在配置文档applicationContext.xml中配置一个BeanPostProcessor,然后对所有的bean进行一个初始化之前和之后的代理。
BeanPostProcessor接口中有两个方法: postProcessBeforeInitialization(初始化之前)和postProcessAfterInitialization(初始化之后)。
总结
共同点都是在初始化bean的时候执行。
afterPropertiesSet 和init-method之间的执行顺序是afterPropertiesSet 先执行,init-method 后执行。
从BeanPostProcessor的作用,可以看出最先执行的是postProcessBeforeInitialization(初始化之前),然后是afterPropertiesSet,然后是init-method,然后是postProcessAfterInitialization(初始化之后)。
5、BeanFactory和ApplicationContext的区别
BeanFactory和ApplicationContext都是一个接口,BeanFactory是spring中比较原始的Factory,他不支持spring插件,例如:AOP、Web应用等功能。而ApplicationContext继承了BeanFactory,还进行了相应的扩展:
1、BeanFactory是Spring容器的基础接口,提供了基础的容器访问能力,他采用的是懒加载形式来注入Bean的,只有在使用Bean的时候通过getBean()方法,才能加载实例化;这样,我们就不能发现一些存在的spring的配置问题。 而我们使用ApplicationContext去获取bean的时候,在加载XXX.xml的时候,会创建所有的配置bean,也就是即时加载,这样在容器启动的时候就能发现spring中存在的配置问题。
2、BeanFactory是不支持国际化功能的,因为他没有扩展Spring中MessageResource接口,而ApplicationContext是扩展了Spring中MessageResource接口,所以他是支持国际化功能的。
3、BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用,但两者之间的区别是:BeanFactory需要手动注册,而ApplicationContext则是自动注册。
4、ApplicationContext支持访问资源,如URL和文件(ResourceLoader),可以通过ClassPathXmlApplicationContext、FileSystemXmlApplicationContext等获取到。
最常被使用的 ApplicationContext 接口实现:
- FileSystemXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你需要提供给构造器 XML 文件的完整路径。
- ClassPathXmlApplicationContext:该容器从 XML 文件中加载已被定义的 bean。在这里,你不需要提供 XML 文件的完整路径,只需正确配置 CLASSPATH 环境变量即可,因为,容器会从 CLASSPATH 中搜索 bean 配置文件。
- WebXmlApplicationContext:该容器会在一个 web 应用程序的范围内加载在 XML 文件中已被定义的 bean。
Spring加载过程
首先Spring里面有一个核心方法refresh(),定义了13个方法,就是spring容器加载的整个过程。
容器加载的第一步就是准备工作
1、prepareRefresh()方法,也就是准备工作,他的作用就是调用容器准备刷新的方法,获取容器的当前时间,同时给容器设置同步标识,日志记录,设置关闭和活跃的标志位,初始化值等等,简单来说就是方面我后面能够创建对象的时候能够获取对象等准备工作;
接着如果我们要读取配置文件,而读取后的配置文件是要放置到容器当中的,所以第二步就是要有个容器工厂(Bean工厂),
2、/əbˈteɪn/| obtainFreshBeanFactory()方法,他的作用就是先判断是否有bean工厂,有的话就行销毁(在我们当前容器启动之前你可能已经启动好了另外一个bean工厂了),然后通过CreateBean()创建BeanFactory并进行初始化,设置相关属性;接着完成了加载xml配置文件和解析两个工作,相当于已经把Bean对象转换成BeanDefinition对象了,然后注册到Bean工厂里的BeanDefinitionMap,其中key存的就是BeanName,value存的就是一个BeanDefinition;
上一步已经把工厂建好了,但是还不能投入使用,因为工厂里什么都没有,还需要配置一些东西
3、prepareBeanFactory()方法,他就是对BeanFactory的准备工作,为我们的bean工厂填充内部属性,例如类加载器、事件处理器、忽略一些不用注入的接口,像一些Aware接口(EnvironmentAware、EmbeddedValueResolverAware、xxx),因为这些接口在ApplicationContextAwareProcessor已经把这5个接口的实现工作做了 ;
Bean工厂准备工作做完后,所有的 beanDefinition 已经加载,但是还没有实例化,所以允许在子类中对 beanFactory 进行扩展处理。比如添加 aware 相关接口自动装配设置,添加后置处理器等
4、postProcessBeanFactory()方法,他是个空方法,主要是子类覆盖该方法做扩展,不过我们一般不做任何扩展工作;
接着实例化并调用所有注册的beanFactory后置处理器(也就是实现了接口BeanFactoryPostProcessor的bean,在beanFactory标准初始化之后执行),然后按照优先级分别执行,优先级的逻辑如下:
- 实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor先全部找出来,然后排序后依次执行
- 实现Ordered接口的BeanDefinitionRegistryPostProcessor找出来,然后排序后依次执行
- 没有实现PriorityOrdered和Ordered接口的BeanDefinitionRegistryPostProcessor找出来执行并依次执行
5、invokeBeanFactoryPostProcessors()方法,
例如可以通过后置处理器可以将BeanDefinition解析出来,包括我们项目中常用的注解(@Component、@ComponentScan)等注解进行解析,解析完成之后把这些bean注册到BeanFactory中,还有一类就是普通的bean工厂后处理器,可以调用其相关方法对BeanFactory进行增强处理
有两类BeanFactoryPostProcessors后处理器
- BeanDefinitionRegistryPostProcessor,继承BeanFactoryPostProcessor,作用跟BeanFactoryPostProcessor一样,这一类就通过后处理器可以将BeanDefinition解析出来,然后通过registry注册到IOC容器
- BeanFactoryPostProcessor,这一类就是普通的bean工厂后处理器,调用其相关方法对BeanFactory进行增强
调用注册完后,紧接着就是
6、registerBeanPostProcessor()方法,注册拦截Bean后置处理器的实现类完成注册,例如将配置文件中的BeanPostProcessor提取出来并注册进入beanFactory,这里只是注册,真正的调用在getBean的时候
7、initMessageSource()方法,初始化MessageSource组件,如果我们项目中定义了国际化,也就是切换不同的语言,spring也会帮我们初始化信息源
8、onRefresh()方法,扩展,
9、initApplicationEventMulticaster()方法,在Spring容器中初始化事件广播器,事件广播器用于事件的发布
10、registerListeners()方法,注册监听器,他主要是和监听器结合,构成观察者模式,监听Spring整个过程所触发的事件,在注册完以后,还会将其前期发生的事件发布给相匹配的监听器
下一步也是最重要的一步就是
11、finishBeanFactoryInitialization()方法,初始化所有剩余的单实例,实例化操作(非懒加载的),比如invokeBeanFactoryPostProcessors方法中根据各种注解解析出来的类,在这个时候都会被初始化,如果解析到他是懒加载的,也就不会进行实例化操作。
最后完成bean创建和初始化过程后
12、finishRefresh(),通知Bean生命周期处理器刷新容器,就可以保证 Spring 会在启动的时候调用其 start 方法开始生命周期,并在 Spring 关闭的时候调用 stop 方法来结束生命周期。
prepare:准备的意思
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
/**
(调用前的准备工作)调用容器准备刷新的方法,
1、获取容器的当时时间;
2、同时给容器设置同步标识,日志记录;
3、设置关闭和活跃的标志位;
4、获取环境(Enviroment)对象,并加载当前系统的属性值到Enviroment对象中,后面要获取的话就可以通过环境对象进行获取;
5、准备监听器和事件集合的对象,默认为空的集合;
等等其他操作......
**/
prepareRefresh();
/**
创建容器对象:DefaultListableBeanFactory;
创建Bean工厂,并加载xml配置文件的属性值到当前工厂中,最重要的就是BeanDefinition();
**/
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
/**
BeanFactory的准备工作,对各属性进行填充
为BeanFactory配置容器特性,例如类加载器、事件处理器等
**/
prepareBeanFactory(beanFactory);
try {
/**
这里需要知道 BeanFactoryPostProcessor 这个知识点,Bean 如果实现了此接口,那么在容器初始化以后,Spring 会负责调用里面的 postProcessBeanFactory 方法;
这里是提供给子类的扩展点,到这里的时候,所有的 Bean 都加载、注册完成了,但是都还没有初始化,此处我们自己一般不做任何扩展工作,他点开后是个空方法,他是留给子类做扩展的;
具体的子类可以在这步的时候添加一些特殊的 BeanFactoryPostProcessor 的实现类或做点什么事;
**/
postProcessBeanFactory(beanFactory);
/**
invokeBeanFactoryPostProcessors方法总结来说就是从Spring容器中找出BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor接口的实现类并按照一定的规则顺序进行执行。 其中ConfigurationClassPostProcessor这个BeanDefinitionRegistryPostProcessor优先级最高,它会对项目中的@Configuration注解修饰的类(@Component、@ComponentScan、@Import、@ImportResource修饰的类也会被处理)进行解析,解析完成之后把这些bean注册到BeanFactory中。需要注意的是这个时候注册进来的bean还没有实例化。
**/
invokeBeanFactoryPostProcessors(beanFactory);
/**
注意这里只是注册功能
为BeanFactory注册BeanPostProcessor
BeanPostProcessor是Bean后置处理器,用于监听容器触发的事件
**/
registerBeanPostProcessors(beanFactory);
//初始化信息源,和国际化相关,也就是切换不同语言,我们auto项目使用的就是这个.
initMessageSource();
//初始化容器事件监听多路广播器.
initApplicationEventMulticaster();
//一个模板方法,不同的Spring容器做不同的事情,也是个空方法,留给子类实现的来来初始化其他bean,这里可以不用管它
onRefresh();
//在所有的注册的bean中查找listener bean,注册到消息广播器中
registerListeners();
//初始化所有剩余的单实例,实例化操作(非懒加载的),会调用createBean()
finishBeanFactoryInitialization(beanFactory);
//初始化容器的生命周期事件处理器,并发布容器的生命周期事件
finishRefresh();
}
catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
}
//销毁以创建的单态Bean
destroyBeans();
//取消refresh操作,重置容器的同步标识.
cancelRefresh(ex);
// Propagate exception to caller.
throw ex;
}
finally {
resetCommonCaches();
}
}
}
Bean的生命周期
Sring启动后,查找并加载需要被Spring管理的bean,进行Bean的实例化;
通过populateBean()方法设置对象属性,进行属性的填充注入;
回调Aware相关接口,例如实现了BeanNameAware接口,Spring就会将bean的Id传递给setBeanName方法,还有一些其他Aware接口,只要实现了他们,都会执行相应的处理;
上面的操作已经把bean对象正确构造,但如果你想要对象被使用前再进行一些自定义的处理,就可以通过BeanPostProcessor接口实现;
通过后置处理器BeanPostProcessor接口,如果实现了该接口,spring就会调用postProcessorBefore方法,也可以叫前置处理器,可以在初始化方法前添加一些自己的逻辑;
如果实现了InitializingBean(/ɪˈnɪʃəlaɪzɪŋ)接口,或者使用了init-method方法,就会进行初始化方法的操作;
通过后置处理器BeanPostProcessor接口,如果实现了该接口,spring就会调用postProcessorAfter方法,也可以叫后置处理器,可以在初始化方法后添加一些自己的逻辑;
容器初始化成功后,Bean就已经准备就绪了,最后如果实现了销毁接口,spring就会调用该销毁方法进行容器的销毁。
aware接口存在的意义在于通过spring中的Bean对象来设置获取对应容器中的相关属性值,例如设置bean在容器中的名字、当前系统的环境等,设置完后就可以获取到了。
举例:
实现了BeanNameAware接口,他里面有个setBeanName()方法,设置完后,就可以获取bean在容器中的名字。
实现了Environmentaware接口,可以通过接口获取在系统中定义的属性值,比如属性值是name=’张三’,通过该接口就可以获取到。
循环依赖
1、Spring循环依赖的三种情况
循环依赖就是N个类中循环嵌套引用,例如我们有两个类A和B,其中A类引用了B类,B类引用了A类,他们之间互相引用,就会造成循环依赖问题,也就是死循环。
1.1:构造器参数循环依赖
构造器注入会引起循环依赖,是因为构造方法创建实例,每次都要new一个要构造的实例bean,而A创建时,依赖B,就去创建B,B又依赖了A,继续构造A,如此循环下去 A(B) B(A) A(B)->….,这种依赖 Spring 无法处理,直接抛出 BeanCurrentlyInCreationException(Bean当前处于创建异常中) 异常。
1.2:setter方式,singleton(单例)
单例模式下的 setter注入 不会造成循环依赖,单例意味着只需要创建一次对象,后面就可以从缓存中取出来,可以通过三级缓存处理。
1.3:setter方式原型,prototype
scope=”prototype” 意思是 每次请求都会创建一个实例对象,所以Spring容器无法完成依赖注入,Spring容器也不进行缓存,因此无法提前暴露一个创建中的Bean,也会抛出BeanCurrentlyInCreationException 异常。
1.4:对象的创建
对象的创建包含实例化和初始化两个部分;
实例化
实例化指的就是在堆中开辟一个空间,对象中的属性值都是默认值。
初始化:
初始化就是给对象中的属性进行赋值操作。
代码说明:
/** Room 包含了一些电器 */
@Getter
@Setter
public class Room {
private String television;
private String airConditioner;
private String refrigerator;
private String washer;
}
配置如下:
<bean id="room" class="xyz.coolblog.demo.Room">
<property name="television" value="Xiaomi"/>
<property name="airConditioner" value="Gree"/>
<property name="refrigerator" value="Haier"/>
<property name="washer" value="Siemens"/>
</bean>
这里展示的也就是初始化后的值,已经进行了赋值操作
从调试信息中可以看得出,Room 的每个成员变量都被赋上值了。然后我们再来看一下“原始的 bean 对象”,也就是实例化的值,展示的都是默认值null,如下:
1.5:set默认的单例为什么能解决循环依赖,构造方法不能解决循环依赖?
首先看一下两种方式下bean的简单创建流程,如下图:
解释:
构造方法不能解决循环依赖的问题在于,构造方法在创建实例的同时每次都要new一个要构造的实例Bean,也就是把实例化和初始化两个步骤合成了一个,这样也就造成了A创建时,依赖B,就去创建B,B又依赖了A,继续构造A,如此循环下去就会造成循环依赖;
setter注入方式(单例)能解决循环依赖因为setter的时候你是先有了当前对象A,然后再给当前对象通过set的方式进行赋值A里面的B,也就是把实例化和初始化分成了两个步骤,而非构造器合成了一个步骤,这也是他们的本质区别。
2、如何解决循环依赖问题?
首先说一下Spring中的三个缓存,三个用Map存放数据的缓存结构,如下图:
- singletonObjects(一级缓存):其中存放的是经历了Spring完整生命周期的bean,这里面的bean的依赖都已经填充完毕了,所以他里面放的是一个完整品。
- earlySingletonObjects(二级缓存):提前暴露出来的对象的map,其中存放的是刚刚创建出来的对象,没有经历Spring完整生命周期的bean,这里面的bean的依赖还未填充完毕,所以他里面存放的是一个半成品。
- singletonFactories(三级缓存):存放Bean工厂对象,用于解决循环依赖。
2.1:如何解决的循环依赖问题?
看如下两张图比较,如果没有缓存那么就会像第一张图一样,陷入死循环,一直创建填充,最后系统内存爆满,程序崩溃;而第二张图加入缓存则可以直接从缓存中获取,不会有死循环的问题。
首先Spring创建对象时,每次都是先从容器中查找,找不到在进行创建,他执行的一个方法就是getBean()->doGetBean()->createBean()->doCreateBean()->createBeanInstance(这里做的就是实例化的操作)->populateBean(填充属性),其中doCreateBean()就是创建Bean的实例,创建成功后就会把当前对象放到三级缓存中;
Spring通过三级缓存解决了循环依赖;
当A、B两个类发生循环引用时,A依次执行doGetBean方法、依次查询三个缓存是否存在该bean、没有就createBean,在A完成实例化后,就使用实例化后的对象去创建一个对象工厂,并添加到三级缓存中;
接着执行populateBean方法装配属性,但是发现装配的属性是B对象,那么这个时候Spring先从一二三级缓存中获取,那么这个时候缓存中肯定是没有注入B的,所以就又进行创建b属性(createBean()),创建完成实例化后,和A属性一样,也放入到三级缓存中,此时的三级缓存中就存放了A和B,当然他们现在都是半成品,因为他们都只完成了实例化而没有完成初始化操作;
接着就该填充B里面的A对象,按时按原先的流程,先从一二三缓存中去取,没有的话在进行创建,而我们上面的步骤已经把A放入到三级缓存中了,不用再走一套上面重复的流程,所以这里就可以从缓存中取出来,不用再重新去创建也就避免了死循环,然后把三级缓存中的数据放到二级缓存中并清除掉三级缓存,这个时候放入二级缓存中的数据还是个半成品,因为他只完成了实例化没有完成初始化;
接着给B里面的A属性这个半成品就进行赋值,完成初始化操作,完成后把B放入到一级缓存中,到这里B就完成了实例化和初始化,所以一级缓存中存放就是一个完整品了,然后在清除掉二三级缓存;
接着去初始化A,给A里面的B属性赋值,这个时候B已经在一级缓存中,是个完整品了,可以直接从缓存中获取,获取到完成初始化后A也就成了一个完成品,然后也放入到一级缓存并清除掉二三级缓存,到这里A和B就都是一个完整品了,就不用再去创建了,可以直接返回了,也就解决了循环依赖问题。
2.2:在解决循环依赖的时候,为什么要移除二三级缓存?
在三级缓存中,我们在查询对象的时候,先找一级,再找二级,然后是三级,那么如果在一级缓存中就已经有对象,还会去二三级缓存中查找吗?不会,既然不会,就不应该让他占用资源,所以此时就要销毁掉。
2.3:一级缓存能解决循环依赖吗?
不能,因为在一级缓存中存放的是成品对象(实例化和初始化都完成了),二级缓存中放的是半成品对象(只完成了实例化没有完成初始化),如果只有一级缓存,初始化完成和未初始化完成的对象都放在一起,拿到的可能是没有初始化的,也就是半成品,例如new User(),User里面有个age属性,如果他只完成了实例化就去get得到age属性,那么得到肯定是null,会造成空指针异常。
2.4:二级缓存能解决循环依赖吗?
如果当前注入的对象没有实现代理是可以解决的,但是如果实现了AOP(比如:代理对象),那么注入到其他bean的时候,不是最终的代理对象,而是原始的。通过三级缓存的ObjectFactory才能实现类最终的代理对象。
解决方案: 在提前曝光半成品时,直接执行getEarlyBeanReference创建到代理,并放入到缓存earlySingletonObjects中。那就不需要通过ObjectFactory来延迟执行getEarlyBeanReference,也就不需要singletonFactories这一级缓存。
spring为什么不这么做?
如果要使用二级缓存解决循环依赖,意味着Bean在构造完后就创建代理对象,这样违背了Spring设计原则。
Spring结合AOP跟Bean的生命周期,是在Bean创建完全之后通过AnnotationAwareAspectJAutoProxyCreator这个后置处理器来完成的,在这个后置处理的postProcessAfterInitialization方法中对初始化后的Bean完成AOP代理。
如果出现了循环依赖,那没有办法,只有给Bean先创建代理,但是没有出现循环依赖的情况下,设计之初就是让Bean在生命周期的最后一步完成代理而不是在实例化后就立马完成代理。
Spring使用的代理模式是JDK动态代理还是Cglib代理?
如果目标对象实现了接口,默认情况下会采用 JDK 的动态代理实现 AOP,但这个实现的接口只限定于是你自己定义的接口,如果你的类实现的spring内部的接口,例如BeanFactory这个接口,它采用的就会是jdk动态代理,因为spring内部会自己排除spring内部自己定义的接口;
如果目标对象没有实现了接口,则采用 Cglib代理,Spring 会自动在 JDK 动态代理和 CGLIB 动态代理之间转换。
AOP
Aop模块提供了面向切面的编程实现,允许你定义拦截器和切入点对代码进行解耦,他实现了应该的分离的功能,而且aop是对面向对象编程(oop)的补充, 面向对象编程将程序分解成各个层次的对象,面向切面编程将程序运行过程分解成各个切面,然后对这些切面进行事务处理,异常处理,还有一些日志记录,性能统计这些。