SpringBoot注解使用
1.@Configuration + @Bean
【比xml配置而言类更好维护、分类和管理阅读】 代理
1.1@Configuration的类就成为了"配置类",【取代application.xml文件】 @Configuration(proxyBeanMethods=true)默认单例,false获取到对象每次不同
总结:
Spring 会创建CGLIB 代理,保证 **@Bean 方法互相调用时,永远返回同一个单例 Bean**。
1.2方法public User getName()上加上@Bean注解【@Bean(name="abc")】对象为abc,无name属性值,则对象为getName\方法名 [取代application.xml中的<bean>标签]
总结:
- 首选
name或value属性 :如果写了@Bean(name="abc"),Bean 的唯一标识就是abc。- 默认使用方法名 :如果没有指定
name,Spring 会直接取方法名getName作为 Bean 的 ID- 用来替代传统的
<bean id="abc" class="...User">配置
2.@Import() 导入组件
底层是一个接口 ()里面的参数是: Class<?>[] value() 【把 普通类 / 配置类 / ImportSelector 导入到spring的IOC容器中去,导入之后我们就可以直接从容器中拿】
特点:常用于第三方类、自动配置
java@Configuration @Import(User.class, OrderConfig.class) // 直接导入多个组件 public class AppConfig {}
3.@ImportResource()导入资源文件( 老xml配置文件 )
以前的xml配置文件的形式编写配置,但是此注解将其注入IOC容器
作用:把传统 xml 配置文件加载进 Spring
**场景:**兼容老项目、遗留 XML
java@Configuration @ImportResource("classpath:spring-context.xml") public class XmlConfig {}
4.@ConditionalOnBean() 条件装配
作用: 容器里有某个 Bean 时,才创建当前 Bean
java@Configuration public class ConditionConfig { @Bean @ConditionalOnBean(User.class) // 有User才注册Order public Order order() { return new Order(); } }
5.前四个注解大致总结:
@Import / @ImportResource / @Conditional 系列注解
都属于「配置类专用注解」,必须标注在 @Configuration 类上才会生效!
6.@ConfigurationProperties
作用:配置绑定 [自动把 application.yml / application.properties 里的配置项批量注入到 JavaBean\Java 类的属性中。]
java/** * * 简单粗暴理解:把当前类丢到Ioc容器中去 */ @Component //表明当前类是一个组件 {只有在容器中的组件,才会拥有SpringBoot提供的强大功能,加注解放入容器} 不代理 @ConfigurationProperties(prefix = "car1") // 这个car1就是properties中配置的car对象的键值对,如:car1.brand=BYD car1.price=123 // 这样car需要的属性会和properties中的自动对应 @Data // 没有手Setter 方法,@ConfigurationProperties 无法把配置文件里的值注入到成员变量中。 public class Car { private String brand; private Integer price; }
ConfigurationProperties的底层逻辑是通过 Java 反射调用 Setter 方法 (如setBrand)来完成赋值的。如果类中只有私有属性而没有对应的 Setter,Car对象中属性值null
注意 :@ConfigurationProperties(prefix = "test.test1") 若找不到prefix前缀时,对象Car创建时bean名称改为:test.test1-cn.stylefeng.guns.tenant.modular.Cat #
- 类所在的包在
@SpringBootApplication的扫描路径下,否则@Component不会生效。
7.@Confinguration @Commponent 不同
用
@Component:如果这个类只是一个纯粹的 POJO 数据载体,或者仅仅是用来承载配置属性,不涉及复杂的 Bean 组装 。每次调用都会 new 一个新对象 ,单例直接失效!用
@Configuration:如果你打算在这个类里定义多个有依赖关系的@Bean(例如Bean A的创建需要调用Bean B的方法),则必须使用它 。是配置类 ,Spring 会创建CGLIB 代理 ,保证 **@Bean 方法互相调用时,永远返回同一个单例 Bean**。
不要迷惑单例原因?从 Spring 容器中获取已创建好的 Bean
java@Configuration public class MyConfig { @Bean public User user() { return new User(); } @Bean public Order order() { // 无论调用多少次 user(),永远返回同一个Bean return new Order(user()); } }
8.@EnableConfigurationProperties
与@Configuration,@Component等配合使用\~!
理解: 当多个bean被@ConfigurationProperties()注解修饰,不愿再每个加@Component时,此注解就牛了
作用: 适合导入第三方配置,[第三方的配置没有用自动装配的相关注解,即没丢到IOC容器中,开发人员无法使用!此注解解决!!!]1、开启属性配置绑定功能( 如:例子中的绑定Car类的属性 )使 @ConfigurationProperties 注解生效!
2、把组件自动注册到容器中( 如:例子中的Car,用了这个注解就可以把Car丢到Ioc容器中去 )
注意:@EnableConfigurationProperties 本身作用1.使@ConfigurationProperties修饰的po类与XXX.properties动态绑定2.将po类交由IOC容器管理!!!
但是要明白:@EnableConfigurationProperties 本身的类的前提是在IOC容器中,不然Spring如何读取。故【@Configuration 常用 @Component也可以但不多,@Service等也可以,很少用罢了,违背spring的设计语义】
java@Configuration @EnableConfigurationProperties(Car.class) public class MyConfig { } //===========另一个Car则是 //@Component 此注解不再需要 @ConfigurationProperties(prefix = "car1") @Data public class Car { private String brand; private Integer price; }
java@EnableConfigurationProperties(ThreadPoolConfigProperties.class) @Configuration public class ThreadPoolConfig { @Bean public ThreadPoolExecutor threadPoolExecutor(ThreadPoolConfigProperties pool) { ThreadFactory oneFactory = new ThreadFactoryBuilder().setNameFormat("one-pool-%d").build(); return new ThreadPoolExecutor( pool.getCoreSize(), pool.getMaxSize(), pool.getKeepAliveTime(), TimeUnit.SECONDS, new LinkedBlockingDeque<>(1000), oneFactory , new ThreadPoolExecutor.AbortPolicy() ); } }类MyConfig中无@Bean注解,代理完全用不上,可改为@Component
总结: @EnableConfigurationProperties 只关心所在的类是不是一个 Spring Bean(在 IOC 里)。但是呢 类似ThreadPoolConfig 内部有@Bean注解,会有调用出现。所以不可改为@Component 。
9.ImportSelector
Spring 中用于 动态、条件化导入配置类 的核心接口
Spring Boot 自动配置(@EnableAutoConfiguration) 的底层灵魂
与 @
Import 对比
- 静态
@Import(XXX.class):编译时就写死要导入的类。ImportSelector:运行时 通过代码逻辑(判断环境、配置、类存在与否)动态决定导入哪些类。
javapackage org.springframework.context.annotation; public interface ImportSelector { /** * 核心方法:返回需要导入的类的**全限定名数组** * @param importingClassMetadata 被@Import标注的类的所有注解信息(可读取注解属性) * @return 要导入的@Configuration类/组件的全类名数组 */ String[] selectImports(AnnotationMetadata importingClassMetadata); }执行时机(非常重要)
selectImports方法在 Spring 容器启动、Bean 实例化之前 被调用。
- 时机:早于
@Bean、@ComponentScan- 作用:决定哪些类会被注册成 BeanDefinition ,进而实例化为 Bean
- 当 Spring 解析到
@Import(YourSelector.class)注解时,会立即 实例化YourSelector并调用其selectImports()方法。- 特点 :它的执行优先级非常高,发生在处理当前配置类中的
@Bean、@ImportResource等其他注解之前 。因此,在selectImports()方法中,你无法感知到由@Bean定义的 Bean。
DeferredImportSelector\后执行 接口方式 【主要用于动态获取bean @Import注解场景】
两者区别:
| 特性 | ImportSelector | DeferredImportSelector |
|---|---|---|
| 执行时机 | 立即执行,在解析配置类时触发。 | 延迟执行,在所有配置类处理完毕后触发。 |
| 感知范围 | 无法感知 @Bean 定义的 Bean。 |
可以感知所有已加载的 Bean 定义和配置。 |
| 主要用途 | 根据简单条件(如注解属性)动态导入配置。 | 根据完整的上下文信息(如某个 Bean 是否存在)进行更复杂的动态导入。 |
| 排序支持 | 不支持。 | 支持通过 @Order 或 Ordered 接口排序。 |
java
3.1.//定义实现类实现接口ImportSelector
public class MyImportSelector implements DeferredImportSelector {
@Override public String[] selectImports(AnnotationMetadata annotationMetadata) {
return new String[]{MyConfiguration1.class.getName(), MyConfiguration2.class.getName(),
MyConfiguration3.class.getName()};
}
}//注意:String[] selectImports 方法内可以根据开发者逻辑返回不同类的全限定名!!
3.2//在配置类上导入实现类
@Configuration
@Import(MyImportSelector.class)
public class MyConfiguration {
@Bean
public Object test() {
log.info("MyConfiguration create a object bean...");
return new Object();
}
}
3.3配置类样式
@Slf4j
public class MyConfiguration3 {
public MyConfiguration3() {
log.info("MyConfiguration3 construct...");
}
public void execute() {
log.info("MyConfiguration3 execute...");
}
}
3.4 调用
@SpringBootApplication
public class XXApplication{
public static void main(String[] args) {
ConfigurableApplicationContext ctx = SpringApplication.run(App.class, args);
// 直接获取动态导入的Bean
MyConfiguration3 myService = ctx.getBean(MyConfiguration3.class);
myService.execute();
}
}
- MyConfiguration create a object bean...
- MyConfiguration1 construct...
- MyConfiguration2 construct...
- MyConfiguration3 construct...
- MyConfiguration3 execute...
原因:Spring 首先解析
MyConfiguration类。虽然发现了@Import,但因为它是DeferredImportSelector,Spring 会先搁置它,转而去注册当前类中定义的test()Bean。因此,@Bean方法会最先执行。
类似场景:ImportBeanDefinitionRegistrar 接口方式 [定义注册器]
#扩展:AnnotationMetadata这个类 [做一个分布式任务调度的框架,如果结合springboot,那么在做一些自定义配置的时候就好很多]
1.定义注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(MyHttpDeferredImportSelector.class)
public @interface MyHttp {
String name() default "";
String value() default "";
}
2.注解实现类
@Slf4j
public class MyHttpDeferredImportSelector implements DeferredImportSelector {
@Override public String[] selectImports(AnnotationMetadata importingClassMetadata) {
importingClassMetadata.getAllAnnotationAttributes(MyHttp.class.getName(),true)
.forEach((k,v) -> {
log.info(importingClassMetadata.getClassName());
log.info("k:{},v:{}",k,String.valueOf(v));
});
return new String[0];
}//结果 MyConfiguration1全类名, name:myc1, MyConfiguration1全类名, value:myc1-value
}
3.使用注解
@Slf4j
@MyHttp(name = "myc1",value = "myc1-value")
public class MyConfiguration1 {
public MyConfiguration1() {
log.info("MyConfiguration1 construct...");
}
public void execute() {
log.info("MyConfiguration1 execute...");
}
}