Rick的博客

导航

【动态代理】底层实现的小例

我们来谈谈什么是动态代理。假设这样一个场景:

你们是一家软件公司,你是一位软件工程师。现在又客户带着一个需求来找你们公司做项目。显然不是找你直接谈,而是去找商务去谈,此时客户就会认为商务就代表你们公司。然后当客户与商务交流的差不多了,商务就会来找你接洽。显然商务与工程师之间是代理与被代理的关系。

Spring中通常用JDK和CGLIB。Mybatis中还是用了javassist

 

  • JDK与CGLIB

             jdk动态代理中,我们必须使用借口。而cglib则不需要。

 

下面我们讨论梁中最常用的代理:

JDK动态代理:

他必须通过接口才能生成代理对象,首先我们先生成一个接口

package com.rick.proxy;

public interface Hello {
	public void sayHello();
}

然后提供接口的实现类

package com.rick.proxy;

public class HelloImpl implements Hello{

	@Override
	public void sayHello() {
		System.out.println("say Hello......");		
	}

}

此时我们就可以开始动态代理了,分为两个步骤:

  1. 首先建立起代理对象和真是对象的关系
  2. 然后实现代理逻辑
package com.rick.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxyExample implements InvocationHandler{
	//真对象
	private Object target = null;
	//创建代理和真实对象的代理关系,并返回对象
	public Object bind(Object target){
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),this);
		/*
		 * 第一个参数是累的加载器,我们采用了target本身的类加载器
		 * 第二个参数是吧生成的动态代理对象挂在哪些接口下面
		 * 第三个表示定义实现方法逻辑的代理类,this表示当前对象他必须
		 *         实现InvocationHandler的invoke方法,他就是代理逻辑方法的实现方法
		 */
	}
	//实现代理逻辑
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		/*
		 * proxy为代理对象,就是bind方法生成的对象
		 * method为当前调度的方法
		 * args为调度方法的参数
		 */
		System.out.println("进入代理逻辑方法");
		System.out.println("在调度真实对象之前。。");
		Object obj = method.invoke(target, args);//相当于调用sayHello方法
		System.out.println("在调度真实对象之后。。");
		return obj;
	}
}

类比前面的例子,proxy相当于商务对象,target相当于软件工程师对象,bind方法就是建立商务和软件工程师代理关系的方法。而invoke就是商务逻辑,他控制软件工程师的访问

测试

package com.rick.proxy;

public class Test {
	public static void main(String[] args) {
		JdkProxy();
	}

	private static void JdkProxy() {
		JdkProxyExample jdkProxyExample = new JdkProxyExample();
		Hello proxy = (Hello) jdkProxyExample.bind(new HelloImpl());
		proxy.sayHello();
	}
}

 

CGLIB动态代理:

jkd动态代理必须提供接口才能使用,在一些不能提供接口的场景中,只能采取第三方技术。比如CGLIB动态代理。它的优势在于不需要提供接口,只要一个非抽象类就能实现动态代理。

例如:

package com.rick.proxy;

public class UserImpl {
	public void sayHello(String name) {
		
		System.out.println("Hello:"+name);
		
	}

}

他不存在实现任何借口,所以没有办法使用JDK动态代理,之力就要采用CGLIB动态代理

package com.rick.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 CglibProxyExample implements MethodInterceptor{
	public Object getProxy(Class cls) {
		// CGLIB enhancer 增强类对象
		Enhancer enhancer = new Enhancer();
		// 设置增强类型
		enhancer.setSuperclass(cls);
		// 定义代理逻辑对象为当前对象,要求当前对象实现MethodInterceptor方法
		enhancer.setCallback(this);
		//生成并返回代理对象
		return enhancer.create();
	}


	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		System.out.println("调用真是对象前");
		// CGLIB反射调用真实对象方法
		Object result = methodProxy.invokeSuper(proxy, args);
		System.out.println("调用真实对象后");
		return result;
	}
}

创建测试:

package com.rick.proxy;

public class Test {
	public static void main(String[] args) {
//		JdkProxy();
		CGLIBProxy();
	}

//	private static void JdkProxy() {
//		JdkProxyExample jdkProxyExample = new JdkProxyExample();
//		Hello proxy = (Hello) jdkProxyExample.bind(new HelloImpl());
//		proxy.sayHello();
//	}
	
	public static void CGLIBProxy() {
	    CglibProxyExample cglibProxyExample = new CglibProxyExample();
	    UserImpl proxy = (UserImpl) cglibProxyExample.getProxy(UserImpl.class);
	    proxy.sayHello("zhangsan");
	}
}

 

posted on 2020-01-19 15:32  Rui_Qi  阅读(95)  评论(0)    收藏  举报