文章目录
反射
什么是反射机制?
反射机制就是正在运行(的程序中)动态获取类的所有信息。
反射机制的作用
-
反编译:.class -> .java。
-
通过反射机制访问对象属性,方法,构造方法等。
-
使用java反射机制可以不用new来初始化类。
-
类私有属性可以通过java反射机制赋值。
提高程序的扩展性,封装一些工具类,写框架都会用到反射机制。
通过java反射机制创建对象例子
初始化类使用new创建对象,现在用反射机制来创建对象。
使用默认无参构造函数创建对象
package ch8;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
private String username;
private String password;
}
public class Test1 {
@SneakyThrows
public static void main(String[] args) {
// forName中写类的完整路径,即包名+类名
Class<?> aClass = Class.forName("ch8.User");
// 使用默认无参构造函数创建对象
User user = (User) aClass.newInstance();
System.out.println(user);
}
}
使用有参构造函数创建对象
package ch8;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.SneakyThrows;
import java.lang.reflect.Constructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
class User{
private String username;
private String password;
}
public class Test1 {
@SneakyThrows
public static void main(String[] args) {
/*
// forName中写类的完整路径,即包名+类名
Class<?> aClass = Class.forName("ch8.User");
// 使用默认无参构造函数创建对象
User user = (User) aClass.newInstance();
System.out.println(user);
*/
// forName中写类的完整路径,即包名+类名
Class<?> aClass = Class.forName("ch8.User");
// 使用getConstructor获取有参构造函数
// 参数传入有参构造函数的参数类型
Constructor<?> constructor = aClass.getConstructor(String.class, String.class);
// 创建对象
User user2 = (User) constructor.newInstance("jack", "123");
System.out.println(user2);
}
}
怎么禁止反射机制?
把构造函数改成private修饰,但是反射还是能通过设置访问权限禁止禁止反射
spring中的运用场景
写标签的时候经常遇到类似下面的格式
<bean id=“user” class=“com.java.bean.User”>
class值写类的reference path,目的之一就是通过反射机制实例化对象
springIoc
jdbc中的运用场景
在加载jdbc驱动类的时候我们会写:
class.forNmae(“com.mysql.jdbc.Driver”)
使用反射机制访问方法和属性
访问属性
package ch8;
import java.lang.reflect.Field;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取类的字节码文件
Class<?> aClass = Class.forName("ch8.User");
// 获取当前类的所有属性
Field[] declaredFields = aClass.getDeclaredFields();
// 遍历
for (Field field : declaredFields) {
// 打印名称
System.out.println(field.getName());
}
}
}
访问方法
package ch8;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException {
// 获取类的字节码文件
Class<?> aClass = Class.forName("ch8.User");
// 获取类的所有方法
Method[] declaredMethods = aClass.getDeclaredMethods();
// 遍历输出
for (Method m : declaredMethods){
System.out.println(m.getName());
}
}
}
给私有属性赋值
package ch8;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test2 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, IllegalAccessException, InstantiationException {
// 获取类的字节码文件
Class<?> aClass = Class.forName("ch8.User");
// 实例化对象
User user = (User) aClass.newInstance();
// 获取属性
Field username = aClass.getDeclaredField("username");
// 设置权限,允许操作私有属性
username.setAccessible(true);
// 属性赋值,第一个参数是类的对象, 第二个参数是赋的值
username.set(user, "likeghee");
// 获取属性
Field password = aClass.getDeclaredField("password");
// 设置权限
password.setAccessible(true);
// 属性赋值
password.set(user, "1234");
// 打印
System.out.println(user);
}
}
乞丐版springIOC实现
什么是springIOC?
控制反转,将bean与bean之间的关系交给第三方容器进行管理,不用自己手动创建对象。
springioc过程
加载springxml配置文件
传入beanid获取bean对象
springioc创建对象
使用无参构造函数
实现思路
- 用dom4j解析xml
- 通过beanid查找对应的xml节点,获取class节点属性
- 使用java反射机制newInstance类
- 使用java的反射机制给类属性赋值
实现
新建一个Context.xml放到resources文件夹下,模拟spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="user1" class="myspringioc.User">
<property name="username" value="ghee"/>
<property name="password" value="123"/>
</bean>
</beans>
新建ClassPathXmlApplicationContext类,实现它的getBean方法
package myspringioc;
import lombok.SneakyThrows;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.lang.reflect.Field;
import java.util.List;
public class ClassPathXmlApplicationContext {
private static String PATH;
private static String ID;
private static String CLASS;
private static String NAME;
private static String VALUE;
public void init() {
ID = "id";
CLASS = "class";
NAME = "name";
VALUE = "value";
}
// 构造函数
public ClassPathXmlApplicationContext(String path) {
PATH = path;
}
@SneakyThrows
public Object getBean(String id) {
init();
/*
1. 用dom4j解析xml
2. 通过beanid查找对应的xml节点,获取class节点属性
3. 使用java反射机制newInstance类
4. 使用java的反射机制给类属性赋值
*/
if (id.equals("")) {
return null;
}
SAXReader saxReader = new SAXReader();
// 读取resources下的配置文件
Document read = saxReader.read(this.getClass().getClassLoader().getResource(PATH));
// 遍历二级节点,查询beanid
// 获取根节点
Element rootElement = read.getRootElement();
// 获取二级节点
List<Element> elements = rootElement.elements();
// 遍历查找
for (Element e : elements) {
// 获取id属性的值
String beanId = e.attributeValue(ID);
// 如果和传入的id相等,找到bean
if (!beanId.equals(id)) {
// 如果不相等就跳出本次循环
continue;
}
// 找到了beanid
// 获取class属性值,也就是包PTAH
String attClassPath = e.attributeValue(CLASS);
// 初始化bean
Class<?> aClass = Class.forName(attClassPath);
Object classObj = aClass.newInstance();
// 获取三级节点
List<Element> propertyList = e.elements();
for(Element property: propertyList){
// 获取name属性的值
String attNameValue = property.attributeValue(NAME);
// 通过反射找到类中的属性
Field declaredField = aClass.getDeclaredField(attNameValue);
// 获取value属性的值
String attValueValue = property.attributeValue(VALUE);
// 设置访问权限
declaredField.setAccessible(true);
// 类属性赋值
declaredField.set(classObj, attValueValue);
}
return classObj;
}
return null;
}
}
测试代码
package myspringioc;
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("Context.xml");
User user1 = (User) classPathXmlApplicationContext.getBean("user1");
System.out.println(user1);
}
}
乞丐版ioc实现成功了
使用方便,但是初始化对象效率很低。