代理机制
简单来说,代理机制是一种模式,是为了让我们在不修改被代理类源代码的情况下通过代理类去改变或者说控制代理类的执行,本质还是为了贯彻落实程序设计核心的开闭原则,即对扩展开放,对修改封闭;简单的说,代理机制分为两大类,静态代理和动态代理,这里只讲动态代理;动态代理的话又分为两大类,一是导入jar包的cglibproxy,一种是JAVA自带的jdkproxy,下面是两种模式的使用例子:
jdkproxy:
如果是jdkproxy的话,首先得准备好一个接口,然后用一个实现类去完成其中的方法,这个实现类就是被代理的类,所有的方法都必须在接口中声明,最后先new出被代理类的对象,然后用它的对象去new代理类也就是jddkproxy,再去调用jdkproxy中的getproxy()方法,返回一个接口的对象,然后用这个接口的对象去调用方法,这就是一全套的代理,也就是说jdk代理需要接口,最后是通过接口.方法来执行的,代码如下:
首先是jdkproxy(代理类)
package com.mec.about_proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy {
private Object object;
public JdkProxy() {
}
public JdkProxy(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
Class<?> klass = object.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
return (T) Proxy.newProxyInstance(classLoader, interfaces,
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
for(Object ob : args) {
System.out.println(ob);
}
System.out.println("前置拦截");
Object result = method.invoke(object, args);
System.out.println("后置拦截");
return result;
}
});
}
}
注意: 上面的invoke()方法中的三个参数
proxy:注意!!这个不是我们的被代理类!可以做测试,这个的类是com.sun.proxy.$Proxy0!
method:被代理类中要调用的方法;
args:被代理类中调用的方法的参数;
然后是被代理类和被代理类的接口:**
MyAction:
package com.mec.about_proxy.jdk.orginal;
public class MyAction implements IMyAction {
public MyAction() {
}
@Override
public String fun1(String message) {
System.out.println("执行了fun1()");
return "["+message+"]";
}
@Override
public void fun2() {
System.out.println("执行了fun2()");
}
public final void finalFun() {
System.out.println("执行了finalFun");
}
public void function() {
System.out.println("执行了普通fun");
}
}
IMyAction:
package com.mec.about_proxy.jdk.orginal;
public interface IMyAction {
String fun1(String message);
void fun2();
}
test:
package com.mec.about_proxy.jdk.test;
import com.mec.about_proxy.cglib.CglibProxy;
import com.mec.about_proxy.jdk.JdkProxy;
import com.mec.about_proxy.jdk.orginal.IMyAction;
import com.mec.about_proxy.jdk.orginal.MyAction;
public class Test {
public static void main(String[] args) {
MyAction ma = new MyAction();
JdkProxy jdkProxy = new JdkProxy(ma);
IMyAction ima = jdkProxy.getProxy();
ima.fun1("执行代理");
ima.fun2();
}
}
cglibproxy
cglibproxy和jdkproxy的不同之处就在于,它不需要接口,它的原理就是相当于生成了一个被代理类的子类,然后去对这个子类进行操作,需要注意的一点是cglibpoxy需要导包,可以去官网下载,具体代码实现如下:
cglibproxy:
package com.mec.about_proxy.cglib;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy {
private Object object;
public CglibProxy() {
}
public CglibProxy(Object object) {
this.object = object;
}
public Object getObject() {
return object;
}
public void setObject(Object object) {
this.object = object;
}
@SuppressWarnings("unchecked")
public <T> T getProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(object.getClass());
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
System.out.println("proxy代理的类:" + proxy.getClass().getName());
Object result = method.invoke(object, args);
System.out.println("代理执行后");
return result;
}
});
return (T) enhancer.create();
}
}
Action和IAction与之前相同
Test:
package com.mec.about_proxy.jdk.test;
import com.mec.about_proxy.cglib.CglibProxy;
import com.mec.about_proxy.jdk.orginal.MyAction;
public class Test {
public static void main(String[] args) {
MyAction ma = new MyAction();
CglibProxy cglibProxy = new CglibProxy(ma);
MyAction myActionProxy = cglibProxy.getProxy();
myActionProxy.fun1("这也是做了一些东西");
myActionProxy.function();
myActionProxy.finalFun();
}
}
代码结果就不展示了,有兴趣的自己尝试一下,然后再提一点,就是jdkproxy模式中你被代理的类的方法,一定要在接口中声明,因为你要使用接口.方法来调用;cglibproxy中,被代理类的方法如果是final,是无法被代理的;
再提一点
其实我们代理模式的使用还可以用在框架的设计中,如果这样,我们不能直接指定用户使用什么代理方式,我们应该写成一个工具,让用户通过使用我的工具来选择使用哪种代理模式,还有就是我们可以改进实现,比如getproxy()我们可以进行重载,让用户使用的时候选择将被代理类的对象传过来,或者将被代理类的类型传过来,或者什么都不传,而用被代理类的对象去new jdkproxy or new cglibproxy,这种的话会更加完善一点,感兴趣的话可以自己尝试一下。