Java中Reflection反射总结

概念

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 完成的。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值