public class Person {
private String name;
private Integer age;
}
(2)配置类
@Component
public class SingleConfiguration {
@Bean
public Person person(){
Person person = new Person();
person.setName("SK");
person.setAge(18);
return person;
}
}
(3)测试
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new
AnnotationConfigApplicationContext(SingleConfiguration.class);
Person person = (Person) context.getBean("person");
System.out.println(person.getName()+"年龄"+person.getAge());
}
}
测试结果:
1.2、批量Bean
(1)多个实体类
@Service
public class OrderService {
}
@Service
public class PersonService {
}
(2)配置类
@Component
@ComponentScan("com.sk.cloud.spring.batch")
public class BatchConfiguration {
}
(3)测试
public class App {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BatchConfiguration.class);
for(String name:context.getBeanDefinitionNames()){
System.out.println(name);
}
}
}
测试结果:
1.3、三方Bean
(1)三方jar包:打成jar包
public class ThirdRedisConnection {
private String host = "127.0.0.1";
private String port = "61616";
public void connect(){
System.out.println("http://"+host+":"+port);
}
}
public class ThirdRedisService {
public void insert(){}
public void update(){}
public void delete(){}
}
(2)引入三方依赖
<dependency>
<groupId>org.third</groupId>
<artifactId>sk-third-spring-boot-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
(3)调用三方方法
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
}
@RestController
@RequestMapping("/sk")
public class Controller {
@Resource
private ThirdRedisService thirdRedisService;
@RequestMapping("/insert")
public void insert(){
thirdRedisService.insert();
}
}
启动报错:
(4)Sping管理
1.3.4.1、引用方配置类注入Bean
@SpringBootApplication
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
@Bean
public ThirdRedisService thirdRedisService () {
return new ThirdRedisService();
}
}
1.3.4.2、三方包里把需要的Bean交给IOC容器
<!--三方包增加依赖-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.24</version>
</dependency>
/*
* 三方增加配置类将Bean交给Spring管理
*/
@Configuration
public class RedisAutoConfiguration {
@Bean
public ThirdRedisService thirdRedisService(){
return new ThirdRedisService();
}
}
/*
* 调用方引入@Import三方配置类
*/
@SpringBootApplication
@Import(RedisAutoConfiguration.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
// @Bean
// public ThirdRedisService thirdRedisService () {
// return new ThirdRedisService();
// }
}
1.3.4.3、自动装配:如果引入很多三方jar都要手动引入很麻烦
1)、创建META-INF/spring.factories文件
2)、指定配置类
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.third.spring.RedisAutoConfiguration
3)、调用方直接依赖就可以使用
@SpringBootApplication
//@Import(RedisAutoConfiguration.class)
public class App {
public static void main(String[] args) {
SpringApplication.run(App.class,args);
}
// @Bean
// public ThirdRedisService thirdRedisService () {
// return new ThirdRedisService();
// }
}
spring boot原理:@SpringBootApplication->@EnableAutoConfiguration->@Import(AutoConfigurationImportSelect.class)->AutoConfigurationImportSelect#getAutoConfigurationEntry-
>getCandidateConfigurations
/*
* 1、AutoConfigurationImportSelect#getAutoConfigurationEntry
*/
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
/*
* 2、org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
*/
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
/*
* 3、org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
*/
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if (classLoader == null) {
classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();
}
String factoryTypeName = factoryType.getName();
return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
/*
* 4、org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
*/
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {
return result;
} else {
Map<String, List<String>> result = new HashMap();
try {
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
while(urls.hasMoreElements()) {
URL url = (URL)urls.nextElement();
UrlResource resource = new UrlResource(url);
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
Iterator var6 = properties.entrySet().iterator();
while(var6.hasNext()) {
Map.Entry<?, ?> entry = (Map.Entry)var6.next();
String factoryTypeName = ((String)entry.getKey()).trim();
String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());
String[] var10 = factoryImplementationNames;
int var11 = factoryImplementationNames.length;
for(int var12 = 0; var12 < var11; ++var12) {
String factoryImplementationName = var10[var12];
((List)result.computeIfAbsent(factoryTypeName, (key) -> {
return new ArrayList();
})).add(factoryImplementationName.trim());
}
}
}
result.replaceAll((factoryType, implementations) -> {
return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));
});
cache.put(classLoader, result);
return result;
} catch (IOException var14) {
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var14);
}
}
}
7.9.3. Condition Annotations
/*
* 1、sk-third-spring-boot-starter 新增ThirdRedisConnection类
*/
public class ThirdRedisConnection {
private String host = "127.0.0.1";
private String port = "61616";
public void connect(){
System.out.println("http://"+host+":"+port);
}
}
/*
* 2、RedisAutoConfiguration 新增ThirdRedisConnection类配置
*/
@Configuration
public class RedisAutoConfiguration {
@Bean
public ThirdRedisService thirdRedisService(){
return new ThirdRedisService();
}
@Bean
public ThirdRedisConnection thirdRedisConnection(){return new ThirdRedisConnection();}
}
2)、引用jar包新增调用方法
@Resource
private ThirdRedisConnection thirdRedisConnection;
@RequestMapping("/connect")
public void connect(){
thirdRedisConnection.connect();
}
此时调试请求/connnct方法,正常调用
2.1、@Conditional
2.1.1、新增Redis连接条件装配逻辑方法
/*
* 根据Application属性判断是否加载Redis连接类链接
* */
public class OnRedisCondition implements Condition {
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String host = context.getEnvironment().getProperty("redis.host");
String port = context.getEnvironment().getProperty("redis.port");
if(StringUtils.isEmpty(host)||StringUtils.isEmpty(port)){
return false;
}
return true;
}
}
2.1.2、新增Redis连接条件装配ConditionalOnRedis注解
@Conditional(OnRedisCondition.class)
@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE, ElementType.METHOD })
@Documented
public @interface ConditionalOnRedis {
}
2.1.2、重写RedisAutoConfiguration 新增ThirdRedisConnection类配置,为条件装配
@Configuration
public class RedisAutoConfiguration {
@Bean
public ThirdRedisService thirdRedisService(){
return new ThirdRedisService();
}
@ConditionalOnRedis
@Bean
public ThirdRedisConnection thirdRedisConnection(){return new ThirdRedisConnection();}
}
2.1.3、重新编译三方包,启动服务异常(因为不满足2.1.1、新增Redis连接条件装配逻辑方法)
Action:
Consider defining a bean of type 'org.third.spring.ThirdRedisConnection' in your configuration.
2.1.4、修改配置,满足Redis连接条件装配逻辑,启动服务正常
<!--application.properties-->
redis.host=172.17.1.150
redis.port=61616
三、属性装配
public class ThirdRedisConnection {
private String host = "127.0.0.1";
private String port = "61616";
public void connect(){
System.out.println("http://"+host+":"+port);
}
}
<!--application.properties-->
redis.host=172.17.1.150
redis.port=61616
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-autoconfigure</artifactId>
<version>2.7.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.7.6</version>
</dependency>
3.2、starter三方Redis连接实现类增加配置:增加ConfigurationProperties注解和SET方法
@ConfigurationProperties(prefix = "redis")
public class ThirdRedisConnection {
private String host = "127.0.0.1";
private String port = "61616";
public void setHost(String host) {
this.host = host;
}
public void setPort(String port) {
this.port = port;
}
public void connect(){
System.out.println("http://"+host+":"+port);
}
}
编译starter包,启动web执行结果如下:
3.3、如果把注入配置项从Redis连接实现类独立出来,解耦类功能
1)、新增配置类 剥离配置项
@ConfigurationProperties(prefix = "redis")
public class RedisProperties {
private String host = "127.0.0.1";
private String port = "61616";
public String getHost() {
return host;
}
public void setHost(String host) {
this.host = host;
}
public String getPort() {
return port;
}
public void setPort(String port) {
this.port = port;
}
}
2)、starter三方Redis连接实现类修改为从配置类获取配置信息
@ConfigurationProperties(prefix = "redis")
public class ThirdRedisConnection {
@Resource
private RedisProperties redisProperties;
public void connect(){
System.out.println("http://"+redisProperties.getHost()+":"+redisProperties.getPort());
}
}
3)、starter三方Redis连接实现类通过容器获取配置类,修改RedisAutoConfiguration配置类将RedisProperties注入到Spring容器中
@Configuration
public class RedisAutoConfiguration {
@Bean
public ThirdRedisService thirdRedisService(){return new ThirdRedisService();}
@ConditionalOnRedis
@Bean
public ThirdRedisConnection thirdRedisConnection(){return new ThirdRedisConnection();}
@Bean
public RedisProperties redisProperties(){return new RedisProperties();}
}
或者通过注解@EnableConfigurationProperties(RedisProperties.class)
@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {
@Bean
public ThirdRedisService thirdRedisService(){return new ThirdRedisService();}
@ConditionalOnRedis
@Bean
public ThirdRedisConnection thirdRedisConnection(){return new ThirdRedisConnection();}
}
三、条件装配扩展
3.1、@ConditionalOnBean注解
这里举个Dubbo例子在做自动装配时,先寻找BASE_PACKAGES_BEAN_NAME这个Bean是否存在,存在自动装载
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@Configuration
@AutoConfigureAfter(DubboRelaxedBindingAutoConfiguration.class)
@EnableConfigurationProperties(DubboConfigurationProperties.class)
@EnableDubboConfig
public class DubboAutoConfiguration {
/**
* Creates {@link ServiceAnnotationPostProcessor} Bean
* dubbo.scan.base-packages
* @param packagesToScan the packages to scan
* @return {@link ServiceAnnotationPostProcessor}
*/
@ConditionalOnProperty(prefix = DUBBO_SCAN_PREFIX, name = BASE_PACKAGES_PROPERTY_NAME)
// 先寻找BASE_PACKAGES_BEAN_NAME这个Bean是否存在, 存在serviceAnnotationBeanProcessor被Spring装载;否则不装载
@ConditionalOnBean(name = BASE_PACKAGES_BEAN_NAME)
@Bean
public ServiceAnnotationPostProcessor serviceAnnotationBeanProcessor(@Qualifier(BASE_PACKAGES_BEAN_NAME)
Set<String> packagesToScan) {
return new ServiceAnnotationPostProcessor(packagesToScan);
}
}
3.2、 @ConditionalOnMissingBean注解
/**
* Dubbo Relaxed Binding Auto-{@link Configuration} for Spring Boot 2.0
*
* @see DubboRelaxedBindingAutoConfiguration
* @since 2.7.0
*/
@Configuration
@ConditionalOnProperty(prefix = DUBBO_PREFIX, name = "enabled", matchIfMissing = true)
@ConditionalOnClass(name = "org.springframework.boot.context.properties.bind.Binder")
@AutoConfigureBefore(DubboRelaxedBindingAutoConfiguration.class)
public class DubboRelaxedBinding2AutoConfiguration {
public PropertyResolver dubboScanBasePackagesPropertyResolver(ConfigurableEnvironment environment) {
ConfigurableEnvironment propertyResolver = new AbstractEnvironment() {
@Override
protected void customizePropertySources(MutablePropertySources propertySources) {
Map<String, Object> dubboScanProperties = getSubProperties(environment.getPropertySources(), DUBBO_SCAN_PREFIX);
propertySources.addLast(new MapPropertySource("dubboScanProperties", dubboScanProperties));
}
};
ConfigurationPropertySources.attach(propertyResolver);
return propertyResolver;
}
/**
* The bean is used to scan the packages of Dubbo Service classes
* 如果没有就创建
* @param environment {@link Environment} instance
* @return non-null {@link Set}
* @since 2.7.8
*/
@ConditionalOnMissingBean(name = BASE_PACKAGES_BEAN_NAME)
@Bean(name = BASE_PACKAGES_BEAN_NAME)
public Set<String> dubboBasePackages(ConfigurableEnvironment environment) {
PropertyResolver propertyResolver = dubboScanBasePackagesPropertyResolver(environment);
return propertyResolver.getProperty(BASE_PACKAGES_PROPERTY_NAME, Set.class, emptySet());
}
@ConditionalOnMissingBean(name = RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME, value = ConfigurationBeanBinder.class)
@Bean(RELAXED_DUBBO_CONFIG_BINDER_BEAN_NAME)
@Scope(scopeName = SCOPE_PROTOTYPE)
public ConfigurationBeanBinder relaxedDubboConfigBinder() {
return new BinderDubboConfigBinder();
}
}
3.2、@ConditionalOnProperty注解
解析application.yml/application.properties 里的配置生成条件生效,与@Configuration注解一起使用
1)、场景1:根据数据库配置类型,加载对应数据库工具,如果条件没有匹配上的话Spring扫描bean是会自动跳过该配置类
@Configuration
@ConditionalOnClass(SQLServerDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(
name = {"spring.datasource.type"},
havingValue ="com.microsoft.sqlserver.jdbc.SQLServerDriver",
matchIfMissing = false
)
public class SqlServerDriver {
2)、场景2:启动Redis缓存,如果没有开启Redis,则不启用Redis配置类
@Configuration
@ConditionalOnClass(RedisAutoConfiguration.class)
@ConditionalOnProperty(
name = {"spring.boot.redis.switch"},
matchIfMissing = false
)
public class RedisAutoConfiguration {
如果设置matchIfMissing = true的话,默认配置类生效,如果application没有配置spring.boot.redis.switch或者设置true都生效,只有配置spring.boot.redis.switch=false才跳过加载此配置类
2)、场景3:使用策略模式,根据环境配置注册不同bean
//1、定义接口
public interface DateSourceFace {
void connection();
}
//2、定义策略抽象类
public abstract class AbstractDbConnetion implementsDateSourceFace{
}
//3、分别实现A 和 B类
@Slf4j
public class MysqlDateSource extendsAbstractDbConnetion{
@Override
public void connection() {
log.info("mysql connect");
}
}
@Slf4j
public class SqlServerDateSource extendsAbstractDbConnetion{
@Override
public void connection() {
log.info("selserver connect");
}
}
//4、配置类
@Configuration
public class DbConnectionConfig {
@Bean
@ConditionalOnProperty(name = "datasource.type",havingValue = "mysql")
public DateSourceFace mysqlDateSource(){
return new MysqlDateSource();
}
@Bean
@ConditionalOnProperty(name = "datasource.type",havingValue = "sqlserver")
public DateSourceFace sqlServerDateSource(){
return new SqlServerDateSource();
}
}
//5、配置文件
datasource:
type: mysql
//6、业务
@Slf4j
@Component
@ConfigurationProperties(prefix = "datasource")
public class InitConfig implements CommandLineRunner {
@Resource
private DateSourceFace dateSourceFace;
// 执行初始化逻辑
@Override
public void run(String... args) throws Exception {
dateSourceFace.connection();
}
}