SpringBoot从入门到精通(二)

SpringBoot自动配置-Condition

Condition是Spring4.0后引入的条件化配置接口,通过实现Condition接口可以完成有条件的加载相应的Bean

 一:@Conditional要配和Condition的实现类(ClassCondition)进行使用

1.创建:ClassCondition类实现Condition接口

public class ClassCondition implements Condition {
/**
*
* @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
* @param metadata 注解元对象。 可以用于获取注解定义的属性值
* @return
*/
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //1.需求: 导入Jedis坐标后创建Bean
        //思路:判断redis.clients.jedis.Jedis.class文件是否存在
        boolean flag = true;
        try {
            Class<?> cls = Class.forName("redis.clients.jedis.Jedis");
            } catch (ClassNotFoundException e) {
            flag = false;
            }
        return flag;
    }
}

2.创建 UserConfig 配置类,在返回User对象的方法上添加 @Conditional ( ClassCondition . class )注解
@Configuration
public class UserConfig {
    @Bean
    @Conditional(ClassCondition.class)
    public User user(){
    return new User();
    }
}

3.测试

@SpringBootApplication
public class SpringbootConditionApplication {
    public static void main(String[] args) {
    //启动SpringBoot的应用,返回Spring的IOC容器
    ConfigurableApplicationContext context =
    SpringApplication.run(SpringbootConditionApplication.class, args);
    Object user = context.getBean("user");
    System.out.println(user);
    }
}

通过@Conditional条件注解,当我们引入redis的maven坐标时候,创建了User的bean

二:在上面我们发现  redis.clients.jedis.Jedis的坐标是写死的,那有什么办法根据不同的坐标动态的创建bean吗?

在SpringBoot中为实现动态创建bean, 常用条件注解:
ConditionalOnProperty :判断配置文件中是否有对应属性和值才初始化 Bean
ConditionalOnClass :判断环境中是否有对应字节码文件才初始化 Bean
ConditionalOnMissingBean :判断环境中没有对应 Bean 才初始化 Bean

我们自定义来实现一下@ConditionalOnClass这个注解

1.自定义注解@ConditionalOnClass

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(ClassCondition.class)
public @interface ConditionOnClass {
    String[] value();
}

2.创建UserConfig 配置类

@Configuration
public class UserConfig {
    @Bean
    //@Conditional(ClassCondition.class)
    @ConditionOnClass("com.alibaba.fastjson.JSON")
        public User user(){
        return new User();
    }
    @Bean
    @ConditionalOnProperty(name = "itcast",havingValue = "itheima")
        public User user2(){
        return new User();
    }
}

3.修改ClassCondition 类

public class ClassCondition implements Condition {
    /**
     *
     * @param context 上下文对象。用于获取环境,IOC容器,ClassLoader对象
     * @param metadata 注解元对象。 可以用于获取注解定义的属性值
     * @return
     */
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {

        //2.需求: 导入通过注解属性值value指定坐标后创建Bean

        //获取注解属性值  value
        Map<String, Object> map = metadata.getAnnotationAttributes(ConditionOnClass.class.getName());
        //System.out.println(map);
        String[] value = (String[]) map.get("value");

        boolean flag = true;
        try {
            for (String className : value) {
                Class<?> cls = Class.forName(className);
            }
        } catch (ClassNotFoundException e) {
            flag = false;
        }
        return flag;

    }
}

4.测试

@SpringBootApplication
public class SpringbootConditionApplication {
    public static void main(String[] args) {
    //启动SpringBoot的应用,返回Spring的IOC容器
    ConfigurableApplicationContext context =
    SpringApplication.run(SpringbootConditionApplication.class, args);
    Object user = context.getBean("user");
    System.out.println(user);
    }
}

通过添加@ConditionalOnClass,并在@ConditionalOnClass的参数里面填写,回传到ClassCondition里面,完成导入坐标动态创建bean

三:上面的两个案例中,我们学会了@Conditional条件注解,以及创建自定义注解@ConditionalOnClass来实现动态创建Userbean,Springboot中redis实际是怎么创建redisTemplate的?

其实springboot为我们提供了很多condition的依赖,他们都放到了包:org.springframework.boot:spring-boot-autoconfigure:x.x.x
我们可以在此包中找到condition包:

在这里插入图片描述

 我们再次找到data包中redis包,我们可以看到RedisAutoConfiguration 类,在里面可以看到创建redisTemplate的bean。

在这里插入图片描述

在里面我们可以看到引入了@ConditionalOnClass(RedisOperations.class),标识必须存在字节码文件RedisOperations.class,并且容器中不存在redisTemplate的时候。才能创建redisTemplate 。这和我们动态创建User的bean是一个套路

四:切换springboot内置web服务器

SpringBoot的web环境中默认使用tomcat作为内置服务器,其实SpringBoot提供了4中内置服务器供我们选择,我们可以很方便的进行切换。

我们在依赖包:org.springframework.boot.autoconfigure 中找到web 包,里面有个embedded 包。
在这里插入图片描述

  •  JettyWebServerFactoryCustomizer :用来配置jetty服务器
  • NettyWebServerFactoryCustomizer:用来配置netty服务器
  • TomcatWebServerFactoryCustomizer:用来配置tomcat服务器
  • UndertowWebServerFactoryCustomizer:用来配置Undertow服务器
  • EmbeddedWebServerFactoryCustomizerAutoConfiguration:用来根据条件配置不通的服务器。

我们来看一下EmbeddedWebServerFactoryCustomizerAutoConfiguration 类:
在这里插入图片描述

  •  我们可以看到在EmbeddedWebServerFactoryCustomizerAutoConfiguration 类中有一个注解:@ConditionalOnWebApplication 标识当项目中配置了web环境的时候才能让这个类起作用。
  • 我们可以看到配置TomcatWebServerFactoryCustomizerConfiguration 的条件为 @ConditionalOnClass({ Tomcat.class, UpgradeProtocol.class }) ,表示项目中必须存在tomcat.class 和UpgradeProtocol.class,才能去实例化tomcat服务。
  • 其他几个web 服务配置条件可以自行查询。
     

在这里插入图片描述

为此,比如我们想使用jettyweb服务器替代tomcat服务器的话,可以直接在项目中排除tomcat依赖,并引入jetty依赖。

在springboot项目中,默认使用的是tomcat服务器,是嵌入在依赖spring-boot-starter-web 中的,我们只需要排除tomcat依赖,并导入jetty依赖即可。

 

 

pom 文件中的排除依赖效果

 

 最后再引入jetty

 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值