Java代理模式

代理模式是什么?

        使用代理对象来代替对真实对象的访问,在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。

        将目标对象注入进代理类,在代理类的对应方法调用目标类中的对应方法。这样的话,我们就可以通过代理类屏蔽对目标对象的访问,并且可以在目标方法执行前后做额外处理。

代理模式有静态代理动态代理两种实现方式,我们 先来看一下静态代理模式的实现。

1. 静态代理

  静态代理的实现步骤

(1)定义一个接口及其实现类

(2)创建一个类同样实现该接口

(3)将被代理对象注入代理类,在代理类的方法中调用被代理对象的方法

//定义接口
public interface SmsService {
    String send(String message);
}

//被代理类
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

//代理类
public class SmsProxy implements SmsService {

    //被代理对象
    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        //调用方法之前操作
        System.out.println("before method send()");
        //调用被代理对象的方法
        smsService.send(message);
        //调用方法之后操作
        System.out.println("after method send()");
        return null;
    }
}

//实际使用
public class Main {
    public static void main(String[] args) {
        创建被代理对象
        SmsService smsService = new SmsServiceImpl();
        //创建代理类对象
        SmsProxy smsProxy = new SmsProxy(smsService);
        //通过代理类调用方法
        smsProxy.send("java");
    }
}


        从代码中可以看出,静态代理对被代理类的每个方法都要手动增强,非常不灵活并且每个被代理类都需要单独的一个代理类

2.动态代理

        Java中动态代理的方式有多种,比如JDK动态代理和CGLIB动态代理。

2.1 JDK动态代理

        在 Java 动态代理机制中 InvocationHandler 接口和 Proxy 类是核心。

2.1.1 JDK 动态代理类使用步骤

1.  定义一个接口及其实现类

2.  自定义 InvocationHandler 并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;

3. 通过 Proxy类的newProxyInstance方法创建代理对象;

//定义接口
public interface SmsService {
    String send(String message);
}

//定义被代理类
public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

//定义JDK动态代理类
public class DebugInvocationHandler implements InvocationHandler {
  
    //被代理类对象
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        //调用方法之前操作
        System.out.println("before method " + method.getName());
        //调用被代理对象方法
        Object result = method.invoke(target, args);
        //调用方法之后操作
        System.out.println("after method " + method.getName());
        return result;
    }
}

//获取代理对象的工厂类
public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(), // 目标类的类加载器
                target.getClass().getInterfaces(),  // 代理需要实现的接口,可指定多个
                new DebugInvocationHandler(target)   // 代理对象对应的自定义 InvocationHandler
        );
    }
}
//实际使用
SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("java");

         从代码中可以看出通过使用JDK动态代理,我们不需要针对每个目标类都单独创建一个代理类。

2.2 CGLIB 动态代理机制

        CGLIB(Code Generation Library)是一个基于ASM的字节码生成库,允许我们在运行时对字节码进行修改和动态生成。CGLIB 通过继承方式实现代理。

        在 CGLIB 动态代理机中 MethodInterceptor 接口和 Enhancer 类是核心。

2.2.1  CGLIB 动态代理类使用步骤

(1)定义一个被代理类(不再需要定义接口)

(2)自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

(3)通过 Enhancer 类的 create()创建代理类

//定义一个被代理类
public class AliSmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        return message;
    }
}

//定义MethodInterceptor(方法拦截器)
public class DebugMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //调用方法之前操作
        System.out.println("before method " + method.getName());
        //调用被代理类的方法
        Object object = methodProxy.invokeSuper(obj, args);
        //调用方法之后操作
        System.out.println("after method " + method.getName());
        return object;
    }

}

//获取代理类
public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
        // 创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        // 设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        // 设置被代理类
        enhancer.setSuperclass(clazz);
        // 设置方法拦截器
        enhancer.setCallback(new DebugMethodInterceptor());
        // 创建代理类
        return enhancer.create();
    }
}

//实际使用
AliSmsService aliSmsService = (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("java");

3.总结

1. JDK 动态代理只能代理实现了接口的类或者直接代理接口,而 CGLIB 可以代理未实现任何接口的类。

2.  CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法

3. JDK 动态代理有一个最致命的问题是其只能代理实现了接口的类。

4. 静态代理在编译时就将接口、实现类、代理类这些都变成了一个个实际的 class 文件。而动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

5. 动态代理更加灵活,不需要必须实现接口,可以直接代理实现类(CGLIB动态代理),并且可以不需要针对每个目标类都创建一个代理类(JDK动态代理)。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值