文章目录
- 前言
- 1、课程安排
- 2、配置文件开发
- 3、注解开发简单案例
- 4、包扫描
- 5、@scope注解
- 6、@Conditional注解(重点)
- 7、组件注册方法
- 8、Bean生命周期
- 9、@Value注解
- 10、自动装配
- 11、使用Spring容器底层组件
- 12、@Profile注解
- 13、AOP
- 14、声明式事务
- 15、扩展原理
- 16、事件监听
- 17、Spring容器创建过程
- 18、Servlet3.0
前言
此笔记为尚硅谷Spring注解驱动教程(雷丰阳源码级讲解)学习笔记
1、课程安排

根据上面这张脑图,我把整个专栏分成了三个大的部分,分别是:容器、扩展原理以及Web。
1.1、容器
容器作为整个专栏的第一大部分,内容包括:
- AnnotationConfigApplicationContext
- 组件添加
- 组件赋值
- 组件注入
- AOP
- 声明式事务
1.2、扩展原理
扩展原理作为整个专栏的第二大部分,内容包括:
- BeanFactoryPostProcessor
- BeanDefinitionRegistryPostProcessor
- ApplicationListener
- Spring容器创建过程
1.3、Web
Web作为整个专栏的第三大部分,内容包括:
- servlet3.0
- 异步请求
这部分,其实就是SpringMVC,在这个部分中,我们会重点来说异步请求。
2、配置文件开发
2.1、导入Spring-context依赖包
<!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.13</version>
</dependency>
2.2、编写Spring配置文件
在 resources 目录下创建 application.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 http://www.springframework.org/schema/beans/spring-beans.xsd">
</beans>
2.3、编写Person类
public class Person {
private String name;
private Integer age;
// 省略构造方法,get/set方法,toString方法
}
2.4、在Spring配置文件中创建Bean
<bean id="person" class="com.xjhqre.pojo.Person">
<property name="name" value="xjhqre"/>
<property name="age" value="18"/>
</bean>
2.5、编写测试类
public class SpringTest {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("application.xml");
Person person = (Person) applicationContext.getBean("person");
System.out.println(person);
}
}
测试结果:
Person{
name='xjhqre', age=18}
3、注解开发简单案例
3.1、创建配置类
在方法上面加上@Bean注解后,Spring会以方法返回类型作为组件的类型,方法名作为组件的 id
当向@Bean中添加参数时,默认添加的第一个参数是value,强制给组件赋值id
// 配置类等同于xml配置文件
@Configuration // 告诉Spring这是一个配置类
public class MainConfig {
// 给容器中注册一个Bean;类型为返回值的类型,id默认是用方法名作为id
@Bean("person")
public Person person01(){
return new Person("lisi", 20);
}
}
3.2、测试代码
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig.class);
Person bean = applicationContext.getBean(Person.class);
System.out.println(bean);
}
测试结果:
Person{
name='lisi', age=20}
4、包扫描
只要标注了@Controller、@Service、@Repository、@Component的,都会被扫描加入到容器里
**注意:**配置类自身也会被扫描到容器中,如果存在多个配置类,则多个配置类里的所有bean对象都会被扫描进容器中
4.1、配置文件包扫描
<context:component-scan base-package="com.xjhqre"/>
4.2、注解扫描
在配置类上添加注解@ComponentScan
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig {
// ...
}
4.3、排除扫描
Filter[] excludeFilters() default {
};
注解排除excludeFilters的返回类型为Filter[]
Filter 的排除类型 FilterType 有一下几种:
- ANNOTATION:按注解排除,常用
- ASSIGNABLE_TYPE:按类型排除,常用
- ASPECTJ:按AspectJ 类型模式表达式排除,不常用
- REGEX:按正则表达式排除
- CUSTOM:自定义排除
下面以ANNOTATION按注解排除来演示:
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
Controller.class, Service.class})
})
public class MainConfig {
// ...
}
4.4、指定扫描
扫描的时候只需要包含哪些组件,编写方式和excludeFilters一样
注意要关闭默认的扫描过滤器
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
Controller.class, Service.class})
}, useDefaultFilters = false)
public class MainConfig {
// ...
}
在 jdk8 之后可以写多个@ComponentScan,写法如下:
@Configuration // 告诉Spring这是一个配置类
@ComponentScans(
value = {
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {
Controller.class, Service.class})
}, useDefaultFilters = false)
}
)
public class MainConfig {
// ...
}
4.5、按类型扫描
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
BookController.class})
}, useDefaultFilters = false)
public class MainConfig {
// ...
}
4.6、自定义扫描
4.6.1、编写TypeFilter的实现类
public class MyTypeFilter implements TypeFilter {
/**
* @param metadataReader 读取到当前正在扫描类的信息
* @param metadataReaderFactory 可以获取其他任何类的信息
*/
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
// 获取当前类注解的信息
AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
// 获取当前正在扫描类的信息
ClassMetadata classMetadata = metadataReader.getClassMetadata();
// 获取当前类的资源信息(类的路径)
Resource resource = metadataReader.getResource();
String className = classMetadata.getClassName();
System.out.println(className);
return className.contains("er");
}
}
4.6.2、修改配置类
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre", includeFilters = {
@ComponentScan.Filter(type = FilterType.CUSTOM, classes = {
MyTypeFilter.class})
}, useDefaultFilters = false)
public class MainConfig {
// ...
}
5、@scope注解
5.1、默认单实例
5.1.1、编写配置类
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
@Bean("person")
public Person person() {
return new Person("张三", 25);
}
}
5.1.2、编写测试
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Person bean = applicationContext.getBean(Person.class);
Person bean2 = applicationContext.getBean(Person.class);
System.out.println(bean.hashCode());
System.out.println(bean2.hashCode());
}
输出结果:
com.xjhqre.config.MainConfig2
com.xjhqre.config.MyTypeFilter
com.xjhqre.controller.BookController
com.xjhqre.pojo.Person
404214852
404214852
可以看到 bean 和 bean2 是同一个实例
5.2、@scope参数
ConfigurableBeanFactory.SCOPE_PROTOTYPE,
ConfigurableBeanFactory.SCOPE_SINGLETON,
org.springframework.web.context.WebApplicationContext.SCOPE_REQUEST,
org.springframework.web.context.WebApplicationContext.SCOPE_SESSION, value
后两个需要导入 Spring web 的jar包,且基本不用,只讨论前两个
在ConfigurableBeanFactory中有两个常量:
// 单实例,默认值,在IOC容器启动时就会调用方法创建对象,以后每次获取直接从容器中拿(map.get())
String SCOPE_SINGLETON = "singleton";
// 多实例,在使用getBean()获取bean对象时才会创建
String SCOPE_PROTOTYPE = "prototype";
WebApplicationContext中的两个常量
// 同一次请求创建一个实例
String SCOPE_REQUEST = "request";
// 同一个session创建一个实例
String SCOPE_SESSION = "session";
5.3、测试多实例
5.3.1、修改配置类
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
@Bean("person")
@Scope("prototype")
public Person person() {
return new Person("张三", 25);
}
}
5.3.2、测试代码
@Test
public void test2() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig2.class);
Person bean = applicationContext.getBean(Person.class);
Person bean2 = applicationContext.getBean(Person.class);
System.out.println(bean.hashCode());
System.out.println(bean2.hashCode());
}
测试结果:
com.xjhqre.config.MainConfig2
com.xjhqre.config.MyTypeFilter
com.xjhqre.controller.BookController
com.xjhqre.pojo.Person
961712517
1928931046
可以看到两个 bean 的哈希值不同
5.4、懒加载
针对单实例设置,因为单实例是在容器启动时创建对象
懒加载就是不让单实例在容器启动时创建,在第一次获取Bean时创建对象并初始化。
只需要在方法上添加注解@Lazy
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig2 {
@Bean("person")
@Lazy
public Person person() {
return new Person("张三", 25);
}
}
6、@Conditional注解(重点)
按照一定的条件进行判断,满足条件给容器中注册Bean
@Conditional源码
@Target({
ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
// 传入一个Condition数组
Class<? extends Condition>[] value();
}
Condition接口源码:
@FunctionalInterface
public interface Condition {
/**
* ConditionContext:判断条件能使用的上下文环境
* AnnotatedTypeMetadata:注释信息
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
6.1、业务场景
如果是linux系统则注册linus,如果是windows系统则注册bill Gates
6.2、编写WindowsCondition
编写windows条件,需要实现接口Condition,重写matches方法
public class WindowsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
// 判断是否是Windows系统
// 1. 获取到IOC使用的beanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 2. 获取类加载器
ClassLoader classLoader = context.getClassLoader();
// 3. 获取运行环境
Environment environment = context.getEnvironment();
// 4. 获取到bean定义的注册类
BeanDefinitionRegistry registry = context.getRegistry();
// 容器中是否包含person,判断容器中的bean注册情况,也可以给容器中注册bean
boolean person = registry.containsBeanDefinition("person");
// 运行环境是否是Windows
String property = environment.getProperty("os.name");
assert property != null;
return property.contains("Windows");
}
}
6.3、编写LinuxCondition
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment environment = context.getEnvironment();
String property = environment.getProperty("os.name");
assert property != null;
return property.contains("linux");
}
}
6.4、编写配置类
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
public class MainConfig3 {
@Bean("bill")
@Conditional({
WindowsCondition.class})
public Person person() {
return new Person("bill gates", 62);
}
@Bean("linus")
@Conditional({
LinuxCondition.class})
public Person person2() {
return new Person("linus", 48);
}
}
6.5、编写测试
@Test
public void test3() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
Map<String, Person> beansOfType = applicationContext.getBeansOfType(Person.class);
System.out.println(beansOfType);
}
测试结果:
{
bill=Person{
name='bill gates', age=62}}
当@Conditional配置在类上时,表示只有满足条件时,这个配置类配置的所有bean才会生效
7、组件注册方法
- 包扫描 + 组件标注注解(@Controller、@Service、@Repository、@Component),这种方式只能注册自己写的类
- @Bean,可以注册第三方包的类
- @Import:快速给容器中导入一个组件,id默认是全类名
- ImportSelector:一个接口,返回需要导入的组件的全类名数组
- ImportBeanDefinitionRegistrar:一个接口,手动注册bean到容器中
- 使用Spring提供的FactoryBean(工厂Bean)
7.1、@Import(重点)
7.1.1、编写配置类
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({
Color.class, Red.class}) // 导入组件,id默认是组件的全类名
public class MainConfig3 {
}
7.1.2、编写测试
@Test
public void test4() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
7.2、ImportSelector
7.2.1、编写MyImportSelector
// 自定义逻辑,返回需要导入的组件
public class MyImportSelector implements ImportSelector {
// 返回值就是要导入到容器中的组件的全类名
// AnnotationMetadata:当前标注@Import注解类的所有信息
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
"com.xjhqre.pojo.Blue", "com.xjhqre.pojo.Yellow"};
}
}
7.2.2、修改配置类
@Configuration // 告诉Spring这是一个配置类
@ComponentScan(value = "com.xjhqre")
@Import({
Color.class, Red.class, MyImportSelector.class}) // 导入组件,id默认是组件的全类名
public class MainConfig3 {
}
7.2.3、测试
@Test
public void test4() {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfig3.class);
String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
for (String beanDefinitionName : beanDefinitionNames) {
System.out.println(beanDefinitionName);
}
}
测试结果:
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
mainConfig3
com.xjhqre.pojo.Color
com.xjhqre.pojo.Red
com.xjhqre.pojo.Blue
com.xjhqre.pojo.Yellow
7.3、ImportBeanDefinitionRegistrar
7.3.1、编写MyImportBeanDefinitionRegistrar
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
/**
* AnnotationMetadata:当前类的注解信息
* BeanDefinitionRegistry:注册类
* 把所有需要添加到容器中的bean,调用BeanDefinitionRegistry.registerBeanDefinition手动注册
*/
@Override
public void registerBeanDefinitions

本文深入探讨了Spring框架的注解驱动开发,包括容器、扩展原理和Web相关概念。讲解了@Profile、@Bean、@ComponentScan、@Import、@Scope、@Conditional等注解的使用,以及如何自定义Bean后置处理器、注册组件、处理生命周期方法。同时,介绍了AOP、事务管理的实现,以及如何利用Spring容器底层组件如ServletContainerInitializer和WebApplicationInitializer。此外,文章还涵盖了SpringMVC的配置和异步请求处理。

1206

被折叠的 条评论
为什么被折叠?



