反射注解案例

1、反射案例:

需求

写一个"框架",不能改变该类的任何代码的前提下,可以帮我们创建任意类的对象,并且执行其中任意方法

实现:
  1. 配置文件
  2. 反射
步骤:
  1. 创建对象
  2. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  3. 在程序中加载读取配置文件
  4. 使用反射技术来加载类文件进内存
  5. 执行方法

第一步:Person类(创建对象)

package cn.demo1015JUnit_Reflect_Anno.demo03ReflectTest;

public class Person{
    public void eat(){
        System.out.println("Person.eat");
    }
    //私有方法需要暴力反射
    private void sleep(){
        System.out.println("Person.sleep");
    }
}

第二步:配置文件 pro.properties(将需要创建的对象的全类名和需要执行的方法定义在配置文件中)

className=cn.demo1015JUnit_Reflect_Anno.demo03ReflectTest.Person
methodName=eat

第三四五步:假设框架类ReflectTestQiang

package cn.demo1015JUnit_Reflect_Anno.demo03ReflectTest;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.Properties;

/**
 * 假设框架类
 * 	3. 在程序中加载读取配置文件
 * 	4. 使用反射技术来加载类文件进内存
 * 	5. 执行方法
 */
public class ReflectTestQiang {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可移植性任意方法

        /**
         * 前提:不能改变该类的任意代码,可以创建任意类的对象,可以执行任意方法。
         *  ReflectTest.clas获取该类字节码对象
         *  ReflectTest.class.getClassLoader()获取类加载器
         *  classLoader.getResourceAsStream():获取资源对应的字节流
         */

        //1、加载配置文件
        //1.1创建Properties对象
        Properties pro = new Properties();
        //1.2加载配置文件,转换为一个集合
        //1.2.1获取class目录下的配置文件
        ClassLoader classLoader = ReflectTestQiang.class.getClassLoader();
        InputStream is = classLoader.getResourceAsStream("cn/demo1015JUnit_Reflect_Anno/demo03ReflectTest/pro.properties");
        pro.load(is);

        //2.获取配置文件中定义的数据
        String className = pro.getProperty("className");
        String methodName = pro.getProperty("methodName");

        //3.获取该类进内存
        Class cls = Class.forName(className);
        //4、创建对象
        Object o = cls.newInstance();
        //5.获取方法对象
        //Method method = cls.getMethod(methodName);
        //暴力反射:可以执行私有方法
        Method method = cls.getDeclaredMethod(methodName);
        method.setAccessible(true);
        //6.执行方法
        method.invoke(o);
    }
}

执行结果
在这里插入图片描述

2、反射注解案例

需求

写一个"框架",自定义注解Pro。根据注解属性设置需要反射的className和methodName。在程序中获得注解的className,methodName。执行该method。

实现
  1. 自定义注解类
  2. 测试类
  3. 包含方法的对象
步骤:
  1. 创建对象
  2. 将需要创建的对象的全类名和需要执行的方法定义在配置文件中
  3. 在程序中加载读取配置文件
  4. 使用反射技术来加载类文件进内存
  5. 执行方法

第一步:创建Demo1对象

package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse;
public class Demo1 {
    public void show(){
        System.out.println("Demo1.show");
    }
}

第二步:自定义注解类Pro

package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
 * 描述需要执行的类名和方法名
 */
@Target(value = {ElementType.TYPE})//描述注解作用在类上
@Retention(RetentionPolicy.RUNTIME)//保留在RunTime时期
public @interface Pro {
    String className();
    String methodName();
}

第三步:测试类ReflectTest

package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse;

import java.lang.reflect.Method;

/**
 * 假设框架类
 */
@Pro(className = "cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo05Parse.Demo2",methodName = "show")
public class ReflectTest {
    public static void main(String[] args) throws Exception {
        //可以创建任意类的对象,可移植性任意方法

        /**
         * 前提:不能改变该类的任意代码,可以创建任意类的对象,可以执行任意方法。
         */

        //1.解析注解
        //1.1获取该类的字节码文件对象
        Class<ReflectTest> reflectTestClass = ReflectTest.class;
        //2.获取上边的注解类对象(其实返回内存中生成了一个该注解接口的子类实现对象)
        Pro anno = reflectTestClass.getAnnotation(Pro.class);
        //3.调用注解对象中定义的抽象方法,获取属性值
        String className = anno.className();
        String methodName = anno.methodName();
        System.out.println(className);
        System.out.println(methodName);

        //3.获取该类进内存
        Class cls = Class.forName(className);
        //4、创建对象
        Object o = cls.newInstance();
        //5.获取方法对象
        Method method = cls.getMethod(methodName);
        //6.执行方法
        method.invoke(o);

    }
}

3、注解案例:简单的测试框架

需求:
当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法)判断方法是否有异常,记录到文件中。

第一步:定义Check注解

package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo06Test;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Check {
}

第二步:Calculator 小明定义的计算器类

package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo06Test;
/**
 * 小明定义的计算器类
 */
public class Calculator {

    //加法
    @Check
    public void add(){
        String str = null;
        str.toString();//空指针异常
        System.out.println("1 + 0 =" + (1 + 0));
    }
    //减法
    @Check
    public void sub(){
        System.out.println("1 - 0 =" + (1 - 0));
    }
    //乘法
    @Check
    public void mul(){
        System.out.println("1 * 0 =" + (1 * 0));
    }
    //除法
    @Check
    public void div(){
        System.out.println("1 / 0 =" + (1 / 0));
    }
    
    public void show(){
        System.out.println("永无bug...");
    }

}

第三步:编写测试类
1.创建计算器对象
2.获取计算器类的字节码文件的对象
3.获取所有方法
4.判断方法上是否有Check
5.有,执行
6.捕获异常

package cn.demo1015JUnit_Reflect_Anno.demo04Anno.demo06Test;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;

/**
 * 简单的测试框架
 *      当主方法执行后,会自动自行被检测的所有方法(加了Check注解的方法)
 *      判断方法是否有异常,记录到文件中
 */
public class TestCheck {
    public static void main(String[] args) throws IOException {
        //1.创建计算器对象
        Calculator calculator = new Calculator();
        //2.获取计算器类的字节码文件的对象
        Class aClass = calculator.getClass();
        //3.获取所有方法
        Method[] methods = aClass.getMethods();

        int number= 0 ;//出现异常的次数
        BufferedWriter bw = new BufferedWriter(new FileWriter("D:\\Java_IDEA\\JAVAData\\JavaBase\\src\\cn\\demo1015JUnit_Reflect_Anno\\demo04Anno\\demo06Test\\bug.txt"));

        for (Method method : methods) {
            //4.判断方法上是否有Check
            if (method.isAnnotationPresent(Check.class)){
                //5.有,执行
                try {
                    method.invoke(calculator);
                } catch (Exception e) {
                    //6.捕获异常
                    //记录到文件中
                    number++;

                    bw.write(method.getName()+"方法出异常了");
                    bw.newLine();
                    bw.write("异常名称"+e.getCause().getClass().getSimpleName());
                    bw.newLine();
                    bw.write("异常的原因"+e.getCause().getMessage());
                    bw.newLine();
                    bw.write("-----------------------");
                    bw.newLine();
                }
            }

        }
        bw.write("本次测试一共出现"+number+"次异常!!");
        bw.flush();
        bw.close();
    }
}

运行结果:
在这里插入图片描述

bug.txt
在这里插入图片描述

小结:

  1. 以后大多数时候,我们会使用注解,而不是自定义注解
  2. 注解给谁用:编译器、给解析程序用
  3. 注解不是程序的一部分,可以理解为注解就是一个标签
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值