Spring Boot注解学习之@SpringBootApplication(二)

简介: Spring Boot注解学习之@SpringBootApplication(二)

@ComponentScan


在讲述 @Configuration 启动容器+@Component 注册 Bean 小节中简单介绍了@ComponentScan 注解的使用。


@ComponentScan 的功能其实就是自动扫描并加载符合条件的组件或 bean 定义,最终将这些 bean 定义加载到容器中。我们可以通过 backPackages 等属性指定@ComponentScan 自动扫描的范围,如果不指定,则默认 Spring 框架实现从声明@ComponentScan 所在类的 package 进行扫描,默认情况下是不指定的,所以 SpringBoot 的启动类最好在 root package 下。


首先看下@ComponentScan的源码:


@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 "**/*.class";
    boolean useDefaultFilters() default true;
    ComponentScan.Filter[] includeFilters() default {};
    ComponentScan.Filter[] excludeFilters() default {};
    boolean lazyInit() default false;
    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;
        @AliasFor("classes")
        Class<?>[] value() default {};
        @AliasFor("value")
        Class<?>[] classes() default {};
        String[] pattern() default {};
    }
}
复制代码


重点有以下几个属性:

  • basePackages 与 value:用于指定包的路径进行扫描;
  • basePackageClasses:用于指定某个类的包的路径进行扫描;
  • nameGenerator:bean 的名称的生成器;
  • useDefaultFilters:是否开启对@Component,@Repository,@Service,@Controller 的类进行检测,默认为 true;
  • includeFilters:包含的过滤条件 FilterType.ANNOTATION:按照注解过滤; FilterType.ASSIGNABLE_TYPE:按照给定的类型;FilterType.ASPECTJ:使用 ASPECTJ 表达式;FilterType.REGEX:正则;FilterType.CUSTOM:自定义规则 。
  • excludeFilters:排除的过滤条件,用法和 includeFilters 一样。


属性


basePackages 与 value


@ComponentScan(basePackages = “”)     //单个
@ComponentScan(basePackages = {“com.example.dao”,“aaa”,“…”})   //多个
//value 同理
复制代码


注意:可以省略 “ basePackages = ”。


@Configuration
@ComponentScan("com.example.dao")
public class MyConfig {}
@Configuration
@ComponentScan("com.example.dao","com.example.service")
public class MyConfig {}
@Configuration
@ComponentScan("com.example.*")   //通配符匹配所有的包
public class MyConfig {}
复制代码


basePackageClasses


@ComponentScan(basePackageClasses = “”)   //单个
@ComponentScan(basePackageClasses = {“HelloController.class”,“bbb”,“…”})  //多个
复制代码


注意:不可以省略“basePackageClasses =”


@Configuration
@ComponentScan(basePackageClasses = HelloController.class)
public class MyConfig {
}
复制代码


includeFilters


接下来我们用以下代码进行测试。


Repository 的注解类:


package com.example.dao;
@Repository
public class BusinessDAO {
    public void update(){
        System.out.println("调用了 dao 层的 update 方法....");
    }
}
复制代码


Service 的注解类:


package com.example.service;
@Service
public class BusinessService {
    @Autowired
    private BusinessDAO businessDAO;
    public void service(){
        System.out.println("调用了 service 层的 service() 方法 .....");
        businessDAO.update();
    }
}
复制代码


Controller 的注解类:


package com.example.controller;
@Controller
public class BusinessController {
    @Autowired
    private BusinessService service;
    public void request() {
        System.out.println(" 调用了 Controller 的 request() 方法...");
        service.service();
    }
}
复制代码


Configuration 配置类:


package com.example.configuration;
@Configuration
//@ComponentScan(basePackages = {"com.example.dao","com.example.service","com.example.controller"})
@ComponentScan(value = {"com.example.dao","com.example.service","com.example.controller"},
        includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Repository.class)},
        useDefaultFilters = false)
public class ScanConfig {
}
复制代码


测试类代码:


public class ScanConfigTest {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ScanConfig.class);
        String[] definitionNames = context.getBeanDefinitionNames();
        for(String name:definitionNames){
            System.out.println(name);
        }
    }
}
复制代码


执行结果为:


19:22:48.157 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
19:22:48.162 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
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
scanConfig
businessDAO
复制代码


根据结果分析: 除了 spring 本身注册的一些 bean 之外,可以看到最后一行,已经将 ScanConfig 这个类和 BusinessDAO 注册进容器中了。


includeFilters  的参数是一个 Filter[] 数组,然后指定 FilterType 的类型为 ANNOTATION,也就是通过注解来过滤,最后的 value 则是 Repository 注解类。配置之后,在 spring 扫描的时候,就会筛选 com.example 下的三个包中,所有被 @Repository 注解标注的类。


注意:需要设置  useDefaultFilters  为 false,否则@ComponentScan 注解会将 被 @Component、@Repository、@Service 和 @Controller 标注的类都注册进容器中。同时因为我们在 Controller 和 Service 注解的类中关联了其他的类(@Autowired),所以最好在 includeFilters  的 Filter 属性中不要设置为 Controller.class 或 Service.class。


excludeFilters


修改配置类:


@Configuration
//@ComponentScan(basePackages = {"com.example.dao","com.example.service","com.example.controller"})
@ComponentScan(value = {"com.example.dao","com.example.service","com.example.controller"},
        excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class)})
public class ScanConfig {
}
复制代码


同 excludeFilters 属性类似, 配置之后,在 spring 扫描的时候,就会跳过 com.example 下的三个包中,所有被 @Controller 注解标注的类。


执行结果为:


19:44:39.191 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
19:44:39.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
19:44:39.195 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessService'
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
scanConfig
businessDAO
businessService
复制代码


添加自定义过滤规则

在前面使用过 @Filter 注解,里面的 type 属性是一个 FilterType 的枚举类型:


public enum FilterType {
    ANNOTATION,
    ASSIGNABLE_TYPE,
    ASPECTJ,
    REGEX,
    CUSTOM
}
复制代码


使用 CUSTOM 类型,就可以实现自定义过滤规则。


package com.example.filter;
public class CustomTypeFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
        // 获取当前扫描到的类的注解元数据
        AnnotationMetadata annotationMetadata = metadataReader.getAnnotationMetadata();
        // 获取当前扫描到的类的元数据
        ClassMetadata classMetadata = metadataReader.getClassMetadata();
        // 获取当前扫描到的类的资源信息
        Resource resource = metadataReader.getResource();
        if (classMetadata.getClassName().contains("Business")){
            return true;
        }
        return false;
    }
}
复制代码


增加 Service 注解修饰的类:


package com.example.service;
@Service
public class UserService {
}
复制代码


修改配置类:


@Configuration
@ComponentScan(value = {"com.example.dao","com.example.service","com.example.controller"},
        excludeFilters = {@ComponentScan.Filter(type = FilterType.CUSTOM,classes = CustomTypeFilter.class)})
public class ScanConfig {
}
复制代码


执行测试类代码,结果为:


19:56:25.419 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
19:56:25.424 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
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
scanConfig
userService
复制代码


这里简单对扫描到的类名进行判断,如果类名包含”Business“的就符合条件,就会被剔除,不会注入到容器中。


@Component


1、不指定 bean 的名称,默认为类名首字母小写 university


@Component
public class BeanWithComponent {
    public void sayHello(){
        System.out.println("BeanWithComponent sayHello...");
    }
    public void start(){
        System.out.println("BeanWithComponent 初始化。。。");
    }
    public void cleanUp(){
        System.out.println("BeanWithComponent 销毁。。。");
    }
}
复制代码


获取 bean 方式:


@Autowired
BeanWithComponent beanWithComponent;
复制代码



ApplicationContext context = new AnnotationConfigApplicationContext(ComfigureWithScan.class);
BeanWithComponent bean = (BeanWithComponent) context.getBean("beanWithComponent");
复制代码


2、指定 bean 的名称


@Component("bean2")
public class BeanWithComponent {
}
复制代码


获取 bean 方式:


@Autowired
BeanWithComponent bean2;
复制代码



ApplicationContext context = new AnnotationConfigApplicationContext(ComfigureWithScan.class);
BeanWithComponent bean = (BeanWithComponent) context.getBean("bean2");
复制代码


总结


@ComponentScan 注解有以下特性:

  • 自定义扫描路径下边带有@Controller@Service@Repository@Component 注解加入 spring 容器
  • 通过 includeFilters 加入扫描路径下没有以上注解的类加入 spring 容器
  • 通过 excludeFilters 过滤出不用加入 spring 容器的类
  • 自定义增加了@Component 注解的注解方式

接下来讲述一下关于@ComponentScan 注解的两个扩展。


@ComponentScans


源码如下:


@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ComponentScans {
    ComponentScan[] value();
}
复制代码


配置类:


@Configuration
@ComponentScans(value = @ComponentScan("com.example.dao"))
public class ScanConfig {
}
复制代码


执行结果为:


20:07:45.908 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'scanConfig'
20:07:45.914 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
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
scanConfig
businessDAO
复制代码


context:component-scan


上述代码我们是通过@Configuration + @ComponentScan 注解来实现 spring 加载,当然也可以在配置文件中加上扫描的配置。


spring-context.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-3.0.xsd">
    <!--<context:component-scan base-package="com.example.dao" annotation-config="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>-->
    <context:component-scan base-package="com.example.dao" />
    <context:component-scan base-package="com.example.controller" />
    <context:component-scan base-package="com.example.service" />
</beans>
复制代码


修改测试类代码:


public class ScanConfigTest {
    public static void main(String[] args) {
        ApplicationContext  context = new ClassPathXmlApplicationContext("spring-context.xml");
        String[] definitionNames = context.getBeanDefinitionNames();
        for(String name:definitionNames){
            System.out.println(name);
        }
    }
}
复制代码


运行得到以下结果:


14:59:12.773 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessDAO'
14:59:12.778 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessController'
14:59:12.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'businessService'
14:59:12.789 [main] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'userService'
businessDAO
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
businessController
businessService
userService
复制代码


关于  的使用,关于属性的详细讲解可以参考:详解


注意: 中有一个  annotation-config 属性,该属性主要是隐式地向 Spring 容器注册 internalConfigurationAnnotationProcessorinternalAutowiredAnnotationProcessorinternalCommonAnnotationProcessorinternalEventListenerProcessor 以及 internalEventListenerFactory 这五个 bean 类。


当 annotation-config 属性值设为 false 时,即不会注册这五个类。但是@ComponentScan 注解没有该属性。 这是因为在几乎所有情况下,使用@ComponentScan 时,都假定使用默认的注释配置处理(例如,处理@Autowired 和 friends )。 此外,在使用AnnotationConfigApplicationContext 时,注释配置处理器始终会被注册,这意味着在@ComponentScan 级别禁用它们的任何尝试都将被忽略。


SpringBootApplication 注解中4个方法


@SpringBootApplication不仅包括上面的三个重要注解,还包含有4个方法:


image.png


  • Class[] exclude() default {}; 根据 Class 来排除特定的类加入 Spring 容器,传入参数是 class 类型;
  • String[] excludeName() default {}; 根据 Class Name 排除特定的类加入 Spring 容器,传入参数是 class 的全类名字字符串数组;
  • String[] scanBasePackages() default {}; 指定扫描包,参数是包名的字符串数组;
  • Class[] scanBasePackageClasses() default {}; 指定扫描包,参数是 Class 类型数组。


小结


这里总结下@SpringBootApplication 中的三个重要注解的特征:

  • @Configuration


定义 Spring Ioc 容器的配置类。


  • @EnableAutoConfiguration

从 classpath 中搜寻所有 META/spring.factories 配置文件,并将其中org.springframework.boot.autoconfigure.EnableAutoConfiguration 对应的配置项,也就是一个自动配置类列表加载到 Ioc 容器中。 简单说,就是@EnawebleAutoConfiguration 让 Spring Boot 根据类路径下的 jar 包依赖为当前项目进行自动配置,例如,添加了 spring-boot-starter-web 依赖,会自动添加 Tomcat 和 Spring MVC 的依赖。而对于所有标注@Configuration 的配置类,统一使用ConfigurationClassParser解析的。


  • @ComponentScan

自动扫描并加载符合条件的组件或者 bean 定义。



目录
相关文章
|
5天前
|
Java
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
Springboot 使用自定义注解结合AOP方式校验接口参数
|
6天前
|
存储 缓存 Java
【JavaEE】Spring中注解的方式去获取Bean对象
【JavaEE】Spring中注解的方式去获取Bean对象
3 0
|
6天前
|
存储 Java 对象存储
【JavaEE】Spring中注解的方式去存储Bean对象
【JavaEE】Spring中注解的方式去存储Bean对象
9 0
|
6天前
|
XML Java 应用服务中间件
【JavaEE】JavaEE进阶:框架的学习 - Spring的初步认识
【JavaEE】JavaEE进阶:框架的学习 - Spring的初步认识
10 0
|
6天前
|
Java 开发工具 Maven
根据SpringBoot Guides完成进行示例学习(详细步骤)
根据SpringBoot Guides完成进行示例学习(详细步骤)
9 1
|
6天前
|
JSON 前端开发 Java
【JAVA进阶篇教学】第七篇:Spring中常用注解
【JAVA进阶篇教学】第七篇:Spring中常用注解
|
6天前
|
存储 前端开发 Java
Spring Boot自动装配的源码学习
【4月更文挑战第8天】Spring Boot自动装配是其核心机制之一,其设计目标是在应用程序启动时,自动配置所需的各种组件,使得应用程序的开发和部署变得更加简单和高效。下面是关于Spring Boot自动装配的源码学习知识点及实战。
17 1
|
6天前
|
JavaScript Java 开发者
Spring Boot中的@Lazy注解:概念及实战应用
【4月更文挑战第7天】在Spring Framework中,@Lazy注解是一个非常有用的特性,它允许开发者控制Spring容器的bean初始化时机。本文将详细介绍@Lazy注解的概念,并通过一个实际的例子展示如何在Spring Boot应用中使用它。
22 2
|
druid Java 数据库连接
「2020最新」Spring最易学习教程 4—整合Mybatis 事务控制
「2020最新」Spring最易学习教程 4—整合Mybatis 事务控制
|
6天前
|
Java 应用服务中间件 Maven
SpringBoot 项目瘦身指南
SpringBoot 项目瘦身指南
63 0