1. 程式人生 > >【框架】[Spring]AOP攔截-三種方式實現自動代理

【框架】[Spring]AOP攔截-三種方式實現自動代理

這裡的自動代理,我講的是自動代理bean物件,其實就是在xml中讓我們不用配置代理工廠,也就是不用配置class為org.springframework.aop.framework.ProxyFactoryBean的bean。

總結了一下自己目前所學的知識。

發現有三種方式實現自動代理

用Spring一個自動代理類DefaultAdvisorAutoProxyCreator:

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean
>

例如:
原來不用自動代理的配置檔案如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation
="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd"
>
<!-- 代理前原物件 --> <bean id="person" class="cn.hncu.xmlImpl.Person"></bean> <!-- 切面 = 切點+通知 --> <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"> <!-- 切點 --> <property name="patterns"> <list> <value>.*run.*</value> </list> </property> <!-- 通知-由我們寫,實際代理動作 --> <property name="advice"> <bean id="advice" class="cn.hncu.xmlImpl.AroundAdvice"></bean> </property> </bean> <!-- 代理工廠 --> <bean id="personProxied" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 放入原型物件 --> <property name="target" ref="person"></property> <!-- 放入切面 --> <property name="interceptorNames"> <list> <value>advisor</value> </list> </property> </bean> </beans>

現在改用自動代理,如下配置:

<beans ...>
<!-- 代理前原物件 -->
    <bean id="person" class="cn.hncu.xmlImpl.Person"></bean>

    <!-- 切面 = 切點+通知 -->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 切點 -->
        <property name="patterns">
            <list>
                <value>.*run.*</value>
            </list>
        </property>
        <!-- 通知-由我們寫,實際代理動作 -->
        <property name="advice">
            <bean id="advice" class="cn.hncu.xmlImpl.AroundAdvice"></bean>
        </property>
    </bean>


    <!-- 自動代理 -->
    <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
</beans>

測試方法

@Test//自動代理
    public void demo4(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("cn/hncu/xmlImpl/4.xml");
        //我們直接在這裡獲取Person物件就可以了,因為在最開始xml檔案newPerson物件後,Spring就已經幫我們代理了!
        Person p =ctx.getBean(Person.class);
        p.run();
        p.say();
    }

相對於前面,也就是把代理工廠部分換成自動代理了。

演示結果:

自己寫一個自動代理底層實現:

我們也可以寫一個類,來實現DefaultAdvisorAutoProxyCreator自動代理的功能!

首先,我們需要實現一個介面,也就是BeanPostProcessor介面。
BeanPostProcessor介面作用是:如果我們需要在Spring容器完成Bean的例項化、配置和其他的初始化前後新增一些自己的邏輯處理,我們就可以定義一個或者多個BeanPostProcessor介面的實現,然後註冊到容器中。

而我們想要在原型物件bean被建立之後就代理了,就必須在原來的容器中拿到原來的原型物件,需要拿到原來spring容器中的切面物件,這個時候,我們就需要原來的容器,這個時候就需要另一個介面,也就是ApplicationContextAware介面!

通過這2個介面,我們就可以實現自動代理了。

package cn.hncu.xmlImpl;

import org.springframework.aop.Advisor;
import org.springframework.aop.framework.ProxyFactoryBean;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class MyAutoProxy implements BeanPostProcessor,ApplicationContextAware{
    private ApplicationContext applicationContext=null;

    //bean建立之前呼叫
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        return bean;//在這裡,我們直接放行
    }

    //bean建立之後呼叫
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        ProxyFactoryBean factory = new ProxyFactoryBean();
        //把原型物件放入代理工廠
        factory.setTarget(bean);
        //在這裡
        Advisor adv = applicationContext.getBean(Advisor.class);
        factory.addAdvisor(adv);
        //返回被代理後的物件
        return factory.getObject();
    }

    //拿到原來的spring中的容器
    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        this.applicationContext=applicationContext;
    }

}

5.xml

<beans...>
<!-- 代理前原物件 -->
    <bean id="person" class="cn.hncu.xmlImpl.Person"></bean>

    <!-- 切面 = 切點+通知 -->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 切點 -->
        <property name="patterns">
            <list>
                <value>.*run.*</value>
            </list>
        </property>
        <!-- 通知-由我們寫,實際代理動作 -->
        <property name="advice">
            <bean id="advice" class="cn.hncu.xmlImpl.AroundAdvice"></bean>
        </property>
    </bean>


    <!-- 自己寫的自動代理 -->
    <bean class="cn.hncu.xmlImpl.MyAutoProxy"></bean>
</beans>

測試方法:

@Test//自己實現的自動代理
    public void demo5(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("cn/hncu/xmlImpl/5.xml");
        Person p =ctx.getBean(Person.class);
        p.run();
        p.say();
    }

測試結果就不上圖了,和前面是一樣的。

其實很多時候,我們如果自己去練一下底層,對上層的框架更好理解。

還有一種方法。

使用aop標籤配自動代理

需要在beans加一個名稱空間

xmlns:aop="http://www.springframework.org/schema/aop"

還需要配xsi:schemaLocation,為aop加一個網路地址。

http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd

xml配置檔案:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        xmlns:aop="http://www.springframework.org/schema/aop"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd ">
    <!-- 利用sop標籤實現自動代理 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!-- 代理前原物件 -->
    <bean id="person" class="cn.hncu.xmlImpl.Person"></bean>

    <!-- 切面 = 切點+通知 -->
    <bean id="advisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
        <!-- 切點 -->
        <property name="patterns">
            <list>
                <value>.*run.*</value>
            </list>
        </property>
        <!-- 通知-由我們寫,實際代理動作 -->
        <property name="advice">
            <bean id="advice" class="cn.hncu.xmlImpl.AroundAdvice"></bean>
        </property>
    </bean>

</beans>

測試方法:

@Test//自動代理
    public void demo6(){
        ApplicationContext ctx = new ClassPathXmlApplicationContext("cn/hncu/xmlImpl/6.xml");
        Person p =ctx.getBean(Person.class);
        p.run();
        p.say();
    }

測試結果:

個人覺得能學會使用一種就OK了,不用全部記下來,為了學習,都瞭解一下就好,別人寫出來,能看懂就好。
哈哈,其實底層學好了,自己寫的時候,就算不會用Spring的自動代理,自己寫出來底層也是蠻好的嘛

本文章由[諳憶]編寫, 所有權利保留。