由浅入深,详细总结 Spring 八种加载 Bean 的方式

news2024/9/23 7:20:58

文章目录

  • 方式一:XML 方式声明 bean
  • 方式二:XML + 注解方式声明 bean
  • 方式三:注解方式声明配置类
    • 扩展一:@Bean 返回的对象和真实 Bean 对象可能不是一个
    • 扩展二:加载配置类的同时,加载配置文件(系统迁移)
    • 扩展三:@ImportResource、@Bean、@Component 加载优先级
    • 扩展四:@ImportResource 引入多个配置文件的优先级
    • 扩展五:proxyBeanMethods=true 生成代理对象
  • 方式四:@Import 注解注入
  • 方式五:上下文对象在容器初始化完毕后注入
  • 方式六:实现 ImportSelector 接口
  • 方式七:实现 ImportBeanDefinitionRegistrar 接口
  • 方式八:实现 BeanDefinitionRegistryPostProcessor 接口


方式一:XML 方式声明 bean

目录初始化:
在这里插入图片描述

pom.xml:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.axy</groupId>
    <artifactId>springboot_bean_init</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.9</version>
        </dependency>
    </dependencies>
</project>

applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--xml方式声明自己开发的bean-->
    <bean id="cat" class="com.axy.bean.Cat"/>
    <bean id="dog" class="com.axy.bean.Dog"/>
</beans>

待注入对象:

public class Dog {
    ...
}
public class Cat {
    ...
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name); // 打印所有 bean 的名称
        }
    }
}

运行结果如下:

在这里插入图片描述
注:如果 application.xml 的 bean 标签不指定 id 属性,那么默认 bean 的名称为 全限定类名#索引 的形式,运行结果如下:

在这里插入图片描述


xml 方式声明第三方开发的 bean:

pom 中添加如下坐标:

<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>

applicationContext.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--xml方式声明自己开发的bean-->
    <bean id="cat" class="com.axy.bean.Cat"/>
    <bean id="dog" class="com.axy.bean.Dog"/>

    <!--xml方式声明第三方开发的bean-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
</beans>

那么这样会使用 DruidDataSource 的默认构造函数来创建 Bean 对象。控制台打印结果如下:
在这里插入图片描述

相关链接:Spring 从入门到精通系列 05 —— Spring 依赖注入的三种方式


方式二:XML + 注解方式声明 bean

applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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
    ">
    <!--指定加载bean的位置,component-->
    <context:component-scan base-package="com.axy.bean"/>
</beans>

待注入对象:

@Component("dog") // 如果不写 value 属性,当前 value 默认为类名首字母小写
public class Dog {
    ...
}

@Component("cat")
public class Cat {
    ...
}

注:如果要注入controller、service 或者 dao 的 Bean 添加至 IOC 容器,要使用其衍生注解 @Controller 、@Service、@Repository

相关链接:Spring 从入门到精通系列 06 —— Spring 中的 IOC 常用注解

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name); // 打印所有 bean 的名称
        }
    }
}

控制台打印结果如下:
在这里插入图片描述

问题: 第三方 bean(如:druid),没有由 ioc 容器创建出来。

解决步骤:① 新建配置类,在其里面定义第三方 bean,并在该配置类上添加 @Component 或 @Configuration 注解使得该方法参与解析
     ② 在 application.xml 中添加扫描当前配置类的路径信息。

配置类中添加返回第三方 Bean 对象的方法,并添加相应注解:

//@Component
@Configuration
public class DbConfig {
    @Bean
    public DruidDataSource dataSource(){ // 方法的名称代表了当前 bean 的名称
        return new DruidDataSource();
    }
}
<!--指定加载bean的位置,component-->
<context:component-scan base-package="com.axy.bean, com.axy.config"/>

控制台打印结果如下:
在这里插入图片描述
注:@Configuration 注解的定义上添加了 @Component,因此配置类使用 @Component也是没问题的,但推荐写 @Configuration
在这里插入图片描述


方式三:注解方式声明配置类

当前工程目录如下:
在这里插入图片描述

声明配置类,并使用 @ComponentScan 注解指定要扫描的包:

@ComponentScan(value = {"com.axy.bean", "com.axy.config"})
public class SpringConfig {
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

控制台打印结果:
在这里插入图片描述
注:因为使用 AnnotationConfigApplicationContext 方法指定加载了 SpringConfig 这个类,那么该类会被加载成 bean 对象,并且类上面的 @Configuration 注解可不用添加了,并且里面也可添加创建第三方 bean 的方法(但一般不这么写)。


扩展一:@Bean 返回的对象和真实 Bean 对象可能不是一个

当前工程目录如下:
在这里插入图片描述

在 bean 包下新建 DogFactoyBean 类,并实现 FactoryBean 接口:

import org.springframework.beans.factory.FactoryBean;

public class DogFactoryBean implements FactoryBean<Dog> {
    @Override
    public Dog getObject() throws Exception {
        return new Dog();
    }

    @Override
    public Class<?> getObjectType() { // 返回工厂所生产对象的类型
    	// 如果泛型是接口类型,那么当前返回其实现类的字节码
        return Dog.class;
    }

    @Override
    public boolean isSingleton() { // 工厂构建的对象是否是单例
        return true;
    }
}

将 Dog 类上的注解去掉:

//@Component("dog")
public class Dog {
	...
}

添加返回 DogFactoryBean 类的方法,并将其返回值生成 Bean 对象:

@ComponentScan(value = {"com.axy.bean"})
public class SpringConfig {
    @Bean
    public DogFactoryBean dog(){ // Bean的名称是当前方法名
        return new DogFactoryBean(); // 返回对象的类型应该是 “DogFactoryBean”
    }
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(SpringConfig.class);
        Object bean = ctx.getBean("dog");
        System.out.println(bean.getClass());
    }
}

控制台打印结果:
在这里插入图片描述

从结果可以看出,public DogFactoryBean dog(){…} 要返回的类型是 DogFactoryBean,但真实返回的类型是 Dog

结论:@Bean 返回的对象类型和真实 Bean 对象类型可能不是一个


扩展二:加载配置类的同时,加载配置文件(系统迁移)

场景:目前需要做一个系统的二次开发,原有系统用的是配置文件的形式声明 Bean,现准备用注解的形式配置声明 Bean。如何在注解的声明中将原有的配置文件加载进来呢?

当前工程目录如下:
在这里插入图片描述

添加旧配置类 applicationContext2.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"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
    ">
    <bean id="tiger" class="com.axy.bean.Tiger" />
</beans>

bean 包下添加 Tiger 类:

public class Tiger {
    private Integer age;
    
    public void setAge(Integer age) {
        this.age = age;
    }
    
    @Override
    public String toString() {
        return "Tiger{" +
                "age=" + age +
                '}';
    }
}

可以在配置类上使用 @ImportResource 注解将配置文件加载进来:

@ComponentScan("com.axy.bean")
@ImportResource("applicationContext2.xml") // 加载旧配置文件
public class SpringConfig2 {
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig2.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

控制台打印结果:
在这里插入图片描述


扩展三:@ImportResource、@Bean、@Component 加载优先级

首先给出结论:@ImportResource、@Bean、@Component 要注入的 Bean 的名称相同时,优先级表现为:@ImportResource > @Bean > @Component。

当前工程目录如下:
在这里插入图片描述

修改 applicationContext2.xml,Tiger 类采用 set 方法注入,age 设置为 30:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
    ">
    <!--set方法注入-->
    <bean id="tiger" class="com.axy.bean.Tiger">
        <property name="age" value="30"/>
    </bean>
</beans>

新建配置类 SpringConfig3,添加生成 Bean 对象方法,其中 tiger 对象 age 设置为 20:

@ComponentScan(value = {"com.axy.bean"})
@ImportResource("applicationContext2.xml")
public class SpringConfig3 {
    @Bean
    public Tiger tiger(){
        Tiger tiger = new Tiger();
        tiger.setAge(20);
        return tiger;
    }
}

修改 Tiger 类,当被 @ComponentScan 扫描生成 bean 对象时,使其 age 初始化为 10:

@Component
public class Tiger {
    private Integer age = 10;
    ...
}

测试类:

public class App3 {
    public static void main(String[] args) {
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(SpringConfig3.class);
        Tiger tiger = (Tiger) ctx.getBean("tiger");
        System.out.println(tiger);
    }
}

控制台打印结果:
在这里插入图片描述

当注释 @ImportResource(“applicationContext2.xml”),控制台打印结果如下:

在这里插入图片描述

结果表明:@ImportResource、@Bean、@Component 要注入的 Bean 的名称相同时,优先级表现为:@ImportResource > @Bean > @Component。


扩展四:@ImportResource 引入多个配置文件的优先级

首先给出结论:同名的 Bean 对象,后加载的配置会覆盖先加载的配置

添加配置文件 applicationContext3.xml,Tiger 类采用 set 方法注入,age 设置为 40:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
    ">
    <!--set方法注入-->
    <bean id="tiger" class="com.axy.bean.Tiger">
        <property name="age" value="40"/>
    </bean>
</beans>

用 @ImportResource 同时引入两个配置文件:

@ImportResource(value = {"applicationContext2.xml", "applicationContext3.xml"})
public class SpringConfig31 {
}

控制台打印结果:
在这里插入图片描述
如果交换引入顺序:

@ImportResource(value = {"applicationContext3.xml", "applicationContext2.xml"})
public class SpringConfig31 {
}

控制台打印结果:
在这里插入图片描述

因此得出结论:同名的 Bean 对象,后加载的配置会覆盖先加载的配置


扩展五:proxyBeanMethods=true 生成代理对象

首先给出结论:proxyBeanMethods=true 可以保障当前配置类在 Spring 容器中生成的是代理对象,其里面定义的 Bean 是从容器中获取的,而不是重新创建的。

@Configuration 注解里面有一个属性 proxyBeanMethods,默认值为 true。

在这里插入图片描述
新建配置类 SpringConfig32,设置 proxyBeanMethods 属性

@Configuration(proxyBeanMethods = true)
public class SpringConfig32 {
    @Bean
    public Cat cat(){
        return new Cat();
    }
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig32.class);
        System.out.println(ctx.getBean("springConfig32"));
        System.out.println("---------------------------");
        System.out.println(ctx.getBean("cat"));
        System.out.println(ctx.getBean("cat"));
        System.out.println(ctx.getBean("cat"));
    }
}
proxyBeanMethods=trueproxyBeanMethods=false
打印结果在这里插入图片描述在这里插入图片描述

结果表明:① 设置 proxyBeanMethods=true,生成的配置类对象是代理对象,通过其调用加载 Bean 的方法, 是从容器中获取的,而不是重新创建的。
     ② 设置 proxyBeanMethods=false,生成的配置类对象是普通对象,每次执行定义 Bean 的方法都会创建一个新的对象。

总结:配置类中设置 proxyBeanMethods=true,若其某一个方法可以得到对象,并且该对象被加载成 Bean。那么这个方法 在该配置类中 不论调用多少次,都是从容器中获取,即只会创建一次。

注:设置 proxyBeanMethods=true,里面的加载 Bean 的方法也要添加 @Bean 注解


方式四:@Import 注解注入

当前工程目录如下:
在这里插入图片描述
添加配置类 SpringConfig4,使用 @Import 注解导入要注入的 bean 对应的字节码:

@Import(Dog.class) // 被导入的 bean (Dog类) 无需使用注解声明为 bean。
public class SpringConfig4 {
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig4.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

控制台打印结果:

在这里插入图片描述

从结果可以看出与前文 Bean 名称不同的是,@Import 注解加载的 Bean 名称采用 全路径类名

注:此形式可以有效的降低源代码与 Spring 技术的耦合度,在 spring 技术底层及诸多框架的整合中大量使用


使用 @Import 加载配置类:

使用 @Import 加载配置类 DbConfig:

@Import(value = {Dog.class, DbConfig.class})
public class SpringConfig4 {
}

@Configuration
public class DbConfig {
    @Bean
    public DruidDataSource dataSource(){
        return new DruidDataSource();
    }
}

控制台打印结果:
在这里插入图片描述
结果表明:DbConfig 被加载了,并且配置类里面 Bean 的声明也会被加载

如果去掉配置类里面的 @Configuration 注解,打印结果同上。

结论:使用 @Import 加载配置类,配置类可不用添加 @Configuration 注解


方式五:上下文对象在容器初始化完毕后注入

上下文对象的 registerBean 方法,是 AnnotationConfigApplicationContext 独有的方法:

测试类:

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx =
                new AnnotationConfigApplicationContext(SpringConfig4.class);
        // 上下文容器对象已经初始化完毕后,手工加载 bean
        ctx.registerBean("monkey", Monkey.class, 1); // 参数三代表构造函数的参数
        ctx.registerBean("monkey", Monkey.class, 2);
        ctx.registerBean("monkey", Monkey.class, 3);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("-------------");
        Monkey monkey = ctx.getBean("monkey", Monkey.class);
        System.out.println(monkey);
    }
}

public class Monkey {
    private Integer age;
    public Monkey() {
    }
    public Monkey(Integer age) {
        this.age = age;
    }
    @Override
    public String toString() {
        return "Monkey{" +
                "age=" + age +
                '}';
    }
}

打印结果:
在这里插入图片描述

结果表明:上下文容器初始化后,注册了三次 monkey 对象,但最终容器中保留的是最后一次注册的对象。

注:如果使用 registerBean 方法的时候,没有指明 bean 的名称,除非待注入的对象使用 @Component 或其衍生注解指名 bean 的名称,否则名称默认类名首字母小写。


方式六:实现 ImportSelector 接口

实现 ImportSelector 接口的类,实现对导入源的编程式处理。

当前工程目录如下:
在这里插入图片描述

添加 MyImportSelector 类,实现 ImportSelector 接口,并重写 selectImports 方法:

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata metadata) {
        return new String[]{"com.axy.bean.Dog", "com.axy.bean.Cat"}; // 数组元素为全路径类名
    }
}

public class Dog {

}

public class Cat {

}

添加配置类 SpringConfig6,使用 @Import 引入 MyImportSelector 类:

@Import(MyImportSelector.class)
public class SpringConfig6 {
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig6.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

控制台打印结果:
在这里插入图片描述


String[] selectImports(AnnotationMetadata metadata) 方法形参 metadata 代表元数据,描述的是使用该类 MyImportSelector 的对象。

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata metadata) {
        System.out.println("------------------");
        System.out.println("提示:" + metadata.getClassName()); // 获取使用当前该类的对象的类名
        System.out.println(metadata.hasAnnotation("org.springframework.context.annotation.Configuration"));
        System.out.println("------------------");
        return new String[]{"com.axy.bean.Dog", "com.axy.bean.Cat"};
    }
}

控制台打印结果:
在这里插入图片描述

结果表明:使用类 MyImportSelector 类的对象,其类名是 SpringConfig6,并且该对象没有使用 @Configuration 注解。

medata 还有很多方法可以用,如:获取 SpringConfig6 是否包含指定注解,该注解是否有某种属性等。
因此,可以用 medata 做各种条件的判定,以此来决定是否加载指定的 Bean,即动态加载 Bean

如以下代码:

public class MyImportSelector implements ImportSelector {
    public String[] selectImports(AnnotationMetadata metadata) {
        boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if (flag) {
            return new String[]{"com.axy.bean.Dog"};
        }
        return new String[]{"com.axy.bean.Cat"};
    }
}

注:源码中大量使用!


方式七:实现 ImportBeanDefinitionRegistrar 接口

当前工程目录如下:
在这里插入图片描述

新建 MyRegistrar 类,实现 ImportBeanDefinitionRegistrar 接口,并重写 public void registerBeanDefinitions(…){…} 方法。

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        // 1.使用元数据去做判定
        // 2.返回值是 void,不同于方式六中直接加载 Bean 的形式
        // 创建 beanDefinition 对象,并将该对象使用 registry 注册进容器当中
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Tiger.class).getBeanDefinition();
        registry.registerBeanDefinition("tiger", beanDefinition); 
        // registerBeanDefinition 参数一:bean 名称,参数二:beanDefinition对象
    }
}

registerBeanDefinitions(…) 方法:
参数一:元数据对象,可以对使用该类 MyRegistrar 的对象各种判定;
参数二: Bean 注册器对象,可以实现对容器中 bean的裁定。如:例设置 bean 的单例多例等…

在这里插入图片描述

新建 SpringConfig7 类,并通过 @Import 注解引入 MyRegistrar 类:

@Import(MyRegistrar.class)
public class SpringConfig7 {
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx =
                new AnnotationConfigApplicationContext(SpringConfig7.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
    }
}

控制台打印结果:
在这里插入图片描述

总结: 相比于方式六中实现 ImportSelector 接口,方式七实现 ImportBeanDefinitionRegistrar 接口将 bean 的管理开放了出来


如果配置类中引入多个 ImportSelector 接口的实现类,那么对 Bean 对象的裁定由顺序决定。如以下代码:

新建 BookService 接口和实现类:

public interface BookService {
    public void check();
}

public class BookServiceImpl1 implements BookService {
    @Override
    public void check() {
        System.out.println("book service 1...");
    }
}

public class BookServiceImpl2 implements BookService {

    @Override
    public void check() {
        System.out.println("book service 2...");
    }
}

新建注册器,并分别注册成同名 bean:

public class MyRegistrar1 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl1.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService", beanDefinition);
    }
}

public class MyRegistrar2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService", beanDefinition);
    }
}

配置类引入两个注册器实现类:

@Import({MyRegistrar1.class, MyRegistrar2.class})
public class SpringConfig71 {
}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig71.class);
        BookService bookService = ctx.getBean("bookService", BookService.class);
        bookService.check();
    }
}

控制台打印结果:
在这里插入图片描述

如果调换引用顺序,则 Bean 对象的最终裁定也会发生变化,即控制台将打印:book service 1…


方式八:实现 BeanDefinitionRegistryPostProcessor 接口

当前工程目录如下:

在这里插入图片描述

新建 MyPostProcessor 类,实现 BeanDefinitionRegistryPostProcessor 方法,并重写里面的方法:

public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
		BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();
        beanDefinitionRegistry.registerBeanDefinition("bookService", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
    }
}

方法 public void postProcessBeanDefinitionRegistry(…){…}, 后处理并定义注册 Bean。简而言之,通过 BeanDefinition 的注册器注册实名bean,实现对容器中 bean 的最终裁定

引荐配置类,并引用两个注册器和后处理注册器 MyPostProcessor:

@Import({MyPostProcessor.class, MyRegistrar1.class, MyRegistrar2.class})
public class SpringConfig8 {

}

测试类:

public class App {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig8.class);
        BookService bookService = ctx.getBean("bookService", BookService.class);
        bookService.check();
    }
}

控制台打印结果:
在这里插入图片描述

结果表明:后处理器的实现类会在所有 Bean 都注册定义后再进行处理,如果有同名 Bean,则会覆盖前面注册的 Bean。

另外,如果配置类引用多个后处理器的实现类,则按照后处理器实现类的顺序对 Bean 进行最终的裁定。


参考链接:黑马程序员SpringBoot2全套视频教程,springboot零基础到项目实战(spring boot2完整版)

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1131846.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

堆体扫描点云体积计算实现思路分享

做了一个初步的 demo 实验&#xff0c; 计算一堆沙子或者煤堆这种物体的扫描点云的体积 思路就是分块计算每个小的立方体的体积&#xff0c;然后累加&#xff0c;wechat 394467238&#xff0c; 具体的实现细节略微麻烦一点&#xff0c;代码暂时不放了

C# 压缩图片

.net下跨平台图像处理 https://github.com/mono/SkiaSharp 安装包 skiasharp 效果 代码 ImageCompression.cs using SkiaSharp;namespace ImageCompressStu01 {/// <summary>/// 图片压缩/// </summary>public class ImageCompression{/// <summary>/…

Mysql第四篇---数据库索引优化与查询优化

文章目录 数据库索引优化与查询优化索引失效案例数据准备1. 全值匹配2 最佳左前缀法则(联合索引)主键插入顺序4 计算、函数导致索引失效5 类型转换(自动或手动)导致索引失效6 范围条件右边的列索引失效7 不等于(!或者<>)索引失效8 is null可以使用索引, is not null无法使…

数据结构之栈的讲解(源代码+图解+习题)

我们在学习过顺序表和链表之后&#xff0c;了解了使用数组存储数据&#xff0c;使用结构体来存储数据和有关的指针&#xff0c;这些都是底层的东西&#xff0c;链表是靠指针的链接&#xff0c;顺序表是靠数组的下标才能得以实现增删查改。众多数据结构其实底层都离不开数组&…

开发ABAP程序中的错误

select语句错误 select abc from <透明表> into table <内表> where <条件>. *字段必须要一一对应.否则会报错或者值平移 select * from <透明表> into corresponding fields of table <内表> where <条件>. *虽然可以自动匹配可以避免…

分享一下门店服务预约系统怎么做

随着科技的不断发展&#xff0c;越来越多的企业开始注重提高服务质量和效率。其中&#xff0c;门店服务预约系统成为了许多企业的选择。本文将探讨门店服务预约系统的意义、设计思路、实现方法、系统测试以及拓展案例&#xff0c;并总结门店服务预约系统设计和实现的重要性。 一…

使用pycharm远程连接到Linux服务器进行开发

预计达到的效果 本地的 PyCharm 能达到和远程服务器之间的文件同步&#xff1b;本地的 PyCharm 能够使用远程服务器的开发环境&#xff1b; 环境配置 PyCharm&#xff1a;PyCharm 2021.3 (Professional Edition)Linux服务器&#xff1a;Ubuntu20.04 步骤 1.进入配置项 配…

设计模式中的黄金原则:引领你的代码风格,提升可维护性与扩展性

中国的先贤说过: 有道无术,术可求.有术无道,止于术. 术指的是技能、技术或方法&#xff0c;而道指的是原则、道德、智慧和理念。 西方古代的哲人也说过同样的话: 智慧之路从感性开始&#xff0c;却终极于理性.为什么要说设计原则呢, 因为设计模式通常需要遵循一些设计原则&…

Docker GitLab-Runner安装

Docker GitLab-Runner安装 GitLab-Runner安装 问题合集GitLab 域名的配置修改Runner容器内注册失败&#xff0c;提示 dial tcp: lookup home.zsl0.com on 192.168.254.2:53: no such host GitLab-Runner 安装 拉去gitlab/gitlab-runner镜像 docker pull gitlab/gitlab-runne…

【STL】:vector用法详解

朋友们、伙计们&#xff0c;我们又见面了&#xff0c;本期来给大家解读一下有关vector的基础用法&#xff0c;如果看完之后对你有一定的启发&#xff0c;那么请留下你的三连&#xff0c;祝大家心想事成&#xff01; C 语 言 专 栏&#xff1a;C语言&#xff1a;从入门到精通 数…

什么是jquery

jquery是一个javascript库&#xff1b;用来简化javascript编程&#xff1b;基本是前端必备&#xff1b; 看一下示例&#xff1b; <!DOCTYPE html> <html> <head> <meta charset"utf-8"> <script src"https://cdn.staticfile.org/j…

智慧燃气:智慧燃气推进设备全面感知及协同运营

关键词&#xff1a;智慧燃气、燃气数字化、数字燃气、智能燃气、智慧燃气建设 智慧燃气建设应用技术迭代与试点推进方面的问题。随着大数据、人工智能、物联网等新兴前沿技术不断推进和产业化&#xff0c;以及国家工业和信息化部、住房和城乡建设部、国务院国资委等部委大力推…

WMS仓储管理系统在电商领域的应用与影响

随着电商行业的快速发展&#xff0c;消费者对购物体验的要求也越来越高。而在电商运营中&#xff0c;仓储管理系统的应用对于提升购物体验和销售业绩有着重要的作用。本文将探讨WMS仓储管理系统在电商行业的应用与影响。 WMS&#xff08;Warehouse Management System&#xff0…

实战经验分享:打造千万级直播项目,如何选择适合的长连接技术,告别CRUD开发

前言 其实不管大厂、小厂&#xff0c;做业务开发的同学都知道&#xff0c;写一个功能&#xff0c;有中台&#xff0c;有架构&#xff0c;有API&#xff0c;有SDK&#xff0c;很多可复用的代码直接调一下RPC接口或者一个注解就搞定了复杂的操作&#xff0c;所以很多螺丝钉们都没…

对python中切片详解

嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 Python中什么可以切片 Python中符合序列的有序序列都支持切片(slice) 如:列表,字符,元祖 &#x1f447; &#x1f447; &#x1f447; 更多精彩机密、教程&#xff0c;尽在下方&#xff0c;赶紧点击了解吧~ python源码、视…

YOLO目标检测——人体行为数据集【含对应voc、coco和yolo三种格式标签】

实际项目应用&#xff1a;在人流量统计、行人闯入/越界检测、人群密集/拥堵检测预警等场景中&#xff0c;对监控场景中是否存在人体进行检测&#xff0c;并精确定位、追踪。数据集说明&#xff1a;&#xff0c;真实场景的高质量图片数据&#xff0c;数据场景丰富&#xff0c;含…

day49--动态规划8

139.单词拆分 关于多重背包&#xff0c;你该了解这些&#xff01; 背包问题总结篇&#xff01; 第一题&#xff1a;单词拆分 给定一个非空字符串 s 和一个包含非空单词的列表 wordDict&#xff0c;判定 s 是否可以被空格拆分为一个或多个在字典中出现的单词。 说明&#xff1…

[Database] MySQL 8.x Window / Partition Function (窗口/分区函数)

&#x1f9f2;相关文章 [1] MySQL 系统表解析以及各项指标查询 [2] MySQL 5.7 JSON 字段的使用的处理 [3] MySQL经典练习50题 简介 MySQL 8.0版本开始支持窗口函数 官方文档 在之前的版本中已存在的大部分聚合函数&#xff0c;在MySQL 8 中也可以作为窗口函数来使用 方法 / …

地址空间收尾-进程控制

子进程为什么能继承父进程的环境变量呢&#xff1f;并且具有全局属性&#xff1f; 通过打印地址实验可以发现&#xff0c;进程的环境变量在栈区之上 父进程的环境变量也是由bash继承下来的&#xff0c;必定有页表帮助我们从虚拟到物理的映射&#xff0c;创建子进程的页表时也会…