Simple Implementation of Spring-like Op Principle

About Op:
Uses: Can be used in connection with the database, such as every time when connecting to the database, need to manually create a new connection, then perform database operations, and finally close the database connection to avoid resource consumption. At this time, some people are wondering if we can let the system automatically create a new connection every time we perform database operations, and then automatically close the connection after we finish the database connection.
A database is needed here.

Principle example of Aop (using cglib dynamic proxy):
Example:
First, create three new annotations

//Definition on a class indicates that the class is a tangent point
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAspect {
}
// Pre-notification annotations
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Before {
    String value();
}

//Postnotification annotations
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface After {
    String value();
}

Create a New Song Category

public class Music {
    public void sing(String str){
        System.out.println(str+"Sing");
    }
}

Of course, if someone sings a song, someone has to prepare for the beginning, for example, before singing, so a new class is created to express the preparation before singing.

@MyAspect // Indicates that this is a tangent class
public class Player {
    @Before("reflec.aop.cglibtest.Music.sing()")  // Pre-notification that when the call sing s method is called, the method will be called before it
    public void beforeSing() {
        System.out.println("Preparing for the Start of Singing");
    }

    @After("reflec.aop.cglibtest.Music.sing()") // Similarly, call the sing method after calling it
    public void afterSing() {
        System.out.println("Start grading after singing.");
    }
}

So how do we call these two methods when we call the sing method, that is, how do we weave them in?
Here we have to consider Cglib dynamic proxy. cglib relies on the asm package to generate a subclass on the basis of the target class, and then realizes the pre-or post-notification when the target method is called through the subclass. As for Cglib, I feel that it belongs to compiler weaving, because bytecodes are generated by subclasses and then invoked.

Establish proxy classes:
The purpose of this class is only to implement the execution of those two methods through dynamic proxy.

public class CGLIBProxy implements MethodInterceptor {
    private Object target;
    private ProxyUtil proxyUtil ;
    public CGLIBProxy(Object target) throws ClassNotFoundException {
        this.target = target;
        proxyUtil =new ProxyUtil();
    }

    public <T> T getProxy(){
        return (T) new Enhancer().create(this.target.getClass(),this);
    }
    public <T> T getProxy(Class<?> clazz){
        return (T) new Enhancer().create(this.target.getClass(),this);
    }
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        ProxyEntity proxyEntity =new ProxyEntity(proxy,this.target.getClass(),obj,method,args);
        return proxyUtil.generateEntity(proxyEntity);
    }

Establishment of Reflective Classes: (i.e. Mainly General)

public class Reflect {
    Map<String,String> map ;   //The method name and its annotations are saved.
    Map<String,String> clazzMap;
    public Reflect() throws ClassNotFoundException {
        map=new HashMap<>();
        clazzMap =new HashMap<>();
        getAnnotationClass();
    }

    public Map<String, String> getMap() {  // The return is the use of ProxyUtil in the fully stored map aspect.
        return map;
    }
    @Test
    public  void getAnnotationClass() throws ClassNotFoundException {
        String clazzName="reflec.aop.cglibtest.Player";
        Class<?>  clazz =Class.forName(clazzName,false,Thread.currentThread().getContextClassLoader());   // This class is loaded directly and dynamically for the sake of simplicity.
        if (clazz.isAnnotationPresent(MyAspect.class)) {  //Suppose it's an annotation class
            Method[] methods =clazz.getDeclaredMethods();   //traversal method
            for (Method method :methods) {
                if (method.isAnnotationPresent(Before.class)) {  // Get annotations
                   Before before =method.getAnnotation(Before.class); 
                    String beforeValue=before.value();  // Get the value of the annotation and the calling method for the name of the current class
                    map.put(method.getName()+ "-"+clazzName+"-"+"before",beforeValue.substring(0,beforeValue.length()-2));
                    // The method name, annotation name and execution order are saved, which is written directly at the end for the sake of saving time.
                if (method.isAnnotationPresent(After.class)) {
                    After after =method.getAnnotation(After.class); /
                    String afterValue=after.value();
                    map.put(method.getName()+ "-"+clazzName+"-"+"after",afterValue.substring(0,afterValue.length()-2));
                }
            }
        }
    }

Create specific classes for handling intercept methods in the MethodInterceptor interface in Cglib

public class ProxyUtil {

    Reflect reflect;

    public ProxyUtil() throws ClassNotFoundException {
        reflect = new Reflect();
    }

    public void getMethod(String name) {
        Map<String, String> map = new HashMap<>();

    }

    //This method is responsible for proxy
    public Object generateEntity(ProxyEntity proxyEntity) throws Throwable {
        String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
        Map<String, String> methodMap = reflect.getMap();
        for (Map.Entry<String, String> map : methodMap.entrySet()) {
            if (map.getValue().equals(proxyMethodValue)) {
                String[] str = mapKeyDivision(map.getKey());
                if (str[2].equals("before")) {
                    Class<?> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // Loading this class
                    Method method = clazz.getDeclaredMethod(str[0]);
                    method.invoke(clazz.newInstance(), null); // Reflective invocation method
                }
            }
        }
        return doAfter(proxyEntity,methodMap); // Post-processing notification
    }
    private Object  doAfter(ProxyEntity proxyEntity,Map<String,String> map) throws Throwable {
        Object object = proxyEntity.getMethodProxy().invokeSuper(proxyEntity.getObject(), proxyEntity.getArgs());  // Call method
        String proxyMethodValue = proxyEntity.getMethod().toString().substring(proxyEntity.getMethod().toString().lastIndexOf(" ") + 1, proxyEntity.getMethod().toString().indexOf("("));
        for(Map.Entry<String,String> aMap:map.entrySet()){
            if (aMap.getValue().equals(proxyMethodValue)){
                String[] str =mapKeyDivision(aMap.getKey());
                    if(str[2].equals("after")){
                        Class<?> clazz = Class.forName(str[1], false, Thread.currentThread().getContextClassLoader()); // Loading this class
                        Method method = clazz.getDeclaredMethod(str[0]);
                        method.invoke(clazz.newInstance(), null); // This step requires the original class
                    }
                }
            }
        return object;
    }
//Decompose the keys in the map, because the method and class names and execution order are stored in it.
    private String[] mapKeyDivision(String value) {
        String[] str = new String[10];
        str[0] = value.substring(0, value.indexOf("-"));  //Note the following method
        str[1] = value.substring(value.indexOf("-") + 1, value.lastIndexOf("-")); //The class where the annotation is located
        str[2]=value.substring(value.lastIndexOf("-")+1,value.length()); //Is it before or after?
        return str;
    }

Finally, a bean

public class ProxyEntity {
    private final MethodProxy methodProxy;
    private final Class<?> clazz;
    private final Object object;
    private final Method method;
    private final Object[] args;

    public Object getObject() {
        return object;
    }

    public Object[] getArgs() {
        return args;
    }

    public Class<?> getClazz() {
        return clazz;
    }

    public Method getMethod() {
        return method;
    }

    public ProxyEntity(MethodProxy methodProxy, Class<?> clazz, Object object, Method method, Object[] args) {
        this.methodProxy = methodProxy;
        this.clazz = clazz;
        this.object = object;
        this.method = method;
        this.args = args;
    }

    public MethodProxy getMethodProxy() {
        return methodProxy;
    }
}

Finally, test:

public class CglibTest {

public static void main(String args[]) throws ClassNotFoundException {
    Music music = new Music();
    CGLIBProxy cglibProxy = new CGLIBProxy(music);
    (Music) cglibProxy. getProxy (). sing ("test person");
}
}

Test results:
Preparing for the Start of Singing
 People who test singing
 Start grading after singing.

In this test, the method in Player class is not invoked, but it runs automatically at run time. This example executes a simple imitation of AOP in Spring. In fact, there are many other places that are not taken into account.

The idea of this class is to get the tangent class by reflection, then save the method name annotated with annotations and the values in the annotations into a map, and finally build a class to process the map.

Github address: https://github.com/Somersames...

Keywords: Java Database Amap github Spring

Added by Ashley Sheridan on Sat, 06 Jul 2019 21:12:26 +0300