【Java.Spring.Core】【IoC】使用Classpath扫描向容器注册beans/components

本文介绍Spring框架中自动组件扫描的功能,包括如何使用@ComponentScan注解进行配置,如何自定义过滤器来扩展扫描行为,以及如何指定组件的作用域和名称。

在上一小节中,演示了如何使用代码层次上的注解来配置元数据。但是,大量的基础bean还是显式的定义在XML文件中,注解只是用来完成依赖注入。

该小节提供了一种选择,通过扫描 classpath 隐式地检测 candidate components。candidate components是匹配过滤策略,拥有bean定义的类。这可以不必使用XML文件来实现bean的注册,而是使用注解,AspectJ 类型表达式,或者自定义的filter criteria来选择哪些类将会被注册到bean 容器中。


further stereotype注解 (@Component, @Service, @Controller, @Repository)

Spring提供了further stereotype注解: @Component, @Service, @Controller, @Repository

@Component是用于Spring管理的组件的泛型 stereotype。

@Repository, @Service, @Controller 是@Component的特例化,例如,其分别用于持久化,服务,表示层。

因此,我们可以使用@Component来注解我们的组件类;但是如果使用@Repository,@Service,@Controller这些注解,我们的类更加适合被相应的工具所处理,或者与特定的切面相关联。


元注解 - Meta-annotation

Spring提供的许多注解可以被用作“meta-annotations”。meta-annotation是一个简单的注解,其可以被应用在其他的注解上。例如@Service注解就是被@Component元注解的一个注解。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component // Spring will see this and treat @Service in the same way as @Component
public @interface Service {

    // ....

}


元注解可以组合起来创建一个复合注解,例如,@RestController注解是由@Controller和@ResponseBody注解组合起来的。

自动检测类,注册bean的定义

Spring会自动检测stereotype类,并在ApplicationContext中注册相应的bean definition

例如,如下的两个类可被自动检测到:

@Service
public class SimpleMovieLister {

    private MovieFinder movieFinder;

    @Autowired
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }

}

@Repository
public class JpaMovieFinder implements MovieFinder {
    // implementation elided for clarity
}

为了检测到这些类,并注册相应的beans,需要在@Configuration类中添加@ComponentScan,basePackages属性指定了扫描的paren package:

@Configuration
@ComponentScan(basePackages = "org.example")
public class AppConfig  {
    ...
}


或者,我们可以在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
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="org.example"/>

</beans>


<context:component-scan>隐式地激活了<context:anntation-config>功能。当使用<context:component-scan>元素时,不必再包含<context:annotation-config>
AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanProcessor当使用component-scan元素的时候会隐式地被包含进来。

使用filter来定制组件扫描

默认,被@Component, @Repository, @Service, @Controller或使用@Component注解的注解所注解时,是唯一可被检测到的组件/beans. 

但是,我们可以通过应用自定义filter来修改或扩展这种行为。

在@ComponentScan注解中添加IncludeFilters或者excludeFilters参数,或者在component-scan元素中添加include-filter或者exclude-filter子元素,都可以实现这一功能。

每一个filter 元素需要包含type 和 expression 属性。


命名被自动检测的组件

当一个组件被自动检测到的时候,bean名称由BeanNameGenerator生成。默认的,Spring stereotype注解(@Component, @Repository, @Service, @Controller)可以包含一个name值,来指定这个bean的名字。

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}

指定被自动检测的组件的作用域-Scope

默认,大部分自动检测到的组件的作用域是单件的。

但是,有时需要指定其他的scope。

可以使用@Scope注解指定新的作用域

@Scope("prototype")
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

当使用非单件的作用域,可能需要生成这些对象的代理。可以在component-scan element中使用scoped-proxy属性。可以使用三个属性值:no , interfaces, targetClass。例如:

@Configuration
@ComponentScan(basePackages = "org.example", scopedProxy = ScopedProxyMode.INTERFACES)
public class AppConfig {
    ...
}
<beans>
    <context:component-scan base-package="org.example"
        scoped-proxy="interfaces" />
</beans>







buildscript { repositories { maven { url 'https://maven.aliyun.com/repository/central' } maven { url 'https://maven.aliyun.com/repository/gradle-plugin' } mavenCentral() } } plugins { id 'org.springframework.boot' version '3.4.5' id 'io.spring.dependency-management' version '1.1.7' id 'java' id "org.sonarqube" version "2.6" id 'maven-publish' } project.ext { springVersion = "6.1.10" // 与Spring Boot 2.3.x兼容的版本 springBootVersion = "3.4.5" openFeignVersion = "4.2.0" commonVersion = "1.3.86-SNAPSHOT" yoohooComponentVersion = "1.3.38-SNAPSHOT" bscVersion = "1.0.61" thumbnailatorVersion = "0.4.8" moduleName = "" //用于父子模块构建 } def mavenUrl = System.getProperty('artifactory.url') ?: artifactoryUrl def userName = System.getProperty('artifactory.username') ?: artifactoryUsername def pwd = System.getProperty('artifactory.password') ?: artifactoryPassword apply from: "$rootDir/gradle/sonar.gradle" group = project['GROUP_ID'] version = project['VERSION'] sourceCompatibility = '17' targetCompatibility = '17' configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } configurations { developmentOnly runtimeClasspath { extendsFrom developmentOnly } } repositories { maven { url "${mavenUrl}/repository/maven-public/" credentials { username = userName password = pwd } } maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://repository.mulesoft.org/nexus/content/repositories/public' } mavenCentral() } dependencyManagement { imports { mavenBom("org.springframework.cloud:spring-cloud-dependencies:2024.0.0") mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") mavenBom("com.kunlun.otwb.common:dependencies:1.0.1.11") } dependencies { dependency("org.springframework.boot:spring-boot:${springBootVersion}") dependency("org.springframework.boot:spring-boot-starter:${springBootVersion}") dependency("org.springframework.boot:spring-boot-starter-web:${springBootVersion}") dependency("org.springframework.boot:spring-boot-starter-aop:${springBootVersion}") dependency("org.springframework.boot:spring-boot-autoconfigure:${springBootVersion}") } } dependencies { implementation "org.springframework:spring-beans:${springVersion}" implementation "org.springframework:spring-web:${springVersion}" implementation "org.springframework:spring-webmvc:${springVersion}" implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.cloud:spring-cloud-starter-openfeign") implementation("com.baomidou:mybatis-plus-boot-starter") implementation("com.alibaba:druid-spring-boot-starter") implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery") implementation("org.redisson:redisson") implementation ("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}") implementation project(":kunlun-file-api") implementation project(":kunlun-file-server") } // 定义变量,运行环境 def getProfile = { def p = "dev" if (project.hasProperty("profile")) { p = project["profile"] } return p } /*sourceSets { main { resources { // 标记从哪里寻找配置,用,隔开可以增加多个路径例如:srcDir "config/${profile}", "config/db/${profile}" srcDir "source-file/" + getProfile() } } }*/ subprojects { apply plugin: 'java' apply plugin: 'io.spring.dependency-management' sourceCompatibility = '17' targetCompatibility = '17' configurations.all { resolutionStrategy.cacheChangingModulesFor 0, 'seconds' } tasks.withType(JavaCompile) { options.encoding = 'UTF-8' } apply from: "$rootDir/gradle/sonar.gradle" repositories { maven { url "${mavenUrl}/repository/maven-public/" credentials { username = userName password = pwd } } maven { url 'https://maven.aliyun.com/repository/public' } maven { url 'https://repository.mulesoft.org/nexus/content/repositories/public' } maven { url 'https://klrepos.sit.sense-map.cn/repository/maven-snapshots/' } mavenCentral() } dependencyManagement { imports { mavenBom("org.springframework.cloud:spring-cloud-dependencies:2024.0.0") mavenBom("org.springframework.boot:spring-boot-dependencies:${springBootVersion}") mavenBom("com.kunlun.otwb.common:dependencies:1.0.1.11") } } //配置依赖 dependencies { // 加载 libs implementation fileTree(dir: 'libs', includes: ['*.jar']) //统一管理版本的依赖 implementation("com.alibaba:easyexcel") implementation("org.springframework.boot:spring-boot-starter-web") implementation("org.springframework.boot:spring-boot-starter-data-redis") implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-discovery") implementation("com.alibaba.cloud:spring-cloud-starter-alibaba-nacos-config") implementation("org.springframework.cloud:spring-cloud-starter-openfeign") implementation("org.springframework.boot:spring-boot-starter-validation") implementation("com.alibaba:druid-spring-boot-starter") implementation("com.baomidou:mybatis-plus-boot-starter") implementation("com.baomidou:mybatis-plus-extension") implementation("com.alibaba:druid-spring-boot-starter") annotationProcessor("org.projectlombok:lombok") compileOnly("org.projectlombok:lombok") implementation("com.github.xiaoymin:knife4j-openapi3-jakarta-spring-boot-starter:4.5.0") implementation("mysql:mysql-connector-java") testImplementation("junit:junit") implementation("org.redisson:redisson") //非统一管理版本的依赖 //组件库依赖 implementation("com.sftcwl.yoohoo.common:yoohoo-common:${commonVersion}") implementation("com.dameng:DmJdbcDriver18:8.1.2.192") implementation ("org.springframework.boot:spring-boot-starter-actuator:${springBootVersion}") implementation ("com.alibaba.csp:sentinel-core:1.8.6") implementation("com.clickhouse:clickhouse-jdbc:0.4.6") } } allprojects { apply plugin: 'maven-publish' def group = project.property('GROUP_ID') def ver = project.property('VERSION') def moduleName = project.moduleName ?: project.name println "set artifactId: ${moduleName}" // 条件禁用非API模块的发布任务 if (!moduleName.endsWith('-api')) { tasks.withType(PublishToMavenRepository) { enabled = false } } publishing { publications { maven(MavenPublication) { // 公共配置 groupId = group version = ver // 子模块必须覆盖的部分 afterEvaluate { artifactId = moduleName from components.java } } } repositories { maven { // 根据版本号自动选择仓库 url = ver.endsWith('-SNAPSHOT') ? 'https://klrepos.sit.sense-map.cn/repository/maven-snapshots/' : 'https://klrepos.sit.sense-map.cn/repository/maven-releases/' // 认证配置(按需添加) credentials { username = project.property('artifactoryUsername') ?: '' password = project.property('artifactoryPassword') ?: '' } } } } } task checkProxySettings { doLast { println "Proxy Settings:" println " http.proxyHost: ${System.getProperty('http.proxyHost')}" println " http.proxyPort: ${System.getProperty('http.proxyPort')}" println " https.proxyHost: ${System.getProperty('https.proxyHost')}" println " https.proxyPort: ${System.getProperty('https.proxyPort')}" println " http.nonProxyHosts: ${System.getProperty('http.nonProxyHosts')}" } } 这是我的配置文件,你看看有什么问题,编译的时候报上面的错误
最新发布
12-18
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值