通过反射机制调用set方法,给Bean的属性赋值。

先来看一个例子:
 

以下是一个完整的例子,演示了如何在ClassPathXmlApplicationContext构造方法中使用反射机制调用set方法,给Bean的属性赋值。这个例子假设有一个Person类,包含name和age两个属性,以及对应的setter方法。

// Person.java
public class Person {
    private String name;
    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ClassPathXmlApplicationContext {

    private Map<String, Object> beanMap = new HashMap<>();

    public ClassPathXmlApplicationContext(String resource) {
        try {
            // 1. 读取配置文件
            InputStream inputStream = getClass().getClassLoader().getResourceAsStream(resource);
            SAXReader reader = new SAXReader();
            Document document = reader.read(inputStream);

            // 2. 获取所有的bean标签
            List<Element> beanElements = document.getRootElement().elements("bean");
            for (Element beanElement : beanElements) {
                // 3. 获取Bean的id和class属性
                String beanId = beanElement.attributeValue("id");
                String beanClassName = beanElement.attributeValue("class");

                // 4. 使用反射机制实例化Bean
                Class<?> beanClass = Class.forName(beanClassName);
                Object beanInstance = beanClass.getDeclaredConstructor().newInstance();

                // 5. 给Bean的属性赋值
                List<Element> propertyElements = beanElement.elements("property");
                for (Element propertyElement : propertyElements) {
                    String propertyName = propertyElement.attributeValue("name");
                    String propertyValue = propertyElement.attributeValue("value");

                    // 根据属性名拼接setter方法名
                    String methodName = "set" + propertyName.substring(0, 1).toUpperCase() + propertyName.substring(1);
                    Method method = beanClass.getMethod(methodName, String.class);
                    method.invoke(beanInstance, propertyValue);
                }

                // 6. 将实例化的Bean对象存放到Map集合中
                beanMap.put(beanId, beanInstance);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    public Object getBean(String beanId) {
        return beanMap.get(beanId);
    }

    public static void main(String[] args) {
        // 创建ClassPathXmlApplicationContext实例并传入配置文件路径
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        // 获取实例化的Bean对象
        Person person1 = (Person) context.getBean("person1");
        Person person2 = (Person) context.getBean("person2");

        // 打印属性值,验证是否成功赋值
        System.out.println(person1.getName()); // 输出: Alice
        System.out.println(person1.getAge());  // 输出: 25
        System.out.println(person2.getName()); // 输出: Bob
        System.out.println(person2.getAge());  // 输出: 30
    }
}
<!-- applicationContext.xml -->
<beans>
    <bean id="person1" class="Person">
        <property name="name" value="Alice" />
        <property name="age" value="25" />
    </bean>
    <bean id="person2" class="Person">
        <property name="name" value="Bob" />
        <property name="age" value="30" />
    </bean>
</beans>

在上面的例子中,我们假设有一个名为Person的类,包含name和age两个属性,以及对应的setter方法。在ClassPathXmlApplicationContext构造方法中,我们通过解析XML配置文件,使用反射机制实例化Bean对象,并通过调用setter方法给属性赋值。在main方法中,我们验证了属性值是否成功赋值。

代码解释:

for (Element propertyElement : propertyElements) {
                    String propertyName = propertyElement.attributeValue("name");
                    String propertyValue = propertyElement.attributeValue("value");

                    // 根据属性名拼接setter方法名
                    String methodName = "set" + propertyName.substring(0, 1).toUpperCase()         
                    + propertyName.substring(1);
                    Method method = beanClass.getMethod(methodName, String.class);
                    method.invoke(beanInstance, propertyValue);
                }
  1. 循环遍历所有的property元素,每个property元素对应一个属性的赋值。
  2. 从property元素中获取属性名和属性值。
  3. 通过属性名拼接出对应的setter方法名。这里假设属性名为"name",那么对应的setter方法名就是"setName"。
  4. 使用反射机制,通过beanClass.getMethod(methodName, String.class)获取对应的setter方法。

    使用 beanClass.getMethod(methodName, String.class) 这行代码,通过反射机制获取了对应属性的setter方法。这里的 beanClass 是指当前属性所属的类,methodName 是拼接出的setter方法名,String.class 表示这个方法的参数类型是String。这行代码的作用是获取对应属性的setter方法对象,以便后续调用。

     

  5. 最后,通过method.invoke(beanInstance, propertyValue)调用setter方法,将属性值赋给Bean对象的对应属性。

    使用 method.invoke(beanInstance, propertyValue) 这行代码,通过反射机制调用了获取到的setter方法,并将属性值 propertyValue 作为参数传入。这样就完成了将属性值赋给Bean对象的对应属性的操作。

这段代码的作用就是根据配置文件中的属性值,通过反射机制动态调用Bean对象的setter方法,完成属性的赋值。

 

  1. 使用 beanClass.getMethod(methodName, String.class) 这行代码:

    通过反射机制获取了对应属性的setter方法。这里的 beanClass 是指当前属性所属的类,methodName 是拼接出的setter方法名,String.class 表示这个方法的参数类型是String。这行代码的作用是获取对应属性的setter方法对象,以便后续调用。

  2. 使用 method.okinve(beanInstance, propertyValue) 这行代码:

    通过反射机制调用了获取到的setter方法,并将属性值 propertyValue 作为参数传入。这样就完成了将属性值赋给Bean对象的对应属性的操作。

 

当我们使用反射机制时,我们需要知道要调用的方法的名称和参数类型。在这里,我们使用 getMethod 方法来获取特定方法,然后使用 invoke 方法来调用该方法。

举个例子,假设我们有一个 Person 类,其中包含了一个 setName 方法:


public class Person {
    private String name;

    public void setName(String name) {
        this.name = name;
    }
}

现在,假设我们想要通过反射来调用 setName 方法,并为 name 属性赋值。下面是如何使用反射来实现这一点:


import java.lang.reflect.Method;

public class Main {
    public static void main(String[] args) throws Exception {
        // 创建Person对象
        Person person = new Person();

        // 获取Person类的Class对象
        Class<?> personClass = person.getClass();

        // 获取setName方法
        Method setNameMethod = personClass.getMethod("setName", String.class);

        // 调用setName方法
        setNameMethod.invoke(person, "Alice");

        // 打印name属性的值,验证是否成功赋值
        System.out.println(person.getName()); // 输出: Alice
    }
}

在上面的例子中,我们首先获取了 Person 对象的 Class 对象,然后使用 getMethod 方法获取了 setName 方法。接下来,我们使用 invoke 方法来调用 setName 方法,并将值 "Alice" 作为参数传入。最后,我们验证了 name 属性的值是否成功赋值为 "Alice"。

这个例子展示了如何使用反射来动态调用方法,以及如何通过反射来为对象的属性赋值。希望这个例子能帮助你更好地理解反射机制的使用。

区别:

第一种方式是基于已有对象实例获取类信息的,而第二种方式是基于类的名称动态地实例化对象。两种方式都使用了反射机制,但是应用场景和目的不同。

这种方式适用于在编写代码时并不知道具体类名,但需要根据类名动态地创建对象的情况。例如,当我们需要根据配置文件或用户输入来决定实例化哪个类时,就可以使用这种方式来实现,
例子如下:

  1. Class<?> personClass = person.getClass(); 这行代码是通过一个已经存在的对象 person 来获取它所属类的 Class 对象。这种方式适用于我们已经有了对象实例,想要获取它的类信息的情况。

  2. Class<?> beanClass = Class.forName(beanClassName); Object beanInstance = beanClass.getDeclaredConstructor().newInstance(); 这段代码是通过类的全限定名(包括包名)来获取该类的 Class 对象,然后使用 newInstance 方法通过默认构造函数实例化一个对象。这种方式适用于我们只知道类的名称,想要动态地实例化一个对象的情况。

    Class<?> beanClass = Class.forName(beanClassName); 这行代码通过类的全限定名(包括包名)来获取该类的 Class 对象。例如,如果 beanClassName 是 "com.example.Person",那么这行代码会获取到 Person 类的 Class 对象。Object beanInstance = beanClass.getDeclaredConstructor().newInstance(); 这行代码使用获取到的 Class 对象,调用其 newInstance 方法来实例化一个对象。这相当于调用了该类的默认构造函数,创建了一个新的对象实例。

假设我们有一个简单的图形绘制应用程序,用户可以通过配置文件指定要使用的图形类。以下是一个简单的示例:

假设我们有一个接口 Shape 和它的两个实现类 CircleRectangle


public interface Shape {
    void draw();
}

public class Circle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Circle");
    }
}

public class Rectangle implements Shape {
    @Override
    public void draw() {
        System.out.println("Drawing Rectangle");
    }
}

现在,假设我们有一个配置文件 config.properties,其中包含了要使用的图形类名:


shapeClassName=Circle

然后,我们可以编写一个程序来读取配置文件中的类名,并根据该类名动态地实例化对象:


import java.io.FileInputStream;
import java.util.Properties;

public class Main {
    public static void main(String[] args) {
        try {
            // 读取配置文件
            Properties properties = new Properties();
            properties.load(new FileInputStream("config.properties"));

            // 获取要实例化的类名
            String className = properties.getProperty("shapeClassName");

            // 根据类名动态实例化对象
            Class<?> shapeClass = Class.forName(className);
            Shape shapeInstance = (Shape) shapeClass.getDeclaredConstructor().newInstance();
            shapeInstance.draw();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

在这个例子中,我们从配置文件中读取了要实例化的图形类名,然后使用反射来根据类名动态地实例化对象,并调用其方法。这样就实现了根据配置文件来决定实例化哪个类的功能。

输出:Drawing Circle

代码解释:

// 读取配置文件 Properties properties = new Properties(); properties.load(new FileInputStream("config.properties")); 

这段代码的作用是创建一个 Properties 对象,并从文件 "config.properties" 中加载配置信息。让我来解释一下:

  1. Properties properties = new Properties(); 这行代码创建了一个 Properties 对象,它用于存储键值对形式的配置信息。

  2. properties.load(new FileInputStream("config.properties")); 这行代码从文件 "config.properties" 中加载配置信息到 Properties 对象中。这个文件通常是一个简单的文本文件,用来存储应用程序的配置信息,以键值对的形式进行存储。

举例来说,如果 "config.properties" 文件包含了以下内容:

shapeClassName=Circle

那么在加载后,properties 对象中就会包含一个键值对 "shapeClassName" -> "Circle"。

通过这段代码,我们可以在程序运行时动态地读取配置文件中的信息,从而实现根据配置文件来确定要实例化哪个类的功能。