反射、注解与依赖注入总结

来源:互联网 发布:淘宝lol代练为什么便宜 编辑:程序博客网 时间:2024/06/06 19:38

● 反射(Reflection)

反射的概念

主要是指程序可以访问,检测和修改它本身状态或行为的一种能力,并能根据自身行为的状态和结果,调整或修改应用所描述行为的状态和相关的语义。

概念看着就有些晕或不知所云啦,可以通过反射的作用理解它的概念。

反射的作用

反射可以让我们在运行时获取类的属性,方法,构造方法、父类、接口等信息,通过反射还可以让我们在运行期实例化对象、调用方法、即使方法或属性是私有的的也可以通过反射的形式调用。

所有为什么第三方库基本都会使用到反射,正是因为反射这种 “看透 Class” 的能力。

反射相关的类、方法

要看透一个类,首先要获取这个类的对象,其它信息都是通过这个对象获取的,下面的所有的示例具体操作代码请参考 【个人学习项目DroidStudy】,我在这个工程下新建一个 ReflectionActivity,包的路径为 com.sun.study.ui.activity.ReflectionActivity,通过反射相关的类、方法让我看透这个类。

1、获取对象的三种方式:

第一种、知道一个类,直接获取 Class 对象

<code class="scala"><span class="hljs-type">Class</span><?> cls1 = <span class="hljs-type">ReflectionActivity</span>.<span class="hljs-keyword">class</span>;</code>

第二种、如果已经得到了某个对象,可以通过这个对象获取 Class 对象

<code class="scala"><span class="hljs-type">ReflectionActivity</span> activity = <span class="hljs-keyword">new</span> <span class="hljs-type">ReflectionActivity</span>();<span class="hljs-type">Class</span><?> cls2 = activity.getClass();</code>

第三种、如果你在编译期获取不到目标类型,但是你知道它的完整类路径,那么你可以通过如下的形式来获取 Class 对象,这样获取可能会抛出异常 ClassNotFoundException。

<code class="scala"><span class="hljs-keyword">try</span> {    <span class="hljs-type">Class</span><?> cls3 = <span class="hljs-type">Class</span>.forName(<span class="hljs-string">"com.sun.study.ui.activity.ReflectionActivity"</span>);} <span class="hljs-keyword">catch</span> (<span class="hljs-type">ClassNotFoundException</span> e) {    e.printStackTrace();}</code>

2、反射的相关方法和示例

列出反射的相关方法

<code class="cs">getName():获得类的完整名字。  newInstance():通过类的不带参数的构造方法创建这个类的一个对象。getFields():获得类的<span class="hljs-keyword">public</span>类型的属性。  getDeclaredFields():获得类的所有属性。getMethods():获得类的<span class="hljs-keyword">public</span>类型的方法。  getDeclaredMethods():获得类的所有方法。  getMethod(String name, Class[] parameterTypes):获得类的特定方法。getModifiers()和Modifier.toString():获得属修饰符,例如<span class="hljs-keyword">private</span>,<span class="hljs-keyword">public</span>,<span class="hljs-keyword">static</span>等  getReturnType():获得方法的返回类型  getParameterTypes():获得方法的参数类型getConstructors():获得类的<span class="hljs-keyword">public</span>类型的构造方法。  getConstructor(Class[] parameterTypes):获得类的特定构造方法。getSuperclass():获取某类的父类  getInterfaces():获取某类实现的接口</code>

示例一:获得类的所有方法(Method)信息

<code class="cpp"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">getMethodsInfo</span><span class="hljs-params">()</span> </span>{    Class<ReflectionActivity> cls = ReflectionActivity.<span class="hljs-keyword">class</span>;    Method[] methods = cls.getDeclaredMethods();    <span class="hljs-keyword">if</span> (methods == null) <span class="hljs-keyword">return</span>;    StringBuilder sb = <span class="hljs-keyword">new</span> StringBuilder();    <span class="hljs-keyword">for</span> (Method method:methods) {        sb.append(Modifier.toString(method.getModifiers())).append(<span class="hljs-string">" "</span>);        sb.append(method.getReturnType()).append(<span class="hljs-string">" "</span>);        sb.append(method.getName()).append(<span class="hljs-string">"("</span>);        Class[] parameters = method.getParameterTypes();        <span class="hljs-keyword">if</span> (parameters != null) {            <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i=<span class="hljs-number">0</span>; i<parameters.length; i++) {                Class paramCls = parameters[i];                sb.append(paramCls.getSimpleName());                <span class="hljs-keyword">if</span> (i < parameters.length - <span class="hljs-number">1</span>) sb.append(<span class="hljs-string">", "</span>);            }        }        sb.append(<span class="hljs-string">")\n\n"</span>);    }    tvInfo.setText(sb.toString());}</code>

运行结果如下图:


reflection_icon1.png

示例一:获得类的所有属性(Field)信息,并修改类型Int属性i的值

<code class="cpp"><span class="hljs-function"><span class="hljs-keyword">private</span> <span class="hljs-keyword">void</span> <span class="hljs-title">modifyFieldValue</span><span class="hljs-params">()</span> </span>{    Class<ReflectionActivity> cls = ReflectionActivity.<span class="hljs-keyword">class</span>;    Field[] fields = cls.getDeclaredFields();    <span class="hljs-keyword">if</span> (fields == null) <span class="hljs-keyword">return</span>;    StringBuilder sb = <span class="hljs-keyword">new</span> StringBuilder();    sb.append(<span class="hljs-string">"获得类的所有属性信息:\n\n"</span>);    <span class="hljs-keyword">for</span> (Field field:fields) {        sb.append(Modifier.toString(field.getModifiers())).append(<span class="hljs-string">" "</span>);        sb.append(field.getType().getSimpleName()).append(<span class="hljs-string">" "</span>);        sb.append(field.getName()).append(<span class="hljs-string">";"</span>);        sb.append(<span class="hljs-string">"\n\n"</span>);    }    <span class="hljs-keyword">try</span> {        sb.append(<span class="hljs-string">"属性i的默认值:i = "</span>);        Field f = cls.getDeclaredField(<span class="hljs-string">"i"</span>);        sb.append(f.getInt(<span class="hljs-string">"i"</span>)).append(<span class="hljs-string">"\n\n"</span>);        f.<span class="hljs-built_in">set</span>(<span class="hljs-string">"i"</span>, <span class="hljs-number">100</span>);        sb.append(<span class="hljs-string">"属性i修改后的值:i = "</span>);        sb.append(f.getInt(<span class="hljs-string">"i"</span>)).append(<span class="hljs-string">"\n\n"</span>);    } <span class="hljs-keyword">catch</span> (Exception e) {        e.printStackTrace();    }    tvInfo.setText(sb.toString());    toolbar.setSubtitle(<span class="hljs-string">"修改类型Int属性i的值"</span>);}</code>

运行结果如下图:


reflection_icon2.png

更多示例请参考 【个人学习项目DroidStudy】

反射的相关内容先记录到这,接下来看看注解相关概念与使用。

● 注解(Annotation)

注解的概念

注解(Annotation),也叫元数据。一种代码级别的说明。它是JDK 1.5及以后版本引入的一个特性,与类、接口、枚举是在同一个层次。它可以声明在包、类、字段、方法、局部变量、方法参数等的前面,用来对这些元素进行说明,注释。

注解的作用

1、标记作用,用于告诉编译器一些信息让编译器能够实现基本的编译检查,如@Override、Deprecated,看下它俩的源码

<code class="java"><span class="hljs-annotation">@Target</span>(ElementType.METHOD)<span class="hljs-annotation">@Retention</span>(RetentionPolicy.SOURCE)<span class="hljs-keyword">public</span> <span class="hljs-annotation">@interface</span> Override {}<span class="hljs-annotation">@Documented</span><span class="hljs-annotation">@Retention</span>(RetentionPolicy.RUNTIME)<span class="hljs-keyword">public</span> <span class="hljs-annotation">@interface</span> Deprecated {}</code>

2、编译时动态处理,动态生成代码,如Butter Knife、Dagger 2

3、运行时动态处理,获得注解信息,如Retrofit

注解的分类

注解的分类有两种分法:

第一种分法

1、基本内置注解,是指Java自带的几个Annotation,如@Override、Deprecated、@SuppressWarnings等

2、元注解(meta-annotation),是指负责注解其他注解的注解,JDK 1.5及以后版本定义了4个标准的元注解类型,如下:

<code class="scala"><span class="hljs-number">1</span>、<span class="hljs-annotation">@Target</span><span class="hljs-number">2</span>、<span class="hljs-annotation">@Retention</span><span class="hljs-number">3</span>、<span class="hljs-annotation">@Documented</span><span class="hljs-number">4</span>、<span class="hljs-annotation">@Inherited</span></code>

3、自定义注解,根据需要可以自定义注解,自定义注解需要用到上面的meta-annotation

第二种分法,根据作用域分类

1、源码时注解(RetentionPolicy.SOURCE)
2、编译时注解(RetentionPolicy.CLASS)
3、运行时注解(RetentionPolicy.RUNTIME)

注解相关知识点

1、元注解相关信息

@Target:指Annotation所修饰的对象范围,通过ElementType取值有8种,如下

<code class="objectivec">TYPE:类、接口(包括注解类型)或枚举FIELD:属性METHOD:方法PARAMETER:参数CO<span class="hljs-built_in">NSTRUCTOR</span>:构造函数LO<span class="hljs-built_in">CAL_VARIABLE</span>:局部变量ANNOTATION_TYPE:注解类型PACKAGE:包</code>

@Retention:指Annotation被保留的时间长短,通过RetentionPolicy取值有3种,如下:

<code class="scala"><span class="hljs-type">SOURCE</span>:在源文件中有效(即源文件保留)  <span class="hljs-type">CLASS</span>:在<span class="hljs-class"><span class="hljs-keyword">class</span><span class="hljs-title">文件中有效(即class保留)</span>  </span><span class="hljs-type">RUNTIME</span>:在运行时有效(即运行时保留)</code>

@Documented:是一个标记注解,用于描述其它类型的注解应该被作为被标注的程序成员的公共API,因此可以被例如javadoc此类的工具文档化。

@Inherited:也是一个标记注解,@Inherited阐述了某个被标注的类型是被继承的

2、注解定义格式

<code class="php"><span class="hljs-keyword">public</span> @<span class="hljs-class"><span class="hljs-keyword">interface</span> 注解名 </span>{ 定义体 }</code>

3、注解参数可支持的数据类型:

<code class="java"><span class="hljs-number">8</span>种基本数据类型 <span class="hljs-keyword">int</span>、<span class="hljs-keyword">float</span>、<span class="hljs-keyword">boolean</span>、<span class="hljs-keyword">byte</span>、<span class="hljs-keyword">double</span>、<span class="hljs-keyword">char</span>、<span class="hljs-keyword">long</span>、<span class="hljs-keyword">short</span>  String、Class、<span class="hljs-keyword">enum</span>、Annotation  以上所有类型的数组</code>

4、⚠注意:自定义注解如果只有一个参数成员,最好把定义体参数名称设为"value",如@Target

<code class="java"><span class="hljs-annotation">@Documented</span><span class="hljs-annotation">@Retention</span>(RetentionPolicy.RUNTIME)<span class="hljs-annotation">@Target</span>(ElementType.ANNOTATION_TYPE)<span class="hljs-keyword">public</span> <span class="hljs-annotation">@interface</span> Target {    ElementType[] value();}</code>

看一个示例

具体要求和运行结果都在下面这张图上显示出来了,贴下图


annotation_icon.png

再贴三块代码,首先是自定义注解代码:

<code class="java"><span class="hljs-annotation">@Target</span>(ElementType.METHOD)<span class="hljs-annotation">@Retention</span>(RetentionPolicy.RUNTIME)<span class="hljs-annotation">@Inherited</span><span class="hljs-annotation">@Documented</span><span class="hljs-keyword">public</span> <span class="hljs-annotation">@interface</span> RequestAnnotation {    <span class="hljs-function"><span class="hljs-keyword">boolean</span> <span class="hljs-title">withDialog</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> <span class="hljs-keyword">true</span></span>;    <span class="hljs-function">String <span class="hljs-title">withMessage</span><span class="hljs-params">()</span> <span class="hljs-keyword">default</span> "正在加载,请稍后..."</span>;}</code>

其次是执行模拟的网络请求,核心代码是通过上面的反射和注解完成的;具体详细代码请参考 【个人学习项目DroidStudy】,下次使用动态代理和Google的dexmaker完成这个功能,敬请关注,如果你对线程池还不清晰请参考我以前的文章【线程、多线程与线程池总结】。贴下核心代码:

<code class="java"><span class="hljs-comment">// 线程池</span><span class="hljs-keyword">private</span> <span class="hljs-keyword">static</span> ExecutorService pool = Executors.newCachedThreadPool();<span class="hljs-comment">// 模拟处理网络请求</span><span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">boolean</span> <span class="hljs-title">process</span><span class="hljs-params">(<span class="hljs-keyword">final</span> Class<?> clazz, String methodName, <span class="hljs-keyword">final</span> Object... args)</span> <span class="hljs-keyword">throws</span> Exception </span>{    Class[] argsClass = getClazzByArgs(args);    <span class="hljs-keyword">final</span> Method method = clazz.getDeclaredMethod(methodName, argsClass);    <span class="hljs-keyword">if</span> (method == <span class="hljs-keyword">null</span>) {        sendMsg(TYPE_ERROR);        <span class="hljs-keyword">return</span> <span class="hljs-keyword">false</span>;    }    <span class="hljs-comment">// 获取注解信息</span>    RequestAnnotation annotation = method.getAnnotation(RequestAnnotation.class);    <span class="hljs-keyword">if</span> (annotation != <span class="hljs-keyword">null</span> && annotation.withDialog()) {        loadingDialog.show(annotation.withMessage());    }    pool.execute(<span class="hljs-keyword">new</span> Runnable() {        <span class="hljs-annotation">@Override</span>        <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">run</span><span class="hljs-params">()</span> </span>{            <span class="hljs-keyword">try</span> {                method.setAccessible(<span class="hljs-keyword">true</span>);                method.invoke(clazz.newInstance(), args);                sendMsg(TYPE_SUCCESS);            } <span class="hljs-keyword">catch</span> (Exception e) {                e.printStackTrace();            }        }    });    <span class="hljs-keyword">return</span> <span class="hljs-keyword">true</span>;}</code>

最后是调用网络请求接口:

<code class="cpp">@RequestAnnotation(withDialog = <span class="hljs-literal">false</span>, withMessage = <span class="hljs-string">"正在加载,请稍后..."</span>)<span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-keyword">void</span> <span class="hljs-title">apiTestFunc</span><span class="hljs-params">(String param1, String param2)</span> </span>{    <span class="hljs-keyword">try</span> {        <span class="hljs-comment">// 模拟网络请求的耗时操作</span>        Thread.sleep(<span class="hljs-number">3000</span>);    } <span class="hljs-keyword">catch</span> (InterruptedException e) {        e.printStackTrace();    }}<span class="hljs-comment">// 点击执行的代码</span>DynamicProxyUtil proxyUtil = <span class="hljs-keyword">new</span> DynamicProxyUtil(AnnotationActivity.<span class="hljs-keyword">this</span>);proxyUtil.process(RequestNetworkApi.<span class="hljs-keyword">class</span>, <span class="hljs-string">"apiTestFunc"</span>, <span class="hljs-string">"参数一"</span>, <span class="hljs-string">"参数二"</span>);</code>

● 依赖注入(Dependency Injection)

依赖注入(Dependency Injection):可以通过这个服务来安全的注入组件到应用程序中,在应用程序部署的时候还可以选择从特定的接口属性进行注入。

看完上面反射和注解的记录后,可以更好的理解依赖注入,如果你不用那些第三方的注入库你也在经常用到依赖注入,比如下面这一段从codekk上截取的代码:

<code class="cs"><span class="hljs-keyword">public</span> <span class="hljs-keyword">class</span> <span class="hljs-title">Human</span> {    ...    Father father;    ...    <span class="hljs-function"><span class="hljs-keyword">public</span> <span class="hljs-title">Human</span>(<span class="hljs-params">Father father</span>) </span>{        <span class="hljs-keyword">this</span>.father = father;    }}</code>

上面代码中,我们将 father 对象作为构造函数的一个参数传入。在调用 Human 的构造方法之前外部就已经初始化好了 Father 对象。像这种非自己主动初始化依赖,而通过外部来传入依赖的方式,我们就称为依赖注入。

依赖注入的实现有多种途径,而在 Java 中,使用注解是最常用的。比如通过Butter Knife、Dagger依赖注入库实现,都是使用注解来实现依赖注入,但它利用 APT(Annotation Process Tool) 在编译时生成辅助类,这些类继承特定父类或实现特定接口,程序在运行时加载这些辅助类,调用相应接口完成依赖生成和注入。

依赖注入在这里仅仅剖析下概念,有时间将会补一个例子,暂且到这吧。



文/孙福生微博(简书作者)
原文链接:http://www.jianshu.com/p/24820bf3df5c
著作权归作者所有,转载请联系作者获得授权,并标注“简书作者”。
0 0
原创粉丝点击