Spring-Boot--容器初始化动态加载(@Conditio

2018-05-19  本文已影响91人  Mr_1214

spring boot集成了jetty,tomcat,undertow等WEB容器,启动时可以智能的选择初始化哪个容器。接下来我们具体分析怎么实现智能选择。


spring boot容器的配置在org.springframework.boot.autoconfigure.web包下的EmbeddedServletContainerAutoConfiguration类,我们看下这个类的具体实现:

@Configuration
@ConditionalOnWebApplication
@Import(BeanPostProcessorsRegistrar.class)
public class EmbeddedServletContainerAutoConfiguration {

    /**
     * TOMCAT 容器实例bean配置信息
     */
    @Configuration
    @ConditionalOnClass({ Servlet.class, Tomcat.class })
    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedTomcat {

        @Bean
        public TomcatEmbeddedServletContainerFactory tomcatEmbeddedServletContainerFactory() {
            return new TomcatEmbeddedServletContainerFactory();
        }

    }

    /**
     * Jetty 容器实例bean配置信息
     */
    @Configuration
    @ConditionalOnClass({ Servlet.class, Server.class, Loader.class,
            WebAppContext.class })
    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedJetty {

        @Bean
        public JettyEmbeddedServletContainerFactory jettyEmbeddedServletContainerFactory() {
            return new JettyEmbeddedServletContainerFactory();
        }

    }

    /**
     *  Undertow 容器实例bean配置信息
     */
    @Configuration
    @ConditionalOnClass({ Servlet.class, Undertow.class, SslClientAuthMode.class })
    @ConditionalOnMissingBean(value = EmbeddedServletContainerFactory.class, search = SearchStrategy.CURRENT)
    public static class EmbeddedUndertow {

        @Bean
        public UndertowEmbeddedServletContainerFactory undertowEmbeddedServletContainerFactory() {
            return new UndertowEmbeddedServletContainerFactory();
        }

    }

源码中分别存在jetty,tomcat,undertow的配置信息,那么spring-boot初始化时,具体是加载哪个呢,我们看到每个配置上都有@ConditionalOnClass与@ConditionalOnMissingBean注解,这两个注解是扩展的Conditional注解,根据条件判断是否加载bean


自定义注解Conditional

在org.springframework.boot.autoconfigure.condition包下,我们可以看到spring boot扩展了很多新的Conditional注解,当然我们也可以定义自己的扩展注解,完成自己特定的需求,上一篇使用Conditional注解完成我们的初始化服务动态加载,接下来,改造以前代码自定义我们的扩展注解完成同样的功能。


  1. 自定义注解MeetingConditional
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional({OnMeetingConditional.class})
public @interface MeetingConditional {
    //当前注解需要传入env值,OnMeetingConditional类是Conditional注解的实际处理类,
    当前注解同时加上Conditional注解,标识需要Conditional处理
    String env() default "local";
}
  1. 定义Conditional注解的处理类OnMeetingConditional
public class OnMeetingConditional implements Condition {

   // 获取环境信息类的meeting_type会议类型    
    private final static String type = System.getProperty("meeting_type") == null ? "local" :System.getProperty("meeting_type");


    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        //获取要装载的bean上配置的环境信息
        List<String> envs = this.getEnvMultiValue(metadata, MeetingConditional.class);
        //对比装载bean与环境信息的配置信息是否相同,相同返回true标识装载当前bean的信息
        return envs.contains(type);
    }

    /**
     * 获取自定义注解上配置的环境值
     * @param metadata
     * @param annotationType
     * @return
     */
    private List<String> getEnvMultiValue(AnnotatedTypeMetadata metadata, Class<?> annotationType){
        MultiValueMap<String, Object> attributes = metadata.getAllAnnotationAttributes(annotationType.getName(), true);
        List<String> candidates = new ArrayList();
        if(attributes == null)
            return Collections.emptyList();
        candidates.addAll((List)attributes.get("evn"));
        return candidates;
    }
}
  1. 修改配置bean 加入自定义注解MeetingConditional
@Configuration
public class
MeetingInstanceConfig {

    //当前bean只有环境信息是local的时候装载
    @Bean
    @MeetingConditional(evn = "local")
    public IMeetingService localMeetingService() {
        return new LocalMeetingServiceImpl();
    }

    //当前bean只有环境信息是romte的时候装载
    @Bean
    @MeetingConditional(evn = "romte")
    public IMeetingService romteMeetingService() {
        return new RomteMeetingServiceImpl();
    }
}

下来测试:

运行结果:
this is Romte Meeting!

运行结果:
this is Local Meeting!

结语

通过代码模拟了根据环境信息动态装载实例,在这一过程对spring-boot通过条件注解动态加载web容器有了进一步的认知,同时可以根据业务的需要定制出我们自己的智能代码。

上一篇 下一篇

猜你喜欢

热点阅读