# 代理模式(Proxy)
代理模式主要用来控制对其他对象的访问,代理通常充当中间人的身份。一般可以分为如下4类:
1. 远程代理(Remote Proxy):控制对远程对象的访问,负责将请求及其参数编码,并向不同地址空间中的对象发送已经编码的请求。
2. 虚拟代理(Virtual Proxy)
3. 保护代理
4. 智能代理
java中的代理模式:
1. 静态代理
Java的静态代理是基于接口的代理模式,可以用于在不修改原来对象的代码的基础上,扩充对象的功能。例如:
~~~
package net.smrobot.proxy;
/**
* 静态代理
*/
public class SimpleProxyDemo {
/**
* 传递一个接口参数
* @param iface
*/
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("zhangsan");
}
public static void main(String[] args) {
System.out.println("没有使用代理模式....");
consumer(new RealObject());
System.out.println("使用了代理模式...");
consumer(new SimpleProxy(new RealObject()));
}
}
interface Interface {
void doSomething();
void somethingElse(String arg);
}
class RealObject implements Interface {
@Override
public void doSomething() {
System.out.println("Real Object:doSomething!");
}
@Override
public void somethingElse(String arg) {
System.out.println("Real Object:somethingElse=" + arg);
}
}
/**
* 创建静态代理类
*/
class SimpleProxy implements Interface {
private Interface proxied;
public SimpleProxy(Interface proxied) {
this.proxied = proxied;
}
@Override
public void doSomething() {
System.out.println("Proxy Object:doSomething!");
proxied.doSomething();
}
@Override
public void somethingElse(String arg) {
System.out.println("Proxy Object:somethingElse!");
proxied.somethingElse(arg);
}
}
~~~
2. 动态代理
Java提供的动态代理可以动态的创建代理对象,对所代理的方法进行调用,在动态代理类上的所有调用都会被重定向到一个单一的调用处理器(Handler)上。因此使用Java的动态代理技术需要如下元素:
1. 要代理的对象是**基于接口**实现的。
2. 创建调用处理器,并实现InvocationHandler接口中的invoke方法。
3. 通过Proxy.newProxyInstance()来动态的创建一个代理对象。调用该代理对象的方法,就会将方法的调用重定向到处理器的invoke方法中。
例如:
~~~
package net.smrobot.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 动态代理
*/
public class DynamicProxyDemo {
public static void consumer(Interface iface) {
iface.doSomething();
iface.somethingElse("zhangsan");
}
public static void main(String[] args) {
RealObject realObject = new RealObject();
consumer(realObject);
System.out.println("使用动态代理并再次调用...");
Interface proxyInstance = (Interface)Proxy.newProxyInstance(Interface.class.getClassLoader(),
new Class[]{Interface.class}, new DynamicProxyHandler(realObject));
consumer(proxyInstance);
}
}
class DynamicProxyHandler implements InvocationHandler {
/**
* 要代理的对象
*/
private Object proxied;
public DynamicProxyHandler(Object proxied) {
this.proxied = proxied;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("\n");
System.out.println("proxy:" + proxy.getClass() + "\nmethod:" + method + "\nargs:" + args);
if (args != null) {
System.out.println("方法的参数为....");
for (Object arg : args) {
System.out.println(" " + arg);
}
}
return method.invoke(proxied, args);
}
}
~~~
JDK的动态代理是基于反射技术实现的,还有基于字节码技术实现ASM或者cglib。
**Cglib的动态代理技术**
JDK的动态代理只能代理基于接口的实现类,否则的话会抛出“ClassCastException异常”。而Cglib的动态代理技术可以实现基于类的动态代理,其主要生成了一个目标类的子类对象来覆盖掉目标类方法,因此如果目标类是用final修饰,则不可以使用Cglib技术。
1. 引入依赖
~~~
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.9</version>
</dependency>
~~~
2. 实现
~~~
package net.smrobot.proxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Cglib的动态代理模式
*/
public class CglibProxyDemo {
public static void main(String[] args) {
// CGLIB dynamic proxy call
CglibProxy proxy = new CglibProxy();
Panda panda = (Panda)proxy.getInstance(new Panda());
System.out.println(panda);
panda.eat();
}
}
class Panda {
public void eat() {
System.out.println("The panda is eating...");
}
}
class CglibProxy implements MethodInterceptor {
// 要代理的目标对象
private Object target;
public Object getInstance(Object target) {
this.target = target;
Enhancer enhancer = new Enhancer();
// 设置代理对象的父类对象,就是要代理的目标对象
enhancer.setSuperclass(this.target.getClass());
// 设置回调函数
enhancer.setCallback(this);
// 创建一个代理对象
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Before calling");
// 执行目标对象的代理方法
Object result = methodProxy.invokeSuper(o, objects);
System.out.println("After calling");
return result;
}
}
~~~
主要组成:
1. 要被代理的目标对象。
2. 实现了MethodInterceptor接口的代理对象,并重写intercept方法。
JDK动态代理和Cglib的动态代理对比:
1. JDK动态代理更可靠,同时JDK的版本升级的比较快,字节码包(cglib)也要随着更新。
2. Cglib有者更高的性能,同时可以用在非接口对象中。
动态代理的应用场景:包装RPC、面向切面编程(AOP)。
【参考】
1. 《Java编程思想》动态代理。
2. https://www.fatalerrors.org/a/reflection-and-dynamic-proxy-jdk-proxy-and-cglib.html
- 第一章 Java基础
- ThreadLocal
- Java异常体系
- Java集合框架
- List接口及其实现类
- Queue接口及其实现类
- Set接口及其实现类
- Map接口及其实现类
- JDK1.8新特性
- Lambda表达式
- 常用函数式接口
- stream流
- 面试
- 第二章 Java虚拟机
- 第一节、运行时数据区
- 第二节、垃圾回收
- 第三节、类加载机制
- 第四节、类文件与字节码指令
- 第五节、语法糖
- 第六节、运行期优化
- 面试常见问题
- 第三章 并发编程
- 第一节、Java中的线程
- 第二节、Java中的锁
- 第三节、线程池
- 第四节、并发工具类
- AQS
- 第四章 网络编程
- WebSocket协议
- Netty
- Netty入门
- Netty-自定义协议
- 面试题
- IO
- 网络IO模型
- 第五章 操作系统
- IO
- 文件系统的相关概念
- Java几种文件读写方式性能对比
- Socket
- 内存管理
- 进程、线程、协程
- IO模型的演化过程
- 第六章 计算机网络
- 第七章 消息队列
- RabbitMQ
- 第八章 开发框架
- Spring
- Spring事务
- Spring MVC
- Spring Boot
- Mybatis
- Mybatis-Plus
- Shiro
- 第九章 数据库
- Mysql
- Mysql中的索引
- Mysql中的锁
- 面试常见问题
- Mysql中的日志
- InnoDB存储引擎
- 事务
- Redis
- redis的数据类型
- redis数据结构
- Redis主从复制
- 哨兵模式
- 面试题
- Spring Boot整合Lettuce+Redisson实现布隆过滤器
- 集群
- Redis网络IO模型
- 第十章 设计模式
- 设计模式-七大原则
- 设计模式-单例模式
- 设计模式-备忘录模式
- 设计模式-原型模式
- 设计模式-责任链模式
- 设计模式-过滤模式
- 设计模式-观察者模式
- 设计模式-工厂方法模式
- 设计模式-抽象工厂模式
- 设计模式-代理模式
- 第十一章 后端开发常用工具、库
- Docker
- Docker安装Mysql
- 第十二章 中间件
- ZooKeeper