乐趣区

关于后端:面试篇之HR问什么是静态代理什么是动态代理

面试篇之 HR 问什么是动态代理?什么是动静代理?

退出交换群返回:CN.ITLTT.COM

何为代理?

Java 中的代理,开源了解为通过代理去拜访理论的指标对象,比方呢?咱们平时交易二手车的中间商,就能够看作一个代理类,不过你也能够间接去和二手车的客人交易。

那这种状况,在 Java 中就被称之为代理,代理类除了去实现目标对象外,他还能够去在其中减少许多额定性能。

实践扩大:

次要解决的问题:在间接拜访对象时带来的问题,比如说:要拜访的对象在近程的机器上。在面向对象零碎中,有些对象因为某些起因(比方对象创立开销很大,或者某些操作须要安全控制,或者须要过程外的拜访),间接拜访会给使用者或者系统结构带来很多麻烦,咱们能够在拜访此对象时加上一个对此对象的拜访层。

代理模式的必要条件:独特接口、代理对象、指标对象。

宏观个性:对客户端只暴露出接口,不裸露它以下的架构。

长处:两头隔离了一层,更加合乎开闭准则。

代理是一种?

代理是一种设计模式

  • 他并非一种自带的性能,而是一种设计模式。
  • 在代理模式中,一个类代表另一个类的性能。
  • 这种类型的设计模式属于结构型模式。
  • 在代理模式中,咱们创立具备现有对象的对象,以便向外界提供性能接口。
  • 目标:为其余对象提供一种代理以管制对这个对象的拜访。

代理模式分类

代理模式分为两种类型:

  • 动态代理
  • 动静代理

实现动态代理

实践不多说,从代码中了解。

创立接口

首先,咱们创立一个接口,个别咱们一个性能都会去设计一个接口,包含咱们的三层架构也是这样,所以我这也写一个接口。

/**
 * @author JanYork
 * @date 2022/10/25 8:59
 * @description 假如 Demo 是买货色的接口
 */
public interface Demo {
    /**
     * 买货色
     * @param name 货色的名字
     * @param price 货色的价格
     * @return 买货色的后果
     */
    String buy(String name, int price);
}

那咱们就,假如这个 Demo 是买货色的接口。

也是提供一个简简单单的购买货色的办法(实际上就是输入测试一下)。

实现接口

咱们还要去实现这个接口,也就是咱们的接口实现类。

/**
 * @author JanYork
 * @date 2022/10/25 9:03
 * @description 实在的 Demo 类(被代理类)
 */
public class RealDemo implements Demo {public RealDemo(String name, int price) {
        this.name = name;
        this.price = price;
    }

    private String name;
    private int price;

    @Override
    public String buy(String name, int price) {System.out.println("买了" + name + "花了" + price + "元");
        return "买了" + name + "花了" + price + "元";
    }
}

给他两个参数,一个结构,而后重写接口提供的办法,这个不须要多说。

代理类

/**
 * @author JanYork
 * @date 2022/10/25 9:05
 * @description Demo 的代理类
 */
public class ProxyDemo implements Demo {

        private RealDemo realDemo;

        public ProxyDemo(String name, int price) {this.realDemo = new RealDemo(name, price);
        }

        @Override
        public String buy(String name, int price) {System.out.println("----- 代理类开始买货色 ------");
            String result = realDemo.buy(name, price);
            System.out.println("------ 代理类买货色完结 ------");
            return result;
        }
}

创立代理后咱们也须要去实现这个 Demo 接口。

而后须要注入实现接口的类的对象(也就是实在类)。

private RealDemo realDemo;

而后实现结构:

public ProxyDemo(String name, int price) {this.realDemo = new RealDemo(name, price);
}

而后在重写办法外面调用,能够在调用办法前后干一些事件。

而后咱们创立一个 Test 类测试:

动态代理缺点

问:既然动态代理能够不便的达到目标,那他有什么毛病吗?

动态代理在代码 运行之前就须要创立好代理类 ,因而对于每一个代理对象都须要建一个代理类去代理。如果说,你须要代理的对象很多,那就 须要创立很多代理类 升高程序的可维护性

问:那如何解决这个缺点呢?

动静构建代理类,也就是动静代理。

动静代理

动静代理的代理类是在运行过程中产生的。

Java提供了两种实现动静代理的形式:

  • 基于 JDK 的动静代理。
  • 基于 Cglib 的动静代理。

特点:字节码随用随创立,随用随加载。

作用:在不批改源码的根底上对办法加强。

基于 JDK 实现

实现 Jdk 的动静代理须要实现 InvocationHandler 接口,而后实现其中的 invoke 办法。

咱们创立一个类,实现 InvocationHandler(来自java.lang.reflect 反射包下)接口。

这个类下,有十分丰盛的具体的解释,能够看看。

实现了接口后,咱们给他用 Object 来代替原先动态代理类中的这一段:

private RealDemo realDemo;
  public ProxyDemo(String name, int price) {this.realDemo = new RealDemo(name, price);
}

也就是这样:

private Object target;

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

而后重写 invoke 办法:

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("----- 动静代理开始 ------");
    Object invoke = method.invoke(target, args);
    System.out.println("------ 动静代理完结 ------");
    return invoke;
}

外面有多个参数,稍后阐明。

调用

首先还是创立实在对象,并且给出结构。

RealDemo realDemo = new RealDemo("苹果", 10);

而后创立动静代理对象,将正式对象传输过来。

ProxyHandler proxyHandler = new ProxyHandler(realDemo);

而后咱们调用,实际上就是应用 Proxy 这个类来调用 newProxyInstance 这个办法。

Proxy.newProxyInstance(realDemo.getClass().getClassLoader(), realDemo.getClass().getInterfaces(), proxyHandler);

这个办法是一个静态方法,参数如下。

newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

第一个是一个类加载器,第二个是一个带泛型的 Class 数组,并且要求是一个 interfaces,第三个参数就是实现了InvocationHandler 的对象了。

那咱们调用时给出的参数就是:

// 获取实在类的类加载器
realDemo.getClass().getClassLoader(), 
// 获取实在类所实现的接口
realDemo.getClass().getInterfaces(),
// 实现 InvocationHandler 接口的对象
proxyHandler

这部分对反射这方面常识有很多设计,不懂的还是倡议去折腾折腾。

那咱们整个调用动静对象的办法,如下:

public class TestDemo {public static void main(String[] args) {//        ProxyDemo proxyDemo = new ProxyDemo("苹果", 10);
//        proxyDemo.buy("苹果", 10);

        // 调用动静代理
        RealDemo realDemo = new RealDemo("苹果", 10);

        ProxyHandler proxyHandler = new ProxyHandler(realDemo);

        Demo demo = (Demo) Proxy.newProxyInstance(realDemo.getClass().getClassLoader(), realDemo.getClass().getInterfaces(), proxyHandler);
        demo.buy("苹果", 10);
    }
}

Proxy.newProxyInstance办法返回的是一个Object,咱们将他转型为咱们的接口对象。

而后通过对象调用对应办法。

成果
劣势

一个动静代理能够对 N 个须要代理的对象起作用,只须要将须要代理类的参数放入 Proxy.newProxyInstance 办法即可。

缺点

JDK只能代理接口!

JDK动静代理的确只能代理接口,JDK 动静代理是基于接口的形式,换句话来说就是代理类和指标类都实现同一个接口。如果想要代理类的话能够应用 CGLibCGLib 动静代理是代理类去继承指标类,而后实现目标类的办法。

基于 CGLib 实现

首先引入依赖:

<dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version> 版本号 </version>
</dependency>

如果你是 Spring 我的项目,那就不用了,曾经整合了。

咱们创立一个类,用于给 CGLib 测试。

/**
 * @author JanYork
 * @date 2022/10/25 10:32
 * @description 基于 Cglib 的动静代理的指标类
 */
public class CgDemo {public String buy(String name, int price) {System.out.println("买了" + name + ",价格是" + price);
        return "买了" + name + ",价格是" + price;
    }
}

而后创立 CGLib 的代理类:

/**
 * @author JanYork
 * @date 2022/10/25 10:34
 * @description
 */
public class CgProxy implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println("-----cglib 代理开始 ------");
        Object invoke = methodProxy.invokeSuper(o, objects);
        System.out.println("------cglib 代理完结 ------");
        return invoke;
    }
}
调用
// 创立 Enhancer 对象
Enhancer enhancer = new Enhancer();
// 设置父类(也就是指标类)
enhancer.setSuperclass(CgDemo.class);
// 设置回调函数
enhancer.setCallback(new CgProxy());
// 创立代理对象
CgDemo cgDemo = (CgDemo) enhancer.create();
// 调用代理对象的办法
cgDemo.buy("苹果", 10);

Enhancercglib.proxy 下的一个 生成动静子类 以启用办法拦挡的类。

Enhancer 类是 CGLib 中最罕用的一个类,和 JDK 1.3 动静代理中引入的 Proxy 类差不多(Proxy 类是 Java 动静代理机制的主类,它提供了一组静态方法来为一组接口动静地生成代理类及其对象)。

Proxy 不同的是,Enhancer 类既可能代理一般的 class,也可能代理接口。Enhancer 创立一个被代理对象的子类并且拦挡所有的办法调用(包含从 Object 中继承的 toString()hashCode() 办法)。

成果

利用场景

问:动静代理这么牛,平时工作中有应用到吗?

在平时的业务代码,简直是用不到代理的。

然而,Spring系列框架中的 AOP,以及RPC 框架中都用到了动静代理。

如:AOP 通过动静代理对指标对象进行了加强,比方咱们最罕用的前置告诉、后置告诉等。

全篇完,我是小简,下篇再见。

退出移动版