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

溫馨提示×

溫馨提示×

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

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

如何將動態代理對象交由Spring容器管理

發布時間:2021-10-20 17:39:09 來源:億速云 閱讀:280 作者:柒染 欄目:大數據

如何將動態代理對象交由Spring容器管理,相信很多沒有經驗的人對此束手無策,為此本文總結了問題出現的原因和解決方法,通過這篇文章希望你能解決這個問題。

1. spring bean的實例化過程

我們平時工作日常開發中,要講類的實例對象注入ioc容器,只需要在類上加上相應的注解即可實現。一般用的多的就是@Controller,@Service,@Component等注解,那么用都是這么用,其中的原理倒是不被人理解。先過一下spring bean的加載過程。

① spring中如何描述一個bean的

說到這里,我們先看看java中一個實例是如何描述的。
如何將動態代理對象交由Spring容器管理

java中通過類來描述對象實例的,一個對象具有什么屬性,方法,都是在類中進行定義。然后將其變異為class字節碼文件,通過new關鍵字就能得到一個實例對象。

那么在spring中的bean是如何描述呢?
如何將動態代理對象交由Spring容器管理

前部分相同,通過Registrar注冊到beanDefinition中,在spring中用beanDefinition對象來描述需要實例化的對象,包含了這個bean的名稱,class,是否是抽象,是否為單例等信息,然后通過preInstantiateSingletons實例化到ioc容器中。我們要使用的對象的時候,就從ioc容器中去獲取相應的對象即可。

在spring bean的實例化過程中,spring設計了很多攔截點,可以在動態的改變實例化對象的相關信息。達到在ioc容器中的對象和最開始注冊到beanDefinition中的信息不同。

2. FactoryBean

現在來看看看FactoryBean。FactoryBean從名字來看以bean結尾那應該就是一個bean吧,沒錯它確實是一個bean,不同于普通Bean的是:它是實現了FactoryBean<T>接口的Bean。
特殊性質:
根據該Bean的ID從BeanFactory中獲取的實際上是FactoryBean的getObject()返回的對象,而不是FactoryBean本身,如果要獲取FactoryBean對象,請在id前面加一個&符號來獲取。

public interface FactoryBean<T> {
    //獲取bean對應的實例對象
    @Nullable
    T getObject() throws Exception;

    //獲取factoryBean獲取到的實例類型
    @Nullable
    Class<?> getObjectType();
    //FactoryBean創建的實例是否是單實例
    default boolean isSingleton() {
        return true;
    }
}

下面我們通過自定義一個FactoryBean來驗證一下這個bean的特殊性質。先準備一個測試bean

public class TestBean {

}

編寫自定義FactoryBean

@Component("myFactoryBean")
public class MyFactoryBean implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        return new TestBean();
    }

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

編寫測試類。從springioc容器中獲取myFactoryBean實例。

Object bean = SpringContextUtils.getBean("myFactoryBean");
System.out.println(bean);  //com.wj.factorybean.TestBean@2d459bda

Object bean = SpringContextUtils.getBean("&myFactoryBean");
System.out.println(bean); //com.wj.factorybean.MyFactoryBean@60ab895f

看到打印結果,驗證了上述的性質。

3. BeanFactoryPostProcessor

BeanFactoryPostProcessor可以在對象實例化到ioc容器之前對原有的beanDefinition的一些屬性進行設置更新。

如何將動態代理對象交由Spring容器管理

先來看例子。準備連個bean,其中TestBean我們@omponent注解,而TestBean1不做處理

@Component("testBean")
public class TestBean {

}

public class TestBean1 {
}

編寫BeanFactoryPostProcessor實現類

@Component
public class MyFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        //獲取testBean的beanDefinition
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition)beanFactory.getBeanDefinition("testBean");
        //更改class為TestBean1
        beanDefinition.setBeanClass(TestBean1.class);
    }
}

編寫測試類進行測試

Object bean = SpringContextUtils.getBean("testBean");
// com.wj.factorybean.TestBean1@45dc7be
System.out.println(bean);

我分明獲取的是testBean的實例,結果盡然獲取到的是TestBean1實例。說明我們通過BeanFactoryPostProcessor在對象實例化之前將對象更改了。所以我們獲取到的是TestBean1。我們有了BeanFactoryPostProcessor,就可以在對象實例化到ioc容器之前對即將要實例化的對象做一些手腳。但是這僅僅是更新。那么我們要手動將對象注冊到BeanDefinition呢。下面的ImportBeanDefinitionRegistrar就發揮用處了

4. ImportBeanDefinitionRegistrar

貌似說了這么多,都沒扯到今天的主題上,動態代理對象????,壓根沒有提到啊。不急,待會就來,我們是一步步慢慢接近主題了。
ImportBeanDefinitionRegistrar可以動態將自己的對象注冊到BeanDefinition,然后會spring的bean實例化流程,生成實例對象到ioc容器。
如何將動態代理對象交由Spring容器管理

編寫測試Dao接口,為什么要是接口呢?因為我們要利用代理生成Dao的實例對象啊

public interface MyDao {
    void query();
}

編寫自定義Registrar

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        這里用到前面定義的MyFactoryBean
        BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyFactoryBean.class);
        //生成beanDefinition
        GenericBeanDefinition beanDefinition = (GenericBeanDefinition)builder.getBeanDefinition();
        //將beanDefinition注冊
        beanDefinitionRegistry.registerBeanDefinition(MyDao.class.getName(),beanDefinition);
    }
}

更改MyFactoryBean,動態代理生成接口MyDao對象

public class MyFactoryBean implements FactoryBean {

    @Override
    public Object getObject() throws Exception {
        //利用動態代理生成MyDao的實例對象
        Object instance = Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), new Class[]{MyDao.class}, new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(”執行業務邏輯“);
                return null;
            }
        });
        return instance;
    }

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

自定義注解@MyScan,并通過@Import導入MyImportBeanDefinitionRegistrar。這樣就會被spring掃描到

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Import({MyImportBeanDefinitionRegistrar.class})
public @interface MyScan {
}

最后一步,在項目啟動類加上@MyScan。并編寫測試。調用Mydao

MyDao myDao = SpringContextUtils.getBean(MyDao.class);
myDao.execute();  //打印出”執行業務邏輯“

到這里我們就將動態代理的類交由ioc管理起來了。

5. 簡單模擬Mybaitis中的動態代理Mapper接口執行sql

我們引申一下。我們不是有一個MyDao嗎?并且在MyFactoryBean中代理實現的時候也是講其硬編碼寫死的。MyImportBeanDefinitionRegistrar中也是寫死的,這樣可不行,那么我們要怎么將其寫活呢。

在MyFactoryBean定義變量來接受class,并通過構造函數設置值。最后修改后的MyFactoryBean如下

public class MyFactoryBean implements FactoryBean {

    private Class classzz;
    public MyFactoryBean(Class classzz){
        this.classzz = classzz;
    }
   @Override
    public Object getObject() throws Exception {
        //利用動態代理生成實例對象
        Object instance = Proxy.newProxyInstance(MyFactoryBean.class.getClassLoader(), new Class[]{classzz.class}, new InvocationHandler(){
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println(”執行業務邏輯“);
                return null;
            }
        });
        return instance;
    }
    @Override
    public Class<?> getObjectType() {
        return this.classzz;
    }
}

更改MyImportBeanDefinitionRegistrar邏輯,我們定義一個Class數據來模擬多個class。通過beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(aClass.getTypeName());調用MyFactoryBean的有參構造函數生成MyFactoryBean。

public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
    @Override
    public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry beanDefinitionRegistry) {
        //這里將數組寫死了。我們可以定義一個包,掃描包下的所有接口class,這里就不做實現了,這里為了演示效果,多定義了一個接口MyDao1,跟MyDao定義相同的,代碼就不貼出來了。
        Class[] classes = {MyDao.class,MyDao1.class};
        for (Class aClass : classes) {
            BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MyFactoryBean.class);
            GenericBeanDefinition beanDefinition = (GenericBeanDefinition)builder.getBeanDefinition();
            //調用剛剛定義的MyFactoryBean有參構造函數
            beanDefinition.getConstructorArgumentValues().addGenericArgumentValue(aClass.getTypeName());
            beanDefinitionRegistry.registerBeanDefinition(aClass.getName(),beanDefinition);
        }
    }
}

測試實例

MyDao myDao = SpringContextUtils.getBean(MyDao.class);
myDao.query(); //執行業務邏輯
MyDao1 myDao1 = SpringContextUtils.getBean(MyDao1.class);
myDao1.query(); //執行業務邏輯

有沒有感覺到有點類似mybatis了,接口Mapper,沒有任何實現,但是可以直接@Autowired進行調用,沒錯,就是在模擬Mybatis。不過,我們自己定義的@MyScan注解,它的是@MapperScan注解,后面參數為Mapper的包路徑,我們這里就沒有實現類,因為我們在MyImportBeanDefinitionRegistrar中定義數組來模擬包路徑掃描class了。下面再完善一下,我們調用了連個Dao都執行了相同的邏輯。應該執行不同的sql查詢才對啊。我們就來實現這點。

自定義@Select注解,這個注解就是用在Dao接口方法定義上的,value為sql語句

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Select {
    String value() default "";
}

在Dao接口中使用@Select注解

public interface MyDao {
    @Select("SELECT * FROM T1")
    void query();
}

public interface MyDao1 {
    @Select("SELECT * FROM T2")
    void query();
}

在動態代理生成代理對象的InvocationHandler編寫具體獲取sql邏輯。

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String value = method.getDeclaredAnnotation(Select.class).value();
        System.out.println(value);
        return null;
    }

調用剛剛剛剛的測試方法,打印sql語句。

MyDao myDao = SpringContextUtils.getBean(MyDao.class);
myDao.query(); //SELECT * FROM T1
MyDao1 myDao1 = SpringContextUtils.getBean(MyDao1.class);
myDao1.query(); //SELECT * FROM T2

6. 總結

spring真是在高擴展這方面做得很好,第三方插件可以實現不同的接口,實現對spring進行擴展。就像Mybatis整合到Spring。我們在日常開發中,為了應付多變的需求,設計出易擴展的程序是非常有必要的。一味的按照需求實現硬編碼,后面業務變更,只有加班的份,然后就天天改,自己技術也局限了。整天被產品拖著鼻子走。天天加班,時間都花在重復的工作上了,對自己的成長沒有啥好處。

看完上述內容,你們掌握如何將動態代理對象交由Spring容器管理的方法了嗎?如果還想學到更多技能或想了解更多相關內容,歡迎關注億速云行業資訊頻道,感謝各位的閱讀!

向AI問一下細節

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

AI

白水县| 南召县| 鲁山县| 陈巴尔虎旗| 渝北区| 苏尼特右旗| 霍州市| 江油市| 太谷县| 江华| 新建县| 石泉县| 邻水| 同心县| 榕江县| 涿鹿县| 揭西县| 北流市| 南郑县| 牟定县| 鹿泉市| 贵南县| 漾濞| 秦安县| 林周县| 八宿县| 金昌市| 富锦市| 麻阳| 仁布县| 当涂县| 逊克县| 阳城县| 班戈县| 邹平县| 台湾省| 东安县| 突泉县| 文昌市| 巍山| 南陵县|