企业如何通过vue小程序开发满足高效运营与合规性需求
1625
2022-11-23
@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小时内删除侵权内容。
发表评论
暂时没有评论,来抢沙发吧~