AOP创建切面

12 篇文章 0 订阅

AOP,就是面向切面编程。
什么是切面呢?增强+切点就是切面。需要向切面里注入一个增强
前面说了增强,这里我们说一说切点,切点就是特定类的特定方法。
Pointcut = ClassFilter + MethodMatcher.
Advisor = Pointcut + Advice
三种切面类型:

  1. 一般切面
  2. 切点切面
  3. 引介切面

Advisor:一般切面,只包含增强,一般不会直接使用
PointcutAdvisor.切点切面,就是比较通用的Advice + Pointcut.
IntroductionAdvisor:引介切面。

切面的实现类

  1. 静态普通方法名匹配切面StaticMethodMatcherPointcutAdvisor.
    匹配所有的目标类
package com.smart.advisor;

import java.lang.reflect.Method;

import org.springframework.aop.ClassFilter;
import org.springframework.aop.support.StaticMethodMatcherPointcutAdvisor;

public class GreetingAdvisor extends StaticMethodMatcherPointcutAdvisor {

    public boolean matches(Method method, Class clazz) {
        return "greetTo".equals(method.getName());
    }   
    public ClassFilter getClassFilter(){
        return new ClassFilter(){
            public boolean matches(Class clazz){
                return Waiter.class.isAssignableFrom(clazz);
            }
        };

    }
}

只需要实现matches方法,匹配所有的类。
覆盖getClassFilter().让他仅匹配waiter类极其子类。
我们还需要一个增强

package com.smart.advisor;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class GreetingBeforeAdvice implements MethodBeforeAdvice {
    public void before(Method method, Object[] args, Object obj) throws Throwable {
        String clientName = (String)args[0];
        System.out.println(obj.getClass().getName()+"."+method.getName());
        System.out.println("How are you!Mr."+clientName+".");
    }
}

spring配置来定义切面

    <!-- 普通方法名匹配切面 -->
    <bean id="waiterTarget" class="com.smart.advisor.Waiter" />
    <bean id="sellerTarget" class="com.smart.advisor.Seller" />

    <bean id="greetingAdvice"
    class="com.smart.advisor.GreetingBeforeAdvice" />
    <!--切面中注入增强-->
    <bean id="greetingAdvisor" 
    class="com.smart.advisor.GreetingAdvisor"
    p:advice-ref="greetingAdvice" />
    <!--公共配置信息-->
    <bean id="parent" abstract="true"
        class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="greetingAdvisor" p:proxyTargetClass="true" />
    <!--两个代理Bean通过parent放入代理工厂中-->
    <bean id="waiter" parent="parent" p:target-ref="waiterTarget" />
    <bean id="seller" parent="parent" p:target-ref="sellerTarget" />

当有多个类似的方法,需要配置切面的时候,就应该使用正则表达式方法匹配切面。
RegexpMethodPointcutAdvisor

    <!-- 正则表达式方法名匹配切面 -->
    <bean id="regexpAdvisor"
        class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
        p:advice-ref="greetingAdvice"> <!--增强注入-->
        <property name="patterns"> <!--匹配多个正则方法-->
            <list>
                <value>.*greet.*</value>
            </list>
        </property>
    </bean>
    <bean id="waiter1" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="regexpAdvisor"  <!--表明切面-->
        p:target-ref="waiterTarget" <!--表明目标类-->
        p:proxyTargetClass="true" />

动态切面
应用场景:比如服务员打招呼,只有当顾客是刘洋的时候,她才启用增强。
这就是动态界面,和方法的入参有关系。的切面
通过继承DynamicMethodMathcherPointcut类来实现。这个只是切点类。
通过配置,切点类+增强类 就有了切面。

package com.smart.advisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

import org.springframework.aop.support.DynamicMethodMatcherPointcut;

public class GreetingDynamicPointcut extends DynamicMethodMatcherPointcut {
    private static List<String> specialClientList = new ArrayList<String>();
    static {
        specialClientList.add("John");
        specialClientList.add("Tom");
    }
//  public ClassFilter getClassFilter() {
//      return new ClassFilter() {
//          public boolean matches(Class clazz) {
//              System.out.println("调用getClassFilter()对"+clazz.getName()+"做静态检查.");
//              return Waiter.class.isAssignableFrom(clazz);
//          }
//      };
//  }
//  public boolean matches(Method method, Class clazz) {
//      System.out.println("调用matches(method,clazz)对"+clazz.getName()+"."+method.getName()+"做静态检查.");
//      return "greetTo".equals(method.getName());
//  }
    public boolean matches(Method method, Class clazz, Object[] args) {
        System.out.println("调用matches(method,clazz)对"+clazz.getName()+"."+method.getName()+"做动态检查.");
        String clientName = (String) args[0];
        return specialClientList.contains(clientName);
    }

}
    <!-- 动态切面 -->
    <bean id="dynamicAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <property name="pointcut">
            <bean class="com.smart.advisor.GreetingDynamicPointcut" />
        </property>
        <property name="advice">
            <bean class="com.smart.advisor.GreetingBeforeAdvice" />
        </property>
    </bean>
        <bean id="waiter2" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="dynamicAdvisor" p:target-ref="waiterTarget"
        p:proxyTargetClass="true" />

流程切面:
前面的动态切面和入参有关,流程切面和调用者有关系。比如:只有通过service调用的greetto,和serveto才能触发他们的增强。通过ControlFlowPointcut实现。

package com.smart.advisor;

public class WaiterDelegate {
    private Waiter waiter;
    public void service(String clientName) {
        waiter.greetTo(clientName);
        waiter.serveTo(clientName);
    }
    public void setWaiter(Waiter waiter) {
        this.waiter = waiter;
    }
}

通过spring配置切面:

    <!-- 控制流程切面 -->
    <bean id="controlFlowPointcut" class="org.springframework.aop.support.ControlFlowPointcut">
        <constructor-arg type="java.lang.Class"
            value="com.smart.advisor.WaiterDelegate" />
        <constructor-arg type="java.lang.String" value="service" /><!--service 所有的方法都会织入增强-->
    </bean>
    <bean id="controlFlowAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
        p:pointcut-ref="controlFlowPointcut" p:advice-ref="greetingAdvice" />
    <bean id="waiter3" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="controlFlowAdvisor" p:target-ref="waiterTarget"
        p:proxyTargetClass="true" />

复合切点切面:
上面的流程切面,service中所有的方法都会织入增强,如果我们只想greeTO()织入增强,我们应该如何做呢?这时候就可以使用复合切点切面:流程切点+方法名切点
ComposablePointcut.提供了切点之前复合运算的功能。

通过Pointcuts工具类,可以完成切点的交集,并集运算。
intersection
union

package com.smart.advisor;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.ComposablePointcut;
import org.springframework.aop.support.ControlFlowPointcut;
import org.springframework.aop.support.NameMatchMethodPointcut;

public class GreetingComposablePointcut {
   public Pointcut getIntersectionPointcut(){
       ComposablePointcut cp = new ComposablePointcut(); //复合切点
       Pointcut pt1 = new ControlFlowPointcut(WaiterDelegate.class,"service");//流程切点
       NameMatchMethodPointcut pt2 = new NameMatchMethodPointcut();//方法名切点
       pt2.addMethodName("greetTo");
       return cp.intersection(pt1).intersection((Pointcut)pt2);    //注意这里的方法
   }
}
<!-- 复合切点切面 -->
    <bean id="gcp" class="com.smart.advisor.GreetingComposablePointcut" />
    <bean id="composableAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor"
        p:pointcut="#{gcp.intersectionPointcut}" p:advice-ref="greetingAdvice" />
    <bean id="waiter4" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="composableAdvisor" p:target-ref="waiterTarget"
        p:proxyTargetClass="true" />

引介切面:
是引介增强的封装器;

另外:
前面我们都通过proxyFactoryBean配置代理。Spring中我们使用了BeanPostProcessor 来完成这项工作。
实现类:
自动创建代理:根据名字找到代理的目标类,根据切面中的信息找到代理的目标类。

    <!-- 通过Bean名称自动创建代理 -->
        <bean
        class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"
        p:beanNames="*er" p:interceptorNames="greetingAdvice"
        p:optimize="true"/>
<!--通过Advisor自动创建代理-->
    <bean id="regexpAdvisor"
        class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"
        p:patterns=".*greet.*" p:advice-ref="greetingAdvice"  />
    <bean
        class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"  p:proxyTargetClass="true" />

注意:目标类的内部方法调用是不会使用代理类处理的。比如serverto()中调用greetTo 的时候不会被增强、

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值