2 SpringBoot3【② 常用注解】
2 SpringBoot3【② 常用注解】
SpringBoot摒弃XML配置方式,改为全注解驱动
1. 组件注册
@Configuration、@SpringBootConfiguration
@Bean、@Scope
@Controller、 @Service、@Repository、@Component
@Import
@ComponentScan
以前的步骤:通过spring配置文件,如果纯xml配置,需要写Bean对象,唯一标识id,全类名,然后通过set注入或者其他注入方式注入默认属性值
现在的步骤:
1、@Configuration
编写一个配置类(配置类其实也会被存入IOC容器
,可以通过ctrl + 左键点击这个注解发现内嵌了@Component
注解)。其实也可以使用上面的@SpringBootConfiguration
注解,本质其实一样,所以Spring相关的核心配置使用SpringBoot的,而通用配置使用默认的
2、在配置类中,自定义方法给容器中注册组件。配合@Bean
。如果是第三方的我们直接根据类型,写入为配置类的方法,返回它的类型,然后直接return new出来的对象
2.1、@Configuration
注解spring 5.2
以后多了属性 proxyBeanMethods
,可以设置为true
或者false
,默认为true
(效果代表:是不是代理Bean
的方法),如果为true
说明为代理对象调用方法,我们在获取这个对象的时候,会从容器检查有没有这个类的对象,有就拿,没有就创建 (保持组件单实例
)。主要用于解决组件依赖问题。
当不更改这个值,组件在配置类配置依赖可以直接通过set
方法然后传入配置类的其他组件的注入方法(即带有@Bean
的方法)。我们不想有这种依赖关系,设置成false
就是轻量级模式。测试设置为false
对于单个Bean
从容器获取多次还是单实例的,但依赖的情况下,Bean
内部其他的Bean
就不是ioc容器
的那个了,而是一个新new
的。
3、或使用@Import
导入第三方的组件(可以写在组件类(@Conponent,@Controller等)或者配置类上面,与配置类的注解放在一起
),在括号中写对应类的 .class
字节码文件(默认value值,不用写属性,且是一个数组(可以一次导入多个组件到容器
))。或者我们可以使用全类名(推荐
:其实不管怎么样默认ioc容器注入的组件的id名称仍是全类名。因为我们开发过程中可能会修改依赖,而如果导入IOC的时候如果删掉之前一些现在用不到的依赖,这种情况下,本身靠字符串的全类名不会引起报错,但是使用字节码文件的时候不存在这个类会大面积报错),对应name属性
2. 条件注解
如果注解指定的条件成立,则触发指定行为 能写在配置类,写了组件注解的类或者是配置类内部的@Bean方法上
@ConditionalOnXxx
@ConditionalOnClass:如果类路径中存在这个类,则触发指定行为
@ConditionalOnMissingClass:如果类路径中不存在这个类,则触发指定行为
@ConditionalOnBean:如果容器中存在这个Bean(组件),则触发指定行为
@ConditionalOnMissingBean:如果容器中不存在这个Bean(组件),则触发指定行为
场景:
● 如果存在FastsqlException
这个类,给容器中放一个Cat
组件,名cat01,
● 否则,就给容器中放一个Dog
组件,名dog01
● 如果系统中有dog01
这个组件,就给容器中放一个 User组件,名zhangsan
● 否则,就放一个User,名叫lisi
自定义去实现Condition
接口,然后自己写规则
@ConditionalOnBean(value=组件类型,name=组件名字):判断容器中是否有这个类型的组件,并且名字是指定的值
@ConditionalOnRepositoryType (org.springframework.boot.autoconfigure.data)
@ConditionalOnDefaultWebSecurity (org.springframework.boot.autoconfigure.security)
@ConditionalOnSingleCandidate (org.springframework.boot.autoconfigure.condition)
@ConditionalOnWebApplication (org.springframework.boot.autoconfigure.condition)
@ConditionalOnWarDeployment (org.springframework.boot.autoconfigure.condition)
@ConditionalOnJndi (org.springframework.boot.autoconfigure.condition)
@ConditionalOnResource (org.springframework.boot.autoconfigure.condition)
@ConditionalOnExpression (org.springframework.boot.autoconfigure.condition)
@ConditionalOnClass (org.springframework.boot.autoconfigure.condition)
@ConditionalOnEnabledResourceChain (org.springframework.boot.autoconfigure. web)
@ConditionalOnMissingClass(org.springframework.boot.autoconfigure.condition)
@ConditionalOnNotWebApplication (org.springframework.boot.autoconfigure.condition)
@ConditionalOnProperty (org.springframework.boot.autoconfigure.condition)
@ConditionalOnCloudPlatform (org.springframework.boot.autoconfigure.condition)
@ConditionalOnBean(org.springframework.boot.autoconfigure.condition)
@ConditionalOnMissingBean(org.springframework.boot.autoconfigure.condition)
@ConditionalOnMissingFilterBean (org.springframework.boot.autoconfigure.web.servlet)
@Profile (org.springframework.context.annotation)
@ConditionalOnInitializedRestarter (org.springframework.boot.devtools.restart)
@ConditionalOnGraphQlSchema (org.springframework.boot.autoconfigure.graphql)
@ConditionalOnJava (org.springframework.boot.autoconfigure.condition)
3. 属性绑定
3.1. 使用方法
-
方法① :@ConfigurationProperties: 声明组件的属性和配置文件哪些前缀开始项进行绑定,可以写在组件Bean上,也可以写到配置类中的配置Bean的方法上
- 将容器中 任意组件(Bean)的属性值 和 配置文件 的配置项的值 进行绑定
- 1、给容器中注册组件
- ①在配置类外使用
@ConfigurationProperties
结合@Component
,配置类内无需写任何方法 - ②配置类内使用
@Bean
结合@ConfigurationProperties
,配置类外不用写任何注解
- ①在配置类外使用
- 2、使用 @ConfigurationProperties 声明组件和配置文件的哪些配置项进行绑定
- 1、给容器中注册组件
- 将容器中 任意组件(Bean)的属性值 和 配置文件 的配置项的值 进行绑定
-
方法② : @EnableConfigurationProperties:快速注册注解(这是写于配置类上的注解,使用@Import注解导入的Bean不会进行属性绑定)
- 场景:SpringBoot 默认只扫描自己主程序所在的包 。如果导入 第三方包,即使组件上标注了 @Component、@ConfigurationProperties 注解,也没用 。因为组件都扫描不进来,此时使用这个注解就可以 快速进行属性绑定并把组件注册进容器
当你有一些自定义的配置属性,并且希望将这些属性封装到一个 Java 类中进行管理时,就可以使用 @EnableConfigurationProperties
注解。
假设在 application.properties
文件中有如下配置:
myapp.name=My Application
myapp.version=1.0
定义一个 Java 类来封装这些属性:
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String name;
private String version;
// Getters and Setters
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
然后在配置类中使用 @EnableConfigurationProperties
注解来启用这个配置属性类:
@Configuration
@EnableConfigurationProperties(MyAppProperties.class)
public class AppConfig {
// 配置类中的其他配置
}
这样,Spring Boot 就会自动将 application.properties
中以 myapp
为前缀的属性值绑定到 MyAppProperties
类的对应字段上。
@ConfigurationProperties
注解用于将配置文件中的属性值绑定到 Java Bean 的字段上。通过指定 prefix
属性,可以将配置文件中以该前缀开头的属性值映射到对应的 Java Bean 字段。在需要将配置文件中的属性值封装到 Java 对象中进行统一管理时使用,这样可以提高代码的可维护性和可读性。
上面的 MyAppProperties
类就是使用 @ConfigurationProperties
注解的示例。通过 @ConfigurationProperties(prefix = "myapp")
,将 application.properties
中以 myapp
开头的属性值绑定到 MyAppProperties
类的 name
和 version
字段上。
综上所述,@EnableConfigurationProperties
和 @ConfigurationProperties
注解通常一起使用,前者用于启用配置属性类,后者用于定义配置属性类并完成属性绑定。
3.2. 一些拓展使用
那假设同时需要激活大量的配置类,那岂不是需要 @EnableConfigurationProperties
中写大量的类?有没有更好的写法
若你需要激活大量的配置类,在 @EnableConfigurationProperties
中罗列大量类会使代码变得冗长且难以维护。下面为你介绍几种更优的处理办法:
- 使用包扫描
可以把所有配置属性类放置在同一个包下,然后借助 @ComponentScan
或者 @ConfigurationPropertiesScan
来自动扫描并注册这些配置属性类,这样就无需在 @EnableConfigurationProperties
里逐一列出每个类。
假设你的配置属性类都存于 com.example.configprops
包中:
@Configuration
@ConfigurationPropertiesScan("com.example.configprops")
public class AppConfig {
// 其他配置
}
在这个示例中,@ConfigurationPropertiesScan
注解会自动扫描 com.example.configprops
包下所有带有 @ConfigurationProperties
注解的类,并将它们注册到 Spring 容器中。
- 组合配置类
你可以把多个配置属性类组合到一个配置类里,然后仅在 @EnableConfigurationProperties
中指定这个组合配置类。
示例代码
首先定义多个配置属性类:
@ConfigurationProperties(prefix = "app.db")
public class DatabaseProperties {
private String url;
private String username;
private String password;
// Getters and Setters
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
@ConfigurationProperties(prefix = "app.cache")
public class CacheProperties {
private String type;
private int expiration;
// Getters and Setters
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public int getExpiration() {
return expiration;
}
public void setExpiration(int expiration) {
this.expiration = expiration;
}
}
接着创建一个组合配置类:
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties({DatabaseProperties.class, CacheProperties.class})
public class CombinedConfigProperties {
// 可以添加其他配置相关的方法或逻辑
}
最后,在主配置类中只需启用这个组合配置类:
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@EnableConfigurationProperties(CombinedConfigProperties.class)
public class MainAppConfig {
// 其他配置
}
- 借助 Java 配置类的模块化
把相关的配置属性类分组到不同的 Java 配置类中,然后在主配置类中引入这些模块化的配置类。
示例代码
创建不同的模块化配置类:
@Configuration
@EnableConfigurationProperties(DatabaseProperties.class)
public class DatabaseConfig {
// 数据库相关的其他配置
}
@Configuration
@EnableConfigurationProperties(CacheProperties.class)
public class CacheConfig {
// 缓存相关的其他配置
}
在主配置类中引入这些模块化配置类:
@Configuration
@Import({DatabaseConfig.class, CacheConfig.class})
public class MainConfig {
// 其他配置
}
通过上述方法,你能够更有效地管理和激活大量的配置类,让代码更具可读性和可维护性。
4. @Scope
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Scope {
@AliasFor("scopeName")
String value() default "";
@AliasFor("value")
String scopeName() default "";
ScopedProxyMode proxyMode() default ScopedProxyMode.DEFAULT;
}
scopeName
scopeName是为了声明Bean的作用域,在Spring4.2版本以前,有singleton
, prototype
两种模式,4.2之后新加了web作用域(request
, session
, globalsession
)。
singleton
: 单例模式,顾名思义即Spring IOC容器对于一个Bean,只会有一个共享的Bean实例。这一个单一的实例会被存储到单例缓存(singleton cache)中,当有请求或者是引用时,IOC容器都会返回存储在singleton cache的同一个实例。
prototype
: 多实例模式,即当每次客户端向容器获取Bean的时候,IOC容器都会新建一个实例并返回。与单例不同的是,在IOC容器启动的时候并不会创建Bean的实例,并且在有请求创建Bean实例之后也不会管理该实例的生命周期,而是由客户端自行处理。
request
:web应用针对每一次HTTP请求都会创建一个新的Bean实例,且该实例仅在该次HTTP请求有效。
session
:针对每一个session会创建一个Bean实例,且生命周期为该session有效期间。
globalsession
:仅基于portlet的web应用才有意义,否则可以当作session使用。
这五种scopeName的使用方法 (也可以直接写对应的字符串,无视大小写)
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_SINGLETON)
@Scope(scopeName = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
@Scope(scopeName = WebApplicationContext.SCOPE_REQUEST)
@Scope(scopeName = WebApplicationContext.SCOPE_SESSION)
@Scope(scopeName = "globalSession")
ScopeProxyMode
proxyMode
表明了@Scope
注解的Bean是否需要代理。
DEFAULT
:proxyMode的默认值,一般情况下等同于NO,即不需要动态代理。NO
:不需要动态代理,即返回的是Bean的实例对象。INTERFACES
:代理的对象是一个接口,即@Scope的作用对象是接口,这种情况是基于jdk实现的动态代理。TARGET_CLASS
:代理的对象是一个类,即@Scope的作用对象是一个类,是以生成目标类扩展的方式创建代理,基于CGLib实现动态代理。 我的Spring6笔记,可以复习动态代理的知识
@Scope(proxyMode = ScopedProxyMode.DEFAULT)
@Scope(proxyMode = ScopedProxyMode.NO)
@Scope(proxyMode = ScopedProxyMode.INTERFACES)
@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
4.1. 单例 Bean 依赖非单例 Bean 的场景
当一个单例 Bean 依赖一个非单例(如原型 prototype
)的 Bean 时,如果不设置 proxyMode
,单例 Bean 在创建时会注入一个非单例 Bean 的实例,后续无论何时使用这个注入的实例,都是同一个,这就无法体现非单例 Bean 的特性。通过设置合适的 proxyMode
可以确保每次调用单例 Bean 中依赖的非单例 Bean 的方法时,都会获取到一个新的非单例 Bean 实例。
示例代码
// 非单例 Bean
class PrototypeBean {
private int count = 0;
public void increment() {
count++;
System.out.println("Count: " + count);
}
}
// 单例 Bean
class SingletonBean {
private PrototypeBean prototypeBean;
public SingletonBean(PrototypeBean prototypeBean) {
this.prototypeBean = prototypeBean;
}
public void doSomething() {
prototypeBean.increment();
}
}
@Configuration
public class AppConfig {
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
@Bean
public SingletonBean singletonBean(PrototypeBean prototypeBean) {
return new SingletonBean(prototypeBean);
}
}
问题分析
在上述代码中,如果不设置 proxyMode
,SingletonBean
在创建时会注入一个 PrototypeBean
实例,后续每次调用 singletonBean.doSomething()
方法时,使用的都是同一个 PrototypeBean
实例,count
值会不断累加,这与原型 Bean 每次使用都应该是新实例的特性不符。
解决方案
通过设置 proxyMode = ScopedProxyMode.TARGET_CLASS
来为 PrototypeBean
创建代理:
@Bean
@Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE, proxyMode = ScopedProxyMode.TARGET_CLASS)
public PrototypeBean prototypeBean() {
return new PrototypeBean();
}
这样,SingletonBean
注入的是 PrototypeBean
的代理对象,每次调用 singletonBean.doSomething()
方法时,都会获取一个新的 PrototypeBean
实例,count
值每次都会从 0 开始。
4.2. Web 应用中的请求和会话作用域
在 Web 应用中,request
和 session
作用域的 Bean 通常需要设置 proxyMode
。因为单例的控制器或服务 Bean 可能会依赖这些请求或会话作用域的 Bean,如果不设置 proxyMode
,会导致在单例 Bean 中无法正确获取不同请求或会话的 Bean 实例。
示例代码
// 请求作用域的 Bean
class RequestScopedBean {
public void printRequestInfo() {
System.out.println("This is a request-scoped bean.");
}
}
// 单例控制器
class MyController {
private RequestScopedBean requestScopedBean;
public MyController(RequestScopedBean requestScopedBean) {
this.requestScopedBean = requestScopedBean;
}
public void handleRequest() {
requestScopedBean.printRequestInfo();
}
}
@Configuration
public class WebAppConfig {
@Bean
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
public RequestScopedBean requestScopedBean() {
return new RequestScopedBean();
}
@Bean
public MyController myController(RequestScopedBean requestScopedBean) {
return new MyController(requestScopedBean);
}
}
解释
在这个示例中,MyController
是单例的,它依赖于 RequestScopedBean
。通过设置 proxyMode = ScopedProxyMode.TARGET_CLASS
,MyController
注入的是 RequestScopedBean
的代理对象,每次处理请求时,都会获取当前请求对应的 RequestScopedBean
实例。
综上所述,当单例 Bean 依赖非单例 Bean 或者在 Web 应用中处理请求和会话作用域的 Bean 时,设置 proxyMode
可以确保 Bean 的作用域特性得到正确体现,避免出现意外的结果。
5. @ImportResource和@Import
如果想把别的xml
文件的配置也导入,不想重新手写配置类和注解,可以直接在配置类或者组件类上写@ImportResource("classpath:/xxxxxx.xml")
可以把xml
的配置文件的组件导入IOC
容器
@Import
注解提供了三种用法
@Import
一个普通类 spring会将该类加载到spring容器
中,需要有无参构造器
@Import
一个类,该类实现了ImportBeanDefinitionRegistrar
接口,在重写的registerBeanDefinitions
方法里面,能拿到BeanDefinitionRegistry bd
的注册器,能手工往beanDefinitionMap
中注册beanDefinition
,这种方式注册既能进行复杂的判断,也能对Bean进行生命周期和名字更改等功能@Import
一个类 该类实现了ImportSelector
重写selectImports
方法该方法返回了String[]
数组的对象,数组里面的类都会注入到spring容器
当中
6. @ComponentScan
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default "**/*.class";
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
1. value
首先它的value值对应要扫描的包名,可以是一个数组,放入要扫描的包名。
2. excludeFilters = {@Filter(type=FilterType.xxxx, classes={xxx,xxx,xxx}) …}
可以填入一个数组,类型为@Filter注解数组,可以排除要扫描的包。可以按注解,或者是类的类型,或者是正则排除【几乎不用】,或者是AspectJ语法【几乎不用】
type=FilterType.ASSUGNABLE_TYPE
按类型排除
如我们不扫描 @Controller
注解的类和 @Service
注解的类
这里我测试发现排除这个注解,并不会把衍生的@RestController
注解的组件排除注册
@ComponentScan(excludeFilters = {
@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class,Service.class})
})
3. includeFilters
需要进行设置属性关闭默认扫描规则useDefaultFilters = false
用法和上面没有什么区别
4. 自定义类型扫描
定义一个类,实现TypeFilter
接口,实现它的方法
第一个参数metadataReader
:可以通过它获取正在扫描的类的信息:如注解,类的信息,类的资源信息。
进一步比如类名,可以用如className.contains("xxx")
的方法去判断是否名字包含字符【其实本质不如用上面的正则】
第二个参数metadataReaderFactory
可以获得其他的容器组件的信息
使用的时候,只需要@Filter(type=FilterType.CUSTOM,classes={MyFilter})
指定使用我们的过滤规则
public class MyFilter implements TypeFilter {
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
return false;
}
}
7. @Lazy
可以给配置类中加载的Bean上加,即便是单实例Bean,也可以实现懒加载,ioc创建不加载,第一次获取才加载
8. FactoryBean
这里可以参考我的Spring6学习部分的笔记,大同小异,就是实现FactoryBean
接口,然后重写方法,主要是补充:
首先,通过配置类注册@Bean
返回工厂Bean
类,得到的仍是我们实现类,不是工厂。(这里已经知道了)
补充:要是想从ioc获得工厂而不是这个实现类,getBean方法输入Bean名称字符串
,在字符串前面加 &
符号。
9. Bean的生命周期
1. 初始化和销毁方法【栈式初始化和销毁(先初始化的后销毁)】
1. xml
Bean以前在基于xml方式的配置的方法中,我们可以在xml配置参数。
现在真的还有用 xml
的人吗?
2. @Bean
而springBoot或者说注解的spring中,我们可以在@Bean
通过同名的参数指定方法名。这些方法很重要,我们可以在一些数据源初始化的时候把很多信息注入进去。
别忘了多实例Bean
是懒加载,而且每次返回不同的Bean
,同时容器也不帮你管理这个Bean
,容器哪怕销毁
,也不会
调用这个Bean
的destroy
,只是可以帮你创建
,单实例被单例池引用,spring容器销毁即销毁,多实例在虚拟机GC时销毁
3. 实现InitializingBean
和DisposableBean
接口
同时也可以不用这个注解,通过实现InitializingBean
接口重写初始化方法,通过实现DisposableBean
接口重写销毁方法。
4. 注解方式定义初始化方法和销毁方法【来自JSR250规范来自JDK非Spring
】
@PostConstruct
:在Bean
创建完成并属性赋值之后,执行初始化方法
@PreDestroy
:在容器销毁之前执行销毁方法
2. Bean的后置处理器: BeanPostProcessor
【它也是个组件需要注册】
具体的生命周期过程
- 1 bean对象创建(调用无参构造器)
- 2 给bean对象设置属性
- 3 bean的后置处理器(初始化之前)
- 4 bean对象初始化(需在配置bean时指定初始化方法)
- 5 bean的后置处理器(初始化之后)
- 6 bean对象就绪可以使用
- 7 bean对象销毁(需在配置bean时指定销毁方法)
- 8 IOC容器关闭
1. 基本使用
实现BeanPostProcessor
接口并重写前置方法和后置方法
在Bean
的初始化前后进行一些操作。
bean后置处理器不是单独针对某一个bean生效,而是针对IOC容器中所有bean都会执行。
方法会获得Object
类型的Bean
实例,它的返回值是可以对参数获得的Bean
,你可以进行包装或者是直接返回
2. 底层原理
至于源码部分可以看雷神的讲解,总而言之就是底层构建ioc容器的时候,在刷新12大步的时候,会给容器注入Bean,而经历Set
属性赋值之后,会for循环遍历所有后置处理器
,依次执行后置处理器的前置方法。
Spring底层用它完成了很多事情,可以利用Idea去看看它的实现类,比如之前Spring学过的一个接口,如果想给Bean
对象获取ioc
容器也很简单,实现ApplicationContextAware
接口会默认让重写一个set方法,我们设置一个ApplicationContext
类的属性,即可在后置处理器方法实现的过程中,调用postProcessBeforeInitialization
方法给Bean
对象注入进去。
如还有一个AutowireAnnotationBeanPostProcessor
类,它就是容器帮我们进行自动装配的。可以说很多的Spring行为在底层都是基于这个后置处理器完成的
10. @Value
使用这个注解标注在我们的Bean
类的字段属性上,可以进行赋值。
- 基本数值
- 可以写spEL:#{}【很少用】
- 可以写${}:取出配置文件中的值【在运行环境变量中的值】
11. @PropertySource
使用这个注解在配置类上可以读取外部配置文件读取到环境变量中。
@PropertySource
用于指定外部属性文件的位置,可以将属性文件中的属性值注入到Spring Bean
中。它通常与@Value
注解一起使用,将属性文件中的值注入到单个属性
中。
@ConfigurationProperties
用于将属性文件中的值绑定到一个Java对象
上。它可以将属性文件中的多个属性值
注入到一个Java对象
中。与@Value
注解不同,@ConfigurationProperties
可以将属性文件中的值注入到多个属性中。
12. @AutoWired
详情见之前的Spring笔记有最基础的用法,其实无非就是能放到哪些地方,以及和@Qualifier的配合,默认的一些参数,比如required什么的
这里补充一下一些原理:AutowiredAnnotationBeanPostProcessor
在Bean
的生命周期中的后置处理器的前置方法,进行注入。同时如果组件只有一个有参构造器,不需要任何注解标注,就能从容器获取嵌套的内部组件
1. @Primary
注册多个相同类型的Bean
,用@Autowired
,想要指定只能通过@Qualifier
,通过名字指定注入哪个,因为@Autowired
是根据类型注入的,但是这么太麻烦了,我们可以在注入的组件的注入注解上再写一个@Primary
,即可完成注入的情况,如果发现多个同类型的组件,优先把这个注入。同时这两个是可以一起使用的。
@Qualifier注解的优先级高于@Primary注解。也就是说,当一个接口有多个实现类时,如果使用了@Qualifier注解指定了要注入哪个实现类,那么即使有一个实现类被标记了@Primary注解,也不会被优先选择。
2. @Resource和@Inject【分别是JSR250规范和JSR330规范的Java注解非Spring】
@Resource
默认byName注入,没有指定name时把属性名当做name,根据name找不到时,才会byType注入。byType注入时,某种类型的Bean只能有一个。但是不支持Spring
的@Primary
功能和@Autowired
的默认值
功能和required=false
功能
想用@Inject
需要专门maven依赖,和@Autowired
功能一样,支持@Primary
了,但是还是不支持@Autowired的默认值
功能和required=false
功能
3. @Bean标注的方法创建对象的时候,方法参数的值从容器获取【如果有的话】
所以我们不必这种时候写@Autowired
注解,如果容器不存在参数写的类型的组件,IDea能识别到,并标识红色波浪号。
13. 想要给Bean组件拿到Spring底层的组件: xxxxxxAware接口
类似给Bean注入ioc容器,实现ApplicationContextAware
接口重写set方法即可,其他组件类似。本质其实底层是靠对应的xxxxxxxProcessor
后置处理器的postProcessorBeforeInitialization方法
实现的
14. @Profile
这里讲讲通过代码的方式,直接通过ioc
容器的getEnvironment().setActiveProfiles("xxx", "xxxx"...)
可以一次设置单个或多个激活的环境。
- 感谢你赐予我前进的力量