Java 中的动态代理
Java cglib 面试 About 5,114 wordsJava 内置 API
使用Proxy
类代理一个对象,该对象必须实现一个接口。故Java
内置的API
只能代理实现了接口的对象。
private static class ProxyFactory {
// 返回代理类的对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler(obj);
return Proxy.newProxyInstance(obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), handler);
}
}
private static class MyInvocationHandler implements InvocationHandler {
// 被代理类的对象
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
// 当通过代理类的对象调用方法A时,就是自动调用invoke方法
// proxy: 代理类的对象
// method: 代理类调用的方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 被代理对象的方法返回值
System.out.println("调用真实方法前");
Object returnValue = method.invoke(obj, args);
System.out.println("调用真实方法后");
return returnValue;
}
}
测试
public static void main(String[] args) {
SuperMan superMan = new SuperMan();
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
System.out.println(proxyInstance.getBelief());
}
private interface Human {
String getBelief();
void eat(String food);
}
private static class SuperMan implements Human {
@Override
public String getBelief() {
System.out.println("调用getBelief方法");
return "I can fly";
}
@Override
public void eat(String food) {
System.out.println("调用eat方法");
System.out.println("喜欢吃#" + food);
}
}
输出
调用真实方法前
调用getBelief方法
调用真实方法后
I can fly
第三方库 cglib
不能对final
类以及final
方法进行代理。
测试类
public class App {
public String test(String input) {
System.out.println("test~~~~~~~~~~~~~input#" + input);
return input;
}
}
拦截方法
public void methodIntercept() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(App.class);
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("调用 " + method.getName() + " 前");
Object returnValue = proxy.invokeSuper(obj, args);
// 不能使用method调用,会造成递归 StackOverflow
//Object returnValue = method.invoke(obj, args);
System.out.println("调用 " + method.getName() + " 后");
return returnValue;
}
});
App obj = (App) enhancer.create();
obj.test("hello cglib");
}
输出:
调用 test 前
test~~~~~~~~~~~~~input#hello cglib
调用 test 后
拦截指定方法
不拦截Object
类中的方法以及方法返回值不为String
的方法。
public static void methodFilterIntercept() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(App.class);
CallbackHelper callbackHelper = new CallbackHelper(App.class, new Class[0]) {
@Override
protected Object getCallback(Method method) {
if(method.getDeclaringClass() != Object.class && method.getReturnType() == String.class){
return new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "From cglib";
}
};
}else{
return NoOp.INSTANCE;
}
}
};
enhancer.setSuperclass(App.class);
enhancer.setCallbackFilter(callbackHelper);
enhancer.setCallbacks(callbackHelper.getCallbacks());
App obj = (App) enhancer.create();
System.out.println("自定义test方法#" + obj.test(null));
System.out.println("toString方法#"+obj.toString());
System.out.println("getClass方法#" + obj.getClass());
System.out.println("hashCode方法#" + obj.hashCode());
}
输出:
自定义test方法#From cglib
toString方法#com.example.App$$EnhancerByCGLIB$$280c2728@2d8e6db6
getClass方法#class com.example.App$$EnhancerByCGLIB$$280c2728
hashCode方法#764308918
拦截返回值
修改返回值为固定值
public void fixValueIntercept() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(App.class);
enhancer.setCallback(new FixedValue() {
@Override
public Object loadObject() throws Exception {
return "From cglib FixedValue";
}
});
App obj = (App) enhancer.create();
System.out.println("自定义test方法#" + obj.test(null));
System.out.println("toString方法#"+obj.toString());
System.out.println("getClass方法#" + obj.getClass());
System.out.println("hashCode方法#" + obj.hashCode());
}
输出:可以看到test
和toString
因为都是非final
方法的,所以都被拦截修改了返回值;getClass
是final
修饰,故无法修改;hashCode
方法返回int
类型,但我们修改后返回自己输入的From cglib FixedValue
字符串类型,故抛出类型转换异常。
自定义test方法#From cglib FixedValue
toString方法#From cglib FixedValue
getClass方法#class com.example.App$$EnhancerByCGLIB$$de29798b
Exception in thread "main" java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Number
at com.example.App$$EnhancerByCGLIB$$de29798b.hashCode(<generated>)
at com.example.App.fixValueIntercept(App.java:40)
at com.example.App.main(App.java:19)
注意
由于cglib
大部分类是直接对Java
字节码进行操作,这样生成的类会在Java
的永久堆中。如果动态代理操作过多,容易造成永久堆满,触发OutOfMemory
异常。
cglib 开源地址
Views: 3,016 · Posted: 2021-04-01
————        END        ————
Give me a Star, Thanks:)
https://github.com/fendoudebb/LiteNote扫描下方二维码关注公众号和小程序↓↓↓
Loading...