服务器之家:专注于VPS、云服务器配置技术及软件下载分享
分类导航

PHP教程|ASP.NET教程|Java教程|ASP教程|编程技术|正则表达式|C/C++|IOS|C#|Swift|Android|VB|R语言|JavaScript|易语言|vb.net|

服务器之家 - 编程语言 - Java教程 - 面试官:抛开Spring来说,如何自己实现Spring AOP?

面试官:抛开Spring来说,如何自己实现Spring AOP?

2023-11-27 17:30Java技术指北 Java教程

虽然Spring框架中的Spring AOP是Java社区中最著名的AOP实现,但为了完全理解这种思想,我们可以不依赖Spring来实现AOP功能。

哈喽,大家好,我是了不起。

作为一名Java程序员,面向切面编程这种编程思想,应该是我们日常编码中常应用的编程思想。

这种编程范式,旨在提高代码的模块化程度。在AOP中,特定类型的问题被定义为“切面”,例如日志、事务管理或安全性等,这些切面可以在不改变核心业务逻辑的情况下,被插入程序的不同部分。对于提高代码的优雅,减少冗余度特别有用。

虽然Spring框架中的Spring AOP是Java社区中最著名的AOP实现,但为了完全理解这种思想,我们可以不依赖Spring来实现AOP功能。

1、AOP 核心概念

1.1 切面(Aspects)

切面是AOP的核心,它将横切关注点(如日志、事务处理等)与主业务逻辑分离。一个切面定义了何时(何处)和如何执行这些横切关注点。

1.2 连接点(Join Points)

连接点是应用执行过程中能够插入切面的点。在Java中,这通常是方法的调用。

1.3 通知(Advice)

通知定义了切面具体要执行的操作。主要类型包括前置通知(before)、后置通知(after)、环绕通知(around)、抛出异常时通知(after throwing)和返回时通知(after returning)。

1.4 切点(Pointcuts)

切点定义了在哪些连接点执行切面代码。它是一组表达式,用于匹配特定的连接点。

2、使用Java动态代理

Java动态代理是一种在运行时创建代理对象的方法,代理对象可以在调用实际对象的方法前后执行额外的操作。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

// 简单的AOP实现
public class SimpleAOP {
    // 获取代理对象
    public static Object getProxy(Object target, Advice advice) {
        return Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    advice.beforeMethod(method);
                    Object result = method.invoke(target, args);
                    advice.afterMethod(method);
                    return result;
                }
            }
        );
    }

    // 通知接口
    public interface Advice {
        void beforeMethod(Method method);
        void afterMethod(Method method);
    }
}

在上述代码中,getProxy 方法创建了一个代理对象,该对象在每次方法调用前后执行定义在 Advice接口中的操作。

3、字节码操作

字节码操作是更高级但复杂的AOP实现方式。这涉及在类加载到JVM时修改其字节码,插入额外的代码。

3.1 使用ASM或ByteBuddy

  • ASM:一种低级字节码操作库,提供了对字节码的细粒度控制。
  • ByteBuddy:相比ASM,ByteBuddy提供了更简洁的API,适合那些不需要深入字节码细节的场景。

下面我以 ByteBuddy 为例,展示一下如何使用ByteBuddy来实现一个基本的AOP功能:在方法执行前后添加日志。

①、添加ByteBuddy依赖到你的项目中。如果你使用Maven,可以在pom.xml文件中加入以下依赖:


    net.bytebuddy
    byte-buddy
    1.11.22

②、使用ByteBuddy来创建一个代理类,这个类在方法执行前后打印日志:

  1. import net.bytebuddy.ByteBuddy; 
  2. import net.bytebuddy.implementation.FixedValue; 
  3. import net.bytebuddy.matcher.ElementMatchers; 
  4. import net.bytebuddy.implementation.MethodDelegation; 
  5. import net.bytebuddy.dynamic.DynamicType; 
  6. import net.bytebuddy.dynamic.loading.ClassLoadingStrategy; 
  7.  
  8. import java.lang.reflect.Modifier; 
  9.  
  10. public class AOPExample { 
  11.  
  12.     public static void main(String[] args) throws Exception { 
  13.         DynamicType.Unloaded dynamicType = new ByteBuddy() 
  14.             .subclass(Object.class
  15.             .method(ElementMatchers.named("toString")) 
  16.             .intercept(MethodDelegation.to(LoggerInterceptor.class)) 
  17.             .make(); 
  18.  
  19.         Class dynamicTypeLoaded = dynamicType 
  20.             .load(AOPExample.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER) 
  21.             .getLoaded(); 
  22.  
  23.         Object dynamicObject = dynamicTypeLoaded.newInstance(); 
  24.         System.out.println(dynamicObject.toString()); 
  25.     } 
  26.  
  27.     public static class LoggerInterceptor { 
  28.         public static String intercept() { 
  29.             System.out.println("Method intercepted before execution"); 
  30.             String result = "Hello from intercepted method"
  31.             System.out.println("Method intercepted after execution"); 
  32.             return result; 
  33.         } 
  34.     } 

在上述代码中,我们创建了一个代理类,它覆盖了toString方法。方法被调用时,我们的LoggerInterceptor类将被调用。在LoggerInterceptor类中,我们在方法执行前后添加了日志。

原文地址:https://mp.weixin.qq.com/s/AgdQC1sf2BFc7NaF87THWw

延伸 · 阅读

精彩推荐