概念
Java反射机制时Java语言的一个重要特性。在学习Java反射机制前,应先了解两个概念:编译期和运行期
编译期:是指吧源码交给编译器编译成计算机可以执行的文件的过程。在Java中,也就是把Java代码编程class文件的过程。编译期知识做了一些翻译功能,并没有把代码放在内存中运行起来,二十把代码当成文本进行操作。如检查语法错误。
运行期:是把编译后的文件交给计算机执行,直到程序运行结束。所谓运行期就是把在磁盘中的代码放到内存中执行起来。
Java反射机制是在运行状态中,对于任意一个类,都能够找到这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性;这种动态获取信息以及动态调用对象方法的功能称为Java语言的反射机制。简单来说,反射机制指的是程序在运行时能够获取自身的信息。在Java中,只要给定类的名字,就可以通过反射机制获得类的所有信息。
Java反射机制在服务器程序和中间件程序中得到了广泛运用。在服务器端,往往需要根据客户的请求,动态调用某一个对象的特定方法或者为属性赋值。
Java反射机制主要提供了以下功能,这些功能都位于java.lang.reflect包。
- 在运行时判断任意一个对象所属的类
- 在运行时构造任意一个类的对象
- 在运行时判断任意一个类所具有的成员变量和方法
- 在运行时判断任意一个对象的方法
Class类
class(包括interface)的本质是数据类型(Type)。class是由JVM在执行过程中动态加载的。JVM在第一次读取到一种class类型时,将其加载进内存。每加载一种class,JVM就为其创建一个Class类型的实例,并关联起来。
注:这里的Class类型是一个名叫Class的class。定义如下:
//final声明不允许继承
public final class Class{
//私有的构造方法
private Class() {}
}
由于JVM为每个加载的class创建了对应的Class实例,并再实例中保存了改class的所有信息,包括类名、包名、父类、实现的接口、所有方法、字段(成员变量)等,因此,如果获取了某个Class实例,我妈就可以通过这个Class实例获取到该实例对应的class的所有信息。这种通过Class实例获取class信息的方法称为反射(Reflection)。
获取class的Class实例方法:
方法一:直接通过一个class的静态变量class获取:
Class cls = String.class;
方法二:如果我们有一个实例变量,可以通过改实例变量提供的getClass()方法获取:
String s = "Hello";
Class cls = s.getClass();
方法三:如果真的一个class的完整类名,可以通过静态方法Class.forName()方法获取:
Class cls = Class.forName("java.lang.String");
可以使用==比较两个Class实例
反射的目的是为了获取某个实例的信息。因此,当我们拿到某个Object实例时,我们可以通过反射获取该Object的class信息:
void printObjectInfo(Object obj) {
Class cls = obj.getClass();
}
Class常用方法
类型 | 访问方法 | 返回值类型 | 说明 |
包路径 | getPackage() | Package对象 | 获取该类的存放路径 |
类名称 | getName() | String对象 | 获取该类的名称 |
继承类 | getSuperclass() | Class对象 | 获取该类继承的类 |
实现接口 | getInterfaces() | Class型数组 | 获取该类实现的所有接口 |
构造方法 | getConstructors() | Constructor型数组 | 获取所有权限为public的构造方法 |
getDeclaredConstructors() | Constructor对象 | 获取当前对象的所有构造方法 | |
方法 | getMethods() | Methods型数组 | 获取所有权限为public的方法 |
getDeclaredMethods() | Methods对象 | 获取当前对象的所有方法 | |
成员变量 | getFields() | Field型数组 | 获取所有权限为public的成员变量 |
getDeclaredFields() | Field对象 | 获取当前对象的所有成员变量 |
动态加载机制
JVM在执行Java程序的时候,并不是一次性把所有用到的class全部加载到内存,二十第一次需要用到class时才加载。
小结
- JVM 为每个加载的 class 及 interface 创建了对应的 Class 实例来保存 class 及 interface 的所有信息
- 获取一个 class对应的 Class 实例后,就可以获取该 class的所有信息
- 通过 Class 实例获取 class信息的方法称为反射 ( Reflection )
- JVM 总是动态加载 class,可以在运行期根据条件来控制加载 class
调用构造方法
调用 Class.newInstance() 的局限是,它只能调用该类的 public 无参数构造方法。如果构造方法带有参数,或者不是 public ,就无法直接通过 Class.newInstance() 来调用。
Constructor类
为了调用任意的构造方法,Java 的反射 API 提供了 Constructor 对象,它包含一个构造方法的所有信息,可以创建个实例。Constructor 对象是一个构造方法,调用结果总是返回实例:
package com.apesource.demo;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
public class Demo05 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
Class cls = Example.class;
//调用无参构造方法创建Example类型的对象
Example ex1 = (Example)cls.newInstance();
System.out.println(ex1);
//获取所有的构造方法
System.out.println("所有的构造方法:");
Constructor[] constructArray = cls.getConstructors();
for(Constructor construct : constructArray) {
System.out.println(construct);
}
System.out.println("-------------------------");
//调用有参构造方法创建Example类型的对象
//一.获取指定参数的构造方法
//无参构造
Constructor construct0 = cls.getConstructor();
//有一个参数的构造方法
Constructor construct1 = cls.getConstructor(int.class);
//有二个参数的构造方法
Constructor construct2 = cls.getConstructor(int.class,double.class);
System.out.println(construct0);
System.out.println(construct1);
System.out.println(construct2);
//第二步:执行构造方法,创建Example类型的对象
Example ex2 = (Example)construct2.newInstance(1024,3.1415926);
System.out.println(ex2);
}
}
class Example {
public Example() {
System.out.println("Example类的无参构造!");
}
public Example(int a) {
System.out.printf("Example类的有参构造:a=%d\n",a);
}
public Example(int a,double b) {
System.out.printf("Example类的有参构造:a=%d,b=%f\n",a,b);
}
}
通过Class实例获取Constructor的方法如下:
- getConstructor(Class...):获取某个public的Constructor
- getDeclaredConstructor(Class...):获取某个定义的Constructor
- getConstructors():获取所有public的Constructor
- getDeclaredConstructors():获取搜友定义的Constructor
小结
- Constructor 对象封装了构造方法的所有信息
- 通过Class 实例的方法可以获取 Constructor 实例:getConstructor()、 getDeclaredConstructor()、getConstructors() 、getDeclaredConstructors()
- 通过Constructor 实例可以创建一个实例对象: newInstance(Object... parameters) ; 通过设置 setAccessible(true)来访问非 public 构造方法
获取继承关系
获取父类的Class和interface
package com.apesource.demo;
import java.io.FileInputStream;
//反射操作:获取继承关系
public class Demo08 {
public static void main(String[] args) {
//父类
Class superClass = FileInputStream.class.getSuperclass();
System.out.println("父类:"+superClass.getName());
System.out.println("爷类:"+superClass.getSuperclass().getName());
//接口
System.out.println("实现接口列表");
Class[] interfaceClassArray = MyStringCompartor.class.getInterfaces();
for(Class inf : interfaceClassArray) {
System.out.println(inf.getName());
}
}
}
class MyStringCompartor {}
要特别注意:getInterfaces() 只返回当前类直接实现的接口类型,并不包括其父类实现的接口类型。此外,对所有 interface 的Class 调用 getSuperclass() 返回的是 null,获取接口的父接口要用 getInterfaces()。如果一个类没有实现任何 interface ,那么 getInterfaces() 返回空数组。
继承关系
判断一个实例是否是某个类型时,正常情况下,使用instanceof操作符:
package com.apesource.demo;
public class Demo09 {
public static void main(String[] args) {
//instanceof运算符,判断"引用"和"类型"之间的关系
Object obj = Integer.valueOf(9527);
System.out.println("是否为Integer类型?"+(obj instanceof Integer));
System.out.println("是否为Double类型?"+(obj instanceof Double));
System.out.println("是否为Number类型?"+(obj instanceof Number));
System.out.println("是否为Comparable类型?"+(obj instanceof Comparable));
//isAssignableFrom()方法, 判断"类型"和"类型"之间的关系
System.out.println("Integer <= Integer ?" +Integer.class.isAssignableFrom(Integer.class));
System.out.println("Integer <= Number ?" +Integer.class.isAssignableFrom(Number.class));
System.out.println("Number <= Integer ?" +Number.class.isAssignableFrom(Integer.class));
System.out.println("Double <= Integer ?" +Double.class.isAssignableFrom(Integer.class));
System.out.println("Comparable <= Integer ?" +Comparable.class.isAssignableFrom(Integer.class));
}
}
小结
- 通过Class对象可以获取继承关系:
Class getSuperclass():获取父类类型
Class[] getInterfaces():获取当前类实现的所欲哦接口
- 通过Class对象的isAssignableFrom()方法可以判断一个向上转型是否可以实现
访问字段
获取Filed字段
对任意一个Object实例,只要我们获取了它的Class,就可以获取它的一切信息。Class类提供了以下几个方法来获取字段:
- Field getField(name):根据字段名获取当前类中某个public的field(包括父类)
- Field getDeclaredField(name):根据字段名获取当前类中定义的某个field(不包括父类)
- Field[] getFields():获取所有public的field(包括父类)
- Field[] getDeclaredFields():获取当前类在定义的所有field(不包括父类)
package com.apesource.demo;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
//反射操作:访问字段(成员变量)
//每个字段都会被封装成一个Field对象
public class Demo10 {
public static void main(String[] args) {
Class cls = Book.class;
//所有public访问修饰符定义的字段
//Field[] fields = cls.getFields();
//所有定义的字段
Field[] fields = cls.getDeclaredFields();
for(Field field : fields) {
System.out.println("成员变量访问修饰符(int):"+field.getModifiers());
System.out.println("成员变量访问修饰符:" + Modifier.toString(field.getModifiers()));
System.out.println("成员变量类型:"+field.getType());
System.out.println("成员变量名称:"+field.getName());
System.out.println();
}
}
}
class Product{
private double price;
@Override
public String toString() {
return "Product [price=" + price + "]";
}
}
class Book extends Product{
public String bookName; //书名
public int stock; //库存
private double sale; //折扣
private boolean isShelf; //是否上架
public String getBookName() {
return bookName;
}
public void setBookName(String bookName) {
this.bookName = bookName;
}
public int getStock() {
return stock;
}
public void setStock(int stock) {
this.stock = stock;
}
public double getSale() {
return sale;
}
public void setSale(double sale) {
this.sale = sale;
}
public boolean isShelf() {
return isShelf;
}
public void setisShelf(boolean isShelf) {
this.isShelf = isShelf;
}
@Override
public String toString() {
return "图书对象 :Book [bookName=" + bookName + ", stock=" + stock + ", sale=" + sale + ", isShelf=" + isShelf + "]";
}
}
一个Field对象包含了一个字段的所有信息:
- getName():返回字段名称,如,"name"
- getType():返回字段类型,也是一个Class实例,如,String.class
- getModifiers():返回字段的修饰符,它时一个int,不同的bit表示不同的含义
获取字段值
利用反射拿到字段的一个Field 实例只是第一步,我们还可以拿到一个实例对应的该字段的值。例如,对于一个 Book 实例,我们可以先拿到 bookName 字段对应的 Field ,再获取这个实例的 bookName字段的值:
package com.apesource.demo;
import java.lang.reflect.Field;
//使用反射的方式,完成成员变量保存值
public class Demo11 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchFieldException, SecurityException {
//编译期完成对象类型和成员变量的访问
// Book book = new Book();
// book.bookName = "葫芦娃大战奥特曼";
//运行期
//使用反射的方式,完成成员变量保存值
Class cls = Book.class; //获取Class类型对象
Object obj = cls.newInstance(); //通过反射创建Book类的对象
//按照字段名称,获取指定字段
Field field1 = cls.getDeclaredField("bookName");
//sale字段(private)
//按照字段名称,获取指定字段
Field field2 = cls.getDeclaredField("bookName");
field2.setAccessible(true); //访问私有成员变量
field2.setDouble(obj, 0.8);
//参数一:目标Book对象
//参数二:存入成员变量中的值
field1.set(obj, "葫芦娃大战奥特曼");
System.out.println(obj);
}
}
//Book Product类在Demo10中
调用 field.setAccessible(true) 的意思是,别管这个字段是不是 public,一律允许访问。
如果使用反射可以获取 private 字段的值,那么类的封装还有什么意义? 正常情况下,我们总是通过 p.name 来访问 Book 的 bookName 字段,编译器会根据 public 、protected 和 private 决定是否允许访问字段,这样就达到了数据封装的目的。
而反射是一和非常规的用法。使用反射,首先代码非常繁琐,其次,它更多地是给工具或者底层框架来使用,目的是在不知道目标实例任何信息的情况下,获取特定字段的值.
此外, setAccessible(true) 可能会失败。如果 JVM 运行期存在 SecurityManager ,那么它会根据规则进行检查,有可能阻止 setAccessible(true) 。例如: 某个 SecurityManager可能不允许对 java 和 javax 开头的 package 的类调用 setAccessible(true),这样可以保证 JVM 核心库的安全
设置字段值
通过Field 实例既然可以获取到指定实例的字段值,自然也可以设置字段的值。设置字段值是通过 Field.set(object,object) 实现的,其中第一个Object 参数是指定的实例,第二个 Object 参数是待修改的值。示例代码如下:
package com.apesource.demo;
import java.lang.reflect.Field;
//反射操作:访问对象中的成员变量(字段)值
public class Demo12 {
public static void main(String[] args) throws IllegalArgumentException, IllegalAccessException {
Book book = new Book();
book.setBookName("一千零一夜");
book.setSale(0.75);
book.setisShelf(true);
book.setStock(453);
printInfo(book);
}
public static void printInfo(Object obj) throws IllegalArgumentException, IllegalAccessException {
//输出参数对象的所有成员变量和值
Class cls = obj.getClass();
Field[] fields = cls.getDeclaredFields();
for(Field field : fields) {
System.out.println("成员变量名称:"+field.getName());
//book.bookName
//判断是否可以访问
if(!field.isAccessible()) {
field.setAccessible(true); //设置私有成员变量允许访问
}
System.out.println("成员变量内容:"+field.get(obj));
System.out.println();
}
}
}
上述代码可以说明通过反射可以直接修改字段的值。同时,修改非public字段,需要首先调用setAccessible(true)。
小结
- Java 的反射 API 提供的Field 类封装了字段的所有信息
- 通过 class实例的方法可以获取 Field 实例:getField(),getFields(),getDeclaredField(),getDeclaredFields()
- 通过Field 实例可以获取字段信息:getName(),getType(),getModifiers()
- 通过Field 实例可以读取或设置某个对象的字段,如果存在访问限制,要首先调用 setAccessible(true) 来访问非 public 字段
- 通过反射读写字段是一种非常规方法,它会破坏对象的封装。
调用方法
Method类
Class类提供了以下几种方法来获取Method:
- Method getMethod(name,Class...): 获取某个public的Method(包括父类)
- Method getDeclaredMethod(name,Class...): 获取当前类的某个Method(不包括父类)
- Method[] getMethods(): 获取所有public的Method(包括父类)
- Method[] getDeclaredMethods(): 获取当前类的所有Method(不包括父类)
package com.apesource.demo;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Parameter;
//反射操作:获取方法
//每一个方法都会被封装成一个Method方法
public class Demo13 {
public static void main(String[] args) {
Class cls = Book.class;
//获取所有public方法(包含父类)
// Method[] methods = cls.getMethods();
//获取所有定义的方法(仅包含当前类)
Method[] methods = cls.getDeclaredMethods();
for(Method method : methods) {
System.out.println("方法的访问修饰符:"+Modifier.toString(method.getModifiers()));
System.out.println("方法的返回值类型:"+method.getReturnType());
System.out.println("方法的名称:"+method.getName());
//获取所有的参数类型
// System.out.println("方法的参数类型:");
// Class[] paramTypes = method.getParameterTypes();
//获取所有的参数对象
Parameter[] params = method.getParameters();
for(Parameter p : params) {
System.out.println(p.getName());
System.out.println(p.getType());
System.out.println("-----------");
}
System.out.println();
}
}
}
一个 Method 对象包含一个方法的所有信息:
- getName() :返回方法名称,例如:getScore"
- getReturnType(): 返回方法返回值类型,也是一个 class 实例,例如: string.class
- getParameterTypes() : 返回方法的参数类型,是一个class 数组,例如:{string.class, int.class}
- getModifiers() : 返回方法的修饰符,它是一个 int ,不同的 value 表示不同的访问修饰符
调用方法
如果用反射来调用substring方法,需要以下代码
package com.apesource.demo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//反射操作:调用方法
public class Demo14 {
public static void main(String[] args) throws InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
// Base b= new Base();
// int r = b.create(1000);
//获取Class对象
Class cls = Book.class;
Object obj = cls.newInstance(); //创建Base对象
//按照方法名称和"参数类型"获取Method方法对象
//creat()
//Method method = cls.getMethod("create");
//create(int x)
Method method = cls.getMethod("create", int.class);
//Method对象的invoke()作用:
//以反射的方式执行create()方法
int r = (int)method.invoke(obj, 1000);
System.out.println(r);
}
}
class Base{
public int create() {
return(100);
}
public int create(int x) {
return(int)(Math.random()*x);
}
}
substring()有两个重载方法,我们获取的时String substring(int)这个方法。
调用静态方法
如果获取到的 Method 表示一个静态方法,调用静态方法时,由于无需指定实例对象,所以 invoke 方法传入的第一个参数永远为 nul1 。
package com.apesource.demo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
//以反射的方式调用静态方法
public class Demo15 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//计算以十为底的对数
//获取某个指定数字的位数
System.out.println((int)Math.log10(1234344432)+1);
//反射调用
Class cls = Math.class;
Method methodLog10 = cls.getMethod("log10",double.class);
int size = Double.valueOf((double)methodLog10.invoke(null, 1234344432)).intValue() + 1;
System.out.println(size);
}
}
调用非public方法
调用非public方法,我们提供Method.setAccessible(true)允许其调用:
package com.apesource.demo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo17 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
Person p = new Person();
Method m = p.getClass().getDeclaredMethod("setName", String.class);
m.setAccessible(true);
m.invoke(p, "Bob");
System.out.println(p.name);
}
}
class Person{
String name;
private void setName(String name) {
this.name = name;
}
}
与Field相同的地方是,Method 实例的 setAccessible(true) 可能会失败。如果JVM 运行期存在 SecurityManager ,那么它会根据规则进行检查有可能阻止 setAccessible(true) 。例如,某个 SecurityManager 可能不允许对 java 和 javax 开头的 package 的类调用 setAccessible(true) ,这样可以保证 JVM 核心库的安全。
多态
使用反射调用方法时,仍然遵循多态原则:即总是调用实际类型的覆写方法(如果存在):
package com.apesource.demo;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class Demo16 {
public static void main(String[] args) throws NoSuchMethodException, SecurityException, IllegalAccessException, IllegalArgumentException, InvocationTargetException {
//获取Person的hello方法
Method h = Personx.class.getMethod("hello");
//对Student实例调用hello方法:
//目标对象绝对调用方法的归属
h.invoke(new Studentx());
}
}
class Personx{
public void hello() {
System.out.println("Person : hello");
}
}
class Studentx extends Personx{
public void hello() {
System.out.println("Student : hello");
}
}
小结
- Java 的反射 API 提供的 Method 对象封装了方法的所有信息
- 通过Class 实例的方法可以获取 Method 实例: getMethod(),getMethods0, getDeclaredMethod() , getDeclaredMethods()
- 通过 Method 实例可以获取方法信息:getName(),getReturnType(),getParameterTypes(),getModifiers()
- 通过 Method 实例可以调用某个对象的方法:Object invoke(object instance,Object... parameters)
- 通过设置 setAccessible(true) 来访问非 public 方法
- 通过反射调用方法时,仍然遵循多态原则
动态代理
代理模式
代理模式:给某一个对象提供一个代理,并由代理对象来控制对真实对象的访问。代理模式是一种结构型设计模式。
代理模式角色分为 3 种:
- Subject (抽象主题角色):定义代理类和真实主题的公共对外方法,通常被设计成接口
- RealSubject (真实主题角色): 真正实现业务逻辑的类
- Proxy (代理主题角色) :用来代理和封装真实主题
代理模式的结构比较简单,其核心是代理类,为了让客户端能够一致性地对待真实对象和代理对象,在代理模式中引入了抽象层。
如果根据字节码的创建时机来分类,可以分为静态代理和动态代理:
- 所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了
- 而动态代理的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以在运行前并不存在代理类的字节码文件。
静态代理
静态代理提供UserServiceProxy实现,代理类同时也需要实现UserService接口。
UserServiceProxy:
package com.apesource.proxy2;
public class UserServiceProxy implements UserService{
private UserService target;
public UserServiceProxy(UserService target) {
target = new UserServiceImpl();
}
@Override
public void select() {
before();
target.select();
after();
}
@Override
public void update() {
before();
target.update();
after();
}
private void before() {
System.out.println("kaishi");
}
private void after() {
System.out.println("结束");
}
}
UserServiceImpl:
package com.apesource.proxy2;
// 真正的实现类
public class UserServiceImpl implements UserService {
@Override
public void select() {
System.out.println("select * ..................");
System.out.println("数据库中完成用户信息的查询执行!");
}
@Override
public void update() {
System.out.println("update ...................");
System.out.println("数据库中用户状态的更新执行!");
}
}
UserService:
package com.apesource.proxy2;
public interface UserService {
void select();
void update();
}
通过静态代理,我们达到了功能增强的目的,而且没有侵入原代码,这是静态代理的一个优点.
虽然静态代理实现简单,且不侵入原代码,但是,当场景稍微复杂一些的时候,静态代理的缺点也会暴露出来。
1.当需要代理多个类的时候,由于代理对象要实现与目标对象一致的接口,有两种方式:
·只维护一个代理类,由这个代理类实现多个接口,但是这样就导致代理类过于庞大
·新建多个代理类,每个目标对象对应一个代理类,但是这样会产生过多的代理类
2.当接口需要增加、删除、修改方法的时候,目标对象与代理类都要同时修改,不易维护。
动态代理
Java中两种常见的动态代理方式:JDK原生动态代理和CGLIB动态代理(第三方开源类库)。
JDK动态代理
JDK动态代理主要涉及两个类: java.lang.reflect.Proxy和 java.lang.reflect.InvocationHandler 。我们通过编写一个调用逻辑处理器 LogHandler 类案例来提供日志增强功能,并实现 InvocationHandler 接口;在 LogHandler 中维护一个目标对象这个对象是被代理的对象 (真实主题角色) ;在 nvoke() 方法中编写方法调用的逻辑处理。
LoginvocationHandlerImpl:
package com.apesource.proxy3;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
//LoginvocationHandlerImpl接口实现类:代理类中扩展逻辑抽取封装
public class LoginvocationHandlerImpl implements InvocationHandler {
private Object target;
public LoginvocationHandlerImpl(Object target) {
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.printf("方法%s开始执行.............\n",method.getName());
//执行目标对象的目标方法
Object retuenValue = method.invoke(target, args);
System.out.printf("方法%s结束执行.............\n",method.getName());
return retuenValue;
}
}
Client:
package com.apesource.proxy3;
import java.lang.reflect.Proxy;
//动态代理
public class Client {
public static void main(String[] args) {
//案例一
//创建目标对象
//创建InvocationHandler
LoginvocationHandlerImpl handler1 =
new LoginvocationHandlerImpl(new UserServiceImpl());
//创建UserService接口的动态代理对象
UserService proxy1 =
(UserService)Proxy.newProxyInstance(
UserService.class.getClassLoader(),
new Class[] {UserService.class},
handler1);
//通过代理对象调用方法 ——> LoginvocationHandlerImpl实现类的invoke
//proxy.select();
//proxy.update();
System.out.println(proxy1.getClass());
//案例二
//创建目标对象
OrderServiceImpl orderServiceTarget = new OrderServiceImpl();
//创建InvocationHandler
LoginvocationHandlerImpl handler2 =
new LoginvocationHandlerImpl(orderServiceTarget);
//创建OrderService的"动态"代理对象
OrderService proxy2 = (OrderService)Proxy.newProxyInstance(
orderServiceTarget.getClass().getClassLoader(),
orderServiceTarget.getClass().getInterfaces(),
handler2);
System.out.println("Proxy2的类型:"+proxy2.getClass());
proxy2.createOrder();
//创建InvocationHandler
LoginvocationHandlerImpl handler3 =
new LoginvocationHandlerImpl(orderServiceTarget);
//创建OrderService的"动态"代理对象
OrderService proxy3 = (OrderService)Proxy.newProxyInstance(
orderServiceTarget.getClass().getClassLoader(),
orderServiceTarget.getClass().getInterfaces(),
handler3);
System.out.println("Proxy3的类型:"+proxy3.getClass());
proxy3.createOrder();
}
}
动态代理的用途
动态代理在 Java 中有着广泛的应用,比如 Spring AOP、 Hibernate 数据查询、测试框架的后端 mock 、 RPC 远程调用、Java注解对象获取、日志、用户鉴权、全局性异常处理、性能监控,甚至事务处理等。
小结
- Java 标准库提供了动态代理功能,允许在运行期动态创建一个接口的实例;
- 动态代理是通过 Proxy 创建代理对象,然后将接口方法“代理”给 InvocationHandler 完成的。