代理模式
代理模式是常用的设计模式之一。《Head First 设计模式》中定义如下:代理模式,即为另一个对象提供一个替身或者占位符以控制对这个对象的访问。
说起代理模式,最熟悉的莫过于Java中的RMI了,即远程方法调用(Remote Method Invocation)。它能够让在某个Java虚拟机上的对象像调用本地对象一样调用另一个java 虚拟机中的对象上的方法。过程大致如下:
1. 客户对象调用的是本地的客户辅助对象(代理)
2. 客户辅助对象通过Socket调用远方的服务辅助对象(代理)
3. 服务辅助对象调用真正的服务对象获取真正的服务
这就是RMI的流程,也是代理模式的一个体现。
UML图
代理模式的类图如下:
这里Proxy和RealSubject实现了相同的接口,保证了实现的透明性。
实现
代理模式可以有两种实现的方式:静态代理、动态代理。
静态代理
所谓静态代理,是指在编译时就指定了代理类。另外,静态代理跟装饰者模式很像。其实,Java中我们常用的Thread和Runnable接口就是静态代理的实现。
public class RealRunnable implements Runnable {
@Override
public void run() {
// TODO
}
}
public class ClientClass {
public static void main(String[] args) {
Thread proxyThread = new Thread(new RealRunnable());
proxyThread.start();
}
}
使用静态代理时,我们要创建代理类和被代理类,这样会导致大量的代理类和重复代码出现,不利于维护。这个时候就用到更常用的动态代理了。
动态代理
Java在java.lang.reflect包中提供了代理支持,利用这个包可以在运行时动态地创建一个代理类,实现一个或多个接口,同时将方法的调用转发到你指定的类。因为代理类是在运行时创建的,因此称为:动态代理。
类图如下:
这里Java为你创建了Proxy类,不过程序还是要告诉Proxy需要做什么,因此我们需要在InvocationHandler中响应代理的调用,并请求实际工作的对象。
例如:
public class MyInvocationHandler implements InvocationHandler {
// 目标对象,完成我们的业务
private Object delegate;
// 真正的实现类
private Class realClass;
public MyInvocationHandler(Object delegate, Class realClass) {
this.delegate = delegate;
this.realClass = realClass;
}
/**
* 返回一个全新的对象(写法,基本上固定的),进行绑定
*/
public Object bind() {
return Proxy.newProxyInstance(this.delegate.getClass().getClassLoader(), this.delegate.getClass().getInterfaces(), this);
}
/**
* 程序会自动调用该方法,在改方法中添加附加操作。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO
}
}
这样我们就可以利用它代理我们的类了。代码中的Proxy.newProxyInstance 就是为JDK为我们生成动态的代理类的对象。Proxy类做了什么呢?查看Proxy源码如下:
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
throws IllegalArgumentException
{
// 省略
/*
* Look up or generate the designated proxy class.
*/
Class<?> cl = getProxyClass0(loader, intfs);
// 省略
}
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) {
if (interfaces.length > 65535) {
throw new IllegalArgumentException("interface limit exceeded");
}
// If the proxy class defined by the given loader implementing
// the given interfaces exists, this will simply return the cached copy;
// otherwise, it will create the proxy class via the ProxyClassFactory
return proxyClassCache.get(loader, interfaces);
}
意思就是说:JDK利用ProxyClassFactory 帮我们生成proxy class。查看内部类Proxy$ProxyClassFactory 发现有代码:
/**
* A factory function that generates, defines and returns the proxy class given
* the ClassLoader and array of interfaces.
*/
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// prefix for all proxy class names
private static final String proxyClassNamePrefix = "$Proxy";
// next number to use for generation of unique proxy class names
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
for (Class<?> intf : interfaces) {
/*
* Verify that the class loader resolves the name of this
* interface to the same Class object.
*/
Class<?> interfaceClass = null;
try {
interfaceClass = Class.forName(intf.getName(), false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != intf) {
throw new IllegalArgumentException(
intf + " is not visible from class loader");
}
/*
* Verify that the Class object actually represents an
* interface.
*/
if (!interfaceClass.isInterface()) { // 只能代理 接口
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* Verify that this interface is not a duplicate.
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null; // package to define proxy class in
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
/*
* Record the package of a non-public proxy interface so that the
* proxy class will be defined in the same package. Verify that
* all non-public proxy interfaces are in the same package.
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {
accessFlags = Modifier.FINAL;
String name = intf.getName();
int n = name.lastIndexOf('.');
String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
if (proxyPkg == null) {
proxyPkg = pkg;
} else if (!pkg.equals(proxyPkg)) {
throw new IllegalArgumentException(
"non-public interfaces from different packages");
}
}
}
if (proxyPkg == null) {
// if no non-public proxy interfaces, use com.sun.proxy package
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* Choose a name for the proxy class to generate.
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* Generate the specified proxy class.
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(// 生成代理类文件,并用类加载器加载
proxyName, interfaces, accessFlags);
try {
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
/*
* A ClassFormatError here means that (barring bugs in the
* proxy class generation code) there was some other
* invalid aspect of the arguments supplied to the proxy
* class creation (such as virtual machine limitations
* exceeded).
*/
throw new IllegalArgumentException(e.toString());
}
}
}
上边的代码做了三件事
1.校验接口。从第36行中可知,动态代理只能代理接口。
2.根据接口的方法,生成类的文件。(实现接口)
3.用类加载器加载生成的代理类
如果要代理类,如Spring AOP中,可以选择 CGLIB.
CGLIB
CGLIB(Code Generation Library)。CGLIB是一个强大的、高性能的代码生成库。其被广泛应用于AOP框架(Spring、dynaop)中,用以提供方法拦截操作。CGLIB作为一个开源项目,源码查看Github。从上边知道,JDK动态代理虽然简单易用,但是只能代理接口。CGLIB与之不同,它采用继承生成一个子类来实现代理。因此,可以对类进行代理。
新建一个pom.xml,加入以下maven依赖。
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2.6</version>
</dependency>
执行命令,查看其依赖树:
mvn dependency:tree
可以看到有以下传递依赖:
[INFO] \- cglib:cglib:jar:3.2.6:compile
[INFO] +- org.ow2.asm:asm:jar:6.0:compile
[INFO] \- org.apache.ant:ant:jar:1.9.6:compile
[INFO] \- org.apache.ant:ant-launcher:jar:1.9.6:compile
发现它依赖一个叫ASM的类库,从官网上可以知道,ASM是一个通用的Java字节码操作和分析框架。它可以直接以二进制形式来修改现有的类或动态地生成类,。ASM提供了一些通用的字节码转换和分析算法,可以从这些算法中构建自定义复杂的转换和代码分析工具。
好了,看例子:
public class MyFacadeCglib implements MethodInterceptor {
// 业务类对象,供代理方法中进行真正的业务方法调用
private Object target;
// 相当于JDK动态代理中的绑定
public Object getInstance(Object target) {
// 给业务对象赋值
this.target = target;
// 创建加强器,用来创建动态代理类
Enhancer enhancer = new Enhancer();
// 为加强器指定要代理的业务类(即:为下面生成的代理类指定父类)
enhancer.setSuperclass(this.target.getClass());
// 设置回调:对于代理类上所有方法的调用,都会调用CallBack,而Callback则需要实现intercept()方法进行拦
enhancer.setCallback(this);
// 创建动态代理类对象并返回
return enhancer.create();
}
// 实现回调方法
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
// TODO something before
proxy.invokeSuper(obj, args); // 调用业务类(父类中)的方法
// TODO something after
return null;
}
}
而实际的业务对象类很简单:
public class RealSubjectImpl {
public void printSubject() {
System.out.println("RealSubject was called");
}
}
然后使用代理:
public static void main(String[] args) {
RealSubjectImpl impl = new RealSubjectImpl();
MyFacadeCglib cglib = new MyFacadeCglib();
Object obj = cglib.getInstance(impl);
System.out.println(obj.getClass().getName());
RealSubjectImpl bookCglib = (RealSubjectImpl)obj;
System.out.println(bookCglib.getClass().getName());
bookCglib.printSubject();
}
输出如下:
com.proxy.RealSubjectImpl$$EnhancerByCGLIB$$27bd53ef
com.proxy.RealSubjectImpl$$EnhancerByCGLIB$$27bd53ef
RealSubject was called
也就是说生成的代理类是com.proxy.RealSubjectImpl$$EnhancerByCGLIB$$27bd53ef,且能向上转型成RealSubjectImpl,即其子类。