代理模式
image.png
- 用户只关心接口功能,而不在乎谁提供了功能。上图中接口是 Subject。
- 接口真正实现者是上图的 RealSubject,但是它不与用户直接接触,而是通过代理。
- 代理就是上图中的 Proxy,由于它实现了 Subject 接口,所以它能够直接与用户接触。
- 用户调用 Proxy 的时候,Proxy 内部调用了 RealSubject。所以,Proxy 是中介者,它可以增强 RealSubject 操作。
静态代理
接口实现方式
image.png
继承方式
image.png
/*
静态代理DEMO
*/
public class StaticProxyDemo {
public static void main(String[] args) {
new WeddingCompany(new Person()).wedding();
}
}
//1. 一个接口
interface Memarry {
void wedding();
}
//2. 人,真正的结婚类
class Person implements Memarry{
@Override
public void wedding() {
System.out.println("这个屌丝正在结婚办婚礼……");
}
}
//3. 婚庆公司,结婚代理类
class WeddingCompany implements Memarry{
private Person target;
public WeddingCompany(Person target) {
this.target = target;
}
@Override
public void wedding() {
System.out.println("婚庆公司提前布置会场");
this.target.wedding();
System.out.println("婚庆公司收尾款");
}
}
动态代理
动态代理技术,不需要人工编写代理类 java 文件,而是在运行过程中,在 JVM 内存中创建出来的代理对象共我们使用。有两种实现技术:
基于 JDK(接口)的动态代理
JDK 自带的动态代理技术,要求目标类要实现一个接口;需要使用 Proxy 类的静态方法 newProxyInstance 在程序运行阶段在内存中生成一个代理对象,生成的代理对象与目标类实现同一个接口,是兄弟关系,不能互相转换,包容性差;(spring 框架中,如果配置 JDK 的动态代理,一定要用接口类型接收代理对象)
Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader: 固定写法,指定目标类对象的类加载器。用于加在目标类及其接口的字节码文件。通常使用目标类的字节码对象调用 getClassLoader() 方法。
interfaces:固定写法,指定目标类实现的所有接口的字节码对象的数组。通常调用 getInterfaces() 方法。
h:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
proxy : 代理类对象的一个引用,即 Proxy.newProxyInstance() 方法返回的值,几乎用不到。
method : 触发 invoke 方法执行的方法的反射对象(Method 对象)。
args : 代理对象调用方法时,传递的实际参数。
/**
* 基于 JDK (接口)的动态代理
*/
public class DynamicProxyDemo2 {
public static void main(String[] args) {
//1、创建目标类对象
Person2 person = new Person2();
//2、使用 JDK 的 API 动态生成一个代理类对象
Memarry2 weddingCompany = (Memarry2)Proxy.newProxyInstance(person.getClass().getClassLoader(),
person.getClass().getInterfaces(),
(proxy, method, args1)->{
System.out.println("+++++++++++++++++++++++++++++");
if(method.getName().equals("wedding")){
System.out.println("婚庆公司正在筹办宴会厅");
String result = (String)method.invoke(person,args);
System.out.println("婚庆公司收尾款");
return result;
} else {
return method.invoke(person,args);
}
});
String result = weddingCompany.wedding();
System.out.println(">>>>>result:"+result);
// 自己模拟实现 JVM 运行中生成的代理类
// InvocationHandler handler = (proxy, method, args1)->{
// System.out.println("+++++++++++++++++++++++++++++");
// if(method.getName().equals("wedding")){
// System.out.println("婚庆公司正在筹办宴会厅");
// String result = (String)method.invoke(person,args);
// System.out.println("婚庆公司收尾款");
// return result;
// } else {
// return method.invoke(person,args);
// }
//
// };
// SelfProxy selfProxy = new SelfProxy(handler);
// System.out.println(">>>>>result:"+selfProxy.wedding());
}
}
//1. 一个接口
interface Memarry2 {
String wedding();
void test1();
void test2();
}
//2. 人,真正的结婚类
class Person2 implements Memarry2{
@Override
public String wedding() {
System.out.println("这个屌丝正在结婚办婚礼……");
return "恭喜恭喜";
}
@Override
public void test1() {
System.out.println("这是test1方法");
}
@Override
public void test2() {
System.out.println("这是test2方法");
}
}
/**
* 模拟 Proxy.newProxyInstance() 方法 是如何实现的
*/
public class SelfProxy implements Memarry2 {
// 接收外部传递过来的 InvocationHandler 对象
private final InvocationHandler handler;
public SelfProxy(InvocationHandler handler){
this.handler = handler;
}
@Override
public String wedding() {
try {
// 调用的是 wedding 方法,则反射获取 wedding 对应的 method 对象,传入 invoke 中
Method method = Memarry2.class.getMethod("wedding");
// 调用 InvocationHandler 中的 invoke 方法
String result = (String)handler.invoke(this,method,null);
return result;
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return null;
}
@Override
public void test1() {
try {
Method method = Memarry2.class.getMethod("test1");
String result = (String)handler.invoke(this,method,null);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void test2() {
try {
Method method = Memarry2.class.getMethod("test2");
String result = (String)handler.invoke(this,method,null);
} catch (Exception e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
基于 CGLIB(父类)的动态代理
CGLIB 也是使用一个静态方法生成代理类对象;他要求目标类不能是最终类,也就是不能被 final 修饰,可以被继承;生成的代理类对象是目标类的子类。可以用父类接收代理对象。
Enhancer.create(Class type,Callback callback)
type: 指定目标类的字节码对象,即目标类的类型。
callback: 回调方法。Callback 是一个接口,只起到名称定义的作用,并不包含方法的声明。所以通常使用它的一个子接口 MethodInterceptor (方法拦截器),它内部只有一个方法 intercept。
Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy)
throws Throwable;
proxy : 代理类对象的一个引用,即 Enhancer.create() 方法返回的值,几乎用不到。
method : 触发 intercept 方法执行的方法的反射对象(Method 对象)。
args : 代理对象调用方法时,传递的实际参数。
methodProxy : 方法的代理对象,一般也不做处理。
/**
* 基于 CGLIB 的动态代理
*/
public class DynamicProxyDemo {
public static void main(String[] args) {
//1、创建目标类对象
Person person = new Person();
//2、使用 CGLIB 的静态方法动态生成一个代理类对象,是父子关系
Person personProxy = (Person)Enhancer.create(person.getClass(), new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("+++++++++++++++++++++++++++++");
if(method.getName().equals("wedding")){
System.out.println("婚庆公司正在筹办宴会厅");
String result = (String)method.invoke(person,args);
System.out.println("婚庆公司收尾款");
return result;
} else {
return method.invoke(person,args);
}
}
});
System.out.println(">>>>>result:"+personProxy.wedding());
}
}
class Person {
public String wedding() {
System.out.println("这个屌丝正在结婚办婚礼……");
return "恭喜恭喜";
}
public void test1() {
System.out.println("这是test1方法");
}
public void test2() {
System.out.println("这是test2方法");
}
}
/**
* 模拟 Enhancer.create() 方法
*/
public class SelfProxy extends Person {
private final MethodInterceptor methodInterceptor;
public SelfProxy(MethodInterceptor methodInterceptor){
this.methodInterceptor = methodInterceptor;
}
@Override
public String wedding() {
Method method = null;
try {
method = Person.class.getMethod("wedding");
String result = (String)methodInterceptor.intercept(this,method,null,null);
return result;
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
return super.wedding();
}
@Override
public void test1() {
Method method = null;
try {
method = Person.class.getMethod("test1");
Object result = methodInterceptor.intercept(this,method,null,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Override
public void test2() {
Method method = null;
try {
method = Person.class.getMethod("test2");
Object result = methodInterceptor.intercept(this,method,null,null);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
}
扩展
如果使用 dubbo + zookeeper,底层进行代理时,最好定死使用 CGLIB 的方式。因为 dubbo 会使用基于包名的扫描方式进行类的处理。JDK 生成的代理类的包名类似于 com.sun.proxy...的格式,而 CGLIB 生成的代理类的包名与目标类一致。
引文列表:
https://www.cnblogs.com/cC-Zhou/p/9525638.html
https://www.bilibili.com/video/BV1M54y1X78p?from=search&seid=12941460499174324218&spm_id_from=333.337.0.0