1 分类
代理就是帮目标对象去完成他应该做,但是不想或者不擅长做的事。增强对象的功能。
- 代理模式分为静态代理和动态代理
- 静态代理:编写一个代理类,去代理目标对象
- 动态代理:在运行期间,通过反射产生一个代理对象
2 静态代理
代理类和目标类实现同一个接口
public interface Moveable{
void move();
void stop();
}
public class Tank implements Moveable{
public void move(){
System.out.println("Tank move……");
try{
Thread.sleep(1000)
}catch(InterruptedException e){
e.printStackTrace();
}
}
public void stop(){
System.out.println("Tank move……");
try{
Thread.sleep(1000)
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
public class TanKTimeProxy implements Moveable{
private Moveable tank;
public TankTimeProxy(Moveable tank){
super();
this.tank = tank;
}
@override
public void move(){
long start= System.currentTimeMillis();
tank.move();
long end = System.currentTimeMillis();
System.out.println("time:"+(end-start));
}
}
public class TankLogProxy implements Moveable{
private Moveable tank;
public TankLogProxy(Moveable tank){
super();
this.tank = tank;
}
@override
public void move(){
System.out.println("tank start...");
tank.move();
System.out.println("tank end...");
}
}
public class Test{
public static void main(String[] arg0){
Tank tank = new Tank();
TankTimeProxy ttp = new TankTimeProxy(tank);
TankLogProxy tlp = new TankLogProxy(ttp);
Moveable m =ttp;
m.move();
}
}
3 动态代理实现方式
- JDK实现:JDK Complier API 基于接口的,目标对象必须实现一个接口
- cglib实现:通过继承的方式,产生一个子类,子类即代理类。
- ASM
3.1 JDK动态代理
相关类、接口
- Proxy用于动态生成代理类,JVM在运行时动态生成字节码,而非预存在任何一个.class文件,通过类加载器将类的字节码装载到jvm中。
- InvocationHandler,包含一个invoke方法,实现对委托类的代理。
使用jdk动态代理使用步骤:
- 创建被代理的接口和类
- 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理
- 调用Proxy静态方法,创建代理类,并生成相应的代理对象
- 使用代理
3.1.1 demo
1.目标对象HelloWorldServiceImp,实现HelloWorldService接口
public class HelloWorldServiceImp implements HelloWorldService {
public void doSome(){
System.out.println("hello word !");
}
}
public interface HelloWorldService {
public void doSome();
}
2.实现InvocationHandler,对目标对象实现功能增强
public class MyInvocationHandler implements InvocationHandler {
private Object target;
public MyInvocationHandler(Object target){
this.target =target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Proxy: "+proxy.getClass().getName());
Object result = method.invoke(target,args);
System.out.println(method.getName()+" :"+result);
return null;
}
}
3.测试,调用Proxy创建代理类
public class ProxyTest {
@Test
public void Test() throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException {
// 生成代理类的class对象
HelloWorldService service =new HelloWorldServiceImp();
Class<?> clazz = Proxy.getProxyClass(action.getClass().getClassLoader(), action
.getClass().getInterfaces());
// 创建InvocationHandler
InvocationHandler myInvocationHandler = new MyInvocationHandler(service);
// 获取代理类的构造器对象
Constructor constructor = null;
constructor = clazz.getConstructor(new Class[] {InvocationHandler.class});
// 反射创建代理对象
HelloWorldService proxy1 = (HelloWorldService)constructor.newInstance(myInvocationHandler);
proxy1.doSome();
// 方法二:创建代理对象
HelloWorldService proxy2= (HelloWorldService)Proxy.newProxyInstance(Action.class.getClassLoader(),service.getClass().getInterfaces(),myInvocationHandler);
proxy2.doSome();
}
}
4.输出结果
Proxy: com.sun.proxy.$Proxy4
hello word !
doSome :null
Proxy: com.sun.proxy.$Proxy4
hello word !
doSome :null
3.1.2 JDK动态代理原理
1.如何生存动态代理类。
- 运行时动态编译字节码
(1)使用拼凑字符串的方式将内存中的代理类拼出来,利用反射获取目标类中的方法和构造函数等
(2)使用IO将拼凑的代理类写入到文件中
(3)编译生成的java文件得到字节码文件
(4)加载代理类到JVM
(5)返回被代理类的实例对象
public class MyProxy {
static String rt = "\r\n";
public static Object newProxyInstance(ClassLoader loader, Class interfaces, InvocationHandler handler) {
try {
Method[] methods = interfaces.getMethods();
//01.使用拼凑字符串的方式将内存中的代理类拼出来
String proxyClass = "package proxy;" + rt + "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfaces.getName() + "{" + rt + "MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h) {" + rt + "this.h = h;" + rt + "}"
+ getMethodString(methods, interfaces) + rt + "}";
//02.使用IO将拼凑的代理类写入到文件中
String filePathName = "C:\\Users\\csdc\\Workspaces\\IDEA\\study\\designpattern\\src\\proxy\\$Proxy0.java";
File file = new File(filePathName);
FileWriter fw = new FileWriter(file);
fw.write(proxyClass);
fw.flush();
fw.close();
//03.编译上面生成的java文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);//文件管理器
Iterable javaFileObjects = fileManager.getJavaFileObjects(filePathName);//获取java文件
JavaCompiler.CompilationTask task = compiler.getTask(null, fileManager, null, null, null, javaFileObjects);//获取编译任务
task.call();//编译
fileManager.close();//关闭文件管理器
/**
* 此时被编译后的class文件还在硬盘之中!它应该存在于JVM内存中!
* 所以要把硬盘中class文件加载到JVM内存中去!
*/
//04.把硬盘中的class文件加载到内存中去,要使用ClassLoader
URL[] urls = new URL[]{new URL("file:/" + System.getProperty("user.dir" + "/src/"))};
loader = new URLClassLoader(urls);
//proxy0Clazz:就是内存中代理类的class对象
Class<?> proxy0Clazz = loader.loadClass("proxy.$Proxy0");//返回被代理类的class对象
/**
* public $Proxy0(MyInvocationHandler h) {..}
* 要通过构造器的参数将handler对象的引用传进来
*/
Constructor<?> constructor = proxy0Clazz.getConstructor(handler.getClass());
Object instance = constructor.newInstance(handler);//返回内存中代理类实例
return instance;
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return null;
}
private static String getMethodString(Method[] methods, Class interfaces) {
String proxyMe = "";
for (Method method : methods) {
proxyMe += "public void " + method.getName() + "() {" + rt + "try{"+rt+"Method md = "
+ interfaces.getName() + ".class.getMethod(\"" + method.getName() + "\",new Class[]{});" + rt
+ "this.h.invoke(this,md,null);" + rt+"}catch(Exception e){"+rt+"e.printStackTrace();"+rt+
"}catch(Throwable t){"+rt+"t.printStackTrace();"+"}"+rt+"}"+rt;
}
return proxyMe;
}
}
生存的$Proxy0文件
package proxy;
import java.lang.reflect.Method;
public class $Proxy0 implements proxy.HelloWorldService {
MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h) {
this.h = h;
}
public void doSome() {
try {
Method md = proxy.HelloWorldService.class.getMethod("doSome", new Class[]{});
this.h.invoke(this, md, null);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable t) {
t.printStackTrace();
}
}
}