Personal notes-implementation of static proxy, dynamic proxy and annotation + AOP

Personal notes-implementation of static proxy, dynamic proxy and annotation + AOP


I spent two weeks dealing with graduation, dissertation and defense related matters. Today I reviewed the dynamic agency, read some articles, wrote articles for recording, and by the way recorded the aop optimization of the timeline operation records previously done for the project.

Static proxy

Ready to work

First implement an interface IHello and an implementation class HelloImpl

/** * @author zhn */ public interface IHello { /** * sayHello */ void sayHello () ; } Copy code
/** * @author zhn */ public class HelloImpl implements IHello { /** * sayHello */ @Override public void sayHello () { System.out.println( "Hello!!" ); } } Copy code

Implement Handler

The static proxy mainly implements the Handler to implement the IHello interface, and defines a proxy object in the Handler method.

When calling the interface method, call the method in the proxy object, and at the same time, you can add other operations before calling the proxy object .

/** * @author zhn */ public class Handler implements IHello { //The target of the proxy private IHello target; public Handler (IHello target) { this .target = target; } /** * sayHello */ @Override public void sayHello () { //Before System.out.println( "Proxy Start" ); //Call the method in the proxy object target.sayHello(); //After System.out.println( "Agent finished" ); } } Copy code

Use static proxy

Using the main method as an example, first create an object that needs a proxy, then pass the proxy object into the Handler (ie proxy), and call the method in the Handler (proxy).

/** * @author zhn */ public class Main { public static void main (String[] args) { HelloImpl hello = new HelloImpl(); Handler handler = new Handler(hello); handler.sayHello(); } } Copy code

Results and summary

The implementation of static proxy is very simple, but it is also easy to see the shortcomings, that is, when there are many things that need to be proxyed, many proxy classes are needed.

Dynamic proxy

Ready to work

Still create an interface and an implementation class, here use the class in the preparatory work in the static proxy.

Create MyInvocationHandler

Implement the InvocationHandler interface and implement your own Handler

/** * @author zhn */ public class MyInvocationHandler implements InvocationHandler { //Proxy target private Object target; //Construct the incoming public MyInvocationHandler (Object target) { this .target = target; } @Override public Object invoke (Object proxy, Method method, Object[] args) throws Throwable { System.out.println( "Front" ); //Reflection call Object res = method.invoke(target, args); System.out.println( "Post" ); return res; } } Copy code

Much like a static proxy, the core is to extend the implementation of other methods based on the method of the proxy object, and reflection is used here.

Use dynamic proxy

Also use the main method as an example, there are two ways

method one:

/** * @author zhn */ public class MyProxyTest { public static void main (String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { //The first type //save the class file generated by the JDK dynamic proxy to the class file generated by the local $proxy0 System .getProperties().put( "sun.misc.ProxyGenerator.saveGeneratedFiles" , "true" ); //1. Get dynamic proxy class Class<?> proxyClass = Proxy.getProxyClass(IHello.class.getClassLoader(), IHello.class); //2. Get the constructor of the proxy class and pass in the parameter type InvocationHandler Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class); //3. Create a dynamic proxy class object through the constructor, and then pass the custom MyInvocationHandler into IHello iHello1 = (IHello)constructor.newInstance( new MyInvocationHandler( new HelloImpl())); //4. Call the target through the proxy object method iHello1.sayHello(); } } Copy code

Method 2: Proxy integrates the above 1~3 steps and it is more convenient to use directly

/** * @author zhn */ public class MyProxyTest { public static void main (String[] args) { //The second method IHello iHello2 = (IHello)Proxy.newProxyInstance(IHello.class.getClassLoader(), new Class[]{IHello.class}, new MyInvocationHandler( new HelloImpl())); //Call method iHello2.sayHello(); } } Copy code

Click in to take a look at the newProxyInstance method

h is the MyInvocationHandler we passed in

Results and summary

To sum up a bit, a dynamic proxy is a proxy created by reflection at runtime. The core mainly implements the InvocationHandler interface to complete the operations we want.


Ready to work

The example implements a HelloService class, which uses Spring's cglib implementation, with similarities and minor differences.

/** * @author zhn */ public class HelloService { public HelloService () { System.out.println( "HelloService Constructor" ); } /** * This method cannot be overridden by subclasses, and cglib cannot proxy final modified methods */ final public String sayOthers () { System.out.println( "HelloService:sayOthers" ); return null ; } public void sayHello () { System.out.println( "HelloService:sayHello" ); } } Copy code

Implement MyMethodInterceptor

Implement the MethodInterceptor interface in the cglib package

/** * @author zhn */ public class MyMethodInterceptor implements MethodInterceptor { /** * o: proxy object generated by cglib * method: the method of the proxied object * objects: method input * methodProxy: proxy method */ @Override public Object intercept (Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println( "pre" ); //Calling method note is to call the method of the parent class Object res = methodProxy.invokeSuper(o, objects); System.out.println( "Post" ); return res; } } Copy code

Use proxy

The Test example is used here. The Enhancer class is mainly used to create subclasses according to its given parent class. In addition to being final defined , we pass in our own MyMethodInterceptor when setting up the callback to achieve functional expansion.

@SpringBootTest public class testMyProxy { @Test public void test () { //The proxy class file is saved to the local disk for us to decompile and view the source code //System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\code"); //Obtained through CGLib dynamic proxy Proxy object /** * Enhancer is a very frequently used class in cglib, it is a bytecode enhancer, * Can be used to create agents for classes without interfaces. Its function is quite similar to the Proxy class that comes with java. * It will create subclasses based on a given class, and all non-final methods have callback hooks. */ Enhancer enhancer = new Enhancer(); //Set the parent class of enhancer enhancer.setSuperclass(HelloService.class); //Set callback enhancer.setCallback( new MyMethodInterceptor()); //Create proxy object HelloService proxyObject = (HelloService) enhancer.create(); //Call method proxyObject.sayHello(); } } Copy code

Results and summary

CGLIB mainly uses bytecode technology to create subclasses for a class through bytecode technology, and uses method interception technology in the subclasses to intercept all parent class method calls and expand.

Both JDK dynamic proxy and CGLIB dynamic proxy are the basis for implementing Spring AOP.

The bytecode has not been studied in depth. I saw a Meituan article and posted it, and the Meituan bytecode article , when I have time, I will come back and learn more.

There is also a Fastclass mechanism in CGLIB, that is, the method interception technology mentioned above. It has not been studied carefully for the time being, and it needs to be understood more deeply when I look back.


Previously, I used the annotation + aop method in the project to try to optimize a timeline function similar to the log operation. Here is also a record.

Ready to work

Use SpringBoot project

Import aop dependencies

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>


/** * @author zhn */ @Target({ElementType.METHOD,ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface TimeLine { /** */ String subject(); /** */ String description() default ""; }

@Target Annotation(java ) Annotation packages types Annotation catch Annotation target


Retention( ) , . :

RetentionPolicy.SOURCE Annotations ,

RetentionPolicy.CLASS Annotations , class , JVM

RetentionPolicy.RUNTIME Annotations JVM , JVM .

: source < class < runtime



@Component @Aspect

/** * */ @Pointcut("@annotation(com.example.test.annotation.TimeLine)") public void pointCut(){} /** * * @param joinPoint */ @AfterReturning (= the pointcut "PointCut ()") public void doAfter (the JoinPoint Joinpoint) { //processing method handleTimeLine(joinPoint); } Copy code

Complete code

/** * @author zhn */ @Component @Aspect public class TimeLineAspect { /** * Configure time axis cut point */ @Pointcut("@annotation(com.example.test.annotation.TimeLine)") public void pointCut () {} /** * Post notification * @param joinPoint connection point */ @AfterReturning(pointcut = "pointCut()") public void doAfter (the JoinPoint Joinpoint) { //processing method handleTimeLine(joinPoint); } /** * Get the currently executed method * * @param joinPoint connection point * @param methodName method name * @return method */ private Method currentMethod (JoinPoint joinPoint, String methodName) { //Get all methods of the target class and find the method to be executed currently Method[] methods = joinPoint.getTarget().getClass().getMethods(); Method resultMethod = null ; for (Method method: methods) { if (method.getName().equals(methodName)) { resultMethod = method; break ; } } return resultMethod; } /** * Whether there is a comment, if it exists, get it */ private TimeLine getAnnotationLog (Method method) { if (method != null ) { return method.getAnnotation(TimeLine.class); } return null ; } private void handleTimeLine (JoinPoint joinPoint) { try { String name = joinPoint.getSignature().getName(); //Get the current method Method method = currentMethod(joinPoint, name); //Get comments TimeLine timeLine = getAnnotationLog(method); //Processing... System.out.println( "aop post notification processed..." ); System.out.println( "Method name:" +name); System.out.println( "Annotation information: Subject=" + timeLine.Subject() + "; description=" +timeLine.description()); } catch (Exception e){ //Record exceptions can be added to the log record e.printStackTrace(); } } } Copy code


Add TimeLine annotation to the corresponding method


/** * @author zhn */ @Service public class AopTestService { @TimeLine(Subject = "Test aop", description = "Use testAop in the AopTestService class") public void testAop () { System.out.println( "AopTestService:testAop" ); } } Copy code

Then as long as the method is called, the method configured in TimeLineAspect will be used

Results and summary

It can be seen that the bottom layer of AOP is still the proxy mode used, and then packaged, and finally the concept of AOP aspect-oriented programming is proposed.

Among them, the main concepts of aop are point of contact, aspect, notification, etc. I won't go into details in detail.


Summarize the characteristics

The biggest feature of static proxy is simple and easy to understand

The characteristic of dynamic proxy is to use reflection mechanism

CGLIB is characterized by bytecode

The characteristic of aop is to package the above agency methods and put forward the concept of aop.

In a few days, I will write down the actual articles of ElasticSearch6.8.x, and then I will be busy with school chores (can't wait to graduate) and prepare for the interview.

related articles