IoC 容器是 Spring 的核心,Spring 经过 IoC 容器来管理对象的实例化和初始化(这些对象就是 Spring Bean),以及对象从建立到销毁的整个生命周期。也就是管理对象和依赖,以及依赖的注入等等。html
Spring 提供 2 种不一样类型的 IoC 容器:BeanFactory 和 ApplicationContext 容器。java
BeanFactory 是一个管理 Bean 的工厂,它主要负责初始化各类 Bean, 并调用它们的生命周期方法。BeanFactory 是最简单的 Bean 容器,它由 org.springframework.beans.factory.BeanFactory 接口定义实现。提供了容器最基本的功能。web
目前 BeanFactory 没多少人用,主要是为了可以兼容 Spring 集成的第三方框架,因此目前仍然保留了该接口。下面是官网的解释spring
The BeanFactory
and related interfaces, such as BeanFactoryAware
, InitializingBean
, DisposableBean
, are still present in Spring for the purposes of backward compatibility with the large number of third-party frameworks that integrate with Spring.api
Beanfactory 是 org.springframework.beans 的顶级接口。数组
ApplicationContext 容器几乎涵盖全部 BeanFactory 容器的功能,它继承了 BeanFactory 接口,由org.springframework.context.ApplicationContext 接口定义实现。在 BeanFactory 的基础上增长了AOP、事务支持等等功能。如今Spring 实际开发中基本上使用的是 ApplicationContext 容器。缓存
ApplicationContext 是 org.springframework.context 的顶级接口。安全
ApplicationContext 有两个经常使用的实现类,分别是 ClassPathXmlApplicationContext 和 FileSystemXmlApplicationContext 。websocket
看名字就知道它是从类路径 ClassPath 中寻找 XML 配置文件,来完成 ApplicationContext 实例化工做:session
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("configlocation"); //configlocation 是指定 Spring 配置文件(XML)的名称和位置,好比 Beans.xml
该类是从文件系统中寻找 XML 配置文件:
ApplicationContext applicationContext = new FileSystemXmlApplicationContext("configlocation"); //configlocation 是从非类路径外中获取 XML 的名称和位置,好比 ”F:/workCode/Beans.xml“
它们都是经过 XML 配置文件来加载 Bean 的。
看官网定义:
In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.
Bean 是由 Spring IoC 容器管理的对象,容器就能经过反射的形式将容器中准备好的对象注入(这里使用的是反射给属性赋值)到需求的组件中去,简单来讲,Spring IoC 容器能够看做是一个工厂,Bean 至关于工厂的产品。 Spring 配置文件则告诉容器须要哪些 Bean,以及须要哪一种方式来装配 Bean。
Bean 其实就是一个 Java 对象,它是根据 bean 规范编写出来的类,而且由容器生成的对象就是一个 bean。
Bean 规范:
它和 POJO 实际上是同样的,只不过是遵循 Bean 规范的 POJO 。
spring 配置文件主要支持两种格式:XML 和 Properties 格式
通常来讲,Spring 的配置文件使用 XML 格式。 XML 配置文件的根元素是
属性名称 | 描述 |
---|---|
id | Bean 的惟一标识符,Spring 容器对 Bean 的配置和管理都经过该属性完成。id 的值必须以字母开始,可使用字母、数字、下划线等符号。 |
name | name 属性中能够为 Bean 指定多个名称,每一个名称之间用逗号或分号隔开。Spring 容器能够经过 name 属性配置和管理容器中的 Bean。 |
class | 该属性指定了 Bean 的具体实现类,它必须是一个完整的类名,即类的全限定名。 |
scope | 用于设定 Bean 实例的做用域,属性值能够为 singleton(单例)、prototype(原型)、request、session 和 global Session。其默认值是 singleton |
constructor-arg |
|
property |
|
ref |
|
value |
|
list | 用于封装 List 或数组类型的依赖注入 |
set | 用于封装 Set 类型的依赖注入 |
map | 用于封装 Map 类型的依赖注入 |
entry | |
init-method | 容器加载 Bean 时调用该方法,相似于 Servlet 中的 init() 方法 |
destroy-method | 容器删除 Bean 时调用该方法,相似于 Servlet 中的 destroy() 方法。该方法只在 scope=singleton 时有效 |
lazy-init | 懒加载,值为 true,容器在首次请求时才会建立 Bean 实例;值为 false,容器在启动时建立 Bean 实例。该方法只在 scope=singleton 时有效 |
Spring 容器在初始化一个 Bean 实例时,同时会指定该实例的做用域。Spring 5 支持 6 种做用域。
默认的做用域,单例模式。表示在 Spring 容器中只有一个 Bean 实例,Bean 以单例的方式存在。在容器启动前就建立好了对象,任什么时候间获取都是以前建立好的那个对象。配置方式能够缺省,由于是默认值。<bean class="..."></bean>
原型做用域,多实例模式。每次调用 Bean 时都会建立一个新实例。Bean 以多实例的方式存在。容器启动默认不会建立多实例 bean,每次获取都会建立一个新的实例 bean 。配置方式为 <bean class="..." scope="prototype"></bean>
在 web 环境下,每次 HTTP 请求都会建立一个 Bean 实例,该做用域只在当前 HTTP Request 内有效。 配置方式为 <bean class="..." scope="request"></bean>
在 web 环境下,每次 HTTP 会话共享一个 Bean 实例,不一样的 Session 使用不一样的 Bean 实例。该做用域只在当前 HTTP Session 内有效。配置方式为 <bean class="..." scope="session"></bean>
在web 环境下,同一个 web application 共享一个 Bean 实例,该做用域在当前 ServletContext 内有效。
在web 环境下,同一个 websocket 共享一个 Bean 实例,该做用域在整个 websocket 中有效。
Bean 的初始化主要分为两个过程:Bean 的注册和 Bean 的实例化。Bean 的注册主要是指 Spring 经过读取配置文件获取各个 bean 的声明信息,而且对这些信息进行注册的过程。
在 XML 中配置好后进行注册
<bean id="person" class="org.springframework.beans.Person"> <property name="id" value="1"/> <property name="name" value="Java"/> </bean>
可使用 @Component 或 @Configuration + @Bean 来注册 Bean
@Component public class Person { private Integer id; private String name // 忽略其余方法 }
@Configuration //能够理解为 XML 配置文件中的 <beans> 标签 public class Person { @Bean //能够理解为 XML 配置文件中的 <bean> 标签 public Person person(){ return new Person(); } // 忽略其余方法 }
使用 BeanDefinitionRegistry.registerBeanDefinition() 方法来注册 Bean, 代码以下:
public class CustomBeanDefinitionRegistry implements BeanDefinitionRegistryPostProcessor { @Override public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {} @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { RootBeanDefinition personBean = new RootBeanDefinition(Person.class); // 新增 Bean registry.registerBeanDefinition("person", personBean); } }
Spring 中的 Bean 的生命周期比较复杂,能够表示为: Bean 的定义 -> Bean 的初始化 -> Bean 的使用 -> Bean 的销毁
Spring 是根据 Bean 的做用域来管理,对于单实例 singleton 做用域的 Bean, Spring 可以精确地知道 这个 Bean 的完整生命周期;而对于 prototype 做用域的 Bean, Spring 只负责建立, 当容器建立了 Bean 的实例后,Bean 的实例就交给客户端管理,Spring 容器将再也不跟踪其生命周期。
首先,从上面几节中看到,关于 Bean 的定义和初始化中的注册都在配置文件中或者其余方式提早写好。下面咱们直接从 Bean 初始化中的实例化开始看,通常会有如下几个过程:
Spring 启动, 查找并加载须要被 Spring 管理的 Bean , 并实例化 Bean ,实例化就是经过容器生成一个 Bean。实例化 Bean 方式主要有三种:类的无参构造方法、静态工厂、实例工厂。
在配置文件 XML 中配置 bean, 默认使用了无参构造器建立 bean
<bean id="bean" class="com.spring.demo.Bean"></bean>
而后再经过 getBean() 方法来获取实例化的 bean
ApplicationContext context = new ClasspathXmlApplicationContext("Bean.xml"); Bean b = (Bean)context.getBean("Bean.xml");
一样也是须要在 XML 中配置 bean :
<bean id="bean" class="com.spring.demo.BeanFactory" factory-method="getBean"></bean>
id 和 class 都定位到某个工厂类,factory-method 表示调用到该类 BeanFactory 下的方法来建立对象,并且这个 getBean 方法必须是静态方法。
一样是用 getBean() 方法获取实例化的 bean ,就不赘余了。
一样的,配置 XML 文件
<bean id="beanfactory" class="com.spring.demo.BeanFactory"></bean> <bean id="bean" factory-bean="beanfactory" factory-method="getbean"></bean>
实例工厂和静态工厂的区别在于,该实例化方式工厂方法不须要是静态的,须要先建立对象(在工厂类中新建一个对象),而后再经过对象调用其方法建立 bean。
这个阶段须要利用依赖注入完成 Bean 中的全部属性值的配置注入。容器的注入方法主要有构造方法和 Setter 方法注入。
注入方式是使用
<bean id="" class=""> <constructor-arg index="0" value=""></constructor-arg> <constructor-arg index="1" ref=""></constructor-arg> </bean>
Setter 方法注入的方式是目前 Spring 主流的注入方式,它能够利用 Java Bean 规范所定义的 Setter/Getter 方法来完成注入,可读性和灵活性都很高,它不须要使用声明式构造方法,而是使用 Setter 注入直接设置相关的值。
<bean id="person" class="org.springframework.beans.Person"> <property name="id" value="1"/> <property name="name" value="Java"/> </bean>
在 Spring 实例化 Bean 的过程当中,首先会调用默认的构造方法实例化 Bean 的对象,而后经过 Java 的反射机制调用 set 方法进行属性的注入。所以,setter 注入要求 Bean 的对应类必须知足一下要求:
若是 Bean 实现了 BeanNameAware 接口,则 Spring 调用 Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
若是 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用 setBeanFactory() 方法传入当前工厂实例的引用。
若是 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用 setApplicationContext() 方法传入当前 ApplicationContext 实例的引用。
若是 Bean 实现了 BeanPostProcessor 接口,则 Spring 调用该接口的预初始化方法 postProcessBeforeInitialzation() 对 Bean 进行加工操做,此处很是重要,Spring 的 AOP 就是利用它实现的。
InitializingBean 是一个接口,它有一个 afterPropertiesSet() 方法,在 Bean 初始化时会判断当前 Bean 是否实现了 InitializingBean,若是实现了则调用 afterPropertiesSet() 方法,进行初始化工做;而后再检查是否也指定了 init-method,若是指定了则经过反射机制调用指定的 init-method 方法,它的实现源码以下:
protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd) throws Throwable { // 判断当前 Bean 是否实现了 InitializingBean,若是是的话须要调用 afterPropertiesSet() boolean isInitializingBean = (bean instanceof InitializingBean); if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) { if (logger.isTraceEnabled()) { logger.trace("Invoking afterPropertiesSet() on bean with name '" + beanName + "'"); } if (System.getSecurityManager() != null) { // 安全模式 try { AccessController.doPrivileged((PrivilegedExceptionAction<Object>) () -> { ((InitializingBean) bean).afterPropertiesSet(); // 属性初始化 return null; }, getAccessControlContext()); } catch (PrivilegedActionException pae) { throw pae.getException(); } } else { ((InitializingBean) bean).afterPropertiesSet(); // 属性初始化 } } // 判断是否指定了 init-method() if (mbd != null && bean.getClass() != NullBean.class) { String initMethodName = mbd.getInitMethodName(); if (StringUtils.hasLength(initMethodName) && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) && !mbd.isExternallyManagedInitMethod(initMethodName)) { // 利用反射机制执行指定方法 invokeCustomInitMethod(beanName, bean, mbd); } } }
若是 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法 postProcessAfterInitialization()。此时,Bean 已经能够被应用系统使用了。
若是在
在 Spring 容器关闭时会执行销毁方法,可是 Spring 容器不会自动去调用销毁方法,而是须要咱们主动的调用。
若是是 BeanFactory 容器,那么咱们须要主动调用 destroySingletons() 方法,通知 BeanFactory 容器去执行相应的销毁方法;若是是 ApplicationContext 容器,那么咱们须要主动调用 registerShutdownHook() 方法,告知 ApplicationContext 容器执行相应的销毁方法。
通常状况下,会在 Bean 被初始化后和被销毁前执行一些相关操做。
Spring 官方提供了 3 种方法实现初始化回调和销毁回调:
不建议使用接口和注解,这会让 pojo 类和 Spring 框架紧耦合。
Bean 的装配能够理解为依赖关系注入,Bean 的装配方式也就是 Bean 的依赖注入方式。Spring 容器支持多种装配 Bean 的方式,如基于 XML 的 Bean 装配、基于 Annotation 的 Bean 装配和自动装配等。基于 XML 的装配方式主要分为两种,在 5.2 设置属性值 中提到的过。
自动装配就是指 Spring 容器在不使用
名称 | 说明 |
---|---|
no | 默认值,表示不使用自动装配,Bean 依赖必须经过 ref 元素定义。 |
byName | 根据 Property 的 name 自动装配,若是一个 Bean 的 name 和另外一个 Bean 中的 Property 的 name 相同,则自动装配这个 Bean 到 Property 中。 |
byType | 根据 Property 的数据类型(Type)自动装配,若是一个 Bean 的数据类型兼容另外一个 Bean 中 Property 的数据类型,则自动装配。 |
constructor | 相似于 byType,根据构造方法参数的数据类型,进行 byType 模式的自动装配。 |
autodetect(3.0版本不支持) | 若是 Bean 中有默认的构造方法,则用 constructor 模式,不然用 byType 模式。 |
尽管可使用 XML 来装配 Bean , 可是若是应用中 Bean 数量过多,会致使 XML 配置文件过于臃肿,对后期维护带来必定的困难
Java 从 JDK 5.0 之后,提供了 Annotation(注解)功能,Spring 2.5 版本开始也提供了对 Annotation 技术的全面支持,咱们可使用注解来配置依赖注入。Spring 默认不使用注解装配 Bean,所以须要在配置文件中添加 < context:annotation-config >,启用注解。
Spring 中经常使用的注解以下。
可使用此注解描述 Spring 中的 Bean,但它是一个泛化的概念,仅仅表示一个组件(Bean),而且能够做用在任何层次。使用时只需将该注解标注在相应类上便可。
用于将数据访问层(DAO层)的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
一般做用在业务层(Service 层),用于将业务层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
一般做用在控制层(如 Struts2 的 Action、SpringMVC 的 Controller),用于将控制层的类标识为 Spring 中的 Bean,其功能与 @Component 相同。
能够应用到 Bean 的属性变量、属性的 setter 方法、非 setter 方法及构造函数等,配合对应的注解处理器完成 Bean 的自动配置工做。默认按照 Bean 的类型进行装配。
做用与 Autowired 相同,区别在于 @Autowired 默认按照 Bean 类型装配,而 @Resource 默认按照 Bean 实例名称进行装配。
@Resource 中有两个重要属性:name 和 type。
Spring 将 name 属性解析为 Bean 的实例名称,type 属性解析为 Bean 的实例类型。若是指定 name 属性,则按实例名称进行装配;若是指定 type 属性,则按 Bean 类型进行装配。若是都不指定,则先按 Bean 实例名称装配,若是不能匹配,则再按照 Bean 类型进行装配;若是都没法匹配,则抛出 NoSuchBeanDefinitionException 异常。
与 @Autowired 注解配合使用,会将默认的按 Bean 类型装配修改成按 Bean 的实例名称装配,Bean 的实例名称由 @Qualifier 注解的参数指定。