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.xmljava

<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

如今項目中愈來愈多Spring Boot項目,並且配置XML的方式仍是相對麻煩且難以維護,下面介紹的兩種方式是現階段中相對常見的方式api

2. 經過@Bean註冊

建造者模式建立Bean,這個類後面都會有提到app

**
 * @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對象dom

/** * @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;
    }
}
複製代碼

其實二者本質都是調用構造函數建立,只是後者是間接將參數傳遞給了構造函數 ide

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 ui

總結

這三種是Bean經常使用的註冊方式,第一種通常在SpringMVC中較爲常見,可是感受不是很方便。第二種是Spring Boot中經常使用的Bean註冊方式,通常在配置類中常常須要手動加載Bean到Spring中去。第三種經常用於須要建立一批同類型的有必定重複命名規則的Bean,像前段時間對Swagger進行動態分組的時候就經過這個方式建立this

附錄

動態建立Swagger分組spa

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

相關文章
相關標籤/搜索