Java: Deep uncover AOP implementation principle

Overview:

Just recently encountered an example can be implemented in AOP development, on the way to study the implementation of the principle of AOP, to learn to be a summary of things. The programming language used in the article is kotlin, needs can be directly converted to java in the IDEA.

This article will expand in the following directory:

About AOP
code implemented for example
AOP realization principle
portion parsing source

1. AOP Introduction

I believe we more or less to find out about AOP, know that it is aspect-oriented programming, the Internet search can find a lot of explanations. Here I use a word to summarize: AOP is able to let us in without affecting the original function, the software scale-out capabilities.

So how scale to understand it, we are in the WEB project development, often to comply with three principles, including the control layer (Controller) -> business layer (Service) -> Data Layer (DAO) , then this structure down to portrait it's a layer of concrete is what we call lateral. AOP is our method can be applied to all of this one among the transverse module.

We look at the difference between AOP and OOP are: AOP complements OOP when we need to introduce a common behavior of multiple objects, such as logs, operating records, you need to reference the public behavior of each object, so that the program It generated a lot of duplicate code, using AOP can be the perfect solution to this problem.

Next we introduce AOP mentioned it is necessary to understand the knowledge points:

Section: interceptor classes, and which define tangent point notification

Cut-off point: a specific business point of interception.

Notify: Method section among statement the target position notification method performed in the service layer, the following types of notification:

Before advice: @Before executed before the target business method executed
after returning advice: @After executed after the target business method executes
a return notification: @AfterReturning executed after the target business method returns the result of
an exception notice: @AfterThrowing target business method throws after the abnormal
around advice: @Around powerful, instead of the above four notifications, you can also control the target business method whether and when to do


Example 2. The code implemented

The above has probably introduced the basic knowledge you need to know AOP, also know the benefits of AOP, then how to achieve it in code? Give you an example: We now have a school management system, has achieved additions and deletions to the teacher and students, but also to a new demand, saying that the teacher and students additions and deletions to make a record every time, that time can be president View a list of records.

So the question is, how to deal with is the best solution? Here I listed three solutions, we look at his strengths and weaknesses.



The simplest is the first method, we directly directly in the function of each of the additions and deletions among the recording method, such a repetition of the code is too high, too strong coupling, not recommended.

The second is the longest we use this method to record pulled out, other additions and deletions to call this function to record, apparently to reduce the degree of code duplication, but such a call or not to reduce coupling.

This time we want to look at the definition of AOP, and then we think about the scene, in fact, we are to change without additions and deletions to the original method, to increase the system's recording method, and the method is also a level of action. This time we will be able to implement the use of AOP.

We look at the code for the concrete realization:

1, first of all I define a custom annotation as a cut-off point

@Target(AnnotationTarget.FUNCTION)  
//注解作用的范围,这里声明为函数
@Order(Ordered.HIGHEST_PRECEDENCE)  
//声明注解的优先级为最高,假设有多个注解,先执行这个
annotation class Hanler(val handler: HandlerType)  
//自定义注解类,HandlerType是一个枚举类型,里面定义的就是学生和老师的增删改操作,在这里就不展示具体内容了复制代码

2, the next step is to define a class section

@Aspect   //该注解声明这个类为一个切面类
@Component
class HandlerAspect{

 @Autowired
 private lateinit var handlerService: HandlerService

@AfterReturning("@annotation(handler)")   //当有函数注释了注解,将会在函数正常返回后在执行我们定义的方法
fun hanler(hanler: Hanler) {
    handlerService.add(handler.operate.value)   //这里是真正执行记录的方法
}
}复制代码

3, the last business methods that we would have a

/**
* 删除学生方法
*/
@Handler(operate= Handler.STUDENT_DELETE)   //当执行到删除学生方法时,切面类就会起作用了,当学生正常删除后就会执行记录方法,我们就可以看到记录方法生成的数据
fun delete(id:String) {
   studentService.delete(id)
}复制代码

3. AOP implementation principle

We now know how to implement the code, then what principle to achieve AOP is it? Speaking before seen a blog, referring to the AOP we all know he is a dynamic proxy implementation principle, obviously I did not know before, ha ha, but I believe reading the article you must know.

Talked about dynamic proxy I would have to say that the agency model, define the proxy mode: one object is to provide a proxy by the proxy object control references to the original object.

Proxy mode contains the following roles:

subject: abstract thematic roles, it is an interface. The interface is a shared object and its proxy interface;
RealSubject: real character theme, the theme is the implementation of the abstract class interface;
the Proxy: a proxy role, comprising an internal reference RealSubject real object, which can operate the real object.


The real object proxy object provides the same interface, so that instead of the real object. Meanwhile, when the proxy object can perform a real operation objects, other additional operations, corresponding to the real object to be packaged. As shown below:




The agent is divided into static and dynamic proxy agent, here to write two small demo, JDK dynamic proxy is used by the agent. For example is now a class of students need homework, homework and now the squad agent, then the agent is the squad leader, students are being proxied object.

3.1 Static Proxy

First, we create a Person. This interface is that students (by proxy class), and monitor (proxy classes) public interface, they all have homework behavior. In this way, we can let the students homework to monitor the implementation of the agent.

/**
 * 创建person接口
 */
public interface Person {
    //交作业
    void giveTask();
}
复制代码

Student Person class implements an interface, Student homework can be embodied this behavior.

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }

    public void giveTask() {
        System.out.println(name + "交语文作业");
    }
}
复制代码

StudentsProxy class that also implements the Person interface, but also another student holds a class object, he can object to perform proxy class student homework behavior.

/**
 * 学生代理类,也实现了Person接口,保存一个学生实体,这样就可以代理学生产生行为
 */
public class StudentsProxy implements Person{
    //被代理的学生
    Student stu;

    public StudentsProxy(Person stu) {
        // 只代理学生对象
        if(stu.getClass() == Student.class) {
            this.stu = (Student)stu;
        }
    }

    //代理交作业,调用被代理学生的交作业的行为
    public void giveTask() {
        stu.giveTask();
    }
}
复制代码

The following test to see how you can use proxy mode:

public class StaticProxyTest {
    public static void main(String[] args) {
        //被代理的学生林浅,他的作业上交有代理对象monitor完成
        Person linqian = new Student("林浅");

        //生成代理对象,并将林浅传给代理对象
        Person monitor = new StudentsProxy(linqian);

        //班长代理交作业
        monitor.giveTask();
    }
}
复制代码

operation result:



Here and there is no homework to perform acts of direct light through the forest (the proxy object), but by proxy by the squad leader (proxy object) executed. This is the proxy mode.

Agent model is introduced at the actual object indirect access to a certain extent, the indirect method refers to here is not the actual object called directly, then we can add some other purpose in the proxy process.

For example, when the monitor homework help forest shallow want to tell the teacher made great progress in recent forest shallow, they can easily do so through a proxy mode. Incorporation to hand in papers before the proxy class. This advantage can use AOP in the spring, we can perform some operations before a cut-off point, perform some operations after a cut-off point, the cut-off point is one method. These methods where the class is to be sure the agent, cut into a number of other operations in the proxy process.

3.2 Dynamic Proxy

The difference between static and dynamic proxy agent is static agents of our own proxy class is defined, it has mutated completed before the program is running, but the dynamic proxy proxy class is created when the program runs.

Compared to static agent, the agent has the advantage of dynamic can be very convenient to the function of the proxy class unified process, without modifying the method for each agent class. For example, we want before each proxy methods plus a processing method, in our example above is only a proxy method, if there are a lot of proxy methods, too much trouble, we look at the dynamic proxy is how to achieve.

First is the definition of a Person:

/**
 * 创建person接口
 */
public interface Person {
    //交作业
    void giveTask();
}
复制代码

Next is to create the actual proxy class needs to be, that is the student category:

public class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }

    public void giveTask() {
        System.out.println(name + "交语文作业");
    }
}
复制代码

Creating StuInvocationHandler class that implements the interface InvocationHandler, this class holds a proxy object is an instance of target. InvocationHandler has a invoke method, all the execution agent objects will be replaced to perform the method of the invoke method.

public class StuInvocationHandler<T> implements InvocationHandler {
    //invocationHandler持有的被代理对象
    T target;

    public StuInvocationHandler(T target) {
        this.target = target;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用目标方法时传入的实参
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("代理执行" +method.getName() + "方法");
        Object result = method.invoke(target, args);
        return result;
    }
}
复制代码

So then we can create a proxy object of a specific.

/**
 * 代理类
 */
public class ProxyTest {
    public static void main(String[] args) {

        //创建一个实例对象,这个对象是被代理的对象
        Person linqian = new Student("林浅");

        //创建一个与代理对象相关联的InvocationHandler
        InvocationHandler stuHandler = new StuInvocationHandler<Person>(linqian);

        //创建一个代理对象stuProxy来代理linqian,代理对象的每个执行方法都会替换执行Invocation中的invoke方法
        Person stuProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(), new Class<?>[]{Person.class}, stuHandler);

        //代理执行交作业的方法
        stuProxy.giveTask();
    }
}
复制代码

We test execution agent class, we first need to create a proxy students to be shallow forest, the forest in light of incoming stuHandler, when we create a proxy object stuProxy, will stuHandler as a parameter, then all methods to perform the proxy object will be replaced to perform invoke method, that is, the last execution was in StuInvocationHandler invoke methods. So we see the results below also granted.



So here the question becomes, why proxy object execution method will be performed by invoke the method InvocationHandler, With this issue, we need to look at the dynamic proxy source code for his simple analysis. More: Dynamic proxy resolution

We use the above methods of the Proxy class newProxyInstance create a dynamic proxy object, look at his source:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
  }
复制代码

Then, we need to focus on Class<?> cl = getProxyClass0(loader, intfs)this code, this creates a proxy class, this class is the key dynamic proxy, because it is dynamically generated class file, we will print this class file to a file.

       byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Student.class.getInterfaces());
        String path = "/Users/mapei/Desktop/okay/65707.class";

        try{
            FileOutputStream fos = new FileOutputStream(path);
            fos.write(classFile);
            fos.flush();
            System.out.println("代理类class文件写入成功");
        }catch (Exception e) {
            System.out.println("写文件错误");
        }
复制代码

This class file decompile, jdk we look for our generation what kind of content:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.Person;

public final class $Proxy0 extends Proxy implements Person
{
  private static Method m1;
  private static Method m2;
  private static Method m3;
  private static Method m0;

  /**
  *注意这里是生成代理类的构造方法,方法参数为InvocationHandler类型,看到这,是不是就有点明白
  *为何代理对象调用方法都是执行InvocationHandler中的invoke方法,而InvocationHandler又持有一个
  *被代理对象的实例,就可以去调用真正的对象实例。
  */
  public $Proxy0(InvocationHandler paramInvocationHandler)
    throws 
  {
    super(paramInvocationHandler);
  }

  //这个静态块本来是在最后的,我把它拿到前面来,方便描述
   static
  {
    try
    {
      //看看这儿静态块儿里面的住giveTask通过反射得到的名字m3,其他的先不管
      m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
      m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
      m3 = Class.forName("proxy.Person").getMethod("giveTask", new Class[0]);
      m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
      return;
    }
    catch (NoSuchMethodException localNoSuchMethodException)
    {
      throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
    }
    catch (ClassNotFoundException localClassNotFoundException)
    {
      throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
    }
  }

  /**
  * 
  *这里调用代理对象的giveMoney方法,直接就调用了InvocationHandler中的invoke方法,并把m3传了进去。
  *this.h.invoke(this, m3, null);我们可以对将InvocationHandler看做一个中介类,中介类持有一个被代理对象,在invoke方法中调用了被代理对象的相应方法。通过聚合方式持有被代理对象的引用,把外部对invoke的调用最终都转为对被代理对象的调用。
  */
  public final void giveTask()
    throws 
  {
    try
    {
      this.h.invoke(this, m3, null);
      return;
    }
    catch (Error|RuntimeException localError)
    {
      throw localError;
    }
    catch (Throwable localThrowable)
    {
      throw new UndeclaredThrowableException(localThrowable);
    }
  }

}
复制代码

After reading the dynamic proxy source code, our next step is to look at the source code in Spring AOP achieve what?

4. Analytical source portion

aop create the source code analysis agency

1, look at how it is packaged as a proxy bean

       protected Object createProxy(
           Class<?> beanClass, String beanName, Object[] specificInterceptors, TargetSource targetSource) {

       if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
           AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
       }

       // 1.创建proxyFactory,proxy的生产主要就是在proxyFactory做的
       ProxyFactory proxyFactory = new ProxyFactory();
       proxyFactory.copyFrom(this);

       if (!proxyFactory.isProxyTargetClass()) {
           if (shouldProxyTargetClass(beanClass, beanName)) {
               proxyFactory.setProxyTargetClass(true);
           }
           else {
               evaluateProxyInterfaces(beanClass, proxyFactory);
           }
       }

       // 2.将当前bean适合的advice,重新封装下,封装为Advisor类,然后添加到ProxyFactory中
       Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
       for (Advisor advisor : advisors) {
           proxyFactory.addAdvisor(advisor);
       }

       proxyFactory.setTargetSource(targetSource);
       customizeProxyFactory(proxyFactory);

       proxyFactory.setFrozen(this.freezeProxy);
       if (advisorsPreFiltered()) {
           proxyFactory.setPreFiltered(true);
       }

       // 3.调用getProxy获取bean对应的proxy
       return proxyFactory.getProxy(getProxyClassLoader());
   }
复制代码

2. What type of Proxy creation? JDKProxy or CGLIBProxy?

    public Object getProxy(ClassLoader classLoader) {
        return createAopProxy().getProxy(classLoader);
    }
    // createAopProxy()方法就是决定究竟创建何种类型的proxy
    protected final synchronized AopProxy createAopProxy() {
        if (!this.active) {
            activate();
        }
        // 关键方法createAopProxy()
        return getAopProxyFactory().createAopProxy(this);
    }

    // createAopProxy()
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
        // 1.config.isOptimize()是否使用优化的代理策略,目前使用与CGLIB
        // config.isProxyTargetClass() 是否目标类本身被代理而不是目标类的接口
        // hasNoUserSuppliedProxyInterfaces()是否存在代理接口
        if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
            Class<?> targetClass = config.getTargetClass();
            if (targetClass == null) {
                throw new AopConfigException("TargetSource cannot determine target class: " +
                        "Either an interface or a target is required for proxy creation.");
            }

            // 2.如果目标类是接口类(目标对象实现了接口),则直接使用JDKproxy
            if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
                return new JdkDynamicAopProxy(config);
            }

            // 3.其他情况则使用CGLIBproxy
            return new ObjenesisCglibAopProxy(config);
        }
        else {
            return new JdkDynamicAopProxy(config);
        }
    }
复制代码

3, getProxy () method

   final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable// JdkDynamicAopProxy类结构,由此可知,其实现了InvocationHandler,则必定有invoke方法,来被调用,也就是用户调用bean相关方法时,此invoke()被真正调用
   // getProxy()
   public Object getProxy(ClassLoader classLoader) {
       if (logger.isDebugEnabled()) {
           logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());
       }
       Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
       findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);

       // JDK proxy 动态代理的标准用法
       return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
   }
复制代码

4, invoke () method Method

    //使用了JDK动态代理模式,真正的方法执行在invoke()方法里,看到这里在想一下上面动态代理的例子,是不是就完全明白Spring源码实现动态代理的原理了。
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        MethodInvocation invocation;
        Object oldProxy = null;
        boolean setProxyContext = false;

        TargetSource targetSource = this.advised.targetSource;
        Class<?> targetClass = null;
        Object target = null;

        try {
            // 1.以下的几个判断,主要是为了判断method是否为equals、hashCode等Object的方法
            if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
                // The target does not implement the equals(Object) method itself.
                return equals(args[0]);
            }
            else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
                // The target does not implement the hashCode() method itself.
                return hashCode();
            }
            else if (method.getDeclaringClass() == DecoratingProxy.class) {
                // There is only getDecoratedClass() declared -> dispatch to proxy config.
                return AopProxyUtils.ultimateTargetClass(this.advised);
            }
            else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
                    method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                // Service invocations on ProxyConfig with the proxy config...
                return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
            }

            Object retVal;

            if (this.advised.exposeProxy) {
                // Make invocation available if necessary.
                oldProxy = AopContext.setCurrentProxy(proxy);
                setProxyContext = true;
            }

            // May be null. Get as late as possible to minimize the time we "own" the target,
            // in case it comes from a pool.
            target = targetSource.getTarget();
            if (target != null) {
                targetClass = target.getClass();
            }
            // 2.获取当前bean被拦截方法链表
            List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

            // 3.如果为空,则直接调用target的method
            if (chain.isEmpty()) {
                Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
                retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
            }
            // 4.不为空,则逐一调用chain中的每一个拦截方法的proceed,这里的一系列执行的原因以及proceed执行的内容,我 在这里就不详细讲了,大家感兴趣可以自己去研读哈
            else {
                // We need to create a method invocation...
                invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                // Proceed to the joinpoint through the interceptor chain.
                retVal = invocation.proceed();
            }

            ...
            return retVal;
        }
    }
    }
复制代码


So coming here, I want to say content almost over, if there is something wrong, or have any doubts, we welcome the guidance!

更多精彩文章,关注公众号【ToBeTopJavaer】,更有数万元精品vip资源免费等你来拿!!!

                                 


Guess you like

Origin juejin.im/post/5dd76de7e51d452327583885