精品欧美一区二区三区在线观看 _久久久久国色av免费观看性色_国产精品久久在线观看_亚洲第一综合网站_91精品又粗又猛又爽_小泽玛利亚一区二区免费_91亚洲精品国偷拍自产在线观看 _久久精品视频在线播放_美女精品久久久_欧美日韩国产成人在线

Spring AOP 中,切點有多少種定義方式?

開發(fā) 前端
這個類不是 public 的,所以意味著我們自己開發(fā)中沒法直接使用這個切點。然后大家看到,在 getClassFilter 和 getMethodMatcher 方法中,這里都返回了對應的 TRUE,而這兩個 TRUE 實現(xiàn)非常簡單,就是在需要做比對的地方,不做任何比對,直接返回 true 即可,這就導致了最終將所有東西都攔截下來。

在 Spring AOP 中,我們最常用的切點定義方式主要是兩種:

  1. 使用 execution 進行無侵入攔截。
  2. 使用注解進行攔截。

這應該是是小伙伴們?nèi)粘9ぷ髦惺褂米疃嗟膬煞N切點定義方式了。但是除了這兩種還有沒有其他的呢?今天松哥就來和大家聊一聊這個話題。

1. Pointcut 分類

來看下 Pointcut 的定義:

public interface Pointcut {
 ClassFilter getClassFilter();
 MethodMatcher getMethodMatcher();
 Pointcut TRUE = TruePointcut.INSTANCE;
}

從方法名上就能看出來,getClassFilter 進行類的過濾,getMethodMatcher 進行方法過濾。通過過濾 Class 和過濾 Method,我們就能夠鎖定一個攔截對象了。

再來看下 Pointcut 類的繼承關系圖:

圖片圖片

可以看到,其實實現(xiàn)類不算多,大部分看名字其實都能猜出來是干嘛的,這些實現(xiàn)類我們大致上又可以分為六大類:

  1. 靜態(tài)方法切點:StaticMethodMatcherPointcut 表示靜態(tài)方法切點的抽象基類,默認情況下匹配所有的類,然后通過不同的規(guī)則去匹配不同的方法。
  2. 動態(tài)方法切點:DynamicMethodMatcherPointcut 表示動態(tài)方法切點的抽象基類,默認情況下它匹配所有的類,然后通過不同的規(guī)則匹配不同的方法,這個有點類似于 StaticMethodMatcherPointcut,不同的是,StaticMethodMatcherPointcut 僅對方法簽名進行匹配并且僅匹配一次,而 DynamicMethodMatcherPointcut 會在運行期間檢查方法入?yún)⒌闹担捎诿看蝹魅氲膮?shù)可能都不一樣,所以沒調(diào)用都要去判斷,因此就導致 DynamicMethodMatcherPointcut 的性能差一些。
  3. 注解切點:AnnotationMatchingPointcut。
  4. 表達式切點:ExpressionPointcut。
  5. 流程切點:ControlFlowPointcut。
  6. 復合切點:ComposablePointcut。

除了上面這六個之外,另外還有一個落單的 TruePointcut,這個從名字上就能看出來是攔截一切。

所以滿打滿算,有七種類型的切點,接下來我們就來逐個分析一下。

2. TruePointcut

這個實現(xiàn)類從名字上看就是攔截所有,攔截一切,我們來看下這個類怎么做的:

final class TruePointcut implements Pointcut, Serializable {
    //...
 @Override
 public ClassFilter getClassFilter() {
  return ClassFilter.TRUE;
 }

 @Override
 public MethodMatcher getMethodMatcher() {
  return MethodMatcher.TRUE;
 }
    //...
}

首先小伙伴們注意,這個類不是 public 的,所以意味著我們自己開發(fā)中沒法直接使用這個切點。然后大家看到,在 getClassFilter 和 getMethodMatcher 方法中,這里都返回了對應的 TRUE,而這兩個 TRUE 實現(xiàn)非常簡單,就是在需要做比對的地方,不做任何比對,直接返回 true 即可,這就導致了最終將所有東西都攔截下來。

3. StaticMethodMatcherPointcut

StaticMethodMatcherPointcut 僅對方法名簽名(包括方法名和入?yún)㈩愋图绊樞颍┻M行匹配,靜態(tài)匹配僅判斷一次。

public abstract class StaticMethodMatcherPointcut extends StaticMethodMatcher implements Pointcut {

 private ClassFilter classFilter = ClassFilter.TRUE;


 /**
  * Set the {@link ClassFilter} to use for this pointcut.
  * Default is {@link ClassFilter#TRUE}.
  */
 public void setClassFilter(ClassFilter classFilter) {
  this.classFilter = classFilter;
 }

 @Override
 public ClassFilter getClassFilter() {
  return this.classFilter;
 }


 @Override
 public final MethodMatcher getMethodMatcher() {
  return this;
 }

}

可以看到,這里類的匹配默認就是返回 true,方法的匹配則返回當前對象,也就是看具體的實現(xiàn)。

StaticMethodMatcherPointcut 有幾個寫好的實現(xiàn)類,我們來看下。

3.1 SetterPointcut

看名字就知道,這個可以用來攔截所有的 set 方法:

private static class SetterPointcut extends StaticMethodMatcherPointcut implements Serializable {
 public static final SetterPointcut INSTANCE = new SetterPointcut();
 @Override
 public boolean matches(Method method, Class<?> targetClass) {
  return (method.getName().startsWith("set") &&
    method.getParameterCount() == 1 &&
    method.getReturnType() == Void.TYPE);
 }
 private Object readResolve() {
  return INSTANCE;
 }
 @Override
 public String toString() {
  return "Pointcuts.SETTERS";
 }
}

可以看到,方法的匹配就是斷定當前方法是否為 set 方法,要求方法名以 set 開始,方法只有一個參數(shù)并且方法返回值為 null,精準定位到一個 set 方法。

舉一個使用的例子,如下:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        return Pointcuts.SETTERS;
    }
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了。。。");
                Object proceed = invocation.proceed();
                System.out.println(name + " 方法執(zhí)行結束了。。。");
                return proceed;
            }
        };
    }
    @Override
    public boolean isPerInstance() {
        return true;
    }
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.setA(5);

由于 SetterPointcut 是私有的,無法直接 new,可以通過工具類 Pointcuts 獲取到實例。

3.2 GetterPointcut

GetterPointcut 和 SetterPointcut 類似,如下:

private static class GetterPointcut extends StaticMethodMatcherPointcut implements Serializable {
 public static final GetterPointcut INSTANCE = new GetterPointcut();
 @Override
 public boolean matches(Method method, Class<?> targetClass) {
  return (method.getName().startsWith("get") &&
    method.getParameterCount() == 0);
 }
 private Object readResolve() {
  return INSTANCE;
 }
 @Override
 public String toString() {
  return "Pointcuts.GETTERS";
 }
}

我覺得這個應該就不用過多解釋了吧,跟前面的 SetterPointcut 類似,對照理解就行了。

3.3 NameMatchMethodPointcut

這個是根據(jù)方法名來做匹配。

public class NameMatchMethodPointcut extends StaticMethodMatcherPointcut implements Serializable {

 private List<String> mappedNames = new ArrayList<>();
 public void setMappedName(String mappedName) {
  setMappedNames(mappedName);
 }
 public void setMappedNames(String... mappedNames) {
  this.mappedNames = new ArrayList<>(Arrays.asList(mappedNames));
 }
 public NameMatchMethodPointcut addMethodName(String name) {
  this.mappedNames.add(name);
  return this;
 }


 @Override
 public boolean matches(Method method, Class<?> targetClass) {
  for (String mappedName : this.mappedNames) {
   if (mappedName.equals(method.getName()) || isMatch(method.getName(), mappedName)) {
    return true;
   }
  }
  return false;
 }
 protected boolean isMatch(String methodName, String mappedName) {
  return PatternMatchUtils.simpleMatch(mappedName, methodName);
    }
}

可以看到,這個就是從外部傳一個方法名稱列表進來,然后在 matches 方法中進行匹配即可,匹配的時候直接調(diào)用 equals 方法進行匹配,如果 equals 方法沒有匹配上,則調(diào)用 isMatch 方法進行匹配,這個最終調(diào)用到 PatternMatchUtils.simpleMatch 方法,這是 Spring 中提供的一個工具類,支持通配符的匹配。

舉個簡單例子:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.setMappedNames("add","set*");
        return pointcut;
    }
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了。。。");
                Object proceed = invocation.proceed();
                System.out.println(name + " 方法執(zhí)行結束了。。。");
                return proceed;
            }
        };
    }
    @Override
    public boolean isPerInstance() {
        return true;
    }
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3,4);
calculator.minus(3, 4);
calculator.setA(5);

這里我設置的是攔截方法名為 add 或者方法名以 set 開頭的方法。

3.4 JdkRegexpMethodPointcut

這個是支持通過正則去匹配方法名,匹配上的方法就會被攔截下來。

public class JdkRegexpMethodPointcut extends AbstractRegexpMethodPointcut {
 private Pattern[] compiledPatterns = new Pattern[0];
 private Pattern[] compiledExclusionPatterns = new Pattern[0];
 @Override
 protected void initPatternRepresentation(String[] patterns) throws PatternSyntaxException {
  this.compiledPatterns = compilePatterns(patterns);
 }
 @Override
 protected void initExcludedPatternRepresentation(String[] excludedPatterns) throws PatternSyntaxException {
  this.compiledExclusionPatterns = compilePatterns(excludedPatterns);
 }
 @Override
 protected boolean matches(String pattern, int patternIndex) {
  Matcher matcher = this.compiledPatterns[patternIndex].matcher(pattern);
  return matcher.matches();
 }
 @Override
 protected boolean matchesExclusion(String candidate, int patternIndex) {
  Matcher matcher = this.compiledExclusionPatterns[patternIndex].matcher(candidate);
  return matcher.matches();
 }
 private Pattern[] compilePatterns(String[] source) throws PatternSyntaxException {
  Pattern[] destination = new Pattern[source.length];
  for (int i = 0; i < source.length; i++) {
   destination[i] = Pattern.compile(source[i]);
  }
  return destination;
 }
}

可以看到,這里實際上就是傳入正則表達式,然后通過正則表達式去匹配方法名是否滿足條件。正則表達式可以傳入多個,系統(tǒng)會在 JdkRegexpMethodPointcut 的父類中進行遍歷逐個進行匹配,我舉一個例子:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        JdkRegexpMethodPointcut pc = new JdkRegexpMethodPointcut();
        pc.setPatterns("org.javaboy.bean.aop3.ICalculator.set.*");
        pc.setExcludedPattern("org.javaboy.bean.aop3.ICalculator.setA");
        return pc;
    }
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了。。。");
                Object proceed = invocation.proceed();
                System.out.println(name + " 方法執(zhí)行結束了。。。");
                return proceed;
            }
        };
    }
    @Override
    public boolean isPerInstance() {
        return true;
    }
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3,4);
calculator.minus(3, 4);
calculator.setA(5);

上面這個例子也是攔截 setXXX 方法,不過需要注意的是,方法名匹配的時候使用的是方法的全路徑。

另外還需要注意,在配置匹配規(guī)則的時候,還可以設置 ExcludedPattern,實際上在匹配的時候,首先進行正向匹配,就是先看下方法名是否滿足規(guī)則,如果滿足,則方法名再和 ExcludedPattern 進行比對,如果不滿足,則這個方法才會被確定下來要攔截。

StaticMethodMatcherPointcut 中主要就給我們提供了這些規(guī)則。

4. DynamicMethodMatcherPointcut

這個是動態(tài)方法匹配的切點,默認也是匹配所有類,但是在方法匹配這個問題,每次都會匹配,我們來看下:

public abstract class DynamicMethodMatcherPointcut extends DynamicMethodMatcher implements Pointcut {

 @Override
 public ClassFilter getClassFilter() {
  return ClassFilter.TRUE;
 }

 @Override
 public final MethodMatcher getMethodMatcher() {
  return this;
 }

}

可以看到,getClassFilter 直接返回 TRUE,也就是類就直接匹配了,getMethodMatcher 返回的則是當前對象,那是因為當前類實現(xiàn)了 DynamicMethodMatcher 接口,這就是一個方法匹配器:

public abstract class DynamicMethodMatcher implements MethodMatcher {

 @Override
 public final boolean isRuntime() {
  return true;
 }
 @Override
 public boolean matches(Method method, Class<?> targetClass) {
  return true;
 }

}

小伙伴們看到,這里 isRuntime 方法返回 true,該方法為 true,意味著三個參數(shù)的 matches 方法將會被調(diào)用,所以這里兩個參數(shù)的 matches 方法可以直接返回 true,不做任何控制。

當然,也可以在兩個參數(shù)的 matches 方法中做一些前置的判斷。

我們來看一個簡單例子:

public class MyDynamicMethodMatcherPointcut extends DynamicMethodMatcherPointcut {
    @Override
    public boolean matches(Method method, Class<?> targetClass) {
        return method.getName().startsWith("set");
    }

    @Override
    public boolean matches(Method method, Class<?> targetClass, Object... args) {
        return method.getName().startsWith("set") && args.length == 1 && Integer.class.isAssignableFrom(args[0].getClass());
    }
}

在實際執(zhí)行過程中,兩個參數(shù)的 matches 方法返回 true,三個參數(shù)的 matches 方法才會執(zhí)行,如果兩個參數(shù)的 matches 方法返回 false,則三個參數(shù)的 matches 就不會執(zhí)行了。所以也可以兩個參數(shù)的 matches 方法直接固定返回 true,只在三個參數(shù)的 matches 方法中做匹配操作即可。

然后使用這個切點:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        return new MyDynamicMethodMatcherPointcut();
    }
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了。。。");
                Object proceed = invocation.proceed();
                System.out.println(name + " 方法執(zhí)行結束了。。。");
                return proceed;
            }
        };
    }
    @Override
    public boolean isPerInstance() {
        return true;
    }
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3,4);
calculator.minus(3, 4);
calculator.setA(5);

5. AnnotationMatchingPointcut

這個就是判斷類或者方法上是否存在某個注解,如果存在,則將之攔截下來,否則不攔截。

先來看下這個類的定義:

public class AnnotationMatchingPointcut implements Pointcut {

 private final ClassFilter classFilter;

 private final MethodMatcher methodMatcher;

 public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType) {
  this(classAnnotationType, false);
 }

 public AnnotationMatchingPointcut(Class<? extends Annotation> classAnnotationType, boolean checkInherited) {
  this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
  this.methodMatcher = MethodMatcher.TRUE;
 }

 public AnnotationMatchingPointcut(@Nullable Class<? extends Annotation> classAnnotationType,
   @Nullable Class<? extends Annotation> methodAnnotationType) {

  this(classAnnotationType, methodAnnotationType, false);
 }

 public AnnotationMatchingPointcut(@Nullable Class<? extends Annotation> classAnnotationType,
   @Nullable Class<? extends Annotation> methodAnnotationType, boolean checkInherited) {

  if (classAnnotationType != null) {
   this.classFilter = new AnnotationClassFilter(classAnnotationType, checkInherited);
  }
  else {
   this.classFilter = new AnnotationCandidateClassFilter(methodAnnotationType);
  }

  if (methodAnnotationType != null) {
   this.methodMatcher = new AnnotationMethodMatcher(methodAnnotationType, checkInherited);
  }
  else {
   this.methodMatcher = MethodMatcher.TRUE;
  }
 }


 @Override
 public ClassFilter getClassFilter() {
  return this.classFilter;
 }

 @Override
 public MethodMatcher getMethodMatcher() {
  return this.methodMatcher;
 }

 public static AnnotationMatchingPointcut forClassAnnotation(Class<? extends Annotation> annotationType) {
  Assert.notNull(annotationType, "Annotation type must not be null");
  return new AnnotationMatchingPointcut(annotationType);
 }
 public static AnnotationMatchingPointcut forMethodAnnotation(Class<? extends Annotation> annotationType) {
  Assert.notNull(annotationType, "Annotation type must not be null");
  return new AnnotationMatchingPointcut(null, annotationType);
 }

}

首先小伙伴們注意到,這個類一共有四個構造方法,從上往下分別是:

  1. 傳入類上的注解名稱,根據(jù)類上的注解去判斷是否需要攔截。
  2. 在 1 的基礎之上,再增加一個 checkInherited,這個表示是否需要檢查父類上是否存在相關的注解。
  3. 傳入類上和方法上的注解類型,根據(jù)這個注解類型去判斷是否需要攔截。
  4. 在 3 的基礎之上,再增加一個 checkInherited,這個表示是否需要檢查父類上或者方法上是否存在相關的注解。

其中,第四個構造方法中處理的情況類型比較多,如果用戶傳入了 classAnnotationType,則構建 AnnotationClassFilter 類型的 ClassFilter,否則構建 AnnotationCandidateClassFilter 類型的 ClassFilter;如果用戶傳入了 methodAnnotationType,則構建 AnnotationMethodMatcher 類型的 MethodMatcher,否則方法匹配器就直接返回匹配所有方法。

那么接下來我們就來看下這幾種不同的匹配器。

5.1 AnnotationClassFilter

public class AnnotationClassFilter implements ClassFilter {
    //...
 @Override
 public boolean matches(Class<?> clazz) {
  return (this.checkInherited ? AnnotatedElementUtils.hasAnnotation(clazz, this.annotationType) :
    clazz.isAnnotationPresent(this.annotationType));
 }
    //...
}

這里省略了一些代碼,關鍵地方就是這個匹配方法了,如果需要檢查父類是否包含該注解,則調(diào)用 AnnotatedElementUtils.hasAnnotation 方法進行查找,否則直接調(diào)用 clazz.isAnnotationPresent 方法判斷當前類上是否包含指定注解即可。

5.2 AnnotationCandidateClassFilter

private static class AnnotationCandidateClassFilter implements ClassFilter {
 private final Class<? extends Annotation> annotationType;
 AnnotationCandidateClassFilter(Class<? extends Annotation> annotationType) {
  this.annotationType = annotationType;
 }
 @Override
 public boolean matches(Class<?> clazz) {
  return AnnotationUtils.isCandidateClass(clazz, this.annotationType);
 }
}

這里就是調(diào)用了 AnnotationUtils.isCandidateClass 方法進行判斷,這個方法用來判斷指定類是不是可以承載指定注解的候選類,返回 true 的規(guī)則是:

  1. 以 java. 開頭的注解,所有的類都能承載,這種情況會返回 true。
  2. 目標類不能以 java. 開頭,也就是說 JDK 中的類都不行,不是以 java. 開頭的類就可以返回 true。
  3. 給定類也不能是 Ordered 類。

滿足如上條件,這個類就是符合規(guī)定的。

AnnotationCandidateClassFilter 主要是針對用戶沒有傳入類上注解的情況,這種情況一般都是根據(jù)方法上的注解進行匹配的,所以這里主要是排除一些系統(tǒng)類。

5.3 AnnotationMethodMatcher

public class AnnotationMethodMatcher extends StaticMethodMatcher {
 @Override
 public boolean matches(Method method, Class<?> targetClass) {
  if (matchesMethod(method)) {
   return true;
  }
  // Proxy classes never have annotations on their redeclared methods.
  if (Proxy.isProxyClass(targetClass)) {
   return false;
  }
  // The method may be on an interface, so let's check on the target class as well.
  Method specificMethod = AopUtils.getMostSpecificMethod(method, targetClass);
  return (specificMethod != method && matchesMethod(specificMethod));
 }
 private boolean matchesMethod(Method method) {
  return (this.checkInherited ? AnnotatedElementUtils.hasAnnotation(method, this.annotationType) :
    method.isAnnotationPresent(this.annotationType));
 }
}

方法匹配就是首先檢查方法上是否有注解,如果開啟了 checkInherited,則去檢查一下父類對應的方法上是否有相關的注解,如果有,則表示方法匹配上了,返回 true。

否則先去檢查一下當前類是否是一個代理對象,代理對象中對應的方法肯定是沒有注解的,直接返回 false。

如果前面兩步還沒返回,最后考慮到目前這個方法可能是在一個接口上,要檢查一下它的實現(xiàn)類是否包含該注解。

這就是 AnnotationMatchingPointcut。松哥也舉一個簡單例子吧。

5.4 實踐

首先我自定義一個注解,如下:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAction {
}

然后將之添加到某一個方法上:

public class CalculatorImpl implements ICalculator {
    @Override
    public void add(int a, int b) {
        System.out.println(a + "+" + b + "=" + (a + b));
    }

    @MyAction
    @Override
    public int minus(int a, int b) {
        return a - b;
    }

    @Override
    public void setA(int a) {
        System.out.println("a = " + a);
    }
}

最后來實踐一下:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        return new AnnotationMatchingPointcut(null, MyAction.class);
    }
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了。。。");
                Object proceed = invocation.proceed();
                System.out.println(name + " 方法執(zhí)行結束了。。。");
                return proceed;
            }
        };
    }
    @Override
    public boolean isPerInstance() {
        return true;
    }
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3,4);
calculator.minus(3, 4);
calculator.setA(5);

只有 minus 方法被攔截下來了。

6. ExpressionPointcut

這個其實就是我們?nèi)粘i_發(fā)中使用最多的一種切點定義方式,可能項目中 99% 的切點定義都是使用的 ExpressionPointcut。這個具體用法我這里就不說了,因為比較豐富,都能單獨整一篇文章了,如果小伙伴對 ExpressionPointcut 的基礎用法還不熟悉的話,可以在公眾號【江南一點雨】后臺回復 ssm,有松哥之前錄制的入門視頻教程可以參考。

我這里就簡單把它的實現(xiàn)思路來和小伙伴們梳理一下,ExpressionPointcut 的實現(xiàn)都在 AspectJExpressionPointcut 類中,該類支持使用切點語言來對要攔截的方法做一個非常精確的描述,精確到要攔截方法的返回值,AspectJExpressionPointcut 類的實現(xiàn)比較長也比較復雜,我這里貼其中一些關鍵的代碼來看下:

public class AspectJExpressionPointcut extends AbstractExpressionPointcut
  implements ClassFilter, IntroductionAwareMethodMatcher, BeanFactoryAware {

 private static final Set<PointcutPrimitive> SUPPORTED_PRIMITIVES = Set.of(
   PointcutPrimitive.EXECUTION,
   PointcutPrimitive.ARGS,
   PointcutPrimitive.REFERENCE,
   PointcutPrimitive.THIS,
   PointcutPrimitive.TARGET,
   PointcutPrimitive.WITHIN,
   PointcutPrimitive.AT_ANNOTATION,
   PointcutPrimitive.AT_WITHIN,
   PointcutPrimitive.AT_ARGS,
   PointcutPrimitive.AT_TARGET);

 @Override
 public ClassFilter getClassFilter() {
  obtainPointcutExpression();
  return this;
 }

 @Override
 public MethodMatcher getMethodMatcher() {
  obtainPointcutExpression();
  return this;
 }

 /**
  * Check whether this pointcut is ready to match,
  * lazily building the underlying AspectJ pointcut expression.
  */
 private PointcutExpression obtainPointcutExpression() {
  if (getExpression() == null) {
   throw new IllegalStateException("Must set property 'expression' before attempting to match");
  }
  if (this.pointcutExpression == null) {
   this.pointcutClassLoader = determinePointcutClassLoader();
   this.pointcutExpression = buildPointcutExpression(this.pointcutClassLoader);
  }
  return this.pointcutExpression;
 }
}

其實關鍵還是要獲取到 ClassFilter 和 MethodMatcher,然后調(diào)用其 matches 方法,當前類剛好就是實現(xiàn)了這兩個,所以直接返回 this 就可以了。在 getClassFilter 或者 getMethodMatcher 方法執(zhí)行之前,都會先調(diào)用 obtainPointcutExpression 方法,去解析我們傳入的 expression 字符串,將之解析為一個 PointcutExpression 對象,接下來的 matches 方法就可以此為依據(jù),進行匹配了。

7. ControlFlowPointcut

ControlFlowPointcut 主要是指目標方法從某一個指定類的指定方法中執(zhí)行,切點才生效,否則不生效。

舉個簡單例子,如下:

public class AopDemo04 {
    public static void main(String[] args) {
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(new CalculatorImpl());
        proxyFactory.addInterface(ICalculator.class);
        proxyFactory.addAdvisor(new PointcutAdvisor() {
            @Override
            public Pointcut getPointcut() {
                return new ControlFlowPointcut(AopDemo04.class, "evl");
            }

            @Override
            public Advice getAdvice() {
                return new MethodInterceptor() {
                    @Override
                    public Object invoke(MethodInvocation invocation) throws Throwable {
                        Method method = invocation.getMethod();
                        String name = method.getName();
                        System.out.println(name + " 方法開始執(zhí)行了。。。");
                        Object proceed = invocation.proceed();
                        System.out.println(name + " 方法執(zhí)行結束了。。。");
                        return proceed;
                    }
                };
            }

            @Override
            public boolean isPerInstance() {
                return true;
            }
        });
        ICalculator calculator = (ICalculator) proxyFactory.getProxy();
        calculator.add(3,4);
        System.out.println("http://///////////////");
        evl(calculator);
    }

    public static void evl(ICalculator iCalculator) {
        iCalculator.add(3, 4);
    }
}

這里切點的意思就是說,必須要從 AopDemo04 這個類的 evl 方法中調(diào)用 add 方法,這個切點才會生效,如果是拿到了 ICalculator 對象后直接調(diào)用 add 方法,那么切點是不會生效的。

ControlFlowPointcut 的原理也很簡單,就是比較一下類名和方法名,如下:

public class ControlFlowPointcut implements Pointcut, ClassFilter, MethodMatcher, Serializable {
 @Override
 public boolean matches(Class<?> clazz) {
  return true;
 }
 @Override
 public boolean matches(Method method, Class<?> targetClass) {
  return true;
 }

 @Override
 public boolean isRuntime() {
  return true;
 }

 @Override
 public boolean matches(Method method, Class<?> targetClass, Object... args) {
  this.evaluations.incrementAndGet();

  for (StackTraceElement element : new Throwable().getStackTrace()) {
   if (element.getClassName().equals(this.clazz.getName()) &&
     (this.methodName == null || element.getMethodName().equals(this.methodName))) {
    return true;
   }
  }
  return false;
 }
 @Override
 public ClassFilter getClassFilter() {
  return this;
 }

 @Override
 public MethodMatcher getMethodMatcher() {
  return this;
 }
}

大家可以看到,isRuntime 方法返回 true,表示這是一個動態(tài)的方法匹配器。關鍵的 matches 方法,就是根據(jù)調(diào)用棧中的信息,去比較給定的類名和方法名是否滿足。

8. ComposablePointcut

看名字就知道,這個可以將多個切點組合到一起,組合關系有求交集和求并集兩種,分別對應 ComposablePointcut 中的 intersection 方法和 union 方法。

如下案例:

ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(new CalculatorImpl());
proxyFactory.addInterface(ICalculator.class);
proxyFactory.addAdvisor(new PointcutAdvisor() {
    @Override
    public Pointcut getPointcut() {
        NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
        nameMatchMethodPointcut.setMappedNames("add");
        ComposablePointcut pc = new ComposablePointcut((Pointcut) nameMatchMethodPointcut);
        pc.union(new AnnotationMatchingPointcut(null, MyAction.class));
        return pc;
    }
    @Override
    public Advice getAdvice() {
        return new MethodInterceptor() {
            @Override
            public Object invoke(MethodInvocation invocation) throws Throwable {
                Method method = invocation.getMethod();
                String name = method.getName();
                System.out.println(name + " 方法開始執(zhí)行了。。。");
                Object proceed = invocation.proceed();
                System.out.println(name + " 方法執(zhí)行結束了。。。");
                return proceed;
            }
        };
    }
    @Override
    public boolean isPerInstance() {
        return true;
    }
});
ICalculator calculator = (ICalculator) proxyFactory.getProxy();
calculator.add(3,4);
calculator.minus(3, 4);
calculator.setA(5);

在上面的案例中,就是把 NameMatchMethodPointcut 和 AnnotationMatchingPointcut 兩個切點聯(lián)合起來,既攔截方法名為 add 的方法,也攔截含有 @MyAction 注解的方法。

如果將 union 方法改為 intersection,就表示攔截方法名為 add 且被 @MyAction 注解標記的方法。如下:

@Override
public Pointcut getPointcut() {
    NameMatchMethodPointcut nameMatchMethodPointcut = new NameMatchMethodPointcut();
    nameMatchMethodPointcut.setMappedNames("add");
    ComposablePointcut pc = new ComposablePointcut((Pointcut) nameMatchMethodPointcut);
    pc.intersection(new AnnotationMatchingPointcut(null, MyAction.class));
    return pc;
}

其實這種組合切點的原理很簡單,先把我們提供的 ClassFilter 和 MethodMatcher 收集到一個集合中,如果是 union,就直接遍歷集合,只要有一個滿足,就返回 true;如果是 intersection,也是直接遍歷,如果有一個返回 false 就直接返回 false 即可。

以 ClassFilter 為例,我們來簡單看下源碼:

public ComposablePointcut union(ClassFilter other) {
 this.classFilter = ClassFilters.union(this.classFilter, other);
 return this;
}
public abstract class ClassFilters {
 public static ClassFilter union(ClassFilter cf1, ClassFilter cf2) {
  return new UnionClassFilter(new ClassFilter[] {cf1, cf2});
 }
 private static class UnionClassFilter implements ClassFilter, Serializable {

  private final ClassFilter[] filters;

  UnionClassFilter(ClassFilter[] filters) {
   this.filters = filters;
  }

  @Override
  public boolean matches(Class<?> clazz) {
   for (ClassFilter filter : this.filters) {
    if (filter.matches(clazz)) {
     return true;
    }
   }
   return false;
  }

 }
}

可以看到,傳入的多個 ClassFilter 被組裝到一起,在 matches 方法中逐個遍歷,只要其中一個返回 true,就是 true。

9. 小結

好啦,這就是松哥今天和小伙伴們介紹的 7 中 Pointcut 了,希望借此小伙伴們對 Spring AOP 中切點的類型有一個完整的了解。再來回顧一下這其中切點:

  1. 靜態(tài)方法切點:StaticMethodMatcherPointcut 表示靜態(tài)方法切點的抽象基類,默認情況下匹配所有的類,然后通過不同的規(guī)則去匹配不同的方法。
  2. 動態(tài)方法切點:DynamicMethodMatcherPointcut 表示動態(tài)方法切點的抽象基類,默認情況下它匹配所有的類,然后通過不同的規(guī)則匹配不同的方法,這個有點類似于 StaticMethodMatcherPointcut,不同的是,StaticMethodMatcherPointcut 僅對方法簽名進行匹配并且僅匹配一次,而 DynamicMethodMatcherPointcut 會在運行期間檢查方法入?yún)⒌闹?,由于每次傳入的參?shù)可能都不一樣,所以沒調(diào)用都要去判斷,因此就導致 DynamicMethodMatcherPointcut 的性能差一些。
  3. 注解切點:AnnotationMatchingPointcut 根據(jù)制定注解攔截目標方法或者類。
  4. 表達式切點:ExpressionPointcut 這個是我們?nèi)粘i_發(fā)中使用最多的一種切點定義方式。
  5. 流程切點:ControlFlowPointcut 這個是要求必須從某一個位置調(diào)用目標方法,切點才會生效。
  6. 復合切點:ComposablePointcut 這個是把多個攔截器組裝在一起使用,有交集和并集兩種組裝方式。
  7. TruePointcut 這是框架內(nèi)部使用的一個切點,表示攔截一切。
責任編輯:武曉燕 來源: 江南一點雨
相關推薦

2015-05-06 10:05:22

javajava框架spring aop

2024-04-01 08:38:57

Spring@AspectAOP

2024-09-26 14:48:35

SpringAOP范式

2010-09-09 15:45:15

IT認證

2021-07-05 18:05:40

SpringBean方法

2011-06-03 11:53:06

Spring接口

2020-06-17 08:31:10

權限控制Spring Secu

2021-07-27 10:49:10

SpringSecurity權限

2022-07-01 09:39:58

SpringAOPIOC

2025-03-17 08:10:00

aviatorSpringJVM

2011-07-22 17:22:20

Spring

2024-12-18 16:19:51

2024-07-08 09:03:31

2011-02-28 13:51:30

Spring事物配置

2009-06-19 18:26:38

Spring事務配置

2012-07-17 09:16:16

SpringSSH

2011-11-25 10:25:27

SpringJava

2022-09-27 12:01:56

Spring異步調(diào)用方式

2019-11-29 16:21:22

Spring框架集成

2022-06-07 07:58:45

SpringSpring AOP
點贊
收藏

51CTO技術棧公眾號

成人免费视频观看视频| 精品欧美乱码久久久久久| 蜜桃视频在线观看91| 波多野结衣高清在线| 国产大片一区| 亚洲精品一区二区三区99| 自慰无码一区二区三区| 超碰在线国产| 国产很黄免费观看久久| 欧洲成人性视频| 国产高清视频免费在线观看| jizz性欧美2| 在线视频一区二区三| 日韩人妻一区二区三区蜜桃视频| 天堂a中文在线| 国内一区二区在线| 热久久免费国产视频| 亚洲一级生活片| 国产毛片一区二区三区| 99er精品视频| 久国产精品韩国三级视频| 性色av一区二区三区免费| 在线免费观看视频| 久久99国产精品久久99大师| 5858s免费视频成人| 99热在线这里只有精品| 五月婷婷视频在线观看| 欧美激情一区二区三区四区| 国产亚洲精品自在久久| 国产高清免费观看| 蜜桃精品视频在线观看| 日本欧美精品在线| 日产亚洲一区二区三区| 欧美激情1区2区3区| 在线亚洲午夜片av大片| 美女久久久久久久久久| 国产精品15p| 欧美一区二区视频在线观看2020| 538在线视频观看| 在线看片福利| 婷婷丁香久久五月婷婷| 日韩久久久久久久久久久久| 高清全集视频免费在线| 国产精品久久久久aaaa樱花 | 18岁视频在线观看| 国产偷人妻精品一区| 日韩综合av| 欧美色成人综合| 国产精品久久久久9999小说| 成人线上视频| 色综合久久99| 女性隐私黄www网站视频| 中文一区一区三区高中清不卡免费| 亚洲综合男人的天堂| 国产小视频免费| 在线看女人毛片| 亚洲丝袜美腿综合| 爱爱爱视频网站| 成人影欧美片| 亚洲精品日韩专区silk| www.国产二区| caoprom在线| 亚洲 欧美综合在线网络| 精品人妻少妇一区二区| 国产一二三在线| 精品国产电影一区| 男人日女人bb视频| 国产欧美一区二区三区精品酒店| 欧美性高潮在线| 毛葺葺老太做受视频| 精品网站在线| 制服丝袜中文字幕亚洲| 爱情岛论坛亚洲自拍| 无码人妻久久一区二区三区| 三妻四妾完整版在线观看电视剧 | 国产一级二级毛片| 亚洲福利久久| 欧美专区日韩视频| 一区二区乱子伦在线播放| 久久精品久久99精品久久| 91精品在线看| 日本黄色三级视频| 久久精品在这里| 亚洲欧洲久久| 亚洲wwwww| 欧美日韩国产专区| 青青视频在线播放| 欧美成人福利| 精品国产乱码久久久久久久久| 三级男人添奶爽爽爽视频| 妖精视频一区二区三区| 日韩天堂在线视频| 男女视频免费看| 久久精品久久99精品久久| 国产 高清 精品 在线 a | 91蜜桃视频在线| 亚洲日本无吗高清不卡| 女同一区二区免费aⅴ| 日韩欧美国产视频| 国产精品9999久久久久仙踪林| 久久综合在线观看| 亚洲1区在线观看| 亚洲精品自产拍| 日本在线一级片| 麻豆成人在线| av成人在线电影| av在线中文| 香蕉乱码成人久久天堂爱免费| 日本美女高潮视频| 99久久免费精品国产72精品九九| 亚洲小视频在线观看| 国产一级片视频| 另类中文字幕网| 精品久久久久久一区| 国产写真视频在线观看| 一本久道久久综合中文字幕| 亚洲天堂小视频| 日韩在线看片| 日韩av日韩在线观看| 亚洲精品97久久中文字幕无码| 国产精品婷婷午夜在线观看| 精品这里只有精品| 中文字幕一区二区三区中文字幕| 在线观看日韩av| 91看片在线播放| 国产福利精品一区| 杨幂一区欧美专区| 欧美精选视频一区二区| 亚洲国产免费av| 青青草原在线免费观看视频| 韩国v欧美v亚洲v日本v| 亚洲蜜桃在线| abab456成人免费网址| 亚洲欧美国内爽妇网| www成人在线| 成人av网站在线| 拔插拔插海外华人免费| 试看120秒一区二区三区| 久久亚洲精品国产亚洲老地址| 中文字幕视频二区| 中文字幕国产一区二区| 欧美大片一区二区| 午夜诱惑痒痒网| 天天综合精品| 成人av在线网址| 夜级特黄日本大片_在线| 在线看一区二区| www..com.cn蕾丝视频在线观看免费版| 99亚洲视频| 国内一区在线| 中文字幕资源网在线观看免费| 亚洲精品电影在线| 国产一级精品视频| 26uuu欧美日本| 丝袜老师办公室里做好紧好爽| 色先锋久久影院av| 清纯唯美亚洲激情| 国产免费av高清在线| 欧亚洲嫩模精品一区三区| 国产又粗又猛又爽视频| 免费亚洲电影在线| 国产系列第一页| 精品91福利视频| 欧美午夜女人视频在线| 日本二区在线观看| 久久福利资源站| 国产黄色激情视频| 欧美人成在线观看ccc36| 青青在线视频一区二区三区| 国产视频网站在线| 91精品欧美综合在线观看最新| 国产黄色片在线免费观看| 成人黄色国产精品网站大全在线免费观看 | 少妇一区二区三区| 国产成人一区二区三区电影| 成av人电影在线观看| 欧美一级在线视频| 欧美福利视频一区二区| 国产三级三级三级精品8ⅰ区| 奇米影音第四色| 欧美日韩p片| 欧美日韩一区二区视频在线| 97久久中文字幕| 欧美激情视频在线观看| 欧美色综合一区二区三区| 欧美日韩一区二区三区免费看| 欧美在线视频第一页| 97精品视频在线观看自产线路二| 午夜精品在线免费观看| 欧美va天堂| 久久久综合亚洲91久久98| 国产亚洲精品精品国产亚洲综合| 精品视频9999| 婷婷久久青草热一区二区| 不卡av免费观看| 亚洲一级黄色片| 精品国产亚洲av麻豆| 色屁屁一区二区| 劲爆欧美第一页| 国产亚洲美州欧州综合国| www.久久com| 日韩精品一二三| 日韩在线观看a| 日韩欧美在线中字| 久久精品欧美| 精品国产不卡一区二区| 国产精品第三页| 黄页网站大全在线免费观看| 最新国产精品拍自在线播放| 色偷偷在线观看| 91精品国产91久久综合桃花| 成人一二三四区| 婷婷久久综合九色综合绿巨人 | 日本少妇激三级做爰在线| 日韩黄色免费网站| 啊啊啊一区二区| 国产精品xvideos88| 一本久道久久综合| 久久99蜜桃| 精品一区久久| 都市激情亚洲| 亚洲自拍小视频免费观看| 成人四虎影院| 国产精品成久久久久三级| 爱搞国产精品| 欧美激情亚洲精品| yellow91字幕网在线| 中文字幕久久久| 国产黄色片在线播放| 日韩电视剧在线观看免费网站| 国产肥老妇视频| 制服丝袜亚洲播放| 亚洲午夜激情视频| 在线看国产一区| 蜜臀尤物一区二区三区直播| 欧美日韩免费观看中文| 国产午夜精品无码| 亚洲国产欧美在线| 久青草视频在线观看| 一区二区视频免费在线观看| 情侣偷拍对白清晰饥渴难耐| 国产精品美女一区二区| 日本综合在线观看| 国产片一区二区| 精品人体无码一区二区三区| 国产欧美日韩精品在线| 国产又大又粗又爽的毛片| 久久午夜免费电影| 一级片视频免费看| 国产欧美综合在线观看第十页| 国产精品jizz在线观看麻豆| 一级黄色免费看| 欧美日本不卡视频| 97视频免费在线| 日韩一区二区在线观看视频| 国产高中女学生第一次| 日韩精品一区二区三区视频| 亚洲精品97久久中文字幕| 亚洲高清福利视频| 全色精品综合影院| 中文字幕日韩av| 国产精品久久麻豆| 欧美激情精品久久久久久变态| 欧美日韩经典丝袜| 久久欧美在线电影| 免费在线小视频| 国产成人精品av| 日韩一区二区三区四区五区 | 97精品视频在线播放| 久久人体大尺度| 成人欧美在线视频| 盗摄牛牛av影视一区二区| 久久99精品久久久久子伦 | 在线一区二区日韩| av在线app| 97视频免费观看| 成人看片在线观看| 亚洲bt天天射| 欧美调教网站| 亚洲免费在线精品一区| 欧美午夜a级限制福利片| 日日橹狠狠爱欧美超碰| 麻豆高清免费国产一区| 善良的小姨在线| 久久免费国产精品| www.av成人| 福利二区91精品bt7086| 好吊色欧美一区二区三区四区 | 91沈先生作品| 三级精品视频| 中文字幕一区二区三区乱码 | 一区二区三区在线观看视频| 久久一区二区三区视频| 欧美丰满一区二区免费视频| 四季av日韩精品一区| 日韩网站免费观看高清| 成人影院在线视频| 国产日韩在线亚洲字幕中文| 果冻天美麻豆一区二区国产| 亚洲国产一区二区三区在线播| 欧美午夜久久| 亚洲天堂2018av| 337p粉嫩大胆色噜噜噜噜亚洲| frxxee中国xxx麻豆hd| 欧美丝袜一区二区| 国产叼嘿视频在线观看| 影音先锋日韩有码| 精品三级久久| 99re在线| 国产精品黑丝在线播放| 成熟老妇女视频| 成人午夜免费av| 午夜剧场免费在线观看| 欧美性猛交一区二区三区精品| 人妻va精品va欧美va| 久久久电影免费观看完整版| 亚洲伦理影院| 久久久水蜜桃| 99精品视频免费全部在线| 五月天六月丁香| 国产精品护士白丝一区av| 黄色一级视频免费看| 欧美人与牛zoz0性行为| 欧美一区二区在线| 国产一区二区精品| 中文字幕天堂av| 亚洲卡通欧美制服中文| 91精品国产综合久| 中文字幕日韩高清| 日本肉肉一区| 视频一区二区三区免费观看| 蜜桃av综合| 女~淫辱の触手3d动漫| 欧美日韩激情网| 天堂成人在线| 26uuu国产精品视频| 欧美男男freegayvideosroom| www.av蜜桃| 成人在线综合网| 国产黄色片视频| 亚洲国内精品在线| 超碰高清在线| 久久精品人人做人人爽电影| 西西裸体人体做爰大胆久久久| 少妇一级淫免费观看| 第一福利永久视频精品| 日本福利片在线| 国产精品91在线观看| 日本一本不卡| 香港日本韩国三级网站| 国产精品久久久一本精品| 一区二区自拍偷拍| 国产午夜精品视频免费不卡69堂| 日本一区免费网站| 宅男av一区二区三区| 国产美女精品在线| 91视频免费在线看| 亚洲成人xxx| 2022成人影院| 亚洲 日韩 国产第一区| 久久精品99国产国产精| 91日韩中文字幕| 亚洲精品在线观看视频| 成人欧美magnet| 亚洲 国产 日韩 综合一区| 美女网站色91| 久久久久久久久久久久久女过产乱| 精品国产一区二区三区不卡| 性国裸体高清亚洲| 亚洲自拍三区| 成人午夜激情片| 成人免费视频国产免费| 精品国产一区二区在线| 亚洲精品黑牛一区二区三区| 少妇人妻无码专区视频| 台湾佬成人网| 91免费国产视频| 在线成人欧美| 欧美福利第一页| 日韩精品一区二区在线| 在线男人天堂| a级网站在线观看| 91网站视频在线观看| 中文字幕在线播放日韩| 欧美激情综合色综合啪啪五月| 一本久久青青| 1314成人网| 色婷婷综合久久久久中文一区二区 | 久久中文免费视频| 日韩精品丝袜在线| 日韩三级成人| 97av视频在线观看| 亚洲精品国产品国语在线app| 天堂a√在线| 92看片淫黄大片看国产片| 亚洲一区国产一区| 国产黄色小视频网站| 亚洲欧美在线x视频| 高清不卡一区| 欧美日韩亚洲一二三|