最新最全面的Spring详解(二)——classpath扫描和组件管理(上)

简介: 最新最全面的Spring详解(二)——classpath扫描和组件管理

本章中的大多数例子都使用【XML来指定配置元数据】,这些元数据在Spring容器启动时被扫描,每一个bean的元数据对应生成一个“BeanDefinition”。


本节我们可以通过【扫描类路径】隐式检测候选组件。 【候选组件】指的是通过扫描筛选并在容器中注册了相应beanDifination的类。 这样就不需要使用XML来执行bean注册。 相反,您可以使用注解(例如,【@Component 】)。


更多操作从Spring 3.0开始,Spring JavaConfig项目提供的许多特性都是核心Spring框架的一部分。 这允许您使用Java而不是使用传统的XML文件来定义bean。


1️⃣@Component 和及其派生出的其他注解


@Component 是任何spring管理组件的通用注解。

@Repository、@Service和@Controller是【@Component】用于更具体用例的注解(分别在持久性、服务和表示层中)。这些注解对于我们后期对特定bean进行批量处理时是有帮助的。


2️⃣自动检测类和注册beanDifination


Spring可以自动检测类的信息,并将相应的【BeanDefinition】实例注册到【ApplicationContext】中。 例如,以下两个类适合这样的自动检测:

@Service
public class SimpleMovieLister {
    private MovieFinder movieFinder;
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}
@Service
public class SimpleMovieLister {
    private MovieFinder movieFinder;
    public SimpleMovieLister(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
}


要自动检测这些类并注册相应的bean,您需要将【@ComponentScan】添加到您的【 @Configuration】类中,其中【basePackages】属性是这两个类的公共父包。说人话就是:指定一个包名,自动扫描会检测这个包及其子包下的所有类信息。

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

为简单起见,前面的示例可能使用了注解的value属性 (即@ComponentScan ("org.example"))。

当然我们可以使用以下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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">
    <context:component-scan base-package="org.example"/>
</beans>

注意: <context:component-scan> 的使用会隐式启用 <context:annotation-config>,当使用 <context:component-scan>时,通常不需要包含<context:annotation-config>元素。


3️⃣组件命名


当组件作为扫描过程的一部分被自动检测时,它的bean名是由该扫描器所知道的“BeanNameGenerator”策略生成的。


默认情况下,会使用【@Component】, 【@Repository】,【@Service】和【@Controller】注解的value值,因此将该名称会提供给相应的beanDefination。 如果你的注解不包含任何名称属性,会有默认bean名称生成器将返回【非首字母大写的非全限定类名】。 例如,如果检测到以下组件类,则名称为【myMovieLister】和【movieFinderImp】,这个和xml自动生成的标识符名称不同:

@Service("myMovieLister")
public class SimpleMovieLister {
    // ...
}
@Repository
public class MovieFinderImpl implements MovieFinder {
    // ...
}

4️⃣为自动检测组件提供scope


与spring管理的组件一样,自动检测组件的默认和最常见的作用域是“单例”。 然而,有时您需要一个不同的范围,可以由' @Scope '注解指定。 您可以在注解中提供作用域的名称,如下面的示例所示:

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

5️⃣使用过滤器自定义扫描


默认情况下,带有【@Component】、【@Repository】、【@Service】、【@Controller】、【@Configuration】注解的类是一定能被筛选器选中并进行注册的候选组件。 但是,您可以通过应用自定义过滤器来修改和扩展此行为,自由定制筛选哪些或不包含那些组件。 将它们作为@ComponentScan注解的includeFilters 或 excludeFilters 属性添加(或者作为XML配置中’ <context:include-filter /> ‘或’ <context:exclude-filter /> ‘元素的子元素)。 每个筛选器元素都需要’ type ‘和’ expression '属性。 下表描述了过滤选项:


过滤方式 示例表达式 描述
annotation (默认) org.example.SomeAnnotation 要在目标组件的类型级别上“存在”或“元注解存在”的注解。
assignable org.example.SomeClass 指定要排除的bean的类
aspectj org.example…*Service+ 要被目标组件匹配的AspectJ类型表达式,后边会学习
regex org.example.Default.* 由目标组件的类名匹配的正则表达式
custom org.example.MyTypeFilter ’ org.springframework.core.type的自定义实现,TypeFilter”接口。


下面的示例显示了忽略所有【@Repository】注解,而使用【stub】包下的类进行替换:

@Configuration
@ComponentScan(basePackages = "org.example",
        includeFilters = @Filter(type = FilterType.REGEX, pattern = ".*Stub.*Repository"),
        excludeFilters = @Filter(Repository.class))
public class AppConfig {
    // ...
}

下面的例子显示了等效的XML:

<beans>
    <context:component-scan base-package="org.example">
        <context:include-filter type="regex"
                expression=".*Stub.*Repository"/>
        <context:exclude-filter type="annotation"
                expression="org.springframework.stereotype.Repository"/>
    </context:component-scan>
</beans>

【小知识】: 您还可以通过在注解上设置useDefaultFilters=false 或通过提供use-default-filters="false" 作为<component-scan/> 元素的属性来禁用默认过滤器。 这将有效地禁用使用【@Component】、【@Repository 】、【@Service】、【 @Controller】、【@Configuration】注解或元注解的类的自动检测。


6️⃣在组件中定义Bean元数据


Spring组件还可以向容器提供beanDifination元数据。 可以使用 @Bean 注解来实现这一点。

@Component
public class FactoryMethodComponent {
    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }
    public void doWork() {
        // Component method implementation omitted
    }
}


前面的类是一个Spring组件,它的【doWork()】方法中包含特定于应用程序的代码。 然而,它还提供了一个beanDifination,该beanDifination有一个引用方法【public Instance()】的工厂方法。 【@Bean注解】标识工厂方法,通过【@Qualifier】注解标识一个限定符值。 其他可以指定的方法级注解有【@Scope 】, 【@Lazy 】等。


下面的例子展示了如何做到这一点:

@Component
public class FactoryMethodComponent {
    private static int i;
    @Bean
    @Qualifier("public")
    public TestBean publicInstance() {
        return new TestBean("publicInstance");
    }
    // use of a custom qualifier and autowiring of method parameters
    @Bean
    protected TestBean protectedInstance(
            @Qualifier("public") TestBean spouse,
            @Value("#{privateInstance.age}") String country) {
        TestBean tb = new TestBean("protectedInstance", 1);
        tb.setSpouse(spouse);
        tb.setCountry(country);
        return tb;
    }
    @Bean
    private TestBean privateInstance() {
        return new TestBean("privateInstance", i++);
    }
}


7️⃣基于Java的容器配置


🍀(1)@Bean和@Configuration


Spring新的java配置支持的中心组件是带注解的【@Configuration】类和带注解的【@Bean】方法。


@Bean注解用于指示一个方法,该方法负责【实例化、配置和初始化】一个由Spring IoC容器管理的新对象。 对于那些熟悉Spring <beans/>XML配置的人来说,@Bean注解扮演着与<bean/> 元素相同的角色。 你可以在任何Spring @Component中使用@Bean注解方法。 但是,它们最常与@Configuration一起使用。


用@Configuration注解的一个类表明它的主要目的是作为beanDifination的源,我们通常称之为【配置类】。 此外,【@Configuration】类允许通过调用同一类中的其他【@Bean 】方法来【定义bean间的依赖关系】。 最简单的【@Configuration】类如下所示:

@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyServiceImpl();
    }
}

前面的’ AppConfig '类等价于下面的Spring <beans/>XML:


<beans>
    <bean id="myService" class="com.acme.services.MyServiceImpl"/>
</beans>

🍀(2)使用 AnnotationConfigApplicationContext实例化Spring容器


下面的章节记录了Spring 3.0中引入的【AnnotationConfigApplicationContext】。 这个通用的【ApplicationContext】实现不仅能够接受【@Configuration】类作为输入,还能够接受普通的【@Component】类和用JSR-330元数据注解的类。


当提供【@Configuration】类作为输入时,【@Configuration】类本身被注册为一个beanDifination,并且类中所有声明的【@Bean】方法也被注册为beanDifination。


当提供【@Component 】和JSR-330相关的注解类时,它们被注册为beanDifination。


a、结构简洁


就像Spring XML文件在实例化【ClassPathXmlApplicationContext】时被用作输入一样,当实例化【AnnotationConfigApplicationContext】时,你可以使用【@Configuration】类作为输入。 这允许Spring容器完全不使用xml,如下例所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

正如前面提到的,【AnnotationConfigApplicationContext】并不局限于只与【@Configuration】类一起工作。 任何【@Component】或JSR-330注解类都可以作为输入提供给构造函数,如下面的例子所示:

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

前面的例子假设【MyServiceImpl】、【Dependency1】和【Dependency2】使用Spring依赖注入注解,比如【@Autowired】。


b、通过使用’ register(Class<?>…)'以编程方式构建容器


你可以使用一个【没有参数的构造函数】来实例化一个【AnnotationConfigApplicationContext】,然后使用【register()】方法来配置它。 当以编程方式构建一个“AnnotationConfigApplicationContext”时,这种方法特别有用。 下面的例子展示了如何做到这一点:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.register(AppConfig.class, OtherConfig.class);
    ctx.register(AdditionalConfig.class);
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
    myService.doStuff();
}

c、使用 scan(String…)启用组件扫描

要启用组件扫描,你可以像下面这样注解你的 @Configuration 类:

@Configuration
@ComponentScan(basePackages = "com.acme") 
public class AppConfig  {
    // ...
}
<beans> 
  <context:component-scan base-package="com.ydlclass" / > 
</beans>

同时,AnnotationConfigApplicationContext也暴露了【 scan(String…)】方法来允许相同的组件扫描功能,如下例所示:

public static void main(String[] args) {
    AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
    ctx.scan("com.acme");
    ctx.refresh();
    MyService myService = ctx.getBean(MyService.class);
}

请记住,【@Configuration】类是带有【@Component】元注解的一个注解,因此它们是组件扫描的候选对象。 在前面的例子中,假设【AppConfig】在"com.acme"中声明。 在’ refresh() ‘之后,它的所有’ @Bean '方法都被处理并注册为容器中的beanDifination。


🍀(3) @Bean注解


【@Bean】是一个方法级注解,与XML<bean/> 元素具有相同的能力。 注解支持<bean/>提供的一些属性,例如:


init-method

destroy-method

autowiring

name


你可以在带有【@Configuration】注解的类或带有【@Component】注解的类中使用【@Bean】注解。


a、声明一个 Bean


使用【@Bean】对方法进行注解可以帮助我们申明一个bean。 您可以使用此方法在【ApplicationContext】中注册一个beanDifination,该bean的类型会被指定为【方法的返回值类型】,而具体的返回值则是交由spring管理的bean实例。 默认情况下,bean名与方法名相同。 下面的例子显示了一个【 @Bean 】方法声明:

@Configuration
public class AppConfig {
    @Bean
    public TransferServiceImpl transferService() {
        return new TransferServiceImpl();
    }
}

上面的配置与下面的Spring XML完全相同:


<beans>
    <bean id="transferService" class="com.acme.TransferServiceImpl"/>
</beans>

注意: 你也可以使用接口(或基类)作为返回类型来声明你的@Bean方法,如下面的例子所示:


@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl();
    }
}


b、Bean的依赖关系

带注解的【@Bean】方法可以有任意数量的参数,这些参数描述构建该bean所需的依赖关系。 例如,如果我们的【TransferService】需要一个【AccountRepository】,我们可以用一个方法参数来实现这个依赖,如下例所示:

@Configuration
public class AppConfig {
    @Bean
    public TransferService transferService(AccountRepository accountRepository) {
        return new TransferServiceImpl(accountRepository);
    }
}

c、接受生命周期回调


任何用【@Bean】注解定义的类都支持常规的生命周期回调,并且可以使用JSR-250的’ @PostConstruct ‘和’ @PreDestroy '注解。

也完全支持常规的Spring lifecycle回调。 如果一个bean实现了’ InitializingBean ‘、’ DisposableBean ‘或’ Lifecycle ',则容器会调用它们各自的方法。

标准的【Aware 】接口也完全支持。

【@Bean注解】支持指定任意的初始化和销毁回调方法,就像Spring XML在’ bean ‘元素上的’ init-method ‘和’ destroy-method '属性一样,如下面的示例所示:

public class BeanOne {
    public void init() {
        // initialization logic
    }
}
public class BeanTwo {
    public void cleanup() {
        // destruction logic
    }
}
@Configuration
public class AppConfig {
    @Bean(initMethod = "init")
    public BeanOne beanOne() {
        return new BeanOne();
    }
    @Bean(destroyMethod = "cleanup")
    public BeanTwo beanTwo() {
        return new BeanTwo();
    }
}

小知识: 对于上面例子中的’ BeanOne ‘,在构造过程中直接调用’ init() '方法同样有效,如下例所示:

@Configuration
public class AppConfig {
    @Bean
    public BeanOne beanOne() {
        BeanOne beanOne = new BeanOne();
        beanOne.init();
        return beanOne;
    }
    // ...
}

当您直接在代码中进行配置时,您可以对您的对象做任何您想做的事情,而不总是需要依赖于容器生命周期。

相关文章
|
5天前
|
Java 应用服务中间件 Nacos
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
Spring Cloud 常用各个组件详解及实现原理(附加源码+实现逻辑图)
37 0
|
5天前
|
Java Spring 容器
【Java】Spring如何扫描自定义的注解?
【Java】Spring如何扫描自定义的注解?
41 0
|
5天前
|
XML Java 数据格式
spring基础之常用组件
spring基础之常用组件
|
5天前
|
开发框架 监控 Java
深入探索Spring Boot的监控、管理和测试功能及实战应用
【5月更文挑战第14天】Spring Boot是一个快速开发框架,提供了一系列的功能模块,包括监控、管理和测试等。本文将深入探讨Spring Boot中监控、管理和测试功能的原理与应用,并提供实际应用场景的示例。
17 2
|
5天前
|
安全 Java 数据库
第4章 Spring Security 的授权与角色管理(2024 最新版)(下)
第4章 Spring Security 的授权与角色管理(2024 最新版)
14 0
|
5天前
|
安全 Java 数据库
第4章 Spring Security 的授权与角色管理(2024 最新版)(上)
第4章 Spring Security 的授权与角色管理(2024 最新版)
35 0
|
5天前
|
XML Java 数据格式
Spring IOC—基于XML配置和管理Bean 万字详解(通俗易懂)
Spring 第二节 IOC—基于XML配置和管理Bean 万字详解!。
97 5
|
5天前
|
前端开发 Java 容器
家族传承:Spring MVC中父子容器的搭建与管理指南
家族传承:Spring MVC中父子容器的搭建与管理指南
26 3
|
5天前
|
SpringCloudAlibaba Java 持续交付
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(一)基础知识+各个组件介绍+聚合父工程创建
【Springcloud Alibaba微服务分布式架构 | Spring Cloud】之学习笔记(一)基础知识+各个组件介绍+聚合父工程创建
151 1
|
5天前
|
Java 开发者 容器
【Java】深入了解Spring容器的两个关键组件
【Java】深入了解Spring容器的两个关键组件
12 0