这里我直接引用百度百科里面的一句话。java
代理模式的定义:为其余对象提供一种代理以控制对这个对象的访问。在某些状况下,一个对象不适合或者不能直接引用另外一个对象,而代理对象能够在客户端和目标对象之间起到中介的做用。git
这些文字上的东西我也说的不太清楚,在下面我会尽量使用代码来让你们明白静态代理和动态代理这两种方式的原理!说清楚原理以后我再说下代理模式能帮咱们作什么??github
tip:这里我用的IntelliJ IDEA写的java项目来作的示例,有想作示例的同窗千万不要用Android项目来写,源代码会有所不一样!!!还有接下来文章比较长,但愿你们能耐心看完,看完收获是确定有的。编程
这里我使用一个明星和经纪人关系的例子来作一个比较形象的代码说明,但愿你们能够看代码就明白其中的关系。设计模式
首先,咱们要实现一个代理模式,那有三个元素是必定要有的,且这三个元素不论是静态代理仍是动态代理都是必须的,这里我先以静态代理为例作下说明。数组
这个元素是一个行为接口类,主要是定义各类行为方法,这里咱们以一位明星掌握的技能为例,如(某明星如今只有一个唱歌的技能):bash
/**
* desc: 定义一些明星拥有的技能行为
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:16
*/
public interface ISkillAction {
/**
* 唱歌
*/
void sing();
}
复制代码
这里咱们须要一个真实的对象,主要是提供给代理者进行操做。app
/**
* desc: 明星 --- 真实对象
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:15
*/
public class Star implements ISkillAction {
@Override
public void sing() {
System.out.println("明星开始唱歌了。");
}
}
复制代码
这里是咱们的代理者,也就是明星经纪人,须要负责明星的各类行程规划和安排,那么用编程的思想来讲的话,咱们确定是要持有这个"明星"的一份引用的,固然了,如今咱们的明星还只会唱歌这一项技能(sing()),那么咱们如今须要代理的也就只有sing()这一个行为方法了,这里是须要跟明星同样实现共同的接口的(ISkillAction):框架
/**
* desc: 明星的经纪人 --- 代理
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:20
*/
public class Agent implements ISkillAction {
/**
* 经纪人是须要持有一份目标对象(真实对象---明星)的引用的。
*/
private Star mStar;
public Agent(Star star) {
mStar = star;
}
@Override
public void sing() {
// 经纪人根据明星的行程计划作好本次的规划。
System.out.println("经纪人首先要安排好本次行程计划。");
// 明星唱歌前经纪人要跟活动商作好相关的布置。
System.out.println("经纪人跟活动商进行唱歌前布置。");
// 明星开始唱歌。
mStar.sing();
// 明星唱完歌后经纪人要作好收尾工做(例如把出演费收到帐等等)。
System.out.println("经纪人跟活动商作收尾工做(例如把出演费收到帐等等)。");
// 本次行程结束,经纪人须要更新行程计划
System.out.println("本次行程结束,经纪人须要更新行程计划。");
}
}
复制代码
以上咱们已经有了这三个必要的元素了,那咱们能够开始进行代理模式的使用了,使用方式以下:ide
public class JavaProxyTest {
public static void main(String[] args) {
// 使用静态代理模式
useStaticProxy();
}
/**
* 静态代理模式的使用
*/
private static void useStaticProxy() {
// 要先有一份真实对象(明星对象)
Star star = new Star();
// 而后代理者(经纪人)要持有这个真实对象
Agent agent = new Agent(star);
// 这样代理者就能够进行这些接口行为的代理操做了
// 就比如有些商业活动负责人先找到某明星经纪人,经过经纪人成功邀请到某明星来进行演出。
agent.sing();
}
}
复制代码
这里我注释应该是比较清楚了的,首先咱们确定是要有一个真实对象的实例的(明星本人),而后咱们能够经过代理者(经纪人)进行代理操做。
输出的日志以下:
这里咱们能够看到,经过这个代理者经纪人,咱们的活动商成功请到咱们的明星进行了一次唱歌出演,咱们的明星只须要负责唱歌这个行为,唱歌先后的布置安排或者其余一些行为的插入等等,都是能够经过代理者完成的。
经过上面咱们能够看到一个简单的静态代理就完成了,可是这个时候可能有些机智的同窗会想到一些问题,如:
在正常状况下,咱们的行为接口须要定义的方法是可能有多个的(明星可能会唱歌,会跳舞等等),那么咱们上面提到的三个元素就会发生一点改变了,代码以下:
/**
* desc: 定义一些明星拥有的技能行为
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:16
*/
public interface ISkillAction {
/**
* 唱歌
*/
void sing();
/**
* 跳舞
*/
void dance();
}
复制代码
/**
* desc: 明星 --- 真实对象
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:15
*/
public class Star implements ISkillAction {
@Override
public void sing() {
System.out.println("明星开始唱歌了。");
}
@Override
public void dance() {
System.out.println("明星开始跳舞了。");
}
}
复制代码
/**
* desc: 明星的经纪人 --- 代理
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:20
*/
public class Agent implements ISkillAction {
/**
* 经纪人是须要持有一份目标对象(真实对象---明星)的引用的。
*/
private Star mStar;
public Agent(Star star) {
mStar = star;
}
@Override
public void sing() {
// 经纪人根据明星的行程计划作好本次的规划。
System.out.println("经纪人首先要安排好本次行程计划。");
// 明星唱歌前经纪人要跟活动商作好相关的布置。
System.out.println("经纪人跟活动商进行唱歌前布置。");
// 明星开始唱歌。
mStar.sing();
// 明星唱完歌后经纪人要作好收尾工做(例如把出演费收到帐等等)。
System.out.println("经纪人跟活动商作收尾工做(例如把出演费收到帐等等)。");
// 本次行程结束,经纪人须要更新行程计划
System.out.println("本次行程结束,经纪人须要更新行程计划。");
}
@Override
public void dance() {
// 经纪人根据明星的行程计划作好本次的规划。
System.out.println("经纪人首先要安排好本次行程计划。");
// 明星开始跳舞。
mStar.sing();
// 本次行程结束,经纪人须要更新行程计划
System.out.println("本次行程结束,经纪人须要更新行程计划。");
}
}
复制代码
从上面咱们能够发现,共同行为接口每多一个方法行为,那咱们的代理者也就须要多实现一个代理方法,而且有可能咱们每一个代理方法先后都有一样的操做,那么咱们就须要在每一个方法里面重复写这些操做,这能够说是静态代理的一些缺点了,这些我会在介绍动态代理的时候经过代码进行对比说明。
咱们的示例中明星只实现了一个技能接口类,可是实际状况中通常不止实现一个,这里咱们增长一个生活共同行为类,以下:
/**
* desc: 定义一些生活上的行为方法
*
* @author Wepon.Yan
* created at 2019/1/23 下午4:18
*/
public interface ILiveAction {
/**
* 吃早餐
*/
void eatBreakfast();
}
复制代码
而后咱们的明星实现了这个接口:
public class Star implements ISkillAction, ILiveAction {
@Override
public void sing() {
System.out.println("明星开始唱歌了。");
}
@Override
public void dance() {
System.out.println("明星开始跳舞了。");
}
@Override
public void eatBreakfast() {
System.out.println("明星开始吃早餐了。");
}
}
复制代码
最后咱们的代理者(经纪人)也须要实现这些共同行为接口类:
public class Agent implements ISkillAction, ILiveAction {
/**
* 经纪人是须要持有一份目标对象(真实对象---明星)的引用的。
*/
private Star mStar;
public Agent(Star star) {
mStar = star;
}
@Override
public void sing() {
// 经纪人根据明星的行程计划作好本次的规划。
System.out.println("经纪人首先要安排好本次行程计划。");
// 明星唱歌前经纪人要跟活动商作好相关的布置。
System.out.println("经纪人跟活动商进行唱歌前布置。");
// 明星开始唱歌。
mStar.sing();
// 明星唱完歌后经纪人要作好收尾工做(例如把出演费收到帐等等)。
System.out.println("经纪人跟活动商作收尾工做(例如把出演费收到帐等等)。");
// 本次行程结束,经纪人须要更新行程计划
System.out.println("本次行程结束,经纪人须要更新行程计划。");
}
@Override
public void dance() {
// 经纪人根据明星的行程计划作好本次的规划。
System.out.println("经纪人首先要安排好本次行程计划。");
// 明星开始跳舞。
mStar.sing();
// 本次行程结束,经纪人须要更新行程计划
System.out.println("本次行程结束,经纪人须要更新行程计划。");
}
@Override
public void eatBreakfast() {
// 这里就以经纪人进行早餐预订作一个假设吧。
System.out.println("经纪人帮明星预订早餐。");
// 明星开始吃早餐了。
mStar.eatBreakfast();
// 吃完后经纪人去帮明星结帐。
System.out.println("经纪人去帮明星结帐。");
}
}
复制代码
经过这里咱们又能够发现,共同行为接口实现的越多,咱们代理者要实现的也一样须要增多,这样仍是挺麻烦的。
动态代理我这里使用的是jdk提供的实现方式。
咱们先实现一下动态代理的写法再来分析它的流程,在使用动态代理前咱们要有如下元素:
首先咱们使用下静态代理里面的共同行为接口类:
public interface ISkillAction {
/**
* 唱歌
*/
void sing();
/**
* 跳舞
*/
void dance();
}
复制代码
public interface ILiveAction {
/**
* 吃早餐
*/
void eatBreakfast();
}
复制代码
这个是jdk中提供的,做用是经过实现InvocationHandler接口建立本身的调用处理器,这里我只对每一个行为的调用先后进行一行日志输出,以下:
/**
* desc: 建立本身的调用处理器
*
* @author Wepon.Yan
* created at 2019/1/23 下午6:52
*/
public class DynamicProxyHandler implements InvocationHandler {
/**
* 持有一份真实对象的引用
*/
private Object mObject;
public DynamicProxyHandler(Object object) {
mObject = object;
}
/**
*
* @param proxy 代理类对象
* @param method 方法
* @param args 方法参数
* @return 方法返回
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前的统一操做");
Object invoke;
// 这里能够根据不一样方法名进行不一样的操做
if ("sing".equals(method.getName())) {
// 拦截下唱歌
// 明星唱歌前经纪人要跟活动商作好相关的布置。
System.out.println("经纪人跟活动商进行唱歌前布置。");
// 调用真实对象的行为
invoke = method.invoke(mObject, args);
// 明星唱完歌后经纪人要作好收尾工做(例如把出演费收到帐等等)。
System.out.println("经纪人跟活动商作收尾工做(例如把出演费收到帐等等)。");
}else{
// 调用真实对象的行为
invoke = method.invoke(mObject, args);
}
System.out.println("代理后的统一操做");
return invoke;
}
}
复制代码
而后咱们经过jdk提供的方式使用动态代理:
/**
* 动态代理模式的使用
*/
private static void useDynamicProxy() {
// 要先有一份真实对象(明星对象)
Star star = new Star();
// 而后要有一个自定义的处理器
DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(star);
// 而后经过jdk方式动态生成代理对象proxy
// 这里是一个Object类型,根据不一样行为接口强转
Object proxy = Proxy.newProxyInstance(
ClassLoader.getSystemClassLoader(),
new Class[]{ISkillAction.class, ILiveAction.class},
dynamicProxyHandler
);
// 进行各个行为的代理,这里须要进行不一样行为类型的转换
((ISkillAction) proxy).sing();
((ISkillAction) proxy).dance();
((ILiveAction) proxy).eatBreakfast();
}
复制代码
而后咱们在控制台看下日志的打印状况:
从上面能够知道咱们代理是成功了的,可是这里尚未完善,后面再补充,这里可能有同窗会问了,为何这么写就实现动态代理了呢?原理是什么呢?不慌,下面咱们经过源码和反编译来追踪分析一下:
首先,咱们分析下这个Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)源码,这个方法的注释开头是这么说的:
* Returns an instance of a proxy class for the specified interfaces
* that dispatches method invocations to the specified invocation
* handler.
* @param loader
the class loader to define the proxy class
* @param interfaces
the list of interfaces for the proxy class
* to implement
* @param h
the invocation handler to dispatch method invocations to
* @return a proxy instance with the specified invocation handler of a
* proxy class that is defined by the specified class loader
* and that implements the specified interfaces
简单翻译一下就是:
* 返回指定接口的代理类的实例,将方法调用调度到指定的调用处理程序。
* @param loader 用于定义代理类的类加载器
* @param interfaces 要实现的代理类的接口列表
* @param h 调度方法调用的调用处理程序
* @return 具备代理类的指定调用处理程序的代理实例,
该代理类的指定调用处理程序由指定的类装入器定义并实现指定的接口
复制代码
重点:返回指定接口的代理类的实例,将方法调用调度到指定的调用处理程序。
从这句话咱们结合代码能够看出,意思就是经过Proxy.newProxyInstance返回一个代理类的实例proxy,而后经过这个代理类proxy在进行方法调用的时候,实际又调度到了咱们指定的DynamicProxyHandler的invoke方法里面进行处理。
1. 那到底是怎么生成的代理类呢?
2. 生成的代理类又是长什么样子的呢?
3. 代理类又是怎么将方法调用调度到咱们指定的调用处理程序的呢(InvocationHandler实现类)?
那么咱们继续分析Proxy.newProxyInstance()方法的代码实现:
这里我把一些没用的注释和不用分析的catch代码去掉了,这里主要是利用反射实现,以下。
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
throws IllegalArgumentException
{
Objects.requireNonNull(h);
final Class<?>[] intfs = interfaces.clone();
final SecurityManager sm = System.getSecurityManager();
if (sm != null) {
checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
}
//查找或生成指定的代理类Class对象。
Class<?> cl = getProxyClass0(loader, intfs);
try {
if (sm != null) {
checkNewProxyPermission(Reflection.getCallerClass(), cl);
}
// 获取代理类Class的构造函数。
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
if (!Modifier.isPublic(cl.getModifiers())) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
// 若是做用域为私有,setAccessible为ture支持访问
cons.setAccessible(true);
return null;
}
});
}
// 经过建立一个代理类的实例(这里构造函数中传入了咱们的调用处理器,至于缘由等下分析),并返回。
return cons.newInstance(new Object[]{h});
// catch部分的代码我所有删除了,不看。
} catch (Exception e) {
}
}
复制代码
从上面的代码能够看到,最核心的是这一行代码,查找或生成指定的代理类Class对象,而后咱们继续盯着这个方法走下去。
Class<?> cl = getProxyClass0(loader, intfs);
复制代码
/**
* Generate a proxy class. Must call the checkProxyAccess method
* to perform permission checks before calling this.
*/
private static Class<?> getProxyClass0(ClassLoader loader,
Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
复制代码
继续看proxyClassCache的生成:
/**
* a cache of proxy classes
*/
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
复制代码
上面有说经过ProxyClassFactory建立代理类,这里直接看ProxyClassFactory类的构造
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
private static final String proxyClassNamePrefix = "$Proxy";
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
.......
.......
/*
*这中间省略掉两个大的for循环代码,太多了,这里不须要进行分析
*/
.......
.......
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
long num = nextUniqueNumber.getAndIncrement();
// 要生成的代理类的名称。下面生成代理类的时候要用到的。
String proxyName = proxyPkg + proxyClassNamePrefix + num;
// 如下代码是生成指定的代理类。
// 如下代码是生成指定的代理类。
// 如下代码是生成指定的代理类。
// 生成代理类Class byte数组
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
// 这里生成代理类Class返回
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
复制代码
到了这一步,我顺便Debug了一下这里的代码,如图:
而后咱们继续看ProxyGenerator.generateProxyClass(); 和 defineClass0(); 这两个方法,发如今defineClass0()是一个native方法,那就没啥好看的了
private static native Class<?> defineClass0(ClassLoader loader, String name,
byte[] b, int off, int len);
复制代码
这里最核心的方法是生成ProxyGenerator.generateProxyClass()这个,咱们继续看下去(这里提示一下,下面追进去的源码显示的格式有问题,不是正常的源码了,并且有不少我不会再解释了,由于我也不能都看懂,只会用注释来解释一部分重要的地方,若是下面这部分看不懂或者不想看能够直接跳过,直接看后面写的动态生成的代理类的内容就ok,不会影响代理模式的理解):
ProxyGenerator.generateProxyClass()追进去后往下看,下面是ProxyGenerator类中的部份内容:
// 这个值是一个系统变量,这里是控制下面生成了代理类Class的时候是否要保存成文件 。
private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
public static byte[] generateProxyClass(String var0, Class<?>[] var1) {
return generateProxyClass(var0, var1, 49);
}
public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
// 这里是生成代理类Class byte数组的地方
final byte[] var4 = var3.generateClassFile();
// 这里是判断是否要对这个文件进行保存
if (saveGeneratedFiles) {
AccessController.doPrivileged(new PrivilegedAction<Void>() {
public Void run() {
try {
int var1 = var0.lastIndexOf(46);
Path var2;
if (var1 > 0) {
Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
Files.createDirectories(var3);
var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
} else {
var2 = Paths.get(var0 + ".class");
}
Files.write(var2, var4, new OpenOption[0]);
return null;
} catch (IOException var4x) {
throw new InternalError("I/O exception saving generated file: " + var4x);
}
}
});
}
// 返回byte数组
return var4;
}
复制代码
从上面咱们看到有三条注释的地方,如今咱们这里先看下生成代理类Class byte数组的地方,也就是generateClassFile()这个方法,这个方法超级长,仍是同样,请注意我注释的地方就OK,我会标明大概意思,具体就不要太纠结了,头发都掉光了:
private byte[] generateClassFile() {
// 首先是添加 hashCode(),equals(),toString()这些方法
this.addProxyMethod(hashCodeMethod, Object.class);
this.addProxyMethod(equalsMethod, Object.class);
this.addProxyMethod(toStringMethod, Object.class);
// 添加接口中的方法
Class[] var1 = this.interfaces;
int var2 = var1.length;
int var3;
Class var4;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
Method[] var5 = var4.getMethods();
int var6 = var5.length;
for(int var7 = 0; var7 < var6; ++var7) {
Method var8 = var5[var7];
this.addProxyMethod(var8, var4);
}
}
Iterator var11 = this.proxyMethods.values().iterator();
List var12;
while(var11.hasNext()) {
var12 = (List)var11.next();
checkReturnTypes(var12);
}
Iterator var15;
try {
// 添加构造方法
this.methods.add(this.generateConstructor());
var11 = this.proxyMethods.values().iterator();
while(var11.hasNext()) {
var12 = (List)var11.next();
var15 = var12.iterator();
while(var15.hasNext()) {
ProxyGenerator.ProxyMethod var16 = (ProxyGenerator.ProxyMethod)var15.next();
this.fields.add(new ProxyGenerator.FieldInfo(var16.methodFieldName, "Ljava/lang/reflect/Method;", 10));
this.methods.add(var16.generateMethod());
}
}
// 添加静态初始化方法
this.methods.add(this.generateStaticInitializer());
} catch (IOException var10) {
throw new InternalError("unexpected I/O Exception", var10);
}
if (this.methods.size() > 65535) {
throw new IllegalArgumentException("method limit exceeded");
} else if (this.fields.size() > 65535) {
throw new IllegalArgumentException("field limit exceeded");
} else {
this.cp.getClass(dotToSlash(this.className));
this.cp.getClass("java/lang/reflect/Proxy");
var1 = this.interfaces;
var2 = var1.length;
for(var3 = 0; var3 < var2; ++var3) {
var4 = var1[var3];
this.cp.getClass(dotToSlash(var4.getName()));
}
// 到了这里要生成的代理类Class的基本信息就都完了,下面就是生成代理类的.class文件了,这里我特意在网上查过,可是没懂,涉及到Class文件的魔数这些东西,我也是刚接触这些知识点,还在看,总之下面就是生成Class文件用的了,看不懂了 - -!!!
this.cp.setReadOnly();
ByteArrayOutputStream var13 = new ByteArrayOutputStream();
DataOutputStream var14 = new DataOutputStream(var13);
try {
var14.writeInt(-889275714);
var14.writeShort(0);
var14.writeShort(49);
this.cp.write(var14);
var14.writeShort(this.accessFlags);
var14.writeShort(this.cp.getClass(dotToSlash(this.className)));
var14.writeShort(this.cp.getClass("java/lang/reflect/Proxy"));
var14.writeShort(this.interfaces.length);
Class[] var17 = this.interfaces;
int var18 = var17.length;
for(int var19 = 0; var19 < var18; ++var19) {
Class var22 = var17[var19];
var14.writeShort(this.cp.getClass(dotToSlash(var22.getName())));
}
var14.writeShort(this.fields.size());
var15 = this.fields.iterator();
while(var15.hasNext()) {
ProxyGenerator.FieldInfo var20 = (ProxyGenerator.FieldInfo)var15.next();
var20.write(var14);
}
var14.writeShort(this.methods.size());
var15 = this.methods.iterator();
while(var15.hasNext()) {
ProxyGenerator.MethodInfo var21 = (ProxyGenerator.MethodInfo)var15.next();
var21.write(var14);
}
var14.writeShort(0);
return var13.toByteArray();
} catch (IOException var9) {
throw new InternalError("unexpected I/O Exception", var9);
}
}
}
复制代码
上面的这个方法就是我能跟到最后的方法了,在这里拿到了Class byte数组,整个动态生成代理类的流程到这里咱们就结束了!
霸特!!! 咱们尚未看到生成的代理类长什么样子啊?总要给咱们看下样子吧?
好好好,继续!!
而后咱们再看下 if (saveGeneratedFiles) {}这个地方,看这里一堆var头疼,可是这里的代码意思是if true的话会保存一份文件(追到这里能够猜想,文件应该是生成的代理类的.class文件了),那咱们仍是Debug一下这个地方,看有没有进行保存,保存的文件又在哪里?这是就扯到怎么看查看生成的代理类的.class文件了,下面开个栏再说。
刚说到咱们要Debug一下 if (saveGeneratedFiles)这个地方,Debug结果以下:
修改saveGeneratedFiles为true:
就这样就好了,记得放在前面,而后继续Debug看,生效了:
而后咱们看下这个方法里面:
经过这里的代码和path,咱们能够看到这里生成了一个$Proxy0.class的文件保存在这个path:com/sun/proxy/$Proxy0.class,那是否是这样呢?
跑了一下,报错了!!不慌,看下:
哦,这错仍是很明显的,那咱们把这些类实现下Serializable就行了:
好了,再跑一下,咱们能够看到确实有生成文件了,运行一下代码后能够看到咱们的项目结构发生了改变:
这里代码以下,一样地,你们注意下我写的注释,大概就能明白这个类作了什么了:
public final class $Proxy0 extends Proxy implements ISkillAction, ILiveAction {
// 保存的每一个方法,利用反射获取,具体的代码在最下面的static{}块中
private static Method m1;
private static Method m5;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
// 首先这里的构造方法是传入了一个自定义的处理器,这里用的super,咱们看下super中作了什么? 其实就是设置属性h引用这个自定义的处理器,这个h很重要,在下面会用到。
// protected Proxy(InvocationHandler h) {
// Objects.requireNonNull(h);
// this.h = h;
// }
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});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
// 这个是咱们的接口行为中的方法(吃早餐),当咱们调用代理类的eatBreakfast()方法的时候就会走到这一步
public final void eatBreakfast() throws {
try {
// 这里咱们看到调用了h.invoke方法,也就是调用了咱们自定义的处理器的方法。如下方法都是这样的逻辑,就不一一注释了。
super.h.invoke(this, m5, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final void dance() 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 void sing() throws {
try {
super.h.invoke(this, m4, (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);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
static {
// 反射获取这些方法,包括接口的方法
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m5 = Class.forName("com.wepon.proxydemo.proxy.ILiveAction").getMethod("eatBreakfast");
m3 = Class.forName("com.wepon.proxydemo.proxy.ISkillAction").getMethod("dance");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.wepon.proxydemo.proxy.ISkillAction").getMethod("sing");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
复制代码
还记得以前源码有注释一个这样的方法吗?
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
复制代码
咱们能够经过这个方法拿到代理类Class的byte[]后本身保存成.class文件,代码以下:
运行一下后能够看到咱们的项目中生成了一个文件 :
这个文件打开后跟上面修改系统变量生成的文件内容是同样的,不用怀疑我,是真的。
从以上代码能够看到,咱们调用代理类的方法的时候,最终会调用到咱们自定义的处理器(我文中写的这个DynamicProxyHandler)的invoke(Object proxy, Method method, Object[] args)方法,这里咱们终于就知道为何会调用到咱们写的处理器里面去了。
好了咱们再回顾一下这个DynamicProxyHandler,加深每一个元素之间的关联印象:
/**
* desc: 建立本身的调用处理器
*
* @author Wepon.Yan
* created at 2019/1/23 下午6:52
*/
public class DynamicProxyHandler implements InvocationHandler, Serializable {
/**
* 持有一份真实对象的引用
*/
private Object mObject;
public DynamicProxyHandler(Object object) {
mObject = object;
}
/**
*
* @param proxy 代理类对象
* @param method 方法
* @param args 方法参数
* @return 方法返回
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("代理前的统一操做");
Object invoke;
// 这里能够根据不一样方法名进行不一样的操做
if ("sing".equals(method.getName())) {
// 拦截下唱歌
// 明星唱歌前经纪人要跟活动商作好相关的布置。
System.out.println("经纪人跟活动商进行唱歌前布置。");
// 调用真实对象的行为
invoke = method.invoke(mObject, args);
// 明星唱完歌后经纪人要作好收尾工做(例如把出演费收到帐等等)。
System.out.println("经纪人跟活动商作收尾工做(例如把出演费收到帐等等)。");
}else{
// 调用真实对象的行为
invoke = method.invoke(mObject, args);
}
System.out.println("代理后的统一操做");
return invoke;
}
}
复制代码
好了,分析到这里,就都结束了,我想你们经过这个流程和最终看到的结果应该是理解比较深入了的,至少我但愿是这样子的 ^_^,码字是真累啊(估计你们看的也累- -!!)。
对了,除去这种jdk提供的动态代理方式,还有一种cglib的代理方式,这里就不讲了,你们能够去了解下。
代理模式最典型的应用是AOP,关于AOP以后我会写一份文章做些说明,而后代理模式在一些RPC框架中也应用普遍。
从一份java23种设计模式pdf文档中摘抄一段代理模式适用性的描述:
1.远程代理(RemoteProxy) 为一个对象在不一样的地址空间提供局部表明。
2.虚拟代理(VirtualProxy) 根据须要建立开销很大的对象。
3.保护代理(ProtectionProxy) 控制对原始对象的访问。
4.智能指引(SmartReference) 取代了简单的指针,它在访问对象时执行一 些附加操做。
复制代码
最后我把文章中的源码上传到github了,地址以下,有问题欢迎交流: