Spring学习笔记
一个核心思想:将对象交给Spring容器管理。
OCP原则:开闭原则。对扩展开发,对修改关闭。
1 基于xml的方式注册Bean
1.1 注册自定义的Bean
1.1.1 <beans>标签
-
default-autowire: -
default-autowire-candidates: -
default-destory-method: -
default-init-method: -
default-lazy-init: -
profile:指定开发环境。可以使用以下两种方式指定被激活的环境:
- 使用命令行动态参数,虚拟机参数位置加载:
-Dspring.profiles.active=test。 - 使用代码的方式设置环境变量:
System.setProperty("spring.profiles.active", "dev")。 -
<beans profile="dev"> <bean id="userService1" class="com.zxs.service.impl.UserServiceImpl"/> </beans>
- 使用命令行动态参数,虚拟机参数位置加载:
-
default-merge: -
xml:base: -
xml:id: -
xml:lang: -
xml:space: -
xsi:nil: -
xsi:noNamespaceSchemaLocation: -
xsi:type
1.1.2 <bean>标签
对于xml配置文件中的<bean>标签,常用的属性有:
-
id:bean的id。 -
class:bean的全限定类名。 -
name:bean的别名,多个别名之间用逗号,隔开。 -
scope:bean的作用域。取值为singleton或者prototype。singleton:单例,默认值,在spring容器创建的时候,就会进行Bean的实例化,并存储到容器内部的单例池中,每次getBean()时都是从单例池中获取相同的bean实例对象。prototype:spring容器初始化的时候不会实例化Bean,当调用getBean()时才会实例化Bean,每次调用getBean()方法都会创建一个新的Bean实例对象。
-
lazy-init:bean的实例化时机。即是否延迟加载。BeanFactory作为容器时失效,因为BeanFactory作为容器时,所有的bean都是懒加载的。- 为
true时,当spring容器创建的时候,不会立即创建Bean实例对象,等待用到时再创建bean实例并存储到单例池中去,后续再使用该Bean直接从单例池(需要scope配置为单例)获取即可。
- 为
-
init-method:在bean实例化后,指定的初始化方法。 -
factory-method:bean工厂方法,应用于静态工厂或实例工厂。 -
factory-bean:实例工厂bean。 -
destroy-method:该bean被销毁前要调用的方法。 -
autowire:自动注入。可以为default|byType|byName|constructor|no。 -
autowire-candidate:可以为true|default|false。 -
depends-on: -
parent: -
primary: -
abstract:
1.1.3 <import>标签
用于导入其他配置文件,项目变大后,就会导致一个配置文件内容过多,可以将一个配置文件根据业务模块进行拆分,拆分后,最终通过<import>标签导入到一个主配置文件中,项目加载主配置文件就连同<import>导入的文件一并加载了。
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--导入商品模块配置文件-->
<import resource="classpath:productModule.xml"/>
<!--导入用户模块配置文件-->
<import resource="classpath:userModule.xml"/>
</beans>
1.1.4 <alias> 标签(很少使用)
作用:为某个Bean添加别名,与在<bean>标签上使用name属性添加别名的方式一样。
在beanFactory中维护着一个名为aliasMap的Map<String, String>集合,存储别名和beanName之间的映射关系。
<?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 http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"/>
<alias name="userService" alias="service"/>
</beans>
1.1.5 <description> 标签
作用:用于描述配置文件的信息。它通常位于配置文件的头部,用来提供关于该配置文件的一些额外信息或注释,比如配置的目的、作者、创建日期等。这些信息对于开发人员来说是非常有用的,因为它们能够帮助开发团队更好地理解配置文件的作用及其背景。
<description>
<![CDATA[
This is the Spring configuration file for the Spring-based JUnit tests.
]]>
</description>
1.2 注册非自定义的Bean
1.3 引入外部的配置文件
常见的配置文件为properties文件。
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring
jdbc.username=root
jdbc.password=root
<!-- system-properties-mode="NEVER"表示不加载系统属性,同时也就不会加载系统环境变量了
多个配置文件之间用逗号隔开
location="classpath:*.properties" 加载所有以properties结尾的配置文件
location="classpath*:*.properties" 加载所有以properties结尾的配置文件,前面的一个*表示不加可以从当前工程目录下读取,还可以从它所依赖的jar包中读取
-->
<context:property-placeholder location="classpath:jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>
<bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
2 基于注解方式注册Bean
3 Bean实例化的基本流程
spring容器在初始化的时候,会读取xml配置文件中的<bean>标签,,并且将每一个<bean>标签的信息封装成一个BeanDefinition对象。所有的BeanDefinition对象存储到BeanDefinitionMap(该集合在类中定义)集合中,然后Spring遍历该集合,使用反射创建Bean实例对象,创建好所有单例的Bean对象存储在DefaultSingletonBeanRegistry对象中的一个名为singletonObjects的集合中,当调用getBean()方法时从该Map集合中取出Bean的实例对象。
注:
BeanDefinitionMap和singletonObjects都是ConcurrentHashMap集合,都定义在org.springframework.beans.factory.support.DefaultListableBeanFactory类中。
4 Bean的生命周期
3.1 Bean后处理器
spring的后处理器是spring对外开放的重要扩张点,允许我们介入到bean的整个实例化流程中,以达到动态注册BeanDefinition,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
BeanFactoryPostProcessor:Bean工厂后处理器,在BeanDefinitionMap填充完毕,Bean实例化之前执行。该处理器主要是对BeanDefinition进行增删改查。BeanPostProcessor:Bean后处理器,一般在Bean实例化之后,填充到单例池之前执行。改处理器主要是对Bean对象进行增删改查。
BeanFactoryPostProcessor是一个接口规范,实现了该接口的类只要交由Spring容器管理的话,那么Spring就会回调该接口的方法,用于对BeanDefinition注册和修改的功能。
package com.zxs;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.support.RootBeanDefinition;
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
// 动态的注册一个BeanDefinition
BeanDefinition beanDefinition = new RootBeanDefinition("com.zxs.dao.impl.PeronDaoImpl");
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition("personDao", beanDefinition);
// 强制修改指定beanName对象的className名
BeanDefinition userDao = beanFactory.getBeanDefinition("userDao");
userDao.setBeanClassName("com.zxs.service.impl.UserServiceImpl");
System.out.println("BeanDefinitionMap填充完毕后回调该方法");
}
}
spring提供了一个BeanFactoryPostProcessor的子接口BeanDefinitionRegistryPostProcessor专门用来注册BeanDefinition。
对于使用注解注册Bean的方式,其底层是通过ClassPathBeanDefinitionScanner(该类实现了BeanDefinitionRegistryPostProcessor接口)类来扫描并注册为Bean。
BeanPostProcessor

3.2 Bean的完整生命周期
spring bean的生命周期大体上可以分为三个阶段:
- Bean的实例化阶段:Spring框架会取出BeanDefinition的信息进行判断当前Bean的范围是否是singleton的,是否是延迟加载的,是否是FactoryBean等,最终将一个普通的singleton的Bean通过反射进行实例化。
- Bean的初始化阶段:Bean创建之后还仅仅是个“半成品”,还需要对Bean实例化的属性进行填充、执行一些Aware接口方法、执行BeanPostProcessor方法、执行InitalizingBean接口方法的初始化方法、执行自定义init方法等。该阶段是Spring最具技术含量和复杂度的阶段,Aop增强功能,Spring的注解功能等。Spring高频面试题Bean的循环引用问题都是在这个阶段体现的。
- Bean的完成阶段:经过初始化阶段,Bean就成为了一个完整的Spring Bean,被存储到单例池singletonObjects中去了,即完成了Spring Bean的整个声明周期。
Bean的初始化阶段是重点。
3.1.1 Bean的初始化阶段
Bean的初始化过程涉及以下几个过程:
- Bean实例的属性填充。
- Aware接口属性注入。
BeanPostProcessor的before()方法回调。Initializing接口的初始化回调。- 自定初始化方法init回调。
BeanPostProcessor的after()方法回调。
5 spring整合其他框架
5.1 spring整合MyBatis
5.2 spring整合junit
注意:junit必须是5版本。
需要导入的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>6.1.14</version>
</dependency>
具体的测试类的写法
import com.zxs.config.SpringConfig;
import com.zxs.domain.Student;
import com.zxs.service.StudentService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.junit.jupiter.SpringJUnitConfig;
import java.util.List;
@SpringJUnitConfig(classes = {SpringConfig.class})
public class TestMybatis {
private StudentService studentService;
@Autowired
public TestMybatis(StudentService studentService) {
this.studentService = studentService;
}
@Test
void test01() {
List<Student> students = studentService.getStudents();
for (Student student : students) {
System.out.println(student);
}
}
}