用示例代码来帮你了解代理模式
对于“设计模式”这个词大家肯定都不陌生,很多框架也用到了设计模式,但是大部分的开发者应该是没有深入的了解过,我准备硬肝下这23设计模式作为专题文章的开端,一共23种设计模式,我尽量在<23天肝完。
为什么要学习设计模式:https://blog.youkuaiyun.com/kaituozhe_sh/article/details/107922339
在我大学四年,对设计模式也没有什么概念,写代码就想着能实现就可以了,不会有设计模式那样的思想,但是当学习到了框架的时候,对于设计模式才有了一些更深入的了解,使用设计模式的代码在扩展性上会比暴力的代码更容易维护,特别是当一个程序猿离职了后,你去接手它的代码,里面是一大堆if else,这样真的会崩溃,修改都不知道从何下手
硬肝系列目录
创建型模式
结构型模式
行为型模式
到目前为止、23种设计模式的创建型模式已经给大家肝完了,现在我们进入到一个全新的章节,结构型模式!!!
什么是代理模式
代理代理,我们从字面上就能看得出来,叫别人帮你你干活,干什么活呢?我来举个例子吧,比如你是一个炒鸡大明星,都是炒鸡的明星了,肯定有很多活动来找你吧,这你就需要一个或者多个助理来帮你处理了,你只负责参加活动,其他的什么都不用你管,表演前的签合同、表演后的善后工作都由小助理帮你处理,这样你才有时间参加更多的活动吧
然而代理呢也分为三种
1.静态代理
2.JDK的动态代理
3.CGlib的动态代理
下面我分别用实例来给你们讲讲它们三个的优缺点
静态代理
现在我们先新建一个Star接口,这里面的方法为明星们需要做的事
public interface Star {
void say();
void play();
}
接下来用一个SuperStar,实现Star接口
public class SuperStart implements Star{
@Override
public void say() {
System.out.println("我是超级大明星,我准备表演节目!!");
}
@Override
public void play() {
System.out.println("我是超级大明星,我正在表演节目!!");
}
}
创建一个静态代理类来实现代理的效果
public class StarStaticProxy {
private Star star;
public StarStaticProxy(Star star){
this.star = star;
}
public void proxySay(){
System.out.println("我是ta的助理,我帮ta接工作,签合同");
star.say();
System.out.println("我是ta的助理,表演完了我帮ta做善后工作");
}
public void proxyPlay(){
System.out.println("我是ta的助理,我帮ta准备演出服装");
star.play();
System.out.println("我是ta的助理,表演完了我帮ta回收演出服装");
}
}
从上面的代码我们可以看出,就相当于我只需要关注自己做的事,其他的事有别人搞定,下面我们来测试一下
public static void main(String[] args) {
StarStaticProxy starStaticProxy = new StarStaticProxy(new SuperStart());
starStaticProxy.proxySay();
System.out.println();
starStaticProxy.proxyPlay();
}
输出结果:
我是ta的助理,我帮ta接工作,签合同
我是超级大明星,我准备表演节目!!
我是ta的助理,表演完了我帮ta做善后工作
我是ta的助理,我帮ta准备演出服装
我是超级大明星,我正在表演节目!!
我是ta的助理,表演完了我帮ta回收演出服装
这样就完成了最简单的代理实现方式,这样写是挺方便的,但是我要是细化到每个人呢,我又100个明星,难道我要写100个静态代理类吗??
显然是不可能的,那我们应该用什么方法来解决呢?这时候就出现了动态代理了,我们Spring AOP使用的就是动态代理技术,对于提供了接口的类,我们使用的是JDK的动态代理,没有提供接口的类,我们选择的是CGlib动态代理,我们先来说说JDK动态代理,为什么它只能对接口进行代理
JDK动态代理
还是上面明星的例子,我们直接建立动态代理类
public class StarDynamicProxy {
private Object object;
public StarDynamicProxy(Object object){
this.object = object;
}
public Object getDynamicProxy(){
return Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是明星的智能助理,开启ta一天的事务");
Object o = method.invoke(object,args);
System.out.println("我是明星的智能助理,总结ta一天的事务");
return o;
}
}
);
}
}
我们先来测试一下,能不能达到我们理想的效果
public static void main(String[] args) {
SuperStart superStart = new SuperStart();
Star star = (Star) new StarDynamicProxy(new SuperStart()).getDynamicProxy();
star.say();
}
运行结果
我是明星的智能助理,开启ta一天的事务
我是超级大明星,我准备表演节目!!
我是明星的智能助理,总结ta一天的事务
好了,接下来我们看看JDK动态代理为什么只能代理接口吧,以及那些参数的意义
Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
//调用处理器
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是明星的智能助理,开启ta一天的事务");
Object o = method.invoke(object,args);
System.out.println("我是明星的智能助理,总结ta一天的事务");
return o;
}
}
);
我说这上面这些代码是最重要的应该没人反对吧,我们先来看看newProxyInstance()的源码
@CallerSensitive
//ClassLoader loader为类加载器 object.getClass().getClassLoader()即可获得
//Class<?>[] interfaces为被代理类实现的接口,为什么是一个数组,因为接口可以多实现
//h为InvocationHandler的实现类,上面我们直接new了一个InvocationHandler,也可以写一个实现类来实现此接口
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方法中
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
//上面这段话的意思是
//JDK对被代理类进行缓存,如果缓存中存在了这个被代理类则直接返回,反之则会通过ProxyClassFactory来进行创建
return proxyClassCache.get(loader, interfaces);
}
proxyClassCache是通过WeakCache实现的,WeakCache<K,P,V>中,K代表key值,P代表参数,V代表存储的值。此类用于缓存{(key,sub-key)–>value}kv对,具体的缓存实现我们可以另开一篇文章来讲,我们主要关心动态代理类如何生成的
上面说到了ProxyClassFactory,它是Proxy里的一个静态内部类,实现了BiFunction接口并重写了apply方法
源码
private static final class ProxyClassFactory
implements BiFunction<ClassLoader, Class<?>[], Class<?>>
{
// 所有代理类名的前缀
private static final String proxyClassNamePrefix = "$Proxy";
// 用于生成唯一代理类名的计数器
private static final AtomicLong nextUniqueNumber = new AtomicLong();
@Override
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
//intf为被代理类的接口,前面newProxyInstance已将它放入
for (Class<?> intf : interfaces) {
/*
*验证类加载器是否将此接口的名称解析为相同的类对象。
*/
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; // 生成代理类的包名
int accessFlags = Modifier.PUBLIC | Modifier.FINAL;
//记录非公共代理接口的包,以便代理类将在同一个包中定义。验证所有非公共代理接口都在同一个包中。
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;
/*
* 生成指定的代理类
*/
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces, accessFlags);
try {
//根据二进制字节码返回对应的Class实例
return defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
} catch (ClassFormatError e) {
throw new IllegalArgumentException(e.toString());
}
}
}
关键代码
我们来看看ProxyGenerator.generateProxyClass方法
到这,我们就可以获得通过JDK动态代理而得到的代理类了,为什么只能代理接口呢,因为被代理类是继承了Proxy类,要知道类不能多继承,而接口可以都多实现,所以它只能实现接口的动态代理,但是我有些类确确实实不需要实现接口,但是我又得实现代理该怎么办呢?这时候CGlib动态代理就很好的弥补了这个问题
CGlib动态代理
CGlib(Code Generation Library)是一个Code生成类库,它可以在运行期扩展Java类与实现Java接口。在Spring AOP上也有它的身影,CGlib包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
我们先上代码来体验一下CGlib的强大之处
新建一个不实现接口的类
public class Superman {
public void say(){
System.out.println("i'm superman");
}
}
CGlib代理类
public class StarCGlibProxy implements MethodInterceptor {
private Object cglib;
public StarCGlibProxy(Object cp){
cglib = cp;
}
public Object getProxyInstance(){
//1.Enhancer工具类
Enhancer enhancer = new Enhancer();
//2.设置被代理类为父类
enhancer.setSuperclass(cglib.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类(也就是被代理的对象)
return enhancer.create();
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("穿上红内裤---");
Object returnReflect = method.invoke(cglib,objects);
System.out.println("不穿红内裤---");
return returnReflect;
}
}
我们来测试一下,是否是我们想要的结果
public static void main(String[] args) {
Superman superman = (Superman) new StarCGlibProxy(new Superman()).getProxyInstance();
superman.say();
}
输出结果
穿上红内裤---
i'm superman
不穿红内裤---
我们来看看代理类中有哪些内容,首先代理类实现了MethodInterceptor接口并重写了intercept()
这个接口中就包含了一个方法,该方法有4个参数:
1)var1 表示要增强的对象,即实现这个接口类的一个对象;
2)var2 表示要被拦截的方法;
3)var3 表示要被拦截方法的参数;
4)var4 表示要触发父类的方法对象;
还有就是大家注意看
public Object getProxyInstance(){
//1.Enhancer工具类
Enhancer enhancer = new Enhancer();
//2.设置被代理类为父类
enhancer.setSuperclass(cglib.getClass());
//3.设置回调函数
enhancer.setCallback(this);
//4.创建子类(也就是被代理的对象)
return enhancer.create();
}
我通过Enhancer来创建代理对象
首先设置被代理类为父类,也就是Superman
继承了父类且实现了Factory接口
其次设置回调函数为MethodInterceptor接口所实现的Callback函数
最后我调用了enhancer.create();其方法调用栈为:
enhancer.create()—>createrHelper()—>AbstractClassGenerator.create(key)来创建代理对象,我们先来看看这个create方法到底是啥
public Object create() {
this.classOnly = false;
this.argumentTypes = null;
return this.createHelper();
}
点击进去只有这么一块,最主要是createHelper()
private Object createHelper() {
this.preValidate();
Object key = KEY_FACTORY.newInstance(this.superclass != null ? this.superclass.getName() : null, ReflectUtils.getNames(this.interfaces), this.filter == ALL_ZERO ? null : new WeakCacheKey(this.filter), this.callbackTypes, this.useFactory, this.interceptDuringConstruction, this.serialVersionUID);
this.currentKey = key;
Object result = super.create(key);
return result;
}
首先**this.preValidate()**进行有效性验证比如有多个callBack却没有callBackFilter
其次再根据KEY_FACTORY 凭当前代理类的配置信息 生成一个组合Key,再利用这个组合Key,调用super.create(key);
再看看父类的create源码
protected Object create(Object key) {
try {
ClassLoader loader = this.getClassLoader();
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> cache = CACHE;
AbstractClassGenerator.ClassLoaderData data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Class var5 = AbstractClassGenerator.class;
synchronized(AbstractClassGenerator.class) {
cache = CACHE;
data = (AbstractClassGenerator.ClassLoaderData)cache.get(loader);
if (data == null) {
Map<ClassLoader, AbstractClassGenerator.ClassLoaderData> newCache = new WeakHashMap(cache);
data = new AbstractClassGenerator.ClassLoaderData(loader);
newCache.put(loader, data);
CACHE = newCache;
}
}
}
this.key = key;
Object obj = data.get(this, this.getUseCache());
return obj instanceof Class ? this.firstInstance((Class)obj) : this.nextInstance(obj);
} catch (Error | RuntimeException var9) {
throw var9;
} catch (Exception var10) {
throw new CodeGenerationException(var10);
}
}
父类方法真正返回代理类对象的是this.nextInstance(obj)方法,该方法在父类为抽象方法,在子类中实现
protected Object nextInstance(Object instance) {
Enhancer.EnhancerFactoryData data = (Enhancer.EnhancerFactoryData)instance;
if (this.classOnly) {
return data.generatedClass;
} else {
Class[] argumentTypes = this.argumentTypes;
Object[] arguments = this.arguments;
if (argumentTypes == null) {
argumentTypes = Constants.EMPTY_CLASS_ARRAY;
arguments = null;
}
return data.newInstance(argumentTypes, arguments, this.callbacks);
}
}
我们接着来看最后一行data.newInstance(argumentTypes, arguments, this.callbacks);
//argumentTypes为代理对象的构成器类型,arguments为代理对象构造方法参数,callbacks为对应回调对象。
public Object newInstance(Class[] argumentTypes, Object[] arguments, Callback[] callbacks) {
this.setThreadCallbacks(callbacks);
Object var4;
try {
if (this.primaryConstructorArgTypes == argumentTypes || Arrays.equals(this.primaryConstructorArgTypes, argumentTypes)) {
var4 = ReflectUtils.newInstance(this.primaryConstructor, arguments);
return var4;
}
var4 = ReflectUtils.newInstance(this.generatedClass, argumentTypes, arguments);
} finally {
this.setThreadCallbacks((Callback[])null);
}
return var4;
}
通过传进来的参数,使用反射工具类来创建代理类对象
我们来看看cglib生成的代理对象
如上图所示
我们来看看反编译后的代码
太长我就不全部贴出来了,我截了比较重要的一段
从这段代码我们可以看出,当我们调用被代理对象的say方法时,会执行intercept方法进行拦截,以达到增强的目的,最后一个tips:
invokeSuper调用的是被代理类的方法, 但只有代理类才存在基类, 必须使用代理类作为obj参数调用
invoke调用的是增强方法, 必须使用被代理类的对象调用, 使用代理类会造成OOM
每次看完源码都感觉身体被掏空
犒劳一下自己
呜呜呜,生活还要继续,加油干!!!
完成:TO: 2021/3/22 00:00