@ComponentScan配置老扫描不到Bean,这下彻底搞懂

网友投稿 1625 2022-11-23

@ComponentScan配置老扫描不到Bean,这下彻底搞懂

@ComponentScan配置老扫描不到Bean,这下彻底搞懂

一、@Configuration 和 @Bean

在说@ComponentScan注解前,先来搞明白@Configuration 和 @Bean 这两个注解是干啥的。

在没有注解驱动开发前,要想在spring中注入一个bean,是通过 .xml 文件来实现的:

@AllArgsConstructor@ToStringpublic class CarDTO { private Integer id; private String brand;}

public class DemoTest { @Test public void test2() { // 1、默认从src下加载.xml,根据配置文件创建 容器对象 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2、从容器对象中取得 需要的 bean对象 CarDTO carDTO = (CarDTO) context.getBean("carDTO"); System.out.println(carDTO); }}

CarDTO(id=1, brand=BMW)

有了注解后是这样实现的:

@Configuration // 相当于原来的xml文件,告诉spring这是个配置类public class TestConfig { @Bean // 给spring注入一个bean,类型是返回值类型,id是默认是方法名 public CarDTO carDTO(){ return new CarDTO(1,"BMW"); }}

/** * 注解 @Configuration 和 @Bean */ @Test public void test() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class); CarDTO bean = applicationContext.getBean(CarDTO.class); Console.log(bean); }

CarDTO(id=1, brand=BMW)

这里可以看出:

@Configuration :相当于原来的xml文件,告诉spring这是个配置类@Bean:给spring注入一个bean,类型是返回值类型,id 默认是方法名 二、容器的 getBeanDefinitionNames() 方法

applicationContext.getBeanDefinitionNames() 是获取容器中所有bean的name,通过这个可以判断 @ComponentScan()扫描配置是否正确。

这个是在这里介绍下这个方法的主要原因。

同时呢,上面我们说到了@Bean:注解生成的bean的 id 默认是方法名,若是指定了则为指定值,我们用getBeanDefinitionNames()来获取下就知道了,如下:

@Configuration // 相当于原来的xml文件,告诉spring这是个配置类public class TestConfig { @Bean("car") // 给spring注入一个bean,类型是返回值类型,id是默认是方法名 public CarDTO carDTO(){ return new CarDTO(1,"BMW"); }}

/** * applicationContext.getBeanDefinitionNames() 获取容器中所有bean的name, * 通过这个可以判断@ComponentScan()扫描配置是否正确 */ @Test public void test1() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig.class); Console.log(applicationContext.getBeanDefinitionNames()); }

这里可以看到这个bean的name变成了“car”,而不是“carDTO”。

三、@ComponentScan

前面都是引子,现在开始介绍主角。

@ComponentScan(value = "XXX")  是用来告诉spring去哪扫描要注入的bean。为了兼容及灵活配置扫描路径,这个注解定义了很多的参数,具体的:

basePackages与value:  用于指定包的路径,进行扫描basePackageClasses: 用于指定某个类的包的路径进行扫描nameGenerator: bean的名称的生成器useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测includeFilters: 包含的过滤条件            FilterType.ANNOTATION:按照注解过滤            FilterType.ASSIGNABLE_TYPE:按照给定的类型            FilterType.ASPECTJ:使用ASPECTJ表达式 (不常用)            FilterType.REGEX:正则 (不常用)            FilterType.CUSTOM:自定义规则excludeFilters: 排除的过滤条件,用法和includeFilters一样

为了介绍下面的例子,先把文件路径及几个bean的定义列举一下:

@Componentpublic interface DeptDao {}

@Componentpublic class DeptDaoClass_Component {}

@Controllerpublic class DeptDaoClass_Controller {}

@Repositorypublic class DeptDaoClass_Repository {}

@Servicepublic class DeptDaoClass_Service {}

可以看到fengge.dao路径下有1个接口、4个类,且分别用 @Component,@Repository,@Service,@Controller 注解标注。

下面我们开始举例。

举例一:value = "fengge.dao"

basePackages与value:  用于指定包的路径,进行扫描。这里扫描下fengge.dao路径下的bean。

@Configuration@ComponentScan(value = "fengge.dao")public class TestConfig01 {}

/** * 扫描路径 @ComponentScan(value = "fengge.dao") * 这个路径下有一个是接口类型DeptDao,不是具体的类,所以不会产生bean,控制台会打印 Ignored because not a concrete top-level 信息 * 同时可以看到,@Bean、@Controller、@Service、@Component、@Repository注解的类都会被扫描成bean,注册到容器 */ @Test public void test3() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig01.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); Stream.of(beanDefinitionNames).forEach(System.out::println); }

testConfig01deptDaoClass_ComponentdeptDaoClass_ControllerdeptDaoClass_RepositorydeptDaoClass_Service

当然真正结果打印不止这些bean,这里只展示了fengge.dao下的bean。

可以看到:

@Controller、@Service、@Component、@Repository注解的类都会被扫描成bean,注册到容器但接口类型即使加上@Component等注解,也不会实例化成bean,比如这里的 DeptDao 接口,可以看到并未生成对应的bean。

举例二:excludeFilters 排除某些范围

这里按注解类型排除了@Controller、@Service注解的bean。

@Configuration@ComponentScan(value = "fengge.dao", excludeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})})public class TestConfig02 {}

/** * excludeFilters排除某些范围 * 这里按注解类型排除了@Controller、@Service注解的bean */ @Test public void test4() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig02.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); Stream.of(beanDefinitionNames).forEach(System.out::println); }

testConfig02deptDaoClass_ComponentdeptDaoClass_Repository

可以看到加了@Controller、@Service注解的bean不会被扫描到。

另外,主配置类(TestConfig、TestConfig01、TestConfig02)无论如何都会生成bean,不受扫描配置的影响。

举例三:includeFilters指定某些范围

(1)先看过滤类型为:FilterType.ANNOTATION:按照注解过滤

这里按注解类型指定@Controller、@Service注解的bean才能被扫描。

@Configuration@ComponentScan(value = "fengge.dao", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})}, useDefaultFilters = false)public class TestConfig03 {

/** * includeFilters指定某些范围 * 这里按注解类型排除了@Controller、@Service注解的bean * useDefaultFilters默认是true。表示使用默认的过滤器。即默认Filter就会处理@Component、@Controller、@Service、@Repository这些注解的Bean。 * 所以useDefaultFilters = true,则不仅fengge.dao下的@Controller、@Service会扫描到,@Component、@Repository也会被扫描到 */ @Test public void test5() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig03.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); Stream.of(beanDefinitionNames).forEach(System.out::println); }

testConfig03deptDaoClass_ControllerdeptDaoClass_Service

可以看到只有@Controller、@Service注解的bean被扫描并生成。

(2)再看过滤类型为:FilterType.ASSIGNABLE_TYPE:按照给定的类型

@Configuration@ComponentScan(value = "fengge.dao", includeFilters = { @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {DeptDaoClass_Component.class})}, useDefaultFilters = false)public class TestConfig04 {}

@Test public void test6() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig04.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); Stream.of(beanDefinitionNames).forEach(System.out::println); }

testConfig04deptDaoClass_Component

可以看到只有指定类型的bean。

(3)最后看下过滤类型为: FilterType.CUSTOM:自定义规则

public class MyFilterType implements TypeFilter { /** * MetadataReader 读取到当前正在扫描类的信息 * MetadataReaderFactory 可以获取到其他任何类信息 */ @Override public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException { //获取当前类注解的信息 AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata(); //获取当前正在扫描的类信息 ClassMetadata classMetadata = metadataReader.getClassMetadata(); //获取当前类资源(类的路径) Resource resource = metadataReader.getResource(); String className = classMetadata.getClassName(); System.out.println("===============>" + className); if (className.contains("Co")) { return true; } return false; }}

@Configuration@ComponentScan(value = "fengge.dao", includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyFilterType.class})}, useDefaultFilters = false)public class TestConfig05 {}

/** * includeFilters指定某些范围 * FilterType.CUSTOM是自定义扫描类型,即className包含“Co”类型 */ @Test public void test7() { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig05.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); Stream.of(beanDefinitionNames).forEach(System.out::println); }

testConfig05deptDaoClass_ComponentdeptDaoClass_Controller

这里是说自定义扫描className包含“Co”类型的bean。

四、@ComponentScans

@ComponentScans可包含多个@ComponentScan,扫描范围取并集。

@Configuration@ComponentScans(value = { @ComponentScan(value = "fengge.dao", includeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = {MyFilterType.class})}, useDefaultFilters = false ), @ComponentScan(value = "fengge.dao", includeFilters = { @ComponentScan.Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class})}, useDefaultFilters = false )})public class TestConfig06 {}

/** * 注解@ComponentScans可包含多个@ComponentScan,扫描范围取并集 */ @Test public void test8() throws ParseException { ApplicationContext applicationContext = new AnnotationConfigApplicationContext(TestConfig06.class); String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames(); Stream.of(beanDefinitionNames).forEach(System.out::println); }

testConfig06deptDaoClass_ComponentdeptDaoClass_ControllerdeptDaoClass_Service

以上代码见:  = "fengge.dao") 这样路径下的所有bean找到又转化成resouse的呢?

Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);

@Overridepublic Resource[] getResources(String locationPattern) throws IOException { if (this.resourceLoader instanceof ResourcePatternResolver) { return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern); } return super.getResources(locationPattern);}

版权声明:本文内容由网络用户投稿,版权归原作者所有,本站不拥有其著作权,亦不承担相应法律责任。如果您发现本站中有涉嫌抄袭或描述失实的内容,请联系我们jiasou666@gmail.com 处理,核实后本网站将在24小时内删除侵权内容。

上一篇:北京开发小程序开发(北京 小程序开发)
下一篇:乌鲁木齐小程序开发(App开发微信开发 乌鲁木齐小程序制作)
相关文章

 发表评论

暂时没有评论,来抢沙发吧~