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...