Java 设计模式系列(十二)代理模式
设计模式之美 - 代理模式
设计模式之美目录:https://www.cnblogs.com/binarylei/p/8999236.html
代理模式:给某一个对象提供一个代理对象,并由代理对象控制对原对象的引用。GoF 的《设计模式》一书中把 RPC 称作远程代理。其它应用场景如缓存、监控、统计、鉴权、限流、事务、幂等、日志等。
package com.github.binarylei.design.proxy;
public interface UserService {
public void say();
}
public class UserServiceImpl implements UserService {
@Override
public void say() {
System.out.println("Hello World!");
}
}
1. 静态代理
public void test() {
UserServiceImpl obj = new UserServiceImpl();
UserService userService = new UserService() {
@Override
public void say() {
System.out.println("这是静态代理");
obj.say();
}
};
userService.say();
}
很明显静态代理每个被代理的类都要手写一个代理类,当修改被代理的类时也要修改对应的代理类。解决这个问题则是由程序来生成对应的代理类,这就是动态代理。
2. 动态代理
2.1 JDK 动态代理
public void test1() throws Exception {
UserServiceImpl obj = new UserServiceImpl();
UserService userService = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
if (method.getDeclaringClass() == Object.class) {
return method.invoke(obj, args);
} else {
System.out.println(proxy.getClass().getName());
System.out.println(method);
Object ret = method.invoke(obj, args);
return ret;
}
}
});
userService.say();
System.out.println(userService);
}
注意:JDK 中所要进行动态代理的类必须要实现一个接口 ,也就是说只能对该类所实现接口中定义的方法进行代理,这在实际编程中具有一定的局限性,而且使用反射的效率也并不是很高。
2.2 CGLib 动态代理
使用 CGLib 实现动态代理,完全不受代理类必须实现接口的限制,而且 CGLib 底层采用 ASM 字节码生成框架,使用字节码技术生成代理类,比使用 Java 反射效率要高。唯一需要注意的是,CGLib 不能对声明为 final 的方法进行代理,因为 CGLib 原理是动态生成被代理类的子类。
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.8</version>
</dependency>
public void test2() {
Enhancer enhancer = new Enhancer();
//1. 设置父类
enhancer.setSuperclass(UserServiceImpl.class);
//2. 设置回调函数
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println(method + " proxy");
Object ret = proxy.invokeSuper(obj, args);
return null;
}
});
//3. 获取代理对象
UserService userService = (UserService) enhancer.create();
userService.say();
}
参数:Object 为由 CGLib 动态生成的代理类实例,Method 为上文中实体类所调用的被代理的方法引用,Object[] 为参数值列表,MethodProxy 为生成的代理类对方法的代理引用。proxy.invokeSuper(obj, arg) 从代理实例的方法调用返回的值。
3. 动态代理原理
3.1 原理
JDK 的动态代理实际上是在内存中生成了一个字节码类,并进行编译,加载。JVM 生成的类名称都是以 $ 开头,eg: $Proxy0
public void test1() throws Exception {
UserServiceImpl obj = new UserServiceImpl();
UserService userService = (UserService) Proxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().getName()); // $Proxy0
Object ret = method.invoke(obj, args);
return ret;
}
});
userService.say();
System.out.println(userService);
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", obj.getClass().getInterfaces());
FileOutputStream out = new FileOutputStream(
this.getClass().getResource("").getPath() + "$Proxy0.class");
out.write(bytes);
}
查看生成的 $Proxy0.class 类如下:
import com.github.binarylei.design.proxy.UserService;
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 UserService {
private static Method m3;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final void say() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m3 = Class.forName("com.github.binarylei.design.proxy.UserService").getMethod("say", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
// ...
}
3.2 手写动态代理
(1) 定义 MyInvocationHandler 接口
@FunctionalInterface
public interface MyInvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
(2) 实现自己的 MyProxy 类
package com.github.binarylei.design.proxy.my;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* @author: leigang
* @version: 2018-10-02
*/
public class MyProxy {
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, MyInvocationHandler h) {
FileWriter out = null;
try {
// 1. 动态生成源代码 .java 文件
String src = generateSrc(interfaces);
// 2. .java 文件生成到磁盘
File file = new File(MyProxy.class.getResource("").getPath() + "$Proxy1.java");
out = new FileWriter(file);
out.write(src);
out.flush();
out.close();
// 3. 把 .java 文件编译成 .class 文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(
null, null, null);
Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(file);
JavaCompiler.CompilationTask task = compiler.getTask(
null, manager, null, null, null, iterable);
task.call();
manager.close();
// 4. 编译生成的 .class 类到 JVM 中
Class<?> clazz = Class.forName("com.github.binarylei.design.proxy.my.$Proxy1");
// 5. 返回字节码重组以后新代理对象
Constructor<?> constructor = clazz.getConstructor(MyInvocationHandler.class);
return constructor.newInstance(h);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private static final String ln = "\r\n";
private static String generateSrc(Class<?>[] interfaces) {
StringBuilder sb = new StringBuilder();
sb.append("package com.github.binarylei.design.proxy.my;").append(ln);
sb.append("import com.github.binarylei.design.proxy.UserService;").append(ln);
sb.append("import java.lang.reflect.Method;").append(ln);
sb.append("public final class $Proxy1 implements ").append(interfaces[0].getSimpleName()).append(" {").append(ln);
sb.append("private static MyInvocationHandler h;").append(ln);
sb.append("public $Proxy1(MyInvocationHandler h) throws Exception {").append(ln);
sb.append("this.h = h;").append(ln);
sb.append("}").append(ln);
for (Class<?> clazz : interfaces) {
Method[] methods = clazz.getMethods();
for (Method m : methods) {
sb.append("public final void say() {").append(ln);
sb.append("try {").append(ln);
sb.append("Method m = Class.forName(\"").append(clazz.getName()).append("\").getMethod(\"")
.append(m.getName()).append("\", new Class[0]);").append(ln);
sb.append("h.invoke(this, m, (Object[]) null);").append(ln);
sb.append("} catch (Throwable e) {").append(ln);
sb.append("e.printStackTrace();").append(ln);
sb.append("}").append(ln);
sb.append("}").append(ln);
}
}
sb.append("}").append(ln);
return sb.toString();
}
}
(3) 测试
public void test3() {
UserServiceImpl obj = new UserServiceImpl();
UserService userService = (UserService) MyProxy.newProxyInstance(
UserServiceImpl.class.getClassLoader(), UserServiceImpl.class.getInterfaces(),
new MyInvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(proxy.getClass().getName());
System.out.println(method);
Object ret = method.invoke(obj, args);
return ret;
}
});
userService.say();
System.out.println(userService);
}
参考:
- 实战CGLib系列文章 MethodInterceptor和Enhancer_CGLib:https://yq.aliyun.com/ziliao/296216
每天用心记录一点点。内容也许不重要,但习惯很重要!
Java 设计模式系列(十二)代理模式的更多相关文章
- Java设计模式系列之动态代理模式(转载)
代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 代理模式示例代码 public interface Sub ...
- C#设计模式之十二代理模式(Proxy Pattern)【结构型】
一.引言 今天我们要讲[结构型]设计模式的第七个模式,也是“结构型”设计模式中的最后一个模式,该模式是[代理模式],英文名称是:Proxy Pattern.还是老套路,先从名字上来看看.“代理”可以理 ...
- Java设计模式之十二 ---- 备忘录模式和状态模式
前言 在上一篇中我们学习了行为型模式的策略模式(Strategy Pattern)和模板模式(Template Pattern).本篇则来学习下行为型模式的两个模式,备忘录模式(Memento Pat ...
- Java 设计模式系列(二十)状态模式
Java 设计模式系列(二十)状态模式 状态模式,又称状态对象模式(Pattern of Objects for States),状态模式是对象的行为模式.状态模式允许一个对象在其内部状态改变的时候改 ...
- Java设计模式(十二) 策略模式
原创文章,同步发自作者个人博客,http://www.jasongj.com/design_pattern/strategy/ 策略模式介绍 策略模式定义 策略模式(Strategy Pattern) ...
- Java 设计模式系列(二二)责任链模式
Java 设计模式系列(二二)责任链模式 责任链模式是一种对象的行为模式.在责任链模式里,很多对象由每一个对象对其下家的引用而连接起来形成一条链.请求在这个链上传递,直到链上的某一个对象决定处理此请求 ...
- Java设计模式(10)代理模式(Proxy模式)
理解并使用设计模式,能够培养我们良好的面向对象编程习惯,同时在实际应用中,可以如鱼得水,享受游刃有余的乐趣. Proxy是比较有用途的一种模式,而且变种较多,应用场合覆盖从小结构到整个系统的大结构,P ...
- Java设计模式菜鸟系列(十四)代理模式建模与实现
转载请注明出处:http://blog.csdn.net/lhy_ycu/article/details/39856435 代理模式(Proxy):代理模式事实上就是多一个代理类出来,替原对象进行一些 ...
- Java 设计模式系列(二)简单工厂模式和工厂方法模式
Java 设计模式系列(二)简单工厂模式和工厂方法模式 实现了创建者和调用者的分离.分为:简单工厂模式.工厂方法模式.抽象工厂模式 简单工厂模式.工厂方法模式都很简单,就不详细介绍了. 一.简单工厂 ...
- Java设计模式---(动态)代理模式
代理设计模式 定义:为其他对象提供一种代理以控制对这个对象的访问. 动态代理使用 java动态代理机制以巧妙的方式实现了代理模式的设计理念. 之前虽然会用JDK的动态代理,但是有些问题却一直没有搞明白 ...
随机推荐
- 使用caddy 进行nodejs web应用近实时编译更新
caddy 相比nginx 是一个不错的轻量代理服务器,支持的功能也是比较多的, 同时插件也挺多 demo 测试的是通过git 插件进行一个使用spec-md 编写的文档近实时编译以及预览 项目使用d ...
- wamp 配置多站点访问
1:在f:\wamp\bin\apache\apache2.2.21\conf目录下打开 httpd.conf 查找到 #include conf/extra/httpd-vhosts.conf 把前 ...
- WPF学习基础
1. d:DesignHeight="300" d:DesignWidth="200": 分别指的是在vs设计界面的宽高,Width="500&quo ...
- 使用Spring发送Email
配置Spring发送邮件 Spring发送邮件底层还是使用JavaMail,我在http://www.cnblogs.com/lz2017/p/6882925.html 中记录过关于JavaMail的 ...
- 使用Java配置SpringMVC
在此之前,一直使用的是XML的方式配置SpringMVC,现在为了适应Servlert3.0以及JavaConfig的Spring配置方式,在这里记录一下使用Java代码配置SpringMVC.首先, ...
- Android ListView根据项数的大小自动改变高度
第一种:按照listview的项数确定高度 ListAdapter listAdapter = listView.getAdapter(); if (listAdapter == null) ...
- oracle link的创建过程
下面做一个测试,在测试中,创建数据库链接的库为XJ(WINDOWS 2003 ORACLE 10g 10.2.0.1),被链接的库为DMDB(LINUX AS5 ORACLE 10g 10.2.0.1 ...
- http协议再复习(二)
HTTP和HTTPS HTTP协议(HyperText Transfer Protocol,超文本传输协议):是一种发布和接收 HTML页面的方法. HTTPS(Hypertext Transfer ...
- 一次使用 Redis 优化查询性能的实践
因为我的个人网站 restran.net 已经启用,博客园的内容已经不再更新.请访问我的个人网站获取这篇文章的最新内容,一次使用 Redis 优化查询性能的实践 应用背景 有一个应用需要上传一组ID到 ...
- 迷你MVVM框架 avalonjs 学习教程2、模块化、ViewModel、作用域
一个项目是由许多人分工写的,因此必须要合理地拆散,于是有了模块化.体现在工作上,PM通常它这为某某版块,某某频道,某某页面.某一个模块,必须是包含其固有的数据,样式,HTML与处理逻辑.在jQuery ...