一、动态代理
代理类在程序运行时创建的代理方式被称为动态代理。
一般有两种实现的方式:
jdk实现特点:由java内部的反射机制来实现(反射机制生成类比较高效)
cglib实现特点:借助asm实现(asm在生成类之后的相关执行过程较高效)
二、jdk实现示例
动态代理就是根据被代理的接口生成所有的方法,也就是说给定一个接口,动态代理会宣称“我已经实现该接口下的所有方法了”,但是,你只能查到一个$proxy0的名称。
1、先定义被代理的接口
public interface Work {
public void work();
}
2、被代理类(真实角色)---角色定义参考代理模式(静态代理)
public class HonestWork implements Work {
@Override
public void work() {
System.out.println("~~~~~~~~~~~~~~这里放着的可是具体实现(乃核心机密)~~~~~~~~~~~~");
}
}
3、动态代理类(实现InvocationHandler接口)-----位于代理类与委托类之间的中介类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* @author Ashes
* @Description
* <p>所谓的动态代理是这样一种class</p>
* <p>它是运行时生成的class</p>
* <p>该class需要实现一组interface</p>
* <p>使用动态代理的时候,必须实现InvocationHandler接口</p>
* 2017年4月27日
*/
public class StudentHandler implements InvocationHandler {
private Object target;
public StudentHandler(Object target) {
super();
this.target = target;
}
/* (non-Javadoc)
* @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
* 参数:
* proxy 被代理对象
* method 被代理对象方法
* args方法的参数
*
* 返回值:
* Object 方法的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("~~~~~~~~~~~~~~~~~~我负责拿下项目,我是学生,有资格做这份工作~~~~~~~~~~~~~~~");
method.invoke(target);
System.out.println("~~~~~~~~~~~~~~~~我无情的使用了你的核心机密,现在来做点善后工作~~~~~~~~~~~~~~~~~~~~~");
return null;
}
}
4、场景测试
/**
* @author Ashes
* @Description 所谓场景就是为了测试,老师要来收报告了
* 所谓的增强其实是为了做功能的叠加
* 2017年6月23日
*/
public class TeacherClient {
public static void main(String[] args) {
HonestWork w = new HonestWork(); //首先我们要有一个老实人给我们写核心机密
Class<?> cls = w.getClass();
InvocationHandler ih = new StudentHandler(w);
/*
* loader - 定义代理类的类加载器
* interfaces - 代理类要实现的接口列表
* h - 指派方法调用的调用处理程序
* return 一个带有代理类的指定调用处理程序的代理实例,它由指定的类加载器定义,并实现指定的接口
* 返回代理类的一个实例,返回后的代理类可以当作被代理类使用(可使用被代理类的在接口中声明过的方法)
* */
Work m = (Work) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ih);
System.out.println("cls.getClassLoader() ===> " + cls.getClassLoader());
System.out.println("cls.getInterfaces() ===> " + cls.getInterfaces());
//未指定代理类的非限定名称。但是,以字符串 "$Proxy" 开头的类名空间应该为代理类保留。
System.out.println("代理类的名字 ===> " + m.getClass().getName()); //com.sun.proxy.$Proxy0
m.work(); //现在调用的是动态产生的类$Proxy0
}
}
结果是什么呢??
cls.getClassLoader() ===> sun.misc.Launcher$AppClassLoader@3e25a5
cls.getInterfaces() ===> [Ljava.lang.Class;@156ee8e
代理类的名字 ===> $Proxy0
~~~~~~~~~~~~~~~~~~我负责拿下项目,我是学生,有资格做这份工作~~~~~~~~~~~~~~~
~~~~~~~~~~~~~~这里放着的可是具体实现(乃核心机密)~~~~~~~~~~~~
~~~~~~~~~~~~~~~~我无情的使用了你的核心机密,现在来做点善后工作~~~~~~~~~~~~~~~~~~~~~
三、jdk实现原理---怎么感受$proxy0的存在
1、动态代理实现步骤:
a、创建一个实现接口InvocationHandler的类,它必须实现invoke方法
b、创建被代理的类以及接口
c、调用Proxy的静态方法,创建一个代理类
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
Class<?>[] interfaces,
InvocationHandler h)
d、通过代理调用方法
2、模拟JDK代理的内部实现思路分析:
实现功能:通过Proxy的newProxyInstance返回代理对象
1、声明一段源码(动态产生代理)
2、编译源码(JDK Compiler API),产生新的类(代理类)
3、将这个类load到内存中,产生一个新的对象(代理对象)
4、return 代理对象
不能在运行时定义这些方法的新代码。而是要提供一个调用处理器(invocationhandler)。调用处理器是实现了invocationhandler的类对象,在这个接口中只有一个方法:public void invoke(Object o, Method m);
3、代码示例
public interface Work {
public void work();
}
public interface InvocationHandler {
/**
* @param o 被代理对象
* @param m 被代理对象方法
* 对某个对象的方法进行处理
*/
public void invoke(Object o, Method m);
}
/**
* @author Ashes
* @Description 老实的工作者,只想着完成实际功能
* 2017年6月23日
*/
public class HonestWork implements Work {
@Override
public void work() {
System.out.println("~~~~~~~~~~~~~~这里放着的可是具体实现(乃核心机密)~~~~~~~~~~~~");
}
}
/**
* @author Ashes
* @Description 事务处理器
* 2017年6月30日
*/
public class StudentHandler implements InvocationHandler {
private Object target;
public StudentHandler(Object target) {
this.target = target;
}
@Override
public void invoke(Object o, Method m) {
try {
System.out.println("~~~~~~~~~~~~~~~~~~我负责拿下项目,我是学生,有资格做这份工作~~~~~~~~~~~~~~~");
m.invoke(target);
System.out.println("~~~~~~~~~~~~~~~~我无情的使用了你的核心机密,现在来做点善后工作~~~~~~~~~~~~~~~~~~~~~ ");
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InvocationTargetException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
a、场景如何调用实现
public class TeacherClient {
public static void main(String[] args) {
try {
HonestWork hon = new HonestWork();
InvocationHandler h = new StudentHandler(hon);
//只能是接口
Work w = (Work) Proxy.newProxyInstance(Work.class, h);
w.work();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
b、那我们的$proxy0到底是如何生成的呢???
public class Proxy {
public static Object newProxyInstance(Class infc, InvocationHandler h) throws Exception {
String rt = "\r\n";
String methodstr = "";
//我要实现该接口下的所有方法
for(Method m : infc.getMethods()) {
methodstr += "@Override" + rt +
"public void " + m.getName() + "() {" + rt +
"try {" + rt +
"Method md = " + infc.getName() + ".class.getMethod(\""
+ m.getName() + "\");" + rt +
"h.invoke(this, md);" + rt +
"}catch(Exception e) {e.printStackTrace();}" + rt +
"}";
}
String str =
"package com.ashes.myselfproxy2;" + rt +
"import com.ashes.myselfproxy2.InvocationHandler;" + rt +
"import java.lang.reflect.Method;" + rt +
"public class $Proxy0 implements " + infc.getName() + " {" + rt +
"private InvocationHandler h;" + rt +
"public $Proxy0(InvocationHandler h) {" + rt +
"super();" + rt +
" this.h = h;" + rt +
"}" + rt +
methodstr + rt +
"}";
//2、产生代理类的java文件
//System.getProperty确定当前的系统属性。
String filename = System.getProperty("user.dir") + "/bin/com/ashes/myselfproxy2/$Proxy0.java";
System.out.println(filename); //E:\DesignPatterns\ProxyMode
File file = new File(filename);
FileUtils.write(file, str);
//3、编译
//拿到编译器
JavaCompiler compile = ToolProvider.getSystemJavaCompiler();
//文件管理者
StandardJavaFileManager fileMgr = compile.getStandardFileManager(null, null, null);
//获取文件
Iterable units = fileMgr.getJavaFileObjects(file);
//编译任务
CompilationTask t = compile.getTask(null, fileMgr, null, null, null, units);
//进行编译
t.call();
try {
fileMgr.close(); //关闭资源
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//load到内存
ClassLoader cl = ClassLoader.getSystemClassLoader();
Class c = cl.loadClass("com.ashes.myselfproxy2.$Proxy0");
System.out.println(c.getName());
//产生代理类对象并返回
/*Constructor ctr = c.getConstructor(infc);
return ctr.newInstance(new Car());*/
//Constructor 提供关于类的单个构造方法的信息以及对它的访问权限。
Constructor ctr = c.getConstructor(InvocationHandler.class);
return ctr.newInstance(h);
//return null;
}
}
代理类是在程序运行过程中创建的,一旦被创建就变成了常规类,与虚拟机中的任何其他类没有什么区别
四、cglib实现
Cglib是一个优秀的动态代理框架,它的底层使用ASM在内存中动态的生成被代理类的子类
和jdk实现的第一个不同就是我们需要导包
public class Work {
public void work() {
System.out.println("我要学习,吼吼吼.......");
}
}
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
public Object getProxy(Class clazz) {
//创建子类的类
enhancer.setSuperclass(clazz);
//设置回调
enhancer.setCallback(this);
return enhancer.create();//创建子类实例并返回
}
/* (non-Javadoc)
* @see net.sf.cglib.proxy.MethodInterceptor#intercept(java.lang.Object, java.lang.reflect.Method, java.lang.Object[], net.sf.cglib.proxy.MethodProxy)
* 拦截所有目标方法的调用
* obj 目标类的实例
* m 目标方法的反射对象
* args 方法的参数
* proxy 代理类的实例
*/
@Override
public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy)
throws Throwable {
System.out.println("日志开始.......");
//代理类调用父类的方法
proxy.invokeSuper(obj, args);
System.out.println("日志结束.....");
return null;
}
}
public class Client {
public static void main(String[] args) {
CglibProxy cp = new CglibProxy();
Work t = (Work) cp.getProxy(Work.class);
t.work();
}
}