写在开头

《深入剖析Java中的反射,由浅入深,层层剥离!》这篇文章中我们讲反射时,曾提到过Java的动态代理中使用了反射技术,那么好,今天我们要就着反射的索引,来学习一下Java中的代理!

代理模式

在Java中有多达23种的设计模式(后面Java基础更新完后,会找个时间详细的去写写这些设计模式),恰当的设计模式的使用能够提升代码的效率,简化代码的复杂性。

而今天我们要说的代理模式就是其中之一,所谓代理是为某对象提供一种代理以控制对该对象的访问。即客户端通过代理间接地访问该对象,从而限制、增强或修改该对象的一些特性。

大白话:买房的(客户方),房产销售(代理方),卖房的(委托方)

在Java中有静态代理和动态代理两种实现方式,继续放下看!!!

静态代理

所谓静态代理,一般是针对编译期就已经完成了接口,实现类,代理类的定义,我们对目标对象的增强需要手工去完成,一个目标对象就要有个代理类,非常不灵活。

静态代理的实现步骤

1,因为代理类与被目标对象有相似的行为(共同),所以我们先创建一个接口。

public interface SaleHouse {
String saleHouse();
}

2,提供接口的实现类,当做目标对象

public class SaleHouseImpl implements SaleHouse{
@Override
public String saleHouse() {
return "我要卖房子啦!!!";
}
}

3,代理类同样也要实现接口,并在目标方法前后做一些控制操作

public class SaleHouseProxy implements SaleHouse{

    private SaleHouse saleHouse;
//提供一个包含目标对象的有参构造
public SaleHouseProxy(SaleHouse saleHouse) {
this.saleHouse = saleHouse;
} @Override
public String saleHouse() {
//调用方法前,我们可以加一些自己的控制
System.out.println("我要收代理费!!!");
System.out.println("--------------------");
String s = saleHouse.saleHouse();
System.out.println(s);
System.out.println("--------------------");
//调用方法后,我们依旧可以操作
System.out.println("我要拿提成!!!");
return "这就是静态代理";
}
}

4,客户端调用代理类,并传入目标对象

public class Test {
public static void main(String[] args) throws FileNotFoundException {
//客户端调用静态代理
SaleHouse saleHouse = new SaleHouseImpl();
SaleHouseProxy saleHouseProxy = new SaleHouseProxy(saleHouse);
saleHouseProxy.saleHouse();
}
}

5,控制台查看打印结果

我要收代理费!!!
--------------------
我要卖房子啦!!!
--------------------
我要拿提成!!!

动态代理

其实无论是静态代理还是静态代理,在我们的日常开发中,使用的都是很多,但对于SpringAop、RPC等框架来说,动态代理发挥着相当大的作用,动态代理具有:运行时控制,灵活性更好的特点。

那怎么实现动态代理呢?

如下三种方式:

JDK 动态代理
CGLib 动态代理
使用 Spring aop 模块完成动态代理功能 //今天先不说这个

JDK动态代理

实现步骤:

1,定义一个接口及其实现类;

代码同静态代理中步骤1,步骤2;

2,自定义 InvocationHandler (调用处理器)并重写invoke方法,在 invoke 方法中我们会调用原生方法(被代理类的方法)并自定义一些处理逻辑;

public class JdkDynamicInvocationHandler implements InvocationHandler {

    //代理类中的真实对象
private final Object target; public JdkDynamicInvocationHandler(Object target) {
this.target = target;
} @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;
}
}

3,通过 Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法创建代理对象;其实,这一步也可以写在第2步的代码里,不过为了代码的可读性,我们进行解耦实现!

3.1,定义一个工厂类,在工厂类中通过Proxy.newProxyInstance()方法获取某个类的代理对象

public class JdkDynamicProxyFactory {
public static Object getProxy(Object target) {
return Proxy.newProxyInstance(
target.getClass().getClassLoader(), // 目标类的类加载器
target.getClass().getInterfaces(), // 代理需要实现的接口,可指定多个
new JdkDynamicInvocationHandler(target) // 代理对象对应的自定义 InvocationHandler
);
}

3.2,客户端传入目标对象,实现代理扩展

  //客户端调用静态代理
SaleHouse proxySaleHouse = (SaleHouse) JdkDynamicProxyFactory.getProxy(new SaleHouseImpl());
proxySaleHouse.saleHouse();

4,控制台输出

我要收代理费!!!
我要卖房子啦!!!
我要拿提成!!!

【扩展】

关于Proxy类的静态工厂方法newProxyInstance()如何创建代理实例的过程,感兴趣的可以去读源码,或者参考下面这篇文章《代理模式在开源代码中的应用》

CGLIB 动态代理

其实在JDK动态代理中有一个弊端,那就是只能代理接口或接口的实现类,那么未实现任何接口的类就不能代理了吗?答案是否定的,因为咱们有CGLIB!

CGLIB (Code Generation Library) 是一个基于ASM的字节码生成库,它允许我们在运行时对字节码进行修改和动态生成,CGLIB 通过继承方式实现代理。

实现步骤:

1,引入cglib依赖

因为是第三方实现的动态代理,所以在使用前先引入依赖包

<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>

2,定义一个类;

public class Person {
public void eat(){
System.out.println("我在吃饭!!!");
}
}

3,自定义 MethodInterceptor 并重写 intercept 方法,intercept 用于拦截增强被代理类的方法,和 JDK 动态代理中的 invoke 方法类似;

public class CglibMethodInterceptor implements MethodInterceptor {

    /**
* @param o 被代理的对象(需要增强的对象)
* @param method 被拦截的方法(需要增强的方法)
* @param args 方法入参
* @param methodProxy 用于调用原始方法
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//调用方法前,我们可以加一些自己的控制
System.out.println("饭前先洗手");
Object object = methodProxy.invokeSuper(o, args);
//调用方法前,我们可以加一些自己的控制
System.out.println("饭后要擦嘴");
return object;
}
}

4,创建一个工厂类,用来构建代理对象,通过 Enhancer 类的 create()方法实现;

public class CglibProxyFactory {

    public static Object getProxy(Class<?> clazz) {
// 创建动态代理增强类
Enhancer enhancer = new Enhancer();
// 设置类加载器
enhancer.setClassLoader(clazz.getClassLoader());
// 设置被代理类
enhancer.setSuperclass(clazz);
// 设置方法拦截器
enhancer.setCallback(new CglibMethodInterceptor());
// 创建代理类
return enhancer.create();
}
}

5、客户端调用,通过反射传入Person类信息

public static void main(String[] args) throws FileNotFoundException {
//客户端调用静态代理
Person person = (Person) CglibProxyFactory.getProxy(Person.class);
person.eat();
}

6、输出

饭前先洗手
我在吃饭!!!
饭后要擦嘴

OK,终于码完了动态代理,自己还去看了很久的源码,头昏脑涨!

结尾彩蛋

如果本篇博客对您有一定的帮助,大家记得留言+点赞+收藏呀。原创不易,转载请联系Build哥!

由反射引出的Java动态代理与静态代理的更多相关文章

  1. Java代理模式/静态代理/动态代理

    代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...

  2. 【Java】代处理?代理模式 - 静态代理,动态代理

    >不用代理 有时候,我希望在一些方法前后都打印一些日志,于是有了如下代码. 这是一个处理float类型加法的方法,我想在调用它前打印一下参数,调用后打印下计算结果.(至于为什么不直接用+号运算, ...

  3. java中代理,静态代理,动态代理以及spring aop代理方式,实现原理统一汇总

    若代理类在程序运行前就已经存在,那么这种代理方式被成为 静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的. 通常情况下, 静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类. ...

  4. java 基础 --- 动态代理和静态代理

    问题  : 代理的应用场景是什么 动态代理的底层原理是什么,为什么只能继承接口 概述 代理模式是设计模式的一种,简单地说就是调用代理类的方法实际就是调用真实类的方法.这种模式在AOP (切面编程)中非 ...

  5. Java设计模式学习06——静态代理与动态代理(转)

    原地址:http://blog.csdn.net/xu__cg/article/details/52970885 一.代理模式 为某个对象提供一个代理,从而控制这个代理的访问.代理类和委托类具有共同的 ...

  6. Spring中AOP的两种代理方式(Java动态代理和CGLIB代理)

    第一种代理即Java的动态代理方式上一篇已经分析,在这里不再介绍,现在我们先来了解下GCLIB代理是什么?它又是怎样实现的?和Java动态代理有什么区别? cglib(Code Generation ...

  7. java运行原理、静态代理和动态代理区分

    1.java的编译和运行原理: ■ 编译:将源文件 .java 文件,通过编译器(javac 命令) 编译成 字节码文件 .class 文件. ■ 运行,通过类加载器(以二进制流形式)把字节码加载进J ...

  8. Java中的代理模式--静态代理和动态代理本质理解

    代理模式定义:为其他对象提供了一种代理以控制对这个对象的访问. 代理模式的三种角色: Subject抽象主题角色:抽象主题类可以是抽象类也可以是接口,是一个最普通的业务类型定义,无特殊要求. Real ...

  9. Java代理(静态代理、JDK动态代理、CGLIB动态代理)

    Java中代理有静态代理和动态代理.静态代理的代理关系在编译时就确定了,而动态代理的代理关系是在运行期确定的.静态代理实现简单,适合于代理类较少且确定的情况,而动态代理则给我们提供了更大的灵活性. J ...

  10. Java动态代理与静态代理以及它能为我们做什么

    相信我们在网上和平时学习和工作中或多或少都接触过Java的代理模式,经常听到什么静态代理.动态代理的一些名词.但我们是否真的很清楚这些呢?至少我在面试时,发现很多人并不很清楚. 首先代理比较好理解,就 ...

随机推荐

  1. 第五届蓝桥杯(2014)C/C++大学A组省赛题解

    第一题.猜年龄 小明带两个妹妹参加元宵灯会.别人问她们多大了,她们调皮地说:"我们俩的年龄之积是年龄之和的6倍".小明又补充说:"她们可不是双胞胎,年龄差肯定也不超过8岁 ...

  2. NSSCTF Round#13 web专项

    rank:3 flask?jwt? 简单的注册个账号,在/changePassword 下查看页面源代码发现密钥<!-- secretkey: th3f1askisfunny --> ,很 ...

  3. springboot 参数通过 @RequestBody传递的写法

    controller中有时候参数是使用 @RequestBody传递的这种怎么写? 直接上代码 @RequestMapping("/test") public void getAc ...

  4. linux驱动开发中copy_from_user open read write等常用函数总结

    目录 open read write copy_to_user copy_from_user open 函数定义: int open( const char * pathname, int flags ...

  5. WebGPU光追引擎基础课系列目录

    大家好~我开设了"WebGPU光追引擎基础课"的线上课程,从0开始,在课上带领大家现场写代码,使用WebGPU开发基础的光线追踪引擎 课程重点在于基于GPU并行计算,实现BVH构建 ...

  6. DOCKER本地仓库

    概述 随着docker的应用越来越多,安装部署越来越方便,批量自动化的镜像生成和发布都需要docker仓库的本地化应用. 试用了docker的本地仓库功能,简单易上手,记录下来以备后用. 环境 cen ...

  7. wxpython窗体之间传递参数

    如何界面存在frame1与frame2,通过frame1打开页面frame2,并将frame2的值传递给frame1 可以使用回调函数传值参考具体代码如下: # -*- coding: utf-8 - ...

  8. python测试小工具

    2022年第三天,祝福大家元旦快乐,此测试工具在原基础上改进,增加一些新的功能,并去除了复杂的层级链接,架构目前比较简单,全部只使用了一个GridBagSizer布局器完成. 一.背景:主要解决软件测 ...

  9. Linux 安装 mysql 及配置存储位置

    本文为博主原创,未经允许不得转载: 新申请的服务器,需要确认服务器的磁盘是否进行了挂载,可参考这篇文章:https://www.cnblogs.com/zjdxr-up/p/14873242.html ...

  10. 【MicroPython】生成QSTR表 - py\makeqstrdata.py

    转义非字母数字的字符,转义结果为预定义字符串codepoint2name[] def qstr_escape(qst): def esc_char(m): c = ord(m.group(0)) tr ...