前言
本文为对java拉勾高薪训练营,spring章节的复习
1 Spring Ioc基础
1.1 BeanFactory
与 ApplicationContext
的区别
(1)BeanFactory
是spring容器中的顶级接口,它用来定义一些基础功能,定义一些规范
(2)ApplicationContext
是它的一个字接口,具有BeanFactory
的全部功能
(3)通常,我们称BeanFactory
为spring的基础容器,ApplicationContext
为spring容器的高级接口
启动Ioc容器的方式
(1)java环境下启动ioc容器
ClassPathXmlApplicationContext
:从类的根路径下加载配置⽂件(推荐使⽤)
FileSystemXmlApplicationContext
:从磁盘路径上加载配置⽂件
AnnotationConfigApplicationContext
:纯注解模式下启动Spring容器
(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>
<paramvalue>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>
1.2 纯xml模式
本部分内容我们不采⽤⼀⼀讲解知识点的⽅式,⽽是采⽤Spring IoC 纯 xml 模式改造我们前⾯⼿写的IoC 和 AOP 实现,在改造的过程中,把各个知识点串起来
(1)xml文件头
<?xml version="1.0" encoding="UTF-8"?>
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
(2)实现bean的三种方式
- 方式一:使⽤⽆参构造函数
在默认情况下,它会通过反射调⽤⽆参构造函数来创建对象。如果类中没有⽆参构造函数,将创建失败。
<!--配置service对象-->
<bean id="userService" class="com.lagou.service.impl.TransferServiceImpl">
</bean>
- 方式二:使⽤静态⽅法创建
在实际开发中,我们使⽤的对象有些时候并不是直接通过构造函数就可以创建出来的,它可能在创建的过程 中会做很多额外的操作。此时会提供⼀个创建对象的⽅法,恰好这个⽅法是static修饰的⽅法,即是此种情 况。
例如,我们在做Jdbc操作时,会⽤到java.sql.Connection
接⼝的实现类,如果是mysql数据库,那么⽤的就是JDBC4Connection
,但是我们不会去写JDBC4Connection connection = new JDBC4Connection()
,因 为我们要注册驱动,还要提供URL和凭证信息,⽤DriverManager.getConnection
⽅法来获取连接。
那么在实际开发中,尤其早期的项⽬没有使⽤Spring框架来管理对象的创建,但是在设计时使⽤了⼯⼚模式 解耦,那么当接⼊spring之后,⼯⼚类创建对象就具有和上述例⼦相同特征,即可采⽤此种⽅式配置。
<!--使⽤静态⽅法创建对象的配置⽅式-->
<bean id="userService" class="com.lagou.factory.BeanFactory"
factory-method="getTransferService"></bean>
- 方式三:使⽤实例化⽅法创建
此种⽅式和上⾯静态⽅法创建其实类似,区别是⽤于获取对象的⽅法不再是static修饰的了,⽽是类中的⼀ 个普通⽅法。此种⽅式⽐静态⽅法创建的使⽤⼏率要⾼⼀些。
在早期开发的项⽬中,⼯⼚类中的⽅法有可能是静态的,也有可能是⾮静态⽅法,当是⾮静态⽅法时,即可 采⽤下⾯的配置⽅式:
<!--使⽤实例⽅法创建对象的配置⽅式-->
<bean id="beanFactory" class="com.lagou.factory.instancemethod.BeanFactory"></bean>
<bean id="transferService" factory-bean="beanFactory" factory-method="getTransferService"></bean>
(3)Bean的作用范围及⽣命周期
- 作⽤范围的改变
在spring框架管理Bean对象的创建时,Bean对象默认都是单例的,但是它⽀持配置的⽅式改变作⽤范围。作⽤范围官⽅提供的说明如下图
在上图中提供的这些选项中,我们实际开发中⽤到最多的作⽤范围就是singleton
(单例模式)和prototype
(原型模式,也叫多例模式)。配置⽅式参考下⾯的代码:
<!--配置service对象-->
<bean id="transferService"
class="com.lagou.service.impl.TransferServiceImpl" scope="singleton">
</bean>
- 不同作⽤范围的⽣命周期
单例模式:singleton
对象出⽣:当创建容器时,对象就被创建了。
对象活着:只要容器在,对象⼀直活着。
对象死亡:当销毁容器时,对象就被销毁了。
⼀句话总结:单例模式的bean对象⽣命周期与容器相同。
多例模式:prototype
对象出⽣:当使⽤对象时,创建新的对象实例。
对象活着:只要对象在使⽤中,就⼀直活着。
对象死亡:当对象⻓时间不⽤时,被java的垃圾回收器回收了。
⼀句话总结:多例模式的bean对象,spring框架只负责创建,不负责销毁。
(4)Bean标签属性
在基于xml的IoC配置中,bean标签是最基础的标签。它表示了IoC容器中的⼀个对象。换句话说,如果⼀个对象想让spring管理,在XML的配置中都需要使⽤此标签配置,Bean标签的属性如下:
id属性: ⽤于给bean提供⼀个唯⼀标识。在⼀个标签内部,标识必须唯⼀。
class属性:⽤于指定创建Bean对象的全限定类名。
name属性:⽤于给bean提供⼀个或多个名称。多个名称用空格分隔。
factory-bean属性:⽤于指定创建当前bean对象的⼯⼚bean的唯⼀标识。当指定了此属性之后,class属性失效。
factory-method属性:⽤于指定创建当前bean对象的⼯⼚⽅法,如配合factory-bean属性使⽤,则class属性失效。如配合class属性使⽤,则⽅法必须是static的。
scope属性:⽤于指定bean对象的作⽤范围。通常情况下就是singleton。当要⽤到多例模式时,可以配置为prototype。
init-method属性:⽤于指定bean对象的初始化⽅法,此⽅法会在bean对象装配后调⽤。必须是⼀个⽆参⽅法。
destory-method属性:⽤于指定bean对象的销毁⽅法,此⽅法会在bean对象销毁前执⾏。它只能为scope是singleton时起作⽤。
(5) DI 依赖注入的xml配置
- 依赖注⼊分类
按照注⼊的⽅式分类
构造函数注⼊:顾名思义,就是利⽤带参构造函数实现对类成员的数据赋值。
set⽅法注⼊:它是通过类成员的set⽅法实现数据的注⼊。(使⽤最多的)
按照注⼊的数据类型分类
基本类型和String:注⼊的数据类型是基本类型或者是字符串类型的数据。
其他Bean类型:注⼊的数据类型是对象类型,称为其他Bean的原因是,这个对象是要求出现在IoC容器中的。那么针对当前Bean来说,就是其他Bean了。
复杂类型(集合类型):注⼊的数据类型是Aarry,List,Set,Map,Properties中的⼀种类型。
1.3 xml与注解相结合模式
首先还是通过配置xml来启动ApplicationContext容器,然后在xml配置文件中开启注解驱动,这样就可以在bean的配置中使用注解进行开发
注意:
(1) 实际企业开发中,纯xml模式使⽤已经很少了
(2) 引⼊注解功能,不需要引⼊额外的jar
(3) xml+注解结合模式,xml⽂件依然存在,所以,spring IOC容器的启动仍然从加载xml开始
(4) 哪些bean的定义写在xml中,哪些bean的定义使⽤注解
1.4 纯注解模式
改造xml+注解模式,将xml中遗留的内容全部以注解的形式迁移出去,最终删除xml,从Java配置类启动
对应注解
@Configuration
注解,表名当前类是⼀个配置类
@ComponentScan
注解,替代 context:component-scan
@PropertySource
,引⼊外部属性配置⽂件
@Import
引⼊其他配置类
@Value
对变量赋值,可以直接赋值,也可以使⽤ ${} 读取资源配置⽂件中的信息
@Bean
将⽅法返回对象加⼊ SpringIOC 容器
2 Spring IOC 高级特性
spring中bean的生命周期
2.1 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
,那么 bean
1 在容器启动时被实例化,⽽ 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 就占⽤资源
2.2 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;
}
}
2.3 后置处理器
Spring提供了两种后处理bean的扩展接⼝,分别为 BeanPostProcessor
和BeanFactoryPostProcessor
,两者在使⽤上是有所区别的。
⼯⼚初始化(BeanFactory)—> Bean对象
在BeanFactory初始化之后可以使⽤BeanFactoryPostProcessor
进⾏后置处理做⼀些事情
在Bean对象实例化(并不是Bean的整个⽣命周期完成)之后可以使⽤BeanPostProcessor
进⾏后置处理做⼀些事情
注意:对象不⼀定是springbean,⽽springbean⼀定是个对象
2.3.1 BeanPostProcess()
BeanPostProcessor
是针对Bean级别的处理,可以针对某个具体的Bean
该接⼝提供了两个⽅法,分别在Bean
的初始化⽅法前和初始化⽅法后执⾏,具体这个初始化⽅法指的是什么⽅法,类似我们在定义bean时,定义了init-method
所指定的⽅法
定义⼀个类实现了BeanPostProcessor
,默认是会对整个Spring容器中所有的bean进⾏处理。如果要对具体的某个bean处理,可以通过⽅法参数判断,两个类型参数分别为Object和String,第⼀个参数是每个bean的实例,第⼆个参数是每个bean的name或者id属性的值。所以我们可以通过第⼆个参数,来判断我们将要处理的具体的bean。
注意:处理是发⽣在Spring容器的实例化和依赖注⼊之后
2.3.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对象
总结
以上是对spring中的基础玉高级特性进行回顾。下一篇对spring源码进行深度解析