preface

In the previous article # Writing AOP to talk about pointcut expressions we’ve seen how AOP expressions do matching, so in this article we’ll talk about how to implement an AOP

Wrap a facet notification message

What is slice notification and what does it do?

To put it bluntly, the target object, method matcher and method interceptor that we need in AOP are all integrated into one class. This allows us to use this aspect to inform the information class later when we implement the proxy class through an association (commonly understood as a member variable of the proxy class).

Section notification member properties

The implementation is also very simple: a TargetSource object class, a MethodMatcher, and a methodInterceptor. We might as well look at the concrete implementation of each member variable

package cn.shark.springframework.aop; import org.aopalliance.intercept.MethodInterceptor; /** * Public class AdvisedSupport {/** * target object */ private TargetSource TargetSource; /** * MethodMatcher */ private MethodMatcher; Private MethodInterceptor MethodInterceptor; private boolean proxyTargetClass; get()/set().... }Copy the code

Targetsource is also very simple. It is not possible to wrap the target object in a layer. In view of the subsequent JDK proxy needs to use the interface information of the class object, it wraps a getTargetClass method

public class TargetSource { private final Object target; public TargetSource(Object target) { this.target = target; } public Class<? >[] getTargetClass(){return target.getClass().getinterfaces (); } public Object getTarget() { return target; }}Copy the code

MethodMatcher

/** * public interface MethodMatcher {/** * check whether the current class method matches @param method * @param targetClass * @return */ boolean matches(Method method,Class<? > targetClass); }Copy the code

MethodInterceptor

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.aopalliance.intercept;

public interface MethodInterceptor extends Interceptor {
    Object invoke(MethodInvocation var1) throws Throwable;
}
Copy the code

Proxy objects

Define a proxy object interface

The way we can delegate the following may be many, so we will use an interface with their actions, and make it easier for other classes to use the interface instead of the association concrete but the association abstract (in plain English, the interface as a member of the member variables to decouple).

/** * AopProxy is the abstract object of proxy. Its implementation is mainly based on JDK proxy and Cglib proxy. */ public interface AopProxy { Object getProxy(); }Copy the code

JDK proxy object design and implementation

If you look at the code below, the implementation logic is simple: method intercepts if methods match, and reflection calls if not. But we have seen in the method of interceptor is a ReflectiveMethodInvocation, what exactly is it? We might as well step into it

public class JdkDynamicAopProxy implements AopProxy, InvocationHandler { private final AdvisedSupport advisedSupport; public JdkDynamicAopProxy(AdvisedSupport advisedSupport) { this.advisedSupport = advisedSupport; } @override public Object getProxy() {return this as the third argument Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),advisedSupport.getTargetSource().getTargetClass(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if (advisedSupport.getMethodMatcher().matches(method,advisedSupport.getTargetSource().getTargetClass().getClass())){ MethodInterceptor methodInterceptor = advisedSupport.getMethodInterceptor(); return methodInterceptor.invoke(new ReflectiveMethodInvocation(advisedSupport.getTargetSource().getTarget(),method,args)); } return method.invoke(advisedSupport.getTargetSource().getTarget(),args); }}Copy the code

As you can see, this class cannot be a class that inherits MethodInvocation and wraps the target object, target object method, and target object method parameters. The only good thing about this is that the invoke invocation needs data from the MethodInvocation type, so we will inherit it and implement a class with the required parameters set in.

/** * is essentially a wrapper around the target object, target object methods, and target object parameters. That's all * / public class ReflectiveMethodInvocation implements the MethodInvocation {protected Object target; protected Method method; protected Object[] args; public ReflectiveMethodInvocation(Object target, Method method, Object[] args) { this.target = target; this.method = method; this.args = args; } get()/set() }Copy the code

Design and implementation of CGLIB proxy object

CGLIB’s getProxy implementation differs from JDK proxies because the Enhancer class used by CGLIB can generate objects at run time for the interface using the underlying ASM bytecode enhancement technology, so proxy objects do not need to inherit any interface. Cglib’s getProxy enhancer uses setSuperclass and setInterfaces to set the target object and all its interfaces. Reoccupy setCallback set a DynamicAdvisedInterceptor namely dynamic interceptors, we might as well step by step and see exactly what is this stuff

public class Cglib2AopProxy implements AopProxy { private final AdvisedSupport advisedSupport; public Cglib2AopProxy(AdvisedSupport advisedSupport) { this.advisedSupport = advisedSupport; } @override public Object getProxy() {// Cglib-based classes that use Enhancer proxies can generate proxy objects at run time for interfaces that use underlying ASM bytecode enhancement techniques to process objects, Therefore the proxied class does not need to implement any interface. Enhancer enhancer=new Enhancer(); enhancer.setSuperclass(advisedSupport.getTargetSource().getTarget().getClass()); enhancer.setInterfaces(advisedSupport.getTargetSource().getTargetClass()); enhancer.setCallback(new DynamicAdvisedInterceptor(advisedSupport)); return enhancer.create(); } private static class DynamicAdvisedInterceptor implements MethodInterceptor { private AdvisedSupport advisedSupport; public DynamicAdvisedInterceptor(AdvisedSupport advisedSupport) { this.advisedSupport = advisedSupport; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { CglibMethodInvocation cglibMethodInvocation = new CglibMethodInvocation(advisedSupport.getTargetSource().getTarget(),method,objects,methodProxy); if (advisedSupport.getMethodMatcher().matches(method,advisedSupport.getTargetSource().getTarget().getClass())){ return advisedSupport.getMethodInterceptor().invoke(cglibMethodInvocation); } return cglibMethodInvocation.proceed(); } } private static class CglibMethodInvocation extends ReflectiveMethodInvocation { private final MethodProxy methodProxy; public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy MethodProxy) { super(target, method, args); this.methodProxy = MethodProxy; } / * * * because of the additional ancient inheritance ReflectiveMethodInvocation rewrite proceed, Complete the method call with your own proxy. * @return * @throws Throwable */ @Override public Object Proceed () throws Throwable {return methodProxy.invoke(this.target, this.args); }}}Copy the code

As you can see, it is an inner class of the Cglib proxy that inherits the MethodInterceptor implementation logic. Notice that the method interceptor calls the Cglib MethodInvocation from cglib’s own implementation, so let’s look at his implementation

private static class DynamicAdvisedInterceptor implements MethodInterceptor { private AdvisedSupport advisedSupport; public DynamicAdvisedInterceptor(AdvisedSupport advisedSupport) { this.advisedSupport = advisedSupport; } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { CglibMethodInvocation cglibMethodInvocation = new CglibMethodInvocation(advisedSupport.getTargetSource().getTarget(),method,objects,methodProxy); if (advisedSupport.getMethodMatcher().matches(method,advisedSupport.getTargetSource().getTarget().getClass())){ return advisedSupport.getMethodInterceptor().invoke(cglibMethodInvocation); } return cglibMethodInvocation.proceed(); }}Copy the code

But so, inherited ReflectiveMethodInvocation, just proceed logic into the form of additional reflection method is called

private static class CglibMethodInvocation extends ReflectiveMethodInvocation { private final MethodProxy methodProxy; public CglibMethodInvocation(Object target, Method method, Object[] args, MethodProxy MethodProxy) { super(target, method, args); this.methodProxy = MethodProxy; } / * * * because of the additional ancient inheritance ReflectiveMethodInvocation rewrite proceed, Complete the method call with your own proxy. * @return * @throws Throwable */ @Override public Object Proceed () throws Throwable {return methodProxy.invoke(this.target, this.args); }}Copy the code

Summarize the above design structure with a class diagram

Walk through the code with a test case

code

A userService interface

public interface IUserService {

    String queryUserInfo();
}
Copy the code

The implementation class

public class UserService implements IUserService { private String uId; private String company; private String location; private UserDao userDao; private ApplicationContext applicationContext; private BeanFactory beanFactory; public String queryUserInfo() { try { Thread.sleep(new Random(1).nextInt(100)); } catch (InterruptedException e) { e.printStackTrace(); } return "89757 shark pepper Fujian "; }}Copy the code

The test case

@throws NoSuchMethodException */ @test public void testAopWithoutSpring() throws NoSuchMethodException { IUserService userService=new UserService(); AdvisedSupport =new AdvisedSupport(); / / set the target object advisedSupport. SetTargetSource (new TargetSource (userService)); / / set the method interceptor advisedSupport. SetMethodInterceptor (new UserServiceInterceptor ()); / / set the match expression object advisedSupport. SetMethodMatcher (new AspectJExpressionPointcut (" execution (* cn.shark.springframework.bean.IUserService.*(..) ) ")); IUserService proxy_jdk = (IUserService) new JdkDynamicAopProxy(advisedSupport).getProxy(); Println (" test result: "+ proxy_jdk.queryUserInfo()); IUserService Proxy_cglib = (IUserService) new Cglib2AopProxy(advisedSupport).getProxy(); Println (" test result: "+ proxy_cglib.queryUserInfo()); }Copy the code

Walk through your implementation of AOP with graphical debug

Go through it using the JDK as the proxy

First, the JDK agent is initialized

Assign values to various member attributes through the section notification message and pass itself in as a method interceptor as a parameter InvocationHandler

Once the proxy class is created, the proxy class is used to invoke the proxied class’s methods

Go to the intercepting logic written by the JDK agent itself that inherits InvocationHandler

MethodInvocation initialization

Get the method interceptor and call the method caller

Finished work

In the next article, I will integrate it into my own Spring framework