说说Java代理模式
代理实现可以分为静态代理和动态代理。
静态代理
静态代理模式其实很常见,比如买火车票这件小事:黄牛相当于是火车站的代理,我们可以通过黄牛买票,但只能去火车站进行改签和退票。在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能,并添加一些额外的业务处理,同时可以屏蔽realSubject中未开放的接口。
1、RealSubject 是委托类,Proxy 是代理类;
2、Subject 是委托类和代理类的接口;
3、request() 是委托类和代理类的共同方法;
具体代码实现如下:
interface Subject {
void request();
}
class RealSubject implements Subject {
public void request(){
System.out.println("RealSubject");
}
}
class Proxy implements Subject {
private Subject subject;
public Proxy(Subject subject){
this.subject = subject;
}
public void request(){
System.out.println("begin");
subject.request();
System.out.println("end");
}
}
public class ProxyTest {
public static void main(String args[]) {
RealSubject subject = new RealSubject();
Proxy p = new Proxy(subject);
p.request();
}
}
静态代理实现中,一个委托类对应一个代理类,代理类在编译期间就已经确定。
动态代理
动态代理中,代理类并不是在Java代码中实现,而是在运行时期生成,相比静态代理,动态代理可以很方便的对委托类的方法进行统一处理,如添加方法调用次数、添加日志功能等等,动态代理分为jdk动态代理和cglib动态代理,下面通过一个例子看看如何实现jdk动态代理。
1、定义业务逻辑
public interface Service {
//目标方法
public abstract void add();
}
public class UserServiceImpl implements Service {
public void add() {
System.out.println("This is add service");
}
}
2、利用java.lang.reflect.Proxy
类和java.lang.reflect.InvocationHandler
接口定义代理类的实现。
class MyInvocatioHandler implements InvocationHandler {
private Object target;
public MyInvocatioHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("-----before-----");
Object result = method.invoke(target, args);
System.out.println("-----end-----");
return result;
}
// 生成代理对象
public Object getProxy() {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class<?>[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
}
3、使用动态代理
public class ProxyTest {
public static void main(String[] args) {
Service service = new UserServiceImpl();
MyInvocatioHandler handler = new MyInvocatioHandler(service);
Service serviceProxy = (Service)handler.getProxy();
serviceProxy.add();
}
}
执行结果:
-----before-----
This is add service
-----end-----
代理对象的生成过程由Proxy类的newProxyInstance方法实现,分为3个步骤:
1、ProxyGenerator.generateProxyClass
方法负责生成代理类的字节码,生成逻辑比较复杂,有兴趣的同学可以继续分析源码 sun.misc.ProxyGenerator;
// proxyName:格式如 "com.sun.proxy.$Proxy.1";
// interfaces:代理类需要实现的接口数组;
// accessFlags:代理类的访问标识;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
2、native方法Proxy.defineClass0
负责字节码加载的实现,并返回对应的Class对象。
Class clazz = defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
3、利用clazz.newInstance
反射机制生成代理类的对象;
反编译代理类
为了更清楚的理解动态代理,通过以下方式把代理类字节码生成class文件。
byte[] classFile = ProxyGenerator.generateProxyClass("com.sun.proxy.$Proxy.1", service.getClass().getInterfaces());
FileOutputStream out = new FileOutputStream("com.sun.proxy.$Proxy.1.class");
out.write(classFile);
out.flush();
使用 反编译工具 jad jad com.sun.proxy.$Proxy.1
看看代理类如何实现,反编译出来的java代码如下:
public final class $proxy1 extends Proxy implements Service {
public $proxy1(InvocationHandler invocationhandler) {
super(invocationhandler);
}
public final boolean equals(Object obj) {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString() {
try {
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final void add() {
try {
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode() {
try {
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception) {
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception) {
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
从上述代码可以发现:
1、生成的$proxy1继承自Proxy类,并实现了Service接口。
2、执行代理对象的方法,其实就是执行InvocationHandle对象的invoke方法,传入的参数分别是当前代理对象,当前执行的方法和参数。
super.h.invoke(this, m3, null);
jdk动态代理使用的局限性
通过反射类Proxy
和InvocationHandler
回调接口实现的jdk动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。
原文链接:http://www.jianshu.com/p/a1d094fc6c00
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
说说Java代理模式的更多相关文章
- Java代理模式
java代理模式及动态代理类 1. 代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目 ...
- Java代理模式示例程序
Java代理模式示例程序 当然不是我想出来的,是我看的一个网上教程里的. 模拟的是一个对电脑公司的代理 真实类的接口: public interface SaleComputer { public S ...
- java 代理模式 总结
1.前言 最近舍友去面试遇到了关于java代理模式的问题. 我虽然知道怎么使用,但是没有做过正经的总结,因此有了这篇随笔,好好总结一下三大代理模式底层原理. 事实上,在开发项目的时候,基本用不上代理, ...
- 浅谈java代理模式
讲解java代理模式 目录 讲解java代理模式 何谓代理模式 静态代理 动态代理 JDK动态代理 CGLIB动态代理 何谓代理模式 代理模式,即Proxy Pattern,23种java常用设计模式 ...
- Java代理模式/静态代理/动态代理
代理模式:即Proxy Pattern,常用的设计模式之一.代理模式的主要作用是为其他对象提供一种代理以控制对这个对象的访问. 代理概念 :为某个对象提供一个代理,以控制对这个对象的访问. 代理类和委 ...
- JAVA代理模式与动态代理模式
1.代理模式 所谓代理,就是一个人或者一个机构代表另一个人或者另一个机构采取行动.在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之前起到中介的作用.代理模式给某 ...
- java 代理模式一: 静态代理
代理模式: 代理模式的作用:为其他对象提供一种代理以控制对 特定对象 的访问. 某种情况下,一个客户不想或者直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用:通过代理对象引用. ...
- 18 java 代理模式 (转)
静态代理 1.新建一个接口,这个接口所提供的方法是关于数据库操作的 public interface EmployeeDao { public void updateSalary(); } 2.建一个 ...
- JAVA 代理模式(Proxy)
1.代理模式 代理模式的作用是:为其他对象提供一种代理以控制对这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 代理模式一般涉 ...
- Java代理模式——静态代理模式
一:代理模式 代理模式的作用是:为其他对象提供一种代理以控制这个对象的访问.在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用. 二:代理模式设计 ...
随机推荐
- Red Hat Enterprise Linux7 配置Tomcat
笔者是Java前端的一个萌新,电脑刚刚经历了一番脱胎换骨,然后重新装了Win10Pro,所有的开发工具都要重新安装,纠结了一番以后决定还是把一些开发工具从Windows上转移到Linux上,首先考虑了 ...
- HTTP架构介绍(2) 缓存
web缓存是自动复制所请求数据并将其保存在本地存储中的设备. 通过这样做, 可以实现: 减少网络流量 消除网络瓶颈 防止服务器超载 减少长距离的响应延迟 因此, 您可以清楚地说, web 缓存可提高用 ...
- js备战春招の四のjs函数
1.普通函数声明: 2.函数表达式:函数表达式可以存储在变量中,在函数表达式存储在变量后,变量也可作为一个函数使用: 以上函数实际上是一个 匿名函数 (函数没有名称).函数存储在变量中,不需要函数名称 ...
- 九度oj题目1002:Grading
//不是说C语言就是C++的子集么,为毛printf在九度OJ上不能通过编译,abs还不支持参数为整型的abs()重载 //C++比较正确的做法是#include<cmath.h>,cou ...
- python爬微信公众号前10篇历史文章(3)-lxml&xpath初探
理解lxml以及xpath 什么是lxml? python中用来处理XML和HTML的library.与其他相比,它能提供很好的性能, 并且它支持XPath. 具体可以查看官方文档->http: ...
- PHP的日志记录-错误与异常记录
PHP的日志记录-错误与异常记录 提到 Nginx + PHP 服务的错误日志,我们通常能想到的有 Nginx 的 access 日志.error 日志以及 PHP 的 error 日志.虽然看起来是 ...
- 使用jitpack来获取github上的开源项目
在开发中我们需要经常使用第三方依赖库,在构建工具Gradle或maven中声明依赖, 大部分使用的是maven中心仓库或者阿里云仓库等等,但是这样也存在一个问题,上述仓库的库虽然简单快捷好用,但并不是 ...
- [css 揭秘]:CSS编码技巧
CSS编码技巧 我的github地址:https://github.com/FannieGirl/ifannie 喜欢的给我一个星吧 尽量减少代码重复 尽量减少改动时需要编辑的地方 当某些值相互依赖时 ...
- Git忽略规则.gitignore梳理
对于经常使用Git的朋友来说,.gitignore配置一定不会陌生.废话不说多了,接下来就来说说这个.gitignore的使用. 首先要强调一点,这个文件的完整文件名就是".gitignor ...
- Spring基于注解开发异常
基于注解开发: 一开始:用的jar包: 百度查到: 导入aop包: 没用 有的说: Spring版本和jdk版本不匹配 于是我换成了4.0版本 导入的jar包: 还是报错. 解决办法:添加spring ...