写在前面的话
代理设计模式是常用的设计模式的一种,设计模式是别人的经验规律总结,某些方面,它可以让你少走一点弯路,但是学习设计模式,一定要带着问题来思考,结合实用场景高效率的学习,而不是生搬硬套。
为什么使用代理?
代理可以使得代码之间的耦合度降低,软件设计的一条原则就是高内聚低耦合,说白了就是分工明确。aop的思路就是借用动态代理实现的,像spring中的日志管理和事务管理等。每一个需要织入的点,同时这个点和业务是无关的,这个时候借助代理,可以使得在不改变既有业务逻辑的同时,增强代码的功能性。
局限
代理最大的局限就是需要一个接口,并不是每个类都会有接口。
静态代理
静态代理需要一个接口,一个代理类,一个业务类,代理类和业务类都实现了这个接口。假定有一个数据操作的接口AccountDao,里面有一个save()的方法, 这个接口有一个实现类AccountDaoImpl。
package proxy;
public interface AccountDao {
public void save();
}
package proxy;
public class AccountDaoImpl implements AccountDao{
@Override
public void save() {
System.out.println("新增账户");
}
}
现在需要在调用save方法的时候增加一些调用的日志,最直观的方法是直接在实现类上增加日志的逻辑,但是当实现类是在一个jar文件中的时候就变得无从下手了,而且这种做法使得代码耦合度变高,不符合高内聚低耦合的特性。
考虑使用另一个实现类:
package proxy;
public class AccountDaoProxy implements AccountDao{
private AccountDao dao;
public AccountDaoProxy(AccountDao dao) {
this.dao = dao;
}
@Override
public void save() {
//打印日志
System.out.println("新增入库数据。");
this.dao.save();
}
public static void main(String args[]) {
AccountDaoImpl dao = new AccountDaoImpl();
AccountDaoProxy proxy = new AccountDaoProxy(dao);
proxy.save();
}
}
这个实现类就是代理类,这种代理方式 需要给每个业务类生成自己的代理类,当给另外几个不同的方法增加相同的代码的时候操作变得很繁琐,比如另一个UserInfoDao也有update()的方法也想有这样的日志增强,这样就不得不再写一个代理类,为了解决这个办法,动态代理出现了。
动态代理
动态代理需要借助jdk反射包的InvocationHandler接口和Proxy类,直接上代码:
public class DaoInvocation<T> implements InvocationHandler{
//封装调用的实际对象
private T t;
public DaoInvocation(T t) {
this.t = t;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("新增 入库数据。");
Object result = method.invoke(t, args);
return result;
}
}
调用:
public static void main(String args[]) {
InvocationHandler invocake = new DaoInvocation<AccountDaoImpl>(new AccountDaoImpl());
//三个参数,类加载器,接口数组,InvocationHandler
AccountDao dao = (AccountDao)Proxy.newProxyInstance(AccountDao.class.getClassLoader(), new Class<?>[]{AccountDao.class}, invocake);
dao.save();
}
newProxyInstance 接受的三个参数为:classLoader, 接口数组,以及InvocationHandler的实现类。这看起来似乎有点摸不着头脑,为什么这么写就实现了代理的功能呢?
别慌,先看看newProxyInstance做了什么操作:
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);
}
}
类是字节码文件,借助字节数组把这个内存中的类打印到本地文件,然后我们再借助反编译的手段即可查阅这个动态代理的类:
借助代理类可供外部调用的的方法getProxyClass(),注意此处不是getProxyClass0(),后者是private方法,但是前者最后也是调用了后者,前者调用前做了权限的校验。
public static void main(String args[]) throws Exception{
//在内存中生成了动态的代理类
Class<?> c = Proxy.getProxyClass(AccountDao.class.getClassLoader(), AccountDao.class);
//字节码文件转换为字节数组
byte b[] = ProxyGenerator.generateProxyClass("MyProxy", new Class[] {c});
//输出到文件
FileOutputStream fos = new FileOutputStream(new File("D:\\code\\MyProxy.class"));
fos.write(b);
fos.close();
}
使用反编译工具后把关键代码贴上来:
public final void save() throws {
try {
super.h.invoke(this, m3, (Object[])null);
} catch (RuntimeException | Error arg1) {
throw arg1;
} catch (Throwable arg2) {
throw new UndeclaredThrowableException(arg2);
}
}
这个h的实例化是在父类Proxy中完成的:
public class Proxy implements java.io.Serializable {
protected InvocationHandler h;
protected Proxy(InvocationHandler h) {
Objects.requireNonNull(h);
this.h = h;
}
}
结合上面Proxy的newProxyInstance方法,知道调用动态代理类的save方法的时候,最后调用的就是InvocationHandler里面的invoke方法。