文章目录
- 前言
- bean的8种加载方式
- 第一种方式 xml配置文件+bean
- 打印所有的bean
- 第二种方式 xml:context+注解(@Component+4个@Bean)
- 第三种方式 配置类+扫描+注解(@Component+4个@Bean)
- @Bean定义FactoryBean接口
- @ImportResource
- @Configuration注解的proxyBeanMethods属性
- 第四种方式 @Import导入类
- 第五种方式 AnnotationConfigApplicationContext调用registrer方法
- 第六种方式 @Import导入ImportSelector
- 第七种方式 @Import导入实现ImportBeanDefinitionRegistrar
- 第八种方式 @Import导入BeanDefinitionRegistryPostProcessor
前言
简述bean的几种加载方式
bean的8种加载方式
第一种方式 xml配置文件+bean
- 新建模块
2. 因为springboot是基于spring所开发的,所以这里引入spring的jar包
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
</dependencies>
- 配置spring的applicationContext1.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 name="dog" class="com.it2.bean.Dog" />
<bean class="com.it2.bean.Cat" />
</beans>
- 编写主类
public class App1 {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext1.xml");
System.out.println(context.getBean("dog"));
System.out.println(context.getBean(Cat.class));
}
}
- 运行项目
打印所有的bean
public class App1 {
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext1.xml");
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println("name:"+name+"---------type:"+context.getBean(name).getClass());
}
}
}
第二种方式 xml:context+注解(@Component+4个@Bean)
通过注解+扫描包的方式,加载bean,注解可以使用@Component,@Service,@Configuration,@Controller等
例如
@Component("cat123")
public class Cat {
}
配置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"
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
https://www.springframework.org/schema/context/spring-context.xsd">
<!--指定组件的扫描位置-->
<context:component-scan base-package="com.it2.bean"/>
</beans>
如何引入第三方的bean?
通过配置的方式,添加Bean,然后配置扫描即可。
添加bean
package com.it2.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
@Component
public class DbConfig {
@Bean
public DruidDataSource dataSource(){
DruidDataSource ds=new DruidDataSource();
return ds;
}
}
配置扫描增加com.it2.config
<!--指定组件的扫描位置-->
<context:component-scan base-package="com.it2.bean,com.it2.config"/>
运行后,可以看到项目所有被加载的bean
public static void main(String[] args) {
ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext2.xml");
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
}
第三种方式 配置类+扫描+注解(@Component+4个@Bean)
相较于第二种方式,第三种方式直接使用@CompoentScan替代了applicationContext.xml文件的配置
新增配置,在头部使用@ComponentScan 设定扫描范围
@ComponentScan({"com.it2"})
public class SpringConfig3 {
}
运行app3
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig3.class);
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
}
查看结果,com.it2下面被注解的内容全部被加载了。
注:
@Configuration配置项如果不用于被扫描可以忽略
@Bean定义FactoryBean接口
@Component
public class DogConfig {
@Bean
public Dog dog(){
return new Dog();
}
@Bean
public DogFactoryBean dog2(){
return new DogFactoryBean();
}
}
上面的代码,两个Bean,dog2创建的是Dog对象吗?
DogFactoryBean通过实现FactoryBean来实现Bean的创建。如下例子。
public class DogFactoryBean implements FactoryBean<Dog> {
public Dog getObject() throws Exception {
return new Dog();
}
public Class<?> getObjectType() {
return Dog.class;
}
/**
* 是否单例
* @return
*/
public boolean isSingleton() {
return false;
}
}
运行代码,查看。可以发现DogFactoryBean创建的也是Dog对象。
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig3.class);
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
System.out.println("------------");
System.out.println(context.getBean("dog"));
System.out.println("------------");
System.out.println(context.getBean("dog2"));
}
通过FactoryBean的方式创建Bean,可以在Bean的初始化时做一些其它事情,比如设置参数,进行参数检查等。
@ImportResource
如何基于旧系统进行二次开发?
通过@ImportResource导入第三方的配置文件,获取到第三方的bean,进行系统集成。
@ImportResource({"applicationContext1.xml"})
public class SpringConfig32 {
}
运行
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig32.class);
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
}
@Configuration注解的proxyBeanMethods属性
proxyBeanMethods 为true和false有什么区别
@Configuration(proxyBeanMethods = true) //proxyBeanMethods默认是true
public class SpringConfig33 {
@Bean
public Cat cat(){
return new Cat();
}
}
运行时,可以观察到它们是同一对象
proxyBeanMethods 改为false,再次运行,可以观察到每次获取到的cat对象一样。
这就是两者的不同。
只有proxyBeanMethods =true,并且里面的方法被@Bean标记,才能产生一个代理对象,否则每次都会得到不同的对象。
第四种方式 @Import导入类
使用@Import注解导入需要被注入的bean对应的字节码
@Import({Dog.class, Cat.class})
public class SpringConfig4 {
}
被导入的bean对象不需要做任何声明
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig4.class);
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
System.out.println("--------");
System.out.println(context.getBean(Dog.class));
}
运行代码,可以发现容器里包含了被import的对象
此形式可以有效的降低源代码与spring技术的耦合度,在spring技术底层以及很多框架的整合种大量使用
使用@Import注解导入配置类,配置类会被加载,配置类里被@Bean声明的方法也会被加载到容器。
第五种方式 AnnotationConfigApplicationContext调用registrer方法
当系统上下文已经初始化完毕后,如何向容器中注入bean?
使用register注入bean
public static void main(String[] args) {
AnnotationConfigApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig5.class);
//上下文容器对象已经初始化完毕后,手工加载bean
context.registerBean("ddd", Dog.class);
System.out.println(context.getBean("ddd"));
context.registerBean("ddd", Dog.class);
System.out.println(context.getBean("ddd"));
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
}
运行后,可以观察到ddd这个对象,使用了context.registerBean(“ddd”, Dog.class)注入bean后,获取可以get到这个bean,同时再次注入同名的bean,可以看到前面的bean会被覆盖。
当然也可以使用类名注册,不指定名称
context.register(Mouse.class);
使用register这种方式,只有AnnotationConfigApplicationContext可以做,其它的ApplicationContext 和ClassPathXmlApplicationContext都不能做。
第六种方式 @Import导入ImportSelector
public class MyImportSelector implements ImportSelector {
public String[] selectImports(AnnotationMetadata annotationMetadata) {
/**
* annotationMetadata 注解的元数据,表使用使用@Import(MyImportSelector.class)的类
* annotation 翻译 注解
* Metadata 元数据
*/
System.out.println("--"+annotationMetadata.getClassName());
System.out.println("--"+annotationMetadata.getAnnotationTypes());
boolean flag=annotationMetadata.hasAnnotation("org.springframework.context.annotation.Configuration");
if (flag){
return new String[]{"com.it2.bean.Cat"};
}
return new String[]{"com.it2.bean.Dog"};
}
}
@Configuration //用来测试的注解
@Import(MyImportSelector.class)
public class SpringConfig6 {
}
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig6.class);
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
}
运行代码,可以看到加载了Cat,MyImportSelector 可以根据注解的导入情况,进行判断,加载不同的bean,这个在Springboot整合其它框架时很常见。
导入实现了ImportSelector接口的类,实现对导入元的编程式处理
第七种方式 @Import导入实现ImportBeanDefinitionRegistrar
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
/**
* 1. 使用元数据判定
*/
/**
* 2. 注入一个BeanDefinition
*/
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
registry.registerBeanDefinition("hellodog", beanDefinition);
}
}
@Import(MyRegistrar.class)
public class SpringConfig7 {
}
public class App7 {
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig7.class);
String[] names= context.getBeanDefinitionNames();
for (String name:names){
System.out.println(name);
}
}
}
打印输出bean,可以看到自己定义的bean被注入到容器
导入实现ImportBeanDefinitionRegistrator接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中的bean的裁定,例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果。
同名bean中ImportBeanDefinitionRegistrar后覆盖前,所以多个ImportBeanDefinitionRegistrar 时,最后一个生效。
第八种方式 @Import导入BeanDefinitionRegistryPostProcessor
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition();
beanDefinitionRegistry.registerBeanDefinition("bookService", beanDefinition);
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
/**
* 1. 使用元数据判定
*/
/**
* 2. 注入一个BeanDefinition
*/
// BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
// registry.registerBeanDefinition("hellodog", beanDefinition);
BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();
registry.registerBeanDefinition("bookService", beanDefinition);
}
}
@Import({BookServiceImpl1.class, MyPostProcessor.class, MyRegistrar.class})
public class SpringConfig8 {
}
public static void main(String[] args) {
ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig8.class);
BookService bookService= context.getBean("bookService",BookService.class);
bookService.query();
}
运行代码,可以发现bookService 的实现类时BookServiceImpl4,
当我们将SpringConfig8修改,更改MyPostProcessor和MyRegistrar的位置,运行结果依然是BookServiceImpl4
因此使用BeanDefinitionRegistryPostProcessor来定义bean,可以保证一锤定音,可以保证bean不会被替换。