中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

向Spring?IOC容器動態注冊bean實現方式是什么

發布時間:2022-07-15 13:40:10 來源:億速云 閱讀:162 作者:iii 欄目:開發技術

本篇內容主要講解“向Spring IOC容器動態注冊bean實現方式是什么”,感興趣的朋友不妨來看看。本文介紹的方法操作簡單快捷,實用性強。下面就讓小編來帶大家學習“向Spring IOC容器動態注冊bean實現方式是什么”吧!

    從一個需求談起

    這周遇到了這樣一個需求,從第三方的數據庫中獲取值,只是一個簡單的分頁查詢,處理這種問題,我一般都是在配置文件中配置數據庫的地址等相關信息,然后在Spring Configuration 注冊數據量連接池的bean,然后再將數據庫連接池給JdbcTemplate, 但是這種的缺陷是,假設填錯了數據庫地址和密碼,或者換了數據庫的地址和密碼,在配置文件里面重啟之后,都需要重啟應用。

    我想能不能動態的向Spring IOC容器中注冊和加載bean呢,項目在界面上填寫數據庫的地址、用戶名、密碼,存儲之后,將JdbcTemplate和另一個數據庫連接池加載到IOC容器中。答案是可以的,我經過一番搜索寫出了如下代碼:

    @Component
    public class BeanDynamicRegister {
        private final ConfigurableApplicationContext configurableApplicationContext;
        public BeanDynamicRegister(ConfigurableApplicationContext configurableApplicationContext) {
            this.configurableApplicationContext = configurableApplicationContext;
        }
        /**
         * 此方法提供出去,供其他bean動態的向IOC容器中注冊bean。
         * 代表使用構造器給bean賦值
         *
         * @param beanName bean名
         * @param clazz    bean類
         * @param args     用于向bean的構造函數中添加值 如果loadType是set,則要求傳遞map.map的key為屬性名,value為屬性值
         * @param <T>      返回一個泛型
         * @param loadType
         * @return
         */
        public <T> T registerBeanByLoadType(String beanName, Class<T> clazz, LoadType loadType, Object... args) {
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(clazz);
            if (args.length > 0) {
                // 將參數加入到構造函數中
                switch (loadType) {
                    case CONSTRUCTOR:
                        for (Object arg : args) {
                            beanDefinitionBuilder.addConstructorArgValue(arg);
                        }
                        break;
                    case SETTER:
                        Map<String, Object> propertyMap = (Map<String, Object>) args[0];
                        for (Map.Entry<String, Object> stringObjectEntry : propertyMap.entrySet()) {
                            beanDefinitionBuilder.addPropertyValue(stringObjectEntry.getKey(), stringObjectEntry.getValue());
                        }
                        break;
                    default:
                        break;
                }
            }
            BeanDefinition beanDefinition = beanDefinitionBuilder.getRawBeanDefinition();
            BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) configurableApplicationContext.getBeanFactory();
            beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
            return configurableApplicationContext.getBean(beanName, clazz);
        }
        public <T> T getBeanByName(String beanName,Class<T> requiredType){
            return  configurableApplicationContext.getBean(beanName,requiredType);
        }
        /**
         * 如果用戶換了地址和密碼,向IOC容器中移除bean。 重新注冊
         *
         * @param beanName
         */
        public void removeBean(String beanName) {
            BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) configurableApplicationContext.getBeanFactory();
            beanDefinitionRegistry.removeBeanDefinition(beanName);
        }
    }
    @SpringBootTest
    class SsmApplicationTests {
        @Autowired
        private LoadBeanService loadBeanService;
        private NamedParameterJdbcTemplate jdbcTemplate;
        @Autowired
        private BeanDynamicRegister beanDynamicRegister;
        @Test
        public void test() {
            loadBeanService.loadDataSourceTest("root", "root");
            jdbcTemplate = beanDynamicRegister.getBeanByName("jdbcTemplateOne", NamedParameterJdbcTemplate.class);
            System.out.println("--------" + jdbcTemplate);
        }
    }

    結果: 

    向Spring?IOC容器動態注冊bean實現方式是什么

    我們就到這里了嗎? 我們觀察一下上面將一個bean加載到Spring IOC容器里經過了幾步:

    • BeanDefineBuilder 構造BeanDefinition

    • 然后BeanDefinitionRegistry將其注冊到IOC容器中。(這一步事實上只完成了注冊,還未完成Bean的實例化,屬性填充)

    聯系我們前面的文章《Spring Bean 的生命周期》,我們將Spring 的生命周期理解為“Spring 給我們提供的一些擴展接口,如果bean實現了這些這些接口,應用在啟動的過程中會回調這些接口的方法。” , 這個理解并不完善,缺少了解析BeanDefinition這個階段。

    Spring Bean的生命周期再完善

    BeanDefinition

    那BeanDefinition是什么? BeanDefinition是一個接口,我們進Spring 官網(https://docs.spring.io/spring...)大致看一下:

    A bean definition can contain a lot of configuration information, including constructor arguments, property values, and container-specific information, such as the initialization method, a static factory method name, and so on. A child bean definition inherits configuration data from a parent definition. The child definition can override some values or add others as needed. Using parent and child bean definitions can save a lot of typing. Effectively, this is a form of templating.

    bean 的定義信息可以包含許多配置信息,包括構造函數參數,屬性值和特定于容器的信息,例如初始化方法,靜態工廠方法名稱等。子 bean 定義可以從父 bean 定義繼承配置數據。子 bean 的定義信息可以覆蓋某些值,或者可以根據需要添加其他值。使用父 bean 和子 bean 的定義可以節省很多輸入(實際上,這是一種模板的設計形式)。

    這段說的可能有點抽象, 你點BeanDefinition進去,你就會發現有很多熟悉的面孔:

    向Spring?IOC容器動態注冊bean實現方式是什么

    Bean的作用域: 單例,還是多例。

    向Spring?IOC容器動態注冊bean實現方式是什么

    lazyInit是否是懶加載。

    這些都是描述Spring Bean的信息,我們可以類比到Java中的類,每個類都會有class屬性,我們在配置類或者xml中的配置Bean的元信息,也被映射到這里。供IOC容器將Bean加入時使用。所以我們可以為對Spring Bean的生命周期的理解打一個補丁:

    • 從xml或配置類中解析BeanDefintion

    • BeanDefinition 注冊,此時還未完成Bean的實例化。

    我們可以打斷點來驗證一下:

    向Spring?IOC容器動態注冊bean實現方式是什么

    向Spring?IOC容器動態注冊bean實現方式是什么

    • Bean 實例化

    • Bean的屬性賦值+依賴注入

    • Bean的初始化階段的方法回調

    • Bean的銷毀。

    向Spring?IOC容器動態注冊bean實現方式是什么

    Bean 加入IOC容器的幾種方式

    我們這里再來總結一下一個Bean注入Spring IOC容器的幾種形式:

    啟動時加入

    • 配置類: @Configuration+@Bean

    • 配置文件: xml

    注解形式

    • @Component

    • @Service

    • @Controller

    • @Repository

    • @import

    • @Qualifier

    • @Resource

    • @Inject

    運行時加入

    這三種最終都是通過BeanDefinitionRegistry來注入的,ImportBeanDefinitionRegistrar是一個接口,留給我們實現的方法如下:

    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
    • ImportBeanDefinitionRegistrar

    • 手動構造BeanDefinition注入(我們上面就是自己手動構造BeanDefinition注入)

    • 借助BeanDefinitionRegistryPostProcessor注入

    BeanDefinitionRegistryPostProcessor也是一個接口,留給我們實現的方法如下:

    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

    從spring容器中動態添加或移除bean

    public class DemoUtil {
        @Autowired
        private ApplicationContext applicationContext;
    //添加bean
        public void addBean(String beanName, Class<?> beanClass) {
            BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
            BeanDefinitionBuilder beanDefinitionBuilder = BeanDefinitionBuilder.genericBeanDefinition(beanClass);
            BeanDefinition beanDefinition = beanDefinitionBuilder.getBeanDefinition();
            if (!beanDefinitionRegistry.containsBeanDefinition(beanName)) {
                beanDefinitionRegistry.registerBeanDefinition(beanName, beanDefinition);
            }
        }
    //移除bean
        public void removeBean(String beanName) {
            BeanDefinitionRegistry beanDefinitionRegistry = (BeanDefinitionRegistry) applicationContext.getAutowireCapableBeanFactory();
            beanDefinitionRegistry.getBeanDefinition(beanName);
            beanDefinitionRegistry.removeBeanDefinition(beanName);
        }
    }

    到此,相信大家對“向Spring IOC容器動態注冊bean實現方式是什么”有了更深的了解,不妨來實際操作一番吧!這里是億速云網站,更多相關內容可以進入相關頻道進行查詢,關注我們,繼續學習!

    向AI問一下細節

    免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

    AI

    怀化市| 溧水县| 新化县| 西贡区| 建始县| 济阳县| 开平市| 阜阳市| 宁陕县| 石狮市| 土默特右旗| 巴青县| 枞阳县| 阿瓦提县| 西平县| 右玉县| 那坡县| 准格尔旗| 株洲县| 襄垣县| 荣成市| 罗甸县| 红桥区| 靖江市| 商河县| 梅河口市| 进贤县| 夏津县| 齐齐哈尔市| 汕尾市| 建始县| 九江市| 黄大仙区| 洛川县| 龙山县| 昆山市| 河北区| 大埔县| 新巴尔虎右旗| 克东县| 紫金县|