动态代理是不通过硬编码,动态生成代理类的技术,分为JDK动态代理与CGlib动态代理两种方式;
假设我们来到了火影忍者的世界,有一个Ninja(忍者)类,它实现了接口Warrior接口
public interface Warrior {
public String fight(String args);
}
public class Ninja implements Warrior{
public String name;
public int chakra;
public Ninja(String name,int chakar){
this.name = name;
this.chakra = chakar;
}
public String fight(String args){
StringBuilder builder = new StringBuilder();
builder.append(name).append("发动忍术:【").append(args).append("】,查克拉量:").append(chakra);
String fighting_trick =builder.toString();
System.out.print(fighting_trick);
return fighting_trick;
}
public static void main(String[] args){
Ninja ninja = new Ninja("长门",100);
ninja.fight("地爆天星");
}
}
输出为:
长门发动忍术:【地爆天星】,查克拉量:100
那么通过动态代理,给他进行增强(开挂)该怎么做呢?
影分身之术-JDK动态代理
忍术的施展(函数调用)交给InvocationHandler
去处理,它只有一个函数invoke(...)
,函数签名如下;
Object invoke(Object proxy, Method method, Object[] args)
而影分身(动态代理类)对象由:Proxy.newProxyInstance()
生成,它会返回指定接口的代理类的实例; 它需要三个参数:
- Classloader 类加载器
- Interfaces 接口数组
- 实现InvocationHandler 接口的类
代理对象会将方法调用(method invacation
)分发到给定的调用处理器invocation handler
上,原函数通过反射机制进行调用Method.invoke(...)
;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class NinjaHandler implements InvocationHandler {
Ninja ninjaCopy;//分身
public NinjaHandler(Ninja ninja){
this.ninjaCopy=ninja;
}
/** * @param proxy 代理类 * @param method 正在调用的方法 * @param args 方法的参数 * @return obj * @throws Throwable */
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("Before NinjaHandler Invoke");
System.out.println("Call Method:"+method);
ninjaCopy.chakra*=10;//将查克拉量*10
Object obj=method.invoke(ninjaCopy,args);
System.out.println("After NinjaHandler Invoke");
System.out.println("Return Result:"+obj);
return obj;
}
}
我们在JDK动态代理中,将分身的查克拉量(属性)*10,然后我们开始调用使用JDK动态代理增强之后的影分身;
public class Test(){
public static void main(String[] args){
public static void main(String[] args){
//要代理的真实对象
Ninja Ninja=new Ninja("长门",100);
InvocationHandler NinjaHandler=new NinjaHandler(Ninja);
Warrior dynamicProxy=(Warrior)Proxy.newProxyInstance(
NinjaHandler.getClass().getClassLoader(),
Ninja.getClass().getInterfaces(),
NinjaHandler
);
String result = dynamicProxy.fight("hello world");
System.out.println("Result is: " + result);
System.out.println(dynamicProxy.getClass().getName());
System.out.println("是否为同一对象:"+dynamicProxy.getClass().isInstance(Ninja));
System.out.println("是否继承自同一接口:"+(dynamicProxy instanceof Warrior));
}
}
输出结果如下,可以看到原Ninja类被增强了:
Before NinjaHandler Invoke
Call Method:public abstract java.lang.String mock.dynamicProxy.Warrior.fight(java.lang.String)
长门发动忍术:【hello world】,查克拉量:1000
After NinjaHandler Invoke
Return Result:长门发动忍术:【hello world】,查克拉量:1000
Result is: 长门发动忍术:【hello world】,查克拉量:1000
=============
com.sun.proxy.$Proxy0
是否为同一对象:false
是否继承自同一接口:true
血继限界-CGlib代理
CGlib代理机制,主要是实现MethodInterceptor接口,intercept(...)
方法,其函数签名如下:
Object intercept(Object var1, Method var2, Object[] var3, MethodProxy var4) throws Throwable;
然后由Enhancer生成动态代理,Enhancer负责制定父类setSuperclass
,设定好方法回调setCallback
;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
public class CglibProxy implements MethodInterceptor {
private Object target;
public CglibProxy(Object target){
this.target=target;
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("### before invocation");
Object result = method.invoke(target, objects);
System.out.println("### end invocation");
return result;
}
public static Object getProxy(Object target) {
Enhancer ninjaChild = new Enhancer();
// 设置需要代理的对象
ninjaChild.setSuperclass(target.getClass());
// 设置代理人
ninjaChild.setCallback(new CglibProxy(target));
return ninjaChild.create();
}
}
测试函数:
Ninja ninjaSuccessor=(Ninja)CGlibProxy.getProxy(new Ninja("长门",100));
String res=ninjaSuccessor.fight("hello world");
System.out.println("Result is: " + res);
输出,这个类似于鸣人与博人的关系,继承其父类(血继限界):
### before invocation
长门发动忍术:【hello world】,查克拉量:100
### end invocation
Result is: 长门发动忍术:【hello world】,查克拉量:100
图示总结如下:
今天的文章影分身之术-Java动态代理分享到此就结束了,感谢您的阅读。
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
如需转载请保留出处:https://bianchenghao.cn/19799.html