写在前面
讲真,我对设计模式其实并没有很深入的了解。这里我想把自己的理解 通过文字的方式表达出来。为啥突然想起写这个文章呢?是因为我在星球里面看见有人提问:
其实,我在去年的时候也写过相关的内容,但是没有展开。这里我就一一把自己所知写出来。
那么接下来就开始吧,如果有什么错误的地方,欢迎指正~我也还在不断的学习中,大家一起加油!
核心:原有对象需要额外的功能,就可以使用代理这项技术
代理模式两大类:
- 静态代理
透明代理 - 动态代理
基于接口的JDK动态代理
基于类的CgLib动态代理
01静态代理
目的:一开始就要抓住我们是去 增强 类的功能的。
所谓静态代理,大致就是需要重新写一个 代理类 去实现接口,然后这个类的构造方法 用待增强类 作为入参。在实现接口的方法中,调用 待增强类 对应的方法。同时,在这个方法的前后可以调用其余的方法,从而实现功能增强的目的。
什么?!没看懂,没关系,我们上代码
1.1 来一个接口:
public interface Subject {
void doSomeThing();
}
1.2 实现这个接口 (就像web里面的 接口+Impl)
public class RealSubject implements Subject {
public void doSomeThing() {
System.out.println("我是真正的实现类,这里可能有大量的逻辑处理代码 doSomeThing()");
}
}
其实到这里,就是我写代码时候的常规操作。感觉很正常嘛,写一个接口,然后再写实现类去实现这个接口定义的方法。需要使用的时候:直接
接口 接口1 = new 接口实现类();
接口1.doSomeThing();
当然这样,也是没错,OK 。
随着业务庞大,你就会知道,拥有一个 proxy 类对真实类的封装对于粒度的控制有着重要的意义?
1.3 写一个 代理类
这个类需要去实现我们的接口,同时将上面的 实现类作为入参 传递给代理类的 构造函数。
注意:我们就是在这里实现类功能的增强的!
/**
* Description: 静态代理
*
*
* 静态代理这个模式本身有个大问题,如果类方法数量越来越多的时候,
* 代理类的代码量是十分庞大的。所以引入动态代理来解决此类问题。
*
* 缺点:如果目标对象的接口有很多方法的话,那我们还是得一一实现,这样就会比较麻烦
*
* @Author: 留歌36
* @Date: 2020/5/21 11:44
*/
public class SubjectProxy implements Subject{
// 真正的实现类
private RealSubject realSubject;
// 构造方法1
public SubjectProxy(RealSubject realSubject) {
this.realSubject = realSubject;
}
// // 构造方法2: 只做 RealSubject 的代理,且RealSubject对外界透明,也叫做透明代理
public SubjectProxy() {
this.realSubject = new RealSubject();
}
/**
* 实现接口的方法
*/
public void doSomeThing() {
// 这个方法 之前之后 还可以做一些其他的事情,就实现了类功能的增强
SubjectProxy.increaseBefore();
// 通过这个静态代理就可以使用一句代码顶真正实现类 的功能实现
realSubject.doSomeThing();
increaseAfter();
}
// 1.在方法之前做增强
private static void increaseBefore() {
System.out.println("1.在方法之前做增强");
}
// 2.在方法之后做增强
void increaseAfter() {
System.out.println("2.在方法之后做增强");
}
}
其实到这里,静态代理就结束了,当然我们得写一个类测试一下撒:
补充:所谓透明代理,就是将我们的真实的 实现类 封装进了代理里面,对于外界来说是透明的。就像:
public SubjectProxy() {
this.realSubject = new RealSubject();
}
关于静态代理就是这样了,其实就是写一个代理类去实现目标对象的接口。
缺点:如果 目标对象的接口 有很多方法的话,那我们还是得一一实现,这样就会比较麻烦,所以引入动态代理来解决此类问题。
02动态代理
抓住一点:静态代理我们是手动写一个 代理类 去实现目标对象的接口,然后一一实现这些接口。这样就太难搞了,,,然后动态代理就不用一一去手动实现,我们利用 反射 技术实现这一目的。
Java提供了一个Proxy类,调用它的newInstance方法 ,该方法 返回指定接口的代理类的实例 将方法调用 分派到 指定的调用 处理程序。我
们只需要写一个动态代理的类,就直接齐活了。是不是很方便~
public class ProxyHandler implements InvocationHandler {
private Object tar;
// 绑定目标对象,并返回 代理对象 【这在之前我们是手写的】
public Object bind(Object tar)
{
this.tar = tar;
/**
* 参数一:生成代理对象使用哪个类装载器【一般我们使用的是被代理类的装载器】
*
* 参数二:生成哪个对象的代理对象,通过接口指定【指定要被代理类的接口】
*
* 参数三:生成的代理对象的方法里干什么事【实现handler接口,我们想怎么实现就怎么实现】
*
注意:代理对象拥有目标对象相同的方法【因为参数二指定了对象的接口,代理对象会实现接口的所有方法】
*/
return Proxy.newProxyInstance(
tar.getClass().getClassLoader(),
tar.getClass().getInterfaces(),
this // 这里的this,即上面定义的Object,代表实现的所有方法 交由Object 的 invoke() 反射方法进行处理
);
}
// 用户调用代理对象的什么方法,都是在调用处理器的invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
//这里就可以进行所谓的AOP编程了,不管你调用 生成对象的什么方法,invoke都是会触发,这里就可以做一些前置或后置的方法
// increaseBefore() 方法
//在调用具体函数方法前,执行功能处理【这里就是调用生成对象的 具体方法】
result = method.invoke(tar,args);
// increaseAfter() 方法
//在调用具体函数方法后,执行功能处理
return result;
}
}
通过动态代理实现的全部方法,全都是通过invoke()方法调用
这里,我在代码中也写了大量的说明,只要知道,我们传入一个 目标对象的实例 ,就能得到一个 动态生成的代理类,这个类拥有 传入的目标对象实例的全部 默认实现方法。
在调用这个目标对象实例的方法的时候,会触发invoke()中定义的增强功能。
代理对象的生成,是利用 JDK API,动态地在内存中构建代理对象(需要我们指定创建 代理对象/目标对象 实现的接口的类型),并且会默认实现接口的全部方法。
补充: 基于接口的JDK动态代理 && 基于类的CgLib动态代理
上面是我小伙伴总结的,我觉得写得不错,就贴过来了,欢迎关注他的博客地址
说了那么多,可是真正回归到我们日常写代码中:
- 我知道Spring的AOP(面向切面编程)底层 其实就是动态代理来实现的,其实也没有很熟悉
- …
- …
讲真,我就不大知道了,后面我有知道,再回来补充
然后差不多,代理模式就写到这里吧。
核心:原有对象需要 增强 功能,就可以使用代理这项技术
下一篇写装饰器模式