1.为什么需要代理
我们先看一个案例:
有一个接口类:
public interface IService {
void play();
void sleep();
void eat();
}
有一个实现类A:
public class ServiceA implements IService {
@Override
public void play() {
System.out.println("serviceA -->" + " play");
}
@Override
public void sleep() {
System.out.println("serviceA -->" + " sleep");
}
@Override
public void eat() {
System.out.println("serviceA -->" + " eat");
}
}
有一个实现类B:
public class ServiceB implements IService{
@Override
public void play() {
System.out.println("serviceB -->" + " play");
}
@Override
public void sleep() {
System.out.println("serviceB -->" + " sleep");
}
@Override
public void eat() {
System.out.println("serviceB -->" + " eat");
}
}
测试类:
public class ServiceTest {
public static void main(String[] args) {
IService serviceA = new ServiceA();
IService serviceB = new ServiceB();
serviceA.eat();
serviceA.play();
serviceA.sleep();
serviceB.eat();
serviceB.play();
serviceB.sleep();
}
}
这时,如果要求:调用IService接口中的任何方法的时候,需要记录方法的耗时。IService接口有2个实现类ServiceA和ServiceB,我们可以在这两个类的所有方法中加上统计耗时的代码,如果IService接口有几十个实现,是不是要修改很多代码,所有被修改的方法需重新测试?
因此我们可以为IService接口创建一个代理类:
public class ServiceProxy {
private IService target;
ServiceProxy(IService target){
this.target = target;
}
void proxyPlay(){
long l = System.currentTimeMillis();
target.play();
long ll = System.currentTimeMillis() - l;
System.out.println(ll);
}
.....
}
测试类:
public static void main(String[] args) {
IService serviceA = new ServiceA();
ServiceProxy serviceProxy = new ServiceProxy(serviceA);
serviceProxy.proxyPlay();
...
}
这样,我们只需调用代理类,然后传入具体实现类,就可以现实记录方法的耗时,不需要在每个实现类都添加计算耗时的代码了。但是这样做,我们有个问题,每个接口都需要生成一个代理类。当接口类多的时候,这会是一个很耗时的操作。因此,需要一个通用的代理类来满足上面的功能。
2.jdk动态代理
jdk自带的代理使用有个限制,只能为接口创建代理类。如果为具体的类创建代理类,可以用cglib
直接上代理:
public class JDKProxy {
static Object createProxy(Object target){
Object proxyInstance = Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long starTime = System.nanoTime();
method.invoke(target, args);
long endTime = System.nanoTime();
return null;
}
});
return proxyInstance;
}
}
测试:
public static void main(String[] args) {
IService proxy = (IService) JDKProxy.createProxy(new ServiceA());
proxy.sleep();
}
这里只是提供简单的用法,另外Proxy类中有几个方法,需要掌握。
3.cglib代理
cglib底层使用了ASM(一个精悍的字节码操作框架)来操作字节码生成新的类
案例1:拦截所有方法(MethodInterceptor)
类A:
public class ServiceA {
public void eat() {
System.out.println("我是serviceA里面的eat()方法。。。");
}
public void sleep() {
System.out.println("我是serviceA里面的sleep()方法。。。");
}
}
CGLIB代理类:
public static void main(String[] args) {
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(ServiceA.class);
/**
* 3.设置回调,需实现org.springframework.cglib.proxy.Callback接口,
* 此处我们使用的是org.springframework.cglib.proxy.MethodInterceptor,也是一个接口,实现了Callback接口,
* 当调用代理对象的任何方法的时候,都会被MethodInterceptor接口的invoke方法处理
*/
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("调用方法:" + method);
//可以调用MethodProxy的invokeSuper调用被代理类的方法
Object result = methodProxy.invokeSuper(o, objects);
return result;
}
});
//4.获取代理对象,调用enhancer.create方法获取代理对象,这个方法返回的是Object类型的,所以需要强转一下
ServiceA serviceA = (ServiceA) enhancer.create();
serviceA.sleep();
serviceA.eat();
}
执行结果:
...
调用方法:public void com.wtc.cglibProxy.ServiceA.sleep()
我是serviceA里面的sleep()方法。。。
调用方法:public void com.wtc.cglibProxy.ServiceA.eat()
我是serviceA里面的eat()方法。。。
...
enhancer.setSuperclass用来设置代理类的父类,即需要给哪个类创建代理类,此处是Service1
enhancer.setCallback传递的是MethodInterceptor接口类型的参数,MethodInterceptor接口有个intercept方法,这个方法会拦截代理对象所有的方法调用。
还有一个重点是Object result = methodProxy.invokeSuper(o, objects);可以调用被代理类,也就是Service1类中的具体的方法,从方法名称的意思可以看出是调用父类,实际对某个类创建代理,cglib底层通过修改字节码的方式为Service1类创建了一个子类。
从输出中可以看出Service1中的2个方法都被MethodInterceptor中的invoke拦截处理了。
案例2:拦截所有的方法(MethodInterceptor)注意:在方法中在调用另一个方法,看看是否会拦截
我们把ServiceA中的方法中,添加另一个方法的调用:
public class ServiceA {
public void eat() {
System.out.println("我是serviceA里面的eat()方法。。。");
sleep();
}
public void sleep() {
System.out.println("我是serviceA里面的sleep()方法。。。");
}
}
测试:
public static void main(String[] args) {
...
ServiceA serviceA = (ServiceA) enhancer.create();
serviceA.eat();
}
控制台输出:
...
调用方法:public void com.wtc.cglibProxy.ServiceA.eat()
我是serviceA里面的eat()方法。。。
调用方法:public void com.wtc.cglibProxy.ServiceA.sleep()
我是serviceA里面的sleep()方法。。。
...
从输出可以看到eat和sleep方法都被拦截器拦截了。
案例2:拦截所有的方法,并返回固定值(FixedValue)
我们改变代理类的方法
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
System.out.println("fixedValue...");
return null;
}
});
serviceA的变动:
public class ServiceA {
public String eat() {
System.out.println("我是serviceA里面的eat()方法。。。");
return "eat";
}
public String sleep() {
System.out.println("我是serviceA里面的sleep()方法。。。");
return "sleep";
}
}
控制台输出:
fixedValue...
fixedValue...
案例4:不拦截方法
代理类:
//其他不变
enhancer.setCallback(NoOp.INSTANCE);
//其他不变
ServiceA:
public class ServiceA {
public String eat() {
System.out.println("我是serviceA里面的eat()方法。。。");
return "eat";
}
public String sleep() {
System.out.println("我是serviceA里面的sleep()方法。。。");
return "sleep";
}
}
控制台输出:
...
我是serviceA里面的eat()方法。。。
我是serviceA里面的sleep()方法。。。
...
案例5:不同的方法进入不同的拦截器(CallbackFilter)
ServiceB类:
public class ServiceB {
public void get1() {
System.out.println("serviceB -- > get1()");
}
public void get2() {
System.out.println("serviceB -- > get2()");
}
public void insert1() {
System.out.println("serviceB -- > insert1()");
}
public void insert2() {
System.out.println("serviceB -- > insert2()");
}
}
代理类:
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(ServiceB.class);
Callback[] callbacks = {
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("methodInterceptor.." + method);
Object o1 = methodProxy.invokeSuper(o, objects);
return o1;
}
},
new FixedValue() {
@Override
public Object loadObject() throws Exception {
System.out.println("fixedValue...");
return "fixedValue";
}
}
};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
//获取当前调用的方法的名称
String methodName = method.getName();
/**
* 方法名称以insert开头,
* 返回callbacks中的第1个Callback对象来处理当前方法,
* 否则使用第二个Callback处理被调用的方法
*/
return methodName.startsWith("insert") ? 0 : 1;
}
});
ServiceB serviceB = (ServiceB) enhancer.create();
serviceB.get1();
serviceB.get2();
serviceB.insert1();
serviceB.insert2();
}
控制台输出:
fixedValue...
fixedValue...
methodInterceptor..public void com.wtc.cglibProxy.ServiceB.insert1()
serviceB -- > insert1()
methodInterceptor..public void com.wtc.cglibProxy.ServiceB.insert2()
serviceB -- > insert2()
由于需求中要对不同的方法做不同的处理,所以需要有2个Callback对象,当调用代理对象的方法的时候,具体会走哪个Callback呢,此时会通过CallbackFilter中的accept来判断,这个方法返回callbacks数组的索引。
CGLIB和Java动态代理的区别
Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效