反射机制和手写IOC

反射基础

从配置文件就可以动态的指定要创建哪个类的对象,创建不同的对象不需要修改源代码

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文件

  • 工厂模式+反射类加载

    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 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 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 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内容

      
      
      	
      	
      		
      		
      	
      	
      	
      		
      		
      	
      
    • 结果打印

    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岁了
    

你可能感兴趣的:(java,开发语言)