Spring关于Bean的一些注入方式


注册方式

  1. 通过配置XML注册
  2. 通过@Bean注册
  3. 通过Beanfactory注入

需要注册的Bean

/**
 * @Description: 自定义Bean
 * @Author: Jonas
 * @Date: 2020-06-01 22:52
 */
@Data
@Slf4j
public class MyBean {

    private String name;

    private int age;

    public MyBean(String name, int age) {
        this.name = name;
        this.age = age;
        log.info("调用构造函数创建Bean,name={},age={}",name,age);
    }
}

具体实现

1. 通过配置XML注册

applicationContext.xml

<bean id="myBean" class="com.jonas.config.bean.MyBean" >
    <constructor-arg name="name" value="Jonas"/>
    <constructor-arg name="age" value="18"/>
</bean>

通过xml注入是Spring MVC中注入Bean常用的方式

现在项目中越来越多Spring Boot项目,而且配置XML的方式还是相对麻烦且难以维护,下面介绍的两种方式是现阶段中相对常见的方式

2. 通过@Bean注册

建造者模式创建Bean,这个类后面都会有提到

**
 * @Description: MyBeanBuilder 常见的建造者的方法
 * @Author: Jonas
 * @Date: 2020-06-01 22:57
 */
public class MyBeanBuilder {

    private String name;

    private int age;

    public MyBeanBuilder withName(String name) {
        this.name = name;
        return this;
    }

    public MyBeanBuilder withAge(int age) {
        this.age = age;
        return this;
    }

    public static MyBeanBuilder getInstance() {
        return new MyBeanBuilder();
    }

    public MyBean build() {
        return new MyBean(this.name,this.age);
    }


}

通过@Bean创建Spring对象

/**
 * @Description: 使用@Bean创建并注入Spring
 * @Author: Jonas
 * @Date: 2020-06-01 23:09
 */
@Configuration
@Slf4j
public class AnnotationBean {

    // 通过@Bean 调用构造函数,生成Bean,并将Bean交由BeanFactory管理
    @Bean
    public MyBean myBean() {
        MyBean myBean = new MyBean("hello", 10);
        log.info("向spring中注入成功 myBean");
        return myBean;
    }

    // 通过@Bean 调用建造方法,生成Bean,本质和上面的方式差不多
    @Bean
    public MyBean myBean2() {
        MyBean tom = MyBeanBuilder.getInstance()
                .withName("Tom")
                .withAge(22)
                .build();
        log.info("向spring中注入成功 myBean2");
        return tom;
    }
}

其实两者本质都是调用构造函数创建,只是后者是间接将参数传递给了构造函数

2. 通过Beanfactory动态注入

/**
 * @Description: 自定义Bean工厂
 * @Author: Jonas
 * @Date: 2020-06-01 22:50
 */
@Configuration
@Slf4j
public class MyBeanFactory implements InitializingBean {

    @Autowired
    private ApplicationContext applicationContext; // Spring上下文

    public MyBean createBean(String name,int age,String string) {
        log.info("调用工厂方法创建Bean");
        log.info("输出第三个参数===>{}",string);
        return new MyBean(name,age);
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 通过 DefaultListableBeanFactory 动态生成Bean
        DefaultListableBeanFactory capableBeanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        log.info("==================进入方法====================");
        // 动态创建10个类型为MyBean的不同的Bean对象
        for (int i=0;i<10;i++) {
            // 调用构造方法动态创建
            // registerBean(applicationContext,"dynamic"+i,MyBean.class,"Bean"+i,10+i);
            // 调用工厂方法动态创建Bean
            registerBean(applicationContext,"dynamic"+i,
                    "myBeanFactory","createBean","Bean" + i,20+i, UUID.randomUUID().toString());
        }


    }

    /**
     * 调用Bean构造函数,注册Bean到上下文中
     * @param applicationContext
     * @param registerName
     * @param clazz
     * @param args
     * @param <T>
     * @return
     */
    private <T> T registerBean(ApplicationContext applicationContext,
                               String registerName,
                               Class<T> clazz,
                               Object... args) {
        if(applicationContext.containsBean(registerName)) {
            Object bean = applicationContext.getBean(registerName);
            if (bean.getClass().isAssignableFrom(clazz)) {
                return (T) bean;
            } else {
                throw new RuntimeException("BeanName 重复 " + registerName);
            }
        }
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
        for (Object arg : args) {
            // 设置构造函数参数
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }
        BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();

        BeanDefinitionRegistry beanFactory = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
        beanFactory.registerBeanDefinition(registerName, beanDefinition);
        return applicationContext.getBean(registerName, clazz);
    }

    /**
     * 调用工厂方法,注册Bean到上下文中
     * @param applicationContext
     * @param registerName
     * @param factoryBeanClazz
     * @param factoryMethod
     * @param args
     * @param <T>
     * @return
     */
    private <T> T registerBean(ApplicationContext applicationContext,String registerName,
                               String factoryBeanClazz,String factoryMethod,Object... args) {
        BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder
                // 建造一个父类的Bean,不可为其他类的子类
                //.rootBeanDefinition()
                // 建造一个子类的Bean,必须传递一个父类Bean的名称,不可为其他类的父类
                // .childBeanDefinition()
                // 建造一个标准的Bean
                .genericBeanDefinition()
                // 设置工厂方法和工厂类
                .setFactoryMethodOnBean(factoryMethod, factoryBeanClazz);
        for (Object arg: args) {
            // 设置工厂方法参数
            beanDefinitionBuilder.addConstructorArgValue(arg);
        }
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) applicationContext.getAutowireCapableBeanFactory();
        AbstractBeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
        beanFactory.registerBeanDefinition(registerName,beanDefinition);

        return (T) applicationContext.getBean(registerName);
    }
}

调用工厂方法createBean动态创建10个不一样的Bean

调用构造方法动态创建10个不一样的Bean

总结

这三种是Bean常用的注册方式,第一种一般在SpringMVC中较为常见,但是感觉不是很方便。第二种是Spring Boot中常用的Bean注册方式,一般在配置类中经常需要手动加载Bean到Spring中去。第三种常常用于需要创建一批同类型的有一定重复命名规则的Bean,像前段时间对Swagger进行动态分组的时候就通过这个方式创建

附录

动态创建Swagger分组

EnableModuleTag.java

/**
 * @Description: 自动设置模块标记,便于日志模块的自动写入和swagger-ui的分组管理
 * @author: Jonas
 * @since: 2020/5/29 14:52
 */
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface EnableModuleTag {

    String moduleName() default "SYSTEM";

}

SwaggerConfig.java

/**
 * @Description: swagger-ui配置,单体应用自动创建Swagger-ui分组
 * @author: Jonas
 * @since: 2020/5/30 22:53
 */
@Configuration
@EnableSwagger2
@Slf4j
public class SwaggerConfig implements InitializingBean {

    private Set<String> groupName = new HashSet<>();

    @Autowired
    private ApplicationContext applicationContext;

    @Bean
    public Docket docket() {
        // basePackage 需要扫描注解生成文档的路径
        return new Docket(DocumentationType.SWAGGER_2)
                // 分组名用aaa开头以便排在最前面
                .groupName("默认分组(全部)")
                .apiInfo(apiInfo())
                .select()
                .apis(RequestHandlerSelectors.basePackage("com.jonas.data"))
                .paths(PathSelectors.any())
                .build();
    }

    //基本信息,页面展示
    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("SwaggerAPI")
                .description("xxxx项目接口api")
                //版本号
                .version("1.0.0")
                .build();
    }

    private Docket buildDocket(String groupName) {
        return new Docket(DocumentationType.SWAGGER_2)
                .apiInfo(apiInfo())
                .groupName(groupName)
                .select()
                .apis(method -> {
                    // 每个方法会进入这里进行判断并归类到不同分组,
                    // **请不要调换下面两段代码的顺序,在方法上的注解有优先级**
                    // 该方法上标注了模块名称
                    if (method.isAnnotatedWith(EnableModuleTag.class)) {
                        EnableModuleTag annotation = method.getHandlerMethod().getMethodAnnotation(EnableModuleTag.class);
                        if (annotation.moduleName() != null && annotation.moduleName().length() != 0) {
                            if (Arrays.asList(annotation.moduleName()).contains(groupName)) {
                                return true;
                            }
                        }

                    }
                    // 方法所在的类是否标注了?
                    EnableModuleTag annotationOnClass = method.getHandlerMethod().getBeanType().getAnnotation(EnableModuleTag.class);
                    if (annotationOnClass != null) {
                        if (annotationOnClass.moduleName() != null && annotationOnClass.moduleName().length() != 0) {
                            if (Arrays.asList(annotationOnClass.moduleName()).contains(groupName)) {
                                return true;
                            }
                        }
                    }
                    return false;
                })
                .paths(PathSelectors.any())
                .build();
    }

    /**
     * 动态得创建Docket bean
     * @throws Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // ApiConstantVersion 里面定义的每个变量会成为一个docket
        Map<String, Object> beanMap = applicationContext.getBeansWithAnnotation(EnableModuleTag.class);
        for (Iterator<Object> iterator = beanMap.values().iterator(); iterator.hasNext();) {
            Object bean = iterator.next();
            EnableModuleTag annotation = bean.getClass().getAnnotation(EnableModuleTag.class);
            // 获取模块名称,并放置到set中
            String moduleName = annotation.moduleName();
            groupName.add(moduleName);
            // 动态注入bean
            AutowireCapableBeanFactory autowireCapableBeanFactory = applicationContext.getAutowireCapableBeanFactory();
//            if (autowireCapableBeanFactory instanceof DefaultListableBeanFactory) {
                DefaultListableBeanFactory capableBeanFactory = (DefaultListableBeanFactory) autowireCapableBeanFactory;
                    // 要注意 "工厂名和方法名",意思是用这个bean的指定方法创建docket
                // 获取驼峰命名
                String registerName = CommonUtils.getHumnName("swagger_config_"+moduleName);
                AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder
                        .genericBeanDefinition()
                        .setFactoryMethodOnBean("buildDocket", "swaggerConfig")
                        .addConstructorArgValue(moduleName)
                        .getBeanDefinition();
                capableBeanFactory.registerBeanDefinition(registerName, beanDefinition);
                log.info("注册Bean:{} 到Spring中",registerName);
//            }
        }

    }
}

用法:
controller加上注解标注

启动的时候自动注册不同的Bean到Spring中,从而实现根据模块自动创建不同分组,方便管理API

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值