Spring系列11:@ComponentScan批量注册bean
回顾
在前面的章节,我们介绍了@Comfiguration
和@Bean
结合AnnotationConfigApplicationContext
零xml配置文件使用Spring容器的方式,也介绍了通过<context:component-scan base-package="org.example"/>
扫描包路径下的bean的方式。如果忘了可以看下前面几篇。这篇我们来结合这2种方式来理解@ComponentScan
本文内容
@ComponentScan
基本原理和使用@ComponentScan
进阶使用@Componet
及其衍生注解使用
@ComponentScan基本原理和使用
基本原理
源码中解析为配置组件扫描指令与@Configuration
类一起使用提供与 Spring XML 的 <context:component-scan>
元素同样的作用支持。简单点说,就是可以扫描特定包下的bean定义信息,将其注册到容器中,并自动提供依赖注入。
@ComponentScan
可以对应一下xml配置。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="org.example"/>
</beans>
提示: 使用
<context:component-scan>
隐式启用<context:annotation-config>
的功能,也就是扫描批量注册并自动DI。
默认情况下,使用@Component
、@Repository
、@Service
、@Controller
、@Configuration
注释的类或本身使用@Component 注释的自定义注释是会作为组件被@ComponentScan
指定批量扫描到容器中自动注册。
使用案例
定义组件
@Component
public class RepositoryA implements RepositoryBase {
}
@Component
public class Service1 {
@Autowired
private RepositoryBase repository;
}
定义配置类
@Configuration
@ComponentScan(basePackages = "com.crab.spring.ioc.demo08")
public class AppConfig {
}
容器扫描和使用
@org.junit.Test
public void test_component_scan1() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
Service1 service1 = context.getBean(Service1.class);
System.out.println(service1);
context.close();
}
@ComponentScan
进阶使用
源码简析
@ComponentScan
源码和解析如下
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
// 见 basePackages
@AliasFor("basePackages")
String[] value() default {};
// 指定扫描组件的包路径,为空则默认是扫描当前类所在包及其子包
@AliasFor("value")
String[] basePackages() default {};
// 指定要扫描带注释的组件的包 可替换basePackages
Class<?>[] basePackageClasses() default {};
// 用于命名 Spring 容器中检测到的组件的类
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
// 指定解析bean作用域的类
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
// 指示为检测到的组件生成代理的模式
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
// 控制符合组件检测条件的类文件;建议使用下面的 includeFilters excludeFilters
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
// 指示是否应启用使用 @Component @Repository @Service @Controller 注释的类的自动检测。
boolean useDefaultFilters() default true;
// 指定哪些类型适合组件扫描。进一步将候选组件集从basePackages中的所有内容缩小到与给定过滤器或多个过滤 器匹配的基本包中的所有内容。
// <p>请注意,除了默认过滤器(如果指定)之外,还将应用这些过滤器。将包含指定基本包下与给定过滤器匹配的任 何类型,即使它与默认过滤器不匹配
Filter[] includeFilters() default {};
// 定哪些类型不适合组件扫描
Filter[] excludeFilters() default {};
// 指定是否应为延迟初始化注册扫描的 bean
boolean lazyInit() default false;
}
其中用到的Filter
类型过滤器的源码和解析如下
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
// 过滤的类型 支持注解、类、正则、自定义等
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
// 指定匹配的类型,多个时是OR关系
@AliasFor("value")
Class<?>[] classes() default {};
// 用于过滤器的匹配模式,valua没有配置时的替代方法,根据type变化
String[] pattern() default {};
}
FilterType
的支持类型如下
过滤类型 | 样例表达式 | 描述 |
---|---|---|
annotation (default) | org.example.SomeAnnotation |
在目标组件的类型级别存在的注释。 |
assignable | org.example.SomeClass |
目标组件可分配(扩展或实现)的类(或接口) |
aspectj | org.example..*Service+ |
要由目标组件匹配的 AspectJ 类型表达式。 |
regex | org\.example\.Default.* |
与目标组件的类名匹配的正则表达式。 |
custom | org.example.MyTypeFilter |
org.springframework.core.type.TypeFilter 接口的自定义实现。 |
案例1:使用Filters过滤
忽略所有@Repository 注释并使用特定包下正则表达式来匹配*Repository
@Configuration
@ComponentScan(basePackages = "org.example",
includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*My.*Repository"),
excludeFilters = @Filter(Repository.class))
public class AppConfig {
// ...
}
案例2:使用自定义的bean名称生成策略
自定义一个生成策略实现BeanNameGenerator
接口
/**
* 自定义的bean名称生成策略
* @author zfd
* @version v1.0
* @date 2022/1/19 9:07
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
public class MyNameGenerator implements BeanNameGenerator {
public MyNameGenerator() {
}
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
// bean命名统一采用固定前缀+类名
return "crab$$" + definition.getBeanClassName();
}
}
在@ComponentScan
中指定生成名称策略
@Configuration
@ComponentScan(basePackages = "com.crab.spring.ioc.demo08",
nameGenerator = MyNameGenerator.class)
public class AppConfig {
}
从 Spring Framework 5.2.3 开始,位于包 org.springframework.context.annotation 中的 FullyQualifiedAnnotationBeanNameGenerator 可用于默认为生成的 bean 名称的完全限定类名称
测试输出
@org.junit.Test
public void test_name_generator() {
AnnotationConfigApplicationContext context =
new AnnotationConfigApplicationContext(AppConfig.class);
Service1 service1 = context.getBean(Service1.class);
Arrays.stream(context.getBeanNamesForType(service1.getClass())).forEach(System.out::println);
System.out.println(service1);
context.close();
}
// bean名称中存在我们自定义的命名了
crab$$com.crab.spring.ioc.demo08.Service1
com.crab.spring.ioc.demo08.Service1@769f71a9
案例3:自定义bean的作用域策略
与一般 Spring 管理的组件一样,自动检测组件的默认和最常见的范围是单例。可以使用@Scope
注解中提供范围的名称,针对单个组件。
@Scope("prototype") //
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
针对全部扫描组件,可以提供自定义作用域策略。
自定义策略实现ScopeMetadataResolver
接口
/**
* 自定义作用域策略
* @author zfd
* @version v1.0
* @date 2022/1/19 9:32
* @关于我 请关注公众号 螃蟹的Java笔记 获取更多技术系列
*/
public class MyMetadataResolver implements ScopeMetadataResolver {
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
ScopeMetadata metadata = new ScopeMetadata();
// 指定原型作用域
metadata.setScopeName(BeanDefinition.SCOPE_PROTOTYPE);
// 代理模式为接口
metadata.setScopedProxyMode(ScopedProxyMode.INTERFACES);
return metadata;
}
}
在@ComponentScan
中指定作用域策略
@Configuration
@ComponentScan(basePackages = "com.crab.spring.ioc.demo08",
// nameGenerator = FullyQualifiedAnnotationBeanNameGenerator.class)
nameGenerator = MyNameGenerator.class,
scopeResolver = MyMetadataResolver.class
)
public class AppConfig {
}
@Componet及其衍生注解使用
@Component
是任何 Spring 管理的组件的通用原型注解。在使用基于注释的配置和类路径扫描时,此类类被视为自动检测的候选对象
@Repository
、@Service
和 @Controller
是 @Component
针对更具体的用例(分别在持久层、服务层和表示层)的特化。
简单看一下的注解@Component
和@Repository
定义,其它的类似
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Indexed
public @interface Component {
// 指定组件名
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // 元注解@Component
public @interface Repository {
@AliasFor(annotation = Component.class)
String value() default "";
}
使用元注解和组合注解
Spring 提供的许多注解都可以在您自己的代码中用作元注解。元注释是可以应用于另一个注释的注释。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // @Component 导致 @Service 以与 @Component 相同的方式处理
public @interface Service {
// ...
}
可以组合元注释来创建“组合注释”,例如@RestController
就是@ResponseBody
和@Controller
的组合。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Controller
@ResponseBody
public @interface RestController {
@AliasFor(annotation = Controller.class)
String value() default "";
}
组合注释可以选择从元注释中重新声明属性以允许自定义。这在只想公开元注释属性的子集时可能特别有用。
例如,Spring 的 @SessionScope 注解将作用域名称硬编码为 session,但仍允许自定义 proxyMode。
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @interface SessionScope {
// 重新声明了元注解的属性并赋予了默认值
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
Spring 中对java的注解增强之一: 通过
@AliasFor
声明注解属性的别名,此机制实现了通过当前注解内的属性给元注解属性赋值。
总结
本文介绍各种@ComponentScan
批量扫描注册bean的基本使用以及进阶用法和@Componet
及其衍生注解使用。
本篇源码地址: https://github.com/kongxubihai/pdf-spring-series/tree/main/spring-series-ioc/src/main/java/com/crab/spring/ioc/demo08
知识分享,转载请注明出处。学无先后,达者为先!
Spring系列11:@ComponentScan批量注册bean的更多相关文章
- java Spring系列之 配置文件的操作 +Bean的生命周期+不同数据类型的注入简析+注入的原理详解+配置文件中不同标签体的使用方式
Spring系列之 配置文件的操作 写在文章前面: 本文带大家掌握Spring配置文件的基础操作以及带领大家理清依赖注入的概念,本文涉及内容广泛,如果各位读者耐心看完,应该会对自身有一个提升 Spri ...
- SpringBoot27 JDK动态代理详解、获取指定的类类型、动态注册Bean、接口调用框架
1 JDK动态代理详解 静态代理.JDK动态代理.Cglib动态代理的简单实现方式和区别请参见我的另外一篇博文. 1.1 JDK代理的基本步骤 >通过实现InvocationHandler接口来 ...
- spring框架应用系列二:component-scan自动扫描注册装配
component-scan自动扫描注册装配 本文系作者原创,转载请注明出处:http://www.cnblogs.com/further-further-further/p/7717331.html ...
- Spring框架系列(二)之Bean的注解管理
微信公众号:compassblog 欢迎关注.转发,互相学习,共同进步! 有任何问题,请后台留言联系! 1.Spring中的两种容器 在系列(一)中我们已经知道,Spring 是管理对象的容器,其中有 ...
- 使用spring配置类代替xml配置文件注册bean类
spring配置类,即在类上加@Configuration注解,使用这种配置类来注册bean,效果与xml文件是完全一样的,只是创建springIOC容器的方式不同: //通过xml文件创建sprin ...
- spring4.1.8扩展实战之六:注册bean到spring容器(BeanDefinitionRegistryPostProcessor接口)
本章是<spring4.1.8扩展实战>系列的第六篇,目标是学习如何通过自己写代码的方式,向spring容器中注册bean: 原文地址:https://blog.csdn.net/boli ...
- Spring框架系列(11) - Spring AOP实现原理详解之Cglib代理实现
我们在前文中已经介绍了SpringAOP的切面实现和创建动态代理的过程,那么动态代理是如何工作的呢?本文主要介绍Cglib动态代理的案例和SpringAOP实现的原理.@pdai Spring框架系列 ...
- Spring 系列教程之 bean 的加载
Spring 系列教程之 bean 的加载 经过前面的分析,我们终于结束了对 XML 配置文件的解析,接下来将会面临更大的挑战,就是对 bean 加载的探索.bean 加载的功能实现远比 bean 的 ...
- (41)Spring Boot 使用Java代码创建Bean并注册到Spring中【从零开始学Spring Boot】
已经好久没有讲一些基础的知识了,这一小节来点简单的,这也是为下节的在Spring Boot中使用多数据源做准备. 从Spring 3.0开始,增加了一种新的途径来配置Bean Definition,这 ...
随机推荐
- python 使用@property 操作属性时,报“RecursionError:maximun recursion depth exceeded”
使用@property获取和修改属性,出现报错"RecursionError:maximun recursion depth exceeded",超过了最大的递归深度 原因: 方法 ...
- bind 标签
<select id="finduserbylikename" parameterType="string" resultMap="cour ...
- 简单谈谈 TCP/IP
1.前言 IP 或 ICMP.TCP 或 UDP.TELNET 或 FTP.以及 HTTP 等都属于 TCP/IP 协议. 他们与 TCP 或 IP 的关系紧密,是互联网必不可少的组成部分.TCP/I ...
- centos7 alias别名永久生效
进入/etc/profile.d/目录 cd /etc/profile.d/ 在profile.d目录随意创建一个sh文件,例如alias_test.sh vi alias_test.sh##里面的内 ...
- Anaconda3+CUDA10.1+CUDNN7.6+TensorFlow2.6安装(Ubuntu16)
欢迎访问我的GitHub https://github.com/zq2599/blog_demos 内容:所有原创文章分类汇总及配套源码,涉及Java.Docker.Kubernetes.DevOPS ...
- kafka学习笔记(二)kafka的基本使用
概述 第一篇随笔从消息队列的定义和各种应用,以及kafka的分类定义和基本知识,第二篇就写一篇关于kafka的基本实际配置和使用的随笔,包括kafka的集群参数的配置,生产者使用机制,消费者使用机制. ...
- JAVA多线程之并发编程三大核心问题
概述 并发编程是Java语言的重要特性之一,它能使复杂的代码变得更简单,从而极大的简化复杂系统的开发.并发编程可以充分发挥多处理器系统的强大计算能力,随着处理器数量的持续增长,如何高效的并发变得越来越 ...
- [MAUI] 在.NET MAUI中结合Vue实现混合开发
在MAUI微软的官方方案是使用Blazor开发,但是当前市场大多数的Web项目使用Vue,React等技术构建,如果我们没法绕过已经积累的技术,用Blazor重写整个项目并不现实. Vue是当前流 ...
- uniapp如何生成自己的小程序码并且携带参数
生成小程序码需要用到的参数appId appSecret这两个参数可以再微信公众平台里面登录获取 也可以用测试号里面的获取小程序码步骤1.首先要请求官方的API`https://api.weixin ...
- pytest文档2-用例执行
用例设计原则 1.文件名以test_******.py文件和*******_test.py 2.以test_****开头的函数 3.以Test***开头的类 4.以test_*****开头的方法 5. ...