三. 自定义Starter 深入理解
自定义的Starter 能够起作用,条件注解起了很大的作用.
演示一下条件注解的使用.
仍然使用 刚才的自定义 Starter
三.一 pom.xml 中添加依赖
<dependencies> <!--添加自动配置的依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-autoconfigure</artifactId> <version>2.2.2.RELEASE</version> </dependency> <!--添加配置文件引用的依赖信息--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <version>2.2.2.RELEASE</version> <optional>true</optional> </dependency> <!--添加 spring-context依赖, 对应的 2.2.2 版本--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.2.RELEASE</version> </dependency> </dependencies>
三.二 定义一个接口和两个实现类
定义 Animal 接口
package top.yueshushu.condition; /** * @ClassName:Animal * @Description 定义一个动物的接口 * @Author zk_yjl * @Date 2021/10/25 20:48 * @Version 1.0 * @Since 1.0 **/ public interface Animal { /** * 定义一个声音的接口 * @date 2021/10/25 20:49 * @author zk_yjl * @param * @return void */ public void voice(); }
有两个实现类, 一个是 Cat 一个是 Dog
Cat.java
package top.yueshushu.condition; /** * @ClassName:Cat * @Description TODO * @Author zk_yjl * @Date 2021/10/25 20:50 * @Version 1.0 * @Since 1.0 **/ public class Cat implements Animal { @Override public void voice() { System.out.println(">>>喵喵喵"); } }
Dog.java
package top.yueshushu.condition; /** * @ClassName:Dog * @Description 狗的实现类 * @Author zk_yjl * @Date 2021/10/25 20:49 * @Version 1.0 * @Since 1.0 **/ public class Dog implements Animal { @Override public void voice() { System.out.println(">>>>汪汪汪"); } }
三.三 定义实现类起作用的条件
Animal 接口有两个实现类 Cat, Dog 在使用中,要使用哪一个呢?
总不能
@Resource("cat") private Animal animal
@Resource("dog") private Animal animal
这样肯定是不行的。
可以定义条件, 当Cat 的条件满足时,就用 Cat 的实现类, 当 Dog 的条件满足时,就用 Dog 的实现类
三.三.一 Cat 实现条件 CatCondition
package top.yueshushu.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; /** * @ClassName:CatCondition * @Description TODO * @Author zk_yjl * @Date 2021/10/25 20:53 * @Version 1.0 * @Since 1.0 **/ // 实现 Condition 接口 public class CatCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { String animal = context.getEnvironment().getProperty("animal"); //为空属性,不创建 if(StringUtils.isEmpty(animal)){ return false; } return "Cat".equalsIgnoreCase(animal); } }
三.三.二 Dog 实现条件 DogCondition
package top.yueshushu.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.util.StringUtils; /** * @ClassName:DogCondition * @Description TODO * @Author zk_yjl * @Date 2021/10/25 20:51 * @Version 1.0 * @Since 1.0 **/ public class DogCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //没有获取到这个属性时,走 Dog String animal = context.getEnvironment().getProperty("animal"); if(StringUtils.isEmpty(animal)){ return true; } return "Dog".equalsIgnoreCase(animal); } }
三.四 让条件被管控
现在有 Cat 的条件,也有 Dog 的条件。
但这两个条件,只是实现了 Condition 接口, 并没有添加形如 @Component @Configuration 等类似的注解
说明,这两个条件,还没有办法被SpringBoot 管控到.
JavaAnimalConfig 管控条件
package top.yueshushu.condition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; /** * @ClassName:JavaAnimalConfig * @Description TODO * @Author zk_yjl * @Date 2021/10/25 20:54 * @Version 1.0 * @Since 1.0 **/ @Configuration public class JavaAnimalConfig { /** 条件符合时,创建 */ @Bean("animal") @Conditional(DogCondition.class) Animal dog(){ return new Dog(); } /** 条件符合时,创建 */ @Bean("animal") @Conditional(CatCondition.class) Animal cat(){ return new Cat(); } }
两者创建的,bean 都是 animal
三.五 测试
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //ctx.getEnvironment().getSystemProperties().put("animal", "Cat"); // ctx.getEnvironment().getSystemProperties().put("animal", "Dog"); // ctx.getEnvironment().getSystemProperties().put("animal", "YJL"); ctx.register(JavaAnimalConfig.class); ctx.refresh(); Animal animal = (Animal) ctx.getBean("animal"); animal.voice(); } }
设置环境变量值的信息, 都去掉, 会生成默认的 Dog
当使用 Cat 时, 会打印 Cat 的实现类
当使用 Dog 时,会打印 Dog 的实现类
当使用 YJL 时, 因为都不符合,所以创建不了相应的 Bean
也可以将这个自定义的Starter clean install 安装后, StarterApply 引用
@RunWith(SpringRunner.class) @SpringBootTest public class StarterTest { @Autowired private UserPropertiesService userPropertiesService; @Test public void starterPrintlnTest(){ String message = userPropertiesService.println(); System.out.println("Apply项目输出信息:"+message); } // 运行测试一下 @Test public void animalJavaTes(){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.getEnvironment().getSystemProperties().put("animal", "Cat"); ctx.register(JavaAnimalConfig.class); ctx.refresh(); Animal animal = (Animal) ctx.getBean("animal"); animal.voice(); } }
发现,也是相同的测试结果.
三.六 Profile 多环境配置
在多环境配置时,我们使用到了 Profile
在 JavaAnimalConfig 里面,
public class JavaAnimalConfig { /** 条件符合时,创建 */ @Bean("animal") @Conditional(DogCondition.class) Animal dog(){ return new Dog(); } /** 条件符合时,创建 */ @Bean("animal") @Conditional(CatCondition.class) Animal cat(){ return new Cat(); } }
我们定义了 DogCondition, 能不能将这个换成 Profile 呢? 这样就不用根据环境变量的值来确定实例化哪一个实现了,而是根据环境配置来实现化具体的实现.
@Configuration public class JavaAnimalConfig { /** 条件符合时,创建 */ @Bean("animal") // @Conditional(DogCondition.class) 不使用 @Profile("Dog") //触发条件 Animal dog(){ return new Dog(); } /** 条件符合时,创建 */ @Bean("animal") //@Conditional(CatCondition.class) @Profile("Cat") //触发条件 Animal cat(){ return new Cat(); } }
进行测试
public class Main { public static void main(String[] args) { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //ctx.getEnvironment().getSystemProperties().put("animal", "Cat"); // ctx.getEnvironment().getSystemProperties().put("animal", "Dog"); // ctx.getEnvironment().getSystemProperties().put("animal", "YJL"); // ctx.getEnvironment().setActiveProfiles("Dog"); //ctx.getEnvironment().setActiveProfiles("Cat"); // ctx.getEnvironment().setActiveProfiles("YJL"); ctx.register(JavaAnimalConfig.class); ctx.refresh(); Animal animal = (Animal) ctx.getBean("animal"); animal.voice(); } }
当启用 Dog 时, 实现的是 Dog
当启用的是 Cat 时,实现的是 Cat 实现
当启用的是 YJL 时,会报异常信息。与上面的测试结果一致.
也同样可以在 StarterTest 类中进行测试,
@Test public void animalJavaTes(){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); //ctx.getEnvironment().getSystemProperties().put("animal", "Cat"); // ctx.getEnvironment().getSystemProperties().put("animal", "Dog"); // ctx.getEnvironment().getSystemProperties().put("animal", "YJL"); ctx.getEnvironment().setActiveProfiles("Dog"); //ctx.getEnvironment().setActiveProfiles("Cat"); // ctx.getEnvironment().setActiveProfiles("YJL"); ctx.register(JavaAnimalConfig.class); ctx.refresh(); Animal animal = (Animal) ctx.getBean("animal"); animal.voice(); }
测试结果,也是一样的。
但如果将 setActiveProfiles 都去掉,在 application.yml 中进行设置
是不可以的。 还是会报错误