1、概念
为其他对象提供一种代理以控制这个对象的访问,在某些情况下一个对象不能直接访问那个对象时,代理就起到了客户端和被代理对象(委托类)中介作用。 代理类和委托类都有同样接口。
可以不用动原来类的逻辑,再次增加一些功能,符合开闭原则。真正的业务还是交给被代理对象处理的,因此在其委托被代理对象处理业务前后实现一些公共逻辑服务,例如加入缓存或日志等功能,无须修改原来的类就可以使用代理进行实现。
2、示例程序
静态代理模式:
类图:
Printable: 代理类和被代理类实现同样的接口
PrintProxy:代理类,里面有被代理类,具体逻辑委托被代理类进行处理
Printer:被代理类,可以在其内做一些访问权限控制,额外的业务处理,日志处理
Main:看到的是代理类,并不知道具体处理业务逻辑的类,降低耦合性
代码实现:
Printable.java
package proxy.print;
public interface Printable {
public void setName(String name);
public String getName();
public void print(int count);
public void SetColor(int color);
}
Printer.java
package proxy.print;
public class Printer implements Printable {
private String name;
private int color;
@Override
public void setName(String name) {
this.name = name;
}
@Override
public String getName() {
return this.name;
}
@Override
public void print(int count) {
System.out.println("---------" + this.name + "------");
for (int i = 0; i < count; i++) {
System.out.println("....");
try {
Thread.sleep(1000L);
} catch (Exception e) {
}
}
System.out.println("-------------------");
}
@Override
public void SetColor(int color) {
this.color = color;
}
}
PrintProxy.java
package proxy.print;
public class PrintProxy implements Printable {
private Printable printable;
public void setPrintable(Printable printable) {
this.printable = printable;
}
@Override
public void setName(String name) {
System.out.println("开始执行setName()");
long currentTimeMillis = System.currentTimeMillis();
printable.setName(name);
System.out.println("执行完成,执行耗时:" + (System.currentTimeMillis() - currentTimeMillis) + "ms");
}
@Override
public String getName() {
return printable.getName();
}
@Override
public void print(int count) {
System.out.println("开始执行print()");
long currentTimeMillis = System.currentTimeMillis();
printable.print(count);
System.out.println("执行完成,执行耗时:" + (System.currentTimeMillis() - currentTimeMillis) + "ms");
}
@Override
public void SetColor(int color) {
printable.SetColor(color);
}
}
Main.java
package proxy.print;
public class Main {
public static void main(String[] args) {
PrintProxy printProxy = new PrintProxy();
printProxy.setPrintable(new Printer());
printProxy.setName("Case 01");
printProxy.print(3);
}
}
Jdk动态代理模式:
要求被代理类必须要实现接口,因为JDK动代理实现是通过实现接口方式来实现的。需要实现 InvocationHandler接口(在invoke方法中实现一些额外的逻辑,添加一些新功能),通过
Proxy.newProxyInstance(Hello.class.getClassLoader(), HelloImpl.class.getInterfaces(), new MyInvocationHandler(new HelloImpl()))
实现步骤:
1.定义一个接口Hello
2.编写该接口的实现类HelloImpl
3.编写InvocationHandler接口的实现类MyInvocationHandler,构造h类对象的时候可以把要代理的对象target传入,target完成实际的动作。在里面的invoke方法里编写自己想实现的逻辑,然后再调用实际要完成的动作就可以。
4.调用Proxy.newProxyInstance方法,传递的三个参数分别是代理类的类加载器、代理类要实现的接口列表、InvocationHandler实现类的实例。
这样就生成了HelloImpl类 的 对 象 ,由于HelloImpl类实现了ProxyObj接口,所以可以将对象强制转型成Hello
再说一下Proxy.newProxyInstance方法的实际过程:
1.使用传入的InvocationHandler实例参数将Proxy类的h实例初始化,注意,如果传入空对象的话,会抛出空指针错误,即h不能为空。
2.运行时生成代理Class,即$Proxy0
3.利用上面动态生成的$Proxy0类,构造出该类的对象,并返回,完成sayHello()方法。
类图:
Hello.java
package proxy.jdk;
public interface Hello {
public void sayHello();
}
HelloImpl.java
package proxy.jdk;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import static java.lang.Class.forName;
public class HelloImpl implements Hello {
@Override
public void sayHello() {
System.out.println("Hello, xixi!");
}
/*
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException, ClassNotFoundException {
// Constructor<HelloImpl> constructor1 = HelloImpl.class.getConstructor();
// Constructor<HelloImpl> constructor2 = HelloImpl.class.getConstructor(String.class);
// Constructor<HelloImpl> constructor3 = HelloImpl.class.getConstructor(Integer.class);
// System.out.println("do nothing");
HelloImpl hello = new HelloImpl();
Method method = Hello.class.getMethod("sayHello");
method.invoke(hello);
System.out.println("do nothing");
}*/
}
MyInvocationHandler.java
package proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
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("------插入前置通知代码-------------");
// 执行相应的目标方法
Object rs = method.invoke(target, args);
System.out.println("------插入后置处理代码-------------");
return rs;
}
}
Main.java
package proxy.jdk;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Proxy;
public class Main {
public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
// Hello hello = new HelloImpl();
// hello.sayHello();
// =========================第一种==========================
// 1、生成$Proxy0的class文件
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
// 2、获取动态代理类
Class proxyClazz = Proxy.getProxyClass(HelloImpl.class.getClassLoader(), Hello.class);
// 3、获得代理类的构造函数,并传入参数类型InvocationHandler.class
Constructor constructor = proxyClazz.getConstructor(InvocationHandler.class);
// 4、通过构造函数来创建动态代理对象,将自定义的InvocationHandler实例传入
Hello Hello1 = (Hello) constructor.newInstance(new MyInvocationHandler(new HelloImpl()));
// 5、通过代理对象调用目标方法
Hello1.sayHello();
//Object proxyInstance = Proxy.newProxyInstance(Hello.class.getClassLoader(), HelloImpl.class.getInterfaces(), new MyInvocationHandler(new HelloImpl()));
// ==========================第二种=============================
/**
* Proxy类中还有个将2~4步骤封装好的简便方法来创建动态代理对象,
*其方法签名为:newProxyInstance(ClassLoader loader,Class<?>[] instance, InvocationHandler h)
*/
Hello Hello2 = (Hello) Proxy.newProxyInstance(Hello.class.getClassLoader(), // 加载接口的类加载器
new Class[]{Hello.class}, // 一组接口
new MyInvocationHandler(new HelloImpl())); // 自定义的InvocationHandler
Hello2.sayHello();
}
}
$Proxy0
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.sun.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import proxy.jdk.Hello;
public final class $Proxy0 extends Proxy implements Hello {
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});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final void sayHello() 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);
} 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"));
m3 = Class.forName("proxy.jdk.Hello").getMethod("sayHello");
m2 = Class.forName("java.lang.Object").getMethod("toString");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
CGLIB动态代理:
CGLIB(Code Generation Library)实现动态代理,并不要求被代理类必须实现接口,底层采用asm字节码生成框架生成代理类字节码(该代理类继承了被代理类)。所以被代理类一定不能定义为final class并且对于final 方法不能被代理。
实现代码:
package proxy.cglib;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import proxy.jdk.Hello;
import proxy.jdk.HelloImpl;
import java.lang.reflect.Method;
public class CglibProxyFactory implements MethodInterceptor {
private Hello target;
public Object getProxy(Hello hello) {
target = hello;
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(hello.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("Cglib动态代理开始执行");
Object invoke = method.invoke(target, objects);
System.out.println("Cglib动态代理执行完成");
return invoke;
}
public static void main(String[] args) {
// System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
CglibProxyFactory proxyFactory = new CglibProxyFactory();
Hello proxy = (Hello) proxyFactory.getProxy(new HelloImpl());
proxy.sayHello();
}
}