跳至主要內容

反射机制

wangdx大约 35 分钟

反射机制

Java 编程开发之所以会存在有反射机制,最为重要的原因是可以使 Java 编写代码更加的灵活,而这种灵活如果要想彻底的领悟,那么也需要通过大量的苦练才可以得到,当你已经可以熟练使用反射之后,那么就可以设计出更加结构性强,且可重用性更高的程序代码,在 Java 里面存在有很多的开发框架,而之所以可以提供大量开发框架,主要的原因也在于反射机制。

Java 的反射机制指的是通过“反方向”的操作来实现类的相关处理,那么既然要有“反”则一定会有“正”,按照传统的开发的行为模式来讲,如果要想进行一个类的操作,那么是需要根据类进行对象的实例化,随后再通过实例化对象实现类中方法的调用处理,于是现在给出如下代码。

详情
//传统范例
class Book{
    public void read(){
        System.out.println("认真学习李兴华老师出版的《Java从入门到项目实战》");
    }
}
class Test{
    public static void main(String[] args) {
        // 1,首先获取Book类的实例化对象
        // 通过实例化对象进行方法调用。
        new Book().read();
    }
}

以上的处理操作是一种正向的处理行为,但是如果是反向操作,则就意味着可以根据实例化对象获取相关的信息来源,在 Java 里面所有的类实际上都属于 Obiect 子类,那么在 Obiect 类中就提供有一个重要的方法,这个方法可以获取“反”的信息:

public final Class<?> getClass()

这个方法可以通过类的实例化对象进行调用,并且会返回有一个 Class 类的对象实例。

详情
class Test1{
    public static void main(String[] args) {
        Book book = new Book();// 1,首先获取Book类的实例化对象
        System.out.println(book.getClass().getName());  // 对象所属类的完整名称
        System.out.println(book.getClass().getSimpleName()); // 对象所属类的简化名称(不包含包名称)
        System.out.println(book.getClass().getTypeName()); // 获得对象所属类的类型
    }
}

通过以上的程序就发现,除了对象的正向处理操作之外,那么还可以通过 getCass0 方法来获取一个类所对应的完整的信息的结构,而这就是反射的开始,严格来讲所有的反射之中最为核心的话题就是 Class。

Class 类对象实例化

在整个的反射处理机制之中,Class 类是整个反射处理操作的源头所在,如果现在可以获取 Clas 类的对象,那么就可以进行所有的更加深层次的反射操作(之前仅仅是根据实例化对象的 Class 获取了类的基本名称),在 Java 的处理机制之中,实际上会有如下的三种方式可以获取 Class 类的实例化对象。

  • 方式一:由于在 Obiect 类中提供有 getClass() 方法,所以任意的实例化对象都可以通过此方法来获取 Class 类的对象实例
详情
class Test2{
    public static void main(String[] args) {
        Book book = new Book();
        printClass(book.getClass());
        printClass(new java.util.Date().getClass());
    }
    public static void printClass(Class<?> clazz){
        System.out.println("【当前操作的类型】" + clazz.getName());
    }
}
  • 方式二:在 Java 处理过程之中,可以直接使用“类名称.class”的形式直接在没有产生类实例化对象的时候获取 Class 类的实例。
详情
interface IBook {
}

class Test3 {
    public static void main(String[] args) {
        Class<?> clazzA = IBook.class; //  属于Java的原生语法支持
        Class<?> clazzB = java.util.Date.class; //  属于Java的原生语法支持
        System.out.println(clazzA);
        System.out.println(clazzB);
    }
}

此时是直接进行了 Class 类对象的实例输出,所以这个时候的信息输出会直接通过 toString() 方法来获取相关的对象完整信息。

  • 方式三:在 Class 类的内部提供有一个根据“类名称”字符串实现类反射加载的处理方法:
  public static Class<?> forName(String className) throws ClassNotFoundException

在之前获取 Class 类对象的情况下都必须获取类本身对应的程序包,但是如果使用了“Class.forNameO”方法进行 Class 类对象实例化获取的时候,就可以直接将类名称以字符串的形式定义在程序之中。

详情
class Test4 {
    public static void main(String[] args) throws ClassNotFoundException {
        Class<?> clazz = Class.forName("java.util.Date") ; // 根据类名称实现信息加载
        System.out.println(clazz);
    }
}

这个时候就通过字符串实现了类的加载,但是需要明确的是,以上的这几点处理语法在整个项目的实际开发过程之中全部都有可能使用到,不可能做一个优先级区分。

反射实例化对象

现在通过了一系列的分析已经可以得到三种实例化 Clas 类对象的方法,但是不理解的是,为什么我们要分析这三种方式,或者是为什么要获取 Class 类的实例化对象呢?

在 Java 之中如果要想产生一个类的实例化对象,那么一点你给要通过关键字 new 进行构造方法的调用,随后再通过该对象进行具体的类的结构操作,可是除了这种关键字 new 之外,如果此时己经获得了 Class 类的对象实例,那么就可以通过 Class 类的如下方法来实现类对象的实例化处理:

@Deprecated(since="g")
public T newInstance() throws InstantiationException, IllegalAccessException

之所以在 newInstance()方法上面追加有一个“@Deprecated”不是说这个方法有问题,而是说这个方法描述的含义不清晰,但是在 JDK1.8 及以前的版本肯定还是使用 newInstance()方法完成,于是在 JDK1.9 之后推荐的做法变更为:。

clazz.getDeclaredConstructor().newInstance()
详情
class Book{
    public Book() {
        System.out.println("【Book】实例化新的Book类对象");
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> bookClazz = Class.forName("com.yix.reflect.w02.Book") ; // 实例化Book类的对象
        bookClazz.newInstance() ; // 在JDK 1.8的时候依然可以正常使用
    }
}

通过以上两个代码的对比可以发现,使用关键字 new 或者使用反射机制中提供的 newInstance() 两个方法都可以实现指定类对象实例化处理,这样就意味着从此之后可以不局限于关键字“new”的使用了,但是在 JDK1.9 之后传统所使用的 newinstance()方法已经变为了一个不推荐使用的方法了,所以以上的代码可以进行如下的变更。

详情
class Test1 {
    public static void main(String[] args) throws Exception {
        Class<?> bookClazz = Class.forName("com.yix.reflect.w02.Book") ; // 实例化Book类的对象
        bookClazz.getDeclaredConstructor().newInstance() ;
    }
}

在 JDK1.9 之后如果要想通过 Cass 类对象获取其他类的实例,那么就需要进行方法的更换,但是有另外一点需要注意的是,当通过 Class 类对象获取指定类实例的时候,newInstance()方法所返回的数据类型为 Obiect,那么这个时候就需要进行一些对象的向下转型处理(对象的向下会存在有安全隐患)。

详情
class Test2 {
    public static void main(String[] args) throws Exception {
        Class<?> bookClazz = Class.forName("com.yix.reflect.w02.Book");
        // newInstance()返回的是一个Object类型,那么就必须进行对象的强制向下转型
        Book book = (Book) bookClazz.getDeclaredConstructor().newInstance();
        book.read();
    }
}

但是需要注意的是,虽然以上的操作可以通过向下转型获取指定类型的对象实例,但是这种操作的代码是存在有设计上问题的,之所以使用反射很大的程度上是不希望进行完整类信息的导入,但是如果仅仅是按照如上的方式进行处理,那么如果真的有些其他包的类,则依然会出现导入包的情况。

范例:观察反射实例化对象强制转型所带来的问题

详情
class Test3 {
    public static void main(String[] args) throws Exception {
        Class<?> bookClazz = Class.forName("java.util.Date") ;
        // 如果强制转型了,那么就必须进行开发包的完整导入
        Date date = (Date) bookClazz.getDeclaredConstructor().newInstance() ;
        System.out.println(date.getTime());
    }
}

反射与工厂设计模式

经过了分析之后就发现通过反射可以获得类的实例化对象,但是现在就需要去思考为什么要提供反射的机制来获取实例化对象,或者说如果直接使用关键字 new 有什么问题吗?如果要想回答这个问题最佳的做法是通过工厂设计模式来进行分析。

详情
//传统工厂模式
public interface IBook {
    public void read();
}

class MathBook implements IBook {
    public void read() {
        System.out.println("【MathBook】认真学习大学数学课程(线性代数、概率统计、离散数学)。");
    }
}
class ProgramBook implements IBook {
    @Override
    public void read() {
        System.out.println("【ProgramBook】认真学习李兴华老师出版的《Java从入门到项目实战》。");
    }
}
class Factory {
    private Factory() {}
    public static IBook getInstance(String className) {
        if ("math".equalsIgnoreCase(className)) {
            return new MathBook() ;
        } else if ("program".equalsIgnoreCase(className)) {
            return new ProgramBook() ;
        } else {
            return null ;
        }
    }
}
class Test{
    public static void main(String[] args) {
        IBook book = Factory.getInstance("program") ;
        book.read();
    }
}

但是如果说此时 IBook 接口里面会提供有 10W 个子类呢?那么请问,这个时候的 Factory 类还能写吗?如果你有耐心会在 Factory.getnstance() 方法里面加入 10W 个判断,如果真的加入了 10W 个判断,那么程序的执行时间复杂度一定会飙高。所以这种传统的静态工厂类是不可能满足于现实的项目开发要求的,最佳的做法要采用动态工厂类,反射机制就可以登场了。在使用反射操作的时候只需要根据字符串(类名称)获取 Cass 类的实例化对象之后就可以直接反射实例化对象处理,这样的操作最适合于完成工厂设计的改良。

详情
class Factory1 {
    private Factory1() {
    }

    public static IBook getInstance(String className) {
        try {
            Object obj = Class.forName(className).getDeclaredConstructor().newInstance();
            if (obj instanceof IBook) {
                return (IBook) obj;
            }
            return null;
        } catch (Exception e) {
            return null;
        }
    }
}
class Test1{
    public static void main(String[] args) {
        IBook book = Factory1.getInstance("com.yix.reflect.w03.ProgramBook");
        book.read();
    }
}

这个时候的工厂类完全变为了一种独立的操作模式,不管你的项目中 Book 接口到底会产生多少种子类,那么对于整个的工厂类来讲都没有任何的区别,只要给出类的完整名称,并且该类属于 Book 接口的子类,就都可以动态实例化。

反射与单例设计模式

单例设计模式也是在 Java 开发中比较常见的一种设计模型,这种设计模型的最重要的特点在于:在一个 JVM 进程之中某一个类只允许提供有唯一的一个实例化对象,同时单例设计模式也分为两种:饿汉式、懒汉式,在面试中最为常见的问题就是懒汉式的单例设计模式、因为这里面与多线程自自相关

详情
public class Singleton {
    private static Singleton instance;

    private Singleton() {
        System.out.println("【" + Thread.currentThread().getName() + "】实例化Singleton");
    }

    public String toString() {
        return "【VIP】李兴华编程训练营:yootk.ke.q9.com";
    }

    public static Singleton getInstance() {
        if (instance == null) {
            instance = new Singleton();
        }
        return instance;
    }
}

class Test {
    public static void main(String[] args) {
        for (int x = 0; x < 10; x++) {
            Singleton singleton1 = Singleton.getInstance();
            System.out.println(singleton1);
        }
    }
}

class Test1 {
    public static void main(String[] args) {
        for (int x = 0; x < 10; x++) {
            new Thread(() -> {
                Singleton singleton1 = Singleton.getInstance();
                System.out.println(singleton1);
            }, "单例操作线程-" + x).start();

        }
    }
}

按照最初的设计来讲,此时采用的应该属于单例设计模式,所以 Singieton 类的对象实例应该始终只保持一个,可是最终执行后的结果会发现构造方法被重复调用了多次,每当调用一次构造方法就意味产生了一个新的 Singleton 类的实例化对象。

既然此时出现有线程不同步的问题,那么所需要解决的方式一定是围绕着线程的同步,那么一旦要想采用同步的方式进行处理,传统的风格应该在方法中加入同步操作。

正确代码形式

详情
class Singleton1 {
    private volatile static Singleton1 instance;

    private Singleton1() {
        System.out.println("【" + Thread.currentThread().getName() + "】实例化Singleton");
    }

    public String toString() {
        return "【VIP】李兴华编程训练营:yootk.ke.q9.com";
    }

    public static Singleton1 getInstance() {
        if (instance == null) {
            synchronized (Singleton.class) {
                if (instance == null) {
                    instance = new Singleton1();
                }
            }
        }
        return instance;
    }
}

class Test2 {
    public static void main(String[] args) {
        for (int x = 0; x < 10; x++) {
            new Thread(() -> {
                Singleton1 singleton1 = Singleton1.getInstance();
                System.out.println(singleton1);
            }, "单例操作线程-" + x).start();

        }
    }
}

反射与类结构操作

反射机制除了可以通过 Class 类的方式获取一个类的实例化对象之外,其最大的特点还可以实现整个类结构的剖析,例如:某一个类对应的父类、它所实现的父接口、类中的构造方法、成员属性或者普通方法等等。

获取类结构信息

详情
interface IBook {
}

interface ISpec {
}

abstract class AbstractPrint {
}

class Book extends AbstractPrint implements IBook, ISpec {
}

public class Test {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.yix.reflect.w05.Book");
        System.out.println(clazz.getPackage()); // 返回Package对象实例
        System.out.println(clazz.getPackageName());
        Class<?> superClazz = clazz.getSuperclass() ;  // 获取父类
        System.out.println("【继承父类】" + superClazz.getName());
        System.out.println("【继承父类】" + superClazz.getSuperclass().getName());
        Class<?> inters [] = clazz.getInterfaces() ; // 获取实现的接口
        for (Class<?> temp : inters) {
            System.out.println("【实现接口】" + temp.getName());
        }
    }
}

package com.yix.reflect.w05
com.yix.reflect.w05
【继承父类】com.yix.reflect.w05.AbstractPrint
【继承父类】java.lang.Object
【实现接口】com.yix.reflect.w05.IBook
【实现接口】com.yix.reflect.w05.ISpec

反射调用构造方法

在一个类中会存在有若干个构造方法的信息,那么这样就在 Cass 类里面可以基于反射机制来获取一个类中全部已经存在的构造方法,具体的操作方法如下:

详情
class Book {    // Book类中的构造方法使用了不同的访问权限
    public Book() {}
    protected Book(String title) {}
    Book(String title, String author) {}
    private Book(String title, String author, double price) {}
}
public class Test {
    public static void main(String[] args) throws Exception{
        Class<?> clazz = Class.forName("com.yix.reflect.w06.Book") ;
        {
            System.out.println("--------------------- getConstructors()获取构造 ---------------------");
            for (Constructor<?> constructor : clazz.getConstructors()) {
                System.out.println("【1 - 构造信息】" + constructor);
            }
        }
        {
            System.out.println("--------------------- getDeclaredConstructors()获取构造 ---------------------");
            for (Constructor<?> constructor : clazz.getDeclaredConstructors()) {
                System.out.println("【2 - 构造信息】" + constructor);
            }
        }
    }
}

详情

class Book {    // Book类中的构造方法使用了不同的访问权限
    private String title;
    private String author;
    private double price;
    public Book(String title, String author, double price) {
        this.title = title;
        this.author = author;
        this.price = price;
    }// 本类中的无参构造、setter、getter方法,略...
    public String toString() {
        return "【Book】图书名称:" + this.title + "、图书作者:" + this.author + "、图书价格:" + this.price ;
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.yootk.demo.Book");
        Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, String.class, double.class);// 获取指定构造
        Object obj = constructor.newInstance("Java从入门到项目实战", "李兴华", 99.8) ; // 反射对象实例化
        System.out.println(obj);
    }
}

反射调用方法

在一个类中除了构造之外还会存在有许多类中提供的方法,那么在这种情况下,所有的方法信息也是可以通过 Class 类的对象反射获取的,使用如下的方法获取即可 :

详情
class Book {
    public void read() throws RuntimeException {}
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.yootk.demo.Book");
        Method methods [] = clazz.getMethods() ;
        for (Method method : methods) {
            System.out.println(method);
        }
    }
}

在 Method 类中提供有如下的几个常用方法:

在程序设计之中,所有方法前所追加的修饰符(public、private、stat1c、synchronized、abstract 等)在程序里面都会有相应的权限编码存在。如果要想将这些编码转换为可以读懂的信息需要通过 java.lang.reflect.Modifier 完成。

详情
class Book {
    public void read() throws RuntimeException {}
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.yootk.demo.Book");
        Method methods [] = clazz.getMethods() ;
        for (Method method : methods) {
            System.out.print(Modifier.toString(method.getModifiers()) + " "); // 获取方法修饰符
            System.out.print(method.getGenericReturnType().getTypeName() + " "); // 获取返回值类型
            System.out.print(method.getName() + "("); // 获取方法名称
            Type[] parameterTypes = method.getGenericParameterTypes();// 获取方法参数信息
            for (int x = 0 ; x < parameterTypes.length ; x ++) {
                System.out.print(parameterTypes[x].getTypeName() + " arg" + x);
                if (x < parameterTypes.length - 1) {    // 后面还有其他参数
                    System.out.print(", ");
                }
            }
            System.out.print(") ");
            Type[] exceptions = method.getGenericExceptionTypes() ; // 获取所抛出的全部异常信息
            if (exceptions.length > 0) {    // 有异常抛出
                System.out.print("throws "); // 输出throws信息
                for (int x = 0; x < exceptions.length; x++) {
                    System.out.print(exceptions[x].getTypeName());
                    if (x < exceptions.length - 1) {
                        System.out.print(", ");
                    }
                }
            }
            System.out.println(); // 换行
        }
    }
}

在实际项目的开发过程之中,使用 Method 类的对象最大的用途并不是进行方法结构的剖析 万法缺陷就是无法获得(Method 参数具体名称定义),而最大的用途在于可以实现方法的反射调用。

范例:反射方法调用。

详情
class Book {
    private String title ;
    public void setTitle(String title) {
        this.title = title;
    }
    public String getTitle() {
        return title;
    }
}

public class Test {
    public static void main(String[] args) throws Exception{
        String fieldName = "title" ; // 明确给出了成员的属性名称
        String fieldValue = "Java从入门到项目实战" ; // 成员属性的内容
        Class<?> clazz = Class.forName("com.yix.reflect.w07.Book");
        // 1、如果要想通过Book类实现属性的操作,那么首先一定要获取Book类的对象
        Object object = clazz.getDeclaredConstructor().newInstance() ; // 反射对象实例化
        // 2、要找到指定调用的setter,setTitle()方法的参数类型为String
        Method setMethod = clazz.getMethod("set" + initcap(fieldName), String.class) ;
        // 3、利用反射结合对象实例(不是具体的Book类,而是Object类型)同时传入所需要的参数
        setMethod.invoke(object, fieldValue) ; // 等价于“book类的对象.setTitle(fieldValue)”
        // 4、找到getter方法,getter方法没有参数类型
        Method getMethod = clazz.getMethod("get" + initcap(fieldName)) ;
        // 5、通过反射获取getter方法的返回值
        Object value = getMethod.invoke(object) ;// 等价于“book类对象.getTitle()”
        System.out.println(value);
    }
    public static String initcap(String str) {
        if (str == null || "".equals(str)) {
            return str ;
        }
        if (str.length() == 1) {
            return str.toUpperCase() ;
        } else {
            return str.substring(0, 1).toUpperCase() + str.substring(1) ;
        }
    }
}

之所以使用如上的形式代替掉传统的关键字 new 以及明确的“对象.方法 ()”调用形式,本质上来讲就是为了进行解耦和设计

反射调用成员属性

类中除了提供有构造还有方法之外,最为重要的概念就是属性,因为在不同的对象里面所保存的内容就属于属性的信息,属性严格来讲在 Java 中称为成员,所以如果要想获得所有成员的信息,就需要通过 Class 类的对象来完成。

在实际项目开发过程之中,如果使用反射进行处理的时候,一般来讲都会采用“getDeclaredFields()、getDeclaredfield()”方式来获取本类的操作属性(即便属性使用了 private 封装也可以返回),所有的成员在 Java 中都使用 Field 类型来进行描述,来观察 Field 类中的相关方法。

详情
interface IBook {
    public static final String FLAG = "沐言优拓:www.yootk.com";
}

abstract class AbstractBook implements IBook {
    protected String type = "编程教育类图书";
    private String company = "沐言科技软件学院";
}

class ProgramBook extends AbstractBook {
    private String title; // 图书名称
}

class Test1 {
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.yix.reflect.w07.ProgramBook"); // 获取操作类
        {
            System.out.println("----------- getFields()执行结果 ------------");
            for (Field field : clazz.getFields()) { // 获取所有的成员
                System.out.println("【继承来的public成员】" + field);
            }
        }
        {
            System.out.println("----------- getDeclaredFields()执行结果 ------------");
            for (Field field : clazz.getDeclaredFields()) { // 获取所有的成员
                System.out.println("【本类定义的成员】" + field);
            }
        }
    }
}
class Book1 {
    private String title ; // 图书名称
}
class Test2{
    public static void main(String[] args) throws Exception {
        Class<?> clazz = Class.forName("com.yix.reflect.w07.Book1") ; // 获取操作类
        Object object = clazz.getDeclaredConstructor().newInstance() ; // 实例化对象
        Field titleFiled = clazz.getDeclaredField("title") ; // 获取指定属性的Field对象
        titleFiled.setAccessible(true); // 取消封装处理
        titleFiled.set(object, "Java从入门到项目实战"); // 等价于“对象.title = "Java从入门到项目实战"”
        System.out.println(titleFiled.get(object)); // 等价于“对象.title”
    }
}

Unsafe 工具类

java.lang.reflect 本身所描述的是一种反射的基本操作功能,除了这个基本的功能之外,在 JDK 里面还提供有一个比较特殊的反射类:sun.misc.Unsafe(按照 Java 开发的原则来讲,所有以“sun”开头的包一般都不建议调用,因为这些包都会与操作系统的底层有关,可以直接通过 C++代码进行操作),其中 Unsafe 类可以实现在没有实例化对象的情况下进行类中方法的调用。

详情
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe") ; // 通过反射获取成员
        unsafeField.setAccessible(true); // 取消封装
        Unsafe unsafe = (Unsafe) unsafeField.get(null) ; // 获取UnSafe对象
        System.out.println(unsafe);
    }
}

获取 Unsafe 类的对象实例最为重要的目的是可以绕过 JVM 的管理机制来实现一些类的调用处理,例如:传统的开发过程之中,只要调用类中的普通方法就必须有实例化对象存在,但是如果使用了 UnSaf 类,这个机制就可以被打破。

详情
class Singleton {
    private Singleton() {
        System.out.println("【Singleton】实例化类对象");
    }
    public void print() {
        System.out.println("李兴华编程训练营:yootk.ke.qq.com");
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe") ; // 通过反射获取成员
        unsafeField.setAccessible(true); // 取消封装
        Unsafe unsafe = (Unsafe) unsafeField.get(null) ; // 获取UnSafe对象
        Singleton singleton = (Singleton) unsafe.allocateInstance(Singleton.class) ; // 获取对象实例
        singleton.print();
    }
}

反射机制与代理设计模式

详情
interface ISleep {  // 核心业务主题
    public void make() ; // 睡觉之前做一些运动
}
class SleepReal implements ISleep {
    @Override
    public void make() {
        System.out.println("【真实业务】和我的爱妃一起XXOO后安静的一个人睡觉。");
    }
}
class SleepProxy implements ISleep {
    private ISleep sleep ; // 要设置被代理者
    public SleepProxy(ISleep sleep) {
        this.sleep = sleep ;
    }
    public void note() {
        System.err.println("【代理业务】向翻牌子的贵妃宣旨。");
    }
    public void give() {
        System.err.println("【代理业务】需要进行提前的香汤沐浴。");
    }
    @Override
    public void make() {
        this.note();
        this.give();
        this.sleep.make(); // 调用真实业务
        this.move();
    }
    public void move() {
        System.err.println("【代理业务】不要让皇上过度放纵,于是赶紧把娘娘送回各自的公府。");
    }
}
class Test{
    public static void main(String[] args) {
        ISleep sleep = new SleepProxy(new SleepReal()) ; // 实现接口的实例化
        sleep.make();
    }
}

使用代理设计模式最大的优势就在于,整个的程序项目设计的过程里面可以避免大量辅助性的操作业务与真实的核心业务产生偶合性的问题,但是如果所有的开发操作都采用如上的模式就会非常的麻烦,例如:假设说现在要实现一个网络数据交换的操作那么这种交互的操作机制可以适用于所有的网络程序。

这个时候所采用的代理设计模式会存在有大量的重复性的代理类出现,而这样的设计明显不是应该在项目中采用的,最佳的做法是,定义公共代理类,这样某一类的功能都可以通过一个代理类来解决。

  • 例如:如果要吃饭,则使用饭店代理类来为所有要吃饭的人服务:
  • 例如:如果要看电影,则使用所有电影院代理类为所有用户服务。。

动态代理设计模式

如果要想解决静态代理设计模式之中所存在的代码重复性的问题,最佳的做法就是采用动态代理设计模式来进行处理,而所谓的动态代理指的是可以为某一类相同功能的真实主题类实现服务,动态代理设计模式是在 JDK1.3 的时候正式引入到 Java 体系结构之中的一种技术实现,如果要想实现这样的动态代理类,那么就必须掌握“Invocation 接口”、“Proxy 类”。.

  • 首先来观察一下 InvocationHandler 接口的定义
  public interface InvocationHandlerf
  public Object invoke(0bject proxy, Method method, 0bject[] args) throws Throwable;

在 invoket()方法中提供有三个重要的参数信息,那么这三个参数信息的含义如下:

  • “Object proxy”:描述代理类的对象实例,这个实例的传递由动态代理类自行完成:
  • “Method method”:描述的是要调用的真实业务的处理方法;
  • “0bject[l args”:描述的是调用真实业务处理方法所需要的参数:

使用 InvocationHandler 最为重要的目的是创建公共代理类,所以 InvocationHandler 是 JDK 系统所支持的动态代理实现的执行接口标准,接口标准下一定需要提供有一个具体的实现类,实现所有的辅助性业务。

  • 动态代理与原始静态代理最大的区别在于,其所有的代理操作程序类是由系统负责生成的。

如果要想生成这样的动态接口代理子类,就必须使用 Proxy 类来完成,本类采用如下的方法实现动态接口字类的创建:

详情
interface IMessage { // 核心业务接口
    public String echo(String msg) ;
}
class MessageImpl implements IMessage {
    @Override
    public String echo(String msg) {
        return "【ECHO】" + msg;
    }
}
class ServerProxy implements InvocationHandler {   // 此为公共的代理实现类
    private Object target ; // 核心业务对象
    /**
     * 由于动态代理设计模式需要保存有真实业务的主题对象,那么这个时候就需要将真实业务对象传递到本方法之中
     * 同时基于Proxy系统类,动态创建有一个代理业务类对象
     * @param target 要绑定的真实业务对象
     * @return 系统生成的代理类对象
     */
    public Object bind(Object target) { // 保存真实业务对象
        this.target = target ;
        // 获取真实业务主题所在类对应的类加载器,因为需要分析真实业务主题接口所拥有的方法,才可以构建动态子类
        // 所有的动态代理都是基于接口的设计应用,那么此时就要获取全部的接口信息
        // 当前的类为InvocationHandler接口子类,所以使用this描述的是本接口的实例化对象
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this) ;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 此时的代理方法中需要进行方法的反射调用,反射调用需要提供有实例化对象、Method对象、参数
        Object returnValue = null ;
        if (this.connect()) {
            returnValue = method.invoke(this.target, args); // 调用真实业务主题
            this.close();
        }
        return returnValue;
    }
    public boolean connect() { // 代理方法
        System.out.println("【代理业务】连接远程服务器,建立消息发送通道。");
        return true ;
    }
    public void close() {
        System.out.println("【代理业务】信息发送完毕,关闭远程消息通道。");
    }
}

public class Test {
    public static void main(String[] args) {
        IMessage messageObject = (IMessage) new ServerProxy().bind(new MessageImpl()) ; // 获取代理对象
        System.out.println(messageObject.echo("沐言优拓:www.yootk.com"));
    }
}

此时实现了基本的代理设计模式,那么对于当前的程序来讲,由于给出的动态代理可以为所有的接口服务,那么所有与之相关的功能的代理操作也就可以保持一致,

CGLIB 实现动态代理

在之前讲解的程序里面通过了 JDK 内部所提供的 Proxy 类实现了动态代理设计,但是在传统的 JDK 的支持里面,所有的动态代理设计是基于接口的应用。

Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getclass().getInterfaces(),this);

但是面对于这样的强制性的要求,很多的开发者就不怎么高兴了,在这样的背景下就由许多的技术爱好者们根据自己的需求设计了新的代理结构(这个结构不属于 JDK 原生结构),基于 CGLB 的开发包实现动态代理机制,避免强制性的接口实现要求。可以基于类的方式实现动态代理设计。

如果要想使用 CGLIB 的开发包,那么肯定要通过网络进行第三方组件包的下载。

  • 1、如果要下载开发包可以通过 Maven 仓库下载:Maven 仓库open in new window,直接搜索“cglib”即可 CGLib Nodep ;

  • 2、 如果要想在 IDEA 中进行本地的 jar 文件的管理,那么一般来讲都会创建一个保存目录“H:workspacelocal-ib”,所以打开 DEA 的模块配置界面。.

详情
class Message { // 不需要使用到接口
    public String echo(String msg) {
        return "【ECHO】" + msg;
    }
}

class ServerProxy implements MethodInterceptor {  // 【CGLIB】方法拦截器
    private Object target; // 真实业务主题对象

    public ServerProxy(Object target) {
        this.target = target;
    }

    public boolean connect() { // 代理方法
        System.out.println("【代理业务】连接远程服务器,建立消息发送通道。");
        return true;
    }

    public void close() {
        System.out.println("【代理业务】信息发送完毕,关闭远程消息通道。");
    }

    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        Object returnValue = null; // 返回值
        if (this.connect()) {
            returnValue = method.invoke(this.target, objects); // 反射调用方法
            this.close(); // 关闭服务器连接
        }
        return returnValue;
    }
}

public class Test {
    public static void main(String[] args) {
        Message target = new Message() ; // 核心业务对象
        Enhancer enhancer = new Enhancer();// 代理控制类
        enhancer.setSuperclass(target.getClass()); // 模拟一个公共父类
        enhancer.setCallback(new ServerProxy(target)); // 配置代理功能
        Message proxy = (Message) enhancer.create() ; // 创建代理对象
        System.out.println(proxy.echo("沐言优拓:www.yootk.com"));
    }
}

反射机制与 Annotation

在 JDK 1.5 之后实际上最为重要的一项技术(改变整个开发行业结构的一项技术)就属于 Annotation 注解了,在 Java 里面对于注解的开发和使用都是基于反射机制的处理操作。

反射获取 Annotation 信息

在 Java 里面反射机制可以实现类结构、方法结构以及属性结构的反射处理,而对于 Annotation 来讲,也可以在类、方法上进行定义,所以所有的 Annotation 的信息都是可以基于反射获取的。

Class 类实现了一个 java.lang,reflect.AnnotatedElement 接口,而在这个接口里面定义有大量的与 Annotation 获取有关的方法,这些方法的定义如下。

详情
@FunctionalInterface // 函数式接口定义
@Deprecated(since = "1.1") // 此接口不推荐使用
interface IMessage {    // 定义一个接口
    public String echo(String msg) ;
}
@Deprecated(since = "3.0")
class MessageImpl implements IMessage {
    @Override
    public String echo(String msg) {
        return "【ECHO】" + msg ;
    }
}
public class Test {
    public static void main(String[] args) throws Exception  {
        {
            System.out.println("---------- 获取IMessage接口上的注解信息 ----------");
            Class<?> clazz = IMessage.class ; // 获取接口的Class对象实例
            for (Annotation annotation : clazz.getAnnotations()) {
                System.out.println(annotation);
            }
        }
        {
            System.out.println("---------- 获取MesageImpl子类上的注解信息 ----------");
            Class<?> clazz = MessageImpl.class ; // 获取接口的Class对象实例
            for (Annotation annotation : clazz.getAnnotations()) {
                System.out.println(annotation);
            }
        }
        {
            System.out.println("---------- 获取MesageImpl.echo()方法上的注解信息 ----------");
            Class<?> clazz = MessageImpl.class ; // 获取接口的Class对象实例
            Method method = clazz.getDeclaredMethod("echo", String.class) ;
            for (Annotation annotation : method.getAnnotations()) {
                System.out.println(annotation);
            }
        }
    }
}

自定义 Annotation

在整个的 Java 里面不是有很多的 Annotation 可以使用吗?为什么现在又需要自定义 Annotation 呢?Annotation 的出现彻底改变了整个程序开发的设计结构,如果你现在要想编写一些灵活性更高的程序设计方案,一定是离不开 Annotation。

详情
@Retention(RetentionPolicy.RUNTIME) // 在当前执行的时候此Annotation生效
@interface Action { // 自定义了一个Annotation
    public String title() ; // 描述的是Annotation中的属性
    public String value() ; // value是一个重要的标记
}
@Action(title = "沐言优拓:www.yootk.com", value = "李兴华编程训练营:yootk.ke.qq.com")
class Message {
    public String echo(String msg) {
        return "【ECHO】" + msg ;
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        {
            System.out.println("---------- 获取IMessage接口上的注解信息 ----------");
            Class<?> clazz = Message.class ; // 获取接口的Class对象实例
            for (Annotation annotation : clazz.getAnnotations()) {
                System.out.println(annotation);
            }
        }
    }
}

所有的 Annotation 都有其运行的范围,例如:这个 Annotation 是在源代码编译的时候生效,还是在程序运行的时候生效,如果要想进行范围的定义,那么就需要在整个的程序之中通过“@Rentetion”注解来进行配置,其有三个配置项(具体的运行范围是由“RetentionPolicy”枚举来进行定义的):

  • SOURCE:在源代码的编写过程中生效;
  • CLASS:在类定义的时候生效;
  • RUNTIME:在类执行的时候生效。

对于此时的自定义的“@Action”的注解里面定义有两个变量信息(title、value),所以在使用的时候必须明确的设置这两个 变量的内容,如果不设置,则认为此 Annotation 是错误的引用:

@Action(title="沐言优拓:www.yootk.com",value ="李兴华编程训练营:yootk.ke.99.com")

如果自定义的 Annotation 里面有某些变量内容在没有用户设置的时候希望可以自动使用默认值填充,那么就通过 default 配置

@Retention(RetentionPolicy.RUNTIME) // 在当前执行的时候此Annotation生效
@interface Action { // 自定义了一个Annotation
    public String title() default "沐言优拓:www.yootk.com" ; // 描述的是Annotation中的属性
    public String value() ; // value是一个重要的标记
}

3、【特别重要】如果在自定义 Annotation 的时候发现有一个属性的名称为“value”,则这个属性的内容的定义可以直接编写,而不需要通过变量名称来进行定义。

@Action("李兴华编程训练营:yootk.ke.qq.com")
class Message {
    @Action("李兴华编程训练营:yootk.ke.qq.com")
    public String echo(String msg) {
        return "【ECHO】" + msg ;
    }
}

4、现在所定义的 Annotation 实际上是可以定义在类中的各个结构上的,这就包括了类、方法、成员属性,但是在很多的情况下,可能只希望某些 Annotation 可以只出现在部分的结构上,例如:只能够在类上使用,或者只能够在方法上使用,则此时就可以通过“@Target”注解来进行配置,这个注解里面通过“ElementType”枚举可以实现定义位置上的限定:

  • ANNOTATION TYPE:该注解只能够出现在注解定义的操作语法上;。
  • METHOD:该注解只能够出现在方法上;
  • FIELD:该注解只能出现在成员属性上:
  • TYPE:该注解只能出现在类结构上;
  • PARAMETER:该注解只能够出现在参数上。
详情
@Retention(RetentionPolicy.RUNTIME) // 在当前执行的时候此Annotation生效
@interface Action { // 自定义了一个Annotation
    public String title() default "沐言优拓:www.yootk.com" ; // 描述的是Annotation中的属性
    public String value() ; // value是一个重要的标记
}
@Action("李兴华编程训练营:yootk.ke.qq.com")
class Message {
    @Action("李兴华编程训练营:yootk.ke.qq.com")
    private String message ;
    @Action("李兴华编程训练营:yootk.ke.qq.com")
    public String echo(String msg) {
        return "【ECHO】" + msg ;
    }
}
public class Test {
    public static void main(String[] args) throws Exception {
        {
            Class<?> clazz = Message.class ; // 获取接口的Class对象实例
            Field field = clazz.getDeclaredField("message");
            for (Annotation annotation : field.getAnnotations()) {
                System.out.println(annotation);
            }
        }
    }
}
@Target({ElementType.TYPE, ElementType.METHOD}) // 这个注解可以出现在类和普通方法定义上
@Retention(RetentionPolicy.RUNTIME) // 在当前执行的时候此Annotation生效
@interface Action { // 自定义了一个Annotation
    public String title() default "沐言优拓:www.yootk.com" ; // 描述的是Annotation中的属性
    public String value() ; // value是一个重要的标记
}
@Action("李兴华编程训练营:yootk.ke.qq.com")
class Message {
    @Action("李兴华编程训练营:yootk.ke.qq.com")
    public String echo(String msg) {
        return "【ECHO】" + msg ;
    }
}
public class YootkDemo {    // 李兴华编程训练营:yootk.ke.qq.com
    public static void main(String[] args) throws Exception {
        {
            Class<?> clazz = Message.class ; // 获取接口的Class对象实例
            Field field = clazz.getDeclaredField("message");
            for (Annotation annotation : field.getAnnotations()) {
                System.out.println(annotation);
            }
        }
    }
}

Annotation 与工厂设计模式

现在为止 Annotation 可以有很多种用法,但是如果从实际的开发来讲,结合工厂设计模式是对于 Annotation 一种比较良好的应用环境,因为只要有字符串,那么就可以实现有类的反射加载,现在假设说要想实现一个消息发送的程序,在消息发送的时候一定要创建有消息发送的通道,这个通道可能使用的是网络通道,或者使用的是无线电波通道。。

详情
@Target({ElementType.TYPE}) // 此注解可以应用在类定义上
@Retention(RetentionPolicy.RUNTIME) // 在当前执行的时候此Annotation生效
@interface Action { // 自定义了一个Annotation
    public String value(); // value可以避免编写变量名称
}

@Target({ElementType.CONSTRUCTOR}) // 此注解可以应用在类定义上
@Retention(RetentionPolicy.RUNTIME) // 在当前执行的时候此Annotation生效
@interface Instance { // 实现实例化对象控制的类型
    public String value(); // value可以避免编写变量名称
}

interface IChannel extends AutoCloseable {
    public boolean build(); // 建立通道
}

class InternetChannel implements IChannel {
    @Override
    public boolean build() {
        System.out.println("【InternetChannel】建立互联网通讯通道。");
        return true;
    }

    @Override
    public void close() throws Exception {
        System.out.println("【InternetChannel】关闭互联网通讯通道。");
    }
}

class RadioChannel implements IChannel {
    @Override
    public boolean build() {
        System.out.println("【RadioChannel】建立无线电通讯通道。");
        return true;
    }

    @Override
    public void close() throws Exception {
        System.out.println("【RadioChannel】关闭无线电通讯通道。");
    }
}

class Factory { // 编写工厂类
    private Factory() {
    }

    public static <T> T getInstance(String className) {
        try {   // 实现一个泛型工厂类
            return (T) Class.forName(className).getDeclaredConstructor().newInstance();
        } catch (Exception e) {
            return null;
        }
    }
}
@Action("com.yix.reflect.w14.RadioChannel") // 通过注解配置了当前Message类所使用的通道信息
class Message { // 进行消息的发送处理
    private IChannel channel ; // 如果需要发送消息就必须提供通道
    @Instance("com.yix.reflect.w14.Factory")
    public Message() {  // 构造方法
        try {// 获取指定的注解配置的信息
            Action actionAnnotation = super.getClass().getAnnotation(Action.class) ; // 获取类上的Annotation
            Instance instanceAnnotation = super.getClass().getConstructor().getAnnotation(Instance.class); // 获取构造方法上的指定构造
            String factoryClassName = instanceAnnotation.value() ;
            Class factoryClazz = Class.forName(factoryClassName) ;
            this.channel = (IChannel) factoryClazz.getMethod("getInstance", String.class).invoke(null, actionAnnotation.value()) ;
        } catch (Exception e) {}
    }
    public String echo(String msg) throws Exception {
        String echoMessage = "〖ERROR〗消息发送失败!" ;
        if (this.channel.build()) { // 通道创建成功
            echoMessage = "【ECHO】" + msg ; // 创建回应信息
            this.channel.close();
        }
        return echoMessage ;
    }
}
public class Test {
    public static void main(String[] args) throws Exception{
        System.out.println(new Message().echo("www.yootk.com"));
    }
}

传统类属性赋值弊端

反射是 Java 之中最为重要的核心技术,同时也是 Java 区别于其他编程语言开发的重要的技术特点,正是因为 Java 存在有了反射处理机制,才让整个的 Java 开发框架的盛行。只有掌握了反射机制才可以真正的开发出可重用的程序代码,在进行反射的开发过程之中最为经典的处理操作就是反射与简单 Java 类之间的关联。

此时利用直观的判断的方式根据属性的名称找到了属性对应的 setter 方法并且进行了内容的设置,但是此时如果再进一步思考:假设说服务器端返回的数据可能包含有 300W 种简单 Java 类的属性内容,例如:雇员、部门、公司、地址等简单 Java 类。

于是这个时候就可以发现传统简单 Java 类对象属性赋值的操作存在有两个重要的设计问题:

  • 问题一:如果某一个类中的属性很多,那么则对应的 seter 方法也一定非常的多,那么要一直进行重复调用吗?。
  • 问题二:如果此时要进行赋值的简单 Java 类有很多,则重复的代码逻辑的执行

属性自动赋值

对于之前程序中可能存在的代码重复的问题,核心的关键本质在于:需要明确的实例化一个具体类的对象,而这个对象是通过关键字 new 的模式来完成的处理,就会与具体的类型产生耦合。但是如果说使用反射的处理机制,利用 Class 类来获取当前简单 Java 类的对象,随后利用这个反射实例化对象(Obiect)再通过 Method 实现方法的反射调用,那么就可以在不知道具体类型对象的情况下从而实现 setter 方法的调用,所以此时可以得出如下的代码设计结构。

  • 1、如果要想避免关键字 new 的出现最佳的做法就是利用反射机制来实现指定类对象的实例化处理:
  • 2 如果要想在不知道明确类型的情况下利用对象(Obiect)进行 setter 方法调用,那么最佳的做法就是通过 Method 类依据属性名称(字符串中给出了属性名称)找到对应的 setter 方法;
  • 3、如果要想找到对应的 seter 方法按照反射调用的原则一定要有 seter 方法中参数的类型才可以正常,于是 setter 中的参数类型与属性的类型是相同,所以通过 Field 类去找到指定属性的类型。
详情
class ObjectInstanceFactory {
    private ObjectInstanceFactory() {
    }

    public static <T> T create(Class<?> clazz, String value) {
        return null;
    }
}

class Emp {
    private String name;
    private int 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;
    }
}

class Test1 {
    public static final String ECHO_DATA = "ename:小李老师|job:沐言僵尸";

    public static void main(String[] args) {
        Emp emp = ObjectInstanceFactory.create(Emp.class, ECHO_DATA);
        System.out.println(emp);
    }
}

属性自动赋值

所谓的单级属性是指在当前给定的简单 Java 类之中没有其他类的对象引用,这样的设计称为单级关系,单级关系相对比较简单,只需要动态获取方法名称,而后再进行动态的对象创建操作即可。

  • 1、由于整个的处理操作全部都属于完全独立的程序,所以最佳的做法是创建一个“com.yootk.util.bean”子包,这个子包中包含有 Bean 类对象的全部相关操作支持;

  • 2、不管现在是否是反射如果要想实现属性内容的保存,那么一定要通过关键字 new 开辟对应的堆内存空间,此时就可以通过反 射机制实现对象实例化控制,这个控制可以通过 ObiectnstanceFactorv.create() 方法来完成,而在对象创建完成之后具体的属性的操作方式就应该交由 BeanUtil 类来完成。

属性类型转换

通过如上的处理操作已经可以基于反射机制实现了对象实例化以及属性的配置处理了,但是在整体的处理过程中,仅仅是处理了字符串型的数据(Sting),而没有其他类型的数据,而在实际的项目开发之中,类中的属性比较常见的几种类型:Integer (int)、Double(double)、Long(lng)、Date(日期、日期时间)、BigInteger、BigDecimal(精度要求较高的环境);.

级联对象实例化

级联属性赋值

详情
package com.yix.reflect.demo.entity;

/**
 * @author wangdx
 */
public class Company {
    private long cno;
    private String name;

    public Company() {
        System.out.println("【company】实例化");
    }

    public long getCno() {
        return cno;
    }

    public void setCno(long cno) {
        this.cno = cno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Company{" +
                "cno=" + cno +
                ", name='" + name + '\'' +
                '}';
    }
}
package com.yix.reflect.demo.entity;

/**
 * @author wangdx
 */
public class Dept {
    private Long deptno;
    private String name;
    private Company company;

    public Dept() {
        System.out.println("【Dept】实例化");
    }

    public Company getCompany() {
        return company;
    }

    public void setCompany(Company company) {
        this.company = company;
    }

    public Long getDeptno() {
        return deptno;
    }

    public void setDeptno(Long deptno) {
        this.deptno = deptno;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "Dept{" +
                "deptno=" + deptno +
                ", name='" + name + '\'' +
                ", company=" + company +
                '}';
    }
}
package com.yix.reflect.demo.entity;

import java.util.Date;

/**
 * @author wangdx
 */
public class Emp {
    private String ename;
    private String job;
    private Long empNo;
    private Double salay;
    private Date hiredate;
    private Dept dept;

    public Dept getDept() {
        return dept;
    }

    public void setDept(Dept dept) {
        this.dept = dept;
    }

    public Emp() {
        System.out.println("【Emp】实例化");
    }

    public String getEname() {
        return ename;
    }

    public void setEname(String ename) {
        this.ename = ename;
    }

    public String getJob() {
        return job;
    }

    public void setJob(String job) {
        this.job = job;
    }

    public Long getEmpNo() {
        return empNo;
    }

    public void setEmpNo(Long empNo) {
        this.empNo = empNo;
    }

    public Double getSalay() {
        return salay;
    }

    public void setSalay(Double salay) {
        this.salay = salay;
    }

    public Date getHiredate() {
        return hiredate;
    }

    public void setHiredate(Date hiredate) {
        this.hiredate = hiredate;
    }

    @Override
    public String toString() {
        return "Emp{" +
                "ename='" + ename + '\'' +
                ", job='" + job + '\'' +
                ", empNo=" + empNo +
                ", salay=" + salay +
                ", hiredate=" + hiredate +
                '}';
    }
}
package com.yix.reflect.demo.test;

import com.yix.reflect.demo.entity.Emp;
import com.yix.reflect.demo.util.ObjectInstanceFactory;

/**
 * @author wangdx
 */
public class Test {
    public static final String DEFAULT_VALUE = "ename:小李老师|job:沐言讲师|empNo:2564|salay:84756.366|hiredate:2024-01-28 06:39:28"
            + "|dept.deptno:25|dept.name:管理部|dept.company.cno:05151684|dept.company.name:一祥集团";

    public static void main(String[] args) {
        Emp e = ObjectInstanceFactory.create(Emp.class, DEFAULT_VALUE);
        System.out.println(e);
        System.out.println(e.getDept());
        System.out.println(e.getDept().getCompany());
    }
}
package com.yix.reflect.demo.util;

import com.yix.reflect.demo.util.bean.BeanUtil;

/**
 * @author wangdx
 */
public class ObjectInstanceFactory {
    private ObjectInstanceFactory() {
    }

    public static <T> T create(Class<?> clazz, String value) {
        try {
            Object object = clazz.getDeclaredConstructor().newInstance();
            BeanUtil.setObjectValue(object, value);
            return (T) object;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
}
package com.yix.reflect.demo.util;

/**
 * @author wangdx
 */
public class StringUtil {
    private StringUtil() {
    }

    public static String initCap(String value) {
        if (value == null || "".equals(value)) {
            return value;
        }
        return value.length() == 1 ? value.toUpperCase() : value.substring(0, 1).toUpperCase() + value.substring(1);
    }
}
package com.yix.reflect.demo.util.bean;

import com.yix.reflect.demo.util.StringUtil;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.time.Instant;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;

/**
 * @author wangdx
 */
public class BeanUtil {
    private static final DateTimeFormatter STRING_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
    private static final ZoneId ZONE_ID = ZoneId.systemDefault();

    private BeanUtil() { //构造方法私有化
    }

    /**
     * 实现对象反射属性赋值操作
     *
     * @param object 要进行实例化处理类(不允许为空)
     * @param value  满足指定格式(“属性名:属性值|属性名:属性值。。。”)字符串
     */
    public static void setObjectValue(Object object, String value) throws Exception {
        String all[] = IFieldContentSplit.getFields(value);
        for (String content : all) {
            try {
                String fields[] = IFieldContentSplit.getFieldValue(content);
                if (fields[0].contains(".")) {//级联关系
                    String cascade[] = fields[0].split("\\.");
                    Object cascadeInstance = instanceCascadeObject(object, cascade);
                    System.out.println(cascadeInstance);
                    setFieldValue(cascadeInstance, cascade[cascade.length - 1], fields[1]);
                } else {
                    setFieldValue(object, fields[0], fields[1]);
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    private static void setFieldValue(Object object, String fieldname, String fieldValue) throws Exception {
        Field field = object.getClass().getDeclaredField(fieldname);
        Method method = object.getClass().getMethod("set" + StringUtil.initCap(fieldname), field.getType());
        method.invoke(object, convert(fieldValue, field)); //通过反射实现set方法调用
    }

    private static Object instanceCascadeObject(Object object, String cascade[]) throws Exception {
        for (int x = 0; x < cascade.length - 1; x++) {
            Method getMethod = object.getClass().getMethod("get" + StringUtil.initCap(cascade[x]));
            Object instance = getMethod.invoke(object);//获取实例化对象
            if (instance == null) {
                Field field = object.getClass().getDeclaredField(cascade[x]);
                Object obj = field.getType().getDeclaredConstructor().newInstance();
                Method setMethod = object.getClass().getMethod("set" + StringUtil.initCap(cascade[x]), field.getType());
                setMethod.invoke(object, obj);
                object = obj;
            } else {
                object = instance;
            }
        }
        return object;
    }

    /**
     * 根据成员类型字符串转换
     *
     * @param value 要转换内容
     * @param field 属性
     * @return 返回具体类型的数据
     */
    private static Object convert(String value, Field field) {
        String fieldTypeName = field.getType().getName();
        try {
            switch (fieldTypeName) {
                case "java.lang.String":
                    return value;
                case "int":
                    return Integer.parseInt(value);
                case "java.lang.Integer":
                    return Integer.parseInt(value);
                case "double":
                    return Double.parseDouble(value);
                case "java.lang.Double":
                    return Double.parseDouble(value);
                case "java.math.BigDecimal":
                    return new BigDecimal(value);
                case "long":
                    return Long.parseLong(value);
                case "java.lang.Long":
                    return Long.parseLong(value);
                case "java.util.Date": {
                    LocalDateTime localDateTime = LocalDateTime.parse(value, STRING_FORMATTER);
                    Instant instant = localDateTime.atZone(ZONE_ID).toInstant();
                    return Date.from(instant);
                }
            }
        } catch (Exception e) {
//            e.printStackTrace();
        }
        return null;
    }
}
package com.yix.reflect.demo.util.bean;

/**
 * @author wangdx
 */
public interface IFieldContentSplit {
    public static final String FIELDS_REGEX = "\\|";
    public static final String FIELD_VALUE_REGEX = ":";

    public static String[] getFields(String value) {
        return value.split(FIELDS_REGEX);
    }

    public static String[] getFieldValue(String value) {
        return value.split(FIELD_VALUE_REGEX, 2);
    }
}
上次编辑于: