纸上学来终觉浅,在B站找了一个视频学习反射机制,跟着视频敲了一遍代码,理解反射机制的作用,视频质量很高,就是这个UP更新的有点慢。视频地址:Java中的反射 Reflection in Java_哔哩哔哩_bilibili
直接上代码。
首先是Main方法,通过代码运行示例,学习反射机制的各种功能,最后是模拟容器读取配置文件,自动进行Bean生成、依赖注入的案例。
package com.wmj.test.reflectionInJava;
import com.sun.istack.internal.NotNull;
import org.springframework.context.annotation.Primary;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
/**
* java反射机制
*/
public class Main {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, NoSuchMethodException, InvocationTargetException, InstantiationException {
//直接通过类对象进行操作
// int filed = User.publicStaticField;
// System.out.println(filed);
// User.myPublicStaticMethod();
// User user = new User("wmj", 20);
// System.out.println(user.name);
// user.myPublicMethod();
//通过反射,在运行时确定调用的雷和方法
//获取Class有3种主要方式
// 1、是根据类的字面常量,如User.class;
// 这种方式不会立即触发类的初始化,因此User类中的静态代码块不会执行
//只有访问类的静态成员变量或创建类实例时才会被执行
// Class<User> clazz = User.class;
//2、通过getClass方法获取
// User user = new User("wmj", 20);
//因为Class对象是在运行时从user实例获取的,而User实例的具体类型只能在运行时创建和确定,
// 在编译阶段无法准确地判断Class对象的确切类型,所以这里用通配符
// Class<?> userClass = user.getClass();
//3、使用Class.forName 方法
//这种方式 User的静态代码块会被立即执行
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
//4、下面 介绍Class的一些方法
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
//getDeclaredFields 获取所有字段,无论是public 还是 private 的字段,但是不能获得其父类的字段
// Field[] fields = clazz.getDeclaredFields();
// for(Field field : fields){
// System.out.println(field.getName());
// }
// System.out.println(" ");
//getFields 只获取public字段。如果存在父类的话,包括父类的public字段
// fields = clazz.getFields();
// for(Field field : fields){
// System.out.println(field.getName());
// }
//若想获取父类的字段信息,可以先通过getSuperclass,再获取字段信息
// fields = clazz.getSuperclass().getDeclaredFields();
// for(Field field : fields){
// System.out.println(field.getName());
// }
//总结:对于其他方法 Declared 表示获取的是所有成员,不管方法是public还是private
//而不带 Declared 仅代表获取public,包括继承自父类的公共成员
//根据字段名获取单个字段的信息
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
// Field field = clazz.getDeclaredField("comments");
// //字段类型
// System.out.println(field.getType());
// //泛型类型
// System.out.println(field.getGenericType());
// //字段的注解类型
// System.out.println(field.getAnnotationsByType(NotNull.class));
//接下来是类层面的操作,包括查看和修改静态字段值,以及调用静态方法。
//User中 有两个静态变量,publicStaticField 、privateStaticField
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
// Field field = clazz.getDeclaredField("publicStaticField");
// System.out.println(field.get(null));
// Field field2 = clazz.getDeclaredField("privateStaticField");
// //私有变量要赋权后才能查看
// field2.setAccessible(true);
// System.out.println(field2.get(null));
//
// //给静态变量赋值
// field2.set(null, 99);
// System.out.println(field2.get(null));
//获取类的方法信息
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
// Method[] methods = clazz.getDeclaredMethods();
// for(Method method : methods){
// System.out.println(method.getName());
// }
// //根据方法名获取单个方法的信息
// Method method = clazz.getDeclaredMethod("myPublicStaticMethod");
// System.out.println(method.getName());
// //方法调用
// method.invoke(null);
//
// Method method2 = clazz.getDeclaredMethod("myPrivateStaticMethod");
// //私有方法,需要先赋权,否则报错
// method2.setAccessible(true);
// System.out.println(method2.getName());
// //方法调用
// method2.invoke(null);
//
// //调用带参数的方法
// Method method3 = clazz.getDeclaredMethod("myPrivateStaticMethod", String.class);
// //私有方法,需要先赋权,否则报错
// method3.setAccessible(true);
// System.out.println(method3.getName());
// //方法调用
// method3.invoke(null, "PPPPPPPPPP");
//下面是类实例的方法使用。对类实例进行访问和操作
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
// //获取无参构造器
// Constructor<?> constructor = clazz.getDeclaredConstructor();
// //有参构造器
// Constructor<?> constructor2 = clazz.getDeclaredConstructor(String.class, int.class);
// Object object = constructor2.newInstance("wmj", 18);
// //类型转换
// if(object instanceof User){
// User user = (User) object;
// }
//也可以使用cast 方法进行转换,但是因为该方法是通过泛型进行转换的,
// 因此要求在编译阶段就确定要转换的类型,因此该方法仅对通过字面常量.class获取的Class对象有效
// Class<User> clazz2 = User.class;
// //有参构造器
// Constructor<?> constructor3 = clazz2.getDeclaredConstructor(String.class, int.class);
// Object object2 = constructor3.newInstance("wmj", 18);
// User user2 = clazz2.cast(object2);
//这里虽然介绍了类型的转换,但是这并不是反射的最佳实践。
//建议使用反射直接调用和操作对象的方法和字段,而不是先进行类型转换,
// 毕竟如果在编写阶段已经明确了要转换的类型,那么直接显示的使用更合适,而不必依赖反射
//反射即真正价值在于在处理编译时未知的类型,从而编写更具有通用性的代码
// Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.User");
// Constructor<?> constructor = clazz.getDeclaredConstructor(String.class, int.class);
// Object object = constructor.newInstance("wmj", 18);
// Field field = clazz.getDeclaredField("name");
// //传入object实例,表示要获取的是这个实例对象的name字段,而不是类的静态变量值
// System.out.println(field.get(object));
//
// Field age = clazz.getDeclaredField("age");
// age.setAccessible(true);
// age.set(object, 30);
// System.out.println(age.get(object));
//上面我们将final 字段 age 的值进行了修改,这其实是破坏了封装性和设计原则,尽量避免使用
// Method method = clazz.getDeclaredMethod("myPrivateMethod");
// method.setAccessible(true);
// method.invoke(object);
//
// Method method2 = clazz.getDeclaredMethod("myPrivateMethod", String.class, String.class);
// method2.setAccessible(true);
// method2.invoke(object, "aaa", "bbb");
//为深入理解反射的应用场景,模拟框架功能,实现通过配置实现服务的依赖注入
//假设有一个需求,需要通过Order类获取客户信息和地址信息
//首先是一般的实现方式。
// Address address = new Address("爱橙街" , "123456");
// Customer customer = new Customer("wmj", "qq.email.com");
// Order order = new Order(customer, address);
// order.getAddress().printPostCode();
// order.getCustomer().printEmail();
//这是通过反射的实现方法
Container container = new Container();
container.init();
String className = "com.wmj.test.reflectionInJava.User";
String fieldName = "message";
Class<?> clazz = Class.forName(className);
Object object = container.createInstance(clazz);
Field field = clazz.getDeclaredField(fieldName);
field.setAccessible(true);
Object fieldValue = field.get(object);
Method[] methods = fieldValue.getClass().getDeclaredMethods();
for(Method method : methods){
if(method.getDeclaredAnnotation(Primary.class) != null){
method.invoke(fieldValue);
}
}
}
}
接下来是其他各种类:
package com.wmj.test.reflectionInJava;
import org.springframework.context.annotation.Primary;
public class Address {
private String street;
private String postCode;
public Address(String street, String postCode) {
this.street = street;
this.postCode = postCode;
}
@Primary
public void printStreet(){
System.out.println("printStreet : " + street);
}
@Primary
public void printPostCode(){
System.out.println("printPostCode : " + postCode);
}
}
package com.wmj.test.reflectionInJava;
import org.springframework.context.annotation.Bean;
public class Config {
@Bean
public Customer customer(){
return new Customer("wmj", "aaa.qq.com");
}
@Bean
public Address address(){
return new Address("大洋路", "123");
}
@Bean
public Message message(){
return new Message("这里有消息");
}
}
package com.wmj.test.reflectionInJava;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
public class Container {
private Map<Class<?>, Method> methods;
private Object config;
private Map<Class<?>, Object> services;
/**
* 读取配置文件的方法,通过Bean注解来过滤不必要的方法,并存进 methods 中,这些方法用于生成 Bean
* @throws ClassNotFoundException
* @throws NoSuchMethodException
* @throws IllegalAccessException
* @throws InvocationTargetException
* @throws InstantiationException
*/
public void init() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
this.methods = new HashMap<>();
this.services = new HashMap<>();
Class<?> clazz = Class.forName("com.wmj.test.reflectionInJava.Config");
Method[] methods = clazz.getDeclaredMethods();
for(Method method:methods){
if(method.getDeclaredAnnotation(Bean.class) != null){
this.methods.put(method.getReturnType(), method);
}
}
this.config = clazz.getConstructor().newInstance();
}
/**
* 在init方法保存了生成Bean的方法后,
* 通过传入clazz获取类实例方法,并生成Bean返回
* @param clazz
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
*/
public Object getServiceInstanceByClass(Class<?> clazz) throws InvocationTargetException, IllegalAccessException {
if(this.services.containsKey(clazz)){
return this.services.get(clazz);
}else{
if(this.methods.containsKey(clazz)){
Method method = this.methods.get(clazz);
Object obj = method.invoke(this.config);
this.services.put(clazz, obj);
return obj;
}
}
return null;
}
/**
* 该方法用于获取Bean,并且进行自动注入
* @param clazz
* @return
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws InstantiationException
* @throws NoSuchMethodException
*/
public Object createInstance(Class<?> clazz) throws InvocationTargetException, IllegalAccessException, InstantiationException, NoSuchMethodException {
//先获取构造方法
Constructor<?> [] constructors = clazz.getDeclaredConstructors();
for(Constructor<?> constructor : constructors){
//如果构造方法被Autowired注解标记
if(constructor.getDeclaredAnnotation(Autowired.class) != null){
//获取构造方法参数数组
Class<?> [] parameterTypes = constructor.getParameterTypes();
Object [] argements = new Object[parameterTypes.length];
//参数装载
for(int i = 0; i < parameterTypes.length; i ++){
argements[i] = getServiceInstanceByClass(parameterTypes[i]);
}
//调用构造方法生成Bean
return constructor.newInstance(argements);
}
}
return clazz.getConstructor().newInstance();
}
}
package com.wmj.test.reflectionInJava;
import org.springframework.context.annotation.Primary;
public class Customer {
private String name;
private String email;
public Customer(String name, String email) {
this.name = name;
this.email = email;
}
@Primary
public void printName(){
System.out.println("printName : " + name);
}
@Primary
public void printEmail(){
System.out.println("printEmail : " + email);
}
}
package com.wmj.test.reflectionInJava;
import org.springframework.context.annotation.Primary;
public class Message {
private String content;
public Message(String content) {
this.content = content;
}
public Message() {
}
@Primary
public void printMessage(){
System.out.println("printMessage: " + this.content);
}
}
package com.wmj.test.reflectionInJava;
import org.springframework.beans.factory.annotation.Autowired;
public class Order {
private Customer customer;
private Address address;
@Autowired
public Order(Customer customer, Address address) {
this.customer = customer;
this.address = address;
}
public Order (){
}
public Customer getCustomer() {
return customer;
}
public void setCustomer(Customer customer) {
this.customer = customer;
}
public Address getAddress() {
return address;
}
public void setAddress(Address address) {
this.address = address;
}
}
package com.wmj.test.reflectionInJava;
public class Person {
public String personPublic;
private String personPrivate;
}
package com.wmj.test.reflectionInJava;
import com.sun.istack.internal.NotNull;
import org.springframework.beans.factory.annotation.Autowired;
import java.util.List;
public class User extends Person{
@NotNull
public String name;
private final int age;
private String email;
private Message message;
private List<String> comments;
public static int publicStaticField = 1;
private static int privateStaticField = 1;
static {
System.out.println("User class is initialized");
}
public User(String name, int age) {
this.name = name;
this.age = age;
}
@Autowired
public User(Message message){
this.message = message;
this.age = 18;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
public Message getMessage() {
return message;
}
public void setMessage(Message message) {
this.message = message;
}
public void myPublicMethod(){
System.out.println("this is a public method");
}
private void myPrivateMethod(){
System.out.println("this is a private method");
}
private void myPrivateMethod(String content, String mark){
System.out.println("this is a private method with parameters. " + content + " " + mark);
}
public static void myPublicStaticMethod(){
System.out.println("this is a public static Method");
}
private static void myPrivateStaticMethod(){
System.out.println("this is a private static Method");
}
private static void myPrivateStaticMethod(String content){
System.out.println("this is a private static method with parameters: " + content);
}
}