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 注解的作用
-
提供信息给编译器:编译器可直接通过注解探测错误和警告信息,例如:@Override,@Deprecated
-
编译阶段时的处理:软件工具可以用来利用注解信息生成代码、html文档或者做其他相应处理,例如:@Param,@Return,@See,@Author用于生成javadoc文档,@Data
-
运行时的处理:某些注解可以在程序运行时接收代码的提取,但注解本身不是代码的一部分
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的使用方式。