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类的无参数构造方法执行");}
}
<?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的名称--><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
<?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="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@79e2c065System.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
<?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--><bean id="orderFactory" class="com.powernode.spring6.bean.OrderFactory"/><!--告诉Spring框架调用哪个工厂对象的哪个实例方法获取Bean--><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@35cabb2aSystem.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接口的工厂Bean
,factory-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> {@Overridepublic Person getObject() throws Exception {// 对Bean创建前加工处理return new Person();// 对Bean创建后加工处理}@Overridepublic Class<?> getObjectType() {return null;}// 这个方法在接口中有默认实现所有可以不用实现@Overridepublic boolean isSingleton() {return true;}
}
第三步: 在spring.xml
配置文件注册实现了FactoryBean接口的工厂Bean
<?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,可以对普通的Bean进行加工处理并返回--><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@79e2c065System.out.println(personBean);// 默认工厂Bean创建的Bean是单例的Person personBean2 = applicationContext.getBean("personBean", Person.class);//com.powernode.spring6.bean.Person@79e2c065System.out.println(personBean2);
}
基于注解实例化
FactoryBean
接口也可以用于声明Bean, 实现了FactoryBean接口的类造出来的对象不是当前类的对象,而是FactoryBean接口泛型指定类型的对象
在FactoryBean接口的getObject
方法中完成对象的加工处理相对于在对象的构造方法中加工处理的优点
在对象的构造方法中加工处理
: 如果当前构造方法的初始化动作并不能满足你的需要,就需要重写一个构造方法在FactoryBean接口的getObject方法中加工处理
: 类是一个抽象后剥离的特别干净的模型, 根据情况不同在对象初始化前后设置不同的初始化动作
public class DogFactoryBean implements FactoryBean<Dog> {@Overridepublic Dog getObject() throws Exception {// 对象创建前做的事情Dog d = new Dog();// 对象创建后做的事情return d;}@Overridepublic Class<?> getObjectType() {return Dog.class;}@Overridepublic boolean isSingleton() {return true;}
}
使用@Bean
的形式创建FactoryBean接口实现类DogFactoryBean
的泛型对象,FactoryBean接口的实现类并不会被加载到容器
@ComponentScan({"com.itheima.bean","com.itheima.config"})
public class SpringConfig3 {@Beanpublic DogFactoryBean dog(){// 返回的是Dog类型的对象return new DogFactoryBean();}
}
注入自定义类型Date
需求: 为Student
对象注入一个Data类型
的日期作为生日
public class Student {private Date birth;public void setBirth(Date birth) {this.birth = birth;}@Overridepublic String toString() {return "Student{" +"birth=" + birth +'}';}
}
java.util.Date
在Spring中被当做简单类型
,所以在注入属性值的时候可以直接使用value属性或value标签
来完成
- 对Date类型属性赋值的时要求日期字符串的格式必须是指定格式
Mon Oct 10 14:30:26 CST 2022
, 如果是其他格式是不会被识别的
<?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"><!--调用Data的无参构造方法获取系统当前时间--><bean id="nowTime" class="java.util.Date"/><bean id="student" class="com.powernode.spring6.bean.Student"><!--把日期类型当做简单类型,要求日期格式必须是指定的才能为Date类型的属性赋值--><property name="birth" value="Mon Oct 10 14:30:26 CST 2022"/><!--把日期类型当做非简单类型,默认格式的日期无法为Date类型的属性赋值--><property name="birth" ref="nowTime"/></bean>
</beans>
第一步: 编写DateFactoryBean
实现FactoryBean<Date>接口
, 对java.util.Date
类型的普通Bean加工并交给容器管理
public class DateFactoryBean implements FactoryBean<Date> {// 定义Date属性接收日期字符串private String date;// 通过构造方法给Date属性赋值public DateFactoryBean(String date) {this.date = date;}// 对Date类型的Bean加工处理并交给容器管理@Overridepublic Date getObject() throws Exception {SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");return sdf.parse(this.date);}@Overridepublic Class<?> getObjectType() {return null;}
}
第二步: 编写spring.xml
的配置文件注册DateFactoryBean
,然后赋值给Student对象Date类型的属性(注入的是java.util.Date
类型的Bean)
<?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"><!--通过DateFactoryBean对java.util.Date类型的Bean做加工处理,最后返回给容器的也是java.util.Date类型的Bean--><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"><!--这里引用的是加工处理后的java.util.Date类型的普通bean--><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配置相结合的方式
<?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.xsdhttp://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容器管理
<?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.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"><!--告诉Spring框架要扫描哪些包中的类--><context:component-scan base-package="com.powernode.spring6.bean"> </context:component-scan>
</beans>
第三步: 根据Id从容器中获取对应的Bean
public class AnnotationTest {@Testpublic 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 {@Beanpublic 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 {@Beanpublic 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 {
}
<?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--><bean id="cat" class="Cat"/><!--声明第三方开发的bean--><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);// 上下文容器对象已经初始化完毕后,手工加载Beanctx.register(Mouse.class);ctx.registerBean("tom", Cat.class,0);ctx.registerBean("tom", Cat.class,1);// 新添加的Bean会覆盖原有的BeanSystem.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 {@Overridepublic 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 {@Overridepublic 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 {@Overridepublic void check() {System.out.println("book service 1..");}
}
// 使用MyRegistrar注册
public class BookServiceImpl2 implements BookSerivce {@Overridepublic void check() {System.out.println("book service 2....");}
}
// 使用MyRegistrar2注册
public class BookServiceImpl3 implements BookSerivce {@Overridepublic void check() {System.out.println("book service 3....");}
}
// 使用MyPostProcessor注册
public class BookServiceImpl4 implements BookSerivce {@Overridepublic void check() {System.out.println("book service 4....");}
}
public class MyRegistrar implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();registry.registerBeanDefinition("bookService",beanDefinition);}
}
public class MyRegistrar2 implements ImportBeanDefinitionRegistrar {@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {//1.使用元数据去做判定BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();registry.registerBeanDefinition("bookService",beanDefinition);}
}
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {@Overridepublic void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {// 通过BeanDefinition的注册器注册实名BeanBeanDefinition 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 {
}