什么是代理?
代理(Proxy)是一种设计模式,提供了间接对目标对象进行访问的方式;即通过代理对象访问目标对象.这样做的好处是:可以在目标对象实现的功能上,增加额外的功能补充,即扩展目标对象的功能.
这就符合了设计模式的开闭原则,即在对既有代码不改动的情况下进行功能的扩展。
一般来说代理包含三部分:
- 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
- 真实角色:需要实现抽象角色接口,定义真实角色所要实现的业务逻辑,以便供给代理角色使用,也就是真正的业务逻辑实现在此。
- 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作,将统一的流程控制都放在代理角色中实现。
三个类的类图如下所示:
静态代理
静态代理相对简单,下面通过一个例子说明:
/**
* 抽象角色
*/
public interface User {
void doAction();
}
/**
* 真实角色
*/
public class RealUser implements User {
@Override
public void doAction() {
System.out.println("做具体的业务逻辑");
}
}
/**
* 代理角色
*/
public class ProxyUser implements User {
private User iUser;
public ProxyUser(User iUser) {
this.iUser = iUser;
}
private void before(){
System.out.println("业务逻辑开始之前的准备工作");
}
@Override
public void doAction() {
before();
iUser.doAction();
after();
}
private void after(){
System.out.println("业务逻辑结束之后的收尾工作");
}
}
最后通过一个main方法来进行测试:
public static void main(String[] args) {
RealUser user = new RealUser();
ProxyUser proxy = new ProxyUser(user);
proxy.doAction();
}
}
输出如下:
如果RealUser实现了多个接口,为了代理RealUser,就需要创建多个代理类,为了解决这种情况,就需要看下面的动态代理。
动态代理
先看看动态代理的实现:
/**
* 代理程序处理类
*/
public class ProxyHandler implements InvocationHandler {
private Object target;
public ProxyHandler(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object result = method.invoke(target, args);
after();
return result;
}
private void before(){
System.out.println("业务逻辑开始之前的准备工作");
}
private void after(){
System.out.println("业务逻辑结束之后的收尾工作");
}
}
使用如下:
public static void main(String[] args) {
RealUser realUser = new RealUser();
InvocationHandler handler = new ProxyHandler(realUser);
User user = (User) Proxy.newProxyInstance(realUser.getClass().getClassLoader(), new Class[]{User.class}, handler);
user.doAction();
}
输出内容和静态代理一样,这里就不再给出,下面解释一下具体方法的参数和含义:
- ProxyHandler的invoke方法
invoke()方法用来处理代理类实例的方法调用并返回结果,实现自己的代理逻辑,第一个参数是代理类实例,第二个参数是需要代理的方法,第三个参数是方法的参数数组 - Proxy.newProxyInstance方法
newProxyInstance()方法用来返回真实角色实例,第一个参数是真实角色的类加载器,第二个参数是需要代理的接口,可以是多个,第三个是代理的回调,当调用通过动态代理生成的实例方法时,会回调到invoke方法,然后执行真实角色的该方法。
简单分析一下动态代理的源码:
@CallerSensitive
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);
}
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
/*
* Invoke its constructor with the designated invocation handler.
*/
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});
} catch (IllegalAccessException|InstantiationException e) {
throw new InternalError(e.toString(), e);
} catch (InvocationTargetException e) {
Throwable t = e.getCause();
if (t instanceof RuntimeException) {
throw (RuntimeException) t;
} else {
throw new InternalError(t.toString(), t);
}
} catch (NoSuchMethodException e) {
throw new InternalError(e.toString(), e);
}
}
首先会对需要代理的接口进行克隆,然后字节码文件获取代理接口的Class,最后再通过反射,生成对象实例。
通过代码生成动态代理的class文件,代码如下:
private static void proxy() {
try {
String name = User.class.getName() + "$Proxy0";
byte[] bytes = ProxyGenerator.generateProxyClass(name,new Class[]{User.class});
FileOutputStream outputStream = new FileOutputStream(name+".class");
outputStream.write(bytes);
outputStream.close();
}catch (Exception e){
System.out.println(e.toString());
}
}
生成的class文件内容如下:
public final class User$Proxy0 extends Proxy implements User {
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
public User$Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {}
public final String toString() throws {}
public final void doAction() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw new UndeclaredThrowableException(var3);
}
}
public final int hashCode() throws {}
static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m2 = Class.forName("java.lang.Object").getMethod("toString");
m3 = Class.forName("com.hanshow.User").getMethod("doAction");
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw new NoClassDefFoundError(var3.getMessage());
}
}
}
hashcode,equals,toString和doAction类似,方法内容不再贴出,当我们调用通过动态代理生成的实例的doAction()方法时,实际调用的是super.h.invoke()方法,h是通过 User$Proxy0构造方法传进来的InvocationHandler ,m3是静态代码块中通过反射获得的doAction方法,最终会调用到ProxyHandler 的invoke方法。