JAVA代理模式


Java代理分为静态代理和动态代理。动态代理分为JDK动态代理和cglib动态代理。单纯从各种性能测试来说,JDK优于cglib。强烈建议掌握代理模式,面试高频问题,面向切面编程(AOP)的理念用的就是他。

静态代理

定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类

代码示例:

创建User接口:
public interface User {
    void show();
}
创建User接口实现类UserImpl.java
public class UserImpl implements User {
    @Override
    public void show() {
        System.out.println("实现Java静态代理");
    }
}
创建代理类Proxy.java
/**
 * 静态代理模式
 * 必须创建代理类
 * 代理模式给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。
 *
 */
public class Proxy implements User {
    private User user;

    //构造函数
    public Proxy(final User user){
        this.user=user;
    }
    
    @Override
    public void show() {
        System.out.println("插入代码执行前的逻辑代码");
        user.show();
        System.out.println("插入代码执行后的逻辑代码");
    }
}
创建测试类
public class Test {
    public static void main(String[] args) {

        User user = new UserImpl();
        Proxy proxy = new Proxy(user);
        proxy.show();

    }
}
运行结果:

在这里插入图片描述

JDK动态代理

JDK动态代理是利用反射机制在运行时创建代理类。核心是InvocationHandler。每一个代理的实例都会有一个关联的调用处理程序(InvocationHandler)。对待代理实例进行调用时,将对方法的调用进行编码并指派到它的调用处理器(InvocationHandler)的invoke方法。所以对代理对象实例方法的调用都是通过InvocationHandler中的invoke方法来完成的,而invoke方法会根据传入的代理对象、方法名称以及参数决定调用代理的哪个方法。

创建User接口
public interface User {
    void show();
}
创建User接口实现类UserImpl.java
public class UserImpl implements User{
    @Override
    public void show() {
        System.out.println("实现JDK动态代理");
    }
}
创建代理类MyProxyHandler.java

Proxy.newProxyInstance()方法接受三个参数:

  • ClassLoader loader:指定当前目标对象使用的类加载器,获取加载器的方法是固定的
  • Class<?>[] interfaces:指定目标对象实现的接口的类型,使用泛型方式确认类型
  • InvocationHandler:指定动态处理器,执行目标对象的方法时,会触发事件处理器的方法
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class MyProxyHandler implements InvocationHandler {

    private Object object;

    public MyProxyHandler(final Object object) {
        this.object = object;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("插入代码执行前的逻辑代码");
        Object result = method.invoke(object, args);
        System.out.println("插入代码执行后的逻辑代码");
        return result;
    }
}
创建测试类
public class Test {
    public static void main(String[] args) {
        
        User user = new UserImpl();
        User o1 = (User) Proxy.newProxyInstance(user.getClass().getClassLoader(),
                new Class[]{User.class}, new MyProxyHandler(user));
        o1.show();

    }
}
运行结果:

在这里插入图片描述

cglib动态代理

CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

pom.xml文件导入Maven依赖
<dependencies>
     <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
     </dependency>
</dependencies>
创建User类
public class User {
    public void show(){
        //实际开发中这里为逻辑业务开发代码
        System.out.println("实现cglib动态代理");
    }
}
创建代理类MyProxy.java
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class MyProxy implements MethodInterceptor {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("可以插入代码执行前的逻辑代码");
        Object o1 = methodProxy.invokeSuper(o,objects);
        System.out.println("可以插入代码执行后的逻辑代码");
        return o1;
    }
}
创建测试类
import net.sf.cglib.proxy.Enhancer;

public class Test {
    public static void main(String[] args) {
        Enhancer enhancer = new Enhancer();  // 通过CGLIB动态代理获取代理对象的过程
        enhancer.setSuperclass(User.class);     // 设置enhancer对象的父类
        enhancer.setCallback(new MyProxy());    // 设置enhancer的回调对象
        User user = (User) enhancer.create();   // 创建代理对象
        user.show();  // 通过代理对象调用目标方法
    }
}
运行结果:

在这里插入图片描述

总结

静态代理总结:

(1)其实,所谓的静态代理,其实就是创建一个类【名为代理类】,然后该类实现了与目标类【业务层对象】一样的接口后,
重写接口内容,里面做增强业务,也就是自定义操作,然后需要执行目标类的方法时,调用注入的目标类[即业务层对象]的方法即可,
说白了就是目标类外再套一层方法而已。
(2)重写目的是保证与目标类的方法一样,不仅可以确保不写错,还可以不用手写,这就很舒服了,
但是并不是说目标类必须要有接口才可以使用静态代理,因此,目标类不论是否有接口都可以使用静态代理。
(3)缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,类太多。同时,一旦接口增加方法,目标对象与代理对象都要维护。

JDK动态代理总结:

(1)动态代理解决静态代理的缺点,代理类不需要再与目标对象实现一样的接口。
(2)实例代理类后,注入目标类对象即可,然后调用代理类的方法会在内存动态的创建代理对象【使用Proxy.newProxyInstance】后将该代理对象返回,然后强转成目标对象一样的类型即可按照目标类型方法调用,创建代理对象需要输入的参数 分别是 目标类的类加载器 、目标对象实现的接口的类型、事件处理器 ,然后重写事件处理器里的方法,该方法内写自定义的增强业务,而目标对象的方法调用则固定使用 Object returnValue = method.invoke(target, args)完成 。
(3)虽然代理类没有直接实现在目标类所实现的接口,但是在内存动态的创建代理对象 方法的注入参数需要 目标对象实现的接口的类型 ,因此目标类必须要最少实现一个接口 ,否则无法使用动态代理。
(4)缺点:有些类不是业务层的,仅仅是一个单独的类 ,没有实现任何接口,是不能使用动态代理的。

CGLib代理总结:

(1)记得需要导入 cglib依赖包 ,这其实是个代码生成包,在内存中构建一个子类对象从而实现对目标对象功能的扩展。
(2)代理类需要实现MethodInterceptor接口 ,用于重写拦截方法【intercept()】,内部的增强操作方式 与 JDK动态代理十分相似。
(3)创建代理对象需要使用工具类Enhancer。
(4)允许代理那些那么没有实现任何接口的目标类,解决了动态代理的缺点。
(5)底层是ASM字节码框架,不建议直接操作ASM。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Map猿

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值