@Conditional условной сборки Spring Framework
предисловие
Те, кто знаком со SpringBoot, знакомы с условными аннотациями.В проекте SpringBoot условные аннотации широко используются, и многие аннотации, производные от условий, расширены. Хотя Conditional был значительно расширен в SpringBoot, он был предложен в Spring Framework 4.0, поэтому эта статья по-прежнему основана на Spring Framework.
Рекомендуемое чтение черные глаза の личный блог Регистрация компонента FactoryBean of Spring Framework @Import регистрации компонентов Spring Framework @Component регистрации компонентов Spring Framework
@Условное описание
нужно использовать@Conditional
Обратите внимание, вы должны сначала понятьConditiona
интерфейс, связанный с@Conditional
Используя аннотации вместе, мы также можем видеть из исходного кода, что они используют@Conditional
В аннотации должна быть указана реализацияConditiona
Класс интерфейса.
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
/**
* All {@link Condition Conditions} that must {@linkplain Condition#matches match}
* in order for the component to be registered.
*/
Class<? extends Condition>[] value();
}
существуетConditiona
В интерфейсе определен только один методmatches
, когда Spring регистрирует компонент, он также основывается на возвращаемом значении этого методаTRUE/FALSE
решить, регистрировать ли компонент в контейнере spring
@FunctionalInterface
public interface Condition {
/**
* Determine if the condition matches.
* @param context 条件判断的上下文环境
* @param metadata 正在检查的类或方法的注解元数据
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
существуетmatches
в мы можем получитьConditionContext
Интерфейс, по этому объекту интерфейса можно получитьBeanDefinitionRegistry
,ConfigurableListableBeanFactory
и другая важная информация об объекте, в соответствии с этими объектами, вся информация, содержащаяся в инициализации контейнера Spring, может быть получена и проверена, и в сочетании с бизнес-требованиями может быть реализовано пользовательское суждение о состоянии во время регистрации компонента.
@Условное использование
Сначала определите два обычных класса JavaBean
@Data
public class Test {
private String id = "@Bean";
}
@Data
public class Test2 {
private String id = "@Conditional";
}
настроив класс и@Bean
Аннотация для регистрации компонентов в контейнере Spring
/**
* spring组件配置类
*/
@Configuration
public class TestConfiguration {
/**
* 向spring容器中注册Test 类型下beanName为test的组件
*/
@Bean
public Test test() {
return new Test();
}
/**
* 根据TestCondition接口的条件判断向spring容器中注册Test2组件
*/
@Bean
@Conditional(TestCondition.class)
public Test2 test2() {
return new Test2();
}
}
пользовательская реализацияCondition
интерфейс
public class TestCondition implements Condition {
/**
* 当IOC容器中包含 Test类的bean定义信息时,条件成立
*
* @param context 条件判断的上下文环境
* @param metadata 正在检查的类或方法的元数据
* @return 条件是否成立
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] testBeanNames = beanFactory.getBeanNamesForType(Test.class);
return ArrayUtils.isNotEmpty(testBeanNames);
}
}
Добавить класс начальной загрузки контейнера Spring
/**
* spring 容器启动引导类,测试 @Conditional 功能
*/
@ComponentScan("com.spring.study.ioc.condition")
public class TestConditionalBootstrap {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext =
new AnnotationConfigApplicationContext(TestConditionalBootstrap.class);
String[] names = applicationContext.getBeanNamesForType(Test.class);
System.out.println("---Test bean names : " + Arrays.asList(names));
names = applicationContext.getBeanNamesForType(Test2.class);
System.out.println("---Test2 bean names : " + Arrays.asList(names));
applicationContext.close();
}
}
Запустите весенний загрузочный класс, и консоль выведет результат:
---hasTestBean : true ---Test bean names : [test] ---Test2 bean names : [test2]
Из результатов видно, что Тест нормально регистрируется в пружинном контейнере, что удовлетворяетTestCondition
Условия интерфейса, все Test2 также зарегистрированы в контейнере spring, для дальнейшей проверки результатов мы удаляем компонент Test, оставляем только регистрацию Test2, модифицируем класс конфигурации следующим образом
/**
* spring组件配置类,将Test组件删除掉
*/
@Configuration
public class TestConfiguration {
/**
* 根据TestCondition接口的条件判断向spring容器中注册Test2组件
*/
@Bean
@Conditional(TestCondition.class)
public Test2 test2() {
return new Test2();
}
}
Перезапустите весенний загрузочный класс, и консоль выведет результат следующим образом:
---hasTestBean : false ---Test bean names : [] ---Test2 bean names : []
Видно, что когда класс Test не зарегистрирован в контейнере spring, он не удовлетворяетсяTestCondition
Условия интерфейса, поэтому компонент Test2 не будет зарегистрирован в контейнере Spring. На данный момент, если test2() зарегистрирован в компоненте@Conditional
Когда компонент удален, компонент Test2 будет нормально зарегистрирован в контейнере Spring.
В приведенном выше примере@Conditional
Аннотация добавляется к методу. В настоящее время условие действует только для текущего метода.@Conditional
Аннотации также могут быть добавлены к类
Выше условие справедливо для регистрации компонента во всем классе. В соответствии с приведенным выше случаем выполните следующие настройки:
-
TestCondition
необходимо реализоватьConfigurationCondition
Интерфейс для обработки классов конфигурации
Когда аннотация @Conditional добавляется в класс конфигурации, следует отметить, что условие в интерфейсе Condition предназначено для управления самим классом конфигурации или всеми компонентами в классе конфигурации, поэтому Spring Framework предоставляет интерфейс ConfigurationCondition и использует значение перечисления чтобы позволить нам пользовательский выбор.
enum ConfigurationPhase {
/**
* Condition接口中的条件控制着配置类本身的注册,当条件不匹配时,不会添加@configuration类
*/
PARSE_CONFIGURATION,
/**
* 控制Condition接口中的条件是对配置类中的组件进行解析,不会影响配置类本身的注册
*/
REGISTER_BEAN
}
-
TestCondition
Реализация интерфейса изменена следующим образом
public class TestCondition implements ConfigurationCondition {
/**
* 当IOC容器中包含 Test的bean定义信息时,条件成立
*
* @param context 条件判断的上下文环境
* @param metadata 正在检查的类或方法的元数据
* @return 条件是否成立
*/
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
String[] testBeanNames = beanFactory.getBeanNamesForType(Test.class);
boolean hasTestBean = ArrayUtils.isNotEmpty(testBeanNames);
System.out.println("---hasTestBean : " + hasTestBean);
return hasTestBean;
}
public ConfigurationPhase getConfigurationPhase() {
return ConfigurationPhase.REGISTER_BEAN;
}
}
- Внесите изменения в классы javaBean и классы конфигурации.
@Data
@Component // 通过@Component注解直接注册 Test 组件
public class Test {
private String id = "@Bean";
}
@Data
public class Test2 {
private String id = "Test2: @Conditional";
}
@Data
public class Test3 {
private String id = "Test3: @Conditional";
}
- Класс конфигурации настраивается следующим образом, добавьте класс конфигурации
@Conditional
аннотация
/**
* spring组件配置类,根据TestCondition接口的条件判断向spring容器中注册Test2,Test3组件
*/
@Configuration
@Conditional(TestCondition.class)
public class TestConfiguration {
@Bean
public Test3 test3() {
return new Test3();
}
@Bean
public Test2 test2() {
return new Test2();
}
}
- Добавить пару в класс начальной загрузки
Test3
тип запроса
String[] names = applicationContext.getBeanNamesForType(Test3.class);
System.out.println("---Test3 bean names : " + Arrays.asList(names));
Запустите класс начальной загрузки, и консоль выведет результат следующим образом:
---hasTestBean : true ---Test bean names : [test] ---Test2 bean names : [test2] ---Test3 bean names : [test3]
Отсюда видно, что когдаTestCondition
При совпадении условий интерфейса Test2 и Test3 регистрируются в контейнере Spring, если компонент Test не зарегистрирован, мы увидим следующие результаты.
- Буду
Test
сорт@Component
Комментарий удаляется, а остальной код остается без изменений
@Data
public class Test {
private String id = "@Bean";
}
Перезапустите класс начальной загрузки и распечатайте результат следующим образом:
---hasTestBean : false ---Test bean names : [] ---Test2 bean names : [] ---Test3 bean names : []
Отсюда видно, чтоTestCondition
Условие управляет регистрацией компонента в классе конфигурации
Примечания по использованию @Conditional
- когда
@Conditional
Когда аннотация добавляется к методу, ее можно использовать напрямую.Condition
Интерфейс реализован, и условие соответствует, чтобы определить, может ли компонент быть зарегистрирован в контейнере Spring. - когда
@Conditional
Когда аннотация добавляется в класс конфигурации, вам нужно использоватьConfigurationCondition
Интерфейс реализованConfigurationPhase
Настроен в соответствии с влиянием самого класса, зарегистрированного с указанными критериями. потому чтоCondition
Состояние интерфейса выполняется в процессе весеннего сканирования компонентов-кандидатов, поэтому нужно обратить внимание на эту проблему при оценке состояния по бину. Если это индивидуальное суждение о бизнес-требованиях, это не повлияет на него.
Суммировать
Когда мы обычно используем Spring или Spring MVC, использование аннотации @Conditional может быть немногочисленным, но Spring Boot сейчас популярен, и Spring Boot значительно расширил @Conditional, поэтому понимание использования и принципа @Conditional также является важным. хорошая идея Базовое изучение Spring Boot делает больше предзнаменований. В этой статье рассказывается об использовании @Conditional, но не объясняется подробно принцип Condition, который будет объяснен в последующем процессе сканирования компонентов Spring.
Учиться никогда не бывает легко, вы можете запутаться, вы можете быть ленивым, но вы никогда не сможете перестать двигаться вперед.
Если вы не наберете несколько шагов, вы не сможете пройти тысячу миль, если вы не наберете небольшой ручей, вы не сможете создать реку или море;