当前位置: 首页> 文旅> 美景 > spring装配

spring装配

时间:2025/7/31 8:18:54来源:https://blog.csdn.net/u011627218/article/details/139057115 浏览次数:0次
一、自动装配
1.1、单个Bean
(1)Person类
public class Person {private String name;private Integer age;
}

        (2)配置类

@Component
public class SingleConfiguration {@Beanpublic 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 = newAnnotationConfigApplicationContext(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 {@Resourceprivate 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);}@Beanpublic 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 {@Beanpublic 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
官网文档地址:Spring Boot Reference Documentation
You almost always want to include one or more @Conditional annotations on your auto-configuration class. The @ConditionalOnMissingBean annotation is one common example that is used to allow developers to override auto-configuration if they are not happy with your defaults.
Spring Boot includes a number of @Conditional annotations that you can reuse in your own code by annotating @Configuration classes or individual @Bean methods. These annotations include:
  • Class Conditions
  • Bean Conditions
  • Property Conditions
  • Resource Conditions
  • Web Application Conditions
  • SpEL Expression Conditions
(1)ConditionalOnClass:当且仅当ClassPath存在指定的Class时,才创建标记上该注解的类的实例;相反ConditionalOnMissingBean是指如果没有找到目标Class, 那么就装载该类
(2)ConditionalOnBean: 当且仅当指定的bean classes and/or bean names在当前容器中,才创建
标记上该注解的类的实例;相反ConditionalOnMissingBean是指如果没有Class, 那么就装载该Bean
(3)ConditionalOnProperty:当且仅当Application.properties存在指定的配置项时,创建标记上
了该注解的类的实例
(4)ConditionalOnResource:在classpath下存在指定的resource时创建
(5)ConditionalOnWebApplication:在web环境下创建
    下面举例条件装配:
1)、采用1.3.4.3自动装配基础上,三方jar新增类ThirdRedisConnection
/*
*    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 {@Beanpublic ThirdRedisService thirdRedisService(){return  new ThirdRedisService();}@Beanpublic 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 {@Beanpublic ThirdRedisService thirdRedisService(){return  new ThirdRedisService();}@ConditionalOnRedis@Beanpublic 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
在条件装配中,虽然application.properties配置文件增加redis配置,但是依赖三方Redis连接属性无法从配置中注入,即使添加@Value也无法注入,如何实现在配置文件中配置对应值
3.1、starter三方项目pom添加mave依赖        
<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 {@Resourceprivate RedisProperties redisProperties;public void connect(){System.out.println("http://"+redisProperties.getHost()+":"+redisProperties.getPort());}}

    3)、starter三方Redis连接实现类通过容器获取配置类,修改RedisAutoConfiguration配置类将RedisProperties注入到Spring容器中

@Configuration
public class RedisAutoConfiguration {@Beanpublic ThirdRedisService thirdRedisService(){return  new ThirdRedisService();}@ConditionalOnRedis@Beanpublic ThirdRedisConnection thirdRedisConnection(){return new ThirdRedisConnection();}@Beanpublic RedisProperties redisProperties(){return new RedisProperties();}
}

    或者通过注解@EnableConfigurationProperties(RedisProperties.class)

@Configuration
@EnableConfigurationProperties(RedisProperties.class)
public class RedisAutoConfiguration {@Beanpublic ThirdRedisService thirdRedisService(){return  new ThirdRedisService();}@ConditionalOnRedis@Beanpublic 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)@Beanpublic 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() {@Overrideprotected 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{@Overridepublic void connection() {log.info("mysql connect");}
}@Slf4j
public class SqlServerDateSource extendsAbstractDbConnetion{@Overridepublic 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 {@Resourceprivate DateSourceFace dateSourceFace;// 执行初始化逻辑@Overridepublic void run(String... args) throws Exception {dateSourceFace.connection();}
}

关键字:spring装配

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com

责任编辑: