什么是动态代理(dynamic proxy)
1)代理的是接口(Interfaces),不是类(Class)。也叫基于接口的jdk动态代理。
2)利用Java的反射技术,在运行时创建一个实现某些给定接口的新类,该类也叫代理类。
3)利用Java的Proxy类,调用Proxy.newProxyInstance(),创建动态对象。
4)给目标对象提供一个代理对象,并由代理对象控制对目标对象的引用
代理对象要明确一下几个概念
动态代理要明确 目标对象是谁 我要为谁生产代理对象 如何获取代理对象 代理对象能做什么
为什么要使用代理
在不改变目标对象方法的情况下对方法进行增强,。例如我们希望对方法的调用增加日志记录,或者对方法的调用进行拦截。(Aop)
代码解释
需求:实现一个计算器的功能,目的是想要在某个方法执行之前都加上日志输出,如果每个方法上都加上一句日志输出也可实现记录日志的功能。但是这种方法会造成代码冗余,并且后期修改和维护很不方便。通过以上的不好之处,也是我们作为开发人员必须要避免的事情。接下来我将通过jdk的动态代理方式来实现日志的输出功能。
目录结构
接口
package com.ghl.service;
public interface ArithmeticCalculator {
//计算器实现连两个位数相加
public int add( int i,int j);
}
接口实现类
package com.ghl.service.impl;
import com.ghl.service.ArithmeticCalculator;
/**
* 被代理类
*/
public class ArithmeticCalculatorImpl implements ArithmeticCalculator{
@Override
public int add(int i, int j) {
int result=i+j;
return result;
}
}
代理类
package com.ghl.proxy;
import java.lang.reflect.Array;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import com.ghl.service.ArithmeticCalculator;
import com.ghl.service.impl.ArithmeticCalculatorImpl;
/**代理类
*生成代理对象
*使用jdk的动态代理
*
*
*Proxy类:所有动态代理类的父类 专门用来生成动态代理类或代理对象
*两个方法:
*getProxyClass(ClassLoader,Class<>... interfaces);
* 用于生成代理类Class 代理类通过类加载器生产Class对象
*newInstanceProxy(loader,interfaces,invocationHandler)(常用此方法)
*用于生成代理对象
*
*invocationHandler接口(很重要)
*用来完成动态代理的整个过程 需要动态代理做什么事都要写在这里
*
*
*/
public class ArithmeticCalculatorProxy {
/*
* 动态代理要明确 目标对象是谁 我要为谁生产代理对象 如何获取代理对象 代理对象要做什么
*/
//1.目标对象
private ArithmeticCalculator target;
//显示生成代理类的构造方法:当前代理类为那个目标类做代理的意思 创建代理类对象的同时在指定目标i类对象
public ArithmeticCalculatorProxy(ArithmeticCalculator target) {
this.target=target;
}
//2.获得代理对象 以方法的形式生成代理对象
public Object getProxy() {
Object proxy;
/*
* loader:类加载器 ClassLoader对象 帮助我们加载动态生成代理类
* interfaces:接口们 jdk基于接口的动态代理 提供目标对象的所有接口 其实就是代理类要查看你的目标类接口都有那些方法 代理类也实现这个接口 代理类和目标类就有相同的方法
* 代理类有目标类的方法只是一个假象 最后还是要通过我目标类来调用我具体的方法
* invocationHandler:完成动态代理的全部过程
*/
ClassLoader loader=target.getClass().getClassLoader();//使用目标对象的类加载器
Class[] interfaces=target.getClass().getInterfaces();//目的是让代理对象保证与目标对象都拥有相同的方法
proxy=Proxy.newProxyInstance(loader, interfaces, new InvocationHandler() {
/*
* invoke:代理对象调用代理方法 会回来调用invoke方法
*
* proxy:生成的代理对象 也就是porxy 在invoke方法中一般不用
*
* method:正在被调用的一个方法对象
*
* args:正在被调用的方法的参数
*/
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {//真正的动态代理从invoke方法开始
// 记录日志
String methodName = method.getName();
System.out.println("LoggingProxy====》The method"+methodName+Arrays.asList(args));
//将方法的调用返回到目标对象上
Object result = method.invoke(target, args); //目标对象执行目标方法 相当于执行ArithmeticCalculatorImpl中的方法
//记录日志
System.out.println("日志输出结束,方法调用结束");
return result;
}
});
return proxy;
}
}
测试
package com.ghl.proxy;
import com.ghl.service.ArithmeticCalculator;
import com.ghl.service.impl.ArithmeticCalculatorImpl;
/**
* 测试动态代理类
*/
public class Main {
public static void main(String[] args) {
// 保存生成的代理类的字节码文件 动态代理在内存中进行
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
//1.创建目标类对象
ArithmeticCalculator target = new ArithmeticCalculatorImpl();
// 2 通过目标类对象获得代理类对象
Object obj = new ArithmeticCalculatorProxy(target).getProxy();
// 3.转化回具体的类型 必须是接口 因为目标类继承了接口 代理类也继承了接口 代理类和目标泪时兄弟关系
ArithmeticCalculator proxy = (ArithmeticCalculator) obj;
System.out.println(proxy.getClass().getName());
int result = proxy.add(1, 1);
System.out.println("result" + result);
}
}