代理模式:为其它对象提供一种代理,以控制对整个对象的访问。代理对象起到中介作用,可去掉某些功能或增加额外的功能。
例如:生活中的住酒店,一般我们在途牛、携程订酒店的时候,往往会看到什么什么旅行社,其实并不是我们自己去订,而是我们下单之后,旅行社为我们去订酒店,此处的旅行社就是我们的代理。
代理模式分类:静态代理、动态代理。
一、静态代理
静态代理:代理和被代理对象在代理之前是确定的,它们都实现相同的接口或者继承相同的类。也就是说代理类和被代理类都是已经写好的。
Subject表示其父类或父接口,它定义了被代理对象需要做的事,也就是抽象行为。RealSubject表示被代理的对象,也就是我自己,需要去订酒店的。ProxySubject表示代理对象,也就是旅行社,真正帮我订票的人。
下面来看看静态代理的代码。
接口Subject:
//抽象出订房的方法,也是需要代理对象帮忙做的事(订房)
public interface Subject {
public void reservation();
}
代理类(旅行社):
//代理对象,也就是帮我买票的旅行社
public class ProxySubject implements Subject {
//接受一个代理对象,也就是为谁订票。
RealSubject realSubject;
ProxySubject(RealSubject realSubject) {
this.realSubject = realSubject;
}
public void reservation() {
System.out.println("收到订单!");
//注意此处,调用的是代理对象的实例去进行订票。
realSubject.reservation();
System.out.println("订票完成!");
}
}
被代理对象(订房的人):
//实现接口并重写方法
public class RealSubject implements Subject{
public void reservation() {
System.out.println("订票!");
}
}
测试:
public class Test {
public static void main(String[] args) {
RealSubject realSubject = new RealSubject();
ProxySubject proxySubject = new ProxySubject(realSubject);
//proxySubject代理人替代理对象realSubject订房!
proxySubject.reservation();
}
}
结果:
收到订单!
订票!
订票完成!
因为代理类和被代理类在编译的时候就已经确定了,所以为静态代理,静态代理的缺点就是要扩展很麻烦,需要创建很多的类和接口。
二、JDK动态代理
JDK动态代理:在动态代理中我们不再需要再手动的创建代理类,我们只需要编写一个事务处理器就可以了。真正的代理对象由JDK再运行时为我们动态的来创建。
JDK动态代理是通过反射来完成的,毕竟只有反射才能在运行时获取到对象或类的信息。动态代理要求代理类必须实现一个接口,并且要实现InvocationHandler接口的invoke方法。(并不是实现这个接口,而是代理类实现的接口,是代理类实现的Subject这个接口)
此处,为了让结构明了,我们先用一个类实现InvocationHandler接口的invoke方法,一般情况下都是使用匿名内部类实现InvocationHandler接口的invoke方法。
接口和被代理类都没有变化,代理类ProxySubject可以删除。
实现InvocationHandler接口:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//实现事务处理器接口
public class DynamicProxyHandler implements InvocationHandler {
//用object接收一个代理对象的实例,因为Method.invoke的方法接收的参数也是Object,所以用Object即可。
private Object targe;
private int day;
//构造方法,传入一个代理对象的实例
public DynamicProxyHandler(Object targe) {
this.targe = targe;
}
/**第一个参数暂时不知道有什么用,可以忽略
* 第三个参数表示被代理对象的方法的参数,
* 该事务处理器一般用匿名内部类的方式实现,待会再改造,会隐式的调用该方法。
**/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**调用Method的invoke方法,第一个参数表示要调用该方法的对象,
* 第二个参数表示传入该方法的形参数组。
* 返回值为该方法的返回值,Object对象
* 在此处,第一个参数表示被代理对象的实例,第二个参数直接写args即可。
**/
method.invoke(this.targe,args);
return null;
}
}
在测试类中具体操作:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//创建一个被代理类的实例。
Subject realSubject = new RealSubject();
//创建一个事务处理器的实例,并传入被代理类的实例。
InvocationHandler dynamicProxyHandler = new DynamicProxyHandler(realSubject);
/**该方法会隐式的调用事务处理器的invoke方法,返回一个代理类对象
* 该方法的第一个参数表示:传入被代理类的类加载器
* 第二个参数表示:被代理类实现的接口,注意看,Subject.class这个类表示接口。
* 第三个参数表示:事务处理器。
*/
Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{ Subject.class},dynamicProxyHandler);
//通过代理类调用代理的行为
proxy.reservation();
//可能有些读者会认为proxy不就是被代理类吗?其实并不是,它是一个代理类,看下面输出的结果,证明它是一个proxy的子类
System.out.println("proxy的类型"+proxy.getClass());
}
}
输出结果:
订房成功!
proxy的类型class com.sun.proxy.$Proxy0
jdk的动态代理,此刻应该也明白得差不多,也就是我们并不需要去创建代理类,需要去写一个接口和一个被代理类即可。
下面在看看匿名内部类实现jdk动态代理的操作,上面的代码,把事务处理器的实现类删除,全部在测试类中进行。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class Test {
public static void main(String[] args) {
//创建一个被代理类的实例。
final Subject realSubject = new RealSubject();
Subject proxy = (Subject) Proxy.newProxyInstance(Subject.class.getClassLoader(),
new Class[]{Subject.class}, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
method.invoke(realSubject,args);
return null;
}
}
);
proxy.reservation();
System.out.println("proxy的类型是"+proxy.getClass());
}
}
输出结果:
订房成功!
proxy的类型是class com.sun.proxy.$Proxy0
使用匿名内部类是不是感觉到精简了很多。
写了这么多,不知道你明白了没有,这就是jdk动态代理。下面继续说,动态代理的另一种形式,cglib动态代理。
三、Cglib动态代理
cglib是一个java的开源项目,它能够在运行期间动态的生产字节码,它是通过字节码技术生成一个被代理类的子类作为代理类,再通过方法拦截技术拦截所有父类方法的调用,原理的实现也就是通过继承,也就要求被代理类不能是被final修饰的。
cglib要求我们实现MethodInterceptor接口并且重写intercept方法,类似jdk代理的事务处理器,在cglib中为父类拦截器,可以通过匿名内部类去实现。我们先通过一个类去实现该接口,之后再改造成匿名内部类。
RealSubject被代理类:
//cglib不需要实现接口
public class RealSubject {
public void reservation() {
System.out.println("订房成功!");
}
}
删除掉Subject接口,因为不需要了。
Maven导包,不会的直接去网上下载即可。
<!-- https://mvnrepository.com/artifact/cglib/cglib -->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>
创建一个类实现MethodInterceptor接口
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Cglib implements MethodInterceptor{
//被代理类
private Object target;
//传入被代理类的实例,返回值为返回被代理的对象。
public Object getInstance(Object target) {
this.target = target;
//创建一个Enhancer类,该类用于创建代理类。
Enhancer enhancer = new Enhancer();
//为Enhancer指定要代理的类。
enhancer.setSuperclass(target.getClass());
//设置回调拦截,此处会调用MethodInterceptor的intercept方法,是显示调用。
enhancer.setCallback(this);
//创建并返回代理对象,通过默认无参的构造方法创建
return enhancer.create();
}
/**
*
* @param object 目标类(被代理类)的实例
* @param method 目标类(被代理类)反射获得到的方法名
* @param args 目标类方法的参数
* @param proxy 代理类的实例
* @return
* @throws Throwable
*/
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//代理类去调用父类的方法实现动态代理
proxy.invokeSuper(object,args);
return null;
}
}
测试类
public class Test {
public static void main(String[] args) {
//创建一个被代理类的实例。
RealSubject realSubject = new RealSubject();
//创建一个cglib实例
Cglib cglib = new Cglib();
//调用getInstance方法创建代理对象。
RealSubject proxy = (RealSubject) cglib.getInstance(realSubject);
proxy.reservation();
//判断代理类的类型
System.out.println(proxy instanceof RealSubject);
}
}
输出结果:
订房成功!
true
这也就证明了cglib是通过继承来实现动态代理的。
下面再来看看匿名内部类的做法。
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class Test {
public static void main(String[] args) {
//创建一个被代理类的实例。
RealSubject realSubject = new RealSubject();
//创建一个Enhancer实例
Enhancer enhancer = new Enhancer();
//为哪个目标类(被代理类)创建代理
enhancer.setSuperclass(realSubject.getClass());
//设置回调,调用MethodInterceptor的intercept方法拦截父类
enhancer.setCallback(new MethodInterceptor() {
public Object intercept(Object object, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//代理类去调用父类的方法实现动态代理
proxy.invokeSuper(object,args);
return null;
}
});
//创建并返回代理对象,通过默认无参的构造方法创建
RealSubject proxy = (RealSubject) enhancer.create();
proxy.reservation();
System.out.println(proxy instanceof RealSubject);
}
}
好了,cglib动态代理就写这么多,当然不仅仅是这些东西,里面还有很多细节值得思考,这就需要你自己去深入研究。老铁们觉得不错的话,可以关注一波,谢谢!