Java基础-反射-反射常见应用场景

Java工程师知识树 / Java基础


反射常见应用场景

  • 反射实现驱动加载
  • 反射实现配置文件加载
  • 反射实现工厂模式
  • 反射实现静态代理
  • 反射实现动态代理

反射实现驱动加载

public static void main(String[] args) throws Exception {
    // 注册 JDBC 驱动
    // 把Driver类装载进JVM 
    Class.forName("com.mysql.cj.jdbc.Driver");
    try (
            Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/myData", "root", "");
            Statement stmt = conn.createStatement();
    ) {
        // 打开链接
        System.out.println("连接数据库...");
        // 执行查询
        System.out.println(" 实例化Statement对...");
        String sql = "SELECT * FROM xtb";
        ResultSet rs = stmt.executeQuery(sql);
        while (rs.next()) {
            // 通过字段检索
            int id = rs.getInt("user_id");
            String name = rs.getString("user_name");
            // 输出数据
            System.out.print("ID: " + id);
            System.out.print(", 名字: " + name);
            System.out.print("\n");
        }
        // 完成后关闭
        rs.close();
    } catch (Exception e) {
        // 处理 Class.forName 错误
        e.printStackTrace();
    }
    System.out.println("jdbc_test_end!");
}

反射实现配置文件加载

import java.io.FileReader;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;  
  
/* 
 * 我们利用反射和配置文件,可以使:应用程序更新时,对源码无需进行任何修改 
 * 我们只需要将新类发送给客户端,并修改配置文件即可
 * dto.txt内容:
         className = com.wechat.management.reflection.TestInvoke
         methodName = invokeMethod
 */  
public class Demo {  
    public static void main(String[] args) throws Exception {  
        //1.通过反射获取Class对象
        Class stuClass = Class.forName(getValue("className"));//"com.wechat.management.reflection.TestInvoke"
        //2.获取invokeMethod()方法
        Method method = stuClass.getMethod(getValue("methodName"));//invokeMethod
        //3.创建对象
        TestInvoke testInvoke = (TestInvoke) stuClass.newInstance();
        //3.调用invokeMethod()方法
        String retrunStr = (String) method.invoke(testInvoke, null);
        System.out.println("获取方法执行后的返回值:"+retrunStr);

    }  
      
    //此方法接收一个key,在配置文件中获取相应的value  
    public static String getValue(String key) throws IOException{  
        Properties pro = new Properties();//获取配置文件的对象  
        FileReader in = new FileReader("E:\\projects\\management\\src\\main\\resources\\dto.txt");//获取输入流
        pro.load(in);//将流加载到配置文件对象中  
        in.close();  
        return pro.getProperty(key);//返回根据key获取的value值  
    }  
} 

反射实现工厂模式

//动物接口
interface Animal {
    public void speak();
}

//人实现类
class Man implements Animal {
    @Override
    public void speak() {
        System.out.println("是人会说话:哈哈哈!!!");
    }
}

//狗实现类
class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("是狗不会说话:汪汪汪!!!");
    }
}

//工厂类,多个对象都实现了相同的接口,工厂模式是把这些对象做一个整合,可以通过工厂类获取指定的对象
class Factory {
    /**
     * 采用反射机制的简单工厂模式,即使增加动物接口的实现也不用改该工厂类的方法
     */
    public Object getAnimal(String className) throws Exception {
        Class<?> cls = Class.forName(className);
        return cls.newInstance();
    }

    /**
     * 简单工厂模式,增加动物接口的实现就要在该方法中做兼容处理
     */
    public Object getAnimalByType(String type) {
        if("com.wechat.management.reflection.Man".equals(type)){
            return new Man();
        }
        if("com.wechat.management.reflection.Dog".equals(type)){
            return new Dog();
        }
        return null;
    }
}

//测试类
public class ReflectFactory {
    public static void main(String[] args) throws Exception {
        Factory factory = new Factory();
        Animal man = (Animal) factory.getAnimal("com.wechat.management.reflection.Man");
        man.speak();
        Animal dog = (Animal) factory.getAnimalByType("com.wechat.management.reflection.Dog");
        dog.speak();
    }
}

执行结果:

是人会说话:哈哈哈!!!
是狗不会说话:汪汪汪!!!

反射实现静态代理

静态代理模式与简单工厂模式的区别

package com.wechat.management.reflection;

//动物接口
interface Animal {
    public void speak();
}

//人实现类
class Man implements Animal {
    @Override
    public void speak() {
        System.out.println("是人会说话:哈哈哈!!!");
    }
}

//狗实现类
class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("是狗不会说话:汪汪汪!!!");
    }
}

//工厂类,多个对象都实现了相同的接口,工厂模式是把这些对象做一个整合,可以通过工厂类获取指定的对象
class Factory {
    /**
     * 采用反射机制的简单工厂模式
     */
    public Object getAnimal(String className) throws Exception {
        Class<?> cls = Class.forName(className);
        return cls.newInstance();
    }

    /**
     * 简单工厂模式
     */
    public Object getAnimalByType(String type) {
        if("com.wechat.management.reflection.Man".equals(type)){
            return new Man();
        }
        if("com.wechat.management.reflection.Dog".equals(type)){
            return new Dog();
        }
        return null;
    }
}

//动物代理类,代理类与被代理对象实现相同的接口,代理模式只是隐藏某个对象的行为
class AnimalProxg implements Animal{

    //内部指定是哪个被代理的对象
    private Animal animalProxg;

    public AnimalProxg() {
        animalProxg = new Dog();//默认是条狗;外部调用AnimalProxg不知道它是Dog对象
    }

    //代理里使用工厂
    public AnimalProxg(String className) {
        try {
            Factory factory = new Factory();
            this.animalProxg = (Animal) factory.getAnimal(className);;//当狗or当人由外部决定
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void speak() {
        animalProxg.speak();//说话了
    }
}

//测试类
public class ReflectFactory {
    public static void main(String[] args) throws Exception {

        //----------工厂模式-------------
        System.out.println("----------工厂模式-------------");
        Factory factory = new Factory();
        Animal man = (Animal) factory.getAnimal("com.wechat.management.reflection.Man");
        man.speak();
        Animal dog = (Animal) factory.getAnimalByType("com.wechat.management.reflection.Dog");
        dog.speak();

        //----------代理模式-------------
        System.out.println("----------代理模式-------------");
        //使用默认值,即默认是条狗
        AnimalProxg animalProxg = new AnimalProxg();
        animalProxg.speak();

        //----------代理+工厂-------------
        System.out.println("----------代理+工厂-------------");
        //把它当成人了
        AnimalProxg animalProxg2 = new AnimalProxg("com.wechat.management.reflection.Man");
        animalProxg2.speak();
    }
}

执行结果:

----------工厂模式-------------
是人会说话:哈哈哈!!!
是狗不会说话:汪汪汪!!!
----------代理模式-------------
是狗不会说话:汪汪汪!!!
----------代理+工厂-------------
是人会说话:哈哈哈!!!

反射实现动态代理

动态代理步骤:

  1. 创建一个要调用的接口实现,获取接口实现的Class。
  2. 实现InvocationHandler接口并重写invoke方法,实例化InvocationHandler对象,用来处理 Proxy 的方法调用。
  3. 通过Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);实例化一个 proxy 对象。

1.实现InvocationHandler接口并重写invoke方法:

每一个动态代理类都必须要实现 InvocationHandler 这个接口,并且每个代理类的实例都关联到了一个 Handler,当我们通过代理对象调用一个方法的时候,这个方法的调用就会被转发为由 InvocationHandler 这个接口的 invoke 方法来进行调用。

为什么动态代理必须实现 InvocationHandler 这个接口呢?是因为可以通过实现 InvocationHandler 这个接口来规范动态代理调用方法时的规范。

/**
 * proxy - 代理的真实对象。具体是接口的一个实现。
 * method - 所要调用真实对象的某个方法的 Method 对象
 * args - 所要调用真实对象某个方法时接受的参数
 */
public interface InvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

实例化一个 proxy 对象:

//返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序InvocationHandler
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler invocationHandler)

Proxy.newProxyInstance(classLoader, interfaces, proxyHandler);三个参数分别是什么作用

classLoader:类加载器,我们手动写的都是java文件,需要编译成class文件,这个是遵循JVM规范的二进制文件,然后通过classLoader将class文件加载进内存,生成我们需要的class对象,这个class对象通过反射就可以拿到类的所有信息。在这边的作用其实就是将Java动态生成的class文件进行加载得到动态代理的class对象,以便后面其他操作。

interfaces:这个就是接口,可以看出无论代理或者Man(Dog)都是实现同样的接口,Java替我们动态生成的class文件中的方法其实就是接口中的方法。这个其实也是Java动态代理的缺点,即使Man(Dog)中声明的方法,但是接口中没有声明该方法,那么在生成的代理中就没有,也就是动态生成的代理类中只有接口中的方法。

proxyHandler:一个 InvocationHandler 对象,表示的是当我这个动态代理对象在调用方法的时候,会关联到哪一个 InvocationHandler 对象上。集成管理Proxy方法的调用映射到Man(Dog)中,主要就是在invoke中方法实现。

动态代理和静态代理最大的不同就是Java替我们动态生成了代理类代码,代理方法的调用最后都通过InvocationHandler映射到具体的实现类中。

动态代理实现代码:

package com.wechat.management.reflection;

import java.lang.reflect.Proxy;

//动物接口
interface Animal {
    public void speak();
}

//人实现类
class Man implements Animal {
    @Override
    public void speak() {
        System.out.println("是人会说话:哈哈哈!!!");
    }
}

//狗实现类
class Dog implements Animal {
    @Override
    public void speak() {
        System.out.println("是狗不会说话:汪汪汪!!!");
    }
}

//工厂类,多个对象都实现了相同的接口,工厂模式是把这些对象做一个整合,可以通过工厂类获取指定的对象
class Factory {
    /**
     * 采用反射机制的简单工厂模式
     */
    public Object getAnimal(String className) throws Exception {
        Class<?> cls = Class.forName(className);
        return cls.newInstance();
    }

    /**
     * 简单工厂模式
     */
    public Object getAnimalByType(String type) {
        if("com.wechat.management.reflection.Man".equals(type)){
            return new Man();
        }
        if("com.wechat.management.reflection.Dog".equals(type)){
            return new Dog();
        }
        return null;
    }
}

//动物代理类,代理类与被代理对象实现相同的接口,代理模式只是隐藏某个对象的行为
class AnimalProxg implements Animal{

    //内部指定是哪个被代理的对象
    private Animal animalProxg;

    public AnimalProxg() {
        animalProxg = new Dog();//默认是条狗;外部调用AnimalProxg不知道它是Dog对象
    }

    //代理里使用工厂
    public AnimalProxg(String className) {
        try {
            Factory factory = new Factory();
            this.animalProxg = (Animal) factory.getAnimal(className);;//当狗or当人由外部决定
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void speak() {
        animalProxg.speak();//说话了
    }
}

//动态代理类,JDK动态代理:Proxy.newProxyInstance与InvocationHandler的接口实现,动态代理隐藏了对象的方法执行行为
class DynamicProxgHandler {
    // 入参:接口对应的Class对象,出参:执行动作(方法)的代理对象
    public static Object newProxgInstance(Object target) {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), (proxy, method, args) -> {
            System.out.println("-----------------------");
            Object result = null;
            //eg:AOP编程
            System.out.println("调用"+target.getClass().getSimpleName()+"的"+method.getName() +"前执行");
            //传入参数 invoke(Object obj, Object... args)第一个参数为入参Class对象
            result = method.invoke(target, args);
            System.out.println("调用"+target.getClass().getSimpleName()+"的"+method.getName() +"后执行");
            return result;
        });
    }

}


//测试类
public class ReflectFactory {
    public static void main(String[] args) throws Exception {

        //----------工厂模式-------------
        System.out.println("----------工厂模式-------------");
        Factory factory = new Factory();
        Animal man = (Animal) factory.getAnimal("com.wechat.management.reflection.Man");
        man.speak();
        Animal dog = (Animal) factory.getAnimalByType("com.wechat.management.reflection.Dog");
        dog.speak();

        //----------代理模式-------------
        System.out.println("----------代理模式-------------");
        //使用默认值,即默认是条狗
        AnimalProxg animalProxg = new AnimalProxg();
        animalProxg.speak();

        //----------代理+工厂-------------
        System.out.println("----------代理+工厂-------------");
        //把它当成人了
        AnimalProxg animalProxg2 = new AnimalProxg("com.wechat.management.reflection.Man");
        animalProxg2.speak();

        //----------动态代理-------------

        System.out.println("----------动态代理-------------");
        Animal animal1 = new Dog();
        animal1.speak();//什么都不使用,直接调用方法
        Animal animalProxy = (Animal) DynamicProxgHandler.newProxgInstance(new Dog());
        animalProxy.speak();//通过动态代理,代理对象执行方法
    }
}

执行结果:

----------工厂模式-------------
是人会说话:哈哈哈!!!
是狗不会说话:汪汪汪!!!
----------代理模式-------------
是狗不会说话:汪汪汪!!!
----------代理+工厂-------------
是人会说话:哈哈哈!!!
----------动态代理-------------
是狗不会说话:汪汪汪!!!
-----------------------
调用Dog的speak前执行
是狗不会说话:汪汪汪!!!
调用Dog的speak后执行

带参数的方法实现

package com.wechat.management.reflection;

import java.lang.reflect.Proxy;

public class ReflectProxg {

    public static void main(String[] args) {
        People people = new Person();
        //创建动态代理对象
        People peopleProxg = (People) Proxy.newProxyInstance(
                people.getClass().getClassLoader(),
                people.getClass().getInterfaces(),
                (proxy, method, args1) -> {
                    System.out.println("---------AOP-----------");
                    return method.invoke(people, args1);
                });
        //调用接口方法,传入参数,执行结果会给People people
        peopleProxg.from("中国");
        System.out.println(people);
        peopleProxg.from("塞尔维亚");
        System.out.println(people);
        peopleProxg.from("巴基斯坦");
        System.out.println(people);
    }
}


interface People {
    public void from(String country);
}

class Person implements People{

    private String country;

    @Override
    public void from(String country) {
        this.country = country;
    }


    public String getCountry() {
        return country;
    }

    public void setCountry(String country) {
        this.country = country;
    }

    @Override
    public String toString() {
        return "我是" + country + '人';
    }
}

打印结果:

---------AOP-----------
我是中国人
---------AOP-----------
我是塞尔维亚人
---------AOP-----------
我是巴基斯坦人
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270

推荐阅读更多精彩内容