jdk动态代理和Cglib代理

jdk动态代理和Cglib代理

spring 中有许多牛逼的功能,这些功能都是通过AOP实现的,而AOP实现的基础就是JDK动态代理和Cglib代理。spring 运用Aop技术实现的功能。

  1. spring事务管理:@Transactional
  2. spring异步处理:@EnableAsync
  3. spring缓存技术的使用:@EnableCaching
  4. spring中各种拦截器:@EnableAspectJAutoProxy

jdk动态代理

JDK 代理的特点

  1. 只能为接口创建代理对象
  2. 创建出来的代理都是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";
	}
}

需求,给这个类创建一个代理需要实现下面的功能:

  1. 以insert开头的方法需要统计方法耗时
  2. 以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动态代理的区别

  1. Java动态代理只能够对接口进行代理,不能对普通的类进行代理(因为所有生成的代理类的父类为Proxy,Java类继承机制不允许多重继承);CGLIB能够代理普通类;

  2. Java动态代理使用Java原生的反射API进行操作,在生成类上比较高效;CGLIB使用ASM框架直接对字节码进行操作,在类的执行过程中比较高效

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值