Spring5参考指南:组件扫描
组件扫描
上一篇文章我们讲到了annotation-config配置,它主要用于bean内部的属性注入。而bean本身则需要通过配置的方式来定义。如果想使用配置的方式来定义bean,则可以使用component-scan,如下:
<context:component-scan base-package="com.flydean"/>
component-scan会扫描类路径里面的注解注解,包括(@Component, @Repository, @Service,
@Controller, @RestController, @ControllerAdvice, 和@Configuration ), 当然component-scan默认包含了annotation-config,我们可以直接在这些配置bean中使用上篇文章讲到的注解。
@Component
@Component表示该bean是一个组件,@Component是任何Spring管理的组件的通用原型。@Repository、@Service和@Controller是@Component针对更具体的用例(分别在持久性、服务和表示层中)的特殊注解。因此,您可以用@Component注解组件类,但是,通过用@Repository、@Service或@Controller注解它们,您的类更具有语义性。通常更适合在AOP中做进一步的业务逻辑处理。
元注解和组合注解
所谓元注解就是可以用在其他注解中的注解。 像之前提到的@Component就是@Service的元注解。如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Service {
// ....
}
@Component会导致@Service和@Component一样被对待。
当然你也可以组合使用元注解,或者自定义元注解。例如,Spring的@SessionScope注解将作用域名称硬编码为session,但仍允许自定义proxyMode。以下列表显示了sessionScope注解的定义:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Scope(WebApplicationContext.SCOPE_SESSION)
public @Interface SessionScope {
/**
* Alias for {@link Scope#proxyMode}.
* <p>Defaults to {@link ScopedProxyMode#TARGET_CLASS}.
*/
@AliasFor(annotation = Scope.class)
ScopedProxyMode proxyMode() default ScopedProxyMode.TARGET_CLASS;
}
你可以不定义@SessionScope里面的proxyMode, 如下:
@Service
@SessionScope
public class SessionScopedService {
// ...
}
你也可以重写proxyMode,如下:
@Service
@SessionScope(proxyMode = ScopedProxyMode.INTERFACES)
public class SessionScopedUserService implements UserService {
// ...
}
#@ComponentScan和filters
上面我们讲到,要是要使用组件扫描,需要在XML配置context:component-scan, 其实也可以使用注解的形式,如下所示:
@Configuration
@ComponentScan(basePackages = "com.flydean.beans")
public class AppConfig {
}
@ComponentScan可以配置一些filters用来过滤不需要的组件。如下所示:
@Configuration
@ComponentScan(basePackages = "com.flydean.beans",
includeFilters = @ComponentScan.Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
excludeFilters = @ComponentScan.Filter(BeanA.class))
public class BeanAConfig {
}
下表是支持的filter类型和例子:
| Filter type | 表达式例子 | 描述 |
|---|---|---|
| annotation(默认) | org.example.SomeAnnotation | type基本的目标组件 |
| assignable | org.example.SomeClass | 目标组件可分配给(扩展或实现)的类(或接口)。 |
| aspectj | org.example…*Service+ | 匹配目标组件的AspectJ类型 |
| regex | org\.example\.Default.* | 匹配目标主键内名的正则表达式 |
| custom | org.example.MyTypeFilter | org.springframework.core.type .TypeFilter的自定义实现 |
组件内部定义Bean元数据
Spring组件还可以为容器提供bean定义元数据。您可以使用用于在@Configuration annotated类中定义bean元数据的相同@Bean注解来实现这一点。以下示例显示了如何执行此操作:
@Component
public class FactoryMethodComponent {
@Bean
@Qualifier("public")
public BeanA publicInstance() {
return new BeanA();
}
public void doWork() {
// Component method implementation omitted
}
}
InjectionPoint
从SpringFramework4.3开始,还可以声明InjectionPoint类型的工厂方法参数,来创建Bean。
注意,这只适用于bean实例的实际创建,而不适用于现有实例的注入。因此,这个特性对于原型范围的bean最有意义。
@Component
public class InjectPointFactoryMethodComponent {
@Bean
@Scope("prototype")
public BeanA prototypeInstance(InjectionPoint injectionPoint) {
return new BeanA("prototypeInstance for " + injectionPoint.getMember());
}
}
常规Spring组件中的@Bean方法的处理方式与Spring@Configuration类中的对应方法不同。不同的是,@Component类没有用cglib来增强以截获方法和字段的调用。cglib代理是调用@Configuration classes中通过@Bean methods内的方法或字段创建对协作对象的bean元数据引用的方法。
你可以将@Bean方法声明为静态方法,允许在不将其包含的配置类创建为实例的情况下调用它们。在定义post-processor bean(例如,BeanFactoryPostProcessor或BeanPostProcessor类型)时,这是特别有意义的,因为这样的bean在容器生命周期的早期就被初始化,应该避免在此时触发配置的其他部分。
由于技术限制,对static @Bean方法的调用永远不会被容器截获,即使是在@Configuration类(如本节前面所述)中也是如此:cglib子类只能重写非静态方法。因此,直接调用另一个@Bean方法相当于标准Java的new方法,导致从工厂方法本身直接返回一个独立的实例。
要注意: @Configuration类中的常规@Bean方法必须是可重写的,也就是说,它们不能声明为私有或最终的。
为自动检测组件命名
默认情况下,可以提供value属性给@Component、@Repository、@Service和@Controller),从而为Bean命名。
如果这样的注解不包含value,则默认bean名称生成器将返回小写的非限定类名。例如,如果检测到以下组件类,则名称为myMovieLister和movieFinderImpl:
@Service("myMovieLister")
public class SimpleMovieLister {
// ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
// ...
}
如果您不想依赖默认的bean命名策略,可以提供一个自定义的bean命名策略。首先,实现BeanNameGenerator接口,并确保包含一个默认的无参数构造函数。然后,在配置扫描器时提供完全限定的类名,如下面的示例注解和bean定义所示:
public class MyNameGenerator implements BeanNameGenerator {
@Override
public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) {
return null;
}
}
@Configuration
@ComponentScan(basePackages = "com.flydean", nameGenerator = MyNameGenerator.class)
public class BeanNameConfig {
}
为自动检测的组件提供作用域
与一般的Spring管理组件一样,自动检测组件的默认和最常见的作用域是singleton。但是,有时您需要一个可以由@Scope注解指定的不同范围。可以在注解中提供作用域的名称,如下示例所示:
@Scope("prototype")
@Component("beanA")
public class BeanA {
public BeanA(){
}
public BeanA(String name){
}
}
自定义范围解析
要为范围解析提供自定义策略,而不是依赖基于注解的方法,可以实现ScopeMetadataResolver接口。如下所示:
public class MyScopeResolver implements ScopeMetadataResolver {
@Override
public ScopeMetadata resolveScopeMetadata(BeanDefinition definition) {
return null;
}
}
@Configuration
@ComponentScan(basePackages = "com.flydean", scopeResolver = MyScopeResolver.class)
public class BeanScopeResolverConfig {
}
scoped-proxy
当使用某些非单例作用域时,可能需要为作用域对象生成代理。为此,组件扫描元素上可以有一个scoped-proxy 属性。三个可能的值是:no、interfaces和targetClass。例如,以下配置将生成标准JDK动态代理:
@Configuration
@ComponentScan(basePackages = "com.flydean", scopedProxy = ScopedProxyMode.INTERFACES)
public class ScopedProxyConfig {
}
生成候选组件的索引
虽然类路径扫描速度非常快,但是可以通过在编译时创建一个静态候选列表来提高大型应用程序的启动性能。
要生成索引,需要每个模块添加一个附加依赖项,该模块包含作为组件扫描指令目标的组件。下面的示例说明如何使用Maven:
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-indexer</artifactId>
<version>5.1.8.RELEASE</version>
<optional>true</optional>
</dependency>
</dependencies>
这个过程生成一个包含在JAR文件中的META-INF/spring.components文件。
当在类路径上找到META-INF/Spring.components时,索引将自动启用。如果某个索引部分可用于某些库(或用例),但无法为整个应用程序生成,则可以通过将spring.index.ignore设置为true(作为系统属性或类路径根目录下的spring.properties文件)来回滚到常规类路径安排(就像根本没有索引一样)。
本节的例子可以参考 component-scan.
更多教程请参考flydean的博客
Spring5参考指南:组件扫描的更多相关文章
- Spring5参考指南:IOC容器
文章目录 为什么使用Spring5 什么是IOC容器 配置元数据 实例化容器 XML嵌套 groovy bean定义DSL 使用容器 最近在翻译Spring Framework Documentati ...
- Spring5参考指南:Bean的创建
文章目录 Spring容器中的Bean Bean的命名 Bean的实例化 Spring容器中的Bean Bean在Spring中就是一个业务组件,我们通过创建各种Bean来完成最终的业务逻辑功能. 在 ...
- Spring5参考指南:AspectJ注解
文章目录 什么是AspectJ注解 启用AOP 定义Aspect 定义Pointcut 切入点指示符(PCD) 切入点组合 Advice 访问JoinPoint Advice参数 Advice参数和泛 ...
- Spring5参考指南: SpEL
文章目录 Bean定义中的使用 求值 支持的功能 函数 Bean引用 If-Then-Else Elvis Safe Navigation 运算符 集合选择 集合投影 表达式模板化 SpEL的全称叫做 ...
- Spring5参考指南: Resources
文章目录 内置Resource实现 ResourceLoader ResourceLoaderAware 资源作为依赖 构造ClassPathXmlApplicationContext-快捷方式 资源 ...
- Spring5参考指南:事件Event
文章目录 基于继承的Event 基于注解的Event 异步侦听器 Spring提供了很方便的事件的处理机制,包括事件类ApplicationEvent和事件监听类ApplicationListener ...
- Spring5参考指南:JSR 330标准注解
文章目录 @Inject 和 @Named @Named 和 @ManagedBean 之前的文章我们有讲过,从Spring3.0之后,除了Spring自带的注解,我们也可以使用JSR330的标准注解 ...
- Spring5参考指南:基于注解的容器配置
文章目录 @Required @Autowired @primary @Qualifier 泛型 @Resource @PostConstruct和@PreDestroy Spring的容器配置可以有 ...
- Spring5参考指南:容器扩展
文章目录 BeanPostProcessor自定义bean BeanFactoryPostProcessor自定义配置元数据 使用FactoryBean自定义实例化逻辑 Spring提供了一系列的接口 ...
随机推荐
- Java的多线程1:线程的使用
概述 进程是线程的容器,线程共享进程的内存空间,所以线程之间彼此通信是比较容易的,而线程又有自己私有的内存地址,其他线程无法访问.了解进程和线程关系,可以看我另一篇博客<进程与线程> Ja ...
- Go语言笔记(1)变量的定义与赋值
变量的定义与赋值 在go笔记系列开始之前,我强烈建议大家使用Goland的IDM,配合vscode使用真的非常亲民. 1.go程序基本结构 首先,是go程序的基本结构,主要有package引入包.im ...
- spring08
这里主要学习的是关于spring之后中与ioc不同的aop技术:面向切面编程是spring基石之一: 解决代码混乱文体,代码分散,当部分修改时,要逐个修改当更多的日志以及验证介入之后会使代码变得更加的 ...
- 让 .NET 轻松构建中间件模式代码
让 .NET 轻松构建中间件模式代码 Intro 在 asp.net core 中中间件的设计令人叹为观止,如此高大上的设计何不集成到自己的代码里呢. 于是就有了封装了一个简单通用的中间件模板的想法, ...
- python 函数--匿名函数
一.匿名函数的定义: 解决一些简单的需要用函数去问题,匿名函数的函数体只有一行. 二.格式: calc = lambda n:n**n 函数名 = 匿名函数 参数:返回值 三.练习:
- Flutter 实现网易云音乐字幕
老孟导读:没有接触过音乐字幕方面知识的话,会对字幕的实现比较迷茫,什么时候转到下一句?看了这篇文章,你就会明白字幕so easy. 先来一张效果图: 字幕格式 目前市面上有很多种字幕格式,比如srt, ...
- Django 已生成数据时怎么查询数据库
数据库已写好时,怎样查询数据库 1.输入命令:python manage.py inspectdb > model1.py 注:>重定向 到model1.py
- web系统安全运营之基础- 基于DFA算法的高性能的敏感词,脏词的检测过滤算法类(c#).
[概述]做好一个web系统的安全运维,除了常规的防注入,防入侵等,还有一个检测并过滤敏感词,脏词.. 这件事做得不好,轻则导致一场投诉或纠纷,重则导致产品被勒令关闭停运. 废话少说,先看下代码,可以 ...
- Array(数组)对象-->sort() 方法
1.定义和用法 sort() 方法用于对数组的元素进行排序. 默认排序顺序为按字母升序. 语法: array.sort(sortfunction) 参数: sortfunction:规定排序顺序.必须 ...
- django 前后台传递数据
前几天,我们完成了用django orm对数据进行操作.接下来,我们要把数据从后台放到前台. 1.用get方式传值 get:就是在URL拼接字符串,在后台,用request.get方式取 2.用pos ...