SpringBoot之bean的多种加载方式

1、xml配置文件

配置文件spring-bean.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.xc.springboot.bean.Cat"/>
    <bean class="com.xc.springboot.bean.Cat"/>
    <bean class="com.xc.springboot.bean.Cat"/>

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

加载xml

public class App {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-bean.xml");
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.err.println(beanDefinitionName);
        }
    }
}

输出结果:

cat
com.xc.springboot.bean.Cat#0
com.xc.springboot.bean.Cat#1
dataSource

2、注解定义bean

2.1、使用AnnotationConfigApplicationContext对象加载

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.err.println(beanDefinitionName);
        }
    }
}
public class MyConfig {
}
  • 使用AnnotationConfigApplicationContext对象加载MyConfig
  • 即使MyConfig类什么注解没有,也会被注册为bean

2.2、加载本地类

  • 使用的注解有@Component或三个衍生注解@Service、@Controller、@Repository
@Service
public class BookServiceImpl implements BookService {
}

2.3、加载第三方jar类

  • 由于我们无法在第三方提供的技术源代码中去添加上述4个注解
  • 因此当你需要加载第三方开发的bean的时候可以使用下列方式定义注解式的bean
  • 使用@Component和@Configuration都可以,一般引入第三方倾向于后者
//@Component
@Configuration
public class DbConfig {
    @Bean
    public DruidDataSource dataSource(){
        return new DruidDataSource();
    }
    @Bean
    public Cat cat(){
    	Cat cat = new Cat();
    	cat.setDruidDataSource(dataSource());
    	return cat; 
    }
}
  • @Configuration(proxyBeanMethods = true):默认设置,使用了cglib动态代理,cat里的dataSource和@Bean创建的dataSource是同一个对象,可以理解为单例
  • @Configuration(proxyBeanMethods = false):此时和@Component注解功能一样,cat里的dataSource和@Bean创建的dataSource不是同一个对象,可以理解为多例
  • 如果配置中@Bean标识的方法之间不存在依赖调用的话,可以设置为false,可以避免拦截方法进行代理操作,提升性能

3、特殊方式

3.1、使用FactroyBean接口

  • spring提供了一个接口FactoryBean,也可以用于声明bean
  • 实现了FactoryBean接口的类造出来的对象不是当前类的对象,而是FactoryBean接口泛型指定类型的对象
  • 一般用来创建复杂对象
public class DogFactoryBean implements FactoryBean<Dog> {
    //创建bean的复杂过程
    @Override
    public Dog getObject() throws Exception {
        Dog d = new Dog();
        //.........
        return d;
    }
    //bean的类型
    @Override
    public Class<?> getObjectType() {
        return Dog.class;
    }
    //bean是否单例
    @Override
    public boolean isSingleton() {
        return true;
    }
}

配置类

public class MyConfig {
    @Bean
    public DogFactoryBean dog(){
        return new DogFactoryBean();
    }
}

启动类

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        System.out.println(context.getBean("dog"));
    }
}

输出结果:

com.xc.springboot.bean.Dog@33cb5951

3.2、注解格式导入XML格式配置的bean

  • 场景:旧项目xml配置bean融入配置类项目中
  • @ImportResource,在配置类上直接写上要被融合的xml配置文件名即可
@Configuration
@ImportResource("spring-bean.xml")
public class SpringConfig {
}

3.3、通过上下文注册bean

  • 在容器初始化完成后手动加载bean
  • 创建方式很多,这里简单说两种

在这里插入图片描述

方式一:无参构造

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
        //上下文容器对象已经初始化完毕后,手工加载bean
        ctx.register(Mouse.class);
    }
}

方式二:多参构造

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

4、@Import注解注入bean

注意: 通过@Import导入的bean名称为全路径名

4.1、@Import导入普通类

  • 场景:将一个无任何注解的类加载为bean,解耦
  • @Improt只能用一次,多个使用{…,…}
  • 只有MyConfig加载为bean,@Import才生效
@Configuration
//@Import(Pig.class)
@Import({Dog.class,Cat.class})
public class MyConfig {
}
public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.err.println(beanDefinitionName);
        }
    }
}

输出结果:

myConfig
com.xc.springboot.bean.Dog
com.xc.springboot.bean.Cat

4.2、@Import导入实现了ImportSelector接口的类

  • 可以通过添加if语句就可以实现bean的加载控制
  • 返回值为多个全路径类名字符串
  • metadata为@Import注解类的元数据,可以拿到MyConfig类所有相关的数据 (类上所有的注解,注解里的属性,继承的接口,父类等等信息)
  • 如下则是判断MyCofig类上有@Configuration注解则加载Dog类,否则加载Cat类
public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
        System.out.println("元数据Class名称:"+metadata.getClassName());
        //各种条件的判定,判定完毕后,决定是否装载指定的bean
        boolean flag = metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if(flag){
            return new String[]{"com.xc.springboot.bean.Dog"};
        }
        return new String[]{"com.xc.springboot.bean.Cat"};
    }
}

配置类

@Configuration
@Import(MyImportSelector.class)
public class MyConfig {

}

启动类

public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class);
        for (String beanDefinitionName : context.getBeanDefinitionNames()) {
            System.err.println(beanDefinitionName);
        }
    }
}

输出结果:

元数据Class名称:com.xc.springboot.bean.MyConfig
myConfig
com.xc.springboot.bean.Dog

4.3、@Import导入实现了ImportBeanDefinitionRegistrar接口的类

  • 返回值为void,通过Bean定义(beanDefinition)注册创建新的bean
  • 同样可以通过添加if语句就可以实现bean的加载控制,更加细粒度判断bean
public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
        BeanDefinition beanDefinition = 	
            BeanDefinitionBuilder.rootBeanDefinition(BookService.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

4.4、@Import导入实现了BeanDefinitionRegistryPostProcessor接口的类

  • 以上导入方式可以对某种类型的bean被接二连三的使用各种方式加载
  • 如果想bean最后确定一个值,可以在这里操作
  • BeanDefinitionRegistryPostProcessor:bean定义注册最后的处理器(在以上处理后执行此操作)
public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = 
            BeanDefinitionBuilder.rootBeanDefinition(BookService.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

5、Bean的加载控制

5.1、纯注解方式判断控制

  • @Import导入实现不同接口的类的方式可以代码方式一些列判断bean是否加载
  • springboot则提供很多@Conditional*注解帮助我们实现,我们只需要添加一下参数即可
  • 放在类上搭配@Component使用,放在方法上搭配@Bean使用
  • 如下:bean或Class存在或找不到

在这里插入图片描述

  • @ConditionalOnClass注解实现了当虚拟机中加载了com.xc.bean.Wolf类时加载对应的bean
@Bean
@ConditionalOnClass(name = "com.xc.bean.Wolf")
public Cat tom(){
    return new Cat();
}
  • @ConditionalOnMissingClass注解控制虚拟机中没有加载指定的类才加载对应的bean
@Bean
@ConditionalOnMissingClass("com.xc.bean.Dog")
public Cat tom(){
    return new Cat();
}

这种条件还可以做并且的逻辑关系,写2个就是2个条件都成立,写多个就是多个条件都成立

@Bean
@ConditionalOnClass(name = "com.xc.bean.Wolf")
@ConditionalOnMissingClass("com.xc.bean.Mouse")
public Cat tom(){
    return new Cat();
}
  • springboot通过jar类的加载判断是否创建对象
  • 判定当前是否加载了mysql的驱动类,如果加载了,我就给你搞一个Druid的数据源对象出来
public class SpringConfig {
    @Bean
    @ConditionalOnClass(name="com.mysql.jdbc.Driver")
    public DruidDataSource dataSource(){
        return new DruidDataSource();
    }
}

5.2、bean的依赖yml属性配置对象的管理

yml配置对象

cartoon:
  cat:
    name: "图多盖洛"
    age: 5
  mouse:
    name: "泰菲"
    age: 1

定义一个封装属性的专用类,加载配置属性,读取对应前缀相关的属性值

@ConfigurationProperties(prefix = "cartoon")
@Data
public class CartoonProperties {
    private Cat cat;
    private Mouse mouse;
}
  • 最后在使用的位置注入对应的配置即可
@EnableConfigurationProperties(CartoonProperties.class)
public class CartoonCatAndMouse{
    @Autowired
    private CartoonProperties cartoonProperties;
}
  • 在业务需要的类上使用@EnableConfigurationProperties声明bean
  • 这样在不使用这个类的时候,也不会无故加载专用的属性配置类CartoonProperties,减少spring管控的资源数量
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

冬天vs不冷

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值