17学Java之AOP以及装饰器模式 (2)

本文探讨了使用JDK动态代理和CGLIB实现代理模式的方法,对比装饰器模式,介绍了如何在不修改原始代码的情况下,为方法添加前置和后置处理逻辑,包括接口代理和类代理的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

前置简介

上一节:https://juejin.im/post/6865852407239147528/

我们说了使用装饰器模式给方法实现额外的功能,这样做的好处是我们无需改动原有的代码,

但是这样有一个缺点,每实现一个额外的功能,我们都需要新增加一个接口实现类,并且改动一些调用时候的代码,

那么可不可以我固定写好了一个类方法,程序可以自动的给我这个方法前后添加一些功能呢,如下:

public class DataService {
	public void 撸铁健身(String name) {
    	System.out.println(name + "正在健身中");
    }
}

我这个 DataService 就是一个类,也不想要继承任何接口,但就是希望在 某某某正在健身中 的前后分别打印出

某某某正在吃饭中

某某某健身完毕

这时候就需要用到动态代理了,上一篇我们说到的装饰器模式实现的代理,称为静态代理

因为我们是手动写好代码并进行调整编译的,所以称为静态代理,

下面我们来看看如何实现动态代理。

使用JDK自带的动态代理

首先我们创建一个接口基类, 因为JDK自带的动态代理是必须要基于接口的。

// 接口
public interface DataService  {
    String 撸铁健身(String name);
}

创建一个接口实现类

// 接口实现类
public class DataServiceImpl implements DataService {
    public String 撸铁健身(String name) {
        System.out.println(name + "正在健身中");
        return "健身完毕";
    }
}

下面创建一个调用处理类,也就是切面,在调用方法的前后做一些处理,实现java.lang.reflect.InvocationHandler这个接口

// 切面类
public class EatProxy implements InvocationHandler {
	// 代理对象
    DataService delegate;
	
    // 这里要注入需要被代理的对象,也就是健身类
    public EatProxy(DataService delegate) {
        this.delegate = delegate;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在调用健身方法前,进行吃饭操作
        System.out.println(args[0] + "正在吃饭中");
        // 调用真正的方法
        return method.invoke(delegate,args);
    }

}

创建代理类并进行方法调用


public class ApplicationMain {

    public static void main(String[] args) {
    	// 委托类
        DataService service = new DataServiceImpl();
        // 代理类
        // 三个参数
        // 第一个是定义生成的动态代理类的类加载器
        // 第二个是需要被代理的接口
        // 第三个是切面处理类
        DataService dataService = (DataService)Proxy.newProxyInstance(service.getClass().getClassLoader(),new Class[]{DataService.class}, new EatProxy(service));
        // 执行方法并输出结果
        System.out.println(dataService.撸铁健身("张三"));
    }

}

这么写,好像还有点类似装饰器模式,但是其实是有差别的,这一个代理类可以代理相关接口的所有方法,

我们只需要在实现的InvocationHandler类中的invoke方法通过method方法名做相应的处理就好了,

使用装饰器模式则需要实现每个接口方法并做处理。

JDK动态代理的优点是:方便,不需要依赖任何第三方的库。

缺点是:功能受限,只适用于接口,class则不行。

CGLIB/ByteBuddy

JDK自带的动态代理仅支持接口,但是我们使用CGLIB/ByteBuddy这些第三方库后就可以直接对class类进行代理了。

CGLIB/ByteBuddy的本质是对class类文件生成一个子类,如下:

这个是我们自己编写的类

public class DataService {
	public void sayHello() {
    	System.out.println("hello");
    }
}

CGLIB/ByteBuddy会偷偷的帮我们生成一个子类,这就是他的核心原理。

public class DataService$EnhancedByCglib extend DataService {
	public void sayHello() {
    	...执行一些操作
        super.hello();
		...执行一些操作
    }
}

下面通过CGLIB实现一个demo:

首先要在项目中引入jar包:

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>

实现代码:

public class ApplicationMain {
    static DataService service = new DataService();
	
    // 设置一个切面处理
    public static class LogInterceptor implements MethodInterceptor {
        private DataService delegate;

        public LogInterceptor(DataService delegate) {
            this.delegate = delegate;
        }

        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println(method.getName() + "执行了, 参数是:" + Arrays.asList(objects));
            // 执行真正的方法
            Object returnValue = method.invoke(delegate, objects);
            System.out.println(method.getName() + "返回结果了,结果是:" + returnValue);
            return returnValue;
        }
    }

    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();
        // 设置父类
        enhancer.setSuperclass(DataService.class);
        // 设置切面处理
        enhancer.setCallback(new LogInterceptor(service));
		// 创建子类
        DataService enhancerService = (DataService)enhancer.create();
        // 调用方法
        enhancerService.撸铁健身("张三");
    }
}

通过调试器查看,可以看到,这个类真正运行的时候,是一个名字叫做DataServiceEnhancerByCGLIBEnhancerByCGLIBEnhancerByCGLIB…的类,所以证明这个是通过父类继承的方式使用的。

查看输出结果,可以看到我们设置的切面执行了。

但是即便如此,我们还是觉得有一些不方便,因为我们设置了一个切面后,还需要手动将切面设置到动态代理类中,

下一节我们说说Aop在Spring中的使用,看看Spring是如何给我们带来方便的。

本篇代码示例:https://github.com/qiaomengnan16/DecoratorPatternDemo

下篇链接:https://juejin.im/post/6866411981163724814/

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值