先来看一个例子:
以下是一个完整的例子,演示了如何在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); }
- 循环遍历所有的property元素,每个property元素对应一个属性的赋值。
- 从property元素中获取属性名和属性值。
- 通过属性名拼接出对应的setter方法名。这里假设属性名为"name",那么对应的setter方法名就是"setName"。
- 使用反射机制,通过beanClass.getMethod(methodName, String.class)获取对应的setter方法。
使用
beanClass.getMethod(methodName, String.class) 这行代码,通过反射机制获取了对应属性的setter方法。这里的beanClass 是指当前属性所属的类,methodName 是拼接出的setter方法名,String.class 表示这个方法的参数类型是String。这行代码的作用是获取对应属性的setter方法对象,以便后续调用。 - 最后,通过method.invoke(beanInstance, propertyValue)调用setter方法,将属性值赋给Bean对象的对应属性。
使用
method.invoke(beanInstance, propertyValue) 这行代码,通过反射机制调用了获取到的setter方法,并将属性值propertyValue 作为参数传入。这样就完成了将属性值赋给Bean对象的对应属性的操作。
这段代码的作用就是根据配置文件中的属性值,通过反射机制动态调用Bean对象的setter方法,完成属性的赋值。
-
使用
beanClass.getMethod(methodName, String.class) 这行代码:通过反射机制获取了对应属性的setter方法。这里的
beanClass 是指当前属性所属的类,methodName 是拼接出的setter方法名,String.class 表示这个方法的参数类型是String。这行代码的作用是获取对应属性的setter方法对象,以便后续调用。 -
使用
method.okinve(beanInstance, propertyValue) 这行代码:通过反射机制调用了获取到的setter方法,并将属性值
propertyValue 作为参数传入。这样就完成了将属性值赋给Bean对象的对应属性的操作。
当我们使用反射机制时,我们需要知道要调用的方法的名称和参数类型。在这里,我们使用
举个例子,假设我们有一个
public class Person { private String name; public void setName(String name) { this.name = 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 } }
在上面的例子中,我们首先获取了
这个例子展示了如何使用反射来动态调用方法,以及如何通过反射来为对象的属性赋值。希望这个例子能帮助你更好地理解反射机制的使用。
区别:
第一种方式是基于已有对象实例获取类信息的,而第二种方式是基于类的名称动态地实例化对象。两种方式都使用了反射机制,但是应用场景和目的不同。
这种方式适用于在编写代码时并不知道具体类名,但需要根据类名动态地创建对象的情况。例如,当我们需要根据配置文件或用户输入来决定实例化哪个类时,就可以使用这种方式来实现,
例子如下:
-
Class<?> personClass = person.getClass(); 这行代码是通过一个已经存在的对象person 来获取它所属类的Class 对象。这种方式适用于我们已经有了对象实例,想要获取它的类信息的情况。 -
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 方法来实例化一个对象。这相当于调用了该类的默认构造函数,创建了一个新的对象实例。
假设我们有一个简单的图形绘制应用程序,用户可以通过配置文件指定要使用的图形类。以下是一个简单的示例:
假设我们有一个接口
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"); } }
现在,假设我们有一个配置文件
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" 中加载配置信息。让我来解释一下:
-
Properties properties = new Properties(); 这行代码创建了一个 Properties 对象,它用于存储键值对形式的配置信息。 -
properties.load(new FileInputStream("config.properties")); 这行代码从文件 "config.properties" 中加载配置信息到 Properties 对象中。这个文件通常是一个简单的文本文件,用来存储应用程序的配置信息,以键值对的形式进行存储。
举例来说,如果 "config.properties" 文件包含了以下内容:
那么在加载后,
通过这段代码,我们可以在程序运行时动态地读取配置文件中的信息,从而实现根据配置文件来确定要实例化哪个类的功能。