【Spring】Bean的八种加载方式

一、XML方式声明bean

配置文件

1.在xml中声明自定义的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">

    <!--xml方式声明自定义的bean-->
    <bean id="cat" class="com.huangzx.bean.Cat"></bean>  <!--命名id-->
    <bean class="com.huangzx.bean.Dog"></bean>  <!--未命名id-->
    <bean class="com.huangzx.bean.Dog"></bean>
    <bean class="com.huangzx.bean.Dog"></bean>

指定bean输出 

 输出所有的bean

输出自定义bean结果

 

2.在xml中声明第三方bean

# 首先在pom.xml中导入第三方bean坐标
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.2.9</version>
</dependency>
<!--在配置文件中  xml方式声明第三方bean-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"></bean>
    <bean class="com.alibaba.druid.pool.DruidDataSource"></bean>
    <bean class="com.alibaba.druid.pool.DruidDataSource"></bean>

输出所有bean结果 

 

 二、XML+注解方式声明bean

进行包扫描,查找需要加载的bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--xml方式声明自己开发的bean-->
    <!--指定加载bean的位置,component-->
    <context:component-scan base-package="com.huangzx.bean,com.huangzx.config"></context:component-scan>


</beans>

 对需要加载的bean进行注解,使用@Component及其衍生注解@Controller 、@Service、@Repository,@Configuration定义bean

 

 

三、注解方式声明配置类

 

 @Configuration配置项如果不用于被扫描可以省略

这里使用的是AnnotationConfigApplicationContext加载配置 

1.拓展1 初始化实现FactoryBean接口的类,实现对bean加载到容器之前的批处理操作

 

使用@ComponentScan进行包扫描加载配置bean 

 

输出返回的bean不是DogFactoryBean,而是dog 

 ​​​​​​输出加载bean结果

 重写FactoryBean<>中的isSingleleton方法,返回false,所以生成的对象非单例

 2.扩展2  加载配置类并加载配置文件

 用于使用注解加载bean时,如何加载之前使用xml文件配置的bean

使用@ImportResource加载xml配置文件

 

 3.扩展3 proxyBeanMethods

使用proxyBeanMethods=true可以保障调用此方法得到的对象是从容器中获取的而不是重新创建的 

 当proxyBeanMethods=false时输出结果

 

 

  当proxyBeanMethods=true时输出结果。proxyBeanMethodtrue

 

 四、使用@Import注解导入要注入的bean

被导入的bean无需使用注解声明为bean

此形式可以有效的降低源代码与Spring技术的耦合度,在spring技术底层及诸多框架的整合中大量使用

 

 输出bean的全路径类名

 扩展  使用@Import注解导入其他配置类,配置类无需使用注解,也可加载出来

 

 五、使用上下文对象在容器初始化完毕后注入bean

使用 AnnotationConfigApplicationContext,在上下文对象初始化完毕后,手工加载bean

输出结果,相同的bean,后面的会把前面的覆盖

 

六、@Import导入ImportSelector接口

 

 结果

 在某类上@Import导入ImportSelector配置类,可以检查该类的某些属性

 

package com.huangzx.bean;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.util.MultiValueMap;

public class MyImportSelector implements ImportSelector {
    @Override
    public String[] selectImports(AnnotationMetadata Metadata) {
        System.out.println("--------------------------");
        //检查加载该配置的类
        System.out.println(Metadata.getClassName());
        //检测加载该配置的类是否有注解@Configuration
        System.out.println(Metadata.hasAnnotation("org.springframework.context.annotation.Configuration"));
        //检查加载该配置的类是否有@ComponentScan属性
        MultiValueMap<String, Object> attributes = Metadata.getAllAnnotationAttributes("org.springframework.context.annotation.ComponentScan");
        System.out.println(attributes);
        System.out.println("--------------------------");

        //完成条件检查后,加载指定的bean
        boolean flag = Metadata.hasAnnotation("org.springframework.context.annotation.Configuration");
        if(flag){
            return new String[] {"com.huangzx.bean.Dog"};
        }
        return new String[] {"com.huangzx.bean.Cat"};
    }
}

 结果

--------------------------
com.huangzx.config.SpringConfig6
true
{basePackageClasses=[[Ljava.lang.Class;@53f65459], basePackages=[[Ljava.lang.String;@3b088d51], excludeFilters=[[Ljava.util.LinkedHashMap;@1786dec2], includeFilters=[[Ljava.util.LinkedHashMap;@74650e52], lazyInit=[false], nameGenerator=[interface org.springframework.beans.factory.support.BeanNameGenerator], resourcePattern=[**/*.class], scopeResolver=[class org.springframework.context.annotation.AnnotationScopeMetadataResolver], scopedProxy=[DEFAULT], useDefaultFilters=[true], value=[[Ljava.lang.String;@15d0c81b]}
--------------------------
org.springframework.context.annotation.internalConfigurationAnnotationProcessor
org.springframework.context.annotation.internalAutowiredAnnotationProcessor
org.springframework.context.annotation.internalCommonAnnotationProcessor
org.springframework.context.event.internalEventListenerProcessor
org.springframework.context.event.internalEventListenerFactory
springConfig6
com.huangzx.bean.Dog

Process finished with exit code 0

七、@Import导入ImportBeanDefinitionRegistrar接口

 导入实现了ImportBeanDefinitionRegistrar接口的类,通过BeanDefinition的注册器注册实名bean,实现对容器中bean的裁定,例如对现有bean的覆盖,进而达成不修改源代码的情况下更换实现的效果

package com.huangzx.bean;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        //首先使用元数据做判定,然后再导入需要的bean
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(Dog.class).getBeanDefinition();
        registry.registerBeanDefinition("yyf",beanDefinition);
    }
}

 

结果

 

 八、@Import导入BeanDefinitionRegistryPostProcessor接口

 导入实现了BeanDefinitionRegistryPostProcessor接口的类,通过BeanDefinition的注册器注册实名bean, 实现对容器中bean的最终裁定

Service接口:BookService

Service实现:BookServiceImpl1、BookServiceImpl2、BookServiceImpl3、BookServiceImpl4

 1.@Import导入BookServiceImpl1.class

结果

 

 2.创建配置类定义bean BookServiceImpl2,并导入该类

package com.huangzx.bean;
import com.huangzx.service.impl.BookServiceImpl2;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyRegistrar_PostProcessor1 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl2.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

 

 结果

 3.创建配置类定义bean BookServiceImpl3,并导入该类

package com.huangzx.bean;
import com.huangzx.service.impl.BookServiceImpl3;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyRegistrar_PostProcessor2 implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl3.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }
}

 结果        MyRegistrar_PostProcessor1和MyRegistrar_PostProcessor2的导入顺序,决定了执行这两个中的哪个实现类,后导入的会覆盖之前导入的

4.定义后配置类MyPostProcessor,并定义bean  BookServiceImpl4

package com.huangzx.bean;

import com.huangzx.service.impl.BookServiceImpl4;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;

public class MyPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BookServiceImpl4.class).getBeanDefinition();
        registry.registerBeanDefinition("bookService",beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {

    }
}

 @Import导入MyPostProcessor

package com.huangzx.config;
import com.huangzx.bean.MyPostProcessor;
import com.huangzx.bean.MyRegistrar_PostProcessor1;
import com.huangzx.bean.MyRegistrar_PostProcessor2;
import com.huangzx.service.impl.BookServiceImpl1;
import org.springframework.context.annotation.Import;

@Import({BookServiceImpl1.class, MyRegistrar_PostProcessor1.class, MyRegistrar_PostProcessor2.class, MyPostProcessor.class})
public class SpringConfig8 {

}

结果      执行BookServiceImpl4  与是否最后导入无关

 

 

能够选择性加载控制bean的方式是:

AnnotationConfigApplicationContext调用register方法 

@Import导入ImportSelector接口 

@Import导入ImportBeanDefinitionRegistrar接口 

@Import导入BeanDefinitionRegistryPostProcessor接口 

以ImportSelector为例

1.根据任意条件确认是否加载bean

2. 使用@Conditional注解的派生注解设置各种组合条件控制bean的加载

          使用@ConditionalOn***注解为bean的加载设置条件

package com.huangzx.config;
import com.huangzx.bean.Cat;
import com.huangzx.bean.Mouse;
import org.springframework.boot.autoconfigure.condition.ConditionalOnBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnWebApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;

//@Import(MyImportSelector.class)
@Import(Mouse.class)
public class SpringConfig {

    @Bean
    //@ConditionalOnClass(Mouse.class)  一般不使用这种方式
    //@ConditionalOnClass(name = "com.huangzx.bean.Mouse") //有Mouse时加载
    //@ConditionalOnMissingClass("com.huangzx.bean.Wolf")  //没有Wolf时加载

    @ConditionalOnBean(name = "jerry")  //有Mouse  bean,且Mouse的名字是jerry时加载
    @ConditionalOnMissingClass("com.huangzx.bean.Dog") //有Mouse  bean,且Mouse的名字是jerry时加载,且没有Dog时加载

    @ConditionalOnWebApplication //是web程序时加载
    public Cat tom(){
        return new Cat();
    }
}

3. 指定类上根据条件选择性加载

 后续可以单独配置bean,当有某种环境时,加载某些bean

这里当有mysql数据库连接时,加载Druid数据源

public class SpringConfig {
    @Bean
    @ConditionalOnClass(name = "com.mysql.jdbc.Driver")
    public DruidDataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        return ds;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值