静态代理 、动态代理

代理: A代理B对象,则以后 需要访问B对象 ,那就去先访问A对象。

如果想要增强代码,这时看A对象在执行B对象前(后)有木有增加什么逻辑。

1、静态代理

什么是静态代理?代理类由程序员自己写,在编译期就已经确定了。

静态代理的用途: 控制真实对象的访问权限 通过代理对象控制对真实对象的使用权限、增强。

 1)、定义一个 公共的接口

public inferface C{
   public void say();
}

2、被代理的对象:

public class B  implements C {
@OverLoad
public void say(){
        Sysout.system.out("我是B对象");
    }
}

3、创建代理对象:

public class A_proxy implement C{

    private B target;

    //代理对象 需要接收 被代理参数吧,所以这里构造函数这样写
    public A_proxy(B object){
        this.target=object;
        
    }
  
    @OverLoad
    public void say(){

     System.out.println("记录日志");//相当于代码增强

     target.say(); //调用代理对象的方法

     System.out.println("清理数据");//相当于代码增强
    }
}

 扩展和疑问:

通过上面的测序发现,代理是 代理对象通过调用 被代理对象的方法,无非在调用前后增加一些代码逻辑。这和  子类继承父类的 后 子类重写父类方法, 在重写父类方法内 先写自己的逻辑 再调用父类的该方法,再写自己的逻辑。这两种形式都达到了 对 原有方法的“增强”,那继承算不算代理的一种呢?

2、动态代理

虽然静态代理模式很好用,但是静态代理还是存在一些局限性的, 比如使用静态代理模式需要程序员手写很多代码,这个过程是比较浪费时间和精力。一旦 需要代理的类中方法比较多,或者需要同时代理多个对象的时候,这无疑会增加很大的复杂度。-------这时候就需要 动态代理了。
动态代理:在运行期动态生成代理对象。

实现动态代理的方式有两种:

1)、JDK 动态代理:

引用:JAVA动态代理 - 简书

java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口提供了生成动态代理类的能力。
优点:
缺点:有限制场景:被代理的对象是实现某个接口的对象,需要用 这个 接口 实现动态代理。
代码实现:
1)、 被代理的对象 实现的接口
public interface HelloInterface {
    void sayHello();
}

2)、被代理对象:

public class Hello implements HelloInterface{
    @Override
    public void sayHello() {
        System.out.println("Hello zhanghao!");
    }
}

2、自定义类(该类目的是:是指定运行时将生成的代理类需要完成的具体任务) 实现InvocationHandler 类,因为任何类调用方法 都会经过InvocationHandler 这个类,Invocation:英文含义 调用

public class ProxyHandler implements InvocationHandler{
    private Object object;
    public ProxyHandler(Object object){
        this.object = object;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Before invoke "  + method.getName());
        method.invoke(object, args);
        System.out.println("After invoke " + method.getName());
        return null;
    }





    public static void main(String[] args) {
        //用于保存生成的字节码文件,查看内容验证,生成的文件路径:classpath:com\sun\proxy下,搜Proxy0就看到了
        System.getProperties().setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        //新jdk可能需要使用这个设置 System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

        HelloInterface hello = new Hello();
        //将 被代理对象的父类引用 丢到自定义的处理器中
        InvocationHandler handler = new ProxyHandler(hello);
        //获取代理对象
        HelloInterface proxyHello = (HelloInterface) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), handler);

        proxyHello.sayHello();
    }
    输出:
    Before invoke sayHello
    Hello zhanghao!
    After invoke sayHello
}

生成的代理对象的字节码文件为:

 内容为:

/*可以看到,动态代理生成的这个类,
首先,继承 被代理的对象(意味着增强代码),且实现 被代理对象的接口(代表实现具体的方法)
其次,该类中,每个方法都去调用super.h.invoke(this, m3, 入参参数);
*/

// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.sun.proxy;

import com.patpat.cb.api.service.HelloInterface;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements HelloInterface {
    private static Method m1;// equal方法
    private static Method m3;//sayHellow方法
    private static Method m2;//toString方法
    private static Method m0;//hashcode方法

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

    public final void sayHello() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.patpat.cb.api.service.HelloInterface").getMethod("sayHello");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

总结: 把我们需要代理的那个类 的 父类引用(接口) 丢到 我们“自定义的处理器”中,自定义的处理器继承了InvocationHandler ,主要是重写它的 invoke()方法。然后通过 Proxy.newPropxyInstance(被代理类的了哦加载器,XX,XX)获取代理对象的引用。

三个传入参数被代理对象的类加载器、被代理对象的接口、继承了InvocationHandler自定义的处理器实例

对于Proxy.newPropxyInstance(XX,XX,XX)是如何工作的?

运行时解析到了需要加载的类的名称,使用反射技术,通过构造方法获取代理对象。(然后通过字节码技术动态生成对应的class字节码,加载到内存中得到class对象,进一步得到实例对象)。接着就可以调用所属的方法

2、Cglib 动态代理:

Cglib (Code Generation Library )是一个第三方类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。
优点:使用 cglib 代理的对象则无需实现接口,达到代理类无侵入。
缺点:

3、自己的想法实现的动态代理

java中可以使用 某些语句 执行 字符串类型的代码,那么 我们把要代理的对象的 代码  写成字符串,让 那些可执行的语句去执行“这些代码”,就能实现动态代理了呀!,像动态代理的 匹配规则可以写成 正则表达式匹配 字符串代码 的规则,所以效果上 岂不是也能实现动态代理?
(缺点应该是  这个代码 以 执行字符串代码 ,可读性不高,执行效率低?)

3、观察者模式实现

 场景:一个对象状态发生改变,监听者发现后就通知其他对象。项目中的应用,数据更新后,需要通知多个系统进行同步更新。

个人理解:虽然也可以不用此模式,但被通知者都是向消息队列中发送消息,就很有用,解耦了嘛,并且异步。

spring的实现:



public class MyEvent extends ApplicationEvent {

    private Object datas; //此属性是接收数据的,应改成你发送数据类型的格式

    public MyEvent(Object source, Object object) {
        super(source);
        this.datas = object;
    }

   //get() set()方法
}

@AllArgsConstructor
@Component
public class MyListener implements ApplicationListener<MyEvent> {



    @Override
    public void onApplicationEvent(MyEvent event) {
        System.out.println("收到事件:" + event);

        //tod 通知其他系统的业务逻辑
        this.log(event);
         this.updateProductPushToMMS(event); //todo
        this.updateProductsPushToCms(event);
         this.updateProductPriceMqToMMs(event); //todo
    }

调用:

class{

@AotuWire
private MyListener myListener;

    void ssss(){

        
     AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
        // 注册自定义事件监听器
        context.addApplicationListener(myListener); //此处也可以先new MyListener()
        // 启动上下文
        context.refresh();
        // 发布事件,事件源为Context
        context.publishEvent(new myEvent(context, eventBO)); // eventBO是封装了我们要携带的数据
        // 结束
        context.close();
}

}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值