首页 > 编程语言 >Java动态代理、AOP和装饰器模式

Java动态代理、AOP和装饰器模式

时间:2024-01-16 22:34:23浏览次数:28  
标签:Java catService CatService 代理 eat CatServiceImpl static AOP public

面向切面编程AOP-Aspect Oriented Programing,主要用于处理核心业务逻辑外的一些东西,
比如日志和缓存。这个“切面”可以理解为在代码的某个地方切一刀,在其中加一些东西。

装饰器

以日志为例,如果没有使用AOP,那么可以使用装饰来实现类似的代码。
我们使用装饰器模式来实现一下在执行一个方法前后打印代码的程序来演示。

// 提供一个接口
interface CatService {
    public String eat();
}

public class CatServiceImpl implements CatService {
    // 提供一个实现
    @Override
    public String eat() {
        System.out.println("Eating");
        return "Eating";
    }
}

// 以Decorator为类名实现一个CatService
public class LogDecorator implements CatService {
    private final CatService catService;

    // 这里提供一个基\父类,赋值给其子类,即多态
    public LogDecorator(CatService service) {
        this.catService = service;
    }

    @Override
    public String eat() {
        System.out.println("before eating");
        String eat = catService.eat();
        System.out.println("after eating");
        return eat;
    }
}

// 在main方法中执行
public class Main {

    static CatService service = new CatServiceImpl();
    static CatServiceImpl serviceImpl = new CatServiceImpl();
    static CatService catService = new LogDecorator(service);

    public static void main(String[] args) {
        // decorator实现
        String eat = catService.eat();
    }
}

JDK Proxy实现AOP

public class LogProxy implements InvocationHandler {
    // 同样是基类
    private CatService catService;

    public LogProxy(CatService catService) {
        this.catService = catService;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy before");
        Object o = method.invoke(catService, args);
        System.out.println("proxy after");
        return o;
    }
}

使用Proxy.newProxyInstance创建基于接口的动态代理实例,方法有三个参数:

  1. 类加载器 (ClassLoader): 用于加载生成的代理类,我们这里要生成CatServiceImpl代理类。
  2. 接口数组 (Class<?>[]): 代理实例所实现的接口的数组, 注意只能接收接口interface,不能接受类class。
  3. InvocationHandler (InvocationHandler): 当调用代理方法时,这里是上面实现了InvocationHandlerLogProxy类,其中实现了invoke方法。
static CatService service = new CatServiceImpl();
static CatService catService = new LogDecorator(service);

public static void main(String[] args) {
    jdkAOP();
}

public static void jdkAOP() {
    Object o = Proxy.newProxyInstance(service.getClass().getClassLoader(),
            new Class[]{CatService.class},
            new LogProxy(service));
    CatService catServiceProxy = (CatService) o;
    String eat = catServiceProxy.eat();
    System.out.println("eat:" + eat);
}

JDK动态代理只适⽤于接⼝,但是不需要依赖任何第三⽅库

Cglib实现AOP日志

cglib是一个用来生成字节码的库,不过已经好多年没更新了,文档中说在新的JDK中可能不能工作了,尤其是JDK 17以后。

public class LoginInterceptor implements MethodInterceptor {
    CatServiceImpl catService;

    public LoginInterceptor(CatServiceImpl catService) {
        this.catService = catService;
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("cglib before");
        Object o1 = methodProxy.invoke(catService, objects);
        System.out.println("cglib after");
        return o1;
    }
}

public static void main(String[] args) {
    jdkAOP();
}
public static void cglibAOP() {
    Enhancer enhancer = new Enhancer();
    enhancer.setSuperclass(CatServiceImpl.class);
    enhancer.setCallback(new LoginInterceptor(serviceImpl)) ;

    CatServiceImpl en = (CatServiceImpl) enhancer.create();
    String eat = en.eat();
    System.out.println("eat:" + eat);
}

不能增强final类/final/private⽅法

ByteBuddy 实现AOP日志

Cglib提到可以迁移到ByteBuddy,那就试一试吧。

先引入

<dependency>
    <groupId>net.bytebuddy</groupId>
    <artifactId>byte-buddy</artifactId>
    <version>1.14.11</version>
</dependency>
public class CatServiceAspect {
    public static CatService apply() {
        CatService catService = null;
        try {
            catService = new ByteBuddy()
                    .subclass(CatServiceImpl.class)                               // 继承CatServiceImpl类
                    .method(ElementMatchers.named("eat"))                         // ElementMatchers.any()可以匹配所有方法
                    .intercept(MethodDelegation.to(CatServiceInterceptor.class))  // 拦截eat()方法并调用CatServiceInterceptor的eat()方法
                    .make()
                    .load(CatServiceAspect.class.getClassLoader())
                    .getLoaded()
                    .getDeclaredConstructor().newInstance();
        } catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
            throw new RuntimeException(e);
        }
        return catService;
    }
}

创建一个拦截器,处理eat方法。

public class CatServiceInterceptor  {
    // 注意这里是static方法,否则会提示None of [] allows for delegation。没有方法能被代理
    // ByteBuddy的SuperCall注解表示zuper是实际的可执行的CatServiceImpl中的eat方法
    public static String eat(@SuperCall Callable<?> zuper) throws Exception {
        System.out.println("before bytebuddy eating");
        String call = (String) zuper.call();
        System.out.println("after bytebuddy eating");
        return call;
    }
}

在main方法中调用

public class Main {

    static CatService service = new CatServiceImpl();
    static CatServiceImpl serviceImpl = new CatServiceImpl();
    static CatService catService = new LogDecorator(service);

    public static void main(String[] args) {
        logBytebuddy();
    }

    public static void logBytebuddy() {
        CatService apply = CatServiceAspect.apply();// 应用面向切面编程的配置
        String eat = apply.eat();
        System.out.println(eat);
    }
}

不能增强final类/final/private⽅法

如果引入maven依赖的时候提示the trustAnchors parameter must be non-empty可能是jdk中的cacerts过期了,不在可信任的根证书颁发机构(CA)中了。可以尝试用新一点的JDK。

标签:Java,catService,CatService,代理,eat,CatServiceImpl,static,AOP,public
From: https://www.cnblogs.com/shames/p/17968717

相关文章

  • java 依赖 stable diffusion
    Java依赖的稳定扩散简介Java的依赖稳定扩散是指将一个Java项目的依赖库从一个环境迁移到另一个环境,确保依赖库在新环境中正常工作。本文将介绍实现这一过程的步骤和相应的代码。流程概览以下是实现Java依赖稳定扩散的步骤概览:步骤描述1.创建空白的Java项目......
  • java实现文心一言流式输出
    Java实现文心一言流式输出教程简介在本教程中,我将教授你如何使用Java实现文心一言流式输出。文心一言是一种短小精悍的句子,适合用于展示在网站的底部或侧边栏等位置。通过流式输出,可以实现每隔一段时间自动更新展示的文心一言。整体流程下面是实现文心一言流式输出的整体流程,可......
  • java链式脱敏
    //1.定义脱敏接口publicinterfaceIReplaceMask{publicStringmaskStr(Stringstr);}importjava.util.regex.Matcher;importjava.util.regex.Pattern;//2.邮箱脱敏实现脱敏接口publicclassEmailMaskimplementsIReplaceMask{publicStringmaskStr(Stri......
  • java基础
    一:java概述:1991 年Sun公司的James Gosling等人开始开发名称为 Oak 的语言,希望用于控制嵌入在有线电视交换盒、PDA等的微处理器;1994年将Oak语言更名为Java;Java的三种技术架构:JAVAEE:Java Platform Enterprise Edition,开发企业环境下的应用程序,主要针对w......
  • java排序算法
    Java中常用的排序算法包括以下几种:冒泡排序(BubbleSort):这是一种简单的排序算法,通过重复地遍历待排序的序列,比较相邻的两个元素,如果它们的顺序错误就把它们交换过来。遍历序列的工作是重复地进行直到没有再需要交换,也就是说该序列已经排序完成。选择排序(SelectionSort):这种排序算法......
  • java使用whisper命令生成字幕文件
    Java使用Whisper命令生成字幕文件引言在日常生活中,我们经常会遇到需要生成字幕文件的情况,例如在制作视频时需要添加字幕。而Java作为一种强大的编程语言,可以通过调用命令行工具来实现生成字幕文件的功能。本文将介绍如何使用Java调用Whisper命令来生成字幕文件,并提供相应的代码示......
  • java怎么调用Stable Diffusion
    使用Java调用StableDiffusion的方案问题背景假设有一个实时数据流,需要使用StableDiffusion算法进行稳定的分发。你的任务是使用Java编写代码来调用StableDiffusion,并将数据正确地分发给不同的客户端。解决方案步骤1:安装StableDiffusion首先,需要安装StableDiffusion库。......
  • 16_Java基础-包
    包机制包=文件夹语法格式:packagepkg1[.pkg2[.pkg3…]];一般利用公司域名倒置作为包名:com.baidu.www域名:www.baidu.com为了能够使用一个包的成员,需要在Java中导入该包,用“import”完成importpackge1*(通配符):导入这个包下所有的类!推荐《阿里巴巴开发......
  • 14_Java基础-运算符4:条件运算符
    运算符***优先级()***扩展赋值运算符a+=b;//a=a+ba-=b;//a=a-b//偷懒时使用,尽量不用字符串链接:+先出现String,结果会拼接。后出现String,结果继续运算。条件运算符(必须掌握)x?y:z如果x=true,结果为y,否则结果为z。......
  • 使用 Picocli 开发 Java 命令行,5 分钟上手
    大家好,我是鱼皮,对不会前端的同学来说,开发命令行工具是一种不错的展示系统功能的方式。在Java中开发命令行工具也很简单,使用框架,几分钟就能学会啦~Picocli入门Picocli是Java中个人认为功能最完善、最简单易用的命令行开发框架,可以帮助大家快速开发命令行工具。网上有关Pico......