前言
本文主要讲解SpringBoot的自动装配特性,这个特性使得第三方应用很容易就集成到SpringBoot中,这也是为什么使用SpringBoot的时候,只需要引入依赖,稍微修改配置,就能迅速使用这些第三方应用进行开发的原因。
创建自己的自动装配
假设我们正在开发一个多节点切换的库,这个库是作为第三方依赖提供给当前项目使用的。
新建一个Master主节点配置类,代码如下:
package geek.springboot.configuration;
import lombok.extern.slf4j.Slf4j;
import javax.annotation.PostConstruct;
/**
* 主节点配置类
*
* @author Bruse
*/
@Slf4j
public class MasterNodeAutoConfiguration {
@PostConstruct
public void init() {
// 在该类的实例初始化时打印
log.info("MasterNodeAutoConfiguration init");
}
}
注意,这里MasterNodeAutoConfiguration全限定类名是
geek.springboot.configuration.MasterNodeAutoConfiguration
, SpringApplication启动类的全限定类名是geek.springboot.application.Application
,SpringApplication启动时,默认只会扫描当前所在的包及其子包,所以这里相当于模拟了一个第三方库,因为第三方库使用的包名类路径不可能和你的项目一样。
这个时候启动SpringApplication,MasterNodeAutoConfiguration其实不会被SpringApplication扫描到的,还需要告诉SpringBoot这是个自动装配的类。
spring.factories 自动装配
在项目resources目录下新建META-INF/spring.factories
文件,并添加以下代码:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=geek.springboot.configuration.MasterNodeAutoConfiguration
MasterNodeAutoConfiguration加上@AutoConfiguration注解,表示它是一个配置类。
@Slf4j
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
启动SpringApplication,可以看到MasterNodeAutoConfiguration已被初始化
AutoConfiguration.imports 自动装配
除了使用spring.factories进行自动装配,还可以在resources目录下新建META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件,并把MasterNodeAutoConfiguration的全限定类名写在该文件中,代码如下:
geek.springboot.configuration.MasterNodeAutoConfiguration
如果有多个需要自动装配的类,直接换行,再补充上相关类名即可,像这样:
geek.springboot.configuration.MasterNodeAutoConfiguration
geek.springboot.configuration.MasterNodeAutoConfiguration
@ImportAutoConfiguration
还可以在SpringApplication启动类加上@ImportAutoConfiguration注解,让SpringBoot在启动的时候加载MasterNodeAutoConfiguration,代码如下:
@Slf4j
@SpringBootApplication
@@ImportAutoConfiguration(MasterNodeAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
注意:用@Import也可以让SpringBoot在启动时加载自定义的Configuration,但是这样无法使用@AutoConfigureOrder、@Order指定加载优先级
使用@Import注解进行MasterConfiguration加载,代码如下:
@Slf4j
@SpringBootApplication
@Import(MasterNodeAutoConfiguration.class)
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
自动装配的顺序
如果想要让自己的配置类按特定顺序初始化,那么可以使用相关注解进行调整。
@AutoConfigureAfter 和 @AutoConfigureBefore
新建一个SlaveNodeAutoConfiguration,代码如下:
package geek.springboot.configuration;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.autoconfigure.AutoConfiguration;
import javax.annotation.PostConstruct;
/**
* 从节点配置类
*
* @author Bruse
*/
@Slf4j
@AutoConfiguration
public class SlaveNodeAutoConfiguration {
@PostConstruct
public void init() {
log.info("SlaveNodeAutoConfiguration init");
}
}
spring.factories也给加上:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=geek.springboot.configuration.MasterNodeAutoConfiguration,
geek.springboot.configuration.SlaveNodeAutoConfiguration
最后给MasterNodeAutoConfiguration加上@AutoConfigureAfter,要求MasterNodeAutoConfiguration在SlaveNodeAutoConfiguration之后才被加载:
@Slf4j
@AutoConfigureAfter(SlaveNodeAutoConfiguration.class)
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
除了用@AutoConfigureAfter外,还可以配置@AutoConfiguration的after属性
,比如:
@AutoConfiguration(after = SlaveNodeAutoConfiguration.class)
public class MasterNodeAutoConfiguration {
......
}
启动SpringApplication,输出如下,说明顺序调整成功:
除了@AutoConfigureAfter,相关的注解还有@AutoConfigureBefore,这里就不做演示了。
注意:@AutoConfigureAfter和@AutoConfigureBefore的顺序调整只能作用在同样加上@AutoConfiguration注解的Bean之间,举个例子,如果当前的@AutoConfigureBefore注解配置的目标类,是一个@Service, 那么当前的Configuration初始化也并不会在Service之前。
@AutoConfigureOrder
除了上边提到的两个注解外,还可以使用@AutoConfigureOrder注解进行自动配置的顺序。它和@Order注解的作用是一样的,只不过它是专门为自动装配的类而用的。
SlaveNodeAutoConfiguration稍作调整,代码如下:
@Slf4j
@AutoConfigureOrder(10) // 自动装配优先级,数值越小越优先
@AutoConfiguration
public class SlaveNodeAutoConfiguration {
......
}
MasterNodeAutoConfiguration稍作调整,代码如下:
@Slf4j
@AutoConfigureOrder(20) // 自动装配优先级,数值越小越优先
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
启动SpringAppcaliton,控制台输出如下,SlaveConfiguration在MasterConfiguration之前被初始化:
特定条件下的灵活自动装配
有时候我们会需要在特定条件下灵活决定SpringBoot是否加载我们的Configuration,那么Spring也提供了相关注解。
@ConditionalOnClass
该注解表示当前项目存在指定的类时,才实例化修饰了该注解的Java Bean。
MasterConfiguration加上该注解,并要求SpringApplicaiton启动后能在classpath中找到WebService类时,才初始化MasterConfiguration :
@Slf4j
// 当前项目classpath存在WebService才初始化该Configuration
@ConditionalOnClass(name = {"geek.springboot.application.service.WebService"})
@AutoConfigureOrder(20)
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
因为当前项目还没新建WebService这个类,所以启动SpringApplication后,会发现MasterConfiguration没有被初始化,仅仅只有SlaveConfiguration初始化了。
@ConditionalOnMissingClass
该注解则刚好相反,反而是表示当前项目不存在指定的类时,才实例化修饰了该注解的Java Bean。
MasterConfiguration稍作调整:
@Slf4j
// 当前项目classpath不存在WebService才初始化该Configuration
@ConditionalOnMissingClass(value = {"geek.springboot.application.service.WebService"})
@AutoConfigureOrder(20)
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
启动SpringApplication,可以看到即便项目没有WebService这个类,MasterConfiguration也被初始化了。
@ConditionalOnBean 和 @ConditionalOnMissingBean
这两个注解作用其实跟上边提到的两个注解相似,只不过@ConditionalOnMissingClass 和 @ConditionalOnClass 关注的是是否存在某些类,而这两个注解关注的是当前Spring IOC容器中是否存在某些Bean。这里就不再做具体演示了。
@ConditionalOnProperty
该注解表示当前项目存在某个配置,且值符合条件时,才初始化Configuration。
MasterNodeAutoConfiguration再次稍作调整:
@Slf4j
// 需要存在node.open配置,且该配置的值为true时,才初始化
@ConditionalOnProperty(name = "node.open", havingValue = "true")
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
启动SpringApplication,会看到因为项目还没加上node.open这个配置,所以MasterConfiguration没有初始化:
往application.yml添加配置:
node:
open: true
启动SpringApplication,这次MasterNodeAutoConfiguration被初始化了。
@ConditionalOnResource
该注解表示当存在某个资源文件时,才初始化Configuration。
倒霉的MasterNodeAutoConfiguration再次做调整:
@Slf4j
// 表示当前classpath下存在node.yml才初始化,也可以是别的路径
@ConditionalOnResource(resources = {"classpath:node.yml"})
@AutoConfiguration
public class MasterNodeAutoConfiguration {
......
}
那么在resources目录下不存在node.yml的情况下,MasterConfiguration不会被初始化,反之如果resources目录下存在node.yml,MasterConfiguration则会被初始化。这里就不再贴图了,不妨自己动手尝试一下。
@ConditionalOnWebApplication 和 @ConditionalOnNotWebApplication
@ConditionalOnWebApplication注解表示只有当前SpirngApplication是一个Web应用程序时,才初始化该注解修饰的Jave Bean。@ConditionalOnNotWebApplication则相反,表示当前SpringApplication不是作为一个Web应用运行时,才初始化Java Bean。
@ConditionalOnExpression
该注解则比较灵活,允许你用SpEL表达式的方式定义什么条件下才初始化Java Bean。
结尾
本文章源自《Learn SpringBoot》专栏,感兴趣的话还请关注点赞收藏.
上一篇文章:《SpringBoot核心特性——异步任务和定时任务那些事》