# spring_implement **Repository Path**: zhenyongli/spring_implement ## Basic Information - **Project Name**: spring_implement - **Description**: 手写spring 参考链接:https://www.bilibili.com/video/BV1s3411s7DX?p=1 - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2022-04-09 - **Last Updated**: 2022-04-10 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # 一、手写Spring [参考链接](https://www.bilibili.com/video/BV1s3411s7DX?p=1) 手写的脑图如下: ![image-20220409215230157](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220409215230157.png) # 1.容器启动 **定义容器:LzyApplicationContext.class** ```java /** * 自定义spring容器 */ public class LzyApplicationContext { private Class configClass; public LzyApplicationContext(Class configClass) { this.configClass = configClass; } /** * 向外提供一个获取Bean的方法 * @param beanName 获取的Bean的名字 * @return */ public Object getBean(String beanName) { return new Object(); } } ``` 提供配置类:AppConfig.classs ```java /** * 配置类 */ @ComponentScan("com.lzy.service") // 使用自定义注解,进行路径的扫描 public class AppConfig { } ``` **提供扫描注解:ComponentScan** ```java /** * 扫描注解 */ @Retention(RetentionPolicy.RUNTIME) //RetentionPolicy.RUNTIME 运行时注解 @Target(ElementType.TYPE) // TYPE:用于描述类、接口(包括注解类型) 或enum声明 public @interface ComponentScan { String value() default ""; // 扫描的路径 } ``` **定义Component注解,将一个类定义为一个Bean** ```java /** * 扫描注解 */ @Retention(RetentionPolicy.RUNTIME) //RetentionPolicy.RUNTIME 运行时注解 @Target(ElementType.TYPE) // TYPE:用于描述类、接口(包括注解类型) 或enum声明 public @interface Component { String value() default ""; // 给当前的Bean取一个名字。Bean的默认名字是类名首字母小写。 } ``` **思考:启动Spring容器的时候,容器应该去干什么?** ​ Spring进行扫描。 **思考:Spring进行扫描,那扫描什么?** ​ 获取扫描路径。 利用容器的构造器获取的配置类对象获取扫描路径。 **思考:我们获取扫描路径,那去加载的就是比如com.lzy.service路径下的包吗?** ​ 当然不是,扫描的对对应包下的.class文件。(编译好的文件) **对应的.class文件的路径的获取:** ![image-20220409230812687](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220409230812687.png) ```java "D:\Program Files\Java\jdk1.8.0_301\bin\java.exe" "-javaagent: D:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\lib\idea_rt.jar=9901: D:\Program Files\JetBrains\IntelliJ IDEA 2021.2.2\bin" -Dfile.encoding=UTF-8 -classpath " D:\Program Files\Java\jdk1.8.0_301\jre\lib\charsets.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\deploy.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\access-bridge-64.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\cldrdata.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\dnsns.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jaccess.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\jfxrt.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\localedata.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\nashorn.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunec.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunjce_provider.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunmscapi.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\sunpkcs11.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\ext\zipfs.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\javaws.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\jce.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\jfr.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\jfxswt.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\jsse.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\management-agent.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\plugin.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\resources.jar; D:\Program Files\Java\jdk1.8.0_301\jre\lib\rt.jar; D:\program_workspace\spring_implement\target\classes" #-------------我们想要的.class文件的路径 ``` ![image-20220409231220503](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220409231220503.png) 获取到类是否是需要进行交给Spring容器管理的Bean ```java // 判断是否含有Bean组件注解 if (clazz.isAnnotationPresent(Component.class)) { ...... } ``` **思考:一般来说,Spring中Bean存在着单例Bean和多例Bean,那么对于一个Bean,应该如何去处理?** ​ 使用自定义的Scope注解进行生成是多例Bean还是单例Bean。 **注解@Scope.class** ```java /** * Bean对象的单例还是多例 */ @Retention(RetentionPolicy.RUNTIME) //RetentionPolicy.RUNTIME 运行时注解 @Target(ElementType.TYPE) // TYPE:用于描述类、接口(包括注解类型) 或enum声明 public @interface Scope { String value() default "singleton"; // 给当前的Bean取一个名字。Bean的默认名字是类名首字母小写。 } ``` **思考:那对于加上Scope注解之后,Spring是管理这些单例或者多例Bean(作用域)的呢?** **创建一个BeanDefinition.class对象** ```java /** * 定义Bean。里面包含对象的非常多的属性,比如如下 */ public class BeanDefinition { private Class type; // Bean对象的类型 private String scope; // Bean的范围,单例还是多例 // ...是否是-懒加载-等等 public Class getType() { return type; } public void setType(Class type) { this.type = type; } public String getScope() { return scope; } public void setScope(String scope) { this.scope = scope; } } ``` **思考:对于getBean()方法我们如何通过Bean的名字去获取对应Bean的实例呢?** ​ 通过Map进行保存,key就是Bean的名字。 # 2、getBean的底层实现 **创建单例Bean对象,在扫描完之后进行**,**使用单例池进行保存** ```java private ConcurrentMap singletonMap = new ConcurrentHashMap<>(); // 这里存储的都是实例化了的对象。 // 存储单例Bean for (String beanName : beanDefinitionMap.keySet()) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if("singleton".equals(beanDefinition.getScope())) { Object object = createBean(beanName, beanDefinition); // 创建单例Bean。创建的Bean后续还有依赖注入处理 singletonMap.put(beanName, object); } } ``` **getBean()方法** ```java /** * 向外提供一个获取Bean的方法 * @param beanName 获取的Bean的名字 * @return */ public Object getBean(String beanName) { // 1.从beanDefinitionMap中获取Beanfinition对象 BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); // 2.判断是否为单例 (处理获取单例对象的许多操作) if(beanDefinition == null) { throw new NullPointerException(); } else { String scope = beanDefinition.getScope(); if("singleton".equals(scope)) { Object beanBefore = singletonMap.get(beanName); if(beanBefore == null) { // 防止意外分支情况,需要当前danliBean,但是单例Bean还未创建 Object bean = createBean(beanName, beanDefinition); // 保存到单例池中 singletonMap.put(beanName, bean); return bean; } return beanBefore; } else { return createBean(beanName, beanDefinition); } } } ``` # 3、Bean的创建 **createBean()方法的实现** ```java /** * 创建单例Bean * @param beanName * @param beanDefinition * @return */ private Object createBean(String beanName, BeanDefinition beanDefinition) { // 1.获取Bean的类型 Class clazz = beanDefinition.getType(); // 2.使用反射获取Bean对象。 try { Object beanInstance = clazz.getConstructor().newInstance(); return beanInstance; } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } return null; } ``` **Test测试模拟的单例和多例的效果:** ```java @Component(value = "userService") //@Scope(value = "prototype") @Scope public class UserService { } public class Test { public static void main(String[] args) { // 两种方式,扫描xml配置文件或者配置类。 // LzyApplicationContext lzyApplicationContext = new LzyApplicationContext("spring-config.class"); LzyApplicationContext applicationContext = new LzyApplicationContext(AppConfig.class); // UserService userService = (UserService)applicationContext.getBean("userService"); // 获取Bean对象 System.out.println(applicationContext.getBean("userService")); System.out.println(applicationContext.getBean("userService")); System.out.println(applicationContext.getBean("userService")); System.out.println(applicationContext.getBean("userService")); } } ``` ![image-20220410005342447](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220410005342447.png) ![image-20220410005418381](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220410005418381.png) **单例和原型的效果实现。** # 4、模拟依赖注入 **实现首字母小写**。 ```java /** * 将首字母小写 */ public static String decapitalize(String name) { // 判断空 if (name == null || name.length() == 0) { return name; } // 长度大于1的话将首字母小写就可以了 if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){ return name; } // 只有一个字母,直接转换为小写 char chars[] = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); } ``` **对于转换为小写,String类也是调用的字符的toLowerCase方法** ```java public String toLowerCase(Locale locale) { if (locale == null) { throw new NullPointerException(); } int firstUpper; final int len = value.length; /* Now check if there are any characters that need to be changed. */ /* ==》 现在检查如果有任何需要更改的字符 */ scan: { for (firstUpper = 0 ; firstUpper < len; ) { char c = value[firstUpper]; if ((c >= Character.MIN_HIGH_SURROGATE) && (c <= Character.MAX_HIGH_SURROGATE)) { int supplChar = codePointAt(firstUpper); if (supplChar != Character.toLowerCase(supplChar)) { break scan; } firstUpper += Character.charCount(supplChar); } else { if (c != Character.toLowerCase(c)) { break scan; } firstUpper++; } } return this; } char[] result = new char[len]; int resultOffset = 0; /* result may grow, so i+resultOffset * is the write location in result */ /* Just copy the first few lowerCase characters. */ System.arraycopy(value, 0, result, 0, firstUpper); String lang = locale.getLanguage(); boolean localeDependent = (lang == "tr" || lang == "az" || lang == "lt"); char[] lowerCharArray; int lowerChar; int srcChar; int srcCount; for (int i = firstUpper; i < len; i += srcCount) { ...... lowerChar = Character.toLowerCase(srcChar); ...... } return new String(result, 0, len + resultOffset); } ``` **@Autowired注解实现** ```java @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.FIELD) // 作用在字段上 public @interface Autowired { // 暂时不写属性 } ``` **思考:自动注入,自动注入,Spring里面应该是如何做的呢?** ​ 应该在创建Bean对象的时候去做。创建Bean对象的方法createBean()去给对象进行赋值。 **依赖注入的实现**: ```java /** * 创建单例Bean * @param beanName * @param beanDefinition * @return */ private Object createBean(String beanName, BeanDefinition beanDefinition) { // 1.获取Bean的类型 Class clazz = beanDefinition.getType(); // 2.使用反射获取Bean对象。 try { Object beanInstance = clazz.getConstructor().newInstance(); // 给对象进行赋值。存在@Autowired注解的话,就进行自动注入。 for (Field field : beanInstance.getClass().getFields()) { if (field.isAnnotationPresent(Autowired.class)) { // 是自动注入属性 field.setAccessible(true); // 将私有属性设置为可以设置 // 依赖注入先ByType再ByName field.set(beanInstance, getBean(field.getName())); } } return beanInstance; } catch (Exception e) { e.printStackTrace(); } return null; } ``` **对于创建对象时,通过路径的扫描,已经将BeanDefinition对象放在Map中,后面获取注入对象可以从Map中获取。** ## 回调 对于依赖注入Bean,对于Bean的名字Spring是可以使用我们自己定义的,但是对于我们没有去定义Bean的名字的,Spring是使用默认的Bean的名字的生成策略。也有可能有其他的机制将名字给改了也不一定。,对于Bean的名字的最终控制权还是在Spring手中。所以我们需要依赖Spring来得到Bean的名字是什么,这里就需要一种回调。将Bean的名字告诉我们,告诉程序员。所以Spring得提供一个接口。 **interface BeanNameAware.class** ```java /** * 回调接口 */ public interface BeanNameAware { public void setBeanName(String beanName); } ``` **然后UserService实现BeanNameAware接口,实现setBeanName方法。** ```java //@Component(value = "userService") @Component //@Scope(value = "prototype") @Scope public class UserService implements BeanNameAware { @Autowired private OrderService orderService; private String beanName; // bean的名字,进行回调赋值 @Override public void setBeanName(String beanName) { this.beanName = beanName; } public void test() { System.out.println(orderService); } } ``` **在createBean()方法中进行判断是否实现了这个方法,实现了就回调赋值beanName属性。** ```java /** * 创建单例Bean * @param beanName * @param beanDefinition * @return */ private Object createBean(String beanName, BeanDefinition beanDefinition) { // 1.获取Bean的类型 Class clazz = beanDefinition.getType(); // 2.使用反射获取Bean对象。 try { Object beanInstance = clazz.getConstructor().newInstance(); // 给对象进行赋值。存在@Autowired注解的话,就进行自动注入。 for (Field field : beanInstance.getClass().getFields()) { if (field.isAnnotationPresent(Autowired.class)) { // 是自动注入属性 field.setAccessible(true); // 将私有属性设置为可以设置 // 依赖注入先ByType再ByName field.set(beanInstance, getBean(field.getName())); // Aware回调,对于许多的回调 机制也是类似的思路。 if(beanInstance instanceof BeanNameAware) { ((BeanNameAware) beanInstance).setBeanName(field.getName()); } } } return beanInstance; } catch (Exception e) { e.printStackTrace(); } return null; } ``` # 5、Spring初始化机制 ​ 对于我们想要自己指定Bean里面的某个属性值的话,可以使用一个接口**InitializingBean.class** ```java /** * 初始化Bean的回调接口 */ public interface InitializingBean { // 初始化bean时提供一个赋值的方法 , 对于这个方法Spring只负责调用这个方法 public void afterPropertiesSet(); } ``` ```java @Component @Scope public class UserService implements BeanNameAware, InitializingBean { // 初始化方法 @Override public void afterPropertiesSet() { // ...... 做非常多的事情,比如给属性赋值,或者是其他的业务逻辑 System.out.println("......执行了 afterPropertiesSet() "); } } ``` ​ **那么对于这个初始化方法何时调用呢?,在创建这个对象的时候进行调用。** ```java /** * 创建单例Bean * @param beanName * @param beanDefinition * @return */ private Object createBean(String beanName, BeanDefinition beanDefinition) { // 1.获取Bean的类型 Class clazz = beanDefinition.getType(); // 2.使用反射获取Bean对象。 try { Object beanInstance = clazz.getConstructor().newInstance(); // 给对象进行赋值。存在@Autowired注解的话,就进行自动注入。 for (Field field : beanInstance.getClass().getFields()) { if (field.isAnnotationPresent(Autowired.class)) { // 是自动注入属性 field.setAccessible(true); // 将私有属性设置为可以设置 // 依赖注入先ByType再ByName field.set(beanInstance, getBean(field.getName())); // Aware回调,对于许多的回调 机制也是类似的思路。 if(beanInstance instanceof BeanNameAware) { ((BeanNameAware) beanInstance).setBeanName(field.getName()); } // 初始化 if(beanInstance instanceof InitializingBean) { ((InitializingBean) beanInstance).afterPropertiesSet(); } } } return beanInstance; } catch (Exception e) { e.printStackTrace(); } return null; } ``` ​ **那么回调和初始化是一个东西吗?** ​ 当然不是,初始化是Spring只调用这个方法,但是对于回调是Spring告诉你这个Bean某个东西/值。 # 6、Spring的AOP的实现机制 ## Bean的后置处理器BeanPostProcessor Spring希望在创建这个Bean的时候,对这个Bean做一些控制、处理。那么如何做到就可以使用这个机制处理。 **BeanPostProcessor.class接口** ```java public interface BeanPostProcessor { // 前置 public void postProcessBeforeInitialization(String beanName, Object bean); // 后置 public void postProcessAfterInitialization(String beanName, Object bean); } ``` class.isAssignableFrom(clazz)表示一个类是否是由某个接口或者类的派生类。 **使用** **LzyBeanPostProcess.class** ```java // 后置处理器 @Component public class LzyBeanPostProcess implements BeanPostProcessor { @Override public void postProcessBeforeInitialization(String beanName, Object bean) { if("userService".equals(beanName)) { System.out.println("userService --- postProcessBeforeInitialization" + bean.getClass()); } } @Override public void postProcessAfterInitialization(String beanName, Object bean) { if("userService".equals(beanName)) { System.out.println("userService --- postProcessAfterInitialization" + bean.getClass()); } } } ``` **LzyApplicationContext.class** ```java // 记录实现了BeanPostProcessor接口的对象 private List beanPostProcessorList = new ArrayList<>(); if(BeanPostProcessor.class.isAssignableFrom(clazz)) { try { beanPostProcessorList.add((BeanPostProcessor)clazz.getConstructor().newInstance()); } catch (Exception e) { e.printStackTrace(); } } /** * 创建单例Bean * @param beanName * @param beanDefinition * @return */ private Object createBean(String beanName, BeanDefinition beanDefinition) { // 1.获取Bean的类型 Class clazz = beanDefinition.getType(); System.out.println(clazz); // 2.使用反射获取Bean对象。 try { Object beanInstance = clazz.getConstructor().newInstance(); // 给对象进行赋值。存在@Autowired注解的话,就进行自动注入。 for (Field field : clazz.getDeclaredFields()) { if (field.isAnnotationPresent(Autowired.class)) { // 是自动注入属性 field.setAccessible(true); // 将私有属性设置为可以设置 // 依赖注入先ByType再ByName field.set(beanInstance, getBean(field.getName())); // Aware回调,对于许多的回调 机制也是类似的思路。 if(beanInstance instanceof BeanNameAware) { ((BeanNameAware) beanInstance).setBeanName(beanName); } } } // 初始化前进行处理 for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { beanPostProcessor.postProcessBeforeInitialization(beanName, beanInstance); } // 初始化 if(beanInstance instanceof InitializingBean) { ((InitializingBean) beanInstance).afterPropertiesSet(); } // 初始化后进行处理 for (BeanPostProcessor beanPostProcessor : beanPostProcessorList) { beanPostProcessor.postProcessAfterInitialization(beanName, beanInstance); } return beanInstance; } catch (Exception e) { e.printStackTrace(); } return null; } ``` **对于单例池中的对象应该是Spring进行代理了之后的对象。** 那么可以对后置处理器进行一个处理,也可以返回的是一个对象不是无返回值。 ```java // 后置处理器 @Component public class LzyBeanPostProcess implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(String beanName, Object bean) { if("userService".equals(beanName)) { System.out.println("userService --- postProcessBeforeInitialization" + bean.getClass()); // 使用jdk的动态代理技术 return Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(), new InvocationHandler() { /** * 去执行本来的对象的方法。 * @param proxy 这个是我们的代理对象 * @param method * @param args * @return * @throws Throwable */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invoke---"); return method.invoke(bean, args); // 调用普通对象的方法。 } }); } return bean; } @Override public Object postProcessAfterInitialization(String beanName, Object bean) { if("userService".equals(beanName)) { System.out.println("userService --- postProcessAfterInitialization" + bean.getClass()); } return bean; } } ``` # 二、读取XML配置文件获取Bean **当前的文件目录:** ![image-20220410231039688](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220410231039688.png) # 1、创建XML配置文件 **创建XML的规范文件DTD文件** ```java ``` **在XML中填充Bean标签数据** ```java ``` # 2、创建UserService类和UserInterface接口 **UserInterface.java** ```java public interface UserInterface { public void test(); } ``` **UserService.java** ```java public class UserService implements UserInterface{ @Override public void test() { System.out.println("执行test()方法......" + this); } } ``` # 3、创建XmlApplicationContext容器 ```java package com.lzy.xmlspring; import org.w3c.dom.*; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.File; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** * 解析xml文件的容器 */ public class XmlApplicationContext { // 保存Bean对象的Map private ConcurrentMap beanDefinitionMap = new ConcurrentHashMap<>(); String xmlPath; // 需要解析的Spring配置文件 public XmlApplicationContext(String xmlPath) { this.xmlPath = xmlPath; ClassLoader classLoader = XmlApplicationContext.class.getClassLoader(); /* URL resource = classLoader.getResource(""); // 使用该种方式xml没有被加载到字节码中。 // 获取项目字节码文件路径。后续xml中解析需要使用 String clazzBasicPath = resource.getFile();*/ String filePath = System.getProperty("user.dir")+"\\src"; System.out.println(filePath); File files = new File(filePath); // 假设xml配置文件是唯一的,获取到xml配置文件 File xmlConfigFile = recursionFindXmlByName(files, xmlPath); // 解析xml配置文件. documentGainBeanClassToMap(xmlConfigFile); } /** * 根据配置文件名获取配置文件的真实路径 * @param files * @param configPath * @return */ public File recursionFindXmlByName(File files, String configPath) { File ans = null; if(files.isDirectory()) { // 是文件夹。 File[] fileList = files.listFiles(); for (File file : fileList) { ans = recursionFindXmlByName(file, configPath); if(null != ans) return ans; } } else { // 是文件 // System.out.println(files.getName() + " "); if(files.getName().equals(configPath)) { return files; } } return ans; } /** * 获取xml配置文件中的对象信息放入到Map中 * @param xmlConfigFile */ private void documentGainBeanClassToMap(File xmlConfigFile) { DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance(); documentBuilderFactory.setValidating(true); // 校验xml中Bean标签的定义是否符合连接的DTD规范 try { DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder(); // 使用setErrorHandler()方法会将错误信息回调给我们 // documentBuilder.setErrorHandler(new ErrorHandler() {......}); // 注意这里如果解析错误发生提示的话不会阻止程序执行 Document document = documentBuilder.parse(xmlConfigFile); // 获取xml文档对象 System.out.println("xml解析完毕"); // xml文档由元素,属性,文本等组成 Element rootElement = document.getDocumentElement(); // 获取根元素 // System.out.println(rootElement.getTagName()); // beans NodeList beanList = rootElement.getElementsByTagName("bean"); for (int i = 0; i < beanList.getLength(); i++) { Node bean = beanList.item(i); // System.out.println(bean.getNodeName()); // bean // 获取Bean标签的属性对象 --> id,class NamedNodeMap beanAttrMap = bean.getAttributes(); // 定义BeanDefinition对象进行接收class路径 BeanDefinition beanDefinition = new BeanDefinition(); String id = null; String classPath = null; for (int j = 0; j < beanAttrMap.getLength(); j++) { Node item = beanAttrMap.item(j); // System.out.println(item.getNodeName()); // 属性值 // System.out.println(item.getNodeValue()); // 属性对象 if("id".equals(item.getNodeName())) { id = item.getNodeValue(); } if("class".equals(item.getNodeName())) { classPath = item.getNodeValue(); } } // 简单处理一下。 if(id == null || classPath == null || id.length() == 0 || classPath.length() == 0) throw new NullPointerException(); String fullClassPath = classPath; // 类文件的真实路径需要为.eg: D:/program_workspace/...../target/../UserService.class。 // System.out.println(fullClassPath); //当前输出的信息为: /D:/program_workspace/spring_implement/target/classes/com.lzy.xmlservice.OrderService.class // 填入Map中。 Class clazz = this.getClass().getClassLoader().loadClass(fullClassPath); // 需要的全限定名字为: com.lzy.xmlservice.UserService beanDefinition.setType(clazz); beanDefinitionMap.put(id, beanDefinition); } } catch (Exception e) { e.printStackTrace(); } } /** * 根据再标签写入的Bean的id名字获取Bean对象 * @param beanNId * @return */ public Object getBean(String beanNId) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanNId); Class clazz = beanDefinition.getType(); Object newInstance = null; try { newInstance = clazz.getConstructor().newInstance(); return newInstance; } catch (Exception e) { e.printStackTrace(); } return newInstance; } } ``` # 4、定义BeanDefinition对象,为后续创建对象类信息提供载体 ```java package com.lzy.xmlspring; /** * Bean定义对象 */ public class BeanDefinition { private Class type; // Bean对象的类型 public Class getType() { return type; } public void setType(Class type) { this.type = type; } } ``` # 5、创建Test类 ```java package com.lzy.xmlservice; import com.lzy.xmlspring.XmlApplicationContext; public class Test { public static void main(String[] args) { // 从类路径中进行获取 XmlApplicationContext application = new XmlApplicationContext("spring-config.xml"); UserInterface userService1 = (UserInterface)application.getBean("userService"); UserInterface userService2 = (UserInterface)application.getBean("userService"); UserInterface userService3 = (UserInterface)application.getBean("userService"); userService1.test(); userService2.test(); userService3.test(); /* 执行结果 --》 三个不同的UserService实例对象 执行test()方法......com.lzy.xmlservice.UserService@1540e19d 执行test()方法......com.lzy.xmlservice.UserService@677327b6 执行test()方法......com.lzy.xmlservice.UserService@14ae5a5 */ } } ``` ![image-20220410231634917](https://gitee.com/zhenyongli/lzy_image/raw/master/images/image-20220410231634917.png)