02-Spring中Bean的8种获取方式

Bean的获取方式

常见获取方式

虽然Spring为Bean对象的创建提供了多种实例化方式如由前期xml配置逐步演化成注解配置,但本质都是通过反射机制加载类名后创建对象,最后交给Spring管控

  • Spring还开放出了若干种可编程控制的bean的初始化方式,通过分支语句由固定的加载bean转成了可以选择bean是否加载或者选择加载哪一种bean
方式 描述
xml+< bean/> 在spring配置文件中直接配置类全路径,然后自动调用该类的无参数构造方法来实例化Bean
xml:context+注解(@Component+4个@Bean)
配置类+扫描+注解 (@Component+4个@Bean) @Bean定义FactoryBean接口,@ImportResource,@Configuration注解的proxyBeanMethods属性
@Import导入Bean的类,这个类也可以是配置类 指定加载某一个类作为spring管控的bean,被加载的类中@Bean相关的定义也会被一同加载
AnnotationConfigApplicationContext调用register方法
@Import导入ImportSelector接口 选择性的加载Bean
@Import导入ImportBeanDefinitionRegistrar接口 选择性的加载Bean,并初始化Bean的相关属性
@Import导入BeanDefinitionRegistryPostProcessor接口 对注册的Bean做最后统一处理

XML声明Bean

通过无参构造方法实例化: 在spring.xml配置文件中直接配置类的全路径,Spring会自动调用该类的无参数构造方法实例化Bean并交给容器管理

  • Bean的名称可以通过bean标签的id属性指定,默认名称是全类名#有序数字

XML声明Bean方式的优缺点

  • 优点: 通过一个配置文件可以查阅当前Spring环境中定义了多少个或者说多少种bean,既可以配置自定义的Bean也可以配置第三方声明的Bean
  • 缺点: 需要将Spring管控的Bean全部写在spring.xml文件中,对于程序员来说非常不友好
public class User {
    public User() {
        System.out.println("User类的无参数构造方法执行");
    }
}

<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="userBean" class="com.powernode.spring6.bean.User"/>
     
    <bean class="com.powernode.spring6.bean.User"/>
beans>

简单(静态)工厂模式实例化

第一步:定义一个BeanVip

public class Vip {
}

第二步:编写工厂类VipFactory并定义静态方法负责创建Bean,同时也可以对创建的Bean进行加工处理

public class VipFactory {
    public static Vip get(){
        // 可以对Bean加工处理
        return new Vip();
    }
}

第三步:在spring.xml配置文件中使用factory-method属性告诉Spring框架调用哪个静态工厂类的哪个静态方法获取Bean


<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="vipBean" class="com.powernode.spring6.bean.VipFactory" factory-method="get"/>
beans>

第四步: 根据Id从容器中获取Bean,此时容器返回的Bean是通过调用静态工厂的静态方法得到的

@Test
public void testSimpleFactory(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Vip vip = applicationContext.getBean("vipBean", Vip.class);
    //com.powernode.spring6.bean.Vip@79e2c065
    System.out.println(vip);
}

工厂方法模式实例化

第一步: 定义一个具体产品类

public class Order {
}

第二步: 定义一个具体工厂类同时定义创建Bean的实例方法

public class OrderFactory {
    public Order get(){
        // 可以对Bean加工处理
        return new Order();
    }
}

第三步: 在spring.xml配置文件中先注册工厂Bean,使用factory-bean和factory-method属性告诉Spring框架,调用哪个工厂对象的哪个实例方法获取Bean


<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="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/>
    
   <bean id="orderBean" factory-bean="orderFactory" factory-method="get"/> 
beans>

第四步: 根据Id从容器中获取Bean,此时获取的Bean对象是通过调用工厂Bean的实例方法得到的

@Test
public void testSelfFactoryBean(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Order orderBean = applicationContext.getBean("orderBean", Order.class);
    //com.powernode.spring6.bean.Order@35cabb2a
    System.out.println(orderBean);
}

FactoryBean接口实例化

BeanFactory和FactoryBean

在Spring中Bean可以分为普通Bean和工厂Bean两类,工厂Bean也是一种特殊的Bean,只不过它可以辅助Spring实例化其它的Bean对象同时对Bean进行加工处理

BeanFactory(Bean工厂)是Spring容器的超级接口专门负责创建Bean对象, ApplicationContext只是BeanFactory的一个子接口

 BeanFactory beanFactory = new ClassPathXmlApplicationContext("spring.xml");
 Object vipBean = beanFactory.getBean("vipBean");
 System.out.println(vipBean);

Spring提供了一个FactoryBean<创建Bean的类型>接口专门负责创建Bean,实现该接口的类被称为工厂Bean即可以协助框架创建Bean对象同时对Bean加工处理

  • 如果工厂类实现了FactoryBean接口的getObject()方法,可以不指定factory-bean和factory-method属性,直接通过工厂Bean就可以获取其创建的Bean

  • factory-bean属性会自动指向实现了FactoryBean接口的工厂Beanfactory-method属性会自动指向工厂Bean的getObject()方法

方法名 功能
getObjectType() Spring会自动调用这个方法来确认创建对象的字节码对象
getObject() Spring自己会调用这个方法返回创建的Bean并交给容器管理,在返回Bean前可以进行其他的加工处理
isSingleton() 指定工厂Bean创建的Bean是否是单例的 , false表示非单例 ,true(默认值)表示单例

基于XML实例化

第一步: 定义一个具体产品类Person

public class Person {
}

第二步: 编写一个工厂类PersonFactoryBean实现FactoryBean接口的getObject方法,接口泛型就是要创建Bean的类型

public class PersonFactoryBean implements FactoryBean<Person> {
    @Override
    public Person getObject() throws Exception {
        // 对Bean创建前加工处理
        return new Person();
        // 对Bean创建后加工处理
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
    // 这个方法在接口中有默认实现所有可以不用实现
    @Override
    public boolean isSingleton() {
        return true;
    }
}

第三步: 在spring.xml配置文件注册实现了FactoryBean接口的工厂Bean


<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="personBean" class="com.powernode.spring6.bean.PersonFactoryBean"/>
beans>

第四步: 根据Id从容器中获取工厂Bean,此时容器返回的是该工厂Bean创建的Bean对象(默认是单例的)

@Test
public void testFactoryBean(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Person personBean = applicationContext.getBean("personBean", Person.class);
    //com.powernode.spring6.bean.Person@79e2c065
    System.out.println(personBean);

    // 默认工厂Bean创建的Bean是单例的
    Person personBean2 = applicationContext.getBean("personBean", Person.class);
    //com.powernode.spring6.bean.Person@79e2c065
    System.out.println(personBean2);
}

基于注解实例化

FactoryBean接口也可以用于声明Bean, 实现了FactoryBean接口的类造出来的对象不是当前类的对象,而是FactoryBean接口泛型指定类型的对象

在FactoryBean接口的getObject方法中完成对象的加工处理相对于在对象的构造方法中加工处理的优点

  • 在对象的构造方法中加工处理: 如果当前构造方法的初始化动作并不能满足你的需要,就需要重写一个构造方法
  • 在FactoryBean接口的getObject方法中加工处理: 类是一个抽象后剥离的特别干净的模型, 根据情况不同在对象初始化前后设置不同的初始化动作
public class DogFactoryBean implements FactoryBean<Dog> {
    @Override
    public Dog getObject() throws Exception {
        // 对象创建前做的事情
        Dog d = new Dog();
        // 对象创建后做的事情
        return d;
    }
    @Override
    public Class<?> getObjectType() {
        return Dog.class;
    }
    @Override
    public boolean isSingleton() {
        return true;
    }
}

使用@Bean的形式创建FactoryBean接口实现类DogFactoryBean的泛型对象,FactoryBean接口的实现类并不会被加载到容器

@ComponentScan({"com.itheima.bean","com.itheima.config"})
public class SpringConfig3 {
    @Bean
    public DogFactoryBean dog(){
        // 返回的是Dog类型的对象
        return new DogFactoryBean();
    }
}

注入自定义类型Date

需求: 为Student对象注入一个Data类型的日期作为生日

public class Student {
    private Date birth;
    public void setBirth(Date birth) {
        this.birth = birth;
    }

    @Override
    public String toString() {
        return "Student{" +
                "birth=" + birth +
                '}';
    }
}

java.util.Date在Spring中被当做简单类型,所以在注入属性值的时候可以直接使用value属性或value标签来完成

  • 对Date类型属性赋值的时要求日期字符串的格式必须是指定格式Mon Oct 10 14:30:26 CST 2022, 如果是其他格式是不会被识别的

<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="nowTime" class="java.util.Date"/>
    <bean id="student" class="com.powernode.spring6.bean.Student">
        
        <property name="birth" value="Mon Oct 10 14:30:26 CST 2022"/>
        
        <property name="birth" ref="nowTime"/>
    bean>
beans>  

第一步: 编写DateFactoryBean实现FactoryBean接口, 对java.util.Date类型的普通Bean加工并交给容器管理

public class DateFactoryBean implements FactoryBean<Date> {
    // 定义Date属性接收日期字符串
    private String date;
    // 通过构造方法给Date属性赋值
    public DateFactoryBean(String date) {
        this.date = date;
    }
    // 对Date类型的Bean加工处理并交给容器管理
    @Override
    public Date getObject() throws Exception {
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.parse(this.date);
    }
    @Override
    public Class<?> getObjectType() {
        return null;
    }
}

第二步: 编写spring.xml的配置文件注册DateFactoryBean,然后赋值给Student对象Date类型的属性(注入的是java.util.Date类型的Bean)


<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="dateBean" class="com.powernode.spring6.bean.DateFactoryBean">
        <constructor-arg name="date" value="1999-10-11"/>
    bean>

    <bean id="studentBean" class="com.powernode.spring6.bean.Student">
        
        <property name="birth" ref="dateBean"/>
    bean>
beans>

第三步: 根据Id从容器中获取Student类型的Bean,查看其Date类型的属性值

@Test
public void testDate(){
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
    Student studentBean = applicationContext.getBean("studentBean", Student.class);
    //Student{birth=Mon Oct 11 00:00:00 CST 1999}
    System.out.println(studentBean);
}

使用注解获取

注解的存在主要是为了简化XML的配置, 只要使用了Spring提供的注解就要使用包扫描机制,这样Spring才会根据配置的注解创建Bean并交给容器管理

  • 使用注解一般加入的是自己写的组件 , 使用bean标签配置加入的是别人的写的组件 , 开发中常用注解和bean配置相结合的方式

<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.xsd">
    <context:component-scan base-package="com.powernode.spring6.bean4"/>
beans>

标识类的四个注解

无论使用标识Bean的四个注解和还是使用XML配置的方式,最终注册到容器中组件的默认行为都是一样的,组件都有Id和默认作用域

  • 如果同时使用了其他方式注册Bean,那么这四个注解只会起到指定Bean名称的作用
  • 四个注解都只有一个value属性用来指定Bean的Id,Id默认是组件的简单类名首字母小写后得到的字符串,作用域默认是单例的

Spring无法识别到一个组件到底是不是它所标记的类型(注解只能起到标识作用),即使将@Respository注解用在一个表述层控制器组件上面也不会产生任何错误

  • @Respository、@Service、@Controller这几个注解仅仅是为了让开发人员自己明确当前的组件扮演的角色,增强程序的可读性
注解名 功能
@Repository 推荐标识受Spring IOC容器管理的持久化层组件
@Service 推荐标识受Spring IOC容器管理的业务逻辑层组件
@Controller 推荐标识受Spring IOC容器管理的表述层控制器组件
@Component 推荐标识受Spring IOC容器管理的普通组件(不属于以上几层的组件)
@Scope 指定放入容器的组件是多实例的还是单实例的 , 默认是单实例的 , prototype表示指定创建的Bean是多实例的

第一步: 在类上使用标识Bean的注解

@Component("userBean")
public class User {
}

第二步: 在spring.xml配置文件中使用context:component-scan标签扫描指定包中加了注解的组件,这样框架会创建对应的Bean对象同时交给IoC容器管理


<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.xsd">
    
    <context:component-scan base-package="com.powernode.spring6.bean"> context:component-scan>
beans>

第三步: 根据Id从容器中获取对应的Bean

public class AnnotationTest {
    @Test
    public void testBean(){
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        User userBean = applicationContext.getBean("userBean", User.class);
        System.out.println(userBean);
    }
}

@Bean注解

@Bean注解可以将它标识方法返回的对象注册成Bean交给IoC容器管理,默认方法的名称就是Bean的Id(注解的name属性可以单独指定Bean的Id)

  • 注解方法一定要在@Configuration(常用)/@Component修饰的类中,Spring只有先扫描到类然后才能将类中@Bean注解标识方法的返回对象交给容器管理
  • Spring还会自动根据方法的形参的类型去容器中找对应的bean实现自动注入,如果找到了多个bean会以方法的形参名作为Id继续匹配,不需要使用自动注入注解

第一步: 定义一个配置类,使用@ComponentScan注解代替包扫描的动作,在配置类中使用@Bean注解标识方法

  • 如果手动指定要加载的配置类那么@Configuration/@Component注解可以省略,如果是通过包扫描的方式加载配置类那么就不能省略注解
// @Component
// @Configuration
@ComponentScan({"com.bean"}) 
public class DbConfig {
    @Bean
    public DruidDataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }
}

第二步: 使用AnnotationConfigApplicationContext加载配置类的字节码对象创建容器对象,配置类也会注册成Bean由容器管理(Id默认是简类名首字母小写)

@Test
public void testNoXml(){
    //不再通过创建ClassPathXmlApplicationContext()获取容器对象
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
    DruidDataSource dataSource = applicationContext.getBean("dataSource", DruidDataSource.class);
} 

@Configuration注解

@Configuration注解的proxyBeanMethods属性可以指定类中@Bean标识的方法创建的对象是否为单实例

  • true(默认值): 此时会创建配置类的代理对象,如果调用配置类中使用Bean注解标识的方法,获取的是容器中第一次加载配置类时创建的那个Bean对象(单例)
  • false:此时会创建配置类的普通对象,每调用一次配置类中使用Bean注解标识的方法获取的都是一个新创建的对象(多实例)

第一步: 创建一个配置类并使用@Bean标识方法

@Configuration(proxyBeanMethods = true)
public class SpringConfig {
    @Bean
    public Cat cat(){
        return new Cat();
    }
    // 在配置类中多次调用Cat()方法得到的是同一个对象
    Cat();
    Cat();
}

第二步: 获取配置类的代理对象并多次调用配置类中使用了@Bean标识方法,最终得到的都是同一个对象(Spring Boot源码和MQ中都应用了此特性)

public class App33 {
    public static void main(String[] args) {
        // 此时创建容器时获取的是配置类的代理对象
        ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        String[] names = ctx.getBeanDefinitionNames();
        for (String name : names) {
            System.out.println(name);
        }
        System.out.println("-------------------------");
        SpringConfig springConfig = ctx.getBean("springConfig", SpringConfig.class);
        System.out.println(springConfig.cat());
        System.out.println(springConfig.cat());
        System.out.println(springConfig.cat());
    }
}

@ImportResource注解

由于早起开发的系统大部分都是采用XML的形式配置Bean,而现在的企业级开发基本采用注解形式,所以就需要两种模式同时使用

Spring提供了一个@ImportResource注解,在配置类上直接写上要被融合的XML配置文件名(类路径下)即可(注意导入的Bean可能发生冲突)

  • 加载的多个XML文件中的Bean冲突时后加载的Bean留下,加载的xml中的Bean和当前采用注解形式配置的Bean冲突时,以XML方式配置的Bean留下
@ImportResource("applicationContext1.xml")
public class SpringConfig {
}

<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="cat" class="Cat"/>

    
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"/>
    <bean class="com.alibaba.druid.pool.DruidDataSource"/>
beans>

@Import注解

相对于@Bean注解通过@Import注解加载第三方Bean更加简便,只需要在注解的参数中写上加载的类对应的Class对象即可(Bean的名称默认是类的全类名)

  • 优点: 无侵入编程,可以降低源代码与Spring技术的耦合度,对当前类的形式没有影响
  • 缺点: 扫描的时候不仅可以加载到你要的东西,还有可能加载到各种各样的乱七八糟的东西
@Import(Dog.class)
public class SpringConfig {
}

使用@Import注解还可以加载配置类(该配置类不需要使用@Configuration/Component注解声明),配置类里面使用了@Bean标识方法的返回对象也会注册成Bean

@Import(DbConfig.class)
public class SpringConfig { 
}

手动向容器中注册Bean

一般加载Bean的方式都是在容器启动阶段完成Bean的加载,但有时候也会在容器初始化完成后向容器中注册Bean

AnnotationConfigApplicationContext接口特有的方法

方法名 功能
register(Class) 向容器注册一个Bean不指定名称(默认名称是类名的首字母小写)
registerBean(Id,Class,构造方法参数可变) 向容器注册一个Bean并指定名称

如果注册的Bean的Id相同新添加的Bean就会覆盖原有的Bean(容器的底层类似Map集合),这样可以保证我们自己手动添加的配置覆盖原有的配置

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        // 上下文容器对象已经初始化完毕后,手工加载Bean
        ctx.register(Mouse.class);
        ctx.registerBean("tom", Cat.class,0);
        ctx.registerBean("tom", Cat.class,1);
        // 新添加的Bean会覆盖原有的Bean
        System.out.println(ctx.getBean(Cat.class));
    }
}

实现接口

ImportSelector

编写一个类重写ImportSelector接口的selectImports方法,返回一个String类型的数组,数组元素就是要注册Bean的全类名(也是注册Bean的名称)

AnnotationMetadata是元数据即@Import注解修饰的类如SpringConfig,通过元数据提供的方法我们可以做一些判断或者获取一些信息

方法名 功能
String getClassName() 获取元数据的简类名
boolean hasAnnotation(“”) 判断元数据上是否有某个注解
Map< String,Object > getAnnotationAttributes(“”) 获取元数据上某个注解的所有属性
@Configuration
//@ComponentScan(basePackages = "com.itheima")
@Import(MyImportSelector.class) 
public class SpringConfig { 
}
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        // 获取@ComponentScan注解的属性集合,判断注解是是否有某个属性
        Map<String, Object> attributes = metadata.getAnnotationAttributes("org.springframework.context.annotation.ComponentScan");
        // 根据各种条件判定是否装载指定的Bean,如判断元数据上是否有Configuration注解
        boolean flag = importingClassMetadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if(flag){
            return new String[]{"com.itheima.bean.Dog"};
        }
        return new String[]{"com.itheima.bean.Cat"};
    }
}   

ImportBeanDefinitionRegistrar

编写一个类重写ImportBeanDefinitionRegistrar接口的registerBeanDefinitions方法,该方法额外提供了一个参数BeanDefinition

BeanDefinition对象可以设置Bean初始化时的相关属性(如名称或是否单例),通过类的Class对象可以获得其对应的BeanDefinition对象(一个Bean对应一个)

@Import(MyRegistrar.class) 
public class SpringConfig { 
}
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        // 使用元数据判定是否加载Bean.....
        
        // 通过Bean的Class对象获得其对应的BeanDefinition对象
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl.class).getBeanDefinition();
        
        // 使用BeanDefinitionRegistry对象设置其对应Bean初始化时的名称
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

BeanDefinitionRegistryPostProcessor

如果使用不同方式加载了同种类型的Bean并且名称也相同,那么此时我们无法确定最后是使用的哪种方式向容器中注册的Bean(具体取决于导入Bean的顺序和方式)

重写BeanDefinitionRegistryPostProcessor接口的postProcessBeanDefinitionRegistry方法,实现对容器中Bean的最终裁定即之前的配置都会被覆盖

// 直接导入
@Service("bookService")
public class BookServiceImpl1 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 1..");
    }
}
// 使用MyRegistrar注册
public class BookServiceImpl2 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 2....");
    }
}
// 使用MyRegistrar2注册
public class BookServiceImpl3 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 3....");
    }
}
// 使用MyPostProcessor注册
public class BookServiceImpl4 implements BookSerivce {
    @Override
    public void check() {
        System.out.println("book service 4....");
    }
}
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}
public class MyRegistrar2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //1.使用元数据去做判定

        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 通过BeanDefinition的注册器注册实名Bean
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
        // 在postProcessBeanDefinitionRegistry方法中注册Bean的优先级最高
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}
// 使用import的方式导入BookService1,使用MyPostProcessor的方式导入BookService2,但它们的名称都为bookService
// 使用MyRegistrar注册Bean的优先级高会被留下,如果是相同的MyRegistrar与配置顺序有关,后面的覆盖前面的
// 使用MyPostProcessor.class注册Bean的优先级最高且与配置顺序无关,无论前面怎么配置都会覆盖
@Import({BookServiceImpl.class,MyRegistrar.class,MyRegistrar2,MyPostProcessor.class}) 
public class SpringConfig { 
}

你可能感兴趣的:(Bean,spring,java,数据库,Bean的获取方式,ImportSelector)