一.代理模式
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