jdk动态代理和Cglib代理
spring 中有许多牛逼的功能,这些功能都是通过AOP实现的,而AOP实现的基础就是JDK动态代理和Cglib代理。spring 运用Aop技术实现的功能。
- spring事务管理:@Transactional
- spring异步处理:@EnableAsync
- spring缓存技术的使用:@EnableCaching
- spring中各种拦截器:@EnableAspectJAutoProxy
jdk动态代理
JDK 代理的特点
- 只能为接口创建代理对象
- 创建出来的代理都是java.lang.reflect.Proxy的子类
JDK 代理对象创建的步骤
1、创建一个 InvocationHandler 的对象,实现invoke方法
2、Proxy的静态方法newProxyInstance创建代理对象,需要三个参数,分别是类加载器,代理对象实现的接口,第一步生成的InvocationHandler 对象作为参数
JDK代理案例
有两个接口,分别是 IService1, IService2
public interface IService1 {
void method1();
}
public interface IService2 {
void method2();
}
有一个 Service3 实现了 接口 IService1, IService2
public class Service3 implements IService1,IService2 {
@Override
public void method1() {
System.out.println("执行method1");
}
@Override
public void method2() {
System.out.println("执行method2");
}
}
通过Proxy的静态方法newProxyInstance创建代理对象,对目标类Service3 的对象进行代理,计算目标对象方法调用的耗时情况.
public class CostTimeInvocationHandler implements InvocationHandler {
private Object target;
public CostTimeInvocationHandler(Object object){
this.target = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startNanoTime = System.nanoTime();
Object invoke = method.invoke(this.target, args); //调用目标方法
System.out.println(method+",耗时(纳秒)"+ (System.nanoTime()-startNanoTime));
return invoke;
}
}
public class JdkAopTest1 {
public static void main(String[] args) {
Service3 target = new Service3();
CostTimeInvocationHandler costTimeInvocationHandler = new CostTimeInvocationHandler(target);
// 创建代理对象 ,第一个参数为类加载器,第二个参数为代理对象实现的接口,第三个参数为 InvocationHanler ,代理对象实现的逻辑
Class<?>[] interfaces = {IService1.class, IService2.class};
@SuppressWarnings("unchecked")
Object proxyObject = Proxy.newProxyInstance(target.getClass().getClassLoader(), interfaces, costTimeInvocationHandler);
//判断代理对象是否是Service3类型的,肯定是false咯
System.out.println(String.format("proxyObject instanceof Service = %s", proxyObject instanceof Service3));
//判断代理对象是否是IService1类型的,肯定是true
System.out.println(String.format("proxyObject instanceof IService1 = %s", proxyObject instanceof IService1));
//判断代理对象是否是IService2类型的,肯定是true
System.out.println(String.format("proxyObject instanceof IService2 = %s", proxyObject instanceof IService2));
//将代理转换为IService1类型
IService1 service1 = (IService1) proxyObject;
//调用IService2的method1方法
service1.method1();
//将代理转换为IService2类型
IService2 service2 = (IService2) proxyObject;
//调用IService2的method2方法
service2.method2();
//输出代理的类型
System.out.println("代理对象的类型:" + proxyObject.getClass());
}
}
执行结果
proxyObject instanceof Service = false
proxyObject instanceof IService1 = true
proxyObject instanceof IService2 = true
执行method1
public abstract void com.jim.ioc.test.proxy.IService1.method1(),耗时(纳秒)313900
执行method2
public abstract void com.jim.ioc.test.proxy.IService2.method2(),耗时(纳秒)24600
代理对象的类型:class com.sun.proxy.$Proxy0
method1 ,method2 被CostTimeInvocationHandler#invoke 方法增强了,增加了计算方法调用耗时情况
Cglib代理
Cglib代理的特点
Cglib优势是不管你是接口还是类都可以通过Cglib来创建对象,而JDK代理只能为接口创建对象。
Cglib 是通过继承的方式创建对象,代理对象继承被代理对象,然后重新父类的方法,在重写的方法中通过super调用父类的方法来实现增强。被曾强的方法必须是可继承可重写的的。因此不能是被final修饰的类,被final修饰的方法,static修饰的方法不能被重写,private修饰的方法也不能被重写。除了这些方法都可以别重写,被重写的方法可以通过cglib进行拦截增强。
cglib 代理对象创建的步骤
1.创建Enhancer对象
2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
3.设置回调,需实现org.springframework.cglib.proxy.Callback接口
4.获取代理对象,调用enhancer.create方法获取代理对象,这个方法返回的是Object类型的,所以需要强转一下
案例1 :拦截所有方法 MethodInterceptor
public class Service3 implements IService1,IService2 {
@Override
public void method1() {
System.out.println("执行method1");
}
@Override
public void method2() {
System.out.println("执行method2");
}
public void method3(){
System.out.println("执行method3");
}
}
下面我们为这个类创建一个代理,代理中实现打印每个方法的调用日志。
public class CgllibAopTest1 {
public static void main(String[] args) {
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service3.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;
}
});
Service3 o = (Service3)enhancer.create();
o.method1();
o.method2();
o.method3();
}
}
创建enhancer 对象,enhancer.setSuperclass(Service3.class) , 用来设置代理的父类为Service3
enhancer.setCallback 设置的是MethodInterceptor 类型的参数,MethodInterceptor 接口有个intercept方法,这个方法会拦截所有代理对象的所有方法的调用,Object result = methodProxy.invokeSuper(o, objects);可以调用被代理类,从方法名可以看出调用的是父类的方法。
执行结果
调用方法:public void com.jim.ioc.test.proxy.Service3.method1()
执行method1
调用方法:public void com.jim.ioc.test.proxy.Service3.method2()
执行method2
调用方法:public void com.jim.ioc.test.proxy.Service3.method3()
执行method3
>从结果中可以看到Service3所有方法调用都被MethodInterceptor的intercept方法拦截处理了
案例2: 拦截所有的方法
修改Service3,在method1() 方法中调用method3().
public class Service3 implements IService1,IService2 {
@Override
public void method1() {
System.out.println("执行method1");
method3();
}
@Override
public void method2() {
System.out.println("执行method2");
}
public void method3(){
System.out.println("执行method3");
}
}
和案例1一样创建对象,现在只调用method1
public static void main(String[] args) {
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service3.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;
}
});
Service3 o = (Service3)enhancer.create();
o.method1();
}
}
执行结果
调用方法:public void com.jim.ioc.test.proxy.Service3.method1()
执行method1
调用方法:public void com.jim.ioc.test.proxy.Service3.method3()
执行method3
从执行结果中可以看出,method1,method3都被拦截了,method3 是在method1 中调用的居然也被拦截了。把method3改为私有的方法就不会被拦截。
案例3 :拦截所有方法并返回固定值(FixedValue)
当调用某个类的任何方法的时候,都希望返回一个固定的值。可以使用FixedValue
创建一个类Service4,如下:
public class Service4 {
public String method1() {
System.out.println("执行method1");
return "hello:method1";
}
public String method2() {
System.out.println("执行method2");
return "hello:method2";
}
public String method3(){
System.out.println("执行method3");
return "hello:method3";
}
}
测试用例
@Test
public void test(){
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service4.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "jim";
}
});
Service4 service4 = (Service4) enhancer.create();
System.out.println(service4.method1());
System.out.println(service4.method2());
System.out.println(service4.method3());
}
执行结果
jim
jim
jim
可以看出输出的都是一个值。
案例4:直接放行,不做任何操作(NoOp.INSTANCE)
@Test
public void test1(){
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service4.class);
enhancer.setCallback(NoOp.INSTANCE);
Service4 service4 = (Service4) enhancer.create();
System.out.println(service4.method1());
System.out.println(service4.method2());
System.out.println(service4.method3());
}
执行结果
执行method1
hello:method1
执行method2
hello:method2
执行method3
hello:method3
从输出中可以看出,被调用的方法没有被代理做任何处理,直接进到目标类Service4的方法中了
案例5:不同的方法使用不同的拦截器(CallbackFilter)
创建一个类Service5,如下:
public class Service5 {
public void insert1(){
System.out.println("执行insert1");
}
public void insert2(){
System.out.println("执行insert2");
}
public String get1(){
System.out.println("执行get1");
return "get1";
}
public String get2(){
System.out.println("执行get2");
return "get2";
}
}
需求,给这个类创建一个代理需要实现下面的功能:
- 以insert开头的方法需要统计方法耗时
- 以get开头的的方法直接返回固定字符串` 卧槽,无情
@Test
public void test2(){
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service5.class);
Callback [] callbacks = new Callback[]{
//这个用来拦截所有insert开头的方法
new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
}
},
//下面这个用来拦截所有get开头的方法,返回固定值的
new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "卧槽,无情";
}
}};
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(new CallbackFilter() {
@Override
public int accept(Method method) {
return method.getName().contains("insert")?0:1;
}
});
Service5 service5 = (Service5) enhancer.create();
service5.insert1();
service5.insert2();
System.out.println(service5.get1());
System.out.println( service5.get2());
}
}
执行结果
执行insert1
public void com.jim.ioc.test.proxy.Service5.insert1(),耗时(纳秒):16517000
执行insert2
public void com.jim.ioc.test.proxy.Service5.insert2(),耗时(纳秒):61700
卧槽,无情
卧槽,无情
由于需求中要对不同的方法做不同的处理,所以需要有2个Callback对象,当调用代理对象的方法的时候,具体会走哪个Callback呢,此时会通过
CallbackFilter
中的accept
来判断,这个方法返回callbacks数组的索引
案例6:对案例5的优化(CallbackHelper)
CallbackHelper 是个抽象类,它实现了CallbackFilter接口,有个getCallback的抽象方法,这个方法在CallbackHelper的构造函数中被调用,在CallbackHelper 构造函数执行后,在CallbackHelper类中有个List 类型callbacks属性,,callbacks会保存getCallback返回的Callback,Map类型methodMap属性保存了Method和Callback的索引关系,CallbackHelper还提供了一个getCallbacks()方法,用于返回所有的Callback。
@Test
public void test3(){
//1.创建Enhancer对象
Enhancer enhancer = new Enhancer();
//2.通过setSuperclass来设置父类型,即需要给哪个类创建代理类
enhancer.setSuperclass(Service5.class);
//这个用来拦截所有insert开头的方法
MethodInterceptor methodInterceptor = new MethodInterceptor() {
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
Object result = methodProxy.invokeSuper(o, objects);
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
}
};
//下面这个用来拦截所有get开头的方法,返回固定值的
FixedValue fixedValue = ()-> "卧槽,无情";
CallbackHelper callbackHelper = new CallbackHelper(Service5.class, null) {
@Override
protected Object getCallback(Method method) {
return method.getName().contains("insert") ? methodInterceptor : fixedValue;
}
};
Callback[] callbacks = callbackHelper.getCallbacks();
enhancer.setCallbacks(callbacks);
enhancer.setCallbackFilter(callbackHelper);
Service5 service5 = (Service5) enhancer.create();
service5.insert1();
service5.insert2();
System.out.println(service5.get1());
System.out.println( service5.get2());
}
执行结果
执行insert1
public void com.jim.ioc.test.proxy.Service5.insert1(),耗时(纳秒):21521800
执行insert2
public void com.jim.ioc.test.proxy.Service5.insert2(),耗时(纳秒):48000
卧槽,无情
卧槽,无情
案例7:LazyLoader的使用
LazyLoader是cglib用于实现懒加载的callback。当被增强bean的方法初次被调用时,会触发回调,之后每次再进行方法调用都是对LazyLoader第一次返回的bean调用。
@Test
public void Test5(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service4.class);
LazyLoader lazyLoader = new LazyLoader() {
@Override
public Object loadObject() throws Exception {
System.out.println("调用LazyLoader的loadObject方法");
return new Service4();
}
};
enhancer.setCallback(lazyLoader);
Service4 service4 = (Service4)enhancer.create();
System.out.println("第一次调用");
service4.method1();
System.out.println("第二次调用");
service4.method1();
}
执行结果
第一次调用
调用LazyLoader的loadObject方法
执行method1
第二次调用
执行method1
调用LazyLoader的loadObject方法只调用一次
案例8 :Dispatcher
Dispatcher和LazyLoader作用很相似,区别是用Dispatcher的话每次对增强bean进行方法调用都会触发回调。
@Test
public void Test6(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(Service4.class);
Dispatcher lazyLoader = new Dispatcher() {
@Override
public Object loadObject() throws Exception {
System.out.println("调用Dispatcher的loadObject方法");
return new Service4();
}
};
enhancer.setCallback(lazyLoader);
Service4 service4 = (Service4)enhancer.create();
System.out.println("第一次调用");
service4.method1();
System.out.println("第二次调用");
service4.method1();
}
执行结果
第一次调用
调用Dispatcher的loadObject方法
执行method1
第二次调用
调用Dispatcher的loadObject方法
执行method1
案例9:NamingPolicy接口
接口NamingPolicy表示生成代理类的名字的策略,通过Enhancer.setNamingPolicy
方法设置命名策略。
默认的实现类:DefaultNamingPolicy, 具体cglib动态生成类的命名控制。
DefaultNamingPolicy中有个getTag方法。
DefaultNamingPolicy生成的代理类的类名命名规则:
被代理class name + "$$" + 使用cglib处理的class name + "ByCGLIB" + "$$" + key的hashcode
spring 提供的
public class SpringNamingPolicy extends DefaultNamingPolicy {
public static final SpringNamingPolicy INSTANCE = new SpringNamingPolicy();
@Override
protected String getTag() {
return "BySpringCGLIB"
}
}
自定义
@Test
public void test7(){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SpringNamingPolicy.class);
enhancer.setCallback(NoOp.INSTANCE);
//通过Enhancer.setNamingPolicy来设置代理类的命名策略
enhancer.setNamingPolicy(new DefaultNamingPolicy() {
@Override
protected String getTag() {
return "-test-";
}
});
Object proxy = enhancer.create();
System.out.println(proxy.getClass());
}
执行结果
class com.jim.ioc.test.proxy.SpringNamingPolicy$$Enhancer-test-$$78e188d6
案例10 :Objenesis:实例化对象的一种方式
cglib中提供了一个接口:Objenesis ,它提供一种可以不使用它的构造方法创建Java对象,所以即使你有空的构造方法,也是不会执行的。
public static class User {
private String name;
public User(String name) {
this.name = name;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
'}';
}
}
@Test
public void test8(){
Objenesis objenesis = new ObjenesisStd();
User user = objenesis.newInstance(User.class);
System.out.println(user);
}
执行结果
User{name='null'}
案例11:实现通用的统计任意类方法耗时代理类
package com.jim.ioc.test.proxy;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* @author Jim
* @date 2021/5/14 16:49
*/
public class CostTimeCglibProxy implements MethodInterceptor {
private Object target;
public CostTimeCglibProxy(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long starTime = System.nanoTime();
//调用被代理对象(即target)的方法,获取结果
Object result = method.invoke(target, objects); //@1
long endTime = System.nanoTime();
System.out.println(method + ",耗时(纳秒):" + (endTime - starTime));
return result;
}
public static <T> T createProxy(T target) {
CostTimeCglibProxy costTimeCglibProxy = new CostTimeCglibProxy(target);
Enhancer enhancer = new Enhancer();
enhancer.setCallback(costTimeCglibProxy);
enhancer.setSuperclass(target.getClass());
return (T) enhancer.create();
}
}
我们可以直接使用上面的静态方法
createProxy
来为目标对象target创建一个代理对象,被代理的对象自动实现方法调用耗时统计。调用被代理对象的方法获取真正的结果。
@Test
public void test4() {
Service4 proxy = CostTimeCglibProxy.createProxy(new Service4());
proxy.method1();
Service5 service5 = CostTimeCglibProxy.createProxy(new Service5());
service5.get1();
}
执行结果
执行method1
public java.lang.String com.jim.ioc.test.proxy.Service4.method1(),耗时(纳秒):51000
执行get1
public java.lang.String com.jim.ioc.test.proxy.Service5.get1(),耗时(纳秒):48800
CGLIB和Java动态代理的区别
-
Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;
-
Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效