代理模式(静态代理)
为其他对象提供一种代理以控制对这个对象的访问。对调用的业务方法作拦截、过滤、预处理,主要是在方法中首先进行预处理动作,然后调用业务实现类的方法,还可以规定调用后的操作。
静态代理示例:
1:首先定义一个接口,说明业务逻辑。
public interface Subject {
/**
* static Proxy
*/
void request();
}
2:然后,定义业务实现类,实现业务逻辑接口
public class RealSuject implements Subject {
@Override
public void request() {
System.out.println("*** static proxy do request !***");
}
}
3:定义业务代理类
public class Proxy implements Subject {
private Subject realSuject;//创建一个业务实现类对象来调用具体的业务方法
public Proxy(Subject realSuject) {//构造器
this.realSuject = realSuject;
}
@Override
public void request() {
System.out.println("预处理——————");//进行预处理操作
if (realSuject != null) {
realSuject.request();//调用真正的业务方法
}
System.out.println("结束处理——————");//进行调用后操作
}
}
4:测试方法
private static void staticProxy() {
RealSuject realSuject = new RealSuject();//先创建业务实现类对象
Proxy proxy = new Proxy(realSuject);//把业务实现类对象作构造参数创建一个代理类对象
proxy.request();//进行业务方法的调用
}
静态代理的缺点很明显:一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。这时我们可以定义这样一个代理类,它能代理所有实现类的方法调用:根据传进来的业务实现类和方法名进行具体调用。——那就是动态代理。
二:JDK动态代理
原理:
1:拿到被代理对象的引用,然后获取它的接口
2:JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
3:拿到被代理对象的引用
4:重新生成一个class字节码
5:然后编译
字节码重组
JDK动态代理所用到的代理类在程序调用到代理类对象时才由JVM真正创建,JVM根据传进来的 业务实现类对象以及方法名 ,动态地创建了一个代理类的class文件并被字节码引擎执行,然后通过该代理类对象进行方法调用。我们需要做的,只需指定代理类的预处理、调用后操作即可。
1:首先,定义业务逻辑接口
public interface Subject {
/**
* dynamicProxy
*/
public abstract void request();
}
2:然后,实现业务逻辑接口创建业务实现类
public class RealSujectImpl implements Subject {
@Override
public void request() {
System.out.println("*** dynamicProxy do request ***");
}
}
3:采用JDK动态代理必须实现InvocationHandler接口,采用Proxy类创建相应的代理类
public class SubjectInvocationHandler implements InvocationHandler {
/**
* 目标对象,用来调用具体的业务方法
*/
private Object target;
public SubjectInvocationHandler(Object target) {
this.target = target;
}
/**
* 包装调用方法:进行预处理、调用后处理 反射,这样你可以在不知道具体的类的情况下,根据配置的参数去调用一个类的方法。在灵活编程的时候非常有用。
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("----Dynamic Proxy invoke method Start ----");
Object result = method.invoke(target, args);//方法调用
System.out.println("----Dynamic Proxy invoke method End----");
return result;
}
public Object getProxy() {
//获取当前线程的 classloder
ClassLoader loader = Thread.currentThread().getContextClassLoader();
//获取所有的interface
Class<?>[] interfaces = target.getClass().getInterfaces();
return Proxy.newProxyInstance(loader, interfaces, this);
}
}
4:测试
private static void dynamicProxy() {
RealSujectImpl realSubject = new RealSujectImpl();//创建一个业务实现类对象
SubjectInvocationHandler handler = new SubjectInvocationHandler(realSubject);//创建一个代理类对象
Subject subject = (Subject) handler.getProxy();//(向上转型)
subject.request();//调用
}
JDK动态代理的代理对象在创建时,需要使用业务实现类所实现的接口作为参数(因为在后面代理方法时需要根据接口内的方法名进行调用)。如果业务实现类是没有实现接口而是直接定义业务方法的话,就无法使用JDK动态代理了。并且,如果业务实现类中新增了接口中没有的方法,这些方法是无法被代理的(因为无法被调用)。
三:CGlib动态代理
cglib是针对类来实现代理的,原理是对指定的业务类生成一个子类,并覆盖其中业务方法实现代理。因为采用的是继承,所以不能对final修饰的类进行代理。
1:首先定义业务类,无需实现接口
public class RealSujectImpl2 {
public void request() {
System.out.println("*** dynamicProxy do request ***");
}
}
2:实现 MethodInterceptor方法代理接口,创建代理类,需要导入cglib.jar,然后使用Enhancer类与MethodInterceptor接口
public class SubjectCglib implements MethodInterceptor {
private Object targerObj; //原对象
//通过原对象返回代理对象
public Object createInstance(Object targerObj) {
this.targerObj = targerObj;
Enhancer en = new Enhancer();
return en.create(this.targerObj.getClass(), this.targerObj.getClass().getInterfaces(), this);
}
// 实现回调方法
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("用户验证...");
Object result = methodProxy.invoke(targerObj, args);
System.out.println("xx用户调用xx方法");
return result;
}
}
3:创建业务类和代理类对象,然后通过代理类对象.createInstance(业务类对象)返回一个动态代理类对象。最后通过动态代理类对象进行方法调用。
/**
* 动态代理
*/
private static void dynamicCglibProxy() {
RealSujectImpl2 realSubject = new RealSujectImpl2();
SubjectCglib cglib = new SubjectCglib();
RealSujectImpl2 sujectCglib = (RealSujectImpl2)cglib.createInstance(realSubject);
sujectCglib.request();
}
优点:
1、一个动态代理类更加简单了,可以解决创建多个静态代理的麻烦,避免不断的重复多余的代码
2、调用目标代码时,会在方法“运行时”动态的加入,决定你是什么类型,才调谁,灵活
缺点:
1、系统灵活了,但是相比而言,效率降低了,比静态代理慢一点
2、动态代理比静态代理在代码的可读性上差了一点,不太容易理解
3、JDK动态代理只能对实现了接口的类进行代理
静态代理VS动态代理,打成了平手,各自有各的独特之处,均不可代替,在项目中到底使用哪种代理,没有最好,只有更合适。