十、Java反射技术
1、反射的概念
反射的引入:当程序在运行时接受一个外部传入的对象,该对象的编译类型是Object,但程序需要调用该对象运行类型的方法:
1、若该对象的编译类型、运行类型都知道,使用nstanceof判断然后强转。
2、编译时无法知道该对象属于什么类,程序只能依靠运行时信息来发现对象的真实信息,此时需要通过反射获取对象真正的类型。
反射机制:类在程序运行时能获取到自身的信息。在java中,只要给定类的名称,就可以根据反射机制获取类的信息。
优点:实现动态创建对象和编译,体现出灵活性。(动态编译:运行时确定类型,绑定对象,灵活,体现多态的引用;静态编译:编译时确定类型,绑定对象)
缺点:对性能有影响,因为反射是一种解释操作,慢于直接执行操作。
2、class类
Class类:class类用来描述java中所有的类。类:class -> 对象:任意java类(具体的表现形式或者说实现方式,是个各类在内存中唯一存在的独立的字节码,一个类被类加载器加载到内存中,是以字节码的形式占据一块内存空间,所以不同类字节码也不同)。
Class类对象相当于某个java类对象照镜子后得到的一个对象,该对象包含这个类的数据成员名、方法、构造器、实现了哪些接口,等等一系列该类的信息。Class 对象只能由系统建立对象;一个类在 JVM 中只会有一个Class实例;每个类的实例都会记得自己是由哪个 Class 实例所生成 。
生成Class对象的过程其实是如此的:
当我们编写一个新的java类时,JVM就会帮我们编译成class对象,存放在同名的.class文件中。在运行时,当需要生成这个类的对象,JVM就会检查此类是否已经装载内存中。若是没有装载,则把.class文件装入到内存中。若是装载,则根据class文件生成实例对象。
Class类的获取:类在加载器加载到内存中时,内存中就有了表征这个类信息的字节码,获取这个类的Class对象,也就是将这些类信息的字节码获取出来。有三种方法:
1) 通过类的class属性,该方法安全性高,程序性能也高,如String.class、Data.class。
2) 使用Class类的forName(String 全类名)方法,可能抛出异常。
3) 调用某个对象的getClass方法,该方法来自Ojbect类。
注:
Java基本类型和关键字void通过class属性也表示为class对象,如int.class;
Class类中的isPrimitive判定class对象是否是基本类型,isArray判定是否是数组类型。
包装类和Void类的TYPE字段就是其基本类型的class对象(唯一,所以是相同class对象),但是其class属性并不是其基本类型的class,即:Integer.TPYE == int.class;Integer.class!=int.class(注:因为重载的方法是根据参数列表判断重载的,分别以int和Integer类参数类型的两个同名方法可以看做重载,编译通过);
数组类型的class对象: Class<int[]> classint = int[].class;
TestClass.java (Class类的三种获取方法、包装类和基本类的class的Type和class属性比较、数组的class)
package blog10;
import org.junit.Test;
/**
* Class类的三种获取方法、包装类和基本类的class的Type和class属性比较
*/
public class TestClass {
//1、Class类的三种获取方法
@Test
public void getMyClass(){
//Method 1:the field class
Class<?> class1 = TestAAA.class; //这种方法并未加载类,因为没有输出静态代码块
Class<?> class2 = TestAAA.class;
System.out.println(class1);
System.out.println( class1 == class2); //true,说明是一个对象,对应一个相同的字节码,对应一份编译出来的.class文件
//Method 2:the method forName of Class
try {
Class<?> class3 = Class.forName("blog10.TestAAA"); //该句加载了类,因为输出了"AAA..."
System.out.println(class3);
//Class<?> class5 = Class.forName("TestAAA"); //抛出异常,必须使用全类名
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
// Method 3:the method getClass of object
Class<?> class4 = new TestAAA().getClass(); //肯定加载了类
System.out.println(class4);
System.out.println(class4 == class1); //true,同一个类只有一个.class文件,一份字节码,和获取class的方法无关
}
// 2、包装类和基本数据类型的Class、数组的class
@Test
public void compareClass(){
// 基本数据类型和包装类型,int和Integer为例
Class<?> in = int.class;
System.out.println(in.isPrimitive()); //true
System.out.println(in);//int
Class<?> in1 = Integer.TYPE;
System.out.println(in1.isPrimitive());//int
System.out.println(in1);//int
Class<?> in2 = Integer.class;
System.out.println(in2.isPrimitive());//false
System.out.println(in2);//java.lang.Integer
System.out.println(in == in1);//true
System.out.println(in == in2);//false
//上述种种测试,说明:int.class和Integer.TYPE是相同的,表示基本数据类型的class,
//而Integer.class是包装类的class,和int.class不同。
//因为上述原因,可知,在重载函数时,分别以int和Integer为参数的同名方法是不同方法,可算作重载
// 数组的class
Class<String[]> arrayClass1 = String[].class;
System.out.println(arrayClass1);//Ljava.lang.String
Class<int[]> arrayClass2 = int[].class; //int虽然不能做泛型参数,但是int[]数组类型可以
System.out.println(arrayClass2);
System.out.println(arrayClass2.isArray());//true,是数组
}
}
class TestAAA{
public TestAAA() {
}
static{
System.out.println("AAA...");
}
}
3、class类的使用
获取class的基本属性(类名,包名之类)、表征的类的字段Filed、方法Method、构造器Constructor。通常包括四类方法:获取全部(考虑修饰符Modifiers)、获取特定(考虑Modifiers)、获取全部(忽略修饰符,暴力获取)、获取特定(忽略修饰符,暴力获取)。
TestGetInfo.java(利用Class类对象获取各种该对象对应的类的信息)
package blog10;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
/**
* 利用Class对象获取各种Class类的信息
*/
public class TestGetInfo extends A implements B,C{
public class D{/*内部类、接口可看做是一种封装类型的属性*/}
public interface E{ }
//获取基本属性
public void getFounderInfo() {
Class<?> myClass = TestGetInfo.class;
System.out.println(myClass); //blog10.TestGetInfo
System.out.println(myClass.getPackage());//获取包名:package blog10
System.out.println(myClass.getName());//获取全类名:blog10.TestGetInfo
System.out.println(myClass.getSimpleName());//获取类名:TestGetInfo
//获取父类
Class<?> parent = myClass.getSuperclass();
System.out.println(parent); //blog10.A
//获取接口
System.out.println(myClass.getInterfaces());//Ljava.lang.class 可见是一个数组
Class<?>[]/*表示任意类型的class类型的数组*/ interfaces = myClass.getInterfaces();
for(Class<?> cla : interfaces){
System.out.println(cla); //遍历输出所有实现的接口:interface blog10.B;interface blog10.C
}
//获取内部类或内部接口,虽然看做是属性field,但是不能用field的方法获取
Class<?>[] innerClass = myClass.getClasses();//方法getDeclaredClasses()获取所有的,包括private的
for(Class<?> cla:innerClass){
System.out.println(cla);//遍历输出内部类和接口如下:
// class blog10.TestGetInfo$D ,可见内部类与外部类以$符号连接输出
// interface blog10.TestGetInfo$E
}
//获取元素(filed、method或者内部类、或者本类)修饰符getModifiers()
System.out.println(myClass.getModifiers()); //输出1,表示public
System.out.println(Modifier.toString(myClass.getModifiers())); // public
System.out.println(parent.getModifiers());//输出0,表示父类A的修饰符为空
}
//获取字段、方法、构造器
public static void getImportantInfo() throws ClassNotFoundException, NoSuchMethodException, SecurityException{
Class<?> cla = Class.forName("blog10.Test");
//获取构造器
Constructor<?>[] con = cla.getConstructors();//获取所有本类public构造器
for(Constructor<?> c:con){
System.out.println(c);
/*
public blog10.Test()
public blog10.Test(int)
*/
}
Constructor<?> con1 = cla.getConstructor(int.class); //获取本类指定的public构造器
System.out.println(con1);//public blog10.Test(int)
Constructor<?>[] con2 = cla.getDeclaredConstructors();//暴力反射获取所有的构造器,忽略修饰符
for(Constructor<?> c:con2){
System.out.println(c);// Test() Test(int) Test(String)
}
Constructor<?> con3 = cla.getDeclaredConstructor(String.class);
System.out.println(con3);//Test(String)
Constructor<?>[] con4 = cla.getSuperclass().getDeclaredConstructors();//暴力获取父类所有构造器
for(Constructor<?> c:con4){
System.out.println(c); //A(string) A()
}
// 获取字段field
Field[] f = cla.getDeclaredFields(); //直接演示暴力获取字段
for(Field a:f){
System.out.println(a); //private int t ;public int t2
}
//获取方法Method:
//getMethods:获取了所有的方法包括从父类继承而来的方法,但不能获取到private方法
//getDeclaredMethods:只能获取子类定义的方法,包括private方法
Method [] ms = cla.getMethods(); // 获取所有的public方法,包括继承自父类的
for(Method ma:ms){
System.out.println(ma); //连超类Ojbect的方法都打印出来了
}
Method m = cla.getMethod("mT1", String.class); //获取指定的public method
System.out.println(m); // public void Test.mT1(String)
ms = cla.getDeclaredMethods();// 暴力获取所有的方法,不受修饰符限制
for(Method mb:ms){
System.out.println(mb); //但是不能获取到直接或间接父类继承而来的方法
}
}
public static void main(String[] args) throws Exception{
@SuppressWarnings("unused")
TestGetInfo myTest= new TestGetInfo();
//myTest.getFounderInfo();
TestGetInfo.getImportantInfo();
}
}
@SuppressWarnings("unused")
class A{
private int a;
public int b;
public A(){}
private A(String s){}
private void mA(){}
public void mB(){}
}
interface B{
}
interface C{
}
@SuppressWarnings("unused")
class Test extends A{
private int t;
public int t2;
public Test(){}
private Test(String s){}
public Test(int i){}
private void mT(){}
public void mT1(String s){}
}
4、反射
利用反射动态创建class对应的类的对象:1)Class.newInstance,此时class对象对应的类必须要有无参的构造器。2)Class对象获取指定的Constructor对象,在调用Constructor对象的newInstance方法创建对象,若该构造方法被private,则需要先设置访问权限为true,setAccessible方法。
TestNewInstance(两种反射创建对象的方法,注:第二种方法功能强大,甚至能破解单例,所以一般的单例不安全,但是枚举实现的单例还是没法破解)
package blog10;
import java.lang.reflect.Constructor;
/**
* 两种反射创建对象的方法
*/
public class TestNewInstance {
public static void main(String[] args) throws Exception {
// new 方法创建对象
System.out.println(new Student());
// 反射方法创建对象
Class<?> cla = Class.forName("blog10.Student");
System.out.println(cla.newInstance()); //必须保证默认的无参构造器存在,调用的就是它,所以和上面的new结果一样
// 调用指定的构造器创建对象
Constructor<?> c = cla.getDeclaredConstructor(String.class,int.class,int.class,int.class);
Object obj = c.newInstance("小明",23,4,34); //若该构造器为私有的,需要setAccessible
System.out.println(obj); // 存在这种方式,所以一般的单例就不安全了
}
}
class Person {
private String name;
private int age;
int weight;
int height;
@SuppressWarnings("unused")
private int test(String name,int age){
System.out.println("!!!");
return age;
}
@SuppressWarnings("unused")
private void PrintInfo2(){
System.out.println("a Person...");
}
public int test(String name,Integer age,Integer ID){
System.out.println("age:" + age);
System.out.println("ID:" + ID);
return age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
System.out.println("Person有参构造器");
}
// 反射时调用该构造器
public Person() {
System.out.println("Person无参构造器");
}
@Override
public String toString() {
return "Person [name=" + name + ", age=" + age + "]";
}
}
class Student extends Person {
private int ID;
public int mark;
public static void test1(int...args){
System.out.println("基本数据类型传入可变参数列表....");
}
public static void test2(String...args){
System.out.println("引用类型传入可变参数列表....");
}
public static void staticMethod(){
System.out.println("This is a static method...");
}
public void setMark(int mark){
this.mark = mark;
}
@SuppressWarnings("unused")
private void PrintInfo1(){
System.out.println("a student...");
}
public Student(String name, int age, int iD,int mark) {
super(name, age);
ID = iD;
this.mark = mark;
}
@Override
public String toString() {
return super.toString()+"Student [ID=" + ID + ", mark=" + mark + "]";
}
//无参构造器给反射用
public Student(){
System.out.println("Student 无参构造器");
}
}
使用反射调用方法Method:获取到Method之后,调用其invoke方法执行底层方法(共有方法、私有方法、静态方法、带返回值方法、不带返回值方法、带可变参数方法、)。
TestRunMethod.java (反射调用各类方法实例)
package blog10;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
/**
* 反射调用各种类型的方法方法
*/
public class TestRunMethod {
public static void main(String[] args) throws Exception {
Class<?> cla = Class.forName("blog10.Student");
//调用void public方法:setMark 为例
Method m = cla.getMethod("setMark", int.class);
Constructor<?> c = cla.getDeclaredConstructor(String.class,int.class,int.class,int.class);
Object obj = c.newInstance("小明",23,4,34);
System.out.println(obj); // mark = 34
m.invoke(obj,60);
System.out.println(obj); //mark = 60
// 调用私有方法
m = cla.getDeclaredMethod("PrintInfo1");
m.setAccessible(true);
m.invoke(obj);
// 调用静态方法,不用传入对象到invoke,所以Object参数为null,参数列表视方法的参数了列表而定
m = cla.getMethod("staticMethod");
m.invoke(null);
// 调用可变参数列表,将可变参数看做一个数组
m = cla.getMethod("test1", int[].class);
m.invoke(null, new int[]{1,2,3});
m = cla.getMethod("test2", String[].class);
m.invoke(null, (Object)new String[]{"A","B","C"}); //这里的强转千万不能少,少了运行错误的!!!!
m.invoke(null, new Object[]{new String[]{"V","D","A"}}); //推荐写法
}
}
使用反射操作字段Field:获取getXXX或get方法(字段为基本类型int getInt(Object)、字段为引用类型String get(Object))。设置setXXX或者set方法(set(Object,value))。若字段为private,则需要设置访问权限。
TestReflectField.java(反射关于字段的操作)
package blog10;
import java.lang.reflect.Field;
/**
* 反射关于字段Field的操作: 获取类 获取字段 赋值字段
*/
public class TestReflectField {
public static void main(String[] args) throws Exception {
//获取
Class<?> cla = Class.forName("blog10.Student");
Field f =cla.getDeclaredField("ID");
f.setAccessible(true);
System.out.println(f);
//操作
Object obj = cla.newInstance();
System.out.println(obj); // ID = 0 ,默认无参构造器创建对象各属性都为默认值
f.set(obj, 9527);
System.out.println(obj); // ID = 9257
Object o = f.get(obj);
System.out.println(o); //字段的提取,赋值号左右必须类型匹配,分为引用类型和基本数据类型两种情况
int i = f.getInt(obj);
System.out.println(i); //这两种提取结果一样
}
}
使用反射获取泛型:
TestReflectGenric.java(反射关于泛型的操作)
package blog10;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
/**
* 反射关于泛型的操作:field、method、class的泛型操作都是一致的,基本步骤:
* 1、获取类
* 2、获取getGeneric泛型的元素。返回的都是Type类型
* 3、将Type类型返回值下转成ParameterizedType带参数的类型
* 4、调用ParameterizedType的getActualTypeArguements返回该类型的参数的数组(Type类型)
*/
public class TestReflectGeneric {
Map<String,Integer> map = new HashMap<>();
public static void main(String[] args) throws Exception {
Class<?> cla = TestReflectGeneric.class;
Field f = cla.getDeclaredField("map");
System.out.println(f); // TestReflectGeneric.map
Class<?> ct1 = f.getType();
System.out.println(ct1);//返回f对应的字段声明的返回类型:java.util.Map
Type t = f.getGenericType();//返回f对应的字段的泛型类型
System.out.println(t);//java.util.Map<String,Integer>
//Type中没有实际方法,向下转型到其子类,调用其子类的方法
ParameterizedType pt = (ParameterizedType)t;
System.out.println(pt.getRawType()); //getRawTpye返回此类型的类或接口
Type[] ta = pt.getActualTypeArguments();//返回此类的实际参数类型的Type数组
for(Type ts:ta){
System.out.println(ts);//分别输出:string ,Integer
}
}
}
使用反射获取注解:
关于注解:Annotation 其实就是代码里的特殊标记, 这些标记可以在编译, 类加载, 运行时被读取, 并执行相应的处理. 通过使用 Annotation, 程序员可以在不改变原有逻辑的情况下, 在源文件中嵌入一些补充信息;Annotation 可以像修饰符一样被使用, 可用于修饰包,类, 构造器, 方法, 成员变量, 参数, 局部变量的声明, 这些信息被保存在 Annotation 的 “name=value” 对中.;Annotation 能被用来为程序元素(类, 方法, 成员变量等) 设置元数据。所谓的元数据就是修饰数据的一些数据。
使用 Annotation 时要在其前面增加 @ 符号, 并把该 Annotation 当成一个修饰符使用. 用于修饰它支持的程序元素
自定义注解:定义新的 Annotation 类型使用 @interface 关键字;Annotation 的成员变量在 Annotation 定义中以无参数方法的形式来声明. 其方法名和返回值定义了该成员的名字和类型.;可以在定义 Annotation 的成员变量时为其指定初始值, 指定成员变量的初始值可使用 default 关键字;没有成员定义的 Annotation 称为标记; 包含成员变量的 Annotation 称为元数据 Annotation。
JDK的元Annotation用于修饰其他的Annotation:@Retention(注解的生命周期)@Target(注解修饰的对象)。
TestReflectAnnotation.java (注解的使用、定义,反射关于注解的操作)
package blog10;
import java.lang.annotation.Annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.reflect.Method;
/**
* 初解及注解的应用与反射
*/
public class TestReflectAnnotation {
//使用注解
public static void main(String[] args) throws Exception {
Class<?> clazz = Class.forName("blog10.MyStudent");
Method method = clazz.getDeclaredMethod("setMark", int.class); //获取这个带注解的方法
Annotation annotation = method.getAnnotation(MarkRange.class); //参数为指定的annotation的class类型
int value = 80; //得到了方法的注解annotation,假设现在反射调用方法,要设定mark=130
//当value超出注解标注的范围,直接抛出错误,比如value = 130,程序异常
if(annotation!=null){
if(annotation instanceof MarkRange){
MarkRange markRange = (MarkRange)annotation;
//强转类型
if(value<markRange.min() || value > markRange.max()){
throw new RuntimeException("错误!成绩超出范围");
}
}
}
Object obj = clazz.newInstance();
System.out.println(obj);
method.setAccessible(true);
method.invoke(obj, value);
System.out.println(obj);
}
}
//自定义注解
@Retention(RetentionPolicy.RUNTIME) //运行时可见
@Target(value={ElementType.METHOD}) // 作用范围:method
@interface MarkRange {
public int min() default 0;
public int max() default 100;//设置参数,mark的最大最小值,规定mark取值范围
}
class MyStudent extends Person{
private int ID;
public int mark;
@MarkRange(min=10,max=90) /*可以不写括号内容,因为已经有default(0——100)*/
public void setMark(int mark){
this.mark = mark;
}
@SuppressWarnings("unused")
private void PrintInfo1(){
System.out.println("this is my student...");
}
public MyStudent(String name, int age, int iD,int mark) {
super(name, age);
ID = iD;
this.mark = mark;
}
@Override
public String toString() {
return super.toString()+"Student [ID=" + ID + ", mark=" + mark + "]";
}
//无参构造器给反射用
public MyStudent(){
System.out.println("Student 无参构造器");
}
}
ReflectionUtils.java (一个各种反射操作的工具类)
package blog10;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
public class ReflectionUtils {
//自定义一个ReflectionUtils工具类,用于反射时调用
//方法1:通过反射,获取定义Class时声明的父类的泛型参数的类型
//如:public A extends B<C,D> ,获取C.class 和D.class
@SuppressWarnings("rawtypes")
public static Class getSuperClassGenericType(Class clazz,int index ){
Type genType = clazz.getGenericSuperclass();
if(!(genType instanceof ParameterizedType)){
return Object.class;//如果父类的泛型没有参数,即没有使用泛型。返回Object.class、
}
//若genType instanceof ParameterizedType,则强转,然后通过ParameterizedType的方法获取参数数组
Type [] params = ((ParameterizedType)genType).getActualTypeArguments();
if(index>params.length || index<0){
throw new RuntimeException("函数传入的参数index错误!");
}
if(!(params[index] instanceof Class)){
//若参数不是Class类型的,返回Object.class
return Object.class;
}
return (Class) params[index];
}
//方法2:通过反射,获取Class定义中声明的父类的泛型参数
@SuppressWarnings({ "unchecked", "rawtypes" })
public static<T> Class<T> getSuperGenericType(Class clazz){
return getSuperClassGenericType(clazz, 0);
}
//方法3:循环向上转型,获取指定的方法
public static Method getDeclaredMethod(Object obj,String methodName,Class<?> [] parameterTypes){
for(Class<?> clazz = obj.getClass();clazz != Object.class;clazz = clazz.getSuperclass()){
try {
return clazz.getDeclaredMethod(methodName, parameterTypes);
} catch (NoSuchMethodException | SecurityException e) {
//指定的Method不在当前类,向上转到父类中寻找
}
}
return null;
}
//方法4:直接调用对象方法,忽略访问权限修饰符
@SuppressWarnings("rawtypes")
public static Object invokeMethod(Object obj,String methodName,
Class [] parameterTypes) throws InvocationTargetException {
Method method = getDeclaredMethod(obj, methodName, parameterTypes);
if(method == null){
throw new IllegalArgumentException("can not find the method!");
}
method.setAccessible(true);
try {
return method.invoke(obj, (Object)parameterTypes);
} catch (IllegalAccessException e) {
System.out.println("不可能抛出的异常");
}
return null;
}
//方法5:直接设置对象属性值,忽略private/protected修饰
@SuppressWarnings("rawtypes")
public static void setFieldValue(Object object,String fieldName,Object value) {
Field field = null;
Class clazz = object.getClass();
for(;clazz!=Object.class;clazz = clazz.getSuperclass()){
try {
field = clazz.getDeclaredField(fieldName);
if(field!=null){
field.setAccessible(true);
field.set(object, value);
return;
}
} catch (Exception e) {
}
}
}
//方法6:直接读取对象的属性值,忽略private/proteccted修饰符,也不经过getter
@SuppressWarnings({ "rawtypes" })
public static Object getFieldValue(Object object,String fieldName){
Class clazz = object.getClass();
for(;clazz!=Object.class;clazz = clazz.getSuperclass()){
try {
Field field = clazz.getDeclaredField(fieldName);
if(field!=null){
field.setAccessible(true);
return (Object)field.get(object);
}
} catch (Exception e) {
}
}
//没有获取到的时候返回null
System.out.println("have not get the field vaule...");
return null;
}
}
5、动态代理
动态代理概念和Proxy类:Java动态代理主要涉及到两个类:
1) InvocationHandler:该接口中仅定义了一个Object : invoke(Object proxy, Method method, Object[] args);参数proxy指代理类,method表示被代理的方法,args为method中的参数数组,返回值Object为代理实例的方法调用返回的值。这个抽象方法在代理类中动态实现。
2) Proxy:所有动态代理类的父类,提供用于创建动态代理类和实例的静态方法。
所谓动态代理类是在运行时生成的class,在生成它时,你必须提供一组interface给它,则动态代理类就宣称它实现了这些interface。当然,动态代理类就充当一个代理,你不要企图它会帮你干实质性的工作,在生成它的实例时你必须提供一个handler,由它接管实际的工作。
使用动态代理实现AOP:Aspect orient Program 面向切片编程。
代理设计模式:使用一个代理将对象包装起来。
TestAOP.java(动态代理实例)
package blog10;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import org.junit.Test;
/**
* 动态代理应用
*/
public class TestAOP {
//动态代理:面向切面编程 (Aspect Orient Program)
//此时想为ArithClacImp中四个方法在调用时都添加前置和后置的一些日志。若没个方法都手动添加,则代码膨胀严重,且维护困难
//此时可以用一个模板,将所有的方法嵌套到这个模板中,任何一个方法被调用时都会调用这个模板
//此时就用到了动态代理:每个方法都需要添加的代码又叫做横切关注点:日志、验证、事务等等
//代理设计模式原理:使用一个代理将对象包装起来,然后用这个代理代替原来的对象,任何对原始对象的操作都通过代理
//代理对象决定是否及何时将方法调用转到原来的对象上
//实现:
@Test
public void testProxy(){
//原业务实现类
final ArithClac arithClac = new ArithClacImp();
//代理类
ArithClac proxy =
(ArithClac)Proxy.newProxyInstance(arithClac.getClass().getClassLoader(),
arithClac.getClass().getInterfaces(), //此句此处可以直接这样写:new Class[]{ArithClac.class}表示动态代理对象实现的接口和原对象实现的接口相同
new InvocationHandler(){
//通过Proxy代理建立一个代理的对象。传入三个参数
//1.这个代理对象(由动态代理根据原始对象处理创建的对象)的类加载器,通常与被代理的对象的加载器相同。因为不是new出来的对象,而是自己手动newProxyInstance创建的对象,所以需要制定加载器
//2.由动态代理产生的对象必须实现的所有接口(的类型),因为不唯一,可以实现多接口,所以是Class数组类型
//3.调用代理对象的时候,应该产生的行为:匿名内部类InvocationHandle接口的实现
public Object invoke(Object proxy,Method method,Object[] args)
//invoke三个参数:proxy:被代理的对象,必须是final修饰的对象
// Method:正在被调用的方法
// args:调用方法时被传入的参数
throws Throwable {
//调用方法时,添加前置日志:
System.out.println("the method " + method.getName() + " begins with : "+ Arrays.asList(args));
Object result = method.invoke(arithClac, args);
System.out.println("the method " + method.getName() + " ends with :" + result);
return result;
}
});
//测试:
int result = proxy.add(10, 2);
System.out.println(result);
result = proxy.sub(8, 4);
System.out.println(result);
//关键的两个语句:1). ArithClac proxy =
// (ArithClac)Proxy.newProxyInstance(arithClac.getClass().getClassLoader(),
// new Class[]{ArithClac.class},
// new InvocationHandler(){} );
//意为:ArithClac proxy创建一个动态代理对象(源对象声明为ArithClac类型的)
//newProxyInstance产生一个对象,该对象由arithClac.getClass().getClassLoader()加载,实现Class[]{ArithClac.class}这些接口
//且调用该动态代理产生的新的代理对象的方法时做出的动作:InvocationHandler(){} ,函数体写出具体实现
//2). public Object invoke(Object proxy,Method method,Object[] args)
//调用方法是的动作:proxy为该动态代理对象对应的原来的对象,method为调用的方法,args为方法的参数
//注:20行的final的语义:若无final,当testProxy方法调用完之后,有可能arithClac会被变成垃圾回收,而ArithClac proxy这个动态代理对象
//还需要继续使用,这个时候,调用proxy方法时就会在34行invoke方法中访问到不存在的arithClac,而出错,所以需要让proxy延长生命,使用final。
}
}
interface ArithClac {
int add(int i,int j);
int sub(int i,int j);
void mul(int i,int j);
void div(int i,int j);
}
//具体的实现类
class ArithClacImp implements ArithClac{
@Override
public int add(int i, int j) {
int result = i + j;
return result;
}
@Override
public int sub(int i, int j) {
int result = i-j;
return result;
}
@Override
public void mul(int i, int j) {
int result = i*j;
System.out.println("result:"+result);
}
@Override
public void div(int i, int j) {
int result = i/j;
System.out.println("result:"+result);
}
}