先来看一个例子:
以下是一个完整的例子,演示了如何在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对象的对应属性的操作。
当我们使用反射机制时,我们需要知道要调用的方法的名称和参数类型。在这里,我们使用 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"。
这个例子展示了如何使用反射来动态调用方法,以及如何通过反射来为对象的属性赋值。希望这个例子能帮助你更好地理解反射机制的使用。
区别:
第一种方式是基于已有对象实例获取类信息的,而第二种方式是基于类的名称动态地实例化对象。两种方式都使用了反射机制,但是应用场景和目的不同。
这种方式适用于在编写代码时并不知道具体类名,但需要根据类名动态地创建对象的情况。例如,当我们需要根据配置文件或用户输入来决定实例化哪个类时,就可以使用这种方式来实现,
例子如下:
-
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
方法来实例化一个对象。这相当于调用了该类的默认构造函数,创建了一个新的对象实例。
假设我们有一个简单的图形绘制应用程序,用户可以通过配置文件指定要使用的图形类。以下是一个简单的示例:
假设我们有一个接口 Shape
和它的两个实现类 Circle
和 Rectangle
:
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" 中加载配置信息。让我来解释一下:
-
Properties properties = new Properties();
这行代码创建了一个 Properties 对象,它用于存储键值对形式的配置信息。 -
properties.load(new FileInputStream("config.properties"));
这行代码从文件 "config.properties" 中加载配置信息到 Properties 对象中。这个文件通常是一个简单的文本文件,用来存储应用程序的配置信息,以键值对的形式进行存储。
举例来说,如果 "config.properties" 文件包含了以下内容:
shapeClassName=Circle
那么在加载后,properties
对象中就会包含一个键值对 "shapeClassName" -> "Circle"。
通过这段代码,我们可以在程序运行时动态地读取配置文件中的信息,从而实现根据配置文件来确定要实例化哪个类的功能。