个人读书笔记,题外话都写在第1章https://blog.youkuaiyun.com/u013652550/article/details/119800308。
第3章主要介绍了Bean容器相关的各种知识,包括配置和注入,作用域和生命周期等。
3.1 IOC容器
Spring提供了BeanFactory、ApplicationContext两个IOC容器来管理众多的bean对象。
1. bean容器的启动
(建议这一节直接跳了,不过考虑到完整性,还是贴一下大致流程)首先是bean对象的定义,又臭又长的源代码就不贴了,见书31~32页。贴一下成员列表。
beanClass | 保存bean的class属性。 |
scope | 保存bean的作用域。 |
abstractFlag | 保存该bean是否抽象。 |
lazyInit | 保存是否延迟初始化。 |
autowireMode | 保存是否自动装配。 |
dependencyCheck | 保存是否坚持依赖。 |
dependsOn | 保存依赖于哪些bean(必须提前初始化)。 |
constructorArgumentValues | 保存通过构造函数注入的依赖。 |
propertyValues | 保存通过setter方法注入的依赖。 |
factoryBeanName | 用于factorybean。 |
factoryMethodName | 用于factorybean。 |
initMethodName | 对应bean的init-method属性。 |
destroyMethodName | 对应bean的destroy-method属性。 |
bean容器启动的第一步即将xml中每个bean元素分别转换成BeanDefinition对象。第一次看时看不懂最后几项没关系,后面几节马上会讲。
第二步是将bean注册到beanFactory中。BeanDefinitionRegistry接口提供了以下方法:
registerBeanDefinition(String, BeanDefinition);
removeBeanDefinition(String);
getBeanDefinition(String);
containsBeanDefinition(String);
getBeanDefinitionNames();
getBeanDefinitionCount();
isBeanNameInUse(String);
通过英文单词含义即可见名知意,不再赘述。DefaultListableBeanFactory类中实现了该接口,并将beanDefinition保存在了ConcurrentHashMap中。
此外,Spring还对外暴露了一些接口来对bean初始化,例如BeanFactoryPostProcessor。(可覆盖、新增甚至重新初始化bean信息)
2. bean容器的实例化
主要通过反射和CGLIB两种方式。Spring暴露了一些接口,如果目标bean对象实现了这些接口他们对应的方法会被调用,调用顺序见3.4节。就这4个接口来说,调用顺序是表格从上到下的。(PS:是否想起了第1章中@FunctionalInterface的相关知识)(PSS:感觉列表都是没啥用的,继续跳了)
接口 | 方法 | 说明 |
---|---|---|
BeanNameAware | setBeanName | 获取该bean在配置文件中对应的id |
BeanFactoryAware | setBeanFactory | 获取实例化该bean的BeanFactory |
InitializingBean | afterPropertiesSet | bean实例化、所有属性设置后调用初始化方法 |
DisposableBean | destroy | 在bean丢弃的时候调用销毁方法 |
本节重点掌握一下bean的配置。
创建Maven project,并引入spring-core、spring-context。
这里提一嘴Spring Boot,在本书正文的最后两章我们将学习,他与Spring的一个显著不同就是简化了配置,比如这里说的配置操作都是不需要的。最方便的创建Maven项目的方法就是通过IDEA新建项目->Spring Initialzr,这里新建的是Spring Boot,其实并不需要进行配置,但是我们可以借此练习。关于Maven是什么:Maven 教程 | 菜鸟教程 (runoob.com)
不变的开头:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0
https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
</project>
找到dependencies项,插入:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</dependency>
如果代码标红记得点击右上角提示“加载Maven变更”。
bean的XML配置放在src/main/resources/目录下,删除默认application.properties并新建ApplicationContext.xml进行配置即可。
开头大体都是如下的:
<?xml version="1.0" encoding="UTF-8" ?>
<beans default-autowire="byName"
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
</beans>
具体对bean的XML配置法将在下一节一并叙述。
贴一段经常用的测试代码开头:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext
(new String[] {"ApplicationContext.xml"});
BeanFactory factory = context;
((ClassPathXmlApplicationContext) context).close();
}
这里讲一下context的获取:可以ClassPathXmlApplicationContext表示从类路径下加载配置文件,相当于默认带classpath:前缀。如果想用绝对路径,需加上file:前缀,更好的解决方案是使用FileSystemXmlApplicationContext。
(一小段可以跳的废话)ApplicationContext接口实现了BeanFactory接口,故可以直接赋值。它还继承了以下接口:
MessageSource:为应用提供国际化访问功能。
ResourcePatternResolver:提供资源(如URL和文件系统)的访问支持。
ApplicationEventPublisher:引入事件机制。
3.2 Bean的配置
1. 基于XML配置Bean:
一个<bean>通常需要定义id和class属性。class属性是必须的,id不是必需的,除非该bean被引用,通常都加上。
<bean id="xmlCollectionsDemo" class="com.example.airdemo.XmlCollectionsDemo"/>
2. 使用注解定义Bean
POJO:Plain Ordinary Java Object,简单的Java对象。实际就是普通Java Beans,是为了避免和EJB混淆所创造的简称。
使用@Component注解在类声明处对类进行标注,它可以被Spring容器识别,自动将POJO转换为容器管理的bean。此外还有以下3个功能基本和@Component等效的Bean的衍型注解:
@Repository:用于对DAO实现类进行标注。
@Service:用于对Service实现类进行标注。
@Controller:用于对Controller实现类进行标注。
3. 基于Java类提供Bean定义
在普通的POJO类中只要标注@Configuration注解,就可以为Spring容器提供bean定义的信息了。每个标注了@Bean的类方法都相当于提供了一个bean的定义信息。
注意在XML中配置开启context扫描。
<context:component-scan base-package="..."/>
3.3 Bean的注入
1. XML方式注入
属性注入
在类中实现setter方法后在XML中使用property类配置属性,name表示属性名,value用来设置基本数据类型的属性值。引用时可以用<ref>标签配置bean的id属性。
<bean id="CleanAir" class="com.demo.model.CleanAir">
<qualifier value="cleanair"/>
</bean>
<bean id="xmlinstance" class="com.demo.model.XMLInstance">
<property name="air" ref="CleanAir"/>
<property name="name" value="abc"/>
</bean>
构造函数注入
在XML中,使用<constructor-arg>来设置构造函数的参数,index属性设置参数的顺序,参数顺序应该与构造函数一致,ref设置引用bean的id,value设置构造函数参数的值。
<bean id="xmlconstructinstance" class="com.demo.model.XMLInstance">
<constructor-arg index="1" ref="CleanAir"/>
<constructor-arg index="0" value="abc"/>
</bean>
工厂方法注入
分为静态工厂方法和非静态工厂方法。在一个类中定义静态方法,或定义一个实例方法。
1. 静态工厂方法:只需设置工厂方法对应的类,以及对应的工厂方法。
<bean id="..." class="..." factory-method="..."/>
2. 实例工厂方法:需要先实例化工厂类,再通过工厂类对象调用实例方法获取bean对象。
<bean id="xmlfactory" class="..."/>
<bean id="xmlfactoryinstance" factory-bean="xmlfactory" factory-method="..."/>
常见数据类型注入
元素可以使用ref指定,或使用<bean>定义新实例。
1. List与Set属性注入:大体相同,区别为List中元素有序,Set中元素无序。
2. Map属性注入:使用<entry>配置Map里的元素,key指定索引,value指定值。
3. Properties属性注入:<props>配置一个properties对象,<prop>配置一条属性,属性key配置索引。
<property name="lists">
<list>
<value>1</value>
<ref bean="cleanAir"/>
<bean class="com.example.airdemo.CleanAir"/>
</list>
</property>
<property name="sets">
<set>
<value>1</value>
<ref bean="cleanAir"/>
<bean class="com.example.airdemo.CleanAir"/>
</set>
</property>
<property name="maps">
<map>
<entry key="key1" value="1"/>
<entry key="key2" value-ref="cleanAir"/>
<entry key="key3">
<bean class="com.example.airdemo.CleanAir"/>
</entry>
</map>
</property>
<property name="pros">
<props>
<prop key="prokey1">prokeyA</prop>
<prop key="prokey2">prokeyB</prop>
</props>
</property>
4. 自定义属性编辑器:通过继承PropertyEditorSupport的类,重写setAsText方法来实现注入。(记得写setter方法)
<bean id="customEditorConfigurer"
class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="customEditors">
<map>
<entry key="java.util.Date" value="..."/>
</map>
</property>
</bean>
代码示例为Date类。省略处填入自定义的PropertyEditorSupport类的子类的全类名。配置完后将Date当作基本数据类型通过value注入即可。
初始化函数、销毁函数
在xml配置bean时设置destroy-method、init-method属性值对应两个方法的方法名。注解中:
@PostConstruct对应init-method。
@PreDestroy对应destroy-method。
<bean id="..." class="..." destroy-method="..." init-method="..."/>
2. 注解方式注入
@Autowired:默认按类型匹配注入bean。
@Required:适用于bean属性setter方法。
@Qualifier:@Autowired默认是单实例的(见3.4),以此解决一个接口可能有多个实现的问题。
@Value:等价于在xml配置属性时property的value设置默认值。
@Resource:默认按名称匹配注入bean。要求提供一个bean名称的属性,如果属性为空,就自动采用标注处的变量名或方法名作为bean的名称。
开启注解:以下两行任意一行即可。
<context:annotatioin-config/>
<context:component-scan base-package="..."/>
3.4 Bean的作用域和生命周期
1. Bean的作用域
这里主要注意一下singleton(默认)与prototype:默认每次使用getBean方法获取的bean对象是相同的,而配置scope="prototype"后,每次获取都会创建新对象。(贴一张可以忽略的表)(后3种只有当使用基于Web的ApplicationContext时才生效)
作用域 | 描述 |
---|---|
单例(singleton) | 默认,每一个Spring IoC容器都拥有唯一的一个实例对象 |
原型(prototype) | 一个Bean定义,任意多个对象 |
请求(request) | 一个HTTP请求会产生一个Bean对象,也就是说,每一个HTTP请求都有自己的Bean实例 |
会话(session) | 限定一个Bean的作用域为HTTPSession的生命周期 |
全局会话(global session) | 限定一个Bean的作用域为全局HTTPSession的生命周期 |
<bean id="..." class="..." scope="prototype"/>
2. Bean的生命周期
(这一节都可以跳了没感觉有什么用)(为了完整性照着书上贴一下)
容器启动后,会对scope为singleton且非懒加载的bean进行实例化,注入所有属性,然后依次回调接口方法。根据书上示例,BeanPostProcessor接口单独定义一个后置处理器实现,其余接口放在类中实现。调用方法顺序列表如下:
方法 | 所属接口 |
---|---|
构造方法 | \ |
setter方法 | (属性注入)\ |
setBeanName | BeanNameAware |
setBeanFactory | BeanFactoryAware |
setApplicationContext | ApplicationContextAware |
postProcessBeforeInitialization | BeanPostProcessor |
afterPropertiesSet | InitializingBean |
自定义初始化函数 | (init-method)\ |
postProcessAfterInitialization | BeanPostProcessor |
destroy | DisposableBean |
自定义的销毁方法 | (destroy-method)\ |