1、BeanFactory与ApplicationContext区别
BeanFactory是Spring框架中IoC容器的顶层接⼝,它只是⽤来定义⼀些基础功能,定义⼀些基础规范,而ApplicationContext是它的⼀个子接口,所以ApplicationContext是具备BeanFactory提供的全部功能的。
通常,我们称BeanFactory为SpringIOC的基础容器,ApplicationContext是容器的⾼级接口,比BeanFactory要拥有更多的功能,比如说国际化⽀持和资源访问(xml,java配置类)等等
2、启动IoC容器的方式
2.1、Java环境下启动IoC容器
- ClassPathXmlApplicationContext:从类的根路径下加载配置⽂件(推荐使用)
- FileSystemXmlApplicationContext:从磁盘路径上加载配置⽂件
- AnnotationConfigApplicationContext:纯注解模式下启动Spring容器
2.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.AnnotationConfigWebApplicationContext</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>
3、实例化Bean的三种方式
方式一:使用无参构造函数
在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>
方式二:使用静态方法创建
在实际开发中,我们使用的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程 中会做很多额外的操作。此时会提供一个创建对象的⽅法,恰好这个方法是static修饰的方法,即是此种情况。
<!--方式二:静态方法-->
<bean id="connectionUtils" class="com.lagou.edu.factory.CreateBeanFactory" factory-method="getInstanceStatic"/>
public class CreateBeanFactory {
public static ConnectionUtils getInstanceStatic() {
return new ConnectionUtils();
}
}
方式三:使用实例化方法创建
此种方式和上面静态方法创建其实类似,区别是用于获取对象的方法不再是static修饰的了,而是类中的一个普通方法。此种方式比静态方法创建的使用几率要高一些。
在早期开发的项目中,工厂类中的方法有可能是静态的,也有可能是非静态方法,当是非静态方法时,即可采用下面的配置方式:
<!--方式三:实例化方法-->
<bean id="createBeanFactory" class="com.lagou.edu.factory.CreateBeanFactory"/>
<bean id="connectionUtils" factory-bean="createBeanFactory" factory-method="getInstance"/>
public class CreateBeanFactory {
public ConnectionUtils getInstance() {
return new ConnectionUtils();
}
}
4、lazy-Init 延迟加载
Bean的延迟加载(延迟创建)
ApplicationContext 容器的默认行为是在启动服务器时将所有 singleton bean 提前进行实例化。提前实例化意味着作为初始化过程的⼀部分,ApplicationContext 实例会创建并配置所有的singleton bean。
比如:
<bean id="testBean" class="cn.lagou.LazyBean" />
该bean默认的设置为:
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="false" />
lazy-init="false",立即加载,表示在spring启动时,立刻进行实例化。
如果不想让⼀个singleton bean 在 ApplicationContext实现初始化时被提前实例化,那么可以将bean设置为延迟实例化。
<bean id="testBean" calss="cn.lagou.LazyBean" lazy-init="true" />
设置 lazy-init 为 true 的 bean 将不会在 ApplicationContext 启动时提前被实例化,而是第⼀次向容器通过 getBean 索取 bean 时实例化的。
如果⼀个设置了立即加载的 bean1,引用了⼀个延迟加载的 bean2 ,那么 bean1 在容器启动时被实例化,而bean2 由于被 bean1 引用,所以也被实例化,这种情况也符合延时加载的 bean 在第⼀次调用时才被实例化的规则。
也可以在容器层次中通过在 元素上使用"default-lazy-init" 属性来控制延时初始化。如下面配置:
<beans default-lazy-init="true">
<!-- no beans will be eagerly pre-instantiated... -->
</beans>
如果⼀个 bean 的 scope 属性为 scope="pototype" 时,即使设置了 lazy-init="false",容器启动时也不会实例化bean,⽽是调⽤ getBean 方法实例化的。
应⽤场景
(1)开启延迟加载⼀定程度提高容器启动和运转性能
(2)对于不常使用的 Bean 设置延迟加载,这样偶尔使用的时候再加载,不必要从⼀开始该 Bean 就占用资源
5、FactoryBean 和 BeanFactory
BeanFactory接口是容器的顶级接口,定义了容器的⼀些基础行为,负责生产和管理Bean的⼀个工厂,具体使用它下下面的子接口类型,比如ApplicationContext;
此处我们重点分析FactoryBean
Spring中Bean有两种,⼀种是普通Bean,⼀种是工厂Bean(FactoryBean),FactoryBean可以生成某⼀个类型的Bean实例(返回给我们),也就是说我们可以借助于它自定义Bean的创建过程。
Bean创建的三种方式中的静态方法和实例化方法和FactoryBean作用类似,FactoryBean使⽤较多,尤其在Spring框架⼀些组件中会使用,还有其他框架和Spring框架整合时使用
// 可以让我们⾃定义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;
......省略getter与setter和tostring方法
}
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>
测试,获取FactoryBean产生的对象
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object companyBean = applicationContext.getBean("companyBean");
System.out.println("bean:" + companyBean);
// 结果如下
bean:Company{name='拉勾', address='中关村', scale=500}
测试,获取FactoryBean,需要在id之前添加“&”
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
Object companyBean = applicationContext.getBean("&companyBean");
System.out.println("bean:" + companyBean);
// 结果如下
bean:com.lagou.edu.factory.CompanyFactoryBean@53f6fd09
6、SpringBean的生命周期
Bean 生命周期的整个执行过程描述:
- 根据配置情况调用Bean 构造方法或工厂方法实例化 Bean。
- 利用依赖注入完成 Bean 中所有属性值的配置注入。
- 如果 Bean 实现了 BeanNameAware 接口,则 Spring 调用Bean 的 setBeanName() 方法传入当前 Bean 的 id 值。
- 如果 Bean 实现了 BeanFactoryAware 接口,则 Spring 调用setBeanFactory() 方法传入当前工厂实例的引用。
- 如果 Bean 实现了 ApplicationContextAware 接口,则 Spring 调用setApplicationContext() 方法传入当前 ApplicationContext 实例的引⽤。
- 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的预初始化方法postProcessBeforeInitialzation() 对 Bean 进行加工操作,此处非常重要,Spring 的 AOP 就是利用它实现的。
- 如果 Bean 实现了 InitializingBean 接口,则 Spring 将调用 afterPropertiesSet() 方法。
- 如果在配置文件中通过 init-method 属性指定了初始化方法,则调用该初始化方法。
- 如果 BeanPostProcessor 和 Bean 关联,则 Spring 将调用该接口的初始化方法postProcessAfterInitialization()。此时,Bean 已经可以被应用系统使用了。
- 如果在 <bean> 中指定了该 Bean 的作用范围为 scope="singleton",则将该 Bean 放入Spring IoC 的缓存池中,将触发 Spring 对该 Bean 的生命周期管理;如果在 <bean> 中指定了该 Bean 的作用范围为 scope="prototype",则将该 Bean 交给调用者,调用者管理
- 如果 Bean 实现了 DisposableBean 接口,则 Spring 会调用destory() 方法将 Spring 中的 Bean 销毁;如果在配置文件中通过 destory-method 属性指定了 Bean 的销毁方法,则 Spring 将调用该方法对 Bean 进行销毁。
注意:Spring 为 Bean 提供了细致全面的生命周期过程,通过实现特定的接口或 <bean> 的属性设置,都可以对 Bean 的生命周期过程产生影响。虽然可以随意配置 <bean> 的属性,但是建议不要过多地使用 Bean 实现接口,因为这样会导致代码和 Spring 的聚合过于紧密
7、后置处理器
Spring提供了两种后处理bean的扩展接口,分别为 BeanPostProcessor 和BeanFactoryPostProcessor,两者在使用上是有所区别的。
工厂初始化(BeanFactory)—> Bean对象
在BeanFactory初始化之后可以使用BeanFactoryPostProcessor进行后置处理做⼀些事情
在Bean对象实例化(并不是Bean的整个生命周期完成)之后可以使用BeanPostProcessor进行后置处理做⼀些事情
注意:对象不⼀定是springbean,而springbean⼀定是个对象
7.1、BeanPostProcessor
BeanPostProcessor是针对Bean级别的处理,可以针对某个具体的Bean
该接⼝提供了两个方法,分别在Bean的初始化方法前和初始化方法后执行,具体这个初始化方法指的是什么方法,类似我们在定义bean时,定义了init-method所指定的方法
定义⼀个类实现了BeanPostProcessor,默认是会对整个Spring容器中所有的bean进行处理。如果要对具体的某个bean处理,可以通过方法参数判断,两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第二个参数,来判断我们将要处理的具体的bean。
注意:处理是发生在Spring容器的实例化和依赖注⼊之后。
7.2、BeanFactoryPostProcessor
BeanFactory级别的处理,是针对整个Bean的工厂进行处理,典型应用:PropertyPlaceholderConfigurer
此接⼝只提供了⼀个方法,方法参数为ConfigurableListableBeanFactory,该参数类型定义了⼀些方法
其中有个方法名为getBeanDefinition的方法,我们可以根据此方法,找到我们定义bean 的
BeanDefinition对象。然后我们可以对定义的属性进行修改,以下是BeanDefinition中的方法
方法名字类似我们bean标签的属性,setBeanClassName对应bean标签中的class属性,所以当我们拿到BeanDefinition对象时,我们可以手动修改bean标签中所定义的属性值。
BeanDefinition对象:我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为⼀个JavaBean,这个JavaBean 就是BeanDefinition
注意:调用 BeanFactoryPostProcessor 方法时,这时候bean还没有实例化,此时 bean 刚被解析成BeanDefinition对象