头条新闻

割腕,魔魅,大葱

在平时写代码时,经常会用到各种设计模式,其中一种就是代理模式,代理实现可以分为静态代理和动态代理。

静态代理

静态代理的模式在平时生活中也很常见,比如买火车票这件小事,黄牛相当于是火车站的代理,我们可以通过黄牛或者代售点进行买票行为,但只能去火车站进行改签和退票,因为只有火车站才有改签和退票的方法。

在代码实现中相当于为一个委托对象realSubject提供一个代理对象proxy,通过proxy可以调用realSubject的部分功能(买票),并添加一些额外的业务处理(收取手续费),同时可以屏蔽realSubject中未开放的接口(改签和退票)。

1、RealSubject 是委托类,Proxy 是代理类;

2、Subject 是委托类和代理类的接口;

3、request() 是委托类和代理类的共同方法;

4、RealSubject 中有未公开的方法

具体代码实现如下:

interface Subject {
void request();
}
class RealSubje割腕,魔魅,大葱ct implementmhxx关键任务s 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.lan爱荷华大学回国认可度g.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, Ob笑傲江湖之林家大少ject[] 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
return Proxy.newProxyInstance(loader, interfaces, this);
}
}

3、使用动态代理

public class ProxyTest {
public static void main(String[] args) {
Service service = new UserServiceImpl();
MyInvocatioHandler handler = new MyInvocatioHan赶尸后裔dler(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.pro我是性瘾者xy.$Proxy.1 看看代理类如何实现,反编译出来的java代码如下:

public final class $proxy1 extends Proxy implements Service {
public $proxy1(InvocationHandler invocationhandler) {
super(invocationhandler);
}
public final boole情艺在线an e新动漫中华quals(Object obj) {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Thro宋永佳纪录片wable 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 UndeclaredThrowableExcepgno面膜tion(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;
pguiz163rivate static Method m2;
private static Method m3;
private static Method m0;
st黄牛不慎落泥塘atic {
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 N轮环的魔导师oSuchMethodError(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动态代理,要求委托类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。

推荐新闻