(2)代理模式
定义:为一个对象提供一个替身,以控制对这个对象的访问。即通过代理对象访问目标对象。这样做的好处是: 可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。
针对对象:被代理的对象可以是远程对象、创建开销大的对象或需要安全控制的对象。
代理模式有不同的形式,主要有三种:静态代理、动态代理(JDK代理、接口代理)和Cglib代理(可以在内存动态的创建对象,而不需要实现接口,属于动态代理的范畴)。
代理模式究竟是干什么的?
代理模式就是为了对目标类的增强,完成对目标类的织入,增强的一般都是切入点(方法),我们将增强的过程称为织入。
a.静态代理
静态代理使用时,需要定义接口或者父类,被代理对象和代理对象需要一起实现相同的接口或者是继承相同父类。
优点:在不修改目标对象的功能前提下,能通过代理对象对目标功能扩展。
缺点:因为代理对象需要与目标对象实现一样的接口,所以会有很多代理类,一旦接口增加方法,目标对象和代理对象都要维护。
例子:
需求:客户带着找房的需求找到中介,中介联系房东
接口:
public interface ProxyInterface {
/**
* 找房的方法
*/
void findHouse();
}
代理对象(中介):
package com.jy.design.proxy;
/**
* 代理对象(中介)客户选好房子,中介联系房东
*/
public class ProxyDao implements ProxyInterface{
/**
* 中介这个类得有房东的引用,但一个中介的房源不止一个房东,所以聚合接口,可以接收所有的房东实例, 面向接口编程
*/
private ProxyInterface impl ;
/**
* 目标对象即房东可能不止一个,所以用接口去接收
* @param impl
*/
public ProxyDao(ProxyInterface impl) {
this.impl = impl;
}
@Override
public void findHouse() {
System.out.println("1、中介带着房客找房");
impl.findHouse();
System.out.println("3、房客入住");
}
}
目标类(房东):
/**
* 目标对象(房东)
*/
public class ProxyDaoImpl implements ProxyInterface{
@Override
public void findHouse() {
System.out.println("2、房子已经找好,签署合同");
}
}
客户类:
/**
* 代理模式的作用是完成对目标类的增强
* 客户(有找房需求):有中介的联系方式
*/
public class ProxyApp {
public static void main(String[] args) {
//创建代理对象(中介对象)
ProxyDao proxyDao = new ProxyDao(new ProxyDaoImpl());
proxyDao.findHouse();
}
}
b.动态代理(又名jdk代理、接口代理)
1)代理对象的实现,是利用JDK的API,通过反射动态的在内存中构建出的。
2)在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,而Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
3)作用:完成对目标方法的增强。
例子:
接口类:
/**
* 代理类和目标类需要实现的共同接口
*/
public interface InterfaceProxy {
void getMethod();
}
目标类:
/**
* 目标类
*/
public class ProxyDaoimpl implements InterfaceProxy{
@Override
public void getMethod() {
System.out.println("调用目标类成功");
}
}
创建代理类的类:
/**
* 当前类不是代理类,是用于创建代理类的类
* 代理类代理的是目标类,所以要引入,但目标类尚且不知,所以使用Object
*/
public class CreateProxy {
/**
* 引入目标对象
*/
private Object object;
public CreateProxy(Object object) {
this.object = object;
}
/**
* 通过反射机制来创建代理对象
*/
public Object getProxy(){
/**
* newProxyInstance 内部参数:
* ClassLoader loader, 目标类的类加载器
* Class[] interfaces, 目标类的接口字节码对象
* InvocationHandler h, 是一个接口,里面提供了增强目标类的方法invoke
*/
ClassLoader classLoader = object.getClass().getClassLoader();
Class[] interfaces = object.getClass().getInterfaces();
InvocationHandler invocationHandler = new InvocationHandler() {
/**
* 实现接口中的invoke就是增强目标类的方法
*
* @param proxy 代理对象
* @param method 代理对象的方法
* @param args 方法对应的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强的内容");
//调用目标对象的方法,目标类和代理类实现了同一个接口,所以调用代理对象的方法等于调用目标对象的方法
Object invoke = method.invoke(object, args);
System.out.println("增强的内容");
return invoke;
}
};
//返回的就是代理对象
Object obj = Proxy.newProxyInstance(classLoader, interfaces,invocationHandler);
return obj;
}
}
测试类:
public class Test {
public static void main(String[] args) {
//创建目标对象
ProxyDaoimpl proxyDaoimpl = new ProxyDaoimpl();
//创建代理类对象的对象
CreateProxy createProxy = new CreateProxy(proxyDaoimpl);
//获取代理对象
InterfaceProxy proxy = (InterfaceProxy)createProxy.getProxy();
proxy.getMethod();
}
}
剖析一下,动态代理底层是如何创建代理类的?
/**
* @author shizi 2022/1/25
* 模拟jdk动态代理在底层的调用过程
*/
public class ProxyDaiLi implements InterfaceProxy{
private InvocationHandler invocationHandler;
public ProxyDaiLi(InvocationHandler invocationHandler) {
this.invocationHandler = invocationHandler;
}
@Override
public void getMethod() {
try {
Method method = InterfaceProxy.class.getDeclaredMethod("getMethod");
System.out.println("增强的内容1");
System.out.println("增强的内容2");
//调用目标类的方法
method.invoke(ProxyDaoimpl.class.newInstance(),new Object[]{});
System.out.println("增强的内容3");
System.out.println("增强的内容4");
} catch (Exception e) {
e.printStackTrace();
}
}
}
使用method.invoke时,增强内容写在当前的方法(getMethod)中
Test02:
/**
* @author shizi 2022/1/25
*与上面Test其实是一样的,这个就像是Test的内部过程
*/
public class Test02 {
public static void main(String[] args) {
ProxyDaiLi proxyDaiLi = new ProxyDaiLi(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object invoke = method.invoke(ProxyDaoimpl.class.newInstance(), args);
return invoke;
}
});
proxyDaiLi.getMethod();
}
}
运行结果:
或者:
@Override
public void getMethod() {
try {
Method method = InterfaceProxy.class.getDeclaredMethod("getMethod");
//调用目标类的方法
invocationHandler.invoke(this,method,new Object[]{});
} catch (Exception e) {
e.printStackTrace();
}
}
用invocationHandler调用invoke方法时,增强内容写在invocationHandler的invoke方法中
/**
* @author shizi 2022/1/25
*/
public class Test02 {
public static void main(String[] args) {
ProxyDaiLi proxyDaiLi = new ProxyDaiLi(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("增强的内容1");
System.out.println("增强的内容2");
Object invoke = method.invoke(ProxyDaoimpl.class.newInstance(), args);
System.out.println("增强的内容3");
System.out.println("增强的内容4");
return invoke;
}
});
proxyDaiLi.getMethod();
}
}
运行结果: