xml地图|网站地图|网站标签 [设为首页] [加入收藏]

皇家娱乐官网注解和动态代理,深入理解

来源:http://www.ccidsi.com 作者:最新解决方案 人气:90 发布时间:2020-01-15
摘要:固然实现了代办接口的类已存在就回来缓存对象,不然就经过ProxyClassFactory生成。ProxyClassFactory又是经过上面包车型地铁代码生成Class对象的。 java.lang.reflect 包 Java 中的 java.lang.reflect 包

固然实现了代办接口的类已存在就回来缓存对象,不然就经过ProxyClassFactory生成。ProxyClassFactory又是经过上面包车型地铁代码生成Class对象的。

java.lang.reflect 包

Java 中的 java.lang.reflect 包提供了反光成效。java.lang.reflect 包中的类都不曾 public 布局方法。

java.lang.reflect 包的为主接口和类如下:

  • Member 接口 - 反映有关单个成员或构造函数的标记消息。
  • Field 类 - 提供叁个类的域的新闻以至访谈类的域的接口。
  • Method 类 - 提供二个类的情势的消息以至访问类的法子的接口。
  • Constructor 类 - 提供八个类的构造函数的消息以至访问类的构造函数的接口。
  • Array 类 - 该类提供动态地转移和会见 JAVA 数组的办法。
  • Modifier 类 - 提供了 static 方法和常量,对类和成员访问修饰符举办解码。
  • Proxy 类 - 提供动态地变化代理类和类实例的静态方法。

静心:二种方式赢得的Class对象相仿的前提是选择了同等的类加载器,比方上述代码中暗许使用应用程序类加载器(sun.misc.Launcher$AppClassLoader)。不一致类加载器加载的同一个类,也会获取差异的Class对象:

看清是或不是为有些类的实例

剖断是还是不是为有个别类的实例有二种艺术:

  1. instanceof 关键字
  2. Class 对象的 isInstance 方法(它是三个 Native 方法)

示例:

public class InstanceofDemo { public static void main(String[] args) { ArrayList arrayList = new ArrayList(); if (arrayList instanceof List) { System.out.println("ArrayList is List"); } if (List.class.isInstance(arrayList)) { System.out.println("ArrayList is List"); } }}//Output://ArrayList is List//ArrayList is List

静态代理

静态代理其实正是指设计格局中的代理情势。

代办方式为别的对象提供生龙活虎种代理以调节对这几个指标的访谈。

皇家娱乐官网 1image.png

Subject 定义了 RealSubject 和 Proxy 的国有接口,那样就在其他利用 RealSubject 的地点都能够运用 Proxy 。

abstract class Subject { public abstract void Request();}

RealSubject 定义 Proxy 所代表的真实实体。

class RealSubject extends Subject { @Override public void Request() { System.out.println; }}

Proxy 保存二个引用使得代理能够访谈实体,并提供一个与 Subject 的接口相像的接口,那样代理就足以用来顶替实体。

class Proxy extends Subject { private RealSubject real; @Override public void Request() { if (null == real) { real = new RealSubject(); } real.Request(); }}

说明:

静态代理格局固然在拜望不恐怕访谈的能源,加强现存的接口业务职能方面有相当的大的长处,不过多量运用这种静态代理,会使大家系统内的类的范畴增大,而且不易维护;并且由于 Proxy 和 RealSubject 的效率本质上是如出意气风发辙的,Proxy 只是起到了中介的功力,这种代理在系统中的存在,招致系统布局比较肥壮和麻痹。

有关Android访谈网关央浼,其分支结构可参看《基于Retrofit 兰德RubiconxJava的Android分层网络须求框架》。

Proxy 类

Proxy 这么些类的效益就是用来动态成立二个代理对象的类,它提供了好多的主意,可是我们用的最多的正是 newProxyInstance 那一个主意:

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException

那些方法的效果正是赢得一个动态的代办对象。

参数表达:

  • loader - 一个 ClassLoader 对象,定义了由哪位 ClassLoader 对象来对转移的代办对象开展加载。
  • interfaces - 一个 Interface 对象的数组,表示的是自己将在给自身索要代理的目的提供生龙活虎组什么接口,假设本身提供了大器晚成组接口给它,那么这么些代理对象就扬言达成了该接口,这样小编就能够调用那组接口中的方法了
  • h - 叁个 InvocationHandler 对象,表示的是当本身那个动态代理对象在调用方法的时候,会涉及到哪一个InvocationHandler 对象上
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)

获得 Class 对象

赢得 Class 的三种方法:

使用 Class 类的 forName 静态方法

示例:

package io.github.dunwu.javacore.reflect;public class ReflectClassDemo01 { public static void main(String[] args) throws ClassNotFoundException { Class c1 = Class.forName("io.github.dunwu.javacore.reflect.ReflectClassDemo01"); System.out.println(c1.getCanonicalName; Class c2 = Class.forName; System.out.println(c2.getCanonicalName; Class c3 = Class.forName("[[Ljava.lang.String;"); System.out.println(c3.getCanonicalName; }}//Output://io.github.dunwu.javacore.reflect.ReflectClassDemo01//double[]//java.lang.String[][]

使用类的完全节制名来反射对象的类。多如牛毛的利用处景为:在 JDBC 开采中常用此方式加载数据库驱动。

一直获得某一个对象的 class

示例:

public class ReflectClassDemo02 { public static void main(String[] args) { boolean b; // Class c = b.getClass(); // 编译错误 Class c1 = boolean.class; System.out.println(c1.getCanonicalName; Class c2 = java.io.PrintStream.class; System.out.println(c2.getCanonicalName; Class c3 = int[][][].class; System.out.println(c3.getCanonicalName; }}//Output://boolean//java.io.PrintStream//int[][][]

调用 Object 的 getClass 方法,示例:

Object 类中有 getClass 方法,因为兼具类都世襲 Object 类。从而调用 Object 类来获取

示例:

package io.github.dunwu.javacore.reflect;import java.util.HashSet;import java.util.Set;public class ReflectClassDemo03 { enum E {A, B} public static void main(String[] args) { Class c = "foo".getClass(); System.out.println(c.getCanonicalName; Class c2 = ReflectClassDemo03.E.A.getClass(); System.out.println(c2.getCanonicalName; byte[] bytes = new byte[1024]; Class c3 = bytes.getClass(); System.out.println(c3.getCanonicalName; Set<String> set = new HashSet<>(); Class c4 = set.getClass(); System.out.println(c4.getCanonicalName; }}//Output://java.lang.String//io.github.dunwu.javacore.reflect.ReflectClassDemo.E//byte[]//java.util.HashSet

代理是风度翩翩种构造型设计情势,当不能或不想直接待上访谈有些对象,恐怕访谈有些对象比较复杂的时候,能够经过叁个代理对象来直接待上访谈,代理对象向客商端提供和实际对象相符的接口效率。杰出设计方式中,代理情势有种种剧中人物:

InvocationHandler 接口

InvocationHandler 接口定义:

public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;}

每三个动态代理类都应当要兑现 InvocationHandler 那一个接口,而且每种代理类的实例都涉嫌到了三个Handler,当大家通过代办对象调用三个艺术的时候,这一个法子的调用就能够被转变为由 InvocationHandler 那个接口的 invoke 方法来开展调用。

大家来探问 InvocationHandler 那一个接口的头一无二一个办法 invoke 方法:

Object invoke(Object proxy, Method method, Object[] args) throws Throwable

参数表明:

  • proxy - 代理的实际对象。
  • method - 所要调用真实对象的某部方法的 Method 对象
  • args - 所要调用真实对象有个别方法时选取的参数

假若不是很精通,等下通过一个实例会对那多少个参数进行更加深的授课。

// 生成代理类字节码文件byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try { // defineClass0为native方法,生成Class对象 return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) { throw new IllegalArgumentException(e.toString;}

动态代理

为了解决静态代理的标题,就有了创办动态代理的主张:

在运作意况中,供给代理的位置,依据 Subject 和 RealSubject,动态地创立三个 Proxy,用完事后,就能够销毁,那样就足以制止了 Proxy 剧中人物的 class 在系统中混杂的标题了。

皇家娱乐官网 2image.png

Java 动态代理基于杰出代理形式,引进了二个InvocationHandler,InvocationHandler 肩负统生龙活虎保管全体的章程调用。

动态代理步骤:

  1. 收获 RealSubject 上的具有接口列表;
  2. 规定要转移的代理类的类名,默以为:com.sun.proxy.$ProxyXXXX
  3. 听新闻说供给落实的接口音讯,在代码中动态创造 该 Proxy 类的字节码;
  4. 将相应的字节码转变为相应的 class 对象;
  5. 创建 InvocationHandler 实例 handler,用来拍卖 Proxy 全部办法调用;
  6. Proxy 的 class 对象 以创设的 handler 对象为参数,实例化叁个 proxy 对象。

从地方能够见见,JDK 动态代理的完结是依靠达成接口的法子,使得 Proxy 和 RealSubject 具有相似的功效。

但骨子里还应该有生机勃勃种思路:通过持续。即:让 Proxy 继承RealSubject,那样双方相符享有同样的效果,Proxy 还能透过重写 RealSubject 中的方法,来促成多态。CGLIB 就是依附这种思路设计的。

在 Java 的动态代理体制中,有四个根本的类,一个是 InvocationHandler 接口、另一个则是 Proxy 类,这三个类和一个接口是兑现大家动态代理所不可不选用的。

那么,Android端能否以dubbo:reference化的措施表明须要拜候的互联网服务呢?怎么样那样,将比相当的大提升Android开采职员和Java后端开荒时期的关系效率,甚至Android端的代码作用。首先,自定义服务的客商评释Reference,通过该申明标识有个别服务。

开创实例

透过反射来创建实例对象主要有二种方式:

  • Class 对象的 newInstance 方法。
  • Constructor 对象的 newInstance 方法。

示例:

public class NewInstanceDemo { public static void main(String[] args) throws IllegalAccessException, InstantiationException, NoSuchMethodException, InvocationTargetException { Class<?> c1 = StringBuilder.class; StringBuilder sb = (StringBuilder) c1.newInstance(); sb.append; System.out.println(sb.toString; //获取String所对应的Class对象 Class<?> c2 = String.class; //获取String类带一个String参数的构造器 Constructor constructor = c2.getConstructor(String.class); //根据构造器创建实例 String str2 =  constructor.newInstance; System.out.println; }}//Output://aaa//bbb
// 自定义类加载器ClassLoader myLoader = new ClassLoader() { @Override public Class<?> loadClass(String name) throws ClassNotFoundException { try { String fileName = name.substring(name.lastIndexOf   ".class"; InputStream is = getClass().getResourceAsStream; if (is == null) { return super.loadClass; } byte[] b = new byte[is.available()]; is.read; return defineClass(name, b, 0, b.length); } catch (IOException e) { throw new ClassNotFoundException; } }};// 采用自定义类加载器加载Class clazz3 = Class.forName("com.yhthu.java.ClassTest", true, myLoader);// clazz0与clazz3并不相同System.out.println("Class对象是否相同:"   clazz0.equals;

Class 对象

要想行使反射,首先须要拿到待操作的类所对应的 Class 对象。Java 中,无论生成某些类的稍稍个对象,那几个指标都会对应于同五个 Class 对象。那几个 Class 对象是由 JVM 生成的,通过它能够意识到整个类的布局。所以,java.lang.Class 能够说是全部反射 API 的入口点。

反射的原形就是:在运作时,把 Java 类中的各样元素映射成一个个的 Java 对象。

比喻来讲,要是定义了以下代码:

User user = new User();

步骤表明:

  1. JVM 加载方法的时候,境遇 new User(),JVM 会根据 User 的全约束名去加载 User.class
  2. JVM 会去当地球磁性盘查找 User.class 文件并加载 JVM 内部存款和储蓄器中。
  3. JVM 通过调用类加载器自动创造那些类对应的 Class 对象,并且存款和储蓄在 JVM 的方法区。注意:三个类有且独有多少个 Class 对象

在调用classTestService的艺术以前,要求注入该接口服务的落到实处,因而,该操作能够在调用组件伊始化的时候举办。

如何是反射

反射(Reflection卡塔尔是 Java 程序开荒语言的特色之生机勃勃,它同意运营中的 Java 程序获取自己的新闻,而且能够操作类或对象的个中属性。

由此反射机制,能够在运作时访谈 Java 对象的质量,方法,布局方法等。

// 缓存(key, sub-key) -> value,其中key为类加载器,sub-key为代理的接口,value为Class对象private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory;// 如果实现了代理接口的类已存在就返回缓存对象,否则就通过ProxyClassFactory生成private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } return proxyClassCache.get(loader, interfaces);}

Array

数组在 Java 里是相比较分外的风度翩翩种档案的次序,它能够赋值给二个对象援引。上边大家看风度翩翩看利用反射创造数组的事例:

public class ReflectArrayDemo { public static void main(String[] args) throws ClassNotFoundException { Class<?> cls = Class.forName("java.lang.String"); Object array = Array.newInstance; //往数组里添加内容 Array.set(array, 0, "Scala"); Array.set(array, 1, "Java"); Array.set(array, 2, "Groovy"); Array.set(array, 3, "Scala"); Array.set(array, 4, "Clojure"); //获取某一项的内容 System.out.println(Array.get); }}//Output://Scala

其中的 Array 类为 java.lang.reflect.Array 类。大家透过 Array.newInstance 创立数组对象,它的原型是:

public static Object newInstance(Class<?> componentType, int length) throws NegativeArraySizeException { return newArray(componentType, length);}

动态代理是反射的三个老大首要的运用项景。动态代理常被用于一些 Java 框架中。譬如 Spring 的 AOP ,Dubbo 的 SPI 接口,就是借助 Java 动态代理达成的。

private Object getProxy() { return Proxy.newProxyInstance(JDKProxyTest.class.getClassLoader(), new Class<?>[]{Subject.class}, new MyInvocationHandler(new RealSubject;}private static class MyInvocationHandler implements InvocationHandler { private Object realSubject; public MyInvocationHandler(Object realSubject) { this.realSubject = realSubject; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Some thing before method invoke"); Object result = method.invoke(realSubject, args); System.out.println("Some thing after method invoke"); return result; }}

反射的采纳场景

反射的根本运用处景有:

  • 支付通用框架 - 反射最重视的用场正是开辟各类通用框架。超多框架(比方Spring)都以配置化的(比如通过 XML 文件配置 JavaBean、Filter 等),为了确认保障框架的通用性,它们大概供给根据安排文件加载差别的目的或类,调用不一致的章程,那个时候就非得用到反射——运维时动态加载必要加载的对象。
  • 动态代理 - 在切面编程中,须求拦截特定的主意,日常,会接纳动态代理情势。那时候,就必要反射本事来兑现了。
  • 注解 - 申明自己只是是起到标识功用,它须求选择反射机制,依据评释标识去调用评释解释器,实行行为。若无反射机制,申明并不及注释更有用。
  • 可扩张性效率 - 应用程序能够透过利用完全限制名称创制可扩张性对象实例来选拔外界的客户定义类。

动态代理实例

上边的剧情介绍完那三个接口今后,我们来通过叁个实例来探视我们的动态代理情势是哪些的:

第生龙活虎大家定义了叁个 Subject 类型的接口,为其评释了八个办法:

public interface Subject { void hello(String str); String bye();}

紧接着,定义了二个类来得以达成那个接口,这一个类正是我们的真人真事对象,RealSubject 类:

public class RealSubject implements Subject { @Override public void hello(String str) { System.out.println("Hello "   str); } @Override public String bye() { System.out.println("Goodbye"); return "Over"; }}

下一步,我们将要定义三个动态代理类了,前边说个,每四个动态代理类都必须要促成 InvocationHandler 这么些接口,由此大家以此动态代理类也不例外:

public class InvocationHandlerDemo implements InvocationHandler { // 这个就是我们要代理的真实对象 private Object subject; // 构造方法,给我们要代理的真实对象赋初值 public InvocationHandlerDemo(Object subject) { this.subject = subject; } @Override public Object invoke(Object object, Method method, Object[] args) throws Throwable { // 在代理真实对象前我们可以添加一些自己的操作 System.out.println("Before method"); System.out.println("Call Method: "   method); // 当代理对象调用真实对象的方法时,其会自动的跳转到代理对象关联的handler对象的invoke方法来进行调用 Object obj = method.invoke(subject, args); // 在代理真实对象后我们也可以添加一些自己的操作 System.out.println("After method"); System.out.println(); return obj; }}

最终,来看看我们的 Client 类:

public class Client { public static void main(String[] args) { // 我们要代理的真实对象 Subject realSubject = new RealSubject(); // 我们要代理哪个真实对象,就将该对象传进去,最后是通过该真实对象来调用其方法的 InvocationHandler handler = new InvocationHandlerDemo(realSubject); /* * 通过Proxy的newProxyInstance方法来创建我们的代理对象,我们来看看其三个参数 * 第一个参数 handler.getClass().getClassLoader() ,我们这里使用handler这个类的ClassLoader对象来加载我们的代理对象 * 第二个参数realSubject.getClass().getInterfaces(),我们这里为代理对象提供的接口是真实对象所实行的接口,表示我要代理的是该真实对象,这样我就能调用这组接口中的方法了 * 第三个参数handler, 我们这里将这个代理对象关联到了上方的 InvocationHandler 这个对象上 */ Subject subject = Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler); System.out.println(subject.getClass().getName; subject.hello; String result = subject.bye(); System.out.println("Result is: "   result); }}

大家先来拜望调节台的输出:

com.sun.proxy.$Proxy0Before methodCall Method: public abstract void io.github.dunwu.javacore.reflect.InvocationHandlerDemo$Subject.hello(java.lang.String)Hello WorldAfter methodBefore methodCall Method: public abstract java.lang.String io.github.dunwu.javacore.reflect.InvocationHandlerDemo$Subject.bye()GoodbyeAfter methodResult is: Over

大家首先来拜会 com.sun.proxy.$Proxy0 那东西,我们看看,那一个东西是由 System.out.println(subject.getClass().getName; 这条语句打印出来的,那么为啥大家回到的这一个代理对象的类名是那样的吗?

Subject subject = Proxy.newProxyInstance(handler.getClass().getClassLoader(), realSubject .getClass().getInterfaces(), handler);

想必小编认为返回的那几个代理对象会是 Subject 类型的对象,可能是 InvocationHandler 的目标,结果却不是,首先大家解释一下干什么我们那边能够将其转变为 Subject 类型的靶子?

案由便是:在 newProxyInstance 那个主意的第一个参数上,大家给这一个代理对象提供了生机勃勃组什么接口,那么自身这么些代理对象就能够落到实处了这组接口,这时大家本来能够将这一个代理对象免强类型转化为这组接口中的狂妄贰个,因为那边的接口是 Subject 类型,所以就能够将其转变为 Subject 类型了。

再者大家必然要铭记在心,通过 Proxy.newProxyInstance 制造的代办对象是在 jvm 运转时动态变化的七个目的,它而不是大家的 InvocationHandler 类型,亦非我们定义的那组接口的类型,而是在运行是动态变化的一个对象,而且命名模式都以这么的款型,以$最初,proxy 为中,最后四个数字代表对象的标记

跟着大家来探视这两句

subject.hello;String result = subject.bye();

此间是因而代理对象来调用达成的这种接口中的方法,那个时候程序就能够跳转到由这么些代理对象关系到的 handler 中的 invoke 方法去施行,而大家的那么些 handler 对象又选取了一个RealSubject 类型的参数,表示自身要代理的正是以此实际对象,所以那个时候就能够调用 handler 中的 invoke 方法去推行。

小编们看出,在真的通过代理对象来调用真实对象的方法的时候,大家得以在该办法前后增添本身的某些操作,同不经常候大家见到我们的这几个method 对象是那般的:

public abstract void io.github.dunwu.javacore.reflect.InvocationHandlerDemo$Subject.hello(java.lang.String)public abstract java.lang.String io.github.dunwu.javacore.reflect.InvocationHandlerDemo$Subject.bye()

无独有偶正是大家的 Subject 接口中的八个法子,那也就证实了当我透过代办对象来调用方法的时候,起实际正是委托由其涉嫌到的 handler 对象的 invoke 方法中来调用,并非团结来真格的调用,而是经过代办的点子来调用的。

反射应用

皇家娱乐官网 3image.png皇家娱乐官网 4image.png

那边,大家珍视关怀元证明,元表明坐落于java.lang.annotation包中,首要用来自定义注明。元注脚满含:

Constructor

Class 对象提供以下办法得到对象的构造方法(Constructor):

  • getConstructor - 重临类的特定 public 构造方法。参数为艺术参数对应 Class 的对象。
  • getDeclaredConstructor - 重回类的特定布局方法。参数为艺术参数对应 Class 的目的。
  • getConstructors - 重返类的具备 public 布局方法。
  • getDeclaredConstructors - 再次回到类的具备构造方法。

拿到叁个 Constructor 对象后,可以用 newInstance 方法来创立类实例。

示例:

public class ReflectMethodConstructorDemo { public static void main(String[] args) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException { Constructor<?>[] constructors1 = String.class.getDeclaredConstructors(); System.out.println("String getDeclaredConstructors 清单(数量 = "   constructors1.length   "):"); for (Constructor c : constructors1) { System.out.println; } Constructor<?>[] constructors2 = String.class.getConstructors(); System.out.println("String getConstructors 清单(数量 = "   constructors2.length   "):"); for (Constructor c : constructors2) { System.out.println; } System.out.println("===================="); Constructor constructor = String.class.getConstructor(String.class); System.out.println(constructor); String str =  constructor.newInstance; System.out.println; }}
  • InvocationHandler的invoke方法是如何调用的?回答那么些主题材料得先看下上面生成的Class对象终究是何许的,将ProxyGenerator生成的字节码保存成文件,然后反编写翻译张开,可以见到生成的Proxy.class首要包罗equals、toString、hashCode和代办接口的request方法达成。

Field

Class 对象提供以下格局赢得对象的积极分子(Field):

  • getFiled - 依据名称获取公有的类成员。
  • getDeclaredField - 依据名称获取已声明的类成员。但不能博取其父类的类成员。
  • getFields - 获取具备国有的类成员。
  • getDeclaredFields - 获取具备已扬言的类成员。

演示如下:

public class ReflectFieldDemo { class FieldSpy<T> { public boolean[][] b = {{false, false}, {true, true}}; public String name = "Alice"; public List<Integer> list; public T val; } public static void main(String[] args) throws NoSuchFieldException { Field f1 = FieldSpy.class.getField; System.out.format("Type: %s%n", f1.getType; Field f2 = FieldSpy.class.getField; System.out.format("Type: %s%n", f2.getType; Field f3 = FieldSpy.class.getField; System.out.format("Type: %s%n", f3.getType; Field f4 = FieldSpy.class.getField; System.out.format("Type: %s%n", f4.getType; }}//Output://Type: class [[Z//Type: class java.lang.String//Type: interface java.util.List//Type: class java.lang.Object
  • @Retention - 标志那么些证明怎么保存,是只在代码中,依旧编入class文件中,只怕是在运行时得以因此反射访问,枚举类型分为别SOURCE、CLASS和RUNTIME;
  • @Documented - 标识这么些注明是或不是包罗在客户文书档案中。
  • @Target - 标识这些评释应该是哪一种Java 成员,枚举类型包罗TYPE、FIELD、METHOD、CONSTRUCTOTiguan等;
  • @Inherited - 标识这么些注明可以三番五次超类评释,即子类Class对象可利用getAnnotations(卡塔尔国方法获得父类被@Inherited修饰的笺注,那个申明只可以用来表明类。
  • @Repeatable - Java 8 起头协助,标记某申明能够在同三个声称上运用频仍。

Method

Class 对象提供以下方法得到对象的办法(Method):

  • getMethod - 重回类或接口的特定措施。当中第叁个参数为方式名称,后边的参数为艺术参数对应 Class 的靶子。
  • getDeclaredMethod - 再次来到类或接口的特定表明方法。个中第八个参数为方式名称,前面包车型客车参数为艺术参数对应 Class 的靶子。
  • getMethods - 再次回到类或接口的兼具 public 方法,蕴含其父类的 public 方法。
  • getDeclaredMethods - 重临类或接口注明的享有办法,富含public、protected、私下认可采访和 private 方法,但不包罗世襲的法子。

获得四个 Method 对象后,可以用 invoke 方法来调用那几个艺术。

invoke 方法的原型为:

public Object invoke(Object obj, Object... args) throws IllegalAccessException, IllegalArgumentException, InvocationTargetException

示例:

public class ReflectMethodDemo { public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException { // 返回所有方法 Method[] methods1 = System.class.getDeclaredMethods(); System.out.println("System getDeclaredMethods 清单(数量 = "   methods1.length   "):"); for (Method m : methods1) { System.out.println; } // 返回所有 public 方法 Method[] methods2 = System.class.getMethods(); System.out.println("System getMethods 清单(数量 = "   methods2.length   "):"); for (Method m : methods2) { System.out.println; } // 利用 Method 的 invoke 方法调用 System.currentTimeMillis() Method method = System.class.getMethod("currentTimeMillis"); System.out.println; System.out.println(method.invoke; }}
// 接口与对应实现的缓存private Map<Class<?>, Object> serviceContainer = new HashMap<>();// 依赖注入public void inject(Object obj) { // 1. 扫描该类中所有添加@Service注解的域 Field[] fields = obj.getClass().getDeclaredFields(); for (Field field : fields) { if (field.isAnnotationPresent(Service.class)) { Class<?> clazz = field.getType(); if (clazz.getAnnotation(Reference.class) == null) { Log.e("ClassTestService", "接口地址未配置"); continue; } // 2. 从缓存中取出或生成接口类的实现 Object impl = serviceContainer.get; if (impl == null) { impl = create; serviceContainer.put(clazz, impl); } // 3. 设置服务接口实现 try { field.setAccessible; field.set(obj, impl); } catch (IllegalAccessException e) { e.printStackTrace(); } } }}

类加载进度

皇家娱乐官网 5image.png

类加载的总体经过如下:

在编写翻译时,Java 编写翻译器编写翻译好 .java 文件之后,在磁盘中发出 .class 文件。.class 文件是二进制文件,内容是唯有 JVM 能够分辨的机器码。

JVM 中的类加载器读取字节码文件,抽出二进制数据,加载到内部存款和储蓄器中,剖析.class 文件内的消息。类加载器会依据类的全约束名来拿到此类的二进制字节流;然后,将字节流所表示的静态存款和储蓄布局转变为方法区的周转时数据布局;接着,在内部存款和储蓄器中变化代表那个类的 java.lang.Class 对象。

加载截至后,JVM 带头举行连接阶段(富含验证、筹划、初步化)。经过那生龙活虎雨后冬笋操作,类的变量会被早先化。

// 获取指定注解类型getAnnotation(Class<T>):T;// 获取所有注解,包括从父类继承的getAnnotations():Annotation[];// 获取指定注解类型,不包括从父类继承的getDeclaredAnnotation(Class<T>):T// 获取所有注解,不包括从父类继承的getDeclaredAnnotations():Annotation[];// 判断是否存在指定注解isAnnotationPresent(Class<? extends Annotation>:boolean

反射的短处

  • 天性花销 - 由于反射涉及动态深入分析的档案的次序,因而不能施行某个 Java 设想机优化。因而,反射操作的性子要比非反射操作的属性要差,应该在性质敏感的应用程序中数十次调用的代码段中避免。
  • 毁掉封装性 - 反射调用方法时方可忽视权限检查,由此只怕会损坏封装性而产生安全主题素材。
  • 内部揭露 - 由于反射允许代码推行在非反射代码中专断的操作,比如访问私有字段和方法,所以反射的施用大概会导致意外的副效用,那只怕会促成代码功能反常并可能破坏可移植性。反射代码打破了聊以自慰,由此恐怕会趁着平台的升高而改过行为。
  • ClassLoader loader,定义代理生成的类的加载器,可以自定义类加载器,也得以复用当前Class的类加载器;
  • Class<?>[] interfaces,定义代理对象要求实现的接口;
  • InvocationHandler h,定义代理对象调用方法的拍卖,其invoke方法中的Object proxy表示生成的代理对象,Method表示代理方法, Object[]代表方法的参数。
// 调用ClassTestService服务的方法classTestService.echo.callback;
  • @Override - 检查该办法是还是不是是重载方法。如若开掘其父类,或许是援引的接口中并未有该形式时,会报编写翻译错误。
  • @Deprecated - 标识过时方法。要是应用该方式,会报编写翻译警示。
  • @SuppressWarnings - 提醒编写翻译器去忽视注解中声称的警告。
  • @SafeVarargs - Java 7 伊始支持,忽视任何利用参数为泛型变量的措施或布局函数调用发生的警报。
  • @FunctionalInterface - Java 8 最初帮忙,标志二个佚名函数或函数式接口。

在HTTP网关得到服务名称、服务方法、服务版本、服务分组等音信之后,就能够完结对后端服务的反射调用。简单来说,就可以兑现Android端dubbo:reference化的互连网访谈。

JDK落成动态代理是由此Proxy类的newProxyInstance方法达成的,该方法的八个入参分别代表:

  • 经过Class的getDeclaredXxxx和getXxx方法获得结构器、方法和域对象,两个的界别在于前面一个再次来到的是当下Class对象评释的布局器、方法和域,包含修饰符为private的;前面一个只回去修饰符为public的结构器、方法和域,但含有从基类中三番陆次的。
private Object getProxy() { // 1. 动态代理类 return Enhancer.create(RealSubject.class, new MyMethodInterceptor; // 2. 动态代理接口 return Enhancer.create(Object.class, new Class<?>[]{Subject.class}, new MyMethodInterceptor;}private static class MyMethodInterceptor implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Some thing before method invoke"); Object result = proxy.invokeSuper(obj, args); System.out.println("Some thing after method invoke"); return result; }}

generateProxyClass方法是用来生成字节码文件的,依照变化的字节码文件,再在native层生成Class对象。

@Inherited@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Reference { // 服务接口名 String service() default ""; // 服务版本 String version() default ""; // 服务分组 String group() default ""; // 省略字段}
// 返回申明为public的方法,包含从基类中继承的for (Method method: String.class.getMethods { System.out.println(method.getName;}// 返回当前类申明的所有方法,包含private的for (Method method: String.class.getDeclaredMethods { System.out.println(method.getName;}

在本案例中,综合使用了自定义表明、反射以致动态代理,是对上述理论知识的三个切实可行运用。

在实现上,代理方式分为静态代理和动态代理,静态代理的代理类二进制文件是在编写翻译时生成的,而动态代理的代理类二进制文件是在运行时生成并加载到虚构机情状的。JDK提供了对动态代理接口的支撑,开源的动态代理库(Cglib、Javassist和Byte Buddy)提供了对接口和类的代办扶助,本节将轻巧比较JDK和Cglib完成动态代理的纠纷,后续章节会对Java字节码编制程序做详细深入分析。

Constructor和Method与菲尔德的分别在于前边贰个世襲自抽象类Executable,是能够在运营时动态调用的,而Field仅仅具有可访问的特征,且默感觉不可访问。上边精通下它们的中坚用法:

  • Subject抽象核心类——申唐宋理对象和真实性对象合营的接口方法;
  • RealSubject真实主旨类——达成了Subject接口,真实实施职业逻辑的地点;
  • ProxySubject代理类——完成了Subject接口,持有对RealSubject的引用,在实现的接口方法中调用RealSubject中相应的艺术施行;
  • Cliect客商端类——使用代理对象的类。

当自定义申明只有三个措施value()时,使用申明可只写值,举例:@AnnotationTest

平凡的接收办法如下:

create():Objectcreate(Class, Callback):Objectcreate(Class, Class[], Callback):Objectcreate(Class, Class[], CallbackFilter, Callback):Objectcreate(Class[], Object):Object

本文由68399皇家赌场发布于最新解决方案,转载请注明出处:皇家娱乐官网注解和动态代理,深入理解

关键词: 68399皇家赌场 java 反射 注解 动态

上一篇:JSP面试题都在这里,4个域对象

下一篇:没有了

最火资讯