spring boot之org.springframework.boot.context.TypeExcludeFilter

曾经碰到过这样一种情况,想让某个使用了spring 注解的类不被spring扫描注入到spring bean池中,比如下面的类使用了@Component和@ConfigurationProperties("example1.user")自动绑定属性,不想让这个类被注入。

 1 package com.github.torlight.sbex;
 2 
 3 import java.io.Serializable;
 4 
 5 import org.springframework.boot.context.properties.ConfigurationProperties;
 6 import org.springframework.stereotype.Component;
 7 
 8 @Component
 9 @ConfigurationProperties("example1.user")
10 public class User implements Serializable{
11     
12     private static final long serialVersionUID = 6913838730034509179L;
13 
14     private String name;
15     
16     private Integer age;
17 
18     public String getName() {
19         return name;
20     }
21 
22     public void setName(String name) {
23         this.name = name;
24     }
25 
26     public Integer getAge() {
27         return age;
28     }
29 
30     public void setAge(Integer age) {
31         this.age = age;
32     }
33     
34     
35 }

一开始不想使用BeanPostProcessor接口的实现类来实现这样的功能,想可以借助@SpringBootApplication注解,使用exclude来实现该功能。

 1 package com.github.torlight.sbex;
 2 
 3 import org.springframework.boot.SpringApplication;
 4 import org.springframework.boot.autoconfigure.SpringBootApplication;
 5 import org.springframework.context.ApplicationContext;
 6 import org.springframework.context.annotation.Bean;
 7 
 8 
 9 /**
10  * Hello world!
11  *
12  */
13 @SpringBootApplication(exclude={com.github.torlight.sbex.User.class})
14 public class Example1 {
15     
16 
17     @Bean
18     public UserAction userAction(){
19         return new UserAction();
20     }
21     
22     
23     public static void main( String[] args ) {
24        
25      ApplicationContext context= SpringApplication.run(Example1.class, args);
26      
27      ((UserAction)context.getBean(UserAction.class)).sysOutUserInfo();  
28        
29     }
30 }

运行程序后,控制台报如下错误:

2018-08-04 13:15:57.079 ERROR 4504 --- [           main] o.s.boot.SpringApplication               : Application startup failed

org.springframework.beans.factory.BeanDefinitionStoreException: Failed to process import candidates for configuration class [com.github.torlight.sbex.Example1]; nested exception is java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
    - com.github.torlight.sbex.User

    at org.springframework.context.annotation.ConfigurationClassParser.processDeferredImportSelectors(ConfigurationClassParser.java:556) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:185) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:308) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:228) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:270) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:93) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:687) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:525) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:122) ~[spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:693) [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:360) [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:303) [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1118) [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1107) [spring-boot-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at com.github.torlight.sbex.Example1.main(Example1.java:25) [classes/:na]
Caused by: java.lang.IllegalStateException: The following classes could not be excluded because they are not auto-configuration classes:
    - com.github.torlight.sbex.User

    at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.handleInvalidExcludes(AutoConfigurationImportSelector.java:193) ~[spring-boot-autoconfigure-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.checkExcludedClasses(AutoConfigurationImportSelector.java:178) ~[spring-boot-autoconfigure-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.boot.autoconfigure.AutoConfigurationImportSelector.selectImports(AutoConfigurationImportSelector.java:100) ~[spring-boot-autoconfigure-1.5.4.RELEASE.jar:1.5.4.RELEASE]
    at org.springframework.context.annotation.ConfigurationClassParser.processDeferredImportSelectors(ConfigurationClassParser.java:547) ~[spring-context-4.3.9.RELEASE.jar:4.3.9.RELEASE]
    ... 14 common frames omitted

错误提示不能使用exclude={com.github.torlight.sbex.User.class},因为该类并不是被spring boot自动装配的类,类似于RedisAutoConfiguration这样的类。仔细研究一番后,发现可以扩展org.springframework.boot.context.TypeExcludeFilter来实现这一功能。spring boot在执行扫描过程中,会使用TypeExcludeFilter进行过滤。

 1 public class TypeExcludeFilter implements TypeFilter, BeanFactoryAware {
 2 
 3     private BeanFactory beanFactory;
 4 
 5     @Override
 6     public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
 7         this.beanFactory = beanFactory;
 8     }
 9 
10     @Override
11     public boolean match(MetadataReader metadataReader,
12             MetadataReaderFactory metadataReaderFactory) throws IOException {
13         if (this.beanFactory instanceof ListableBeanFactory
14                 && getClass().equals(TypeExcludeFilter.class)) {
15             Collection<TypeExcludeFilter> delegates = ((ListableBeanFactory) this.beanFactory)
16                     .getBeansOfType(TypeExcludeFilter.class).values();
17             for (TypeExcludeFilter delegate : delegates) {
18                 if (delegate.match(metadataReader, metadataReaderFactory)) {
19                     return true;
20                 }
21             }
22         }
23         return false;
24     }
25 
26     @Override
27     public boolean equals(Object obj) {
28         throw new IllegalStateException(
29                 "TypeExcludeFilter " + getClass() + " has not implemented equals");
30     }
31 
32     @Override
33     public int hashCode() {
34         throw new IllegalStateException(
35                 "TypeExcludeFilter " + getClass() + " has not implemented hashCode");
36     }
37 
38 }

可以看出,spring boot会加载spring bean池中所有针对TypeExcludeFilter的扩展,并循环遍历这些扩展类调用其match方法。那么思路出来了,只要继承该类并重写match方法,在该方法内部进行相应的处理即可。示例代码如下:

 1 public class MyTypeExcludeFilter extends TypeExcludeFilter {
 2 
 3     @Override
 4     public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory)
 5             throws IOException {
 6         
 7         if("com.github.torlight.sbex.User".equals(metadataReader.getClassMetadata().getClassName())){
 8             return true;
 9         }
10         
11         return false;
12     }
13     
14 }

 

Error starting ApplicationContext. To display the auto-configuration report re-run your application with 'debug' enabled.
2018-08-04 13:27:14.556 ERROR 4964 --- [           main] o.s.b.d.LoggingFailureAnalysisReporter   : 

***************************
APPLICATION FAILED TO START
***************************

Description:

Field user in com.github.torlight.sbex.UserAction required a bean of type 'com.github.torlight.sbex.User' that could not be found.


Action:

Consider defining a bean of type 'com.github.torlight.sbex.User' in your configuration.

相关示例代码已经上传到github上面,https://github.com/gittorlight/springboot-example

 

posted @ 2018-08-04 14:24  来自非洲大草原的食人虎  阅读(6540)  评论(0编辑  收藏  举报