Create notifications using entry points for SpringAOP

Keywords: Java Lambda Spring github

Several notification types in SpringAOP and how to create simple notifications have been described previously See address

1. What is the starting point

In the previous example, we could create a ProxyFactory to create notifications and get the methods in the target class.Different types of notifications allow you to do different things with these methods.However, this will work for all methods in the class as a whole, but for a lot of the time we just want to notify some of the methods in this class, we need to use entry points to precisely control specific methods

  • That is, our starting point is to identify the method (precise to method) within a class, and to find the class that matches this rule, just like defining some rules, it's much easier to see below.

2. Classification of entry points

To create a starting point in Spring, implement the Pointcut class.

package org.springframework.aop;

public interface Pointcut{
    ClassFilter getClassFilter();
    MethodMatcher getMethodMacher();
}

The source code of the class returned by the above two methods is as follows:

  • ClassFilter
package org.springframework.aop;
/**
*   This is a functional interface that passes in a class.
*   Return true if this class meets our requirements
*   That is, this entry point applies to this class (that is, this class does not match our rules)
*/
@FunctionalInterface
public interface ClassFilter {
    boolean matches(Class<?> var1);
}
  • MethodMatcher
package org.springframework.aop;

import java.lang.reflect.Method;
/**
*   This is of course the matching method.
*   There are two types, dynamic and static, which are determined by the return value of isRuntime(), true is dynamic and false is static.This type determines whether this entry point is dynamic or static
*   
*/
public interface MethodMatcher {
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
    //For static matching, it is independent of the method's parameters
    boolean matches(Method var1, Class<?> var2);

    boolean isRuntime();
    //Used for dynamic matching, which is related to the method's parameters because they are variable
    boolean matches(Method var1, Class<?> var2, Object... var3);
}

To sum up, there are two types of entry points, dynamic and static. Dynamic entry points check each time whether the parameters of the method meet the requirements, which will incur additional expenses.If possible, use static entry points whenever possible.

3. Eight implementation classes of entry points

Implementation Class describe
org.springframework.aop.support.annotation.AnnotationMatchingPointcut Finding specific annotations on a class or method requires JDK5 or higher
org.springframework.aop.aspectj.AspectJExpressionPointcut Evaluating entry point representations in AspectJ syntax using AspectJ Weaver
org.springframework.aop.support.ComposablePointcut Combine two or more entry points using operations such as union() and intersection()
org.springframework.aop.support.ControlFlowPointcut Is a special entry point that matches all methods in the control flow of another method, any method that is called directly or indirectly as a result of another method
org.springframework.aop.support.JdkRegexpMethodPointcut Use regular expressions to define entry points for method names, above JDK4
org.springframework.aop.support.NameMatchMethodPointcut As the name implies, this is a simple match to a list of method names
org.springframework.aop.support.DynamicMethodMatcherPointcut This class serves as the base class for creating dynamic entry points
org.springframework.aop.support.StaticMethodMatcherPointcut Base class as a starting point for creating a representation

4. Use StaticMethodMatcherPointcut to create a static entry point

  • Create a class with two methods.Our goal is to create surround notifications only in the walk() method, printing a sentence, "I am a cute cat."
public class Cat {
    public void sleep(){
        System.out.println("sleep....");
    }
    public void walk(){
        System.out.println("walking....");
    }
}
  • Create a starting point
public class MethodPointcutDemo extends StaticMethodMatcherPointcut {

    @Override
    public boolean matches(Method method, Class<?> aClass) {
        return method.getName().equals("walk");
    }

    @Override
    public ClassFilter getClassFilter() {
        return clz -> clz == Cat.class;
        
//        The upper lambda expression equals the lower one
//        return new ClassFilter() {
//            @Override
//            public boolean matches(Class<?> clz) {
//                return clz == Cat.class;
//            }
//        };
    }
}

The lambda expression above can be viewed https://www.cnblogs.com/Lyn4ever/p/11967959.html , only lambda expression is written when appearing later, and no further explanation is given

In the above method, of course, we don't need to implement the getClassFilter() method because it's already implemented by a superior, so we can directly determine whether this class is a Cat.class in the matches method.

  • Notification class (this is the same as the previous one, creating a surround notification)
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class CatAdvisor implements MethodInterceptor{

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        //The least reliable way, we can tell here if this method is walk or not, so we don't want to notify you
        System.out.println("I am a cute Cat.");
        Object proceed = invocation.proceed();
        return proceed;
    }
}

Of course, we can also judge the method name and class name here. Why use a starting point?It's not unreliable, however, that we need to implement our logic here and control which classes and methods are notified through entry points, which is more flexible.

  • test method
public static void main(String[] args) {
        Cat cat = new Cat();

        Pointcut pointcut = new MethodPointcutDemo();//Start Point Instances
        Advice advice = new CatAdvisor();//Notification class instance (the object of the class where our notification code resides)

        Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);//Face class, which is a collection of entry points and notification classes

        ProxyFactory proxyFactory = new ProxyFactory();
        //The difference from the previous code is this sentence, where we're using the entry point control, and if you comment on this sentence, you'll use the settings notification class
        proxyFactory.addAdvisor(advisor);//Set up the facet class, including the entry point (control notification point) and notification class (logical code)
        
        //If you comment on the previous sentence, you will be notified of all methods in this class by using this sentence
//        proxyFactory.addAdvice(advice);//Set Notification Class
        proxyFactory.setTarget(cat);
        Cat proxy = (Cat) proxyFactory.getProxy();

        proxy.sleep();
        proxy.walk();
    }

The result must be exactly what we want

sleep....
I am a cute Cat.
walking....

5. Creating dynamic entry points using DyanmicMatcherPointcut

This is the same static entry point as above, except that the notification is only executed if the parameters of the incoming method satisfy certain requirements.Because of the length of the article, I did not write it, so I can download the code at the end of this article to understand it.

6. Implementation Classes of Other Types of PointCut

1. Simple name matching (NameMatchMethodPointcut)

The target and notification classes are also the Cat classes. The implementation classes of the previous entry points are not written, because this class has already been implemented by default. If you are interested, you can see its source code. Simply, it matches the class name, which is similar to the static entry point we just created.

public class NameMatchPointcutDemo {
    public static void main(String[] args) {
        Cat cat = new Cat();

        //This class is already an implementation class, so we don't need to write any more implementation classes
        NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
        pointcut.addMethodName("walk");

        Advisor advisor = new DefaultPointcutAdvisor(pointcut,new CatAdvisor());

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setTarget(cat);
        Cat proxy = (Cat) proxyFactory.getProxy();

        proxy.sleep();
        proxy.walk();
    }
}

2. Use regular expressions to create entry points (JdkRegexpMethodPointcut)

Write only test classes, everything else is the same as above

public class JdkRegexpPointcutDemo {

    public static void main(String[] args) {
        Cat cat = new Cat();

        JdkRegexpMethodPointcut pointcut = new JdkRegexpMethodPointcut();
        pointcut.setPattern(".*ee.*");//Match with ee in the middle, sleep()

        Advisor advisor = new DefaultPointcutAdvisor(pointcut,new CatAdvisor());

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setTarget(cat);
        Cat proxy = (Cat) proxyFactory.getProxy();

        proxy.sleep();
        proxy.walk();
    }
}

3. Create an entry point (AspectJExpressionPointcut) using the AspectJ entry point expression

Adding dependencies when using AspectJ

    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjrt</artifactId>
      <version>1.9.1</version>
    </dependency>
    <dependency>
      <groupId>org.aspectj</groupId>
      <artifactId>aspectjweaver</artifactId>
      <version>1.9.1</version>
    </dependency>
public class AspectJExpressionPointcutDemo {
    public static void main(String[] args) {
        Cat cat = new Cat();

        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* walk*(..))");

        Advisor advisor = new DefaultPointcutAdvisor(pointcut, new CatAdvisor());

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setTarget(cat);
        Cat proxy = (Cat) proxyFactory.getProxy();

        proxy.sleep();
        proxy.walk();
    }
}

This execution expression means any method that starts with a wall and has any parameters and any return values

4. Create annotation matching entry points (AnnotationMatchingPointcut)

First customize a comment, if you don't understand it, refer to Customize and apply annotation classes in Java

/**
 * This comment is for runtime, for classes, for methods
 */
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyAdvice {
}

Then add this comment on the target method (class to be notified)

    /**
     * To avoid previous conflicts, a new method has been written
     */
    @MyAdvice
    public void eat(){
        System.out.println("eating....");
    }

Then specify this annotation name in the main method:

public class AnnotationPointcutDemo {
    public static void main(String[] args) {
        Cat cat = new Cat();

        AnnotationMatchingPointcut pointcut = AnnotationMatchingPointcut
                .forMethodAnnotation(MyAdvice.class);
        //This class also has a.forClassAnnotation() method, which specifies the class's

        Advisor advisor = new DefaultPointcutAdvisor(pointcut, new CatAdvisor());

        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.addAdvisor(advisor);
        proxyFactory.setTarget(cat);
        Cat proxy = (Cat) proxyFactory.getProxy();

        proxy.sleep();//Will not be notified
        proxy.walk();//Will not be notified
        proxy.eat();//Will be notified
    }
}

Code uploaded to github, if you like, give a star

Last: SpringAOP Foundation

Posted by jamesl on Fri, 06 Dec 2019 23:37:43 -0800