【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则
写在前面
在实际项目中,我们更多的是使用Spring的包扫描功能对项目中的包进行扫描,凡是在指定的包或子包中的类上标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并将这个类注入到Spring容器中。Spring包扫描功能可以使用XML文件进行配置,也可以直接使用@ComponentScan注解进行设置,使用@ComponentScan注解进行设置比使用XML文件配置要简单的多。
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
使用XML文件配置包扫描
我们可以在Spring的XML配置文件中配置包的扫描,在配置包扫描时,需要在Spring的XML文件中的beans节点中引入context标签,如下所示。
<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/context/spring-context.xsd ">
接下来,我们就可以在XML文件中定义要扫描的包了,如下所示。
<context:component-scan base-package="io.mykit.spring"/>
整个beans.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.xsd">
<context:component-scan base-package="io.mykit.spring"/>
<bean id = "person" class="io.mykit.spring.bean.Person">
<property name="name" value="binghe"></property>
<property name="age" value="18"></property>
</bean>
</beans>
此时,只要在io.mykit.spring包下,或者io.mykit.spring的子包下标注了@Repository、@Service、@Controller、@Component注解的类都会被扫描到,并自动注入到Spring容器中。
此时,我们分别创建PersonDao、PersonService、和PersonController类,并在这三个类中分别添加@Repository、@Service、@Controller注解,如下所示。
- PersonDao
package io.mykit.spring.plugins.register.dao;
import org.springframework.stereotype.Repository;
/**
* @author binghe
* @version 1.0.0
* @description 测试的dao
*/
@Repository
public class PersonDao {
}
- PersonService
package io.mykit.spring.plugins.register.service;
import org.springframework.stereotype.Service;
/**
* @author binghe
* @version 1.0.0
* @description 测试的Service
*/
@Service
public class PersonService {
}
- PersonController
package io.mykit.spring.plugins.register.controller;
import org.springframework.stereotype.Controller;
/**
* @author binghe
* @version 1.0.0
* @description 测试的controller
*/
@Controller
public class PersonController {
}
接下来,我们在SpringBeanTest类中新建一个测试方法testComponentScanByXml()进行测试,如下所示。
@Test
public void testComponentScanByXml(){
ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
运行测试用例,输出的结果信息如下所示。
personConfig
personController
personDao
personService
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
person
可以看到,除了输出我们自己创建的bean名称之外,也输出了Spring内部使用的一些重要的bean名称。
接下来,我们使用注解来完成这些功能。
使用注解配置包扫描
使用@ComponentScan注解之前我们先将beans.xml文件中的下述配置注释。
<context:component-scan base-package="io.mykit.spring"></context:component-scan>
注释后如下所示。
<!--<context:component-scan base-package="io.mykit.spring"></context:component-scan>-->
使用@ComponentScan注解配置包扫描就非常Easy了!在我们的PersonConfig类上添加@ComponentScan注解,并将扫描的包指定为io.mykit.spring即可,整个的PersonConfig类如下所示。
package io.mykit.spring.plugins.register.config;
import io.mykit.spring.bean.Person;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/**
* @author binghe
* @version 1.0.0
* @description 以注解的形式来配置Person
*/
@Configuration
@ComponentScan(value = "io.mykit.spring")
public class PersonConfig {
@Bean("person")
public Person person01(){
return new Person("binghe001", 18);
}
}
没错,就是这么简单,只需要在类上添加@ComponentScan(value = "io.mykit.spring")注解即可。
接下来,我们在SpringBeanTest类中新增testComponentScanByAnnotation()方法,如下所示。
@Test
public void testComponentScanByAnnotation(){
ApplicationContext context = new AnnotationConfigApplicationContext(PersonConfig.class);
String[] names = context.getBeanDefinitionNames();
Arrays.stream(names).forEach(System.out::println);
}
运行testComponentScanByAnnotation()方法输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
personDao
personService
person
可以看到使用@ComponentScan注解同样输出了bean的名称。
既然使用XML文件和注解的方式都能够将相应的类注入到Spring容器当中,那我们是使用XML文件还是使用注解呢?我更倾向于使用注解,如果你确实喜欢使用XML文件进行配置,也可以,哈哈,个人喜好嘛!好了,我们继续。
关于@ComponentScan注解
我们点开ComponentScan注解类,如下所示。
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.beans.factory.support.BeanNameGenerator;
import org.springframework.core.annotation.AliasFor;
import org.springframework.core.type.filter.TypeFilter;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
@AliasFor("basePackages")
String[] value() default {};
@AliasFor("value")
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;
ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
String resourcePattern() default ClassPathScanningCandidateComponentProvider.DEFAULT_RESOURCE_PATTERN;
boolean useDefaultFilters() default true;
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
boolean lazyInit() default false;
@Retention(RetentionPolicy.RUNTIME)
@Target({})
@interface Filter {
FilterType type() default FilterType.ANNOTATION;
@AliasFor("classes")
Class<?>[] value() default {};
@AliasFor("value")
Class<?>[] classes() default {};
String[] pattern() default {};
}
}
这里,我们着重来看ComponentScan类的两个方法,如下所示。
Filter[] includeFilters() default {};
Filter[] excludeFilters() default {};
includeFilters()方法表示Spring扫描的时候,只包含哪些注解,而excludeFilters()方法表示不包含哪些注解。两个方法的返回值都是Filter[]数组,在ComponentScan注解类的内部存在Filter注解类,大家可以看下上面的代码。
1.扫描时排除注解标注的类
例如,我们现在排除@Controller、@Service和@Repository注解,我们可以在PersonConfig类上通过@ComponentScan注解的excludeFilters()实现。例如,我们在PersonConfig类上添加了如下的注解。
@ComponentScan(value = "io.mykit.spring", excludeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class, Service.class, Repository.class})
})
这样,我们就使得Spring在扫描包的时候排除了使用@Controller、@Service和@Repository注解标注的类。运行SpringBeanTest类中的testComponentScanByAnnotation()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
person
可以看到,输出的结果信息中不再输出personController、personService和personDao说明Spring在进行包扫描时,忽略了@Controller、@Service和@Repository注解标注的类。
2.扫描时只包含注解标注的类
我们也可以使用ComponentScan注解类的includeFilters()来指定Spring在进行包扫描时,只包含哪些注解标注的类。
这里需要注意的是,当我们使用includeFilters()来指定只包含哪些注解标注的类时,需要禁用默认的过滤规则。
例如,我们需要Spring在扫描时,只包含@Controller注解标注的类,可以在PersonConfig类上添加@ComponentScan注解,设置只包含@Controller注解标注的类,并禁用默认的过滤规则,如下所示。
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
此时,我们再次运行SpringBeanTest类的testComponentScanByAnnotation()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
person
可以看到,在输出的结果中,只包含了@Controller注解标注的组件名称,并没有输出@Service和@Repository注解标注的组件名称。
注意:在使用includeFilters()来指定只包含哪些注解标注的类时,结果信息中会一同输出Spring内部的组件名称。
3.重复注解
不知道小伙伴们有没有注意到ComponentScan注解类上有一个如下所示的注解。
@Repeatable(ComponentScans.class)
我们先来看看@ComponentScans注解是个啥,如下所示。
package org.springframework.context.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
ComponentScan[] value();
}
可以看到,在ComponentScans注解类中只声明了一个返回ComponentScan[]数组的value(),说到这里,大家是不是就明白了,没错,这在Java8中是一个重复注解。
对于Java8不熟悉的小伙伴,可以到【Java8新特性】专栏查看关于Java8新特性的文章。专栏地址小伙伴们可以猛戳下面的链接地址进行查看:
在Java8中表示@ComponentScan注解是一个重复注解,可以在一个类上重复使用这个注解,如下所示。
@Configuration
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false)
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)
public class PersonConfig {
@Bean("person")
public Person person01(){
return new Person("binghe001", 18);
}
}
运行SpringBeanTest类的testComponentScanByAnnotation()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
personService
person
可以看到,同时输出了@Controller注解和@Service注解标注的组件名称。
如果使用的是Java8之前的版本,我们就不能直接在类上写多个@ComponentScan注解了。此时,我们可以在PersonConfig类上使用@ComponentScans注解,如下所示。
@ComponentScans(value = {
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Controller.class})
}, useDefaultFilters = false),
@ComponentScan(value = "io.mykit.spring", includeFilters = {
@Filter(type = FilterType.ANNOTATION, classes = {Service.class})
}, useDefaultFilters = false)
})
再次运行SpringBeanTest类的testComponentScanByAnnotation()方法,输出的结果信息如下所示。
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
personConfig
personController
personService
person
与使用多个@ComponentScan注解输出的结果信息相同。
总结:我们可以使用@ComponentScan注解来指定Spring扫描哪些包,可以使用excludeFilters()指定扫描时排除哪些组件,也可以使用includeFilters()指定扫描时只包含哪些组件。当使用includeFilters()指定只包含哪些组件时,需要禁用默认的过滤规则
好了,咱们今天就聊到这儿吧!别忘了给个在看和转发,让更多的人看到,一起学习一起进步!!
项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation
写在最后
如果觉得文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习Spring注解驱动开发。公众号回复“spring注解”关键字,领取Spring注解驱动开发核心知识图,让Spring注解驱动开发不再迷茫。
【Spring注解驱动开发】组件注册-@ComponentScan-自动扫描组件&指定扫描规则的更多相关文章
- 【spring 注解驱动开发】spring组件注册
尚学堂spring 注解驱动开发学习笔记之 - 组件注册 组件注册 1.@Configuration&@Bean给容器中注册组件 2.@ComponentScan-自动扫描组件&指定扫 ...
- 【Spring注解驱动开发】自定义TypeFilter指定@ComponentScan注解的过滤规则
写在前面 Spring的强大之处不仅仅是提供了IOC容器,能够通过过滤规则指定排除和只包含哪些组件,它还能够通过自定义TypeFilter来指定过滤规则.如果Spring内置的过滤规则不能够满足我们的 ...
- 【Spring注解驱动开发】使用@Scope注解设置组件的作用域
写在前面 Spring容器中的组件默认是单例的,在Spring启动时就会实例化并初始化这些对象,将其放到Spring容器中,之后,每次获取对象时,直接从Spring容器中获取,而不再创建对象.如果每次 ...
- 【Spring注解驱动开发】使用@Import注解给容器中快速导入一个组件
写在前面 我们可以将一些bean组件交由Spring管理,并且Spring支持单实例bean和多实例bean.我们自己写的类,可以通过包扫描+标注注解(@Controller.@Servcie.@Re ...
- 【Spring注解驱动开发】在@Import注解中使用ImportBeanDefinitionRegistrar向容器中注册bean
写在前面 在前面的文章中,我们学习了如何使用@Import注解向Spring容器中导入bean,可以使用@Import注解快速向容器中导入bean,小伙伴们可以参见<[Spring注解驱动开发] ...
- 0、Spring 注解驱动开发
0.Spring注解驱动开发 0.1 简介 <Spring注解驱动开发>是一套帮助我们深入了解Spring原理机制的教程: 现今SpringBoot.SpringCloud技术非常火热,作 ...
- 【spring 注解驱动开发】spring事务处理原理
尚学堂spring 注解驱动开发学习笔记之 - 事务处理 事务处理 1.事务处理实现 实现步骤: * 声明式事务: * * 环境搭建: * 1.导入相关依赖 * 数据源.数据库驱动.Spring-jd ...
- 【Spring注解驱动开发】面试官:如何将Service注入到Servlet中?朋友又栽了!!
写在前面 最近,一位读者出去面试前准备了很久,信心满满的去面试.没想到面试官的一个问题把他难住了.面试官的问题是这样的:如何使用Spring将Service注入到Servlet中呢?这位读者平时也是很 ...
- 【Spring注解驱动开发】使用InitializingBean和DisposableBean来管理bean的生命周期,你真的了解吗?
写在前面 在<[Spring注解驱动开发]如何使用@Bean注解指定初始化和销毁的方法?看这一篇就够了!!>一文中,我们讲述了如何使用@Bean注解来指定bean初始化和销毁的方法.具体的 ...
随机推荐
- 2018-08-27 jq筛选选择器
筛选选择器:为了辅助选择器更简便.快速的找到元素: 1.过滤 eq(n) -> 第n个元素(从零开始) $('h1').eq(2) // 第三个h1 first() -> 第一个元素 la ...
- Codeforces 1272E (Nearest Opposite Parity,反向建边)
题意:给你n个数,每个数的值为a[i],每个点可以从i这号点跳转至(i - a[i]) 或 (i + a[i])点,点的范围为[1,n],然后问的是从偶数点跳至奇数点,从奇数点跳至偶数点的最少次数是多 ...
- myeclipse 2017 CI 破解
1.首先下载破解文件(破解前先关闭myeclipse),链接:https://pan.baidu.com/s/1CPFH4Nga3xITSyj-BCVeaw 提取码:mkvz 2.将下载的破解文件解压 ...
- 最短路径——floyd算法代码(c语言)
最短路径问题 昨天自己试了试写一下dijkstra的算法博客 dijkstra链接在这← 今天来更floyd算法,感觉非常简单果然暴力才是解决一切的王道 一.总体思想 floyd算法就是每一次从邻接矩 ...
- Centos7中磁盘管理及扩展
前提要求: 虚拟机:centos7 虚拟机软件:VMware Workstation 12 在安装Centos系统时,磁盘选择为LVM逻辑卷.当选择为LVM后才能创建逻辑卷等(必须) 数据格式选择的是 ...
- 王艳 201771010127《面向对象程序设计(java)》第十周学习总结
一:理论部分. 1.泛型程序设计意味着编写的代码可以被很多不同类型的对象所重用. 1)泛型(参数化类型):在定义类.接口和方法时,通过类型参数指示将要处理的对象类型.如ArrayList类是一个泛型程 ...
- xv6 操作系统的环境搭建
xv6 是 MIT 设计的一个教学型操纵系统.xv6 可在 Intel X86 框架上运行,为了方便,建议将 xv6 运行在 QEMU 虚拟机器上,本人的实验环境是 ubuntu 18.04 . 1. ...
- 模板技术:JSP、Thymeleaf之间的比较学习
JSP Thymeleaf 可以写java代码的html JSP的替代品 执行过程 页面元素 include 跳转 cookie session 作用域 隐式对象 JS ...
- Python3高级核心技术97讲
可以毫不夸张的说:这门课程是初中级Python开发人员向高级进阶的必学课程 许多Pythoner喜欢追求新的框架,但却不重视Python本身基础知识的学习, 他们不知道的是,语言本身的进阶优先于框架, ...
- SQL——SQL日期
SQL日期 MySQL: NOW() 返回当前的日期和时间 CURDATE() 返回当前的日期 CURTIME() 返回当前的时间 DAT ...