揭示 Spring AOP 的内部工作原理(揭示.工作原理.Spring.AOP...)

wufei1232024-09-08java1

揭示 spring aop 的内部工作原理

在这篇文章中,我们将揭秘 spring 中面向方面编程 (aop) 的内部机制。重点将放在理解 aop 如何实现日志记录等功能,这些功能通常被认为是一种“魔法”。通过浏览核心 java 实现,我们将了解 java 的反射、代理模式和注释,而不是任何真正神奇的东西。

先决条件
  • java 核心代理 api
  • 反射api
  • 注释api

这些都是 java.lang.reflect、java.lang.annotation 和 javassist.util.proxy 包的一部分。

核心机制

spring aop 的核心是代理对象、方法拦截器和反射的概念。此模式中的关键角色是 methodhandler(或调用处理程序)。该处理程序通过拦截方法调用来控制代理对象的行为。当在代理上调用方法时,它会通过处理程序传递,其中可以通过反射对注释进行内省。根据应用的注释,可以在异常之前、之后或异常时执行必要的逻辑(例如日志记录)。

分解它
  1. 代理对象:这些是动态创建的对象,代表您的实际业务对象,通过方法处理程序路由方法调用。
  2. 调用处理程序:这就是拦截的神奇之处。使用反射,处理程序可以检查目标方法上存在的注释并相应地改变行为。
  3. 自定义注释:您可以定义自定义注释,它们用作触发日志记录、安全检查或事务管理等附加功能的标记。

示例: 假设我们想在某些方法执行之前和之后添加日志记录。我们可以使用 @beforemethod 和 @aftermethod 注释方法,而不是到处硬编码日志记录。我们的处理程序检查此注释的方法并动态添加适当的日志记录逻辑。

下面是我们示例中控制器和服务的类。

workercontroller.java

package edu.pk.poc.aop.controller;

import edu.pk.poc.aop.annotation.aftermethod;
import edu.pk.poc.aop.annotation.all;
import edu.pk.poc.aop.annotation.beforemethod;
import edu.pk.poc.aop.helper.proxyfactory;
import edu.pk.poc.aop.service.worker;
import edu.pk.poc.aop.service.workerservice;
import edu.pk.poc.aop.service.workerserviceimpl;

public class workercontroller {
    workerservice workerservice = proxyfactory.createproxy(workerserviceimpl.class);
    /**
     * this method 1s annotated with @beforemethod and @aftermethod, so the log statements
     * will be generated before and after method call.
     */
    @beforemethod
    @aftermethod
    public void engagefulltimeworker() throws exception {
        worker fulltimeworker = new worker();
        fulltimeworker.setname("fulltime-worker");
        fulltimeworker.setparttime(false);
        fulltimeworker.setduration(9);
        workerservice.dowork(fulltimeworker);
    }
    /**
     * this method is annotated with @all, so the log statements will be generated before and after method call
     * along with exception if raised.
     */
    @all
    public void engageparttimeworker() throws exception {
        worker parttimeworker = new worker();
        parttimeworker.setname("parttime-worker");
        parttimeworker.setparttime(true);
        parttimeworker.setduration(4);
        workerservice.dowork(parttimeworker);
    }
}

workerserviceimpl.java

package edu.pk.poc.aop.service;

import edu.pk.poc.aop.annotation.aftermethod;

public class workerserviceimpl implements workerservice {
    /**
     * here this method is annotated with only @aftermethod, so only log statement
     * will be generated after method call
     */
    @aftermethod
    @override
    public void dowork(worker worker) throws exception {
        if (worker.isparttime()) {
            throw new exception("part time workers are not permitted to work.");
        }
        system.out.print("a full time worker is working for " + worker.getduration() + " hours :: ");
        for (int i = 1; i 



<p><strong>main.java 测试类</strong><br></p>

<pre class="brush:php;toolbar:false">package edu.pk.poc.aop.test;

import edu.pk.poc.aop.controller.workercontroller;
import edu.pk.poc.aop.helper.proxyfactory;
import edu.pk.util.logger;

public class main {
    public static void main(string[] args) {
        workercontroller controller = proxyfactory.createproxy(workercontroller.class);
        logger logger = new logger();
        try {
            system.out.println("testing @beforemethod and @aftermethod");
            system.out.println("-----------------------------------------");
            controller.engagefulltimeworker();
            system.out.println("testing @all");
            system.out.println("-----------------------------------------");
            controller.engageparttimeworker();
        } catch (exception e) {
            logger.error("exception caught in main class");
        }
    }
}

输出

testing @beforemethod and @aftermethod
-----------------------------------------
&gt;&gt;&gt; entering into edu.pk.poc.aop.controller.workercontroller.engagefulltimeworker()
a full time worker is working for 9 hours :: * * * * * * * * 
&gt;&gt;&gt; exiting from edu.pk.poc.aop.service.workerserviceimpl.dowork()
&gt;&gt;&gt; exiting from edu.pk.poc.aop.controller.workercontroller.engagefulltimeworker()
testing @all
-----------------------------------------
&gt;&gt;&gt; entering into edu.pk.poc.aop.controller.workercontroller.engageparttimeworker()
&gt;&gt;&gt; exception in edu.pk.poc.aop.controller.workercontroller.engageparttimeworker()
exception caught in main class
它是如何运作的

当在代理对象上调用方法时,该调用会被处理程序拦截,处理程序使用反射来检查目标方法上的所有注释。根据这些注释,处理程序决定是否记录方法进入/退出、记录异常或完全跳过记录。

以下是可视化它的方法:

  • 执行前:记录方法条目。
  • 执行后:记录方法退出或成功。
  • 全部:记录方法条目、方法条目以及引发的异常。 这种动态行为表明 spring aop 利用了核心 java api,而不是使用一些神奇的技巧。

定义注释

package edu.pk.poc.aop.annotation;

import java.lang.annotation.elementtype;
import java.lang.annotation.retention;
import java.lang.annotation.retentionpolicy;
import java.lang.annotation.target;

@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface aftermethod {

}
package edu.pk.poc.aop.annotation;

import java.lang.annotation.elementtype;
import java.lang.annotation.retention;
import java.lang.annotation.retentionpolicy;
import java.lang.annotation.target;

@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface beforemethod {

}
package edu.pk.poc.aop.annotation;

import java.lang.annotation.elementtype;
import java.lang.annotation.retention;
import java.lang.annotation.retentionpolicy;
import java.lang.annotation.target;

@retention(retentionpolicy.runtime)
@target(elementtype.method)
public @interface all {

}

定义代理工厂

package edu.pk.poc.aop.helper;

/**
 * the {@code proxyfactory} class is responsible for creating proxy objects using the javassist library.
 * it allows for dynamic generation of proxies for classes or interfaces, with support for method interception.
 */
public class proxyfactory {

    /**
     * a javassist proxyfactory instance used to generate proxy classes.
     */
    private static final javassist.util.proxy.proxyfactory factory = new javassist.util.proxy.proxyfactory();

    /**
     * creates a proxy object for the given class or interface.
     * if the class is an interface, the proxy implements the interface.
     * if it's a concrete class, the proxy extends the class.
     *
     * @param <t>   the type of the class or interface for which the proxy is to be created
     * @param klass the {@code class} object representing the class or interface to proxy
     * @return a proxy instance of the specified class or interface, or {@code null} if proxy creation fails
     */
    public static <t> t createproxy(class<t> klass) {
        if (klass.isinterface())
            factory.setinterfaces(new class[]{klass});
        else
            factory.setsuperclass(klass);
        try {
            return (t) factory.create(new class&gt;[0], new object[0], new aoploggingmethodhandler());
        } catch (exception e) {
            system.err.println(e.getmessage());
        }
        return null;
    }
}
</t></t></t>

定义 methodhandler

package edu.pk.poc.aop.helper;

import edu.pk.poc.aop.annotation.AfterMethod;
import edu.pk.poc.aop.annotation.All;
import edu.pk.poc.aop.annotation.BeforeMethod;
import edu.pk.poc.aop.annotation.OnException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import edu.pk.util.Logger;
import javassist.util.proxy.MethodHandler;

public class AOPLoggingMethodHandler implements MethodHandler {

    private static final Logger logger = new Logger();

    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        if (proceed != null) { // Concrete Method
            Object result = null;
            String className = resolveClassName(self);
            try {
                if (isAnnotationPresent(thisMethod, BeforeMethod.class) || isAnnotationPresent(thisMethod, All.class)) {
                    logger.info("&gt;&gt;&gt; Entering into " + className + "." + thisMethod.getName() + "()");
                }
                result = proceed.invoke(self, args);
                if (isAnnotationPresent(thisMethod, AfterMethod.class) || isAnnotationPresent(thisMethod, All.class)) {
                    logger.info("&gt;&gt;&gt; Exiting from " + className + "." + thisMethod.getName() + "()");
                }
            } catch (Throwable t) {
                if (isAnnotationPresent(thisMethod, OnException.class) || isAnnotationPresent(thisMethod, All.class)) {
                    logger.error("&gt;&gt;&gt; Exception in " + className + "." + thisMethod.getName() + "()");
                }
                throw t;
            }
            return result;
        }
        throw new RuntimeException("Method is Abstract");
    }

    private boolean isAnnotationPresent(Method method, Class klass) {
        Annotation[] declaredAnnotationsByType = method.getAnnotationsByType(klass);
        return declaredAnnotationsByType != null &amp;&amp; declaredAnnotationsByType.length &gt; 0;
    }

    private String resolveClassName(Object self) {
        String className = self.getClass().getName();
        if (className.contains("_$$")) {
            className = className.substring(0, className.indexOf("_$$"));
        }
        return className;
    }
}
结论

spring aop 是一个用于横切关注点的强大工具,但它并没有做任何革命性的事情。它建立在反射和代理等核心 java 概念之上,这些概念在语言本身中可用。通过理解这一点,您可以更好地理解 spring 如何简化这些底层机制以方便开发人员。

以上就是揭示 Spring AOP 的内部工作原理的详细内容,更多请关注知识资源分享宝库其它相关文章!

发表评论

访客

◎欢迎参与讨论,请在这里发表您的看法和观点。