SpringBoot-按照条件去装配对象
先给出例子:根据配置项的编码格式来配置不同的编码转换器:
1,创建一个编码格式转换器接口
package com.boot.condition.bootconditionconfig.converter;
/**
* 编码格式的转换接口
*/
public interface EncodingConverter {
}
2,创建两个转换器的实现类UTF8,和GBK的:
package com.boot.condition.bootconditionconfig.converter;
public class GBKEncodingConverter implements EncodingConverter {
}
package com.boot.condition.bootconditionconfig.converter;
public class UTF8EncodingConverter implements EncodingConverter {
}
3,把上面的两个转换器的实现类通过配置类的方式来装载到spring容器中去:在装配EncodingConverterConfig这个对象的同时把Bean对象的实例装配进去。
package com.boot.condition.bootconditionconfig.config;
import com.boot.condition.bootconditionconfig.condition.GBKCondition;
import com.boot.condition.bootconditionconfig.converter.EncodingConverter;
import com.boot.condition.bootconditionconfig.converter.GBKEncodingConverter;
import com.boot.condition.bootconditionconfig.converter.UTF8EncodingConverter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@SpringBootConfiguration
public class EncodingConverterConfig {
@Bean
public EncodingConverter createGbkEncodingConverter(){
return new GBKEncodingConverter();
}
@Bean
public EncodingConverter createUtf8EncodingConverter(){
return new UTF8EncodingConverter();
}
}
4,在主函数里面拿出接口下面的所有的在spring容器中的实例:
package com.boot.condition.bootconditionconfig;
import com.boot.condition.bootconditionconfig.converter.EncodingConverter;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import java.util.Map;
@SpringBootApplication
public class BootConditionConfigApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context =
SpringApplication.run(BootConditionConfigApplication.class, args);
Map<String, EncodingConverter> beansOfType = context.getBeansOfType(EncodingConverter.class);
System.out.println(beansOfType);
context.close();
}
}
运行的结果如下:这时就把两个bean的实例装配到spring容器中了。
提问:在一次的运行当中不可能用到两个转换器的过程,比如:我想要GBK的就用GBK的转换器来转换,如果想用UTF-8的就用UTF-8的转换器来转换。
按照上面的做法是做不到的,如何去通过配置去装配要装配的对象呢?
这时需要用@Conditional的注解和Condition的接口:
来看下@Conditional的注解:
这个注解就是一个普通的注解:就是一个条件:里面有一个Class类型的
value的属性,要求的范围必须是Condition的子接口,实现或者继承这个接口的子类。才能够成为Conditioanl注解对应的操作类:
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Conditional {
Class<? extends Condition>[] value();
实际上Condition是一个接口:里面有一个抽象方法的声明。一个参数是ConditionContext类型的参数,一个是AnnotatedTypeMetadata:获取注解上的元数据的信息。里面的方法:matches,当方法的返回值为true的时候就会把bean对象装配到spring容器中去。当返回false的时候就不会装配进去。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotatedTypeMetadata;
@FunctionalInterface
public interface Condition {
boolean matches(ConditionContext var1, AnnotatedTypeMetadata var2);
}
我们可以在ConditionContext里面可以拿很多的东西:1,可以拿到当前注册的bean对象。2,工厂对象。3,环境。4,资源装载器。5,类加载器。
package org.springframework.context.annotation;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
import org.springframework.lang.Nullable;
public interface ConditionContext {
BeanDefinitionRegistry getRegistry();
@Nullable
ConfigurableListableBeanFactory getBeanFactory();
Environment getEnvironment();
ResourceLoader getResourceLoader();
@Nullable
ClassLoader getClassLoader();
}
在AnnotatedTypeMetadata里面可以获取到:就是获取类上面注解所对应的信息都可以拿出来,这块的信息可以在@Enable*的工作原理会写到
package org.springframework.core.type;
import java.util.Map;
import org.springframework.lang.Nullable;
import org.springframework.util.MultiValueMap;
public interface AnnotatedTypeMetadata {
boolean isAnnotated(String var1);
@Nullable
Map<String, Object> getAnnotationAttributes(String var1);
@Nullable
Map<String, Object> getAnnotationAttributes(String var1, boolean var2);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String var1);
@Nullable
MultiValueMap<String, Object> getAllAnnotationAttributes(String var1, boolean var2);
}
5:解决办法如何按照条件的方式去装配我们的对象呢?可以通过运行时的参数来更改file.encoding的编码格式从而选择不同的编码格式的转换器:在运行的过程中通过下面的方式去适配。
package com.boot.condition.bootconditionconfig.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class GBKCondition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String encoding = System.getProperty("file.encoding");
if("gbk".equals(encoding.toLowerCase())){
return true;
}
return false;
}
}
package com.boot.condition.bootconditionconfig.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class UTF8Condition implements Condition {
@Override
public boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {
String encoding = System.getProperty("file.encoding");
if("utf-8".equals(encoding.toLowerCase())){
return true;
}
return false;
}
}
在装配对象的时候需要加上@Conditional的注解:这样就可以按照条件装配我们的对象:当装配下面的对象的时候会回调condition接口中的方法。
package com.boot.condition.bootconditionconfig.config;
import com.boot.condition.bootconditionconfig.condition.GBKCondition;
import com.boot.condition.bootconditionconfig.condition.UTF8Condition;
import com.boot.condition.bootconditionconfig.converter.EncodingConverter;
import com.boot.condition.bootconditionconfig.converter.GBKEncodingConverter;
import com.boot.condition.bootconditionconfig.converter.UTF8EncodingConverter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@SpringBootConfiguration
public class EncodingConverterConfig {
@Bean
@Conditional(GBKCondition.class)
public EncodingConverter createGbkEncodingConverter(){
return new GBKEncodingConverter();
}
@Bean
@Conditional(UTF8Condition.class)
public EncodingConverter createUtf8EncodingConverter(){
return new UTF8EncodingConverter();
}
}
改变下运行时的参数如下:
运行的结果如下:如果把运行时的参数改为utf-8的就只会装配utf-8的转换器。
这个@Conditonal的注解可以用到方法的上面,也可以用到类上面。
代码如下:
package com.boot.condition.bootconditionconfig.config;
import com.boot.condition.bootconditionconfig.condition.GBKCondition;
import com.boot.condition.bootconditionconfig.condition.UTF8Condition;
import com.boot.condition.bootconditionconfig.converter.EncodingConverter;
import com.boot.condition.bootconditionconfig.converter.GBKEncodingConverter;
import com.boot.condition.bootconditionconfig.converter.UTF8EncodingConverter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@SpringBootConfiguration
@Conditional(GBKCondition.class)
public class EncodingConverterConfig {
@Bean
public EncodingConverter createGbkEncodingConverter(){
return new GBKEncodingConverter();
}
@Bean
public EncodingConverter createUtf8EncodingConverter(){
return new UTF8EncodingConverter();
}
}
运行结果如下:这时两个对象都会装配进去。这个注解标在类上了,如果返回的结果为flase的话,相当于一个对象都不会装配进去,如果为true的话,这两个对象都会装配进去。
@Conditional的注解里面可以配置多个,是与的关系,如果同时为true的话,才会装配进去:代码如下:
package com.boot.condition.bootconditionconfig.config;
import com.boot.condition.bootconditionconfig.condition.GBKCondition;
import com.boot.condition.bootconditionconfig.condition.UTF8Condition;
import com.boot.condition.bootconditionconfig.converter.EncodingConverter;
import com.boot.condition.bootconditionconfig.converter.GBKEncodingConverter;
import com.boot.condition.bootconditionconfig.converter.UTF8EncodingConverter;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
@SpringBootConfiguration
@Conditional({GBKCondition.class,UTF8Condition.class})
public class EncodingConverterConfig {
@Bean
public EncodingConverter createGbkEncodingConverter(){
return new GBKEncodingConverter();
}
@Bean
public EncodingConverter createUtf8EncodingConverter(){
return new UTF8EncodingConverter();
}
}
运行的结果如下:
总结:使用按照条件装配的过程:
- @Conditional [value的值必须事实现Condition接口的类]
- 根据接口中的matches方法的返回值确定事否装配bean对象到spring容器中:false不装配,true就会装配到spring容器中去。
在springBoot里面提供这样的注解很多:比如@ConditionalOnClass:(如果在classpath下存在这个文件的话,这个时候也会被装载)
@ConditionalOnBean:(只要在spring容器中存在了这个bea类的话就被装载到spring容器中去)等等