代理模式(Proxy Pattern)是指为其他对象提供一种代理, 以控制对这个对象的访问。代理对象在客户端和目标对象之间起到中介作用。
代理模式属于结构型设计模式,属于GOF23种设计模式之一
代理模式可以分为静态代理和动态代理两种类型,而动态代理中又分为JDK动态代理和CGLIB代理两种。
标准的静态代理模式需要定义一个接口,然后被代理对象与代理对象均需要重写接口方法,被代理对象本身只需要实现真正的业务逻辑,而代理对象中的方法需要调用被代理对象的方法,且可以在调用前后新增一些其他逻辑处理。
代理对象中需要显式声明被代理对象,也就是需要持有被代理对象的引用,一般通过代理对象的构造器传入被代理对象,以达到持有被代理对象的目的
1、定义一个标准接口
package com.zwx.design.pattern.proxy;
public interface Travel {
void buyTrainTickey();//买火车票
}
2、定义一个被代理对象实现接口Travel,并重写接口中的方法:
package com.zwx.design.pattern.proxy.staticProxy;
import com.zwx.design.pattern.proxy.Travel;
public class TravelPerson implements Travel{
@Override
public void buyTrainTickey() {
System.out.println("北京到上海");
System.out.println("早上9:00出发");
}
}
3、定义一个代理对象实现接口Travel,并重写接口中的方法,此代理对象需要持有被代理对象的引用,可以通过构造器传入:
package com.zwx.design.pattern.proxy.staticProxy;
import com.zwx.design.pattern.proxy.Travel;
public class TravelAgency implements Travel{
private TravelPerson travelPerson;//持有被代理对象
public TravelAgency(TravelPerson travelPerson) {
this.travelPerson = travelPerson;
}
@Override
public void buyTrainTickey(){
before();
this.travelPerson.buyTrainTickey();//调用被代理对象的方法
after();
}
private void before() {
System.out.println("付定金");
}
private void after() {
System.out.println("付尾款");
}
}
4、建立测试类测试:
package com.zwx.design.pattern.proxy.staticProxy;
public class TestStaticProxy {
public static void main(String[] args) {
TravelAgency travelAgency = new TravelAgency(new TravelPerson());
travelAgency.buyTrainTickey();
}
}
输出结果为:
付定金
北京到上海
早上9:00出发
付尾款
为了解决静态代理的局限性,所以我们就需要有动态代理来动态分配代理对象。
JDK内置了一种动态代理的实现方式,主要有以下两个条件:
1、创建一个被代理对象JdkTravelPerson实现Travel接口:
package com.zwx.design.pattern.proxy.dynamicProxy.jdkProxy;
import com.zwx.design.pattern.proxy.Travel;
public class JdkTravelPerson implements Travel{
@Override
public void buyTrainTickey() {
System.out.println("北京到上海");
System.out.println("早上9:00出发");
}
}
2、创建一个JdkTravelAgency代理类,实现InvocationHandler接口:
package com.zwx.design.pattern.proxy.dynamicProxy.jdkProxy;
import com.zwx.design.pattern.proxy.Travel;
import javafx.beans.binding.ObjectExpression;
import javax.sound.midi.Track;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkTravelAgency implements InvocationHandler {
private Object target;//被代理对象
public Object getInstance(Object target){//动态获取代理对象
this.target = target;
Class<?> clazz = target.getClass();
return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
Object obj = method.invoke(this.target,args);//调用代理对象方法
after();
return obj;
}
private void before() {
System.out.println("付定金");
}
private void after() {
System.out.println("付尾款");
}
}
3、创建测试类:
package com.zwx.design.pattern.proxy.dynamicProxy.jdkProxy;
import com.zwx.design.pattern.proxy.Travel;
public class TestJdkProxy {
public static void main(String[] args){
Travel travel = (Travel) new JdkTravelAgency().getInstance(new JdkTravelPerson());
travel.buyTrainTickey();
}
}
运行结果:
付定金
北京到上海
早上9:00出发
付尾款
首先来看一下上面示例中的类图关系:
动态代理最核心的就是如何生成代理类,所以最核心的逻辑是在JdkTravelAgency 中的getInstance方法调用的Proxy.newProxyInstance方法。
我们先把上面示例中的代理类通过ProxyGenerator类提供的方法generateProxyClass,生成出来后反编译看一下代理类究竟是什么样子的一个类:
package com.zwx.design.pattern.proxy.dynamicProxy.jdkProxy;
import com.zwx.design.pattern.proxy.Travel;
import sun.misc.ProxyGenerator;
import java.io.FileOutputStream;
import java.io.IOException;
public class TestJdkProxy {
public static void main(String[] args){
try{
byte[] bytes = ProxyGenerator.generateProxyClass("$proxy0",new Class[]{Travel.class});//生成代理类
FileOutputStream out = new FileOutputStream("G:\\$proxy0.class");
out.write(bytes);
out.flush();
out.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
然后将$proxy0反编译(反编译工具用的是jad,jd-gui反编译出来的有点小问题):
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.
// Jad home page: http://www.kpdus.com/jad.html
// Decompiler options: packimports(3)
import com.zwx.design.pattern.proxy.Travel;
import java.lang.reflect.*;
public final class $proxy0 extends Proxy
implements Travel
{
public $proxy0(InvocationHandler invocationhandler)
{
super(invocationhandler);
}
public final boolean equals(Object obj)
{
try
{
return ((Boolean)super.h.invoke(this, m1, new Object[] {
obj
})).booleanValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final String toString()
{
try
{
return (String)super.h.invoke(this, m2, null);
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final void buyTrainTickey()
{
try
{
super.h.invoke(this, m3, null);
return;
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) { }
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m2;
private static Method m3;
private static Method m0;
static
{
try
{
m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
Class.forName("java.lang.Object")
});
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.zwx.design.pattern.proxy.Travel").getMethod("buyTrainTickey", new Class[0]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
我们可以看到,生成的代理类 $proxy0(原类为TravelPerson),自动帮我们继承了Proxy类,然后定义了m0-m4中的4个方法,其中,m0,m1,m4这三个方法是Object类提供的,而m3方法是我们自己定义的接口中的方法。
我们看到buyTrainTickey方法中,调用了super.h.invoke(this, m3, null)方法。
h是什么呢?h实际上是其父类Proxy中的持有的InvocationHandler对象:
而Proxy里面的h就是我们传进去的JdkTravelAgency对象,所以最终就是调用了我们自己的方法。
@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);
}
}
这个方法就是三步:
getProxyClass0方法获得代理类的字节码文件,会先去WeakCache缓存中获取,如果获取不到,则通过Proxy类中的内部工厂类ProxyClassFactory获取:
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) {
/*
* 验证类加载器是否可以将接口名称解析为相同的Class对象
*/
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");
}
/*
* 校验一下加载出来的Class对象是否是一个接口
*/
if (!interfaceClass.isInterface()) {
throw new IllegalArgumentException(
interfaceClass.getName() + " is not an interface");
}
/*
* 校验接口是否重复
*/
if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
throw new IllegalArgumentException(
"repeated interface: " + interfaceClass.getName());
}
}
String proxyPkg = null;// 代理类的包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;//访问标志
/*
* 记录非public类型接口的包,所以代理类将会被定义在同一个包下
* 校验所有的非public代理接口是否在同一个包下
*/
for (Class<?> intf : interfaces) {
int flags = intf.getModifiers();
if (!Modifier.isPublic(flags)) {//如果不是public类型
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) {//默认包名
// public static final String PROXY_PACKAGE = "com.sun.proxy";
proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
}
/*
* 生成代理类名:包名+$proxy+num
*/
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
/*
* 生成代理类的byte数组
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//将byte数组通过类加载器加载成为Class字节码文件返回
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
通过上面的源码,可以得到JDK动态代理分为以下几步:
所以说JDK动态代理的核心是通过重写被代理对象所实现的接口中的方法来重新生成代理类来实现的,那么假如被代理对象没有实现接口呢?那么这时候就需要CGLIB动态代理了。
JDK动态代理是通过重写被代理对象实现的接口中的方法来实现,而CGLIB是通过继承被代理对象来实现,和JDK动态代理需要实现指定接口一样,CGLIB也要求代理对象必须要实现MethodInterceptor接口,并重写其唯一的方法intercept。
注意:因为CGLIB是通过继承目标类来重写其方法来实现的,故而如果是final方法则无法被重写,也就是无法被代理。
1、引入依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib-nodep</artifactId>
<version>2.2</version>
</dependency>
2、创建一个被代理对象(不需要实现接口):
package com.zwx.design.pattern.proxy.dynamicProxy.cglibProxy;
public class CglibTravelPerson{
public void buyTrainTickey() {
System.out.println("cglib:北京到上海");
System.out.println("cglib:早上9:00出发");
}
}
3、建立一个代理对象实现MethodInterceptor接口:
package com.zwx.design.pattern.proxy.dynamicProxy.cglibProxy;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibTravelAgency implements MethodInterceptor {
public Object getInstance(Class<?> clazz){
Enhancer enhancer = new Enhancer();//相当于JDK动态代理中的Proxy类
enhancer.setSuperclass(clazz);//设置为即将生成的代理类的父类
enhancer.setCallback(this);//设置回调对象
return enhancer.create();//相当于JDK动态代理的Proxy.newProxyInstance方法,生成新的字节码文件,并加载到JVM中
}
/**
*
* @param o - CBLIG生成的代理对象
* @param method - 被代理对象中被拦截的方法
* @param objects - 方法中的参数
* @param methodProxy - 代理对象中对应的方法
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
before();
Object obj = methodProxy.invokeSuper(o,objects);
after();
return obj;
}
private void before() {
System.out.println("付定金");
}
private void after() {
System.out.println("付尾款");
}
}
4、创建测试类测试
package com.zwx.design.pattern.proxy.dynamicProxy.cglibProxy;
public class TestCglibProxy {
public static void main(String[] args){
CglibTravelPerson cglibTravelPerson = (CglibTravelPerson) new CglibTravelAgency().getInstance(CglibTravelPerson.class);
cglibTravelPerson.buyTrainTickey();
}
}
5、输出结果如下:
付定金
cglib:北京到上海
cglib:早上9:00出发
付尾款
我们先通过如下方法将代理类生成出来:
package com.zwx.design.pattern.proxy.dynamicProxy.cglibProxy;
import net.sf.cglib.core.DebuggingClassWriter;
public class TestCglibProxy {
public static void main(String[] args){
System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY,"G:\\");
}
}
生成之后发现有三个class文件(CGLIB底层通过asm框架来生成字节码文件):
通过反编译之后,发现CglibTravelPerson$$EnhancerByCGLIB$$c590ba4f类继承了CglibTravelPerson,所以这个类就是新生成的代理类,里面重写了我们被代理对象中的所有方法,并且每一个方法都有一个MethodProxy与之对应(因为类比较长,所以就只选取片段过来说明,感兴趣的可以自己生成出来看一下):
另外两个带有FastClass的类:CglibTravelPerson$$FastClassByCGLIB$$629469e0是被代理对象CglibTravelPerson对应的FastClass,另一个也就是类名最长的那个就是新生成的代理对象所对应的FastClass。
CGLib动态代理采用了FastClass机制,其分别为代理类和被代理类各生成一个FastClass,这个FastClass类会为代理类或被代理类的方法分配一个 index(int类型)。这个index当做一个入参,FastClass 就可以直接定位要调用的方法直接进行调用,这样省去了反射调用,所以调用效率比 JDK 动态代理通过反射调用更高。
但是我们看上面的源码也可以明显看到,JDK动态代理只生成一个文件,而CGLIB生成了三个文件,所以生成代理对象的过程会更复杂。