JDK代理
故事是:Tom过年回家没抢到车票,于是找黄牛强,于是黄牛加价100帮他抢了一张票。本故事纯属虚构,如有雷同,当为知己。
src/main/java/com/xh/pattern/proxy/
└── jdk
├── Huangniu.java
├── JDKProxyTest.java
├── Tom.java
└── Travelers.java
接口
package com.xh.pattern.proxy.jdk;
/**
* Created by root on 3/13/18.
*/
/**
* 旅行者
*/
public interface Travelers {
/**
* 买车票
*
* @param
*/
void buyTrainTicket();
}
被代理者
package com.xh.pattern.proxy.jdk;
/**
* Created by root on 3/13/18.
*/
public class Tom implements Travelers {
private String name;
public Tom(String name) {
this.name = name;
}
public void buyTrainTicket() {
System.out.println("###我是:" + name + ",我要买一张票!");
}
}
代理
package com.xh.pattern.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by root on 3/13/18.
*/
public class Huangniu implements InvocationHandler {
private Object target;
public Object getInstance(Object target) {
this.target = target;
Class clazz = target.getClass();
/**
* 重要,重新生成的对象
*/
return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("===我是黄牛,说说需求");
/**
* 注意:
* method.invoke(target, args);
* 写成 method.invoke(proxy, args);就死循环啦
*/
method.invoke(target, args);
System.out.println("===OK,一张票多收100元");
return null;
}
}
测试类
package com.xh.pattern.proxy.jdk;
/**
* Created by root on 3/13/18.
*/
public class JDKProxyTest {
public static void main(String[] args) {
Travelers travelers = (Travelers) new Huangniu().getInstance(new Tom("TomJin"));
travelers.buyTrainTicket();
}
}
结果:
===我是黄牛,说说需求
###我是:TomJin,我要买一张票!
===OK,一张票多收100元
CGLIB代理
├── cglib
│ ├── CgHuangniu.java
│ ├── CgProxyTest.java
│ └── CgTom.java
和JDK代理不一样,cglib代理不需要实现接口
被代理者
package com.xh.pattern.proxy.cglib;
/**
* Created by root on 3/13/18.
*/
public class CgTom {
private String name;
public CgTom(String name) {
this.name = name;
}
public CgTom() {
}
public void buyTrainTicket() {
System.out.println("###我是CG:" + name + ",我要买一张票!");
}
}
代理者
package com.xh.pattern.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* Created by root on 3/13/18.
*/
public class CgHuangniu implements MethodInterceptor {
public Object getInstance(Object target) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("===我是CG黄牛,说说需求");
/**
* 注意:
* methodProxy.invokeSuper(o, objects);
* 这里是代理父类方法
* 写成 method.invoke(o, objects) 就死循环啦
*/
methodProxy.invokeSuper(o, objects);
System.out.println("===OK,一张票多收100元");
return null;
}
}
测试类
package com.xh.pattern.proxy.cglib;
/**
* Created by root on 3/13/18.
*/
public class CgProxyTest {
public static void main(String[] args) {
CgTom cgTom = (CgTom) new CgHuangniu().getInstance(new CgTom("Tom2"));
cgTom.buyTrainTicket();
}
}
结果:
===我是CG黄牛,说说需求
###我是CG:null,我要买一张票!
===OK,一张票多收100元
分析JDK代理
观察发现jdk代理对象可以获取到被代理对象的属性,而cglib代理对象不能,why?
看看Proxy.newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
...
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
cons.setAccessible(true);
return null;
}
});
}
return cons.newInstance(new Object[]{h});
...
由上可知,返回的对象应该是一个Proxy类型的,那么怎么又可以强转为Travelers呢?除非它也是Travelers类型
修改测试类为:
package com.xh.pattern.proxy.jdk;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
/**
* Created by root on 3/13/18.
*/
public class JDKProxyTest {
public static void main(String[] args) {
Travelers travelers = (Travelers) new Huangniu().getInstance(new Tom("TomJin"));
travelers.buyTrainTicket();
//原理:
//1、拿到被代理对象的引用,并且获取到它的所有的接口,反射获取
//2、JDK Proxy类重新生成一个新的类、同时新的类要实现被代理类所有实现的所有的接口
//3、动态生成Java代码,把新加的业务逻辑方法由一定的逻辑代码去调用(在代码中体现)
//4、编译新生成的Java代码.class
//5、再重新加载到JVM中运行
//以上这个过程就叫字节码重组
//JDK中有个规范,只要要是$开头的一般都是自动生成的
//通过反编译工具可以查看源代码
byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Travelers.class});
FileOutputStream os = null;
try {
os = new FileOutputStream("/tmp/$Proxy0.class");
os.write(bytes);
os.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
通过idea打开class文件看看返回的是个什么
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import com.xh.pattern.proxy.jdk.Travelers;
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 Travelers {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue();
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void buyTrainTicket() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final String toString() throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {
try {
return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue();
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[]{Class.forName("java.lang.Object")});
m3 = Class.forName("com.xh.pattern.proxy.jdk.Travelers").getMethod("buyTrainTicket", new Class[0]);
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
其中:super.h.invoke(this, m3, (Object[])null);就相当于调用Huangniu的invoke方法啦。
之前在网上看到有写用反射实例化对象[必须要有]无参构造器,很明显的是不对的。Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;Constructor.newInstance() 可以根据传入的参数,调用任意构造构造函数。
分析CGLIB代理
如果想看看cglib生成的代理类是不能用上面的方法的。有一个更牛逼的工具,可以查看运行时JVM里的类信息。
1、在测试类最后添加一句
public class CgProxyTest {
public static void main(String[] args) throws InterruptedException {
CgTom cgTom = (CgTom) new CgHuangniu().getInstance(new CgTom("Tom2"));
cgTom.buyTrainTicket("tom4");
System.out.println("END");//断点
}
}
2、查看Java进程
jps
3、debug启动测试类,重复2,对比会发现多了一个进程,后面会用到
4、启动HSDB
java -classpath "/opt/Java/jdk1.8.0_121/lib/sa-jdi.jar" sun.jvm.hotspot.HSDB
点击File-->Attach to HotSpot process ,添加进程号
再点击Tools-->Class Browser ,输入关键字CgTom过滤。
5、生成class文件
点击链接
Create .class for all classes
会在启动HSDB的目录下生成文件
6、查看class文件
如果直接丢到idea下会有一些内容看不到,只能看看方法名。
like this
public class CgTom$$EnhancerByCGLIB$$ee6f616a extends com.xh.pattern.proxy.cglib.CgTom implements net.sf.cglib.proxy.Factory {
private boolean CGLIB$BOUND;
public static java.lang.Object CGLIB$FACTORY_DATA;
private static final java.lang.ThreadLocal CGLIB$THREAD_CALLBACKS;
private static final net.sf.cglib.proxy.Callback[] CGLIB$STATIC_CALLBACKS;
...
public CgTom$$EnhancerByCGLIB$$ee6f616a(java.lang.String s) { /* compiled code */ }
public CgTom$$EnhancerByCGLIB$$ee6f616a() { /* compiled code */ }
public final boolean equals(java.lang.Object o) { /* compiled code */ }
public final java.lang.String toString() { /* compiled code */ }
...
如果用jd-gui打开
package com.xh.pattern.proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CgTom$$EnhancerByCGLIB$$ee6f616a
extends CgTom
implements Factory
{
private boolean CGLIB$BOUND;
public static Object CGLIB$FACTORY_DATA;
private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
...
public CgTom$$EnhancerByCGLIB$$ee6f616a(String paramString)
{
super(paramString);
CGLIB$BIND_CALLBACKS(this);
}
public CgTom$$EnhancerByCGLIB$$ee6f616a()
{
CGLIB$BIND_CALLBACKS(this);
}
static {}
...
public final void buyTrainTicket(String paramString)
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_0 != null) {
return;
}
super.buyTrainTicket(paramString);
}
public final void buyTrainTicket()
{
MethodInterceptor tmp4_1 = this.CGLIB$CALLBACK_0;
if (tmp4_1 == null)
{
tmp4_1;
CGLIB$BIND_CALLBACKS(this);
}
if (this.CGLIB$CALLBACK_0 != null) {
return;
}
super.buyTrainTicket();
}
public void setCallback(int paramInt, Callback paramCallback)
{
switch (paramInt)
{
case 0:
this.CGLIB$CALLBACK_0 = ((MethodInterceptor)paramCallback);
break;
}
}
...
}
分析这个class可以知道cglib生成的代理类继承了我们的CgTom,实现了Factory接口
他有个属性private MethodInterceptor CGLIB$CALLBACK_0;这个便是我们的CgHuangniu
再看看buyTrainTicket(String paramString),它会调用CGLIB$BIND_CALLBACKS(this);
这样就和CgHuangniu关联了。
其实这样分析一遍也就了解了大概,细节之处还是依旧理解的有限,比如我们发现CgTom有四个,有一个原始的,上面一个还有两个是也cglib生成的,看的是一脸懵逼啊。