代理,或者称为Proxy。意思就是你不需要去做,别人代替你去处理。它在程序开发中起到了非常重要的作用,比如传说中的AOP,就是针对代理的一种应用。此外,在设计模式中,还有一个代理模式。在公司里要上外网,要在浏览器设置一个HTTP代理,可见代理无处不在。
一:简单的代理
学习由浅到深,先看一个简单的HelloWorld,首先写一个接口:
package org.smart4j.framework.test;上面是一个接口,下面是实现类:
/**
* Created by jack on 2017/6/27.
*/
public interface Hello {
void sayHello(String name);
}
package org.smart4j.framework.test;
/**
* Created by liangboqun on 2017/6/27.
*/
public class HelloImp implements Hello{
public void sayHello(String name) {
System.out.println("Hello ! "+name);
}
}
如果要在println方法前面和后面分别需要处理一些逻辑,该怎么做了?把这些逻辑写死在方法里面吗?这样代码肯定不优雅,这时候我们就可以使用代理模式,写一个HelloProxy类,让它去调用HelloImp的sayHello方法,在调用的前后分别进行逻辑处理,代码如下:
package org.smart4j.framework.test;
/**
* Created by liangboqun on 2017/6/27.
*/
public class HelloProxy implements Hello{
private Hello hello;
public void sayHello(String name) {
before();
hello.sayHello(name);
after();
}
public HelloProxy() {
this.hello = new HelloImp();
}
private void before(){
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
用HelloProxy类实现了Hello接口,并且在构造方法中new出一个HelloImp类的实例,这样一来,我们就可以在HelloProxy的sayHello方法去调用HelloImp的sayHello方法了。更重要的是,我们还可以在调用的前后分别加上before与after方法,在这两个方法里去实现那些前后逻辑。
下面是一个测试类,代码如下:
package org.smart4j.framework.test;
/**
* Created by liangboqun on 2017/6/27.
*/
public class MainTest1 {
public static void main(String [] args){
Hello helloProxy = new HelloProxy();
helloProxy.sayHello("JACK");
}
}
运行测试程序,输出如下:
看是不是很简单,其实这只是入门,并且是静态代理,真正的代理远远没这么简单。
二:JDK动态代理
下面是使用JDK提供的动态代理方案,代码如下:
package org.smart4j.framework.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* Created by jack on 2017/6/27.
* 使用JDK实现动态代理
*/
public class DynamicProxy implements InvocationHandler{
private Object target;
public DynamicProxy(Object target) {
//保存真实的对象
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//通过反射调用函数
Object result = method.invoke(target,args);
after();
//返回函数调用的返回结果
return result;
}
private void before(){
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
}
在DynamicProxy类中,定义了一个Object类型的target变量,它就是被代理的目标对象,通过构造函数来初始化。 DynamicProxy实现了InvocationHandler接口,那么必须实现该接口的invoke方法,参数是JRE给我们传递的,直接通过反射区invoke method,在调用前后,分别处理before与after,最后将result返回。
下面是测试代码:
package org.smart4j.framework.test;
import java.lang.reflect.Proxy;
/**
* Created by jack on 2017/6/27.
*/
public class MainTest1 {
public static void main(String[] args) {
/* Hello helloProxy = new HelloProxy();
helloProxy.sayHello("JACK");*/
//测试JDK实现的动态代理
Hello hello = new HelloImp();
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello helloProxy = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), dynamicProxy);
helloProxy.sayHello("jack");
}
}
上面代码的意思就是用这个通用的DynamicProxy类去包装HelloImp实例,然后再调用JDK给我们提供的Proxy类的工厂方法new ProxyInstance去动态地创建一个Hello接口的代理类,最后调用这个代理类的sayHello方法。
运行结果如下:
注意看测试代码,Proxy.newProxyInstance方法的参数是不是醉啦
参数1:ClassLoader
参数2:该实现类的所有接口
参数3:动态代理对象
调用完了还要来一个强制类型转换一下。
这一块需要想办法封装一下,避免再次出现到处是Proxy.newProxyInstance方法的情况。于是将这个DynamicProxy重构一下,代码如下:
package org.smart4j.framework.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by jack on 2017/6/27.
* 使用JDK实现动态代理
*/
public class DynamicProxy implements InvocationHandler{
private Object target;
public DynamicProxy(Object target) {
//保存真实的对象
this.target = target;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before();
//通过反射调用函数
Object result = method.invoke(target,args);
after();
//返回函数调用的返回结果
return result;
}
private void before(){
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
public <T> T getProxy(){
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
在DynamicProxy里添加了一个getProxy方法,无需传入任何参数,将刚才所说的那一块代码放在这个方法中,并且该方法返回一个泛型类型,就不会强制转换类型了。修改测试代码,如下:
package org.smart4j.framework.test;
import java.lang.reflect.Proxy;
/**
* Created by jack on 2017/6/27.
*/
public class MainTest1 {
public static void main(String[] args) {
/* Hello helloProxy = new HelloProxy();
helloProxy.sayHello("JACK");*/
//测试JDK实现的动态代理
/* Hello hello = new HelloImp();
DynamicProxy dynamicProxy = new DynamicProxy(hello);
Hello helloProxy = (Hello) Proxy.newProxyInstance(hello.getClass().getClassLoader(), hello.getClass().getInterfaces(), dynamicProxy);
helloProxy.sayHello("jack");*/
DynamicProxy dynamicProxy = new DynamicProxy(new HelloImp());
Hello helloProxy = dynamicProxy.getProxy();
helloProxy.sayHello("jack");
}
}
重构之后,使用简单的几行代码就实现了动态代理了。
三:CGlib动态代理
用了这个DynamicProxy以后,还是非常好的,好的地方是接口变了,这个动态代理类不用动。而静态代理就不一样了,接口变了,代理类也要动。但动态代理并不是万灵丹,它也有搞不定的时候,比如要代理一个没有任何接口的类,它就没有用武之地了。这个时候就只能使用CGlib这个类库。在spring和hibernate中都有使用CGlib实现动态代理。它是在一个运行期间动态生成字节码的工具,也就是动态生成代理类了,下面使用CGlib实现动态代理,写一个CGLibProxy:
package org.smart4j.framework.test;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by jack on 2017/6/27.
* 使用CGlib实现动态代理
*/
public class CGLibProxy implements MethodInterceptor{
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(object,args);
after();
return result;
}
private void before(){
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
public <T> T getProxy(Class<T> cls){
return (T) Enhancer.create(cls,this);
}
}
注意:需要引入cglib类库,代码如下:
<!--添加cglib类库,实现动态代理-->
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
需要实现CGLib给我们提供的MethodInterceptor实现类,并填充intercept方法。方法中最后一个MethodProxy类型的参数proxy需要注意。CGLlib给我们提供的是方法级别的代理,也可以理解为对方法的拦截。这个供暖对我们程序员而言,如同雪中送炭。我们直接调用proxy的invokeSuper方法,将代理的对象obj以及方法参数args传入其中即可。
与DynamicProxy类似,在CGlibProxy中也添加了一个getProxy方法,便于我们可以快速地获取自动生成的代理对象。下面是测试代码:
package org.smart4j.framework.test;
/**
* Created by jack on 2017/6/27.
* CGlib动态代理测试
*
*/
public class MainTest2 {
public static void main(String[] args) {
CGLibProxy cgLibProxy = new CGLibProxy();
Hello helloProxy = cgLibProxy.getProxy(HelloImp.class);
helloProxy.sayHello("jack");
}
}
通过2行代码就可以返回代理对象,与JDK动态代理不同的是,这里不需要任何的接口信息,对谁都可以生成动态代理对象。
用2行代码返回代理对象还是有一些多余,不行总是取new这个CGLibProxy对象,最后new一次,以后随时拿随时用,于是我们可以采用单例模式,代码如下:
package org.smart4j.framework.test;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* Created by jack on 2017/6/27.
* 使用CGlib实现动态代理
*/
public class CGLibProxy implements MethodInterceptor{
private static CGLibProxy instance = new CGLibProxy();
public CGLibProxy() {
}
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
before();
Object result = methodProxy.invokeSuper(object,args);
after();
return result;
}
private void before(){
System.out.println("Before");
}
private void after(){
System.out.println("After");
}
public <T> T getProxy(Class<T> cls){
return (T) Enhancer.create(cls,this);
}
public static CGLibProxy getInstance(){
return instance;
}
}
测试代码如下:
package org.smart4j.framework.test;
/**
* Created by jack on 2017/6/27.
* CGlib动态代理测试
*
*/
public class MainTest2 {
public static void main(String[] args) {
Hello helloProxy = CGLibProxy.getInstance().getProxy(HelloImp.class);
helloProxy.sayHello("jack");
}
}
只需一行代码就可以获取代理对象。测试结果如下: