当前位置: 首页 > article >正文

Java阶段三04

第3章-第4节

一、知识点

反射、注解、实现一个简单的IOC

二、目标

  • 理解反射的概念和优点

  • 学会反射的使用

  • 理解注解的概念和作用

  • 学会使用注解

  • 理解并掌握IOC是如何通过反射和注解实现的

三、内容分析

  • 重点

    • 学会反射的使用

    • 学会使用注解

    • 理解并掌握IOC是如何通过反射和注解实现的

  • 难点

    • 理解并掌握IOC是如何通过反射和注解实现的

四、内容

1、反射的概念

1.1 概念

Java反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法,对于任意一个对象,都能够调用它的任意一个方法和属性,这种动态的获取信息,以及动态调用对象的方法的功能成为java语言的反射机制。反射机制一般用来解决Java 程序运行期间,对某个实例对象一无所知的情况下,如何调用该对象内部的方法问题。

1.2 优点
  • 在程序运行过程中,操作这些对象

  • 解耦,提高程序的扩展性(是反射造就了Java)

  • 可以动态地创建和使用对象,反射机制是 Java 框架的底层核心,其使用灵活,没有反射机制,底层框架就失去支撑。

 

2、反射的使用

2.1 获取class对象的方式
  • Class.forName("全类名"),将字节码文件加载进内存,返回class对象(多用于配置文件中,类名配置在配置文件中)

    // 获取对象的方式
    // Class.forName("全类名")
    Class StudentClass1 = Class.forName("com.company.test12.entity.Student");
  • 类名.Class,通过类名的属性获取(多用于参数的传递)

    // 类名.class
    Class studentClass2 = Student.class;

  • 对象.getClass(),getClass()方法在Object类中有定义(获取对象字节码方式)

    注意点:同一个字节码文件(*.class)在一次程序运行中,只会被加载一次,无论是通过哪种方式获取的class对象都是同一个

    // 对象.getClass()
    Student student = new Student();
    Class studentClass3 = student.getClass();
    // class对象包含了一个类的所有信息
    // 属性、构造函数、方法

2.2 获取构造函数
  • 无参构造器
    Class<Student> studentClass = Student.class;
    Constructor<Student> studentConstructor = studentClass.getConstructor();
  • 有参构造器
    Constructor<Student> studentConstructor1 = studentClass.getConstructor(String.class, int.class, String.class, int.class);
    // 用来生成对象
    Student student1 = studentConstructor1.newInstance("张三", 20, "男", 180);
    System.out.println(student1);

  • 使用构造器创建对象
    // 用来生成对象(无参)
    Student student = studentConstructor.newInstance();
    System.out.println(student);
    // 用来生成对象(有参)
    Student student1 = studentConstructor1.newInstance("张三", 20, "男", 180);
    System.out.println(student1);

2.3 获取成员变量

getFields(): 获取某个类所有的公共(public)的字段,包括父类的字段

getDeclaredField(): 获取某个类的所有声明字段,包括public、private、protected,但是不包括父类的字段

  • 获取所有public属性
    // 获取类里面的public属性
    Field[] fields = StudentClass1.getFields();
    for (Field f : fields) {
        System.out.println(f);
    }

  • 获取所有属性
    // 获取类里面的所有属性
    Field[] fields2 = StudentClass1.getDeclaredFields();
    for (Field f : fields2) {
        System.out.println(f);
    }

  • 根据属性名获取属性对象
    Field nameField = StudentClass1.getField("name");
    System.out.println(nameField);
    Field heightField = StudentClass1.getDeclaredField("height");
    System.out.println(heightField);

  • 设置属性值
    Student student1 = new Student();
    nameField.set(student1, "张三");
    // 由于修饰符的问题,不能修改数据
    // 但是可以通过setAccessible忽略修饰符
    heightField.setAccessible(true);
    heightField.set(student1, 20);

2.4 获取方法
  • public方法
    // public方法
    Class studentClass = Student.class;
    Method[] methods = studentClass.getMethods();
    for (Method m : methods) {
        System.out.println(m);
    }

  • 所有方法
    // 所有方法,不包括父类
    Method[] methods2 = studentClass.getDeclaredMethods();
    for (Method m : methods2) {
        System.out.println(m);
    }

  • 根据方法名获取方法
    // 获取无参
    Student student = new Student();
    Method testMethod = studentClass.getDeclaredMethod("test");
    // 获取有参
    Method setNameMethod = studentClass.getMethod("setName", String.class);

  • 执行方法
    // 执行方法
    // 如果有权限问题,可以使用setAccessible忽略权限的验证
    testMethod.setAccessible(true);
    testMethod.invoke(student);
    setNameMethod.invoke(student, "张三");

3、IOC实现

3.1 创建XML文件
<?xml version="1.0" encoding="UTF-8"?>
<!--自己封装可以自己定义标签-->
<wkf>
    <entity id="student" class="com.company.entity.Student"></entity>
</wkf>
3.2 导入依赖包用于读取xml
<!-- https://mvnrepository.com/artifact/org.dom4j/dom4j -->
<dependency>
    <groupId>org.dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>2.1.1</version>
</dependency>
3.3 封装读取xml的类
/**
 * 用来生成容器的类
 */
public class GenBean {
​
    private static Map<String, Object> ctx = new HashMap<>();
​
    // 读取配置文件 一次就可以了
    static {
        try {
            // 创建SAXReader对象
            SAXReader reader = new SAXReader();
            // 加载xml文件
            Document dc = reader.read(new File("C:\\Users\\Administrator\\Desktop\\Class\\java17\\s2\\s1\\src\\main\\resources\\test.xml"));
            // 获取根节点
            Element e = dc.getRootElement();
            // 获取迭代器
            Iterator it = e.elementIterator();
            // 遍历迭代器,获取根节点信息
            while (it.hasNext()) {
                Element element = (Element) it.next();
                // 获取对应的属性
                Attribute attrId = element.attribute("id");
                Attribute attrClass = element.attribute("class");
                Class cls = Class.forName(attrClass.getValue());
                Constructor constructor = cls.getConstructor();
                ctx.put(attrId.getValue(), constructor);
            }
        } catch (Exception e) {
            // TODO: handle exception
        }
    }
}
3.4 封装getBean
public Object getBean(String id) {
    return ctx.get(id);
}
3.5 使用测试
// 弄一个xml文件,像spring一样,配置标签,自动生成对象
// 放到容器中,要使用的时候根据id获取对象
GenBean beans = new GenBean();
Student student = (Student) beans.getBean("student");
Teacher teacher = (Teacher) beans.getBean("teacher");
System.out.println(student);
System.out.println(teacher);

4、注解的概念

4.1 什么是注解

java注解是在JDK5的时候引入的一种新特性。 注解(也可以称为元数据)为在代码中添加信息提供了一种形式化的方法,使得在代码中任一时刻可以非常方便的使用这些数据。 注解类型定义了一种新的特殊接口类型,在接口关键字interface之前加@符号,即用@interface即可区分注解与普通接口声明。目前大部分框架都是通过使用注解简化代码提高编码效率

4.2 注解的作用
  1. 提供信息给编译器:编译器可直接通过注解探测错误和警告信息,例如:@Override,@Deprecated

  2. 编译阶段时的处理:软件工具可以用来利用注解信息生成代码、html文档或者做其他相应处理,例如:@Param,@Return,@See,@Author用于生成javadoc文档,@Data

  3. 运行时的处理:某些注解可以在程序运行时接收代码的提取,但注解本身不是代码的一部分

5、注解的使用

5.1 内置注解
@Deprecated

注解为 @Deprecated 的类型是不鼓励程序员使用的元素,通常是因为这样做很危险,或者是因为存在更好的替代方法。当在不推荐使用的代码中使用或覆盖不推荐使用的程序元素时,编译器会发出警告。该注解可以用来修饰构造器、字段、局部变量、方法等类型。

@Deprecated
public static void say(){
    System.out.println("say");
}
@Override

@Override 注解我们经常用到,提示子类需要重写父类的方法。方法重写或实现了在父类中声明的方法时需要加上该注解,该注解用于编译器检查重写的操作是否正确。

@Override
public static void say(){
    System.out.println("say");
}
@SuppressWarnings

用来关闭编译器生成警告信息,可以用来修饰类、方法、成员变量等,在使用该注解时,应采用就近原则,如方法产生警告是,应该针对方法声明该注解,而不是对类声明,有利于发现该类的其他警告信息。

public class Test {
    @SuppressWarnings("all")
    private List test() {
        return new ArrayList();
    }
}
5.2 元注解

元注解即注解的注解且只能作用于注解上的注解,也就是说元注解负责其他注解的注解,而且只能用在注解上面。

@Target

该注解用于定义注解能使用的范围(表示注解被用在什么地方),取值为 ElementType 枚举。

@Retention

该注解定义注解的保留策略或者说定义注解的有效期,取值范围为 RetationPolicy 枚举。(SOURCE<CLASS<RUNTIME)

  • SOURCE 代表着注解仅保留在源级别中,编译器将Java文件编译成class文件时将之遗弃。(生成一些代码)

  • CLASS 代表着注解被保留在class文件中,JVM加载class文件时将之遗弃。(@Mappers)

  • RUNTIME 代表着标记的注解会由JVM保留,因此运行时环境可以使用它。(可以存一些变量或者运行时才要执行的事情)

@Documented

该注解的使用表示是否包含在生成的 javadoc 文档中.

// 比如自定义注解 @ABC
​
@Inherited
@Documented
public @interface ABC {
    
}
​
@ABC
public class Man {
    
}
​
​
public class Student extend Man {
    
}
​
@Inherited

该注解表示注解是否具有继承的特性,子类可以继承父类中的这个注解。

// 表示注解可以用在什么地方
@Target(value = {ElementType.METHOD, ElementType.TYPE})
// 表示注解在什么地方有效
@Retention(value = RetentionPolicy.RUNTIME)
// 表示注解是否生成在JAVADOC中
@Documented
// 表示子类可以寄继承父类的注解
@Inherited
public @interface MyAnnotation {
}
​
public class MyFn {
    @MyAnnotation
    public static void say(){
        System.out.println("say");
    }
}
5.3 自定义注解
// 表示注解可以用在什么地方
@Target(value = {ElementType.METHOD, ElementType.TYPE})
// 表示注解在什么地方有效
@Retention(value = RetentionPolicy.RUNTIME)
// 表示注解是否生成在JAVADOC中
@Documented
// 表示子类可以寄继承父类的注解
@Inherited
// 使用@interface表示这是一个注解类
public @interface MyAnnotation {
}
​
public class MyFn {
    @MyAnnotation
    public static void say(){
        System.out.println("say");
    }
}
​
public class MyFn {
    // 如果没有默认值就要给注解赋值
    @MyAnnotation2(name = "张三")
    public static void say(){
        System.out.println("say");
    }
}

6、通过注解封装@Value的功能

6.1 注解类
@Target(value = {ElementType.FIELD})
@Retention(value = RetentionPolicy.RUNTIME)
public @interface Value {
    String value();
}
6.2 反射获取注解信息
public static void main(String[] args) throws Exception {
    Class<MyFn> myFnClass = MyFn.class;
    Constructor<MyFn> constructor = myFnClass.getConstructor();
    MyFn myFn = constructor.newInstance();
    Field[] fields = myFnClass.getDeclaredFields();
    for (Field field : fields) {
        Value annotation = field.getAnnotation(Value.class);
        field.setAccessible(true);
        field.set(myFn, annotation.value());
    }
    System.out.println(myFn);
}

7、小结

本章节中我么学习了注解和反射的概念、作用以及使用方式,封装了一个简易的IOC的XML使用和注解使用,深入理解了IOC是如何一步一步实现的,加深了对Spring的理解。

下一节中,我们将会学习动态代理和Spring另外的一个重要知识点AOP(面向切面编程)从而掌握Spring的使用方式。


http://www.kler.cn/a/404975.html

相关文章:

  • .net6 使用 FreeSpire.XLS 实现 excel 转 pdf - docker 部署
  • 《Python制作动态爱心粒子特效》
  • JDK、MAVEN与IDEA的安装与配置
  • 【前端】CSS修改div滚动条样式
  • iOS应用网络安全之HTTPS
  • 三种复制只有阅读权限的飞书网络文档的方法
  • Java集合ConcurrentHashMap——针对实习面试
  • 微服务架构:10个实用设计模式
  • springboot基于微信小程序的旧衣回收系统的设计与实现
  • Web中间件漏洞总结——IIS篇
  • 【K8S系列】Kubernetes 中如何调试imagePullSecrets配置详细步骤介绍
  • Unity图形学之灯光的原理
  • LeetCode131:分割回文串
  • STM32芯片EXIT外部中断的配置与原理以及模板代码(标准库)
  • C语言-11-18笔记
  • 利用开源的低代码表单设计器FcDesigner高效管理和渲染复杂表单结构
  • 网络层8——IP多播
  • 论文复现_How Machine Learning Is Solving the Binary Function Similarity Problem
  • mapStruct详解
  • docker部署redis7
  • 说一说JS伪数组和数组的区别?
  • 云原生基础-云计算概览
  • 算法-二分查找2(代码笔记)
  • 在 Ubuntu 上配置防火墙以开放特定端口
  • 【Redis_Day5】String类型
  • Python Matplotlib 数据可视化全面解析:选择它的七大理由与入门简介