JDK&CGLIB动态代理源码解析

一.代理模式

1.1.基本介绍

代理模式:一个对象提供一个替身,以控制对这个对象的访问。

即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上,增强额外的功能操作,即扩展目标对象的功能。-

代理模式有不同的形式,主要有三种静态代理、动态代理(JDK 代理、接口代理)和CGLIB 代理(可以在内存动态的创建对象,而不需要实现接口,他是属于动态代理的范畴)。

1.2.静态代理

1.2.1.基本介绍

静态代理在使用时,需要定义接口或者父类,被代理对象(即目标对象)与代理对象一起实现相同的接口或者是继承相同父类。

 定义一个接口

public interface Teacher {
	void teach();
}

定义一个实现类 

public class JTeacher implements Teacher {

	// 实现具体的内容
	public void teach() {
		System.out.println("Jak正在讲解代理模式");

	}
}

定义代理类 

// 静态代理的方式
public class TeacherProxy implements Teacher{
	// 定义引用
	private Teacher teacher;

	//具体的目标对象传入进来
	public TeacherProxy(Teacher teacher) {
		this.teacher = teacher;
	}
	public void teach() {
		System.out.println("begin...");
		// 调用目标对象的方法
		teacher.teach();
		System.out.println("end...");
	}
}

使用代理前结果:

使用代理后结果:

成功通过静态代理,对目标方法进行了增强

上述是实现了一个接口,假如有多个接口呢?

代理类就会做很大的修改,很不容易扩展维护

再定义一个接口

public interface School {
	void work();
}

实现此接口 

public class JTeacher implements Teacher, School {
	// 实现具体的内容
	public void teach() {
		System.out.println("Jak正在讲解代理模式");

	}
	// 在工作
	public void work() {
		System.out.println("Jak在清华工作");
	}
}

代理类进行修改

/ 静态代理的方式
public class TeacherProxy implements Teacher, School{
	// 定义引用
	private Teacher teacher;
	private School school;

	//具体的目标对象传入进来
	public TeacherProxy(Teacher teacher, School school) {
		this.teacher = teacher;
		this.school = school;
	}
	public void teach() {
		System.out.println("begin...");
		// 调用目标对象的方法
		teacher.teach();
		System.out.println("end...");
	}
	public void work() {
		System.out.println("begin...");
		school.work();
		System.out.println("end...");
	}
}

1.3 JDK动态代理

jdk代理是基于接口的代理,所以被代理的对象必须是有接口实现的类,代理创建时通过Proxy.newProxyInstance实现的,这个方法有三个参数:

//指定要使用的类加载器
ClassLoader loader,
//被代理的类所实现的接口,增强接口的方法
Class<?>[] interfaces,
//方法处理器,会拦截所有方法,然后执行增强参数。
InvocationHandler inoker

 定义一个类

public interface Teacher {
	void teach();
}

定义一个实现类 

public class JTeacher implements Teacher {
	// 实现具体的内容
	public void teach() {
		System.out.println("Jak正在讲解代理模式");

	}
}

定义一个生成代理的代理工厂

// 生成代理对象的工厂类
public class JdkProxyFactory implements InvocationHandler {
	// 定义目标对象引用
	private Object target;
	public void setTarget(Object target) {
		this.target = target;
	}
	// 提供方法,生成代理对象的
	public Object getProxy() {
		// 使用JDK的API方法
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("begin...");
		// 让目标对象的方法去执行,使用反射方式
		// 执行method
		Object result = method.invoke(target, args);
		System.out.println("end...");
		return result;
	}
}

测试结果:

再看一下动态代理,实现多个接口的情况

public class JTeacher implements Teacher, School {
	// 实现具体的内容
	public void teach() {
		System.out.println("Jak正在讲解代理模式");

	}
	// 在工作
	public void work() {
		System.out.println("Jak在清华工作");
	}
}

代理工厂不需要任何改动

// 生成代理对象的工厂类
public class JdkProxyFactory implements InvocationHandler {
	// 定义目标对象引用
	private Object target;
	public void setTarget(Object target) {
		this.target = target;
	}
	// 提供方法,生成代理对象的
	public Object getProxy() {
		// 使用JDK的API方法
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("begin...");
		// 让目标对象的方法去执行,使用反射方式
		// 执行method
		Object result = method.invoke(target, args);
		System.out.println("end...");
		return result;
	}
}

 测试结果

此时,我们的代理工厂代码就不用动,测试的时候,只需要School来接收,然后,调用相应的方法即可实现增强功能。这样以来,对我们程序的扩展就非常方便。

1.4 Cglib动态代理

public interface TargetInterface {

	String sayHello(String name);

	String sayThanks(String name);
}

 

public class TargetInterfaceImpl implements TargetInterface {

	public String sayHello(String name) {
		return "Hello, " + name;
	}

	public String sayThanks(String name) {
		return "Thanks, " + name;
	}
}

 

**
 * TargetProxy类还不是一个真正的代理类,它是代理类的一部分
 */
public class TargetProxy implements MethodInterceptor {
	// 获取真正的代理类
	public <T> T getProxy(Class clazz) {
		// 继承目标类,覆盖目标类的方法
		// 字节码增强的一个类
		Enhancer enhancer = new Enhancer();
		// 设置父类
		enhancer.setSuperclass(clazz);
		// 设置回调类,继承了MethodInterceptor,而它又继承了CallBack
		enhancer.setCallback(this);
		// 创建代理类
		return (T) enhancer.create();
	}

    /**
     * @param obj       CGLIB 生成的代理对象
     * @param method    被代理对象方法
     * @param args      方法入参
     * @param proxy     方法代理
     */
	// 即可以拦截sayHello,也可以拦截sayThanks
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
		System.out.println(method.getName() + "服务限流.......");
		// 调用目标方法(代理类是子类,目标类是父类)
		return proxy.invokeSuper(obj, args);
		/**
		// 获取一个接口的代理,那么需要在此对接口进行实现
		String hello = "Hello, " + args;
		return hello;
		 */
	}
}
public class Main {
	public static void main(String[] args) {
		// 通过参数设置,把动态代理生成的字节码.class文件输出到D盘
		System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:/");
		TargetProxy targetProxy = new TargetProxy();

		// 拿到目标类的代理类
		TargetInterfaceImpl targetInterface = targetProxy.getProxy(TargetInterfaceImpl.class);
		System.out.println(targetInterface.sayHello("jak"));
		System.out.println(targetInterface.sayThanks("jak"));
	}
}

完成了对目标方法的增强

二、源码解析

2.1 JDK动态代理源码

核心点:从源码-->编译好一个字节码文件.class-->类加载器变成对象-->构造器变成实例对象

类的加载机制类加载详解

	// 提供方法,生成代理对象的
	public Object getProxy() {
		// 使用JDK的API方法
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

对于上面这个方法,调用后就直接是一个对象了,对应流程图中,相当于已经到了实例对象User对象那步了,但是,前面的那些流程我们都没有做

肯定是Java替我们做了这些工作,那么它是怎么做的呢?

如果对反射创建对象不清楚的可以先看下这个:反射创建对象

// newProxyInstance


                
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值