spring5注解开发学习笔记
目录
- 前言
- 1 @Configuration和@Bean(给容器中注册组件)
- 2 @ComponentScan( 包扫描)
- 3 使用TypeFilter指定@ComponentScan注解的过滤规则
- 4 @Scope(设置作用域)
- 5 @Lazy --- bean懒加载
- 6 @Conditional --- 按照一定条件给容器中注入bean
- 7 @Import --- 给容器中快速导入一个组件
- 8 在@Import注解中使用ImportSelector接口导入bean
- 9 在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean
- 10 使用FactoryBean向Spring容器中注册bean
- 11 @Bean指定初始化和销毁方法
- 12 使用InitializingBean和DisposableBean来管理bean的生命周期
- 13 @PostConstruct注解和@PreDestroy
- 14 BeanPostProcessor后置处理器
- 15 @Value --- 为属性赋值
- 16 @PropertySource --- 加载配置文件
- 17 @Autowired、@Qualifier、@Primary --- 自动装配
- 18 @Resource注解和@Inject注解
- 19 构造器、参数、方法和属性的自动装配
- 20 自定义组件想要使用Spring容器底层的一些组件
- 21 @Profile --- 环境配置与切换
- 22 搭建AOP测试环境
- 23 扩展原理
- 24 ioc容器创建原理
- 25 spring源码总结
- 26 servlet3.0
- 27 定制与接管 Spring MVC
- 28 异步处理
- 29 Spring MVC中的异步请求处理(返回Callable)
- 30 pring MVC中的异步请求处理(返回DeferredResult)
- 总结
前言
首先本篇文章是基于尚硅谷雷神讲解的spring注解驱动开发视频。
注意:视频中使用的是spring4版本,目前使用spring5版本,略有差异。
本篇文章将会带你进入spring注解驱动开发的学习大门中。
文章将会按照下图的脉络进行学习。
特别注意:源码讲解部分因为本人无任何工作经验, 学习时间过短,理解不了。这部分等工作之后再来看,未完待续。
容器
容器作为第一大部分,内容包括:
- AnnotationConfigApplicationContext
- 组件添加
- 组件赋值
- 组件注入
- AOP
- 声明式事务
扩展原理
扩展原理作为第二大部分,内容包括:
- BeanFactoryPostProcessor
- BeanDefinitionRegistryPostProcessor
- ApplicationListener
- Spring容器创建过程
这部分主要是源码讲解。
Web
Web作为第三大部分,内容包括:
- servlet3.0
- 异步请求
这部分,其实就是SpringMVC,在这个部分中,我们会重点来说异步请求。
1 @Configuration和@Bean(给容器中注册组件)
1.1 准备工作 — 创建工程
使用idea21.3创建一个maven工程(spring-annotion)。
1.2 配置依赖 — pom.xml
将打包方式设置为jar包。引入spring-context依赖,这里我们选择最新的5.3.22版本作为配置工具。
1.3 创建实体类 — Person
1.4 使用xml配置方式
1.4.1 创建一个beans.xml
1.4.2 创建测试类 — MainTest
1.5 使用注解方式
使用上面的xml文件配置方式,我们发现太过于繁琐。那不如使用注解方式来实现。
1.5.1 创建配置类 — MainConfig
配置类 MainConfig
其实就相当于配置文件beans.xml
。
@Configuration : 告诉spring这是一个配置类。
@Bean :给容器中注册一个bean。相当于beans.xml中的bean。beans.xml中的id对应配置类中的方法名,beans.xml中的class对应的是配置类中的返回值类型。
启动时调用方法,将方法的返回值放到IOC容器中,方法名作为组件的id。
1.5.2 测试
上面我们发现是person,这不就是我们创建的配置类的方法名吗。
经观察,使用注解注入JavaBean时,bean在IOC容器中的名称就是使用@Bean注解标注的方法名称。我们可不可以为bean单独指定名称呢?那必须可以啊!只要在@Bean注解中明确指定名称就可以了。如果我们将person
这个方法名改为person01
,那么测试类中测试组件在IOC容器中的名字时输出就是person01
。都不需要修改方法名,只需要在@Bean(“person01”)既可。
输出结:person01
。
1.6 小结
我们在使用注解方式向Spring的IOC容器中注入JavaBean时,如果没有在@Bean注解中明确指定bean的名称,那么就会使用当前方法的名称来作为bean的名称;如果在@Bean注解中明确指定了bean的名称,那么就会使用@Bean注解中指定的名称来作为bean的名称。
2 @ComponentScan( 包扫描)
在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。
Spring包扫描功能可以使用XML配置文件进行配置,也可以直接使用@ComponentScan注解进行设置,使用@ComponentScan注解进行设置比使用XML配置文件来配置要简单的多。
2.1 创建三层架构组件
BookController
BookDao
BookService
2.2 使用xml配置方式
2.2.1 在beans.xml中配置
使用xml配置方式进行包扫描需要引入名称空间context
具体配置:
-
包扫描,只要标注了@Controller @Service @Repository @Component的都会被加入到ioc容器中。
-
注意包扫描还有一个默认规则,是扫描全部,即use-default-filters = true,我们设置只包含/只扫描的时候要禁用,设置为false。
2.2.2 测试
问题:不知道为什么包扫描使用xml配置文件扫描不出自己创建的三层架构组件?
2.3 @ComponentScan(包扫描)
2.3.1 配置中上进行注解
excludeFilters = Filter[]
设置哪些不扫描 指定扫描的时候按照什么规则排除哪些组件,不包含哪些组件,
type
是指按哪种类型来进行过滤,classes
为一个数组,里面为具体的过滤条件实体。includeFilter =Filter[]
只包含哪些组件,必须设置useDefaultFilters = false
,禁用默认全局扫描。@ComponentScans
:可设置多个扫描策略。
2.3.2 测试
2.3.3 @ComponentScan本质
3 使用TypeFilter指定@ComponentScan注解的过滤规则
3.1 FilterType的几种类型
FilterType
- ANNOTATION :按照注解
- ASSIGNABLE_TYPE :按照指定类型 ,比如说classes={BookService.class}
- ASPECTJ:基本不用
- REGEX:按照正则表达式
- CUSTOM:按照自定义规则。需要我们创建一个 TypeFilter的实现类
3.2 使用自定义过滤规则
3.2.1 创建一个TypeFilter的实现类 — MyTypeFilter
继承TypeFilter
- match方法返回值是布尔类型,如果是true–匹配成功,false–匹配失败
- metadataReader ----读取到的当前正在扫描的类的信息
- metadataReaderFactory -----可以获取到其他任何类信息的
3.2.2 配置类中配置
3.2.3 测试
4 @Scope(设置作用域)
4.1 注解方式
4.1.1 创建MainConfig2配置类
scopr的四种类型:
prototype
:
- 多实例
- ioc容器启动并不会去调用方法创建对象放到容器中,每次获取的时候才会调用方法创建对象,获取一次创建一次。
singleton
:
- 单实例(默认)
- ioc容器启动会调用方法创建对象放到Ioc容器中,以后每次获取就是直接从容器(相当于map.get())中拿。
request
:
- 同一次请求创建一个实例 — web工程中 -----基本不使用
session
:
- 同一个session创建一个实例 — web工程中 -----基本不使用
4.1.2 测试
当我们设置多实例的时候:
输出结果:
结果为false,表示每次创建的实例都不是同一个。
4.1.2 beans.xml中配置
在beans.xml中我们这样设置。如下图:
5 @Lazy — bean懒加载
Spring在启动时,默认会将单实例bean进行实例化,并加载到Spring容器中去。也就是说,单实例bean默认是在Spring容器启动的时候创建对象,并且还会将对象加载到Spring容器中。如果我们需要对某个bean进行延迟加载,那么该如何处理呢?此时,就需要使用到@Lazy注解了。
5.1 什么是懒加载
懒加载 :容器启动不创建对象,第一次使用(获取)Bean创建对象,并初始化。
5.2 注解实现方式
5.3 测试
在类中方法里面加入一句输出语句,代表实例被创建调用。如上。
[1] 使用懒加载
输出结果中没有调用类中的方法,意味着容器创建完成了,但是实例没有被创建。
当我们获取bean的时候,看测试结果
发现调用方法了,即创建实例。
[2] 未使用懒加载
不设置获取对象。
我们看到在容器创建的时候,就已经调用方法了。创建实例了。
6 @Conditional — 按照一定条件给容器中注入bean
6.1 概述
-
Spring支持按照条件向IOC容器中注册bean,满足条件的bean就会被注册到IOC容器中,不满足条件的bean就不会被注册到IOC容器中。
-
@Conditional注解可以按照一定的条件进行判断,满足条件向容器中注册bean,不满足条件就不向容器中注册bean。
-
@Conditional注解是由Spring Framework提供的一个注解,它位于 org.springframework.context.annotation包内。
从@Conditional注解的源码来看,@Conditional注解不仅可以添加到类上,也可以添加到方法上。在@Conditional注解中,还存在着一个Condition类型或者其子类型的Class对象数组,Condition是个啥呢?
可以看到,它是一个接口。所以,我们使用@Conditional注解时,需要写一个类来实现Spring提供的Condition接口,它会匹配@Conditional所符合的方法,然后我们就可以使用我们在@Conditional注解中定义的类来检查了。
6.2 创建接口实现类
@Conditional ({Condition}):按照一定的条件进行判断,满足条件给容器中注册Bean
- 如果当前IOC容器操作环境是windows系统,则给容器中注册bill
- 如果当前IOC容器操作环境是LINXU系统,则给容器中注册linus
6.3 创建两个方法
6.4 在方法上使用注解
6.5 测试
[1] 使用注解
测试结果,当前操作环境是window 10,会创建bill这个bean。
[2] 不使用注解
7 @Import — 给容器中快速导入一个组件
目前给容器中注册组件有以下几种方法。
- 包扫描+组件标注注解(@Controller/@Service/@Reposity/@Component ) ----局限于我们自己创建的类
- @Bean(导入的第三方包里面的组件)
- @Import(快速给容器中导入一个组件)
下面我们来使用@import注解给容器中快速导入一个组件。
7.1 创建两个类
7.2 使用注解快速给容器中导入组件
7.3 测试
8 在@Import注解中使用ImportSelector接口导入bean
ImportSelector:返回需要导入的组件的全类名数组;
8.1 创建MyImportSelector
使用selectImports方法自定义返回需要导入的组件。比如我们想要导入blue和yellow这两个。
8.2 创建两个类 — Blue Yellow
8.3 测试
9 在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean
不仅可以使用@Import注解快速向容器中导入bean,也可以在@Import注解中使用ImportSelector接口的方法导入bean,今天,我们就来说说,如何在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean。
ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar本质上是一个接口。在ImportBeanDefinitionRegistrar接口中,有一个registerBeanDefinitions()方法,通过该方法,我们可以向Spring容器中注册bean实例。
Spring官方在动态注册bean时,大部分套路其实是使用ImportBeanDefinitionRegistrar接口。
所有实现了该接口的类都会被ConfigurationClassPostProcessor处理,ConfigurationClassPostProcessor实现了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中动态注册的bean是优先于依赖其的bean初始化的,也能被aop、validator等机制处理。
9.1 创建接口实现类 — MyImportBeanDefinitionRegistrar
以上registerBeanDefinitions()方法的实现逻辑很简单,就是判断Spring容器中是否同时存在以com.atguigu.bean.Red
命名的bean和以com.atguigu.bean.Blue
命名的bean,如果真的同时存在,那么向Spring容器中注入一个以rainBow命名的bean。
9.2 创建RainBow
9.3 测试
9.4 @import注解小结
@Import(快速给容器中导入一个组件)
- 1)@Import(要导入到容器中的组件):容器中就会自动注册这个组件,id默认是全类名。
- 2)ImportSelector:返回需要导入的组件的全类名数组;
- 3)MyImportBeanDefinitionRegistrar : 手动注册bean到容器中
10 使用FactoryBean向Spring容器中注册bean
10.1 创建一个spring自定义的FactoryBean
- T getObject():返回由FactoryBean创建的bean实例,如果isSingleton()返回true,那么该实例会放到Spring容器中单实例缓存池中
- boolean isSingleton():返回由FactoryBean创建的bean实例的作用域是singleton还是prototype
- Class getObjectType():返回FactoryBean创建的bean实例的类型
这里,需要注意的是:当配置文件中标签的class属性配置的实现类是FactoryBean时,通过 getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()代理了getBean()方法。
10.2 创建一个color类
10.3 在配置类中配置 — 注册组件
10.4 测试
[1] 输出对象
默认获取到的是工厂bean调用getobject创建的对象。设置单实例返回true:即单实例。
创建一次,实例是单实例,返回的是getObject调用的对象。
[2] 返回工厂bean本身
10.5 小结
使用Spring提供的FactoryBean(工厂Bean)
- 1)默认获取到的是工厂bean调用getobject创建的对象
- 2)要获取工厂bean本身,我们需要给id前面加一个前缀“&”
&colorFactorBean
11 @Bean指定初始化和销毁方法
在Spring中,我们可以自己来指定bean的初始化和销毁的方法。我们指定了bean的初始化和销毁方法之后,当容器在bean进行到当前生命周期的阶段时,会自动调用我们自定义的初始化和销毁方法。
11.1 使用xml配置文件方式
如下:
需要注意的是:在我们自己写的Person类中,需要存在init()方法和destroy()方法。而且Spring中还规定,这里的init()方法和destroy()方法必须是无参方法,但可以抛出异常。
11.2 使用@Bean注解方式
11.2.1 创建Car类
11.2.2 创建配置类 — MainConfigOfLifeCycle
在配置类中将car对象注入spring容器,在bean注解中加入代码,即可实现指定初始化和销毁方法。
11.2.3 创建测试类 — IocTest_LifeCycle
[1] 单实例对象
测试结果输出:
对于单实例:
- 初始化:对象创建完成,并赋值好,调用初始化方法。
- 销毁:容器关闭的时候。
[2] 多实例对象
那么多实例的情况是如何的呢?
- 获取bean的时候才进行初始化,可以从上面的测试类中看到我们加入了获取bean实例这一行代码。
- 容器只会帮你创建bean实例,不会调用销毁方法帮你销毁。
11.3 小结
指定初始化和销毁方法:
- xml配置方式:指定init-method 和 destory-method
- 通过@Bean注解方式:initMethod 和 destroyMethod
12 使用InitializingBean和DisposableBean来管理bean的生命周期
12.1 接口概述
通过让Bean实现 InitializingBean(定义初始化逻辑),DisposableBean(定义销毁逻辑)。
InitializingBean接口:
afterPropertiesSet()方法是在属性赋好值之后调用的。
DisposableBean接口:
在销毁前,Spring将会调用DisposableBean接口的destroy()方法。
12.2 创建接口实现类 — Cat
12.3 测试
[1] 配置类中进行包扫描注册组件
[2] 测试
13 @PostConstruct注解和@PreDestroy
13.1 概述
JSR250:
- @PostConstruct : 在bean创建完成并且属性赋值完成;来执行初始化方法
- @PreDestroy : 在容器销毁bean之前通知我们进行清理工作
在JDK中还提供了两个注解能够在bean创建完成并且属性赋值完成之后执行一些初始化工作和在容器销毁bean之前通知我们进行一些清理工作。
@PostConstruct注解
@PostConstruct注解被用来修饰一个非静态的void()方法。被@PostConstruct注解修饰的方法会在服务器加载Servlet的时候运行,并且只会被服务器执行一次。@PostConstruct注解修饰的方法通常在构造函数之后,init()方法之前执行。
通常我们是会在Spring框架中使用到@PostConstruct注解的,该注解的方法在整个bean初始化中的执行顺序如下:
Constructor(构造方法)→@Autowired(依赖注入)→@PostConstruct(注释的方法)
@PreDestroy注解
被@PreDestroy注解修饰的方法会在服务器卸载Servlet的时候运行,并且只会被服务器调用一次,类似于Servlet的destroy()方法。被@PreDestroy注解修饰的方法会在destroy()方法之后,Servlet被彻底卸载之前执行。执行顺序如下所示:
调用destroy()方法→@PreDestroy→destroy()方法→bean销毁
13.2 创建Dog类
13.3 测试
从结果可以看出被@PostConstruct注解修饰的方法是在bean创建完成并且属性赋值完成之后才执行的,而被@PreDestroy注解修饰的方法是在容器销毁bean之前执行的,通常是进行一些清理工作。
13.4 小结
- @PostConstruct ---- 对象创建并赋值之后调用
- @PreDestroy ------ 容器移除对象之前
14 BeanPostProcessor后置处理器
14.1 后置处理器 概述
从源码可以看出,BeanPostProcessor是一个接口,其中有两个方法,即postProcessBeforeInitialization和postProcessAfterInitialization这两个方法,这两个方法分别是在Spring容器中的bean初始化前后执行,所以Spring容器中的每一个bean对象初始化前后,都会执行BeanPostProcessor接口的实现类中的这两个方法。
也就是说,postProcessBeforeInitialization方法会在bean实例化和属性设置之后,自定义初始化方法之前被调用,而postProcessAfterInitialization方法会在自定义初始化方法之后被调用。当容器中存在多个BeanPostProcessor的实现类时,会按照它们在容器中注册的顺序执行。对于自定义的BeanPostProcessor实现类,还可以让其实现Ordered接口自定义排序。
因此我们可以在每个bean对象初始化前后,加上自己的逻辑。实现方式是自定义一个BeanPostProcessor接口的实现类,例如MyBeanPostProcessor,然后在该类的postProcessBeforeInitialization和postProcessAfterInitialization这俩方法中写上自己的逻辑。
14.2 创建MyBeanPostProcessor
14.3 测试
14.4 小结
BeanPostProcessor[interface]:bean的后置处理器。
在bean初始化前后进行一些处理工作
postProcessBeforeInitialization:在初始化之前工作
postProcessAfterInitialization:在初始化之后工作
15 @Value — 为属性赋值
从@Value注解的源码中我们可以看出,@Value注解可以标注在字段、方法、参数以及注解上,而且在程序运行期间生效。
15.1 赋值的几种方式
对于第三种方式,我们创建一个Person.properties文件。使用${}从其中取出里面的值。
15.2 测试
16 @PropertySource — 加载配置文件
使用 的配置类依旧是上面创建的Person.properties.
16.1 未进行配置前测试
我们在测试类中测试当前环境中输出person.nickName.测试结果如下:
如上图没有被加载,取出值。
16.2 使用xml配置方式
16.3 使用注解方式
16.3.1 创建 配置类 — MainConfigOfPropertyValues
创建一个配置类MainConfigOfPropertyValues
使用@propertySource读取外部配置文件中的k/v保存到运行的环境变量中;加载完外部的配置文件以后使用${}取出配置文件的值。
16.3.1 测试
如上图,看出已经取出。
其实也可以从当前环境中取出配置文件中的值。
如上图,我们看到已经从环境中取出配置文件中的值了。
17 @Autowired、@Qualifier、@Primary — 自动装配
自动装配:
17.1 @Autowired注解
- 默认优先按照类型去容器中找对应的组件
applicationContext.getBean(BookDao.class)
找到就赋值。 - 如果找到多个相同类型的组件,再将属性的名称作为组件的id去容器中查找
applicationContext.getBean("bookDao")
。
17.1.1 测试
在BookService类中使用@Autowired注解注入的BookDao对象和直接从IOC容器中获取的BookDao对象是同一个对象。
17.2 @Qualifier注解
@Autowired注解默认是优先按照类型去容器中找对应的组件,找到就赋值;如果找到多个相同类型的组件,那么再将属性的名称作为组件的id,到IOC容器中进行查找。
- @Autowired是根据类型进行自动装配的,如果需要按名称进行装配,那么就需要配合@Qualifier注解来使用了。
- @Qualifier(“bookDao”):使用@Qualifier指定需要装配的组件的id,而不是使用属性名。
17.2.1 测试
如果IOC容器中存在多个相同类型的组件时,那么我们可不可以显示指定@Autowired注解装配哪个组件。
如上图。
17.3 无装配的情况
自动装配默认一定要将属性赋值好,没有就会报错。
可以使用@Autowired(required = false);
在注解后面加一个这个,表示找到就装配,找不到就算了。因为加入我们不这样设置的话,执行测试程序会报错。(容器中无任何组件,却还要去获取组件)。
{@org.springframework.beans.factory.annotation.Qualifier(value=bookDao), @org.springframework.beans.factory.annotation.Autowired(required=true)}
为@Autowired注解添加属性required=false后,即使IOC容器中没有对应的对象,Spring也不会抛出异常了。不过,此时装配的对象就为null了。
17.4 @Primary注解
在Spring中使用注解时,常常会使用到@Autowired这个注解,它默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口而言,可能会有几种不同的实现类,而在默认只会采取其中一种实现的情况下,就可以使用@Primary注解来标注优先使用哪一个实现类。
@Primary:默认使用首选的bean.也可以使用@Qualifier指定默认使用哪个
也就是说如果是在没有明确指定的情况下,那么就装配优先级最高的首选的那个bean,如果是在明确指定了的情况下,那么自然就是装配指定的那个bean了。
18 @Resource注解和@Inject注解
spring还支持使用@Resource(JSR250)和@Inject(JSR330)[java规范的注解]
- @Resource:
- 可以和@Autowired一样实现自动装配功能;默认是按照组件名称进行装配的; 不支持@Primary功能,不支持@Autowired(required = false);
- @Inject:
- 需要导入javax.inject的包,和Autowires的功能一样,支持@Primary,只不过接口里没有任何方法(比如说@Autowired(required = false))
18.1 @Resource注解
@Resource注解是Java规范里面的,也可以说它是JSR250规范里面定义的一个注解。该注解默认按照名称进行装配,名称可以通过name属性进行指定,如果没有指定name属性,当注解写在字段上时,那么默认取字段名将其作为组件的名称在IOC容器中进行查找,如果注解写在setter方法上,那么默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的一点是,如果name属性一旦指定,那么就只会按照名称进行装配。
18.2 @Inject注解
@Inject注解也是Java规范里面的,也可以说它是JSR330规范里面定义的一个注解。该注解默认是根据参数名去寻找bean注入,支持Spring的@Primary注解优先注入,@Inject注解还可以增加@Named注解指定要注入的bean。
要想使用@Inject注解,需要在项目的pom.xml文件中添加如下依赖,即导入javax.inject这个包。
18.3 小结
@Resource和@Inject这俩注解与@Autowired注解的区别
- 不同点
- @Autowired是Spring中的专有注解,而@Resource是Java中JSR250规范里面定义的一个注解,@Inject是Java中JSR330规范里面定义的一个注解
- @Autowired支持参数required=false,而@Resource和@Inject都不支持
- @Autowired和@Inject支持@Primary注解优先注入,而@Resource不支持
- @Autowired通过@Qualifier指定注入特定bean,@Resource可以通过参数name指定注入bean,而@Inject需要通过@Named注解指定注入bean
- 相同点
三种注解都可以实现bean的自动装配。
19 构造器、参数、方法和属性的自动装配
19.1 概述
可以看出@Autowired注解不仅可以标注在字段上,而且还可以标注在构造方法、实例方法以及参数上。
19.2 测试
-
当@Autowired注解标注在方法上时,Spring容器在创建当前对象的时候,就会调用相应的方法为对象赋值。如果标注的方法存在参数时,那么方法使用的参数和自定义类型的值,需要从IOC容器中获取。
-
使用@Autowired注解标注在构造方法上时,构造方法中的参数对象也是从IOC容器中获取的.
注意:使用@Autowired注解标注在构造方法上时,如果组件中只有一个有参构造方法,那么这个有参构造方法上的@Autowired注解可以省略,并且参数位置的组件还是可以自动从IOC容器中获取。
19.3 小结
无论@Autowired注解是标注在字段上、实例方法上、构造方法上还是参数上,参数位置的组件都是从IOC容器中获取。
-
如果方法只有一个IOC容器中的对象作为参数,当@Autowired注解标注在这个方法的参数上时,我们可以将@Autowired注解省略掉。也就说@Bean注解标注的方法在创建对象的时候,方法参数的值是从IOC容器中获取的,此外,标注在这个方法的参数上的@Autowired注解可以省略。
-
其实,我们用到最多的还是把@Autowired注解标注在方法位置,即使用@Bean注解+方法参数这种形式,此时,该方法参数的值从IOC容器中获取,并且还可以默认不写@Autowired注解,因为效果都是一样的,都能实现自动装配!
20 自定义组件想要使用Spring容器底层的一些组件
20.1 概述与测试
- 实现一个BeanNameAware接口,我们可以通过BeanNameAware接口获取到当前bean在Spring容器中的名称
- 实现一个EmbeddedValueResolverAware接口,我们通过EmbeddedValueResolverAware接口能够获取到String值解析器
值解析器:用来帮我们解析那些String类型的值的,如果这个String类型的值里面有一些占位符,那么也会帮我们把这些占位符给解析出来,最后返回一个解析后的值。
小结
xxAware接口的底层原理是由XxxAwareProcessor实现类实现的,也就是说每一个XxxAware接口都有它自己对应的XxxAwareProcessor实现类。
例如,我们这里以ApplicationContextAware接口为例,ApplicationContextAware接口的底层原理就是由ApplicationContextAwareProcessor类实现的。从ApplicationContextAwareProcessor类的源码可以看出,其实现了BeanPostProcessor接口,本质上是一个后置处理器。
自定义组件实现xxxAware,在创建对象的时候,会调用接口规定的方法注入相关组件,Aware;
- 把spring底层一些组件注入到自定义的Bean中;
- xxxAware:功能使用XXXProcessor;
- ApplicationContext == > ApplicationContextProcessor;
21 @Profile — 环境配置与切换
21.1 概述
在实际的企业开发环境中,往往都会将环境分为开发环境、测试环境和生产环境,并且每个环境基本上都是互相隔离的,也就是说,开发环境、测试环境和生产环境它们之间是互不相通的。在以前的开发过程中,如果开发人员完成相应的功能模块并通过单元测试后,那么他会通过手动修改配置文件的形式,将项目的配置修改成测试环境,发布到测试环境中进行测试。测试通过后,再将配置修改为生产环境,发布到生产环境中。这样手动修改配置的方式,不仅增加了开发和运维的工作量,而且总是手工修改各项配置文件会很容易出问题。那么,有没有什么方式可以解决这些问题呢?答案是:有!通过@Profile注解就可以完全做到这点。
在容器中如果存在同一类型的多个组件,那么可以使用@Profile注解标识要获取的是哪一个bean。也可以说@Profile注解是Spring为我们提供的可以根据当前环境,动态地激活和切换一系列组件的功能。这个功能在不同的环境使用不同的变量的情景下特别有用,例如,开发环境、测试环境、生产环境使用不同的数据源,在不改变代码的情况下,可以使用这个注解来动态地切换要连接的数据库。
@Profile注解不仅可以标注在方法上,也可以标注在配置类上。
- 如果@Profile注解标注在配置类上,那么只有是在指定的环境的时候,整个配置类里面的所有配置才会生效。
- 如果一个bean上没有使用@Profile注解进行标注,那么这个bean在任何环境下都会被注册到IOC容器中,当然了,前提是在整个配置类生效的情况下。
21.2 引入德鲁伊数据源
21.3 创建jdbc.properties
jdbc.user=root;
jdbc.password=abc123;
jdbc.driverClassName=com.mysql.jdbc.Driver
21.4 编写配置类
21.5 测试
激活环境两种方式:
- 运行时在命令行参数中动态设置:虚拟机参数位置加载:
- -Dspring.profiles.active=test (dev prod)
- 使用代码方式
其实主要是通过AnnotationConfigApplicationContext类的无参构造方法来实现,具体步骤如下:
- 在bean上加@Profile注解,其value属性值为环境标识,可以自定义
- 使用AnnotationConfigApplicationContext类的无参构造方法创建容器
- 设置容器环境,其值为第1步设置的环境标识
- 设置容器的配置类
- 刷新容器
22 搭建AOP测试环境
22.1 引入依赖
22.2 定义目标类
22.3 定义切面类
通知的几种类型:
- 前置通知(对应的注解是@Before):在目标方法运行之前运行
- 后置通知(对应的注解是@After):在目标方法运行结束之后运行,无论目标方法是正常结束还是异常结束都会执行
- 返回通知(对应的注解是@AfterReturning):在目标方法正常返回之后运行
- 异常通知(对应的注解是@AfterThrowing):在目标方法运行出现异常之后运行
- 环绕通知(对应的注解是@Around):动态代理,我们可以直接手动推进目标方法运行(joinPoint.procced())
切入点表达式:
public int com.atguigu.aop.MathCalculator.*(…)
抽取切入点表达式:需要在类上加入@Pointcut
这个注解。
22.4 创建配置类
@EnableAspectJAutoProxy
注意加入这个注解。
22.5 测试
22.6 小结
搭建AOP测试环境时,虽然步骤繁多,但是我们只要牢牢记住以下三点,就会无往而不利了。
- 将切面类和业务逻辑组件(目标方法所在类)都加入到容器中,并且要告诉Spring哪个类是切面类(标注了
@Aspect
注解的那个类)。 - 在切面类上的每个通知方法上标注通知注解,告诉Spring何时何地运行,当然最主要的是要写好切入点表达式,这个切入点表达式可以参照官方文档来写。
- 开启基于注解的AOP模式,即加上
@EnableAspectJAutoProxy
注解,这是最关键的一点。
22.7 AOP动态代理原理
23 扩展原理
24 ioc容器创建原理
25 spring源码总结
26 servlet3.0
26.1 环境准备
1.创建maven工程
注意添加javax.servlet这个依赖,因为如果不加这个依赖,下面的servlet继承不了HttpServlet。
26.2 创建index.html及HelloServlet并测试
index.html
HelloServlet
在注解上使用/hello。
请注意将html的文件创建在webcontent目录下面,而不是web-inf.
结果如上图所示。
小结:
- @WebFilter -------- 注册Filter
- @WebServlet ---- 注册Servlet
- @WebListener — 注册Listener的
- @WebInitParam — 配置servlet和filter初始化参数
26.3 Shared libraries(共享库)/ runtimes pluggability(运行时插件能力)
26.3.1 概述
我们好好看看Servlet 3.0标准规范文档中Shared libraries / runtimes pluggability
这一小节,大概在该小节的第二段描述中,有句话说的是,container(即Servlet容器,比如Tomcat服务器之类的)在启动我们的应用的时候,它会来扫描jar包里面的ServletContainerInitializer
的实现类。
哦豁,我们现在知道了,当Servlet容器启动我们的应用时,它会扫描我们当前应用中每一个jar里面的ServletContainerInitializer的实现类。那究竟是一个怎么扫描法呢?我们再好好看看该小节的第二段描述,它说,我们得提供ServletContainerInitializer的一个实现类,提供完这个实现类之后还不行,我们还必须得把它绑定在META-INF/services/
目录下面的名字叫javax.servlet.ServletContainerInitializer
的文件里面。
也就是说,必须将提供的实现类绑定在META-INF/services/javax.servlet.ServletContainerInitializer
文件中,所谓的绑定就是在javax.servlet.ServletContainerInitializer
文件里面写上ServletContainerInitializer实现类的全类名,也就是说,javax.servlet.ServletContainerInitializer文件中的内容就是咱们提供的ServletContainerInitializer实现类的全类名。
至此,我们才总算搞清楚了这个非常重要的机制,总结一下就是,Servlet容器在启动应用的时候,会扫描当前应用每一个jar包里面的META-INF/services/javax.servlet.ServletContainerInitializer
文件中指定的实现类,然后,再运行该实现类中的方法。
26.3.2 相关类创建
MyServletContainerInitializer
继承ServletContainerInitializer
Servlet容器在启动应用的时候,会将@HandlesTypes注解里面指定的类型下面的子类,包括实现类或者子接口等,全部都给我们传递过来。
26.4 注册三大组件
26.4.1 概述
使用ServletContext注册web组件。这些web组件就是我们通常所说的web三大组件,也就是Servlet、Filter以及Listener。
为什么我们一定要掌握使用ServletContext来注册web组件呢?因为我们是一定会遇到这样场景的,如果是以注解的方式来注册web组件,那么前提是这些web组件是由我们自己来编写的,这样,我们才可以把注解加上。但是,如果项目中导入的是第三方jar包,它们里面是有一些组件的,比如在项目中导入了阿里巴巴的连接池里面的Filter,对于这些组件而言,如果项目中有web.xml文件,那么我们就可以将它们配置在该配置文件中了,但是,我们现在的项目中是没有web.xml文件的,所以我们就要利用ServletContext将它们给注册进来了。
26.4.2 测试
分别创建filter\servlet\listener。
在类中进行注册组件测试
注意点:
- 调用ServletContext对象的addServlet方法(即注册Servlet)和addFilter方法(即注册Filter),都会返回一个Dynamic对象,只不过一个是ServletRegistration里面的Dynamic,一个是FilterRegistration里面的Dynamic。
关于addMappingForUrlPatterns
方法:
- addMappingForUrlPatterns方法中传入的第一个参数还是蛮奇怪的,居然是EnumSet.of(DispatcherType.REQUEST),该参数表示的是Filter拦截的请求类型,即通过什么方式过来的请求,Filter会进行拦截。我们不妨点进DispatcherType枚举的源码里面去看一看,如下图所示,可以看到好多的请求类型,不过常用的就应该是FORWARD和REQUEST它俩。
- 现在addMappingForUrlPatterns方法中传入的第一个参数是EnumSet.of(DispatcherType.REQUEST),表明我们写的UserFilter会拦截通过request方式发送过来的请求。
该方法中的第二个参数(即isMatchAfter)我们直接传入true就行,第三个参数(即urlPatterns)就是Filter要拦截的路径,目前我们传入的是/*,即拦截所有请求。
以上就是我们以编码的方式向ServletContext对象中注册web中的三大组件。
26.4.3 总结
我们可以通过编码的方式在项目启动的时候,给ServletContext(即当前项目)里面来注册组件。当然,并不是说,你只要拿到了ServletContext对象就能注册组件了,因为必须是在项目启动的时候,才能注册组件。
而且,在项目启动的时候,我们可以有两处来使用ServletContext对象注册组件。
第一处就是利用基于运行时插件的ServletContainerInitializer机制得到ServletContext对象,然后再往其里面注册组件。本讲通篇所讲述的就是在这一处使用ServletContext对象来注册组件。
第二处,你可能想不到,我们上面不是编写过一个监听器(即UserListener)吗?它是来监听项目的启动和停止的,在监听项目启动的方法中,传入了一个ServletContextEvent对象,即事件对象,我们就可以通过该事件对象的getServletContext方法拿到ServletContext对象,拿到之后,是不是就可以往它里面注册组件了啊?
26.5 Servlet 3.0与Spring MVC的整合分析
官方文档如下:
Servlet容器在启动我们Spring应用之后,会传入一个我们感兴趣的类型的集合,然后在onStartup方法中拿到之后就会来挨个遍历,如果遍历出来的我们感兴趣的类型不是接口,也不是抽象类,但是WebApplicationInitializer接口旗下的,那么就会创建该类型的一个实例,并将其存储到名为initializers的LinkedList集合中。
也可以这样说,我们Spring的应用一启动就会加载感兴趣的WebApplicationInitializer接口旗下的所有组件,并且为这些WebApplicationInitializer组件创建对象,当然前提是这些组件即不是接口,也不是抽象类。
未完待续
27 定制与接管 Spring MVC
过时?
定制SpringMVC;
1)、@EnableWebMvc:开启SpringMVC定制配置功能;
mvc:annotation-driven/;
2)、配置组件(视图解析器、视图映射、静态资源映射、拦截器。。。)
extends WebMvcConfigurerAdapter
28 异步处理
WebServlet注解有一个asyncSupported属性,其值默认为false,我们只须将其值设置为true,HelloAsyncServlet就能支持异步处理了。
异步处理步骤:
29 Spring MVC中的异步请求处理(返回Callable)
30 pring MVC中的异步请求处理(返回DeferredResult)
总结
这部分spring注解开发驱动视频教程中源码部分需要一定工作经验,只能等工作之后再来看,提升自己。前期部分有些知识已经过时。目前就这样吧,等以后完善。
__EOF__

本文链接:https://www.cnblogs.com/hxld/p/16829438.html
关于博主:评论和私信会在第一时间回复。或者直接私信我。
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!
声援博主:如果您觉得文章对您有帮助,可以点击文章右下角【推荐】一下。您的鼓励是博主的最大动力!
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· 上周热点回顾(3.3-3.9)
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具