spring注解驱动开发-(5)-向Spring容器中注册组件的方法

1. 包扫描+组件标注注解

@ComponentScan+ @Controller + @Service + @Repository

2. @Bean注解导入

导入第三方包里的组件

3. @Import快速导入

几个实体类的声明:

package com.niewj.bean;

public class PojoBean {
    public PojoBean() {
        System.out.println("PojoBean[无注解java类] 初始化~~~");
    }
}
package com.niewj.bean;

public class PojoBean2 {
    public PojoBean2(){
        System.out.println("PojoBean2[无注解java类] 初始化~~~");
    }
}
package com.niewj.bean;

public class PojoBean3 {
    public PojoBean3(){
        System.out.println("PojoBean3[无注解java类] 初始化~~~");
    }
}
package com.niewj.bean;

public class Pojo4 {
    public Pojo4(){
        System.out.println("Pojo4[无注解java类] 初始化~~~");
    }
}
package com.niewj.bean;

public class Pojo5 {
    public Pojo5(){
        System.out.println("Pojo5[无注解java类] 初始化~~~");
    }
}

(1) @Import导入单个

package com.niewj.config;

import com.niewj.bean.PojoBean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
@Import(PojoBean.class) // 这里
public class ImportTestConfig {
}
private AnnotationConfigApplicationContext printAnnoBeans(Class clazz) {
    AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(clazz);
    Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
    return ctx;
}

@Test
public void testImport(){
    // PojoBean*.java并无注解类, 但是会初始化归入spring容器管理! -> @Import的作用!
    ApplicationContext ctx = printAnnoBeans(ImportTestConfig.class);
}

output:

PojoBean[无注解java类] 初始化~~~
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
importTestConfig
com.niewj.bean.PojoBean

可以看到 PojoBean 的初始化和bean id的展示!

(2) @Import导入多个(数组)

普通Javabean并没有任何注解的:

package com.niewj.config;

import com.niewj.bean.PojoBean;
import com.niewj.bean.PojoBean2;
import com.niewj.bean.PojoBean3;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
//@Import(PojoBean.class)
@Import({PojoBean.class, PojoBean2.class, PojoBean3.class}) // 这里
public class ImportTestConfig {
}

测试:

private AnnotationConfigApplicationContext printAnnoBeans(Class clazz) {
    AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(clazz);
    Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
    return ctx;
}

@Test
public void testImport(){
    // PojoBean*.java并无注解类, 但是会初始化归入spring容器管理! -> @Import的作用!
    ApplicationContext ctx = printAnnoBeans(ImportTestConfig.class);
}

output: 可以看到这三个无注解的普通bean也被容器管理和初始化了!

PojoBean[无注解java类] 初始化~~~
PojoBean2[无注解java类] 初始化~~~
PojoBean3[无注解java类] 初始化~~~
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
importTestConfig
com.niewj.bean.PojoBean
com.niewj.bean.PojoBean2
com.niewj.bean.PojoBean3

(3) @ImportSelector接口

@ImportSelector接口的实现类, 可以放入@Import中使用:

@Import是列出所有; @ImportSelector是返回一个 class name的字符串的数组, 可以理解为@Import的简化版(方便使用)

package com.niewj.config;

import com.niewj.bean.PojoBean;
import com.niewj.bean.PojoBean2;
import com.niewj.bean.PojoBean3;
import com.niewj.condition.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
//@Import(PojoBean.class)
//@Import({PojoBean.class, PojoBean2.class, PojoBean3.class})
@Import({PojoBean.class, PojoBean2.class, PojoBean3.class, MyImportSelector.class}) // 这里
public class ImportTestConfig {
}

MyImportSelector:

package com.niewj.condition;

import com.niewj.bean.Pojo4;
import com.niewj.bean.Pojo5;
import org.springframework.context.annotation.ImportSelector;
import org.springframework.core.type.AnnotationMetadata;

import java.util.ArrayList;
import java.util.List;

public class MyImportSelector implements ImportSelector {

    /**
     * 返回的数组中的类名, 也会作为加入容器管理的bean
     * @param importingClassMetadata 可以获取到当前类的所有注解信息!
     * @return
     */
    @Override
    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        List<String> classNameList = new ArrayList<>();

        // 可以假设Pojo4/Pojo5的类名都读取字配置文件, 比如 spring.factories-->类似springboot源码
        classNameList.add(Pojo4.class.getName());
        classNameList.add(Pojo5.class.getName());

        return classNameList.toArray(new String[0]);
    }
}

testcase:

private AnnotationConfigApplicationContext printAnnoBeans(Class clazz) {
    AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(clazz);
    Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
    return ctx;
}

@Test
public void testImport(){
    // PojoBean*.java并无注解类, 但是会初始化归入spring容器管理! -> @Import的作用!
    ApplicationContext ctx = printAnnoBeans(ImportTestConfig.class);
}

output: 可见: PojoBean/PojoBean2/PojoBean3都初始化了; 而且 ImportSelector中加入数组的 Pojo4/Pojo5也初始化了!

PojoBean[无注解java类] 初始化~~~
PojoBean2[无注解java类] 初始化~~~
PojoBean3[无注解java类] 初始化~~~
Pojo4[无注解java类] 初始化~~~
Pojo5[无注解java类] 初始化~~~
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
importTestConfig
com.niewj.bean.PojoBean
com.niewj.bean.PojoBean2
com.niewj.bean.PojoBean3
com.niewj.bean.Pojo4
com.niewj.bean.Pojo5

(4). ImportBeanDefinitionRegistrar

可以结合已有beanDefinition信息!

package com.niewj.config;

import com.niewj.bean.PojoBean;
import com.niewj.bean.PojoBean2;
import com.niewj.bean.PojoBean3;
import com.niewj.condition.MyImportBeanDefRegistrar;
import com.niewj.condition.MyImportSelector;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

@Configuration
//@Import(PojoBean.class)
//@Import({PojoBean.class, PojoBean2.class, PojoBean3.class})
@Import({PojoBean.class, PojoBean2.class, PojoBean3.class, MyImportSelector.class, MyImportBeanDefRegistrar.class})
public class ImportTestConfig {
}

MyImportBeanDefRegistrar:

package com.niewj.condition;

import com.niewj.bean.Pojo4;
import com.niewj.bean.Pojo5;
import com.niewj.bean.PojoWhenHasPojo4AndPojo5;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.RootBeanDefinition;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.type.AnnotationMetadata;

public class MyImportBeanDefRegistrar implements ImportBeanDefinitionRegistrar {

    /** 可以获取容器中已有 BeanDefinition 信息来做一些判断; 
     * 比如, 如果有这两个类, 也加入类 PojoWhenHasPojo4AndPojo5
     * @param importingClassMetadata 可以获取到当前类(扫描到的类)的所有注解信息
     * @param registry beanDefinition的注册接口
     */
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        boolean hasPojo5 = registry.containsBeanDefinition(Pojo5.class.getName());
        boolean hasPojo4 = registry.containsBeanDefinition(Pojo4.class.getName());

        if(hasPojo4 && hasPojo5){
            RootBeanDefinition beanDefinition = new RootBeanDefinition(PojoWhenHasPojo4AndPojo5.class);
            registry.registerBeanDefinition("pojoWhenHasPojo4AndPojo5", beanDefinition);
        }
    }
}

测试:

private AnnotationConfigApplicationContext printAnnoBeans(Class clazz) {
    AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(clazz);
    Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
    return ctx;
}

@Test
public void testImport(){
    // PojoBean*.java并无注解类, 但是会初始化归入spring容器管理! -> @Import的作用!
    ApplicationContext ctx = printAnnoBeans(ImportTestConfig.class);
}

output: 可以看到 PojoWhenHasPojo4AndPojo5 的初始化!

PojoBean[无注解java类] 初始化~~~
PojoBean2[无注解java类] 初始化~~~
PojoBean3[无注解java类] 初始化~~~
Pojo4[无注解java类] 初始化~~~
Pojo5[无注解java类] 初始化~~~
PojoWhenHasPojo4AndPojo5[无注解java类] 初始化~~~
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
importTestConfig
com.niewj.bean.PojoBean
com.niewj.bean.PojoBean2
com.niewj.bean.PojoBean3
com.niewj.bean.Pojo4
com.niewj.bean.Pojo5
pojoWhenHasPojo4AndPojo5

4. Spring的FactoryBean<T>接口(工厂Bean)

  • getObject() 获取要生产的对象

  • getObjectType() 获取要生产的对象的类型

  • isSingleton() 定义是单例还是多例

    注意: 通过 ctx.getBean(“beanId”) 调用, 得到的是 getObject()方法返回的对象的实际内容;

    通过ctx.getBean(“&beanId”)调用, 得到的才是实际的FactoryBean对象

package com.niewj.bean;

public class Pojo7 {
    public Pojo7(){
        System.out.println("Pojo7[无注解java类] 初始化~~~");
    }
}
package com.niewj.bean;

import org.springframework.beans.factory.FactoryBean;

public class Pojo7FactoryBean implements FactoryBean<Pojo7> {
    @Override
    public Pojo7 getObject() throws Exception {
        System.out.println("Pojo7FactoryBean---FactoryBean---初始化");
        return new Pojo7();
    }

    @Override
    public Class<?> getObjectType() {
        return Pojo7.class;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}
    private AnnotationConfigApplicationContext printAnnoBeans(Class clazz) {
        AnnotationConfigApplicationContext ctx =  new AnnotationConfigApplicationContext(clazz);
        Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println);
        return ctx;
    }

    @Test
    public void testImport(){
        // PojoBean*.java并无注解类, 但是会初始化归入spring容器管理! -> @Import的作用!
        ApplicationContext ctx = printAnnoBeans(ImportTestConfig.class);
        Pojo7 bean1 = ctx.getBean("pojo7", Pojo7.class);
        System.out.println(bean1);
        Pojo7FactoryBean bean2 = ctx.getBean("&pojo7", Pojo7FactoryBean.class);
        System.out.println(bean2);
    }

output: 看最后两行: 说明了 FactoryBean的特征: &beanId得到是 FactoryBean; beanId得到是 getObject()返回的实际Bean!

PojoBean[无注解java类] 初始化~~~
PojoBean2[无注解java类] 初始化~~~
PojoBean3[无注解java类] 初始化~~~
Pojo4[无注解java类] 初始化~~~
Pojo5[无注解java类] 初始化~~~
PojoWhenHasPojo4AndPojo5[无注解java类] 初始化~~~
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
importTestConfig
com.niewj.bean.PojoBean
com.niewj.bean.PojoBean2
com.niewj.bean.PojoBean3
com.niewj.bean.Pojo4
com.niewj.bean.Pojo5
pojo7
pojoWhenHasPojo4AndPojo5
Pojo7FactoryBean---FactoryBean---初始化
Pojo7[无注解java类] 初始化~~~
com.niewj.bean.Pojo7@2bbf180e
com.niewj.bean.Pojo7FactoryBean@163e4e87

小结

向spring容器中注册组件有四种方式:

  1. @ComponentScan+注解 @Controller/@Service/@Response;
  2. @Bean注解导入;
  3. @Import注解导入普通bean;
  4. 实现spring的FactoryBean接口, 通过@Bean加入容器;

@Import导入普通bean也有四种方式:

  1. @Import(单个bean)
  2. @Import({多个bean-1, 多个bean-2, …}
  3. @Import({bean1.class, bean2.class, ImportSelector.class})
  4. @Import({bean1.class, bean2.class, ImportSelector.class, ImportBeanDefinitionRegistrar.class})

转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 hi@niewj.com

×

喜欢就点赞,疼爱就打赏