spring bean加载过程_Spring的Bean加载容器机制

46894281a483bb88ba3fecdd1e4b6d63.png
  1. xml配置文件加载的容器;
  2. 通过注解加载的容器;

xml容器用如下方式获得:

ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");

通过XML添加的所有组件只会都在该容器中,用注解容器是拿不到对应的实例对象。同理,用注解添加的组件也只会在注解容器中。

注解容器通过如下方式获得:

AnnotationConfigApplicationContext context =                new AnnotationConfigApplicationContext(                MainConfig.class,                OtherConfig.class,                 ImportConfig.class);

如果想精确的控制可以调用AnnotationConfigApplicationContext的无参构造器,然后手动调用里面的方法;如下:

AnnotationConfigApplicationContext context =                 new AnnotationConfigApplicationContext();        context.register(MainConfig.class);        context.register(MainConfig.class);        context.register(MainConfig.class);        context.refresh();

分步调用可以在register()执行完后进行额外的处理在调用refresh()。后面说到这些额外的处理。

注入组件的四种方式

  1. @Configuration + @Bean
    导入的第三方包里面的组件。因为我们无法直接在三方代码上加@Component。
  2. @ComponentScans + @ComponentScan +@Component
    直接在代码上添加如上注解,导入自己写的类。
  3. @Import 快速给容器中导入一个组件
    a. @Import(WantImportClass.class);容器中自动注册这个组件,id默认是全类名
    b. 协议ImportSelector:返回需要导入的组件的全类名数组;
    c. 协议ImportBeanDefinitionRegistrar:手动注册bean到容器中
  4. 使用Spring提供的 FactoryBean(工厂Bean);
    a. 默认获取到的是工厂bean调用getObject创建的对象
    b. 要获取工厂Bean本身,我们需要给id前面加一个&colorFactoryBean

方式一 @Configuration @Bean

该方式主要用来导入三方的类作为Bean添加IoC容器中。因为我们自己的类通常是在代码上添加@Component和@ComponentScan直接把类注入到IoC容器中。使用时通常有几个Config类,用来描述Bean组件。注意,需要手动调用AnnotationConfigApplicationContext的register方法将配置类加载到IoC容器中,当然调用Context的有参构造器也可以。

@Configuration

@Configuration继承自@Component,说明用该注解的修饰类本身也会作为组件添加的IoC容器中。其作用范围是@Target({ElementType.TYPE}),也就是不能修饰方法上,通常用于修饰类,并在该配置类中声明要加载的bean。使用如下:

@Configurationpublic class MainConfig {    @Bean(value = "tom")    public Person person01 (){        return person02();    }    @Bean(value = "jack")    public Person person02 (){        return new Person("jack",20);    }}

注意,如果类上没有@Configuration,Bean会议'lite Mode'的模式加载到IoC容器中。在lite Mode中tom和jack是不同的实例对象。有@Configuration注解,tom和jack则是同一个实例对象。 详见官方文档

@Bean

@Bean的作用范围是@Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE}),通常情况下用于修饰有对象返回值的方法。 @Bean常用三个属性:

  1. value或name;
    使用:@Bean("person")。如果value为空,默认用方法名作为id。
  2. initMethod;
  3. destroyMethod;

initMethod和destroyMethod这两个方法是用来指定bean的初始化之后和销毁之前调用的方法。会在Bean的生命周期在讨论。

方式二 @ComponentScans + @ComponentScan +@Component

该方式主要是将自己代码中的类注入成bean组件。

@ComponentScans + @ComponentScan

扫描制定路径下的所有类,将带有@Component注解的类注入成Bean组件。这两个注解的使用范围都是ElementType.TYPE,通常都是加在类上,Spring建议同时加上@Configuration注解使用。示例:

//告诉Spring这是一个配置类@Configuration  @ComponentScans(        value = {                @ComponentScan(value={                "com.springDemo.Bean",                "com.springDemo.Controller"}                includeFilters = {            @Filter(type=FilterType.ANNOTATION,classes={Controller.class}),                        @Filter(type=FilterType.ASSIGNABLE_TYPE,classes={BookService.class}),                        @Filter(type=FilterType.CUSTOM,classes={MyTypeFilter.class})                },                useDefaultFilters = false)        })public class MainConfig {    // other bean ...}

@ComponentScans的value是一个@Component的数组,用以描述多个组件扫描的规则。 @ComponentScan主要有以下四个属性:

  1. value : 指定要扫描的包,值是String数组;
  2. excludeFilters = Filter[] :指定扫描的时候按照什么规则排除那些组件;
  3. includeFilters = Filter[] :指定扫描的时候只需要包含哪些组件;
  4. useDefaultFilters

默认只指定value的值,会将指定包中所有带@Component的类注入成Bean。如果不想用默认的扫描规则需要useDefaultFilters = false。同时指定excludeFilters或includeFilters。

@Filter

过滤规则使用@Filter注解。该注解用以详细描述过滤的规则,有以下5个值。常用的是FilterType.CUSTOM。这里不对过滤规则多详细介绍。

  1. FilterType.ANNOTATION:按照注解
  2. FilterType.ASSIGNABLE_TYPE:按照给定的类型;
  3. FilterType.ASPECTJ:使用ASPECTJ表达式
  4. FilterType.REGEX:使用正则指定
  5. FilterType.CUSTOM:使用自定义规则

@Component

该注解的范围是ElementType.TYPE,通常只作用在类上。使用如下:

// @Component("customAnimalName")@Componentpublic class Animal {    // ... }

通常不需要指定Value的值,bean的id默认是类名。value值,用以改变bean id。

方式三 @Import

使用Import管理配置类

假设有N个配置类,我们可以通过在某一个配置类中用@Import其他所有配置类,这样在AnnotationConfigApplicationContext注册时就只要注册一个配置类即可,不需要记住那么多配置类。 使用如下:

@Configurationpublic class ConfigA {    @Bean    public A a() {        return new A();    }}@Configuration@Import(ConfigA.class)public class ConfigB {    @Bean    public B b() {        return new B();    }}
public static void main(String[] args) {    ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class);    // now both beans A and B will be available...    A a = ctx.getBean(A.class);    B b = ctx.getBean(B.class);}

使用Import直接导入类作为Bean

使用如下:

//@Import导入组件,id默认是组件的全类名@Import({Color.class,Red.class,MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})public class MainConfig {}

这种方式不太建议,因为直接用@Import注册组件很难对组件做自定义设置,比如初始化方法,生命周期的管理,是单例还是多实例组件。

ImportSelector

实现接口ImportSelector,在实现类中返回要添加Bean的全类名,就会把类注入到Ioc容器中。在selectImports方法中可以做注入组件的逻辑判断,但是不能对组件的生命周期做业务处理。 示例:

//自定义逻辑返回需要导入的组件public class MyImportSelector implements ImportSelector {    //返回值,就是到导入到容器中的组件全类名    //AnnotationMetadata:当前标注@Import注解的类的所有注解信息    @Override    public String[] selectImports(AnnotationMetadata importingClassMetadata) {        // TODO Auto-generated method stub        //importingClassMetadata        //方法不要返回null值        return new String[]{"com.spring.bean.Blue","com.spring.bean.Yellow"};    }}
// 导入自定义组件@Configuration@Import({MyImportSelector.class})public class MainConfig {// ...}

ImportBeanDefinitionRegistrar

实现接口ImportBeanDefinitionRegistrar,在registerBeanDefinitions方法中拿到registry对象,调用registry.registerBeanDefinition()方法将Bean对象注入到IoC容器中。这种方式比ImportSelector方法更灵活一点,可以对Bean注入做更灵活的控制。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {    /**     * AnnotationMetadata:当前类的注解信息     * BeanDefinitionRegistry:BeanDefinition注册类;     *      把所有需要添加到容器中的bean;调用     *      BeanDefinitionRegistry.registerBeanDefinition手工注册进来     */    @Override    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {                boolean definition = registry.containsBeanDefinition("com.spring.bean.Red");        boolean definition2 = registry.containsBeanDefinition("com.spring.bean.Blue");        if(definition && definition2){            //指定Bean定义信息;(Bean的类型,Bean。。。)            RootBeanDefinition beanDefinition = new RootBeanDefinition(RainBow.class);            //注册一个Bean,指定bean名            registry.registerBeanDefinition("rainBow", beanDefinition);        }    }}
// 导入自定义组件@Configuration@Import({MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})public class MainConfig {// ...}

注意虽然Import了MyImportBeanDefinitionRegistrar和MyImportSelector,但这两个类的Bean组件。

方式四 FactoryBean工厂注入Bean

如果一个Bean特别复杂,需要做很多处理,则可以用FactoryBean来完成。该接口的实现类的不同方法共同定义了一个Bean对象。使用有两步:

  1. 实现FactoryBean;
  2. 使用@Bean注入;

使用如下:

  1. 实现FactoryBean;
//创建一个Spring定义的FactoryBeanpublic class ColorFactoryBean implements FactoryBean {    //返回一个Color对象,这个对象会添加到容器中    @Override    public Color getObject() throws Exception {        // TODO Auto-generated method stub        System.out.println("ColorFactoryBean...getObject...");        return new Color();    }    @Override    public Class> getObjectType() {        // TODO Auto-generated method stub        return Color.class;    }    //是单例?    //true:这个bean是单实例,在容器中保存一份    //false:多实例,每次获取都会创建一个新的bean;    @Override    public boolean isSingleton() {        // TODO Auto-generated method stub        return false;    }}
  1. 注入Bean
@Configurationpublic class MainConfig    @Bean    public ColorFactoryBean colorFactoryBean(){        return new ColorFactoryBean();    }}

这种方式即注入了Color Bean对象,同时也注入了ColorFactoryBean对象。两个Bean对象获取如下:

// 获取 Color BeanObject bean1 = applicationContext.getBean("colorFactoryBean");// 获取 ColorFactoryBeanObject bean2 = applicatio


原文链接:https://www.jianshu.com/p/332818c34861

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值