JAVA静态代理与动态代理的原理和实现

反射(Reflection)

  反射机制允许程序在执行期借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。

      Class clazz = Person.class; // 创建类对象,clazz 指向了 Person.class(源头)这个文件

      Person P = clazz.newInstance(); // 创建运行时类的对象

  可以用 clazz(类对象)直接 get ~(); 获取 类的属性或方法再对它进行相应的操作。

   

Filed f = clazz.getDeclaredField("属性名"); // 获取类中的属性(private也可以获取)
Method m = clazz.getDeclaredMethod("方法名"); // 获取类中的方法
// 开启可获取 private属性
f.setAccessible(true);
m.setAccessible(true);
// 操作属性
f.set(操作的对象, 属性值);
m.invoke(操作的对象);

    declared 表示:声明(只要类中有就可以获取到)

 

对比

  正常方式:

    引入需要的“包类”名称 -> 通过new实例化 -> 取得实例化对象

  反射方式:

    实例化对象 -> 调用 getClass() 方法 -> 得到完整的“包类”名称

  故:反射就是正常获取对象的逆操作。

  注:.class 文件加载到内存以后,就是一个运行时类。这个运行时类本身就是一个Class的实例,每个运行时类只加载一次。

Class clazz = Person.class; // Class 的实例 <-> 类对象
Person p = new Person(); // 运行时类的对象 <-> 类的对象

获取 Class 实例的方法

  1、通过调用运行时类本身的 .class 属性 

Class clazz = Person.class;

  2、通过运行时类的对象获取

Person p = new Person();
Class clazz = p.getClass();

  3、通过 Class 的静态方法获取

Class clazz = Class.forName("com.mysql.jdbc.Driver"); // 获取jdbc驱动的Class实例

  4、类的加载器获取

Class clazz = this.getClass().getClassLoader().loadClass("com.mysql.jdbc.Driver");

  4.1、类加载器的一个作用:获取文件输入流

ClassLoader loader = this.getClass().getClassLoader(); // 获取类加载器
InputStream in = loader.getResourceAsStream("文件路径"); // 获取文件的输入流
// 若操作的是 properties 文件
Properties props = new Properties();
props.load(in); // 把文件输入流加载到 props 对象中
props.getProperty("Key"); // 根据 key 获取相应的 value

 

代理设计模式

  什么是代理模式:

        使用一个代理将对象包装起来,然后用该代理对象取代原始对象。任何对原始对象的调用都要通过代理。代理对象决定是否以及何时将方法调用转到原始对象上。

  以下源码下载:src/day18_1205proxy

  1、静态代理

        要求被代理类同时实现相应的一套接口,在实例化代理对象的时候同时包装被代理类对象;通过代理类的对象调用重写接口的方法时,实际上执行的是被代理类的对象的同样方法的调用;

/**
 * 静态代理模式
 */
// 接口
interface ClothFactory{
    void productCloth();
}

// 被代理类
class NikeClothFactory implements ClothFactory{

    @Override
    public void productCloth() {
        System.out.println("Nike工厂生产一批衣服(被代理对象的方法)");
    }
}

// 代理类
class ProxyFactory implements ClothFactory{

    ClothFactory cf;
    // 创建代理对象的时候需要将被代理对象传进来,对被代理对象进行包装
    // 创建代理对象时,实际传入一个被代理对象
    public ProxyFactory(ClothFactory cf){
        this.cf = cf;
    }

    @Override
    public void productCloth() {
        System.out.println("代理类开始执行 *******");
        // 使用被代理对象来调用对应的方法
        cf.productCloth();
    }
}

// 测试
public class StaticProxy {

    public static void main(String[] args) {

        ClothFactory nike = new NikeClothFactory(); // 创建被代理对象
        ProxyFactory proxy = new ProxyFactory(nike);  // 创建代理对象来包装被代理对象
        // 实际是在代理对象中的方法里面用被代理对象来调用对应的方法
        proxy.productCloth();
    }


}

  

  2、动态代理

        在程序运行时,根据代理类及其实现接口,动态的创建一个代理类。(比起静态代理的好处是不用 为每一个被代理类写一个对应实现统一接口的代理类,而是通过反射的方式动态生成一个代理类对象)。当调用代理类对的实现方法的抽象方法时,就会发起对被代理类同样的方法的调用

涉及到的技术点:
    1、需要一个实现 InvocationHandler接口的实现类,并重写invoke()方法,
        代理类执行调用方法时都会跳转到invoke()方法中;
    2、创建一个包装了被代理类的代理类对象
    Proxy.newInstance(obj.getClass().getClassLoader,obj.getClass().getInterfaces().handler);
    // 注: obj = 被代理类对象 ; handler = 实现了InvocationHandler接口的实现类的对象
/**
 *  动态代理的使用
 */
interface Subject{
    void action();
}

// 被代理类
class ReadSubject implements Subject{
    @Override
    public void action() {
        System.out.println("被代理类执行了 action 方法!!!");
    }
}


/**
 *  代理类
 *
 *  需要实现 InvocationHandler 并在其 invoke方法中调用对应方法
 *
 *  在运行时动态的创建与被代理类实现相同接口并包装被代理类的代理类
 *  (为什么要实现同一个接口? 为了能够调用被代理类的方法)
 */
class MyInvocationHandler implements InvocationHandler{
    Object obj; // 实现了接口的被代理类的对象的声明

    // 返回一个代理对象
    public Object blind(Object obj){
        // 给 被代理类 obj 对象实例化
        this.obj = obj;
        // 返回一个代理类对象; 三个参数:被代理类的类加载器、被代理类实现的接口、实现了 InvocationHandler的对象
        return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);
    }


    // 当通过代理类的对象发起对被重写的方法调用时,都会转换为对如下的invoke方法的调用
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("===代理开始===");
        // method方法的返回值 returnVal
        Object returnVal = method.invoke(obj,args);
        System.out.println("===代理结束===");
        return returnVal;
    }
}



public class DynamicProxy {

    public static void main(String[] args) {
        // 1、创建被代理对象
        ReadSubject real = new ReadSubject();
        // 2、创建一个实现了InvocationHandler接口的类的对象
        MyInvocationHandler handler = new MyInvocationHandler();
        // 3、调用blind()方法,动态的返回一个同样实现了real所在类实现的接口Subject的代理类的对象
        Subject sub = (Subject)handler.blind(real); // 此时的sub就是代理类的对象

        sub.action();// 代理对象执行此方法时,都会转到对InvocationHandler接口的实现类的invoke()方法调用

        // 对比,动态生成代理对象
        NikeClothFactory nike = new NikeClothFactory();
        // 生成 实现了 ClothFactory 接口的代理NikeClothFactory的代理对象
        ClothFactory clothProxy = (ClothFactory) handler.blind(nike);
        // 此时代理对象调用这个方法则会跳到 handler 中的invoke()方法中来调用被代理类的productCloth()方法
        clothProxy.productCloth();

    }

}

  代理设计模式总结:

  代理步骤:
    1、获取被代理类的实例
    2、在InvocationHandler接口的实现类中,包装被代理类,并通过反射的方式获取到被代理类的类加载器、实现接口
        来创建相应实现相同接口的代理类对象 Object proxy =
        Proxy.newInstance(被代理类类加载器,被代理类实现接口,InvocationHandler接口的自定义实现类的对象);
    3、通过代理类对象来调用 被代理类相应的方法;这时会跳转到 InvocationHandler接口的实现类中 的 invoke()方法中
        在invoke() 方法中再添加 相应方法调用的逻辑和 调用方法前后的操作;即AOP的表现;
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值