Spring——2、使用@ComponentScan自动扫描组件并指定扫描规则

简介: 在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。Spring包扫描功能可以使用XML配置文件进行配置,也可以直接使用@ComponentScan注解进行设置,使用@ComponentScan注解进行设置比使用XML配置文件来配置要简单的多。

image.png

📫作者简介:zhz小白

公众号:小白的Java进阶之路

专业技能:

1、Java基础,并精通多线程的开发,熟悉JVM原理

2、熟悉Java基础,并精通多线程的开发,熟悉JVM原理,具备⼀定的线上调优经验

3、熟悉MySQL数据库调优,索引原理等,⽇志原理等,并且有出过⼀篇专栏

4、了解计算机⽹络,对TCP协议,滑动窗⼝原理等有⼀定了解

5、熟悉Spring,Spring MVC,Mybatis,阅读过部分Spring源码

6、熟悉SpringCloud Alibaba体系,阅读过Nacos,Sentinel,Seata,Dubbo,Feign,Gateway核⼼源码与设计,⼆次开发能⼒

7、熟悉消息队列(Kafka,RocketMQ)的原理与设计

8、熟悉分库分表ShardingSphere,具有真实⽣产的数据迁移经验

9、熟悉分布式缓存中间件Redis,对其的核⼼数据结构,部署架构,⾼并发问题解决⽅案有⼀定的积累

10、熟悉常⽤设计模式,并运⽤于实践⼯作中

11、了解ElasticSearch,对其核⼼的原理有⼀定的了解

12、了解K8s,Jekins,GitLab

13、了解VUE,GO

14、⽬前有正在利⽤闲暇时间做互游游戏,开发、运维、运营、推销等==

本人著作git项目:star-jersey-platform,有兴趣的可以私聊博主一起编写,或者给颗star

领域:对支付(FMS,FUND,PAY),订单(OMS),出行行业等有相关的开发领域

🔥如果此文还不错的话,还请👍关注、点赞、收藏三连支持👍一下博主~

在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或其子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。


Spring包扫描功能可以使用XML配置文件进行配置,也可以直接使用@ComponentScan注解进行设置,使用@ComponentScan注解进行设置比使用XML配置文件来配置要简单的多。

使用XML文件配置包扫描

我们可以在Spring的XML配置文件中配置包的扫描,在配置包扫描时,需要在Spring的XML配置文件中的beans节点中引入context标签,如下所示。

<?xmlversion="1.0" encoding="UTF-8"?><beansxmlns="http://www.springframework.org/schema/beans"xmlns:context="http://www.springframework.org/schema/context"xmlns:p="http://www.springframework.org/schema/p"xmlns:aop="http://www.springframework.org/schema/aop"xmlns:tx="http://www.springframework.org/schema/tx"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.2.xsdhttp://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context-4.2.xsd"><!-- 包扫描:只要是标注了我们熟悉的@Controller、@Service、@Repository、@Component这四个注解中的任何一个的组件,它就会被自动扫描,并加进容器中 --><context:component-scanbase-package="com.zhz"/><!-- 注册组件 --><beanid="person"class="com.zhz.bean.Person"><propertyname="age"value="18"/><propertyname="name"value="zhz"/></bean></beans>

这样配置以后,只要在com.zhz包下,或者com.zhz的子包下标注了**@Repository、@Service、@Controller、@Component**注解的类都会被扫描到,并自动注入到Spring容器中。

此时,我们分别创建**BookDao、BookService**以及**BookController**这三个类,并在这三个类中分别添加**@Repository、@Service、@Controller**注解,如下所示。

BookDao

packagecom.zhz.dao;
importorg.springframework.stereotype.Repository;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:56* @since v1*/@RepositorypublicclassBookDao {
}

BookService

packagecom.zhz.service;
importorg.springframework.stereotype.Service;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:56* @since v1*/@ServicepublicclassBookService {
}

BookController

packagecom.zhz.controller;
importorg.springframework.stereotype.Controller;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:57* @since v1*/@ControllerpublicclassBookController {
}

我们写一个测试类,在src/test/java/com/zhz/test下,建立一个IOCTest测试类

packagecom.zhz.test;
importorg.junit.Test;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:58* @since v1*/publicclassIOCTest {
@SuppressWarnings("resource")
@Testpublicvoidtest() {
ClassPathXmlApplicationContextapplicationContext=newClassPathXmlApplicationContext("spring-bean.xml");
// 我们现在就来看一下IOC容器中有哪些bean,即容器中所有bean定义的名字String[] definitionNames=applicationContext.getBeanDefinitionNames();
for (Stringname : definitionNames) {
System.out.println(name);
        }
    }
}

我们可以清晰的发现,扫到的包中包含我们写的bean

image.png


使用注解配置包扫描(常用)

我们只需要在MainConfig中添加

packagecom.zhz.config;
importcom.zhz.bean.Person;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.ComponentScan;
importorg.springframework.context.annotation.Configuration;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:27* @since v1*/@ComponentScan("com.zhz")
@ConfigurationpublicclassMainConfig {
/*** @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id*/@Bean(name="person")
publicPersonperson1(){
returnnewPerson("zhz", 20);
    }
}

然后我们改造一下IOCTest

packagecom.zhz.test;
importcom.zhz.config.MainConfig;
importorg.junit.Test;
importorg.springframework.context.annotation.AnnotationConfigApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:58* @since v1*/publicclassIOCTest {
@SuppressWarnings("resource")
@Testpublicvoidtest() {
AnnotationConfigApplicationContextapplicationContext=newAnnotationConfigApplicationContext(MainConfig.class);
// 我们现在就来看一下IOC容器中有哪些bean,即容器中所有bean定义的名字String[] definitionNames=applicationContext.getBeanDefinitionNames();
for (Stringname : definitionNames) {
System.out.println(name);
        }
    }
}

我们就会发现他跟XML文件配置方法的输出结果是一样的

image.png

关于@ComponentScan注解的源码解析

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by FernFlower decompiler)//packageorg.springframework.context.annotation;
importjava.lang.annotation.Documented;
importjava.lang.annotation.ElementType;
importjava.lang.annotation.Repeatable;
importjava.lang.annotation.Retention;
importjava.lang.annotation.RetentionPolicy;
importjava.lang.annotation.Target;
importorg.springframework.beans.factory.support.BeanNameGenerator;
importorg.springframework.core.annotation.AliasFor;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented@Repeatable(ComponentScans.class)
public@interfaceComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<?extendsBeanNameGenerator>nameGenerator() defaultBeanNameGenerator.class;
Class<?extendsScopeMetadataResolver>scopeResolver() defaultAnnotationScopeMetadataResolver.class;
ScopedProxyModescopedProxy() defaultScopedProxyMode.DEFAULT;
StringresourcePattern() default"**/*.class";
booleanuseDefaultFilters() defaulttrue;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
booleanlazyInit() defaultfalse;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public@interfaceFilter {
FilterTypetype() defaultFilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
    }
}

我们可以发现他又两个比较核心的方法:


  • Filter[] includeFilters() default {}:方法指定Spring扫描的时候按照什么规则只需要包含哪些组件
  • Filter[] excludeFilters() default {}:指定Spring扫描的时候按照什么规则排除哪些组件

扫描时排除注解标注的类(excludeFilters)

假设我们要排除@Controller包下的Bean,那么我们要怎么实现呢?

我们只需要在MainConfig类中添加如下

packagecom.zhz.config;
importcom.zhz.bean.Person;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.ComponentScan;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.context.annotation.FilterType;
importorg.springframework.stereotype.Controller;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:27* @since v1*/@ComponentScan(value= {"com.zhz"},excludeFilters= {
/** type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等* classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller这俩注解标注的组件。*/@ComponentScan.Filter(type=FilterType.ANNOTATION,classes= {Controller.class})
})
@ConfigurationpublicclassMainConfig {
/*** @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id*/@Bean(name="person")
publicPersonperson1(){
returnnewPerson("zhz", 20);
    }
}

我们可以观察到运行结果中并没有@Controller注释修饰的Bean

image.png


扫描时只包含注解标注的类(includeFilters)

跟上面哪个相反,代表只会扫描注解下的包,其他不会扫描,如下,我们只要@Controller修饰的包

packagecom.zhz.config;
importcom.zhz.bean.Person;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.ComponentScan;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.context.annotation.FilterType;
importorg.springframework.stereotype.Controller;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:27* @since v1*/@ComponentScan(value= {"com.zhz"},includeFilters= {
/** type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等* classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都不要,即相当于是我要排除@Controller注解以外的标注的组件。*/@ComponentScan.Filter(type=FilterType.ANNOTATION,classes= {Controller.class})
},useDefaultFilters=false)
@ConfigurationpublicclassMainConfig {
/*** @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id*/@Bean(name="person")
publicPersonperson1(){
returnnewPerson("zhz", 20);
    }
}

我们可以发现运行结果中是没有**@Repository、@Service**注解修饰的Bean,具体演示效果如下:

image.png

重复注解(@Repeatable)

java8之后

我们发现@ComponentScan注解下面是有这样的一个注解的:@Repeatable

image.png

然后我们可以进入他括号里面的类ComponentScans,可以发现他是一个数组注解,因此我们可知@Repeatable代表重复注解,也就是说我们可以在一个类上重复使用这个注解

image.png


让我们来演示一下具体的demo

packagecom.zhz.config;
importcom.zhz.bean.Person;
importorg.springframework.context.annotation.Bean;
importorg.springframework.context.annotation.ComponentScan;
importorg.springframework.context.annotation.Configuration;
importorg.springframework.context.annotation.FilterType;
importorg.springframework.stereotype.Controller;
importorg.springframework.stereotype.Service;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:27* @since v1*/@ComponentScan(value= {"com.zhz"},includeFilters= {
/** type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等* classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都不要,即相当于是我要排除@Controller注解以外的标注的组件。*/@ComponentScan.Filter(type=FilterType.ANNOTATION,classes= {Controller.class})
},useDefaultFilters=false)
@ComponentScan(value= {"com.zhz"},includeFilters= {
/** type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等* classes:除了@Service标注的组件之外,IOC容器中剩下的组件我都不要,即相当于是我要排除@Service注解以外的标注的组件。*/@ComponentScan.Filter(type=FilterType.ANNOTATION,classes= {Service.class})
},useDefaultFilters=false)
@ConfigurationpublicclassMainConfig {
/*** @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id*/@Bean(name="person")
publicPersonperson1(){
returnnewPerson("zhz", 20);
    }
}

从下图运行效果中,我们可以发现他是两个@ComponentScan注解都生效了。

image.png


Java8之前

如果是Java8之前,我们要实现上面那种方式,需要改成如下

packagecom.zhz.config;
importcom.zhz.bean.Person;
importorg.springframework.context.annotation.*;
importorg.springframework.stereotype.Controller;
importorg.springframework.stereotype.Service;
/*** @author zhouhengzhe* @description: todo* @date 2022/11/4 10:27* @since v1*/@ComponentScans({
@ComponentScan(value= {"com.zhz"},includeFilters= {
/** type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等* classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller和@Service这俩注解标注的组件。*/@ComponentScan.Filter(type=FilterType.ANNOTATION,classes= {Controller.class})
        },useDefaultFilters=false),
@ComponentScan(value= {"com.zhz"},includeFilters= {
/** type:指定你要排除的规则,是按照注解进行排除,还是按照给定的类型进行排除,还是按照正则表达式进行排除,等等* classes:除了@Controller标注的组件之外,IOC容器中剩下的组件我都要,即相当于是我要排除@Controller和@Service这俩注解标注的组件。*/@ComponentScan.Filter(type=FilterType.ANNOTATION,classes= {Service.class})
        },useDefaultFilters=false)
})
@ConfigurationpublicclassMainConfig {
/*** @Bean注解是给IOC容器中注册一个bean,类型自然就是返回值的类型,id默认是用方法名作为id*/@Bean(name="person")
publicPersonperson1(){
returnnewPerson("zhz", 20);
    }
}

从下面演示效果上可以发现是跟Java8之后实现上是一样的

image.png


总结

我们可以使用@ComponentScan注解来指定Spring扫描哪些包,可以使用excludeFilters()方法来指定扫描时排除哪些组件,也可以使用includeFilters()方法来指定扫描时只包含哪些组件。当使用includeFilters()方法指定只包含哪些组件时,需要禁用掉默认的过滤规则。

目录
相关文章
|
14天前
|
Java Spring
【Spring】方法注解@Bean,配置类扫描路径
@Bean方法注解,如何在同一个类下面定义多个Bean对象,配置扫描路径
140 73
|
2月前
|
负载均衡 算法 Java
除了 Ribbon,Spring Cloud 中还有哪些负载均衡组件?
这些负载均衡组件各有特点,在不同的场景和需求下,可以根据项目的具体情况选择合适的负载均衡组件来实现高效、稳定的服务调用。
126 5
|
4月前
|
XML 缓存 Java
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
spring源码剖析-spring-beans(内部核心组件,BeanDefinition的注册,BeanWapper创建)
67 10
|
4月前
|
XML 存储 Java
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
spring源码刨析-spring-beans(内部核心组件,beanDefinition加载过程)
|
5月前
|
人工智能 自然语言处理 Java
Spring AI,Spring团队开发的新组件,Java工程师快来一起体验吧
文章介绍了Spring AI,这是Spring团队开发的新组件,旨在为Java开发者提供易于集成的人工智能API,包括机器学习、自然语言处理和图像识别等功能,并通过实际代码示例展示了如何快速集成和使用这些AI技术。
Spring AI,Spring团队开发的新组件,Java工程师快来一起体验吧
|
4月前
|
消息中间件 NoSQL 安全
(转)Spring Boot加载 不同位置的 application.properties配置文件顺序规则
这篇文章介绍了Spring Boot加载配置文件的顺序规则,包括不同位置的application.properties文件的加载优先级,以及如何通过命令行参数或环境变量来指定配置文件的名称和位置。
134 0
|
5月前
|
Java 开发工具 Spring
【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
【Azure 事件中心】azure-spring-cloud-stream-binder-eventhubs客户端组件问题, 实践消息非顺序可达
|
缓存 负载均衡 监控
Spring Cloud 五大组件 简介 Eureka、Ribbon、Hystrix、Feign和Zuul
Spring Cloud 五大组件 简介 Eureka、Ribbon、Hystrix、Feign和Zuul
1378 0
|
负载均衡 算法 网络协议
Spring Cloud 五大核心组件解析之Ribbon简介
Spring Cloud 五大核心组件解析之Ribbon简介
|
负载均衡 Java Spring
Spring Cloud Ribbon 全解 (2) - 基本组件简介
Spring Cloud Ribbon 全解 (2) - 基本组件简介
Spring Cloud Ribbon 全解 (2) - 基本组件简介