spring 切入点 后置通知

来源:互联网 发布:传智播客java教程下载 编辑:程序博客网 时间:2024/04/29 13:11

SpringAOP其实使用的是一种代理机制,可以算是代理或是装饰模式的应用。下面是一些常用的术语:

Aspect:通常翻译成切面,是一个横跨多个核心逻辑的功能,比如说散落在系统中多个角落的一块代码。

Joinpoint:通常翻译成连接点,是说程序中什么地方插入一个Aspect

Pointcut:切入点,一组Joinpoint的集合,比如一个Aspect需要在每个以query开头的方法时执行,可以用正则(.*query.*)来表示这一组Joinpoint

Advice:对Joinpoint的增强。

另外还有IntroductWeavingInterceptorTarget ObjectProxy

 

常用的Advice

1.MethodBeforeAdvice

2.AfterReturningAdvice

3.ThrowsAdvice

4.MethodInterceptor

 

1. 如果对UserServiceImpl增加advice增强,例如:

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd"

>

    <!-- 配置UserServiceImpl -->

 

    <bean id="userServiceTarget" class="example.chapter4.UserServiceImpl">

        <property name="userDao">

            <bean class="example.chapter4.UserDao" />

        </property>

    </bean>

 

    <!-- 定义MethodBeforeAdvice -->

 

    <bean id="loginMethodBeforeAdvice" class="example.chapter4.LoginMethodBeforeAdvice" />

 

    <!-- 定义AfterReturningAdvice -->

 

    <bean id="loginAfterReturningAdvice" class="example.chapter4.LoginAfterReturningAdvice" />

 

    <!-- 定义ThrowsAdvice -->

 

    <bean id="loginThrowsAdvice" class="example.chapter4.LoginThrowsAdvice" />

 

    <!-- 定义MethodInterceptor -->

 

    <bean id="loginAroundAdvice" class="example.chapter4.LoginAroundAdvice" />

 

    <!-- 配置AOP -->

 

    <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">

            <list>

                <value>loginMethodBeforeAdvice</value>

                <value>loginAfterReturningAdvice</value>

                <value>loginThrowsAdvice</value>

            </list>

        </property>

        <property name="target" ref="userServiceTarget" />

    </bean>

</beans>

 

2.但是,有个缺点,这样使用的话,会对UserServiceImpl中每个方法都加上这几种advice,这个时候就需要Advisor

<?xml version="1.0" encoding="UTF-8"?>

<beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd"

>

    <!-- 配置UserServiceImpl -->

 

    <bean id="userServiceTarget" class="example.chapter4.UserServiceImpl">

        <property name="userDao">

            <bean class="example.chapter4.UserDao" />

        </property>

    </bean>

 

    <!-- 使用NameMatchMethodPointcutAdvisor来定义loginBeforeAdvisor -->

    <bean id="loginBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

        <!-- 指定切入方法为login -->

        <property name="mappedName" value="login" />

        <!-- 指定Advice -->

        <property name="advice">

            <bean class="example.chapter4.LoginMethodBeforeAdvice" />

        </property>

    </bean>

 

    <!-- 使用RegexpMethodPointcutAdvisor来定义createAfterAdvisor -->

    <bean id="createAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

        <!-- 指定切入方法为.*create.*,拦截所有以create开头的方法 -->

        <property name="pattern" value=".*create.*" />

        <!-- 指定Advice -->

        <property name="advice">

            <bean class="example.chapter4.CreateAfterReturningAdvice" />

        </property>

    </bean>

 

    <!-- 配置AOP -->

    <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean">

        <property name="interceptorNames">

            <list>

                <value>loginBeforeAdvisor</value>

                <value>createAfterAdvisor</value>

            </list>

        </property>

        <property name="target" ref="userServiceTarget" />

    </bean>

</beans>

 

这样一来,可以分别对不同的方法进行分别增强。

 

3.不过,还是有个问题,用户此时可以绕过AOP代理直接获取原始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"

       xsi:schemaLocation="http://www.springframework.org/schema/beans

       http://www.springframework.org/schema/beans/spring-beans.xsd"

>

    <!-- 配置UserServiceImpl -->

 

    <bean id="userService" class="example.chapter4.UserServiceImpl">

        <property name="userDao">

            <bean class="example.chapter4.UserDao" />

        </property>

    </bean>

 

    <bean id="loginBeforeAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">

        <!-- 限定切入点仅作用于UserService的login()方法 -->

        <property name="pattern" value=".*UserService/.login" />

        <property name="advice">

            <bean class="example.chapter4.LoginMethodBeforeAdvice" />

        </property>

    </bean>

 

    <bean id="createAfterAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">

        <!-- 限定切入点仅作用于UserService的create()方法 -->

        <property name="pattern" value=".*UserService/.create" />

        <property name="advice">

            <bean class="example.chapter4.CreateAfterReturningAdvice" />

        </property>

    </bean>

 

    <!-- 配置BeanNameAutoProxyCreator -->

<!--

    <bean id="beanNameAutoProxy"

        class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">

        <property name="beanNames" value="userService" />

        <property name="interceptorNames">

            <list>

                <value>loginBeforeAdvisor</value>

                <value>createAfterAdvisor</value>

            </list>

        </property>

    </bean>

-->

    <!-- 配置DefaultAdvisorAutoProxyCreator -->

   <bean id="defaultAutoProxy"

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

</beans>

经过这样配置后,可以将advisor应用的某个接口的某些方法上。

BeanNameAutoProxyCreator 与DefaultAdvisorAutoProxyCreator的不同,是后者可以自动查找advisor(不查找advice)和普通bean,然后根据advisor计算他们能够适用于哪些bean,凡是能被适用的bean都将被自动创建AOP代理。

 

对于.*UserService/.create的形式,如果不指定接口,spring会去找其他符合条件的类中的create方法,那么会提示CGLIB没有找到。因为AOP代理对接口适用JDK动态代理,而对类级别使用CGLIB代理。

 

4.还有一中Introduction的使用,它是一种拦截器,我感觉是装饰模式的一种应用

比如说有一个类User,在编译器类A并未静态实现其他接口,而想动态增强其行为,可以创建一个接口Mutable

配置代码如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="
http://www.springframework.org/schema/beans"
       xmlns:xsi="
http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd"
>
    <!-- 具有Mutable接口的代理Bean -->
    <bean id="user" scope="prototype"
        class="org.springframework.aop.framework.ProxyFactoryBean">
        <property name="target" ref="target" />
        <property name="proxyTargetClass" value="true" />
        <property name="proxyInterfaces">
            <list>
                <value>example.chapter4.Mutable</value>
            </list>
        </property>
        <property name="interceptorNames">
            <list>
                <value>introductionAdvisor</value>
            </list>
        </property>
    </bean>

    <bean id="introductionAdvisor" scope="prototype"
        class="org.springframework.aop.support.DefaultIntroductionAdvisor">
        <constructor-arg>
            <bean class="example.chapter4.MutableInterceptor" />
        </constructor-arg>
    </bean>

    <!-- 原始Bean -->
    <bean id="target" class="example.chapter4.User" scope="prototype">
        <property name="username" value="default-name" />
        <property name="password" value="PaSsWoRd" />
        <property name="email" value="
default-email@abc.xyz" />
    </bean>

</beans>