java核心技术——代理(Proxy)附源码

24 篇文章 0 订阅
1 篇文章 0 订阅

一、了解代理

代理(Proxy) 是一种设计模式,提供了对目标对象另外的访问方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能.
这里使用到编程中的一个思想:不要随意去修改别人已经写好的代码或者方法,如果需改修改,可以通过代理的方式来扩展该方法
在这里插入图片描述
java中的代理模式:

定义:给目标对象提供一个代理对象,并且由代理对象控制对目标对象的引用
目的:

  • ①:通过代理对象的方式间接的访问目标对象,防止直接访问目标对象给系统带来不必要的复杂性
  • ②:通过代理业务对原有业务进行增强

java当中有三种方式来创建代理对象:静态代理,基于jdk(接口)的动态代理,基于CGLLIB(父类)的动态代理
下面逐步讲解

二、代理讲解

1. 静态代理

细节:我们的真实对象必须实现我们的接口,同时代理对象也必须实现这一接口
在这里插入图片描述

接口类TargetInteface接口
内部定义了三个方法

package com.step.aopdemo.staticproxy;

public interface TargetInteface {
    void method1();
    void method2();
    int method3(Integer i);
}

接口实现目标类Target

package com.step.aopdemo.staticproxy;

import com.step.aopdemo.jdkproxy.TargetInteface;

public class Target implements TargetInteface {
    @Override
    public void method1() {
        System.out.println("method1 running ...");
    }

    @Override
    public void method2() {
        System.out.println("method2 running ...");
    }

    @Override
    public int method3(Integer i) {
        System.out.println("method3 running ...");
        return i;
    }
}

接口实现代理类TargetProxy

package com.step.aopdemo.staticproxy;

public class TargetProxy implements TargetInteface {

    @Override
    public void method1() {
        System.out.println("执行方法前...");
        new Target().method1();
        System.out.println("执行方法后...");
    }

    @Override
    public void method2() {
        System.out.println("执行方法前...");
        new Target().method2();
        System.out.println("执行方法后...");
    }

    @Override
    public int method3(Integer i) {
        System.out.println("执行方法前...");
        int method3 = new Target().method3(i);
        System.out.println("执行方法后...");
        return method3;
    }
}

测试类

package com.step.aopdemo.staticproxy;

public class TargetUser {

    public static void main(String[] args) {
        TargetInteface target = new TargetProxy();
        target.method1();
        System.out.println("-----------------------------");
        target.method2();
        System.out.println("-----------------------------");
        System.out.println(target.method3(3));
    }
}

执行结果
在这里插入图片描述
总结:
静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。
静态代理存在哪些问题?
不足:静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。

静态代理违反了开闭原则:

程序对外扩展开放,对修改关闭,换句话来说,当需求发生变化时,我们可以增加新模块来解决新需求,而不是通过该变原来的代码来解决我们的新需求

2.JDK动态代理

为解决静态代理对象必须实现接口的所有方法的问题,Java给出了动态代理,动态代理具有如下特点:

  1. Proxy对象不需要implements接口;
  2. Proxy对象的生成利用JDK的Api,在JVM内存中动态的构建Proxy对象。需要使用java.lang.reflect.Proxy类的

首先我们看一下java.lang.reflect.Proxy类的源码,先了解原理
源码703行开始讲解了 static Object newProxyInstance方法
在这里插入图片描述

2.1 这里需要讲解一下红框内的参数

  1. 来看源码ClassLoader loader 他是一个抽象类
    在这里插入图片描述
    ClassLoader loader:指定当前target对象使用类加载器,获取加载器的方法是固定的;
  2. Class<?>[] interfaces源码介绍
    在这里插入图片描述
    Class<?>[] interfaces:target对象实现的接口的类型,使用泛型方式确认类型
  3. InvocationHandler h介绍——(interface)接口
    在这里插入图片描述
    InvocationHandler h:事件处理,执行target对象的方法时,会触发事件处理器的方法,会把当前执行target对象的方法作为参数传入。

2.2 代码

接口和静态代理接口TargetInteface一毛一样,不再赘述
目标类和静态目标类Target一致,同上
看点不一样的

package com.step.aopdemo.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
 * 
 * @author step
 * @date 2021/8/14 15:53
 */
public class TargetProxy {
    public static  <T> Object getTarget(T t) {
        return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
     @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
          // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
          //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
          System.out.println("jdk执行方法前...");
          Object invoke = method.invoke(t, args);
          System.out.println("jdk执行方法后...");
          return invoke;
            }
        });
    }
}

单纯就这个书写的来说我又学到一个新的书写模式,这里做个小笔记用另外一种lambda 表达式传参

package com.step.aopdemo.jdkproxy;

import java.lang.reflect.Proxy;

public class TargetProxy {
    public static  <T> Object getTarget(T t) {
        return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(),(proxy,method,args)->{
                // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
                //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
                System.out.println("jdk执行方法前...");
                Object invoke = method.invoke(t, args);
                System.out.println("jdk执行方法后...");
                return invoke;

        });
    }
}

使用了method.invoke(t, args);方法,查看源码
在这里插入图片描述
上节我们讲过反射的原理java核心技术——反射,方便解释直接截图
在这里插入图片描述

上述两个方法二选一即可
测试类

package com.step.aopdemo.jdkproxy;

public class TargetUser {

    public static void main(String[] args) {
        TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
        target.method1();
        System.out.println("-----------------------------");
        target.method2();
        System.out.println("-----------------------------");
        System.out.println(target.method3(3));
    }

}

执行结果
在这里插入图片描述

3. Cglib代理

JDK动态代理要求target对象是一个接口的实现对象,假如target对象只是一个单独的对象,并没有实现任何接口,这时候就会用到Cglib代理(Code Generation Library),即通过构建一个子类对象,从而实现对target对象的代理,因此目标对象不能是final类(报错),且目标对象的方法不能是final或static(不执行代理功能)。
Cglib依赖的jar包

   <!-- https://mvnrepository.com/artifact/cglib/cglib -->
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>2.2.2</version>
        </dependency>

3.1代码

目标类Target,(不用实现接口)

package com.step.aopdemo.cglibproxy;

/**
 * 
 * @author zxj
 * @date 2021/8/14 16:15
 */
public class Target {
    public void method1() {
        System.out.println("method1 running ...");
    }

    public void method2() {
        System.out.println("method2 running ...");
    }

    public int method3(Integer i) {
        System.out.println("method3 running ...");
        return i;
    }
}

代理类TargetProxy

package com.step.aopdemo.cglibproxy;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class TargetProxy {

    static <T> Object getProxy(T t) {
        Enhancer en = new Enhancer(); //帮我们生成代理对象
        en.setSuperclass(t.getClass());//设置要代理的目标类
        en.setCallback(new MethodInterceptor() {//代理要做什么
            @Override
            public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                System.out.println("Cglib执行方法前。。。");
                //调用原有方法
                Object invoke = methodProxy.invokeSuper(object, args);
                // Object invoke = method.invoke(t, args);// 作用等同与上面。
                System.out.println("Cglib执行方法后。。。");
                return invoke;
            }
        });
        return en.create();
    }
}

测试类

package com.step.aopdemo.cglibproxy;


public class TargetUser {

    public static void main(String[] args) {
        Target target = (Target) TargetProxy.getProxy(new Target());
        target.method1();
    }

}

运行结果
在这里插入图片描述

4. 总结:

理解上述Java代理后,也就明白Spring AOP的代理实现模式,即加入Spring中的target是接口的实现时,就使用JDK动态代理,否是就使用Cglib代理。Spring也可以通过<aop:config proxy-target-class="true">强制使用Cglib代理,使用Java字节码编辑类库ASM操作字节码来实现,直接以二进制形式动态地生成 stub 类或其他代理类,性能比JDK更强。

  • 2
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值