一、Condition
Condition是spring4.0增加的条件判断功能,通过这个功能可以实现选择性的创建Bean操作。
案例:需求
在 Spring 的 IOC 容器中有一个 User 的 Bean,现要求:
- 导入Jedis坐标后,加载该Bean,没导入,则不加载。
- 将类的判断定义为动态的。判断哪个字节码文件存在可以动态指定
要求1.代码实现
在config目录下创建UserConfig,实现注入方法代码:
package com.example.springbootcondition.config; import com.example.springbootcondition.condition.ClassConditon; import com.example.springbootcondition.entity.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean @Conditional(ClassConditon.class) //引入类返回true则注入该方法,如果返回false则不注入该方法 public User user(){ return new User(); } }
package com.example.springbootcondition.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; public class ClassConditon implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { boolean ret=true; try { Class<?> aClass = Class.forName("redis.clients.jedis.Jedis"); } catch (ClassNotFoundException e) { ret=false; } return ret; } }
在启动类调用user方法,同时测试maven引入Jedis和不引入Jedis返回结果
package com.example.springbootcondition; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class SpringbootConditionApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootConditionApplication.class, args); Object user = run.getBean("user"); System.out.println(user); } }
要求2.代码实现
创建动态引入的注解
package com.example.springbootcondition.condition; import org.springframework.context.annotation.Conditional; import java.lang.annotation.*; @Target({ElementType.TYPE, ElementType.METHOD}) //定义注解可以加载哪个范围上,ElementType.TYPE:类;ElementType.METHOD:方法 @Retention(RetentionPolicy.RUNTIME) //标识主键生效时期 @Documented //表示生成java duc的文档 @Conditional(ClassConditon.class) public @interface ConditonOnClass { String[] value(); }
UserConfig引入自己定义的注解,传入要判断的包
package com.example.springbootcondition.config; import com.example.springbootcondition.condition.ConditonOnClass; import com.example.springbootcondition.entity.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean @ConditonOnClass("redis.clients.jedis.Jedis") public User user(){ return new User(); } }
更改matches方法为判读自定义属性值
package com.example.springbootcondition.condition; import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map; public class ClassConditon implements Condition { /** * * @param context 上下文对象,用于获取环境、IOC容器、ClassLoader对象 * @param metadata 注解的源对象,可以获取注解的定义属性值 * @return */ @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { //需求2,导入通过注解属性值value指定坐标后创建Bean //思路:判断redis.clients.jedis.Jedis.class是否存在 Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(ConditonOnClass.class.getName()); System.out.println(annotationAttributes); String[] value = (String[]) annotationAttributes.get("value"); boolean ret=true; try { for (String className : value) { Class<?> aClass = Class.forName(className); } } catch (ClassNotFoundException e) { ret=false; } return ret; } }
在启动类调用user方法,同时测试maven引入Jedis和不引入Jedis返回结果
package com.example.springbootcondition; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; @SpringBootApplication public class SpringbootConditionApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootConditionApplication.class, args); Object user = run.getBean("user"); System.out.println(user); } }
Condition – 小结
- 自定义条件:
- 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回
boolean值 。 matches 方法两个参数:
• context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
• metadata:元数据对象,用于获取注解属性。 - 判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解。
- 定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回
- SpringBoot 提供的常用条件注解:
- ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
- ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
二、切换内置web服务器
SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可以很方便的进行切换。
在pom文件下加入
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <!--排除tomcat 依赖--> <exclusions> <exclusion> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> </exclusion> </exclusions> </dependency> <!--引入jetty 依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jetty</artifactId> </dependency>
三、@Enable*注解
SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用@Import注解导入一些配置类,实现Bean的动态加载。
SpringBoot 工程是否可以直接获取jar包中定义的Bean?
componentscan扫描范围:当前引导类所在包及其子包*
1.使用@Componentscan扫描com.ailuti.config包
2.可以使用@Import注解,加载类。这些类都会被spring创建,并放入Ioc容器
3.可以对Import注解进行封装。
代码实现
创建springboot-enble工程和springboot-enble-other工程,在springboot-enble-other工程里创建User和UserConfig。
package com.example.springbootenbleoter.config; import com.example.springbootenbleoter.entity.User; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class UserConfig { @Bean public User user(){ return new User(); } }
在springboot-enble的pom文件里引入springboot-enble-other工程
<dependency> <groupId>com.example</groupId> <artifactId>springboot-enble-oter</artifactId> <version>0.0.1-SNAPSHOT</version> </dependency>
在springboot-enble的启动类测试调用User Bean的几种方式
package com.example.springbootenble; import com.example.springbootenbleoter.config.EnableUser; import com.example.springbootenbleoter.config.UserConfig; import com.example.springbootenbleoter.entity.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Import; @SpringBootApplication //@ComponentScan("com.example.springbootenbleoter") //@Import(UserConfig.class) //@EnableUser public class SpringbootEnbleApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnbleApplication.class, args); Object user = run.getBean("user"); System.out.println(user); } }
其中EnableUser注解需要在springboot-enble-other工程里创建
package com.example.springbootenbleoter.config; import org.springframework.context.annotation.Import; import java.lang.annotation.*; @Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented //复制Import里引的注解 @Import(UserConfig.class) public @interface EnableUser { }
四、@Import注解
@Enable*底层依赖于@Import注解导入一些类,使用@Import导入的类会被Spring加载到IOC容器中。而@Import提供4中用法:
① 导入Bean
package com.example.springbootenble; import com.example.springbootenbleoter.entity.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Import; import java.util.Map; //第一种:直接导入Bean @SpringBootApplication @Import(User.class) public class SpringbootEnbleApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnbleApplication.class, args); User user = run.getBean(User.class); System.out.println(user); Map<String, User> beansOfType = run.getBeansOfType(User.class); System.out.println(beansOfType); } }
② 导入配置类
package com.example.springbootenble; import com.example.springbootenbleoter.config.UserConfig; import com.example.springbootenbleoter.entity.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Import; import java.util.Map; //第二种:导入配置类 @SpringBootApplication @Import(UserConfig.class) public class SpringbootEnbleApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnbleApplication.class, args); User user = run.getBean(User.class); System.out.println(user); Map<String, User> beansOfType = run.getBeansOfType(User.class); System.out.println(beansOfType); } }
③ 导入 ImportSelector 实现类。一般用于加载配置文件中的类
先创建一个MyImportSelector类,集成ImportSelector并实现selectImports方法
package com.example.springbootenbleoter.config; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; public class MyImportSelector implements ImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { return new String[]{"com.example.springbootenbleoter.entity.User"}; } }
然后导入 ImportSelector 实现类
package com.example.springbootenble; import com.example.springbootenbleoter.config.MyImportSelector; import com.example.springbootenbleoter.config.UserConfig; import com.example.springbootenbleoter.entity.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Import; import java.util.Map; //第三种:导入 ImportSelector 实现类 @SpringBootApplication @Import(MyImportSelector.class) public class SpringbootEnbleApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnbleApplication.class, args); User user = run.getBean(User.class); System.out.println(user); Map<String, User> beansOfType = run.getBeansOfType(User.class); System.out.println(beansOfType); } }
④ 导入 ImportBeanDefinitionRegistrar 实现类。
创建MyImportBeanDefinitionRegistrar集成ImportBeanDefinitionRegistrar并实现registerBeanDefinitions方法
package com.example.springbootenbleoter.config; import com.example.springbootenbleoter.entity.User; import org.springframework.beans.factory.support.AbstractBeanDefinition; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.annotation.ImportBeanDefinitionRegistrar; import org.springframework.core.type.AnnotationMetadata; public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar{ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(User.class).getBeanDefinition(); registry.registerBeanDefinition("user",beanDefinition); } }
然后导入ImportBeanDefinitionRegistrar实现类后测试
package com.example.springbootenble; import com.example.springbootenbleoter.config.MyImportBeanDefinitionRegistrar; import com.example.springbootenbleoter.config.MyImportSelector; import com.example.springbootenbleoter.config.UserConfig; import com.example.springbootenbleoter.entity.User; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.annotation.Import; import java.util.Map; //第四种:导入 ImportBeanDefinitionRegistrar 实现类 @SpringBootApplication @Import(MyImportBeanDefinitionRegistrar.class) public class SpringbootEnbleApplication { public static void main(String[] args) { ConfigurableApplicationContext run = SpringApplication.run(SpringbootEnbleApplication.class, args); User user = run.getBean(User.class); System.out.println(user); Map<String, User> beansOfType = run.getBeansOfType(User.class); System.out.println(beansOfType); } }
五、EnableAutoConfiguration 注解
- @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class)来加载配置类
- 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化Bean
- 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
文章评论