SpringBoot相关注解详细使用
SpringBoot相关注解详细使用
1. @SpringBootApplication 启动类
SpringBootApplication包含以下注解
1.1 @ComponentScan
@Service,@Repository,@Component,@Controller
用来定义一个bean.@ComponentScan
注解就是用来自动扫描被这些注解标识的类,最终生成ioc容器里的bean.可以通过设置@ComponentScan(basePackages,includeFilters,excludeFilters)
属性来动态确定自动扫描范围,类型以及不扫描的类型.
默认情况下:它扫描所有类型,并且扫描范围是@ComponentScan注解所在配置类包及子包的类
总结:
使用@SpringBootApplication注解,就说明你使用了@ComponentScan的默认配置,这就建议你把使用@SpringBootApplication注解的类放置在root package(官方表述)下,其他类都置在root package的子包里面,这样bean就不会被漏扫描.
1.2 @SpringBootConfiguration
这个注解的作用与@Configuration
作用相同,都是用来声明当前类是一个配置类.可以通过@Bean
注解生成IOC容器管理的bean.
例如,启动类:
@SpringBootApplication
public class QuickStartApplication {
public static void main(String[]args){
SpringApplication.run(QuickStartApplication.class,args);
}
//直接生成ioc容器管理的bean
@Bean
public BeanTest beanTest(){
return new BeanTest();
}
}
1.3 @EnableAutoConfiguration
@EnableAutoConfiguration
是springboot实现自动化配置的核心注解,通过这个注解把spring应用所需的bean注入容器中.@EnableAutoConfiguration
源码通过@Import
注入了一个ImportSelector
的实现类 AutoConfigurationImportSelector
,这个ImportSelector
最终实现根据我们的配置,动态加载所需的bean.
AutoConfigurationImportSelector
的完成动态加载实现方法源码如下:
@Override
//annotationMetadata 是@import所用在的注解.这里指定是@EnableAutoConfiguration
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
//加载XXConfiguration的元数据信息(包含了某些类被生成bean条件),继续跟进这个方法调用,就会发现加载的是:spring-boot-autoconfigure jar包里面META-INF的spring-autoconfigure-metadata.properties
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//获取注解里设置的属性,在@SpringBootApplication设置的exclude,excludeName属性值,其实就是设置@EnableAutoConfiguration的这两个属性值
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//从spring-boot-autoconfigure jar包里面META-INF/spring.factories加载配置类的名称,打开这个文件发现里面包含了springboot框架提供的所有配置类
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//去掉重复项
configurations = removeDuplicates(configurations);
//获取自己配置不需要生成bean的class
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
//校验被exclude的类是否都是springboot自动化配置里的类,如果存在抛出异常
checkExcludedClasses(configurations, exclusions);
//删除被exclude掉的类
configurations.removeAll(exclusions);
//过滤刷选,满足OnClassCondition的类
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
//返回需要注入的bean的类路径
return StringUtils.toStringArray(configurations);
}
总结:
springboot是通过注解@EnableAutoConfiguration
的方式,去查找,过滤,加载所需的configuration
@ComponentScan
扫描我们自定义的bean@SpringBootConfiguration
可以简单的理解为就是一个@Configuration注解,表示当前类是一个配置类,通过@Configuration
与 @Bean
结合,将Bean注册到Spring ioc 容器。因此@SpringBootApplication
的作用等价于同时组合使用@EnableAutoConfiguration,@ComponentScan,@SpringBootConfiguration.
2. @RestControllerAdvice 统一异常处理
返回统一处理异常 json处理的字符串
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
@AliasFor("basePackages") //设置要扫描的包 一般为控制器所在的包
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
2.1 @ControllerAdvice
https://blog.csdn.net/zxfryp909012366/article/details/82955259
返回非json处理的字符串
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?>[] assignableTypes() default {};
Class<? extends Annotation>[] annotations() default {};
}
3. 自动注入属性
baidu: api: APP_ID: 17757379 API_KEY: QkqPC4AHa7ZCqVMGrWCvQ5Ns SECRET_KEY: rUIGyeIXpgWomyVASObwgNOgqc5fD9j9
3.1 @ConfigurationProperties
需要注入到IOC容器
@Getter
@Setter
//将组装的属性注入到ioc中 无需开启自动注入
@Configuration
@ConfigurationProperties(prefix = "baidu.api")
public class BaiduProperties {
private String APP_ID;
private String API_KEY;
private String SECRET_KEY;
}
3.2 @EnableConfigurationProperties
@Getter
@Setter
//此时未注入ioc
@ConfigurationProperties(prefix = "baidu.api")
public class BaiduProperties {
private String APP_ID;
private String API_KEY;
private String SECRET_KEY;
}
@SpringBootApplication
//开启属性配置
@EnableConfigurationProperties(BaiduProperties.class)
public class WechartSpringBoot {
public static void main(String[] args) {
SpringApplication.run(WechartSpringBoot.class);
}
}
3.3 @Value("${name:默认值}")
注意:在
@Value
注入属性的时候,该类必须注册到IOC中才能获取到该值
属性上加@value可不用定义该属性的get,set方法,否则要加get,set方法
# 阿里云短信配置
aliyun:
sms:
expireTime: 1
limitTimes: 10
@RestController
public class MatchUserController {
//短信过期时间 默认1分钟
@Value("${aliyun.sms.expireTime:1}")
private long expireTime;
//短信获取上限 默认一天10次
@Value("${aliyun.sms.limitTimes:10}")
private Integer limitTimes;
}
4. @Enable*
功能:开启某方面的支持
@EnableScheduling
开启计划任务的支持
@EnableAsync
开启异步方法的支持
@EnableAspectJAutoProxy
开启对 AspectJ 代理的支持
@EnableTransactionManagement
开启对事务的支持
@EnableCaching
开启对注解式缓存的支持
所有@Enable 注解都是有@Import的组合注解,@Enable 自动开启的实现其实就是导入了一些自动配置的Bean**
4.1 示例@EnableScheduling原理
4.1.2 直接导入配置类(@Configuration 类)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
@Configuration
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public class SchedulingConfiguration {
@Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
4.1.3 依据条件选择配置类(实现 ImportSelector 接口)
作用:配置类不确定,需根据@Import注解所标识的类或另一个注解(通常是注解)里的定义信息选择配置类
public class AsyncConfigurationSelector extends AdviceModeImportSelector<EnableAsync> {
private static final String ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME =
"org.springframework.scheduling.aspectj.AspectJAsyncConfiguration";
@Override
@Nullable
public String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] {ProxyAsyncConfiguration.class.getName()};
case ASPECTJ:
return new String[] {ASYNC_EXECUTION_ASPECT_CONFIGURATION_CLASS_NAME};
default:
return null;
}
}
}
4.1.4 动态注册Bean(实现 ImportBeanDefinitionRegistrar 接口)
如果注入的bean不是确定类或者不是spring专用的(一般确切知道注入某个类的话,可以用@Component,@Service,@Repository,@Bean等),并不想用spring的注解进行侵入式标识,那么就可以通过@Import注解,实现ImportBeanDefinitionRegistrar接口来动态注册Bean
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
boolean exposeProxy() default false;
}
关键点在于 AspectJAutoProxyRegistrar实现了ImportBeanDefinitionRegistrar接口。ImportBeanDefinitionRegistrar的作用是在运行时自动添加Bean到已有的配置类。例子:Mybatis 中大名鼎鼎的@MapperScan
5.导入文件
5.1 @PropertySource 导入*.properties文件
@Configuration
@PropertySource("classpath:person.properties")
@ConfigurationProperties(prefix = "person")
@Getter
@Setter
public class Person {
private String name;
private Integer age;
private String gender;
}
person.properties
:
person.name=张三
person.age=10
person.gender=男
5.2 @ImportResource("classpath:cd-config.xml") 导入xml
public class HelloController {
public String sayHello() {
return "say hello";
}
}
<?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 class="org.lc.xml.controller.HelloController" id="sayHello"></bean>
</beans>
@Configuration
//加载bean.xml配置文件,注入到spring容器中
@ImportResource(locations = "classpath:beans.xml")
public class WebMvcConfig {
}
注入使用
:
@RunWith(SpringRunner.class)
@SpringBootTest
public class XmlTest {
@Autowired
private HelloController helloController;
@Test
public void Test1() {
String s = helloController.sayHello();
System.out.println(s);
}
}
6.Jackson注解总结
https://blog.csdn.net/blwinner/article/details/98532847
6.1 @RequestBody部分属性为null的问题
https://blog.csdn.net/qq_41170102/article/details/105905761
- 使用
@JsonProperty
7.@Configuration中的proxyBeanMethods属性详解
首先引出两个概念:Full 全模式
,Lite 轻量级模式
- Full(proxyBeanMethods = true) :proxyBeanMethods参数设置为true时即为:Full 全模式。 该模式下注入容器中的同一个组件无论被取出多少次都是同一个bean实例,即单实例对象,在该模式下SpringBoot每次启动都会判断检查容器中是否存在该组件
- Lite(proxyBeanMethods = false) :proxyBeanMethods参数设置为false时即为:Lite 轻量级模式。该模式下注入容器中的同一个组件无论被取出多少次都是不同的bean实例,即多实例对象,在该模式下SpringBoot每次启动会跳过检查容器中是否存在该组件
- 什么时候用Full全模式,什么时候用Lite轻量级模式? 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间有依赖关系时,建议使用Full全模式 当在你的同一个Configuration配置类中,注入到容器中的bean实例之间没有依赖关系时,建议使用Lite轻量级模式,以提高springboot的启动速度和性能
总结
- Full 全模式,proxyBeanMethods默认是true:使用代理,也就是说该配置类会被代理,直接从IOC容器之中取得bean对象,不会创建新的对象。SpringBoot总会检查这个组件是否在容器中是否存在,保持组件的单实例
- Lite 轻量级模式,proxyBeanMethods设置为false:每次调用@Bean标注的方法获取到的对象是一个新的bean对象,和之前从IOC容器中获取的不一样,SpringBoot会跳过检查这个组件是否在容器中
实例:
@Data public class Car { private String name; private String model; public Car() { } public Car(String name, String model) { this.name = name; this.model = model; } }
@Data public class Driver { private String name; private Integer age; private Car car; public Driver() { } public Driver(String name, Integer age, Car car) { this.name = name; this.age = age; this.car = car; } }
@Configuration(proxyBeanMethods = true) public class MyConfiguration { @Bean public Car car() { return new Car("奥迪","A6L"); } @Bean public Driver driver() { return new Driver("张三",20,car()); } }
启动类:
@SpringBootApplication public class DemoApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(DemoApplication.class, args); String[] beanDefinitionNames = run.getBeanDefinitionNames(); //打印所有容器中的bean System.out.println(Arrays.toString(beanDefinitionNames)); MyConfiguration bean = run.getBean(MyConfiguration.class); Car car1 = bean.car(); Car car2= bean.car(); System.out.println("是否相等" + (car1 == car2)); Car car3 = bean.car(); Car car4 = bean.driver().getCar(); System.out.println("是否相等" + (car3 == car4)); } }
- 当
proxyBeanMethods = true
时,结果都相等。- 当
proxyBeanMethods = true
时,结果都不等。