反射基础
从配置文件就可以动态的指定要创建哪个类的对象,创建不同的对象不需要修改源代码
classfullpath=com.xtq.fanshe.bean.Cat
method=hi
arg1=\u5c0f\u82b1
arg2=18
bean包下的实体类
import lombok.*;
/**
* @Author: xietianqi
* @Time: 2024-01-28 14:15
* @Description:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class Cat {
private String name;
private Integer age;
public static double weight;
private Cat(String name){
this.name=name;
}
public void hi(){
System.out.println("我是"+this.getClass()+"类,我叫"+name+age+"岁了");
}
}
=====================================================
package com.xtq.fanshe.bean;
import lombok.*;
/**
* @Author: xietianqi
* @Time: 2024-01-28 14:15
* @Description:
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@ToString
@EqualsAndHashCode
public class Dog {
private String name;
private Integer age;
public static double weight;
private Dog(String name){
this.name=name;
}
public void hi(){
System.out.println("我是"+this.getClass()+"类,我叫"+name+age+"岁了");
}
}
使用反射机制创建对象调用方法
package com.xtq.fanshe;
import org.junit.Test;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Properties;
/**
* @Author: xietianqi
* @Time: 2024-01-28 14:14
* @Description: 反射是掌握框架技术的基础
*/
@SuppressWarnings("all")
public class Clazz {
@Test
public void main() throws Exception {
/**
* 读文件
*/
var properties = new Properties();
properties.load(Files.newInputStream(Paths.get("src/main/resources/re.properties")));
var classfullpath = properties.get("classfullpath").toString();
var method = properties.get("method").toString();
var arg1 = properties.get("arg1").toString();
var arg2 = Integer.valueOf(properties.get("arg2").toString());
/**
* 反射创建对象,得到对象所有信息对象
*/
// 根据类名反射得到Class对象
var clazz = Class.forName(classfullpath);
// 得到无参构造
var conNon = clazz.getConstructor();
// 得到有参构造 ..
var con = clazz.getConstructor(String.class, Integer.class);
// 根据clazz类获取对应的对象
var object = con.newInstance(arg1, arg2);
// 得到obj运行类型
System.out.println(object.getClass());
// 得到方法对象
var method1 = clazz.getMethod(method);
// 得到所有权限属性
var fields1 = clazz.getDeclaredFields();
// 得到父类
System.out.println(clazz.getSuperclass());
// 得到接口
var interfaces = clazz.getInterfaces();
/**
* 访问这些成员对象
*/
// 对私有属性进行爆破
fields1[0].setAccessible(true);
// 对obj对象的fields[0]设置值
fields1[0].set(object, "小黄");
// 查看修改的效果
System.out.println(object);
// 设置静态属性值
fields1[2].set(null, 1.2);
// 得到静态的属性
System.out.println(fields1[2].get(null));
// 调用方法,同理静态的null即可
method1.invoke(object);
// 带decalared的是不管权限的只拿本类
// 不带decalared的属性和方法可以拿到父类的
}
}
IOC容器的基本原理
IOC (inversion of control) 控制反转也叫依赖注入,原理解析xml文件,利用反射机制,在对象工厂中利用反射机制创建对象,目的是为了解耦合,降低对象之间的依赖关系;
举一个不恰当的例子,你在点外卖的时候可以直接打电话给商家订餐,这样你和商家的依赖度就很高,如果借助外卖平台,你就可以在平台上选择不同的商家点单,商家和顾客的耦合度就不高....
而IOC容器就像是这个中间平台一样调度骑手把外卖从商家送到你手上,商家是否能卖出这一单和你是否能吃上饭就取决于这个中间平台;
-
xml文件
<bean id="userDao" class="com.xtq.dao.UserDao"></bean>
-
工厂模式+反射类加载
class UserFactory{ ... public static UserDao getDao(){ String className = ...// 解析xml获取类的名字 Class cls = Class.forName(className); // 通过反射创建对象 return (UserDao) cls.newInstance(); } }
模拟实现IOC
-
模拟容器类解析xml文件
-
使用反射机制创建bean对象
-
通过反射机制调用set方法,给Bean的属性赋值。
-
实例化对象进入hashmap (真正的是ConcurrentHashMap)
package com.xtq.frame; import org.dom4j.Document; import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.Node; import org.dom4j.io.SAXReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.List; import java.util.Map; /** * @Author: xietianqi * @Time: 2024-01-28 14:15 * @Description: */ public class ClassPathXmlApplicationContext { /** * 存储bean的Map集合 */ private Map<String, Object> beanMap = new HashMap<>(); /** * 在该构造方法中,解析myspring.xml文件,创建所有的Bean实例,并将Bean实例存放到Map集合中。 * * @param resource 配置文件路径(要求在类路径当中) */ public ClassPathXmlApplicationContext(String resource) { SAXReader reader = new SAXReader(); try { // 读取配置文件 Document document = reader.read(resource); // 拿到所有的bean标签 List<Node> beanNodes = document.selectNodes("//bean"); // 遍历集合拿到对bean进行处理 beanNodes.forEach(beanNode -> { Element beanElt = (Element) beanNode; // 获取id String id = beanElt.attributeValue("id"); // 获取className String className = beanElt.attributeValue("class"); try { // 反射获取对象 Class<?> clazz = Class.forName(className); Constructor<?> constructor = clazz.getDeclaredConstructor(); Object bean = constructor.newInstance(); // 存储到Map集合中 beanMap.put(id, bean); } catch (Exception e) { e.printStackTrace(); } }); /// ///给对象属性赋值/// /// // 创建和赋值分步进行解决循环依赖 beanNodes.forEach(beanNode -> { Element beanElt = (Element) beanNode; // 获取bean的id String beanId = beanElt.attributeValue("id"); // 获取所有的property标签 List<Element> propertyEls = beanElt.elements("property"); propertyEls.forEach(propertyElt -> { try { // 获取属性名 String propertyName = propertyElt.attributeValue("name"); // 获取属性类型 Class<?> propertyType = beanMap.get(beanId).getClass().getDeclaredField(propertyName).getType(); // 获取set方法名 String setMethodName = "set" + propertyName.toUpperCase().charAt(0) + propertyName.substring(1); // 获取set方法 Method setMethod = beanMap.get(beanId).getClass().getDeclaredMethod(setMethodName, propertyType); // 获取value的值 String propertyValue = propertyElt.attributeValue("value"); // 如果是简单属性 if (propertyValue != null) { // 获取属性类型名 String simpleName = propertyType.getSimpleName(); Object propertyVal = switch (simpleName) { case "byte", "Byte" -> Byte.valueOf(propertyValue); case "short", "Short" -> Short.valueOf(propertyValue); case "int", "Integer" -> Integer.valueOf(propertyValue); case "long", "Long" -> Long.valueOf(propertyValue); case "float", "Float" -> Float.valueOf(propertyValue); case "double", "Double" -> Double.valueOf(propertyValue); case "boolean", "Boolean" -> Boolean.valueOf(propertyValue); case "char", "Character" -> propertyValue.charAt(0); case "String" -> propertyValue; default -> throw new RuntimeException("这个类还没有设计复杂属性"); }; setMethod.invoke(beanMap.get(beanId), propertyVal); } // ... } catch (Exception e) { e.printStackTrace(); } }); }); } catch (DocumentException e) { e.printStackTrace(); } } public Object getBean(String beanId) { return beanMap.get(beanId); } }
-
测试
package com.xtq.frame; import com.xtq.fanshe.bean.Cat; import com.xtq.fanshe.bean.Dog; /** * @Author: xietianqi * @Time: 2024-01-28 14:15 * @Description: */ public class Main { public static void main(String[] args) { ClassPathXmlApplicationContext ioc = new ClassPathXmlApplicationContext("src/main/resources/beans.xml"); Object dog = ioc.getBean("dog"); Object cat = ioc.getBean("cat"); // ... System.out.println(dog.getClass()); System.out.println(cat.getClass()); ((Dog) dog).hi(); ((Cat) cat).hi(); } }
-
beans.xml内容
<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="cat" class="com.xtq.fanshe.bean.Cat"> <property name="name" value="小xml猫"/> <property name="age" value="19"/> </bean> <bean id="dog" class="com.xtq.fanshe.bean.Dog"> <property name="name" value="小xml狗"/> <property name="age" value="19"/> </bean> </beans>
-
结果打印
class com.xtq.fanshe.bean.Dog class com.xtq.fanshe.bean.Cat 我是class com.xtq.fanshe.bean.Dog类,我叫小xml狗19岁了 我是class com.xtq.fanshe.bean.Cat类,我叫小xml猫19岁了
-