文章目录
设计模式
一、使用代理的原因
在开发中,经常会遇到之前已经有很多代码,但是又有新的需求,如果对之前的代码进行直接修改,那无疑是很大的工作量,如果采用代理模式,可以通过不修改源代码对代码的执行过程及其结果进行修改
二、代理模式的介绍
代理模式分为两种:静态代理模式和动态代理模式。
静态代理模式实际上就是目标对象已确定,即目标对象已经写死。
动态代理模式运行时才确定目标对象。
这里着重于动态代理的实现
三、动态代理的实现:接口实现
3.1、具体使用
下面的部分操作先可当巫师的咒语。
JDkproxy.class
public class JDKProxy {
public JDKProxy() {
}
@SuppressWarnings("unchecked")
public static <T> T getProxy(Class<?> klass) {
try {
Object object = klass.getDeclaredConstructor().newInstance();
return (T) getProxy(object);
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("unchecked")
public static <T> T getProxy(Object object) {
Class<?> klass = object.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
});
}
}
Interfaze.class
public interface Interfaze {
void doOneThing(String str);
String doOtherThing(int num, String str);
}
MyImplement.class Interfaze接口的实现类
public class MyImplement implements Interfaze{
public MyImplement() {
}
@Override
public void doOneThing(String str) {
System.out.println("doOneThing :" + str);
}
@Override
public String doOtherThing(int num, String str) {
System.out.println("doOtherThing :" + num + str);
return num + str;
}
}
Test.class
public class Text {
public static void main(String[] args) {
Interfaze oneproxy = JDKProxy.getProxy(new MyImplement());
oneproxy.doOneThing("abd");
String str = oneproxy.doOtherThing(34, "肥肠");
System.out.println(str);
}
}
输出结果
根据输出结果,可以看到Interfaze oneproxy可以去调用MyImplement()实现了它的方法。
不禁有人就开始发问了,为什么不用MyImplement()去他调用方法呢?非要绕这么大的弯!
你看输出结果,在执行方法的前后都执行了“前”和“后”的输出语句,这说明什么,我们在一定程度上修改了这个方法,而且没有破坏他。
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
//在执行这个真正的方法前,我们有这个方法method,有他的参数args,那完全可以在执行调用的方法之前进行提前处理,在执行调用方法之后进行善后处理。这就是代理模式的真正精髓所在。
3.2、源码分析
- 在经过上面的实例后,你是否会对以下问题好奇:
- 动态代理类 及其对象实例是如何生成的?
- 如何通过调用动态代理对象方法,从而调用目标对象方法?
此处有两个重要的源码分析点:
- 关注1:创建动态代理类 & 对象:通过调用处理器类对象
newProxyInstance()
解决的问题是:动态代理类 及其对象实例是如何生成的?
- 关注2:通过调用动态代理对象方法从而调用目标对象方法
解决的问题是:如何通过调用动态代理对象方法,从而调用目标对象方法?
下面,我们将主要分析这两处源码。
3.2.1、(关注1)创建动态代理类&对象:通过调用处理器类对象newProxyInstance()
<-- 关注1:调用处理器 类的newProxyInstance() -->
@SuppressWarnings("unchecked")
public static <T> T getProxy(Object object) { //这里的object就是代理对象
Class<?> klass = object.getClass();
ClassLoader classLoader = klass.getClassLoader();
Class<?>[] interfaces = klass.getInterfaces();
//Proxy.newProxyInstance()作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类实例,并最终返回
return (T) Proxy.newProxyInstance(classLoader, interfaces, new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
});
}
// 复写InvocationHandler接口的invoke()
// 动态代理对象调用目标对象的任何方法前,都会调用调用处理器类的invoke()
@Override
public Object invoke(Object proxy, Method method, Object[] args)
// 参数说明:
// 参数1:动态代理对象(即哪个动态代理对象调用了method()
// 参数2:目标对象被调用的方法
// 参数3:指定被调用方法的参数
throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后\n");
return result;
}
<-- 关注2:newProxyInstance()源码解析-->
// 作用:根据指定的类装载器、一组接口 & 调用处理器 生成动态代理类及其对象实例,并最终返回
public static Object newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h) throws IllegalArgumentException {
// 参数说明:
// 参数1:指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
// 参数2:指定目标对象的实现接口
// 即要给目标对象提供一组什么接口。若提供了一组接口给它,那么该代理对象就默认实现了该接口,这样就能调用这组接口中的方法
// 参数3:指定InvocationHandler对象。即动态代理对象在调用方法时,会关联到哪个InvocationHandler对象
...
// 仅贴出核心代码
// 1. 通过 为Proxy类指定类加载器对象 & 一组interface,从而创建动态代理类
// >>关注3
Class cl = getProxyClass(loader, interfaces);
// 2. 通过反射机制获取动态代理类的构造函数,其参数类型是调用处理器接口类型
Constructor cons = cl.getConstructor(constructorParams);
// 3. 通过动态代理类的构造函数 创建 代理类实例(传入调用处理器对象
return (Object) cons.newInstance(new Object[] { h });
// 特别注意
// 1. 动态代理类继承 Proxy 类 & 并实现了在Proxy.newProxyInstance()中提供的一系列接口(接口数组)
// 2. Proxy 类中有一个映射表
// 映射关系为:(<ClassLoader>,(<Interfaces>,<ProxyClass>) )
// 即:1级key = 类加载器,根据1级key 得到 2级key = 接口数组
// 因此:1类加载器对象 + 1接口数组 = 确定了一个代理类实例
...
// 回到调用关注2的原处
}
<-- 关注3:getProxyClass()源码分析 -->
// 作用:创建动态代理类
public static Class<?> getProxyClass(ClassLoader loader,Class<?>... interfaces)
throws IllegalArgumentException {
...
// 仅贴出关键代码
<-- 1. 将目标类所实现的接口加载到内存中 -->
// 遍历目标类所实现的接口
for (int i = 0; i < interfaces.length; i++) {
// 获取目标类实现的接口名称
String interfaceName = interfaces[i].getName();
Class interfaceClass = null;
try {
// 加载目标类实现的接口到内存中
interfaceClass = Class.forName(interfaceName, false, loader);
} catch (ClassNotFoundException e) {
}
if (interfaceClass != interfaces[i]) {
throw new IllegalArgumentException(
interfaces[i] + " is not visible from class loader");
}
}
<-- 2. 生成动态代理类 -->
// 根据传入的接口 & 代理对象 创建动态代理类的字节码
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
proxyName, interfaces);
// 根据动态代理类的字节码 生成 动态代理类
proxyClass = defineClass0(loader, proxyName,
proxyClassFile, 0, proxyClassFile.length);
}
// 最终返回动态代理类
return proxyClass;
}
// 回到调用关注3的原处
3.2.1总结
- 通过调用处理器类对象的
.newProxyInstance()
创建动态代理类 及其实例对象(需传入目标类对象) - 具体过程如下:
- 通过 为
Proy
类指定类加载器对象 & 一组接口,从而创建动态代理类的字节码;再根据类字节码创建动态代理类 - 通过反射机制获取动态代理类的构造函数(参数类型 = 调用处理器接口类型
- 通过动态代理类的构造函数 创建 代理类实例(传入调用处理器对象
- 通过 为
3.2.2、(关注2)通过调用动态代理对象方法从而调用目标对象方法
//使用代码
oneproxy.doOneThing("abd");
Interfaze oneproxy = JDKProxy.getProxy(new MyImplement());
System.out.println("oneProxy : " + oneproxy);
System.out.println(Proxy.class.isAssignableFrom(oneproxy.getClass()));
//输出结果:oneProxy : class com.sun.proxy.$Proxy0 true
// 这里关于isAssignableFrom()理解这个就可以 :父类.class.isAssignableFrom(子类.class)
//发现oneproxy其实是proxy的子类
oneproxy.doOneThing("abd");
// 该方法的逻辑实际上是调用了父类Proxy类的h参数的invoke()
// h参数即在Proxy.newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)传入的第3个参数InvocationHandler对象
// 即调用了调用处理器的InvocationHandler.invoke()
// 而复写的invoke()利用反射机制:Object result=method.invoke(proxied,args)
// 从而调用目标对象的的方法
3.2.2总结
- 动态代理类实现了与目标类一样的接口,并实现了需要目标类对象需要调用的方法
- 该方法的实现逻辑 = 调用父类
Proxy
类的h.invoke()
其中h参数 = 在创建动态代理实例中
newProxyInstance(ClassLoader loader, Class<?>[]interfaces,InvocationHandler h)
传入的第3个参数InvocationHandler
对象
- 在
InvocationHandler.invoke()
中通过反射机制,从而调用目标类对象的方法
四、动态代理的实现:继承实现
这里需要cglib- nodep -2.1.3.jar这个架包
4.1、具体使用
public class CGLIBproxy {
public CGLIBproxy() {
}
public static <T> T getProxy(Class<?> klass) {
try {
return getProxy(klass.newInstance());
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
@SuppressWarnings("unchecked")
public static <T> T getProxy(Object object) {
Class<?> klass = object.getClass();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后");
return result;
}
});
return (T) enhancer.create();
}
}
Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。它的功能与java自带的Proxy类挺相似的。它会根据某个给定的类创建子类,并且所有非final的方法都带有回调钩子。
4.2、方法介绍
Callback-MethodInterceptor
方法拦截器。这个东西和JDK自带的InvocationHandler很类似
Object intercept(Object obj, java.lang.reflect.Method method, Object[] args, MethodProxy proxy) throws Throwable
这其中MethodProxy proxy参数一般是用来调用原来的对应方法的。比如可以proxy.invokeSuper(obj, args)
。那么为什么不能像InvocationHandler那样用method来调用呢?因为如果用method调用会再次进入拦截器。为了避免这种情况,应该使用接口方法中第四个参数methodProxy调用invokeSuper方法。
测试类:
Complex twoProxy = CGLIBproxy.getProxy(Complex.class);
twoProxy.setReal(10);
twoProxy.setVir(-1.3);
System.out.println(twoProxy);
五、总结
接口实现:
继承实现:
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(klass);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object result = null;
System.out.println("前");
result = method.invoke(object, args);
System.out.println("后");
return result;
}
});