【spring-boot】spring-boot学习之ImportSelector接口
一、ImportSelector说明
ImportSelector接口是至spring中导入外部配置的核心接口,在SpringBoot的自动化配置和@EnableXXX(功能性注解)都有它的存在,关于SpringBoot的分析可以参考:https://www.cnblogs.com/niechen/p/9027804.html
其主要作用是收集需要导入的配置类,如果该接口的实现类同时实现EnvironmentAware, BeanFactoryAware ,BeanClassLoaderAware或者ResourceLoaderAware,那么在调用其selectImports方法之前先调用上述接口中对应的方法,如果需要在所有的@Configuration处理完在导入时可以实现DeferredImportSelector接口。
在这里我举个Spring中的实例来看一下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Import(TransactionManagementConfigurationSelector.class) public @interface EnableTransactionManagement { /** * Indicate whether subclass-based (CGLIB) proxies are to be created ({@code true}) as * opposed to standard Java interface-based proxies ({@code false}). The default is * {@code false}. <strong>Applicable only if {@link #mode()} is set to * {@link AdviceMode#PROXY}</strong>. * <p>Note that setting this attribute to {@code true} will affect <em>all</em> * Spring-managed beans requiring proxying, not just those marked with * {@code @Transactional}. For example, other beans marked with Spring's * {@code @Async} annotation will be upgraded to subclass proxying at the same * time. This approach has no negative impact in practice unless one is explicitly * expecting one type of proxy vs another, e.g. in tests. */ boolean proxyTargetClass() default false; /** * Indicate how transactional advice should be applied. The default is * {@link AdviceMode#PROXY}. * @see AdviceMode */ AdviceMode mode() default AdviceMode.PROXY; /** * Indicate the ordering of the execution of the transaction advisor * when multiple advices are applied at a specific joinpoint. * The default is {@link Ordered#LOWEST_PRECEDENCE}. */ int order() default Ordered.LOWEST_PRECEDENCE; }
此注解是开启声明式事务的注解,那么它的@Import所导入的类为TransactionManagementConfigurationSelector,那么我们看一下其类图:
由此可知该类实现类ImportSelector接口
一、ImportSelector案例
目标:自定义一个自动装配注解,向IOC容器中注入指定的bean
1、自动装配的注解类
package com.boot.annotations; import com.boot.annotations.importselector.GlobalConfigImport; import org.springframework.context.annotation.Import; import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; /** * */ @Retention(RetentionPolicy.RUNTIME) @Documented @Target(ElementType.TYPE) @Import(GlobalConfigImport.class) public @interface EnableSpringImport { }
2、自动装配的注解使用的导入类,即ImportSelector接口的实现类
package com.boot.annotations.importselector; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import lombok.extern.slf4j.Slf4j; /** * */ @Slf4j public class GlobalConfigImport implements ImportSelector, BeanFactoryAware { private BeanFactory beanFactory; @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { for(String a:importingClassMetadata.getAnnotationTypes()){ log.info("包含的注解有:{}",a); } return new String[]{GlobalConfigAutoSetting.class.getName()}; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
3、需要导入的指定bean以及导入该bean的配置算法类。
package com.boot.annotations.importselector; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import lombok.extern.slf4j.Slf4j; /** * */ @Slf4j public class GlobalConfigImport implements ImportSelector, BeanFactoryAware { private BeanFactory beanFactory; /** * 包含的注解有:org.springframework.context.annotation.PropertySource * 包含的注解有:org.mybatis.spring.annotation.MapperScan * 包含的注解有:org.springframework.boot.autoconfigure.SpringBootApplication * 包含的注解有:com.boot.annotations.EnableSpringImport * @param importingClassMetadata * @return */ @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { for(String a:importingClassMetadata.getAnnotationTypes()){ log.info("包含的注解有:{}",a); } return new String[]{GlobalConfigAutoSetting.class.getName()}; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
4、spring-boot项目的启动类
package com.spring.sxf.study.springtradeweb; import com.boot.annotations.EnableSpringImport; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.annotation.PropertySource; @PropertySource(value = "classpath:env.properties", encoding="utf-8") @MapperScan("com.spring.sxf.study.springtradedao.mapper") @SpringBootApplication(scanBasePackages="com.spring.sxf") @EnableSpringImport public class SpringTradeWebApplication { public static void main(String[] args) { SpringApplication.run(SpringTradeWebApplication.class, args); } }
5、spring项目启动后,验证指定bean是否被导入spring容器的测试类
@Slf4j @Controller @RequestMapping("/blog") public class BlogController implements ApplicationContextAware { @Value("${env.envDesc}") private String envDesc; private ApplicationContext applicationContext; @Autowired private BlogService blogService; @RequestMapping("/env") @ResponseBody public String testEnv() { log.info("current envDesc:{}", envDesc); boolean globalConfig= applicationContext.containsBean("globalConfig"); if(!globalConfig){ log.info("spring context not have GlobalConfig"); }else{ GlobalConfig globalConfig1=applicationContext.getBean(GlobalConfig.class); log.info("spring context hasGlobalConfig key:{} ,value:{}",globalConfig1.getKey(),globalConfig1.getValue()); } return envDesc; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext=applicationContext; } }