Spring AOP (一)

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/liouyi250/article/details/79392253

一、 术语

连接点(JointPoint):程序执行的某个特定位置,Spring仅支持方法的连接点。
切点(Pointcut):特定的连接点。
增强(Advice):织入目标对象的一段程序代码。
引介(Introduction):特殊的增强,为类添加一些方法和属性。
织入(Weave):将增强添加到目标类连接点上的过程。
目标对象(Target):增强逻辑的织入目标类。
代理(Proxy):融合了原类和增强逻辑的代理类。
切面(Advisor):由切点和增强组成。

二、创建增强

2.1 增强类型

前置增强 MethodBeforeAdvice:在目标方法执行前实施增强。
后置增强 AfterReturningAdvice:在目标方法执行后实施增强。
环绕增强 MethodInterceptor:在目标方法执行前后实施增强。
抛出异常增强 ThrowableAdvice:在目标方法抛出异常实施增强。
引介增强 IntroductionInterceptor:在目标类中添加一些新的属性和方法。

2.2 前置增强

//创建接口
public interface Waiter {
    void greetTo(String name);
    void serveTo(String name);
}

//创建类实现该接口
public class NaiveWaiter implements Waiter{
    public void greetTo(String name) {
        System.out.println("greeting to,Mr."+name);
    }

    public void serveTo(String name) {
        System.out.println("serve to,Mr."+name);
    }
}

//前置增强
public class GreetingBeforeAdvice implements MethodBeforeAdvice{
    public void before(Method method, Object[] args, Object target) throws Throwable {
        String name=(String)args[0];
        System.out.println("How are you,Mr."+name);
    }
}

//xml配置
//前置增强配置
<bean id="naiveWaiter" class="aop.beforeadvice.NaiveWaiter"/>
<bean id="greetingBeforeAdvice" class="aop.beforeadvice.GreetingBeforeAdvice"/>
<bean id="waiter" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:target-ref="naiveWaiter" //目标类
          p:proxyInterfaces="aop.beforeadvice.Waiter"  //代理的接口,时一个list元素
          p:interceptorNames="greetingBeforeAdvice" //织入目标对象的bean列表
          //p:proxyTargetClass="true" 使用cglib代理
          //p:optimize="true" 强制使用cglib代理
          /> 


//测试方法
 Waiter waiter=(Waiter)context.getBean("waiter",Waiter.class);
 waiter.greetTo("John");
 waiter.serveTo("John");

//测试结果
How are you,Mr.John
greeting to,Mr.John
How are you,Mr.John
serve to,Mr.John

2.3 后置增强

//后置增强类
public class GreetingAfter implements AfterReturningAdvice{
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        String name=(String)args[0];
        System.out.println("Please wait a minute,Mr."+name);
    }
}

//xml配置
<!--后置增强配置-->
<bean id="greetingAfter" class="aop.after.GreetingAfter"/>
<bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="greetingAfter,greetingBeforeAdvice"
      p:target-ref="naiveWaiter"
      p:proxyTargetClass="true"/>
//测试结果
How are you,Mr.John  //前置增强
greeting to,Mr.John
Please wait a minute,Mr.John  //后置增强
How are you,Mr.John
serve to,Mr.John
Please wait a minute,Mr.John

2.4 环绕增强

//环绕增强类,MethodInterceptor是AOP联盟定义的
public class GreetingInterceptor implements MethodInterceptor{
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[] args=invocation.getArguments();
        String name=(String) args[0];
        System.out.println("How are you,Mr."+name);
        Object object=invocation.proceed();
        System.out.println("Please wait a minute,Mr."+name);
        return object;
    }
}

//xml配置
<bean id="greetingInterceptor" class="aop.interceptor.GreetingInterceptor"/>
<bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:proxyTargetClass="true"
          p:target-ref="naiveWaiter"
          p:interceptorNames="greetingInterceptor"/>
//测试
 Waiter waiter=context.getBean("waiter2",Waiter.class);
 waiter.greetTo("John");
 waiter.serveTo("John");
 //结果
How are you,Mr.John
greeting to,Mr.John
Please wait a minute,Mr.John
How are you,Mr.John
serve to,Mr.John
Please wait a minute,Mr.John

2.5 抛出异常增强

//目标类
public class ForumService {
    public void removeForum(int id){
        throw new RuntimeException("运行异常");
    }

    public void updateForum(int id) throws Exception{
        throw new SQLException("数据更新异常");
    }
}

//抛出异常增强类
public class TransactionManager implements ThrowsAdvice{

    public void afterThrowing(Method method, Object[] args, Object target, Exception ex) throws Throwable{
        System.out.println("---------------");
        System.out.println("method:"+method.getName());
        System.out.println("message:"+ex.getMessage());
    }
}

//xml配置
<bean id="transactionManager" class="aop.throwableadvice.TransactionManager"/>
<bean id="forumTarget" class="aop.throwableadvice.ForumService"/>
<bean id="forumService" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:interceptorNames="transactionManager"
      p:target-ref="forumTarget"
      p:proxyTargetClass="true"/>
//测试方法
ForumService service=context.getBean("forumService",ForumService.class);
try{
    service.removeForum(1);
}catch(Exception e){
}try{
    service.updateForum(1);
}catch(Exception e){
}

//结果
---------------
method:removeForum
message:运行异常
---------------
method:updateForum
message:数据更新异常

2.6 引介增强

//创建接口
public interface MyMonitor {
    void setMonitorActive(boolean flag);
}

//创建未实现该接口的类
public class TimeMonitor{
    public void record(){
        System.out.println("开始计时");
    }
}

//引介增强类
public class PerformanceMonitor extends DelegatingIntroductionInterceptor implements MyMonitor{
    private ThreadLocal<Boolean> monitorMap=new ThreadLocal<Boolean>();

    public void setMonitorActive(boolean flag) {
        monitorMap.set(flag);
    }

    @Override
    public Object invoke(MethodInvocation mi) throws Throwable {
        Object obj=null;
        if(monitorMap.get()!=null && monitorMap.get()){//当变量为true时进行拦截
            System.out.println(mi.getClass().getName()+"."+mi.getMethod().getName());
            obj=super.invoke(mi);
        }else {
            obj = super.invoke(mi);
        }
        return obj;
    }
}

//xml配置
<bean id="monitor" class="aop.introduction.PerformanceMonitor"/>
<bean id="timeMonitor" class="aop.introduction.TimeMonitor"/>
<bean id="myMonitor" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interfaces="aop.introduction.MyMonitor"
          p:interceptorNames="monitor"
          p:target-ref="timeMonitor"
          p:proxyTargetClass="true"/>
//测试方法
TimeMonitor monitor=(TimeMonitor)context.getBean("myMonitor");
monitor.record();

MyMonitor myMonitor=(MyMonitor)monitor;
myMonitor.setMonitorActive(true);
monitor.record();

//结果
开始计时  //默认参数为false
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.record  //设置参数为true
开始计时

三、创建切面

3.1 切点类型

静态方法切点,StaticMetodMatcherPointcut是抽象基类,默认匹配所有类,子类NameMatchMethodPointcut提供简单字符串匹配方法签名,子类AbstractRegexpMethodPointcut使用正则表达式匹配方法签名。
动态方法切点,基类DynamicMethodMatcherPointcut,默认匹配所有类。
注解切点,AnnotationMatchingPointcut。
表达式切点,ExpressionPointcut主要是为了支持AspectJ。
流程切点,ControlFlowPointcut,它查看目标方法是否由某一个方法直接或间接发起调用,以此判断是否为匹配的连接点。
复合切点,ComposablePointcut,切点大于两个

3.2 切面类型

Advisor:一般切面,默认所有类的所有方法
PointcutAdvisor:具有切点的切面,不支持引介切点。有以下6个实现类
————–StaticMethodMatcherPointcutAdvisor,静态方法匹配器切点定义的切面,默认情况匹配所有类。
————–NameMatchMethodPointcutAdvisor,通过该类可以定义按方法名定义切点的切面。
————–DefaultPointcutAdvisor,最常用的切面类型,不支持引介切面,一般通过扩展该类实现自定义的切面。
————–RegexpMethodPointcutAdvisor,按正则表达式匹配方法名进行切点定义的切面,可以通过扩展该实现类进行操作。
————–AspectJExpressionPointcutAdvisor,用于AspectJ切点定义表达式定义切点的切面。
————–AspectJPointcutAdvisor,用于AspectJ语法定义切点的切面。
IntroductionAdvisor:引介切面,应用于类层面上。

3.3 普通静态方法名匹配切面

//匹配NaiveWaiter#greetTo()
//Seller类
public class Seller {
    public void greetTo(String name){
        System.out.println("seller greet to,Mr."+name);
    }
}

//切面类
public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor{
    public boolean matches(Method method, Class<?> targetClass) { //切点方法匹配规则:方法名为greetTo
        return "greetTo".equals(method.getName());
    }

    @Override
    public ClassFilter getClassFilter() {  //切点类匹配规则:NaiveWaiter类或子类
        return new ClassFilter() {
            public boolean matches(Class<?> clazz) {
                return NaiveWaiter.class.isAssignableFrom(clazz);
            }
        };
    }
}

//xml配置
<bean id="sellerTarget" class="advisor.Seller" />
<bean id="waiterTarget" class="aop.beforeadvice.NaiveWaiter" />
<bean id="greetingAdvisor" class="advisor.GreetingAdvisor" p:advice-ref="greetingBeforeAdvice" /> //切面定义
<bean id="parent" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true"
          p:proxyTargetClass="true"
          p:interceptorNames="greetingAdvisor"/>  //公共信息配置
<bean id="seller" parent="parent" p:target-ref="sellerTarget"/>
<bean id="waiter" parent="parent" p:target-ref="waiterTarget"/>

//测试方法
Seller seller=(Seller)context.getBean("seller");
Waiter waiter=(Waiter)context.getBean("waiter");
seller.greetTo("John");
waiter.greetTo("John");
waiter.serveTo("John");

//测试结果
seller greet to,Mr.John  //Seller#greetTo()
How are you,Mr.John //Waiter#greetTo()
waiter greeting to,Mr.John
waiter serve to,Mr.John  //Waiter#serveTo()

3.4 静态正则表达式方法匹配切面

<bean id="greetingAdvisor1" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
        p:patterns="aop\.beforeadvice\..*Waiter\..*greetTo.*"  //匹配aop.beforeadvice/*Waiter/*greetTo*
        p:advice-ref="greetingBeforeAdvice"
/>

<bean id="parentRegexp" class="org.springframework.aop.framework.ProxyFactoryBean" abstract="true"
          p:proxyTargetClass="true"
          p:interceptorNames="greetingAdvisor1"/>
<bean id="sellerRegexp" parent="parentRegexp" p:target-ref="sellerTarget"/>
<bean id="waiterRegexp" parent="parentRegexp" p:target-ref="waiterTarget"/>
//测试代码和结果同上

3.5 动态切面

静态切面是在生成代理对象时就确定了增强是否需要织入目标类的连接点上。而动态切面是指必须在运行期根据方法入参的值来判断增强是否需要织入目标类的连接点上。
Spring在创建代理织入切面时,对目标类中的所有方法进行静态切点检查,在生成织入切面的代理对象后,第一次调用代理类中每一个方法时都会进行一次静态切点检查,如果本次检查就能从候选者列表中将该方法排除,则以后对该方法的调用就不再执行静态切点检查,对于哪些在静态切点检查时匹配的方法,在后续调用该方法时,将执行动态切点检查。
由于动态切面对性能的影响较大,在定义动态切点时,一定要同时覆盖getClassFilter()和matches(Method,Class)方法,通过静态切点检查排除大部分方法。

//切面类
public class GreetDynamicPointcut extends DynamicMethodMatcherPointcut{
    private static List<String> specialClient=new ArrayList<String>();

    static{
        specialClient.add("John");
        specialClient.add("Peter");
    }

    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            public boolean matches(Class<?> clazz) {
                System.out.println("对"+clazz.getName()+"进行静态类切点检查");
                return NaiveWaiter.class.isAssignableFrom(clazz);
            }
        };
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        System.out.println("对"+targetClass.getName()+"."+method.getName()+"进行静态方法检查");
        return "greetTo".equals(method.getName());
    }

    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        System.out.println("进行动态方法检查");
        String name=(String)args[0];
        return specialClient.contains(name);
    }
}

//xml配置
<bean id="greetingAdvisor2" class="org.springframework.aop.support.DefaultPointcutAdvisor" >
        <property name="advice">
            <bean class="aop.beforeadvice.GreetingBeforeAdvice"/>
        </property>
        <property name="pointcut">
            <bean class="advisor.GreetDynamicPointcut"/>
        </property>
</bean>
bean id="waiterDynamic" class="org.springframework.aop.framework.ProxyFactoryBean"
         p:interceptorNames="greetingAdvisor2"
         p:proxyTargetClass="true"
         p:target-ref="waiterTarget"/>
//测试方法
Waiter waiter=(Waiter)context.getBean("waiterDynamic");
waiter.serveTo("Tom");
waiter.greetTo("Tom");

waiter.serveTo("Peter");
waiter.greetTo("Peter");
//结果
对aop.beforeadvice.NaiveWaiter进行静态类切点检查
对aop.beforeadvice.NaiveWaiter.greetTo进行静态方法检查
对aop.beforeadvice.NaiveWaiter进行静态类切点检查
对aop.beforeadvice.NaiveWaiter.serveTo进行静态方法检查
对aop.beforeadvice.NaiveWaiter进行静态类切点检查
对aop.beforeadvice.NaiveWaiter.toString进行静态方法检查
对aop.beforeadvice.NaiveWaiter进行静态类切点检查
对aop.beforeadvice.NaiveWaiter.clone进行静态方法检查
对aop.beforeadvice.NaiveWaiter进行静态类切点检查
对aop.beforeadvice.NaiveWaiter.serveTo进行静态方法检查
waiter serve to,Mr.Tom
对aop.beforeadvice.NaiveWaiter进行静态类切点检查
对aop.beforeadvice.NaiveWaiter.greetTo进行静态方法检查
进行动态方法检查
waiter greeting to,Mr.Tom
waiter serve to,Mr.Peter
进行动态方法检查
How are you,Mr.Peter
waiter greeting to,Mr.Peter

3.6 流程切面

public class WaiterDelegate {
    private Waiter waiter;

    public void setWaiter(Waiter waiter){
        this.waiter=waiter;
    }

    //现在想所有由WaiterDelegate#service()方法发起调用的其他方法都织入增强,则需要使用流程切面
    public void service(String name){
        waiter.greetTo(name);
        waiter.serveTo(name);
    }
}

//xml配置
<bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
        <constructor-arg type="java.lang.Class" value="advisor.WaiterDelegate"/>
        <constructor-arg type="java.lang.String" value="service"/>
</bean>
<bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
      p:pointcut-ref="controlFlowPointcut"
      p:advice-ref="greetingBeforeAdvice"/>
<bean id="waiterDelegate" class="org.springframework.aop.framework.ProxyFactoryBean"
      p:target-ref="waiterTarget"
      p:proxyTargetClass="true"
      p:interceptorNames="controlFlowAdvisor"/>
//测试代码
Waiter waiter=(Waiter)context.getBean("waiterDelegate");
waiter.greetTo("Peter");
waiter.serveTo("Peter");

WaiterDelegate waiterDelegate=new WaiterDelegate();
waiterDelegate.setWaiter(waiter);
waiterDelegate.service("John");
//结果
waiter greeting to,Mr.Peter
waiter serve to,Mr.Peter
How are you,Mr.John
waiter greeting to,Mr.John
How are you,Mr.John
waiter serve to,Mr.John

3.7 复合切点切面

//现在想在WaiterDelegate#service()方法里执行greetTo方法时再织入增强。
public class GreetingComposablePointcut {

    public Pointcut getIntersectionPointcut(){
        ComposablePointcut composablePointcut=new ComposablePointcut();// 复合切点
        Pointcut p1=new ControlFlowPointcut(WaiterDelegate.class,"service");  //流程切点
        NameMatchMethodPointcut p2=new NameMatchMethodPointcut(); //方法名切点
        p2.addMethodName("greetTo");
        return composablePointcut.intersection(p1).intersection((Pointcut)p2);  //两个切点交集操作
     }
}

//xml配置
<bean id="composablePointcut" class="advisor.GreetingComposablePointcut"/>
<bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
          p:pointcut="#{composablePointcut.intersectionPointcut}"
          p:advice-ref="greetingBeforeAdvice"/>
<bean id="waiterComposable" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:interceptorNames="composableAdvisor"
          p:proxyTargetClass="true"
          p:target-ref="waiterTarget"/>
//测试方法
Waiter waiter=(Waiter)context.getBean("waiterComposable");
WaiterDelegate waiterDelegate=new WaiterDelegate();
waiterDelegate.setWaiter(waiter);
waiterDelegate.service("Peter");
//结果
How are you,Mr.Peter
waiter greeting to,Mr.Peter
waiter serve to,Mr.Peter

3.8 引介切面

//xml配置
<bean id="introductionAdvisor" class="org.springframework.aop.support.DefaultIntroductionAdvisor">
        <constructor-arg>
            <bean class="aop.introduction.PerformanceMonitor"/>
        </constructor-arg>
</bean>
<bean id="timeMonitor1" class="aop.introduction.TimeMonitor"/>
<bean id="monitor1" class="org.springframework.aop.framework.ProxyFactoryBean"
          p:target-ref="timeMonitor1"
          p:proxyTargetClass="true"
          p:interceptorNames="introductionAdvisor"/>

//测试方法
 TimeMonitor monitor=context.getBean("monitor1",TimeMonitor.class);
 monitor.record();
 MyMonitor myMonitor=(MyMonitor)monitor;
 myMonitor.setMonitorActive(true);
 monitor.record();
 //结果
开始计时
org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.record
开始计时

四、自动创建代理

4.1 分类

—————-基于Bean配置名规则的自动代理创建器,实现类为BeanNameAutoProxyCreator。
—————-基于Advisor匹配机制的自动代理创建器,它会对容器中所有的Advisor进行扫描,自动将这些切面应用到匹配的bean中,实现类是DefaultAdvisorAutoProxyCreator。
—————-基于Bean中AspectJ注解标签的自动代理创建器,实现类为AnnotationAwareAspectJAutoProxyCreator。

4.2 BeanNameAutoProxyCreator

//xml配置
<bean id="waiterauto" class="aop.beforeadvice.NaiveWaiter"/>
<bean id="sellerAuto" class="advisor.Seller"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
      p:interceptorNames="greetingBeforeAdvice"
      p:beanNames="*Auto"
      p:optimize="true"/>
//测试方法
Waiter waiter=(Waiter)context.getBean("waiterauto");
Seller seller=(Seller)context.getBean("sellerAuto");
waiter.greetTo("Peter");
seller.greetTo("Peter");
//结果
waiter greeting to,Mr.Peter
How are you,Mr.Peter
seller greet to,Mr.Peter

4.3 DefaultAdvisorAutoProxyCreator

//xml配置
<bean id="waiterauto" class="aop.beforeadvice.NaiveWaiter"/>
<bean id="sellerAuto" class="advisor.Seller"/>
<bean id="greetingBeforeAdvice" class="aop.beforeadvice.GreetingBeforeAdvice"/>
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
          p:advice-ref="greetingBeforeAdvice"
          p:patterns="advisor\..*\.*greetTo.*"/>
//方法测试
Waiter waiter=(Waiter)context.getBean("waiterauto");
Seller seller=(Seller)context.getBean("sellerAuto");
waiter.greetTo("Peter");
seller.greetTo("Peter");
waiter.serveTo("Peter");
//结果
waiter greeting to,Mr.Peter
How are you,Mr.Peter
seller greet to,Mr.Peter
waiter serve to,Mr.Peter

猜你喜欢

转载自blog.csdn.net/liouyi250/article/details/79392253
今日推荐