代理模式(Proxy Pattern)
是指为其他对象提供一种代理,以控制对这个对象的访问。
代理对象在客服端和目标对象之间起到中介作用,代理模式属于结构型设计模式。
使用代理模式主要有两个目的:一保护目标对象,二增强目标对象
事务代理、非侵入式日志监听等,这些都是代理模式的实际体现。
静态代理
概述
一个类要完成的事情交给另一个类去做,以快递举例,快递送到家本来需要自己去取,现在由快递员送到家
首先编写顶层接口,里面有取快递方法
public interface GetPackage {
void getPackage();
}
定义两个类,我和快递员,都具有取快递方法
//我
public class Myself implements GetPackage {
private String myName = "我";
@Override
public void getPackage() {
System.out.println(myName + "得到了快递");
}
}
//快递员
public class Postman implements GetPackage {
private Myself me;
//创建时,需要将被代理的类传入
Postman(Myself me){
this.me = me;
}
@Override
public void getPackage() {
this.me.getPackage();
}
}
调用
public static void main(String[] args) {
Postman postman = new Postman(new Myself());
postman.getPackage();
}
输出结果:
应用场景
实际生产时,我们可能进行了分库分表,那么在实际操作时,我们使用代理对象的方法去处理分表分库的策略,而被代理的对象,只完成订单的插入
强制代理
防止其被他的类代理,保证代理的类是唯一的,可以采用强制代理,将初始化的过程隐藏在内部,不接受从外部传入
顶层接口中加入获取代理对象的方法
public interface GetPackage {
//取快递
void getPackage();
//获取代理对象
GetPackage getProxy();
}
继承该方法
//我
public class Myself implements GetPackage {
private String myName = "我";
private Postman postman;
@Override
public void getPackage() {
if(postman == null){
System.out.println("未创建代理对象");
return;
}
System.out.println(myName + "得到了快递");
}
//只有执行该方法时,this.postman对象不为null,才可成功调用getPackage()方法打印想要的结果
@Override
public GetPackage getProxy() {
this.postman = new Postman(this);
return this.postman;
}
}
//快递员
public class Postman implements GetPackage {
private Myself me;
//创建时,需要将被代理的类传入
Postman(Myself me){
this.me = me;
}
@Override
public void getPackage() {
this.me.getPackage();
}
}
主方法未调用getProxy()时,
public static void main(String[] args) {
Postman postman = new Postman(new Myself());
postman.getPackage();
}
运行结果:
主方法调用了getProxy()时,
public static void main(String[] args) {
GetPackage me = new Myself();
GetPackage postman = me.getProxy();
postman.getPackage();
}
运行结果:
动态代理
静态代理可以完成解耦的需要,但是静态代理类的代码维护较为复杂,一旦接口或父类的方法改变,则代理类的代码也需要随之修改,所以在实际开发时,我们通常采用动态代理
概述
动态代理技术,是在内存中生成代理对象的一种技术,整个代理过程在内存中进行,不需要手写代理类的代码,不存在类编译的过程,而是在运行期由JVM创建一个对象供我们使用
基于JDK(接口)的动态代理
JDK自带的代理技术,需要使用一个静态方法来创建代理对象,要求被代理对象必须实现接口,生成的代理对象和原对象都实现相同的接口,是兄弟关系
我
public interface GetPackage {
void getPackage();
}
//我
public class Myself implements GetPackage {
private String myName = "我";
@Override
public void getPackage() {
System.out.println(myName + "得到了快递");
}
}
动态代理类:
public class ProxyHandler implements InvocationHandler {
Object object = null;
//代理的目标
public ProxyHandler (Object object) {
this.object = object;
}
//调用目标方法
//在方法中,编写对被代理对象方法的增强或扩展逻辑
//在调用代理对象的方法时,该方法会被调用
//参数1:代理类对象的一个引用,即Proxy.newProxyInstance()方法的返回值
//参数2:被代理对象的方法
//参数3:被代理对象方法的参数
//method.invoke(object,args)方法 参数1:被代理对象实例 参数2:方法参数
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return method.invoke(this.object,args);
}
}
public class MyProxy {
public static <T> T newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h){
T newProxyInstance = (T) Proxy.newProxyInstance(loader,interfaces, h);
return newProxyInstance;
}
}
开始取快递
public static void main(String[] args) throws Throwable{
GetPackage me = new Myself();
ClassLoader cl = me.getClass().getClassLoader();
//newProxyInstance(
// ClassLoader loader,
// Class<?>[] interfaces,
// InvocationHandler h
// )
//参数1:被代理对象的类加载器 参数2:被代理对象实现的接口
//参数3:这个参数是一个接口,里面有唯一的invoke()方法,该方法会在代理类对象调用方法时执行,
//我们在重写该invoke方式时,对原方法进行扩展或增强
GetPackage proxy = MyProxy.newProxyInstance(cl,new Class[] {GetPackage .class}, new ProxyHandler (me));
proxy.getPackage();
}
输出结果:
可以看到,通过这种方式,我们不需要创建固定的代理类Postman对象,而是在使用时,通过proxy去动态的代理对象
基于CGLIB(父类)的动态代理
第三方动态代理技术,也是使用一个静态方法来创建对象,它基于目标类生成一个子类,所以要求目标类不能是最终类,不能被final修饰
public static void main(String[] args) throws Throwable{
GetPackage me = new Myself();
//参数1:Class type 目标类的字节码对象
//参数2:Callback callback 回调方法,Callback是一个起定义名称作用的接口,
//一般我们使用它的子接口MethodIntercepter,其中有唯一方法intercept()
GetPackage proxy = Enhancer.create(me.getClass() ,new Intercepter({
//参数1:代理类对象引用Enhancer.create()方法的返回值
//参数2:目标类方法
//参数3:方法参数
//参数4:方法的代理对象
@Override
public Object intercept(
Object proxy,
Method method,
Object[] args,
MethodProxy methodProxy
) throws Exception{
return method.invoke(me,args);
}
}));
proxy.getPackage();
}
执行结果:
基于父类的动态代理,在内存中生成了一个对象,该对象继承了原对象,是原对象的子类,可以用父类对象的引用接收