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类的无参数构造方法执行");}
}
<?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接口的工厂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> {@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 { 
}

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

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

相关文章

【Amazon】云上探索实验室—了解 AI 编程助手 Amazon Codewhisperer

文章目录 一、前言&#x1f4e2;二、关于云上探索实验室&#x1f579;️三、领学员需要做什么&#xff1f;✴️四、领学员能获得什么&#xff1f;&#x1f523;五、学课通道入口&#x1f447;1️⃣CSDN平台2️⃣网易云课堂3️⃣Skill Builder 平台 六、活动详情链接 一、前言&a…

Qt QWebEngine 加载网页及交互,实现C++与JS 相互调用

目录 前言1、QtWebEngine介绍2、安装3、核心类介绍3.1 QWebEngineView3.2 QWebEnginePage3.3 QWebEngineProfile3.4 QWebEngineHistory3.5 QWebEngineSettings 4、加载网页5、C调用JS5.1 无返回值5.2 有返回值 6、JS调用C6.1 新建WebObject 类继承自QObject。6.2 将WebObject对…

spring boot validation使用

spring-boot-starter-validation 是 Spring Boot 中用于支持数据验证的模块。它建立在 Java Validation API&#xff08;JSR-380&#xff09;之上&#xff0c;提供了一种方便的方式来验证应用程序中的数据。以下是使用 spring-boot-starter-validation 的基本方法&#xff1a; …

11 Go的作用域

概述 在上一节的内容中&#xff0c;我们介绍了Go的映射&#xff0c;包括&#xff1a;声明映射、初始化映射、操作映射等。在本节中&#xff0c;我们将介绍Go的作用域。在Go语言中&#xff0c;作用域是指变量的可见性范围&#xff0c;它定义了变量在程序中的生命周期和可访问性。…

c语言从入门到实战——基于指针的数组与指针数组

基于指针的数组与指针数组 前言1. 数组名的理解2. 使用指针访问数组3. 一维数组传参的本质4. 冒泡排序5. 二级指针6. 指针数组7. 指针数组模拟二维数组 前言 指针的数组是指数组中的元素都是指针类型&#xff0c;它们指向某种数据类型的变量。 1. 数组名的理解 我们在使用指针…

MongoDB是一个NoSQL数据库,有着多种不同的命令和操作。以下是一些常见的MongoDB命令:

show dbs&#xff1a;列出所有数据库use db_name&#xff1a;切换到指定的数据库db.dropDatabase()&#xff1a;删除当前数据库db.createCollection("collection_name")&#xff1a;创建集合db.collection_name.insert(document)&#xff1a;向指定集合插入数据db.co…

手机厂商参与“百模大战”,vivo发布蓝心大模型

在2023 vivo开发者大会上&#xff0c;vivo发布自研通用大模型矩阵——蓝心大模型&#xff0c;其中包含十亿、百亿、千亿三个参数量级的5款自研大模型&#xff0c;其中&#xff0c;10亿量级模型是主要面向端侧场景打造的专业文本大模型&#xff0c;具备本地化的文本总结、摘要等…

【PyQt】(自制类)处理鼠标点击逻辑

写了个自认为还算不错的类&#xff0c;用于简化mousePressEvent、mouseMoveEvent和mouseReleaseEvent中的鼠标信息。 功能有以下几点&#xff1a; 鼠标当前状态&#xff0c;包括鼠标左/中/右键和单击/双击/抬起鼠标防抖(仅超出一定程度时才判断鼠标发生了移动)&#xff0c;灵…

【算法练习Day48】回文子串最长回文子序列

​&#x1f4dd;个人主页&#xff1a;Sherry的成长之路 &#x1f3e0;学习社区&#xff1a;Sherry的成长之路&#xff08;个人社区&#xff09; &#x1f4d6;专栏链接&#xff1a;练题 &#x1f3af;长路漫漫浩浩&#xff0c;万事皆有期待 文章目录 回文子串最长回文子序列总结…

深入理解Java中的String.join方法

在 Java 编程中&#xff0c;字符串操作是非常常见的需求。在 Java 8 中引入了一个方便的字符串连接方法 String.join&#xff0c;它能够简洁而高效地将多个字符串连接起来。本篇博客将深入介绍 String.join 方法的使用和原理。 什么是String.join方法&#xff1f; String.join…

百度文心一言

1分钟了解一言是谁&#xff1f; 一句话介绍【文心一言】 我是百度研发的人工智能模型&#xff0c;任何人都可以通过输入【指令】和我进行互动&#xff0c;对我提出问题或要求&#xff0c;我能高效地帮助你们获取信息、知识和灵感哦 什么是指令&#xff1f;我该怎么和你互动&am…

2023年03月 Python(五级)真题解析#中国电子学会#全国青少年软件编程等级考试

Python等级考试(1~6级)全部真题・点这里 一、单选题(共25题,每题2分,共50分) 第1题 已知一个列表lst = [2,3,4,5,6],lst.append(20),print(lst)的结果是?( )(2分) A.[10,2,3,4,5,6,20] B.[20,2,10,3,4,5,6] C.[2,3,4,5,6,20] D.[2,3,4,5,6,10,20] 答案:C 第2…

Vatee万腾的数字化掌舵:Vatee科技引领未来的新高度

随着数字化时代的到来&#xff0c;Vatee万腾以其卓越的科技决策力成为引领潮流的掌舵者。 Vatee万腾通过对科技前沿的不懈探索&#xff0c;站在了数字化创新的最前沿。其领先的科技团队致力于发掘并应用最新的数字技术&#xff0c;为用户提供卓越的数字化体验。 Vatee万腾以其…

HarmonyOS元服务实现今天吃什么

一、前言 作为一个职业打工人&#xff0c;每天点外卖吃啥东西都有选择综合症&#xff0c;突发奇想让程序帮我们随机选择一个吃的&#xff0c;是不是可以解决我们的选择问题呢&#xff0c;说干就干&#xff0c;我们就使用HarmonyOS元服务实现一下这个功能。为什么选择这个Harmon…

wpf devexpress设置行和编辑器

如下教程示范如何计算行布局&#xff0c;特定的表格单元编辑器&#xff0c;和格式化显示值。这个教程基于前一个文章 选择行显示 GridControl为所有字段生成行和绑定数据源&#xff0c;如果AutoGenerateColumns 属性选择AddNew。添加行到GridControl精确显示为特别的几行设置。…

联想小新Pro14默认设置的问题

联想小新Pro14 锐龙版&#xff0c;Win11真的挺多不习惯的&#xff0c;默认配置都不符合一般使用习惯。 1、默认人走过自动开机。人机互动太强了&#xff1b; 2、默认短超时息屏但不锁屏&#xff0c;这体验很容易觉得卡机然后唤起&#xff0c;却又不用密码打开&#xff1b; 3…

[论文分享] Never Mind the Malware, Here’s the Stegomalware

Never Mind the Malware, Here’s the Stegomalware [IEEE Security & Privacy 2022] Luca Caviglione | National Research Council of Italy Wojciech Mazurczyk | Warsaw University of Technology and FernUniversitt in Hagen 近年来&#xff0c;隐写技术已逐渐被观…

kubernetes istio

目录 一、部署 二、部署示例应用 三、部署遥测组件 四、流量管理 五、熔断 官网&#xff1a;https://istio.io/latest/zh/about/service-mesh/ 一、部署 提前准备好文件 tar zxf 15t10-1.19.3-linux-amd64.tar.gz cd 15t10-1.19.3/ export PATH$PWD/bin:$PATHistioctl install …

python生成n个指定范围内的随机数 tensor转list list中的小数保留两位小数 根据零样本学习的S和H和偏移量计算H的值

python生成n个随机数 import randomdef get_random_num1(n5,m2):# 生成4个-1到1之间的随机小数&#xff0c;包含左边的元素&#xff0c;不包含右边的元素random_numbers [round(random.uniform(-2, 0), 2) for _ in range(n)]random_numbers[m] 0return random_numbers# 生成…

刷题笔记(第七天)

1.找出对象 obj 不在原型链上的属性(注意这题测试例子的冒号后面也有一个空格~) 返回数组&#xff0c;格式为 key: value结果数组不要求顺序 输入&#xff1a; var C function() {this.foo ‘bar’; this.baz ‘bim’;}; C.prototype.bop ‘bip’; iterate(new C()); 输出…