一、何为代理?为何要用代理?
个人愚见,代理就是代替处理的意思。即,用另外的类(代理类)来处理已经存在的类(原类即被代理的类)。
使用代理这种模式,可以在不改变原类代码的基础上实现对原类方法的拦截,方法参数的识别等操作,也就是在调用原类方法的时候可以加以判别、限制等操作以达到自己的需求。使用这种方法的好处就是不需要改动原类代码(要知道,随意改动代码是一件很危险的事,很容易造成已有逻辑的混乱)也可以在调用原类时添加新的需求。
二、代理的分类
代理大致可以分为两类:静态代理和动态代理,动态代理又有两种方式(JDK和CGLib)。
下面会讲解各代理的实现以及其中代码的大致理解。
三、代理的实现
3.1、静态代理
静态代理的实现特别简单,那就让代理类也实现该接口,在代理类的方法中加以拦截,实现过程如下(由于静态代理是临时写的,原类就用为JDK准备的原类吧,只是名字的区别,不必在意)。
3.1.1、首先定义一个将要实现的接口
package com.about_jdk.classes;
public interface Iinterface1 {
void setId(String id);
String getId();
}
package com.about_jdk.classes;
public interface Iinterface2 {
void setPassword(String password);
String getPassword();
}
3.1.2、再定义原类
package com.about_jdk.classes;
public class JDKClassModel implements Iinterface1, Iinterface2{
private String id;
private String password;
public JDKClassModel() {
}
@Override
public String getId() {
System.out.println("ClassModel.getId()");
return id;
}
@Override
public void setId(String id) {
this.id = id;
System.out.println("ClassModel.setId(): id= " + id);
}
@Override
public void setPassword(String password) {
this.password = password;
System.out.println("ClassModel.setPassword(): password= " + password);
}
@Override
public String getPassword() {
System.out.println("ClassModel.getPassword()");
return password;
}
3.1.3、然后再定义代理类
package com.staticProxy.proxy;
import com.about_jdk.classes.Iinterface1;
import com.about_jdk.classes.Iinterface2;
import com.about_jdk.classes.JDKClassModel;
public class staticProxy implements Iinterface1, Iinterface2{
JDKClassModel model;
public staticProxy(JDKClassModel model) {
this.model = model;
}
@Override
public void setId(String id) {
System.out.println("置前拦截(staticProxy.setId)");
model.setId(id);
System.out.println("置后拦截(staticProxy.setId)");
}
@Override
public String getId() {
System.out.println("置前拦截(staticProxy.getId)");
String id = model.getId();
System.out.println("置后拦截(staticProxy.getId)");
return id;
}
@Override
public void setPassword(String password) {
System.out.println("置前拦截(staticProxy.setPassword)");
model.setPassword(password);
System.out.println("置后拦截(staticProxy.setPassword)");
}
@Override
public String getPassword() {
System.out.println("置前拦截(staticProxy.getPassword)");
String password = model.getPassword();
System.out.println("置后拦截(staticProxy.getPassword)");
return password;
}
3.1.4、定义测试类
package com.about_jdk.demo;
import com.about_jdk.classes.JDKClassModel;
import com.staticProxy.proxy.staticProxy;
public class StaticProxyTest {
public static void main(String[] args) {
staticProxy sp = new staticProxy(new JDKClassModel());
sp.setId("123");
sp.setPassword("000");
System.out.println("mian():id= " + sp.getId() + " password= " + sp.getPassword());
}
}
运行结果:
置前拦截(staticProxy.setId)
ClassModel.setId(): id= 123
置后拦截(staticProxy.setId)
置前拦截(staticProxy.setPassword)
ClassModel.setPassword(): password= 000
置后拦截(staticProxy.setPassword)
置前拦截(staticProxy.getId)
ClassModel.getId()
置后拦截(staticProxy.getId)
置前拦截(staticProxy.getPassword)
ClassModel.getPassword()
置后拦截(staticProxy.getPassword)
mian():id= 123 password= 000
小结:这个方法很简单,相信大家都能看的懂,当然,我所使用的类是有接口的,如果不使用接口,用这种方式代理,就必须在代理得中通过传进来的对象点出原类的方法并再手动定义一次原类中的方法,这当然也是可以实现的,但应该没人愿意这么做吧。这里就说一下它的局限性:假想一下,我们如果需要加入一个新的代理接口,就必须在代理类中实现新的接口,这是很麻烦,使用起来很不方便的。当然,也因此,将它称之为静态代理。接下来,我们看看比较高大上的动态代理。
3.2、JDK动态代理
JDK的代理是有接口的代理,拦截的也是接口中的方法,接下来我们看看他的实现。
3.2.1 定义接口(和上面静态代理的接口一致)
package com.about_jdk.classes;
public interface Iinterface1 {
void setId(String id);
String getId();
}
package com.about_jdk.classes;
public interface Iinterface2 {
void setPassword(String password);
String getPassword();
}
3.2.2 定义原类(与上面静态代理的一样)
package com.about_jdk.classes;
public class JDKClassModel implements Iinterface1, Iinterface2{
private String id;
private String password;
public JDKClassModel() {
}
@Override
public String getId() {
System.out.println("ClassModel.getId()");
return id;
}
@Override
public void setId(String id) {
this.id = id;
System.out.println("ClassModel.setId(): id= " + id);
}
@Override
public void setPassword(String password) {
this.password = password;
System.out.println("ClassModel.setPassword(): password= " + password);
}
@Override
public String getPassword() {
System.out.println("ClassModel.getPassword()");
return password;
}
}
3.2.3 定义代理类
package com.about_jdk.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class JdkProxy {
public JdkProxy() {
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass) throws Exception { // 方法重载,为了方面用户调用
Object obj = klass.newInstance();
return (T) getProxy(klass, obj);
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Object obj) { // 方法重载,为了方便用户调用
Class<?> klass = obj.getClass();
return (T) getProxy(klass, obj);
}
@SuppressWarnings("unchecked")
private <T> T getProxy(Class<T> klass, Object obj) { // 返回值为泛型,可让用户不再进行类型强转
return (T) Proxy.newProxyInstance(
klass.getClassLoader(), // 得到原类的加载
klass.getInterfaces(), // 得到原类实现的接口
new InvocationHandler() { // 查看原码,可以知道,这其实就是一个要实现如下invoke方法的接口
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 这里的三个参数分别是:代理类的对象(proxy),原类的方法(method),原类方法中的参数数组(args)
Object result = null;
System.out.println("前置拦截...");
// 这里的拦截可以加一些判断和自己需要的操作,让拦截器发挥真正的作用,这也是代理的必要所在,这里只讲代理的实现,便不再细讲
result = method.invoke(obj, args);
// 这是一个方法的反射,其中需要的东西有原类中的方法(method),原类方法中的参数数组(args),以及原类的对象(obj)
// 由运行结果可知,这里反射的就是原类中的方法,而这正式代理的关键一步,通过这一步便将原类和代理类联系在了一起。
// 我们可以看到,invoke传进来的三个参数用到了其中两个,那剩下的一个代理对象究竟有何作用。有代码可知,我们并没有得到原类的方法名及参数,
// 因此,我们没办法对其进行反射,所以我们可以猜想,java内部帮我们实现了这一步,而其所需要的就是我们的代理对象。
System.out.println("后置拦截...");
// 置后拦截同样可以添加很多自己需要的操作。
return result;
}
});
}
}
3.2.4、测试
package com.about_jdk.demo;
import com.about_jdk.classes.JDKClassModel;
import com.about_jdk.classes.Iinterface1;
import com.about_jdk.classes.Iinterface2;
import com.about_jdk.proxy.JdkProxy;
public class JDKTest {
public static void main(String[] args) {
Iinterface1 ii1 = new JdkProxy().getProxy(new JDKClassModel());
ii1.setId("06163063");
System.out.println("ii1.getId(): id= " + ii1.getId());
try {
Iinterface2 ii2 = new JdkProxy().getProxy(JDKClassModel.class);
ii2.setPassword("000000");
System.out.println("ii2.getPassword(): password= " + ii2.getPassword());
} catch (Exception e) {
e.printStackTrace();
}
}
}
运行结果:
前置拦截...
ClassModel.setId(): id= 06163063
后置拦截...
前置拦截...
ClassModel.getId()
后置拦截...
ii1.getId(): id= 06163063
前置拦截...
ClassModel.setPassword(): password= 000000
后置拦截...
前置拦截...
ClassModel.getPassword()
后置拦截...
ii2.getPassword(): password= 000000
小结:这个代理方式,代理的是接口中的方法,上述返回类型为泛型<T>,也是为了用户调用时不必再次强转。对于非接口实现的类中的方法,我们只能用CGLib代理来实现。
3.3、CGLib代理
3.3.1、定义原类
package com.about_cgl.classes;
public class CGLClassModel {
private String id;
private int money = 1000;
public CGLClassModel() {
}
public void setId(String id) {
this.id = id;
System.out.println("CGLClassModel.setId(): id=" + id);
}
public String getId() {
System.out.println("CGLClassModel.getId()");
return id;
}
public int changeMoney(int newMoney) {
this.money = newMoney;
System.out.println("CGLClassModel.changeMoney()");
return money;
}
}
3.3.2、定义代理类
package com.about_cgl.proxy;
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 CGLProxy {
public CGLProxy() {
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Class<?> klass) throws InstantiationException, IllegalAccessException {
Object obj = klass.newInstance();
return (T) getProxy(klass, obj);
}
@SuppressWarnings("unchecked")
public <T> T getProxy(Object obj) {
Class<?> klass = obj.getClass();
return (T) getProxy(klass, obj);
}
@SuppressWarnings("unchecked")
private <T> T getProxy(Class<?> klass, Object obj) {
Enhancer enhancer = new Enhancer(); // 定义并实例一个增强类
enhancer.setSuperclass(klass); // 指定父类
enhancer.setCallback(new MethodInterceptor() { // 回调该增强类
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
if(method.getName().equals("changeMoney")) {
System.out.println("有人修改money,已拦截!!!");
return null;
}else {
System.out.println("CGL前置拦截...");
result = method.invoke(obj, args);
}
System.out.println("CGL后置拦截...");
return result;
}
});
return (T) enhancer.create(); // 返回值为泛型,使用户不必再次强转
}
}
3.3.3、测试
package com.about_jdk.demo;
import com.about_cgl.classes.CGLClassModel;
import com.about_cgl.proxy.CGLProxy;
public class CGLibTest {
public static void main(String[] args) {
try {
CGLClassModel cglClassModel = new CGLProxy().getProxy(CGLClassModel.class);
cglClassModel.setId("123#");
cglClassModel.changeMoney(900);
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
运行结果:
CGL前置拦截...
CGLClassModel.setId(): id=123#
CGL后置拦截...
有人修改money,已拦截!!!
小结:这里只是对代理的简单实现,没有添加任何的骚操作。
总结:静态代理不再多说,对于动态代理,反射是它的基础,由代码可以看出,我们在调用的时候用的都是代理类,在代理类的执行中,先是执行前置拦截(可以加一些判断,如CGLib代理中给出的那样),再就是执行反射方法,这一步将执行的是原类的.class文件,对原类代码不会造成威胁,却有确确实实执行的是原类的代码(这是反射机制的功劳,详细了解可查询反射机制的实现),最后就是执行后置拦截,当然我们也可以添加一个异常拦截,对对原类反射执行的过程加以监控。
代理是一个很牛逼的模式,它的存在,让我们对已有的代码在不伤害其(改变其原码)的同时加以更多的控制。
希望这篇博文会对您有所帮助。