JDK动态代理 案例
**需求: ** 简单的实现增强(AOP)
JDK动态代理只能代理实现了接口的类,不支持对类的直接代理
在此提供一个接口和一个实现类, 需要对接口实现类中的方法进行增强
1 2 3 4 5
| public interface IHello { public void hello(); public void bye(); }
|
1 2 3 4 5 6 7 8 9 10 11
| public class HelloImpl implements IHello{ @Override public void hello() { System.out.println("Hello"); } @Override public void bye() { System.out.println("Bye"); } }
|
增强部分:
首先需要自定义类实现InvocationHandler
接口, 其次重写invoke
方法, 通过invoke方法进行代理: 案例如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37
| public class ProxyHandler implements InvocationHandler { private Object target;
public ProxyHandler(Object target) { this.target = target; } public Object createProxy(){ Object proxy = Proxy.newProxyInstance(ProxyHandler.class.getClassLoader(), target.getClass().getInterfaces(), this); return proxy; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equalsIgnoreCase("hello")){ showTime(); } Object reflectObj = method.invoke(target, args);
return reflectObj; } private void showTime() { System.out.println("当前时间为: " + new Date()); } }
|
- 对于24-26行: 对于方法名为 hello进行增强, 此处以前置增强为例, 在方法执行(invoke)前, 执行自定义方法(showTime)显示时间
测试方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| public class Main { public static void main(String[] args) { System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true"); IHello target = new HelloImpl(); ProxyHandler handler = new ProxyHandler(target); Object proxy = handler.createProxy(); System.out.println(proxy);
IHello obj = (IHello) proxy; obj.hello(); obj.bye(); } }
|
输出如下:
1 2 3 4
| com.hang.HelloImpl@61bbe9ba 当前时间为: Sat Nov 25 10:31:56 SGT 2023 Hello Bye
|
可以很明显的看到, 在hello方法执行前, 输出了自定义增强方法showTime
此时需要观察生成代理字节码类可以在测试中添加
1
| System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
|
添加该行后, 在当前Module的同目录下会生成文件夹为com.sum.proxy
,文件夹内会生成字节码$Proxy0 … $Proxy20 … 数字部分可变
以当前案例生成的字节码为例: ($Proxy0.class)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| public final class $Proxy0 extends Proxy implements IHello { private static Method m1; private static Method m4; private static Method m3; private static Method m2; private static Method m0;
public $Proxy0(InvocationHandler var1) throws { super(var1); }
public final void bye() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final void hello() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m4 = Class.forName("com.hang.IHello").getMethod("bye"); m3 = Class.forName("com.hang.IHello").getMethod("hello"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
|
通过该字节码文件可以很明显的发现一共生成了 5个 Method (两个为刚刚定义的方法, 三个(1.equals 2.toString 3.hashCode)为默认生成的)
在代码尾部的static部分, 生成了方法上述的5个方法
以当前字节码文件中的 hello()
为例 (因为我们对该方法增强了):
该方法主要逻辑为执行super.h.invoke(this, m3, (Object[])null);
总结: 通过代理接口实现接口方法, 当调用时执行代理类中的同名方法然后回调到我们自定义的invoke, 在该方法内可以实现增强(前置,后置,环绕)
JDK谓词
直接上案例
1 2 3 4
| List<String> ns= Arrays.asList("calyee blog","cal","ccaaalyyyyeeee"); ArrayList<String> names = new ArrayList<>(ns); Predicate<String> predicate = s -> s.length() > 3; names.removeIf(predicate);
|
追踪Predicate
1 2 3 4 5 6 7 8 9
| @FunctionalInterface public interface Predicate<T> { boolean test(T t); default Predicate<T> and(Predicate<? super T> other) { Objects.requireNonNull(other); return (t) -> test(t) && other.test(t); } }
|
当前案例为: 筛选长度大于3的对象
其中removeIf
方法为传入一个谓词对象, 通过谓词条件构造筛选条件, 然后重复遍历(类似于构造if-else判断)
所以: 谓词工厂就是一个判断
自定义实现Xxx