网站首页 > 技术文章 正文
一、@Configuration 和 @Bean
在说@ComponentScan注解前,先来搞明白@Configuration 和 @Bean 这两个注解是干啥的。
在没有注解驱动开发前,要想在spring中注入一个bean,是通过 .xml 文件来实现的:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean name="people" class="fengge.DTO.CarDTO">
<property name="id" value=1></property>
<property name="brand" value="BMW"></property>
</bean>
</beans>
@AllArgsConstructor
@ToString
public 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)
这里可以看出:
1. @Configuration :相当于原来的xml文件,告诉spring这是个配置类
2. @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。为了兼容及灵活配置扫描路径,这个注解定义了很多的参数,具体的:
1. basePackages与value: 用于指定包的路径,进行扫描
2. basePackageClasses: 用于指定某个类的包的路径进行扫描
3. nameGenerator: bean的名称的生成器
4. useDefaultFilters: 是否开启对@Component,@Repository,@Service,@Controller的类进行检测
5. includeFilters: 包含的过滤条件
FilterType.ANNOTATION:按照注解过滤
FilterType.ASSIGNABLE_TYPE:按照给定的类型
FilterType.ASPECTJ:使用ASPECTJ表达式 (不常用)
FilterType.REGEX:正则 (不常用)
FilterType.CUSTOM:自定义规则
6. excludeFilters: 排除的过滤条件,用法和includeFilters一样
为了介绍下面的例子,先把文件路径及几个bean的定义列举一下:
?编辑
@Component
public interface DeptDao {
}
@Component
public class DeptDaoClass_Component {
}
@Controller
public class DeptDaoClass_Controller {
}
@Repository
public class DeptDaoClass_Repository {
}
@Service
public 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);
}
testConfig01
deptDaoClass_Component
deptDaoClass_Controller
deptDaoClass_Repository
deptDaoClass_Service
当然真正结果打印不止这些bean,这里只展示了fengge.dao下的bean。
可以看到:
1. @Controller、@Service、@Component、@Repository注解的类都会被扫描成bean,注册到容器
2. 但接口类型即使加上@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);
}
testConfig02
deptDaoClass_Component
deptDaoClass_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);
}
testConfig03
deptDaoClass_Controller
deptDaoClass_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);
}
testConfig04
deptDaoClass_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);
}
testConfig05
deptDaoClass_Component
deptDaoClass_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);
}
testConfig06
deptDaoClass_Component
deptDaoClass_Controller
deptDaoClass_Service
以上代码见: https://github.com/ImOk520/myspringcloud
五、源码
源码中到底是怎样把 @ComponentScan(value = "fengge.dao") 这样路径下的所有bean找到又转化成resouse的呢?
?编辑
Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
@Override
public Resource[] getResources(String locationPattern) throws IOException {
if (this.resourceLoader instanceof ResourcePatternResolver) {
return ((ResourcePatternResolver) this.resourceLoader).getResources(locationPattern);
}
return super.getResources(locationPattern);
}
?编辑
?编辑
?编辑
?编辑
?编辑
?编辑
?
猜你喜欢
- 2024-09-30 LEAP Bridge Concrete混凝土桥梁设计和分析软件
- 2024-09-30 超越幻想的神化事记 《Concrete Revolutio超人幻想》考察
- 2024-09-30 COMME des GAR?ONS 以纽约为灵感打造「Concrete」全新香水
- 2024-09-30 PS4《壁中精灵》:自霸凌议题诞生的治愈系高分游戏
- 2024-09-30 关于创意的书单 创意书籍设计图片
- 2024-09-30 DA Davidson:维持U.S. Concrete(USCR)为中性评级,目标价为50.00美元
- 2024-09-30 设计创意又从何来? 设计 创意
- 2024-09-30 《CONCRETE VISIONS》Steam上线 PS复古风FPS
- 2024-09-30 PS4创意动作冒险游戏《壁中精灵》17分钟试玩演示
- 2024-09-30 Concrete Win Limited增持碧桂园(02007)300万股,每股作价11.84元
你 发表评论:
欢迎- 最近发表
- 标签列表
-
- oraclesql优化 (66)
- 类的加载机制 (75)
- feignclient (62)
- 一致性hash算法 (71)
- dockfile (66)
- 锁机制 (57)
- javaresponse (60)
- 查看hive版本 (59)
- phpworkerman (57)
- spark算子 (58)
- vue双向绑定的原理 (68)
- springbootget请求 (58)
- docker网络三种模式 (67)
- spring控制反转 (71)
- data:image/jpeg (69)
- base64 (69)
- java分页 (64)
- kibanadocker (60)
- qabstracttablemodel (62)
- java生成pdf文件 (69)
- deletelater (62)
- com.aspose.words (58)
- android.mk (62)
- qopengl (73)
- epoch_millis (61)
本文暂时没有评论,来添加一个吧(●'◡'●)