精品欧美一区二区三区在线观看 _久久久久国色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技術棧公眾號

日韩午夜精品视频| 欧美激情在线看| 668精品在线视频| 美女被到爽高潮视频| 素人啪啪色综合| 亚洲人成7777| 美女精品国产| 国产精品嫩草影院桃色| 亚洲午夜极品| 国产亚洲欧美另类中文| 波多野结衣中文字幕在线播放| 爱啪视频在线观看视频免费| 国产午夜精品在线观看| 亚洲专区中文字幕| 在线观看污污网站| 欧美精品观看| 中文字幕成人在线| 欧美 变态 另类 人妖| 四虎在线精品| 欧美日韩一区二区三区| 加勒比海盗1在线观看免费国语版| 亚洲欧洲成人在线| 国产一区二区三区免费看| 欧美重口另类videos人妖| 中国一级片在线观看| 亚洲精品中文字幕99999| 日韩三级.com| 亚洲污视频在线观看| 成人在线黄色电影| 亚洲精品老司机| 亚洲欧美日韩精品综合在线观看| 香蕉视频免费在线看| 国产传媒一区在线| 国产日韩综合一区二区性色av| 久久久久久少妇| 影音国产精品| 欧美成人午夜剧场免费观看| 精品人妻中文无码av在线 | 蜜臀aⅴ国产精品久久久国产老师| 91p九色成人| 一本色道久久加勒比精品| 99在线免费视频观看| av免费在线免费| 中文字幕在线一区免费| 日本一区二区三区视频在线观看| 欧美一级淫片免费视频魅影视频| 国精品**一区二区三区在线蜜桃| 国产精品美女久久| 欧美性猛交xxxx乱大交hd| 免费日韩av片| 日本久久久a级免费| 日韩欧美一级视频| 一区二区三区精品视频在线观看 | 欧美lavv| 婷婷丁香花五月天| 99热99精品| 久久精品成人一区二区三区蜜臀| 亚洲国产精品久久久久爰性色 | 国产精品久久久久久久久影视| 欧美一区二区激情视频| 国产精品社区| 日本国产高清不卡| 少妇一级淫片日本| 美国三级日本三级久久99| 国产精品一区二区三区免费视频| 国内av在线播放| 美女视频黄a大片欧美| 国产综合在线观看视频| 国产美女无遮挡永久免费| 韩国av一区二区三区| 97碰碰视频| 秋霞视频一区二区| 久久综合一区二区| 日韩欧美亚洲在线| 成人在线观看免费网站| 亚洲一区在线观看免费 | 日本不卡视频在线观看| 国产精品丝袜视频| 夜夜爽8888| 国产伦精品一区二区三区在线观看 | 日韩不卡一区二区| 成人免费视频网| 亚洲精品久久久蜜桃动漫 | 三级福利片在线观看| 激情亚洲一区二区三区四区| 亚洲乱码中文字幕久久孕妇黑人| 亚洲一区二区三区四区| 欧美精品v国产精品v日韩精品| 先锋资源在线视频| 婷婷综合电影| 久久精品人人做人人爽| 国产亚洲精久久久久久无码77777| 日韩一级大片| 国产精品视频xxxx| 亚洲成熟女性毛茸茸| 久久久电影一区二区三区| 亚洲一区二区在线看| 超碰97免费在线| 在线免费视频一区二区| 在线观看欧美一区二区| 九九久久精品| 欧美激情第99页| 亚洲av无码不卡| 国产a视频精品免费观看| 欧美日本韩国国产| 日韩精品卡一| 欧美日韩黄色一区二区| 久久久久久婷婷| 91视频精品| 欧美孕妇孕交黑巨大网站| 国产又黄又大又爽| 久久久亚洲高清| 蜜桃视频一区二区在线观看| 日韩av首页| 精品国产一区二区三区不卡 | 制服丝袜中文字幕第一页| 婷婷成人在线| 欧美激情精品久久久久久变态 | 丰满大乳少妇在线观看网站| 欧美日韩国产高清一区二区三区| 亚洲av片不卡无码久久| 欧美91福利在线观看| 国产精品极品美女在线观看免费 | 国产手机视频精品| 久久久久99精品成人片试看| 石原莉奈在线亚洲三区| 国产伦精品一区二区三区视频黑人 | 在线播放日韩欧美| 国产女同在线观看| 福利视频网站一区二区三区| 亚洲一区二区三区免费观看| 欧美男女交配| 日韩精品免费综合视频在线播放| 中文字幕在线有码| 久久91精品国产91久久小草| 性高潮久久久久久久久| 欧美性猛交xxx高清大费中文| 亚洲成人教育av| 久热精品在线观看| 国产风韵犹存在线视精品| 在线国产精品网| 久久国内精品| 色噜噜久久综合伊人一本| 亚洲综合成人av| 国产亚洲综合在线| 成人性做爰aaa片免费看不忠| 欧洲在线一区| 777午夜精品福利在线观看| 国产18精品乱码免费看| 亚洲综合色成人| 国产xxx在线观看 | 欧美夫妻性视频| www.久久色| 亚洲一区二区三区小说| 苍井空张开腿实干12次| 欧美日韩中文| 国产日韩欧美一区二区| 川上优av中文字幕一区二区| 亚洲精品按摩视频| av黄色在线播放| 国产亚洲欧美激情| 精品999在线| 91影院成人| 99久久久精品免费观看国产| av影视在线| 日韩av在线免播放器| 精产国品一区二区| 国产精品国产三级国产普通话99 | 亚洲精品小视频| 丰满少妇乱子伦精品看片| 99这里都是精品| 少妇激情一区二区三区| 日韩欧美高清| 粉嫩av免费一区二区三区| av免费不卡| 中文字幕亚洲欧美日韩高清| 国产精品久久久久久久久久久久久久久久久久 | 伊人影院综合网| 久久国产免费看| 成人免费网站入口| 美女久久久久| 成人精品在线视频| aa国产成人| 自拍亚洲一区欧美另类| 91在线公开视频| 亚洲福利视频一区| 先锋影音av在线| 国产精品白丝jk白祙喷水网站| 91免费黄视频| 欧美裸体在线版观看完整版| 亚洲一区中文字幕| 中文字幕在线直播| 精品国产拍在线观看| 污污网站在线免费观看| 欧美日韩亚州综合| 国产无码精品一区二区| 国产精品视频一二三区| 男女性杂交内射妇女bbwxz| 丝袜亚洲另类丝袜在线| 成人小视频在线观看免费| 亚洲自拍电影| 国产成人精品免费视频大全最热 | 色老汉一区二区三区| 免费看一级大片| 97成人超碰视| 欧美日韩一区二区区| 天堂影院一区二区| 韩国无码av片在线观看网站| 精品美女久久| 精品国产一区二区三区久久久久久| 成人在线不卡| 91sa在线看| 香蕉成人app免费看片| 正在播放欧美一区| 四虎精品成人影院观看地址| 7777女厕盗摄久久久| 国产一卡二卡三卡| 天天免费综合色| 国精品无码一区二区三区| 久久精品视频免费观看| 无码成人精品区在线观看| 精品一二线国产| 999精品视频在线| 99亚洲视频| 国产乱淫av片杨贵妃| 午夜激情久久| 亚洲精品乱码久久久久久蜜桃91| 欧美激情影院| 国产激情美女久久久久久吹潮| 色8久久久久| 国产精品久久久久久久久久99| 天堂中文最新版在线中文| 欧美黑人巨大精品一区二区| v片在线观看| 色伦专区97中文字幕| 国产一级片在线播放| 国产视频精品久久久| 人妻精品一区二区三区| 亚洲精品一线二线三线无人区| 精品人妻av一区二区三区| 欧美肥妇毛茸茸| 国产一区二区在线视频聊天| 欧美性大战久久久| 欧美性猛交xxxx乱大交hd| 日韩欧美aaa| 国内自拍视频在线播放| 韩曰欧美视频免费观看| 国产成人愉拍精品久久 | 国产手机免费视频| 一区精品久久| 老太脱裤让老头玩ⅹxxxx| 国产精品第十页| 国产日韩欧美精品在线观看| 国产一区日韩一区| 成人毛片一区二区| 国产精品免费看| 久久久久久香蕉| 日韩电影免费在线看| 日韩av片网站| 精品无人区卡一卡二卡三乱码免费卡 | 黄色激情小视频| 国产精品电影一区二区三区| 蜜桃av.com| 亚洲女人小视频在线观看| 欧美成人精品欧美一级| 亚洲国产日日夜夜| 精品不卡一区二区| 欧美偷拍一区二区| 国产强被迫伦姧在线观看无码| 欧美成人免费网站| 少妇性bbb搡bbb爽爽爽欧美| 亚洲欧美日韩区| 美女免费久久| 欧美大片在线看| 中文字幕人成乱码在线观看| 国产精品美女www| 免费观看在线一区二区三区| 劲爆欧美第一页| 亚洲女与黑人做爰| 久久黄色免费视频| 日韩欧美中文免费| 国产成人a v| 欧美一区二区视频在线观看2022 | 日韩欧美国产系列| 污污的视频网站在线观看| 国产午夜一区二区| www免费在线观看| 欧美一区在线直播| 日日夜夜综合| 精品久久久久久乱码天堂| 国产精品免费大片| 国产成人亚洲综合无码| 午夜一级久久| 一区二区三区四区毛片| 不卡视频免费播放| 精品在线观看一区| 欧美日韩另类视频| 91精品人妻一区二区三区果冻| 亚洲精品一区二区三区福利| 川上优的av在线一区二区| 色综合五月天导航| 欧美日韩视频免费观看| 亚洲一区二区三区乱码aⅴ蜜桃女| 先锋影音国产精品| 日韩视频在线免费播放| 校园激情久久| www.欧美com| 国产精品久久久久影院| 日韩 国产 在线| 欧美精品v日韩精品v韩国精品v| 亚洲欧美日韩动漫| 色综合久久久久久中文网| xxxxx.日韩| 久久久久久久有限公司| 午夜日韩福利| 911福利视频| 国产欧美视频在线观看| 日韩欧美亚洲一区二区三区| 6080午夜不卡| 18视频免费网址在线观看| 4388成人网| 成人免费在线电影网| 中文字幕av导航| 日本不卡的三区四区五区| 538国产视频| 亚洲韩国精品一区| jizz国产视频| 久久久999精品视频| 九九热这里有精品| 色99中文字幕| 视频精品一区二区| 在哪里可以看毛片| 第一福利永久视频精品| 亚洲欧美另类日韩| 九九精品在线视频| va天堂va亚洲va影视| 一本色道久久99精品综合| 日韩专区在线视频| 免费看污片网站| 色综合咪咪久久| 精华区一区二区三区| 51色欧美片视频在线观看| 精品三级av| 免费一级特黄毛片| proumb性欧美在线观看| 日本五十熟hd丰满| 亚洲国产高清福利视频| 55av亚洲| 美国av一区二区三区| 欧美亚洲网站| 亚洲午夜精品久久久久久高潮 | 99久久精品国产一区色| 久久五月情影视| 精品国产三区在线| 日韩精品手机在线观看| 国产盗摄女厕一区二区三区| 欧美日韩大片在线观看| 日韩美女一区二区三区| 伦理av在线| 九九九热999| 久久一区中文字幕| xxxxx在线观看| 欧美日韩一区高清| 蜜桃视频在线观看www社区| 亚洲aⅴ日韩av电影在线观看| 欧美日韩视频| 中国av免费看| 日本道色综合久久| 看女生喷水的网站在线观看| 亚洲综合av影视| 极品中文字幕一区| 三级网站在线免费观看| 欧美三级在线看| 在线观看小视频| 久中文字幕一区| 麻豆精品新av中文字幕| www.色小姐com| 日韩av在线免播放器| 韩国理伦片久久电影网| 黄色一级片国产| 久久毛片高清国产| 中文字幕日产av| 欧美激情一区二区三区成人| 亚洲三级网页| 久久人人爽人人片| 欧美性生活大片免费观看网址| 久草资源在线观看| 国产日韩久久| 久久国产精品色| 国产精品成人aaaa在线| 夜夜嗨av一区二区三区免费区| 亚洲精品一二三**| 精品久久久噜噜噜噜久久图片 | 国产精品黄色大片| 日韩在线观看高清| 久久动漫网址| 超碰人人草人人| 大桥未久av一区二区三区| 看女生喷水的网站在线观看| 美女亚洲精品|