网站制作合同书,炒股配资网站建设,深圳网站设计公司,wordpress4.4.7漏洞反射和动态代理放有一定的相关性#xff0c;但单纯的说动态代理是由反射机制实现的#xff0c;其实是不够全面不准确的#xff0c;动态代理是一种功能行为#xff0c;而它的实现方法有很多。要怎么理解以上这句话#xff0c;请看下文。
一、反射
反射机制是 Java 语言提…反射和动态代理放有一定的相关性但单纯的说动态代理是由反射机制实现的其实是不够全面不准确的动态代理是一种功能行为而它的实现方法有很多。要怎么理解以上这句话请看下文。
一、反射
反射机制是 Java 语言提供的一种基础功能赋予程序在运行时自省introspect官方用语的能力。通过反射我们可以直接操作类或者对象比如获取某个对象的类定义获取类声明的属性和方法调用方法或者构造对象甚至可以运行时修改类定义。
1、获取类Class对象
获取类对象有三种方法
通过forName() - 示例Class.forName(“PeopleImpl”)通过getClass() - 示例new PeopleImpl().getClass()直接获取.class - 示例PeopleImpl.class
2、类的常用方法
getName()获取类完整方法getSuperclass()获取类的父类newInstance()创建实例对象getFields()获取当前类和父类的public修饰的所有属性getDeclaredFields()获取当前类不包含父类的声明的所有属性getMethod()获取当前类和父类的public修饰的所有方法getDeclaredMethods()获取当前类不包含父类的声明的所有方法
更多方法http://icdn.apigo.cn/blog/class-all-method.png
3、类方法调用
反射要调用类中的方法需要通过关键方法“invoke()”实现的方法调用也分为三种
静态static方法调用普通方法调用私有方法调用
以下会分别演示各种调用的实现代码各种调用的公共代码部分如下
// 此段代码为公共代码
interface People {int parentAge 18;public void sayHi(String name);
}
class PeopleImpl implements People {private String privSex 男;public String race 汉族;Overridepublic void sayHi(String name) {System.out.println(hello, name);}private void prvSayHi() {System.out.println(prvSayHi~);}public static void getSex() {System.out.println(18岁);}
}3.1 静态方法调用
// 核心代码省略了抛出异常的声明
public static void main(String[] args) {Class myClass Class.forName(example.PeopleImpl);// 调用静态static方法Method getSex myClass.getMethod(getSex);getSex.invoke(myClass);
}静态方法的调用比较简单使用 getMethod(xx) 获取到对应的方法直接使用 invoke(xx)就可以了。
3.2 普通方法调用
普通非静态方法调用需要先获取类示例通过“newInstance()”方法获取核心代码如下
Class myClass Class.forName(example.PeopleImpl);
Object object myClass.newInstance();
Method method myClass.getMethod(sayHi,String.class);
method.invoke(object,老王);getMethod 获取方法可以声明需要传递的参数的类型。
3.3 调用私有方法
调用私有方法必须使用“getDeclaredMethod(xx)”获取本类所有什么的方法代码如下
Class myClass Class.forName(example.PeopleImpl);
Object object myClass.newInstance();
Method privSayHi myClass.getDeclaredMethod(privSayHi);
privSayHi.setAccessible(true); // 修改访问限制
privSayHi.invoke(object);除了“getDeclaredMethod(xx)”可以看出调用私有方法的关键是设置 setAccessible(true) 属性修改访问限制这样设置之后就可以进行调用了。
4、总结
1.在反射中核心的方法是 newInstance() 获取类实例getMethod(…) 获取方法使用 invoke(…) 进行方法调用通过 setAccessible 修改私有变量/方法的访问限制。
2.获取属性/方法的时候有无“Declared”的区别是带有 Declared 修饰的方法或属性可以获取本类的所有方法或属性private 到 public但不能获取到父类的任何信息非 Declared 修饰的方法或属性只能获取 public 修饰的方法或属性并可以获取到父类的信息比如 getMethod(…)和getDeclaredMethod(…)。
二、动态代理
动态代理是一种方便运行时动态构建代理、动态处理代理方法调用的机制很多场景都是利用类似机制做到的比如用来包装 RPC 调用、面向切面的编程AOP。
实现动态代理的方式很多比如 JDK 自身提供的动态代理就是主要利用了上面提到的反射机制。还有其他的实现方式比如利用传说中更高性能的字节码操作机制类似 ASM、cglib基于 ASM等。
动态代理解决的问题
首先它是一个代理机制。如果熟悉设计模式中的代理模式我们会知道代理可以看作是对调用目标的一个包装这样我们对目标代码的调用不是直接发生的而是通过代理完成。通过代理可以让调用者与实现者之间解耦。比如进行 RPC 调用通过代理可以提供更加友善的界面。还可以通过代理可以做一个全局的拦截器。
1、JDK Proxy 动态代理
JDK Proxy 是通过实现 InvocationHandler 接口来实现的代码如下
interface Animal {void eat();
}
class Dog implements Animal {Overridepublic void eat() {System.out.println(The dog is eating);}
}
class Cat implements Animal {Overridepublic void eat() {System.out.println(The cat is eating);}
}// JDK 代理类
class AnimalProxy implements InvocationHandler {private Object target; // 代理对象public Object getInstance(Object target) {this.target target;// 取得代理对象return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);}Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println(调用前);Object result method.invoke(target, args); // 方法调用System.out.println(调用后);return result;}
}public static void main(String[] args) {// JDK 动态代理调用AnimalProxy proxy new AnimalProxy();Animal dogProxy (Animal) proxy.getInstance(new Dog());dogProxy.eat();
}如上代码我们实现了通过动态代理在所有请求之前和之后打印了一个简单的信息。
注意 JDK Proxy 只能代理实现接口的类即使是extends继承类也是不可以代理的。
JDK Proxy 为什么只能代理实现接口的类
这个问题要从动态代理的实现方法 newProxyInstance 源码说起
CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class?[] interfaces,InvocationHandler h)throws IllegalArgumentException
{
// 省略其他代码来看前两个源码参数说明
* param loader the class loader to define the proxy class
* param interfaces the list of interfaces for the proxy class to implementloader为类加载器也就是 target.getClass().getClassLoader()interfaces接口代理类的接口实现列表
所以这个问题的源头在于 JDK Proxy 的源码设计。如果要执意动态代理非接口实现类就会报错 Exception in thread “main” java.lang.ClassCastException: com.sun.proxy.$Proxy0 cannot be cast to xxx 2、Cglib 动态代理
JDK 动态代理机制只能代理实现了接口的类Cglib 是针对类来实现代理的他的原理是对指定的目标类生成一个子类并覆盖其中方法实现增强但因为采用的是继承所以不能对 final 修饰的类进行代理。
Cglib 可以通过 Maven 直接进行版本引用Maven 版本地址https://mvnrepository.com/artifact/cglib/cglib
本文使用的是最新版本 3.2.9 的 Cglib在 pom.xml 添加如下引用
dependencygroupIdcglib/groupIdartifactIdcglib/artifactIdversion3.2.9/version
/dependencyCglib 代码实现如下
class Panda {public void eat() {System.out.println(The panda is eating);}
}
class CglibProxy implements MethodInterceptor {private Object target; // 代理对象public Object getInstance(Object target) {this.target target;Enhancer enhancer new Enhancer();// 设置父类为实例类enhancer.setSuperclass(this.target.getClass());// 回调方法enhancer.setCallback(this);// 创建代理对象return enhancer.create();}Overridepublic Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {System.out.println(调用前);Object result methodProxy.invokeSuper(o, objects); // 执行方法调用System.out.println(调用后);return result;}
}public static void main(String[] args) {// CGLIB 动态代理调用CglibProxy proxy new CglibProxy();Panda panda (Panda)proxy.getInstance(new Panda());panda.eat();
}cglib 的调用通过实现 MethodInterceptor 接口的 intercept 方法调用 invokeSuper 进行动态代理的可以直接对普通类进行动态代理。
三、JDK Proxy VS Cglib
JDK Proxy 的优势
最小化依赖关系减少依赖意味着简化开发和维护JDK 本身的支持更加可靠平滑进行 JDK 版本升级而字节码类库通常需要进行更新以保证在新版上能够使用
Cglib 框架的优势
可调用普通类不需要实现接口高性能
总结 需要注意的是我们在选型中性能未必是唯一考量可靠性、可维护性、编程工作量等往往是更主要的考虑因素毕竟标准类库和反射编程的门槛要低得多代码量也是更加可控的如果我们比较下不同开源项目在动态代理开发上的投入也能看到这一点。
本文所有示例代码https://github.com/vipstone/java-core-example.git
四、参考文档
Java核心技术36讲http://t.cn/EwUJvWA
Java反射与动态代理https://www.cnblogs.com/hanganglin/p/4485999.html 关注作者公众号 如果觉得本文对您有帮助请我喝杯咖啡吧。