纯手写SpringFramework-完结版(原创)
个人简介
作者是一个来自河源的大三在校生,以下笔记都是作者自学之路的一些浅薄经验,如有错误请指正,将来会不断的完善笔记,帮助更多的Java爱好者入门。
文章目录
-
- 个人简介
- 纯手写SpringFramework-完结版
-
- 详细介绍Spring是什么?
- 这一代的Spring新增了什么?
- 手写Spring的生命周期
- 项目结构图
- pom.xml
- MyClassPathXmlApplicationContext底层核心
-
- 细节-1
- 细节-2
- 细节-3
- 细节-4
- 细节-5
- 细节-6
- 细节-7
- MyClassPathXmlApplicationContext源码
- 使用教程
-
- xml配置文件
- 测试类
纯手写SpringFramework-完结版
详细介绍Spring是什么?
- 再来介绍一下什么是Spring,一方面为了字数防止限流、另一方面是为了有的朋友直接看第二代手写SpringFramework而不知道什么是Spring。为了防止这种情况(之后的每一篇都要介绍一下伟大的Spring),当然这是题外话了=__=
Spring是一个开源框架,它由Rod Johnson创建。它是为了解决企业应用开发的复杂性而创建的。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。
Spring是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。
轻量——从大小与开销两方面而言Spring都是轻量的。完整的Spring框架可以在一个大小只有1MB多的JAR文件里发布。并且Spring所需的处理开销也是微不足道的。此外,Spring是非侵入式的:典型地,Spring应用中的对象不依赖于Spring的特定类。
控制反转——Spring通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。
面向切面——Spring提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。
容器——Spring包含并管理应用对象的配置和生命周期,在这个意义上它是一种容器,你可以配置你的每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以创建一个单独的实例或者每次需要时都生成一个新的实例——以及它们是如何相互关联的。然而,Spring不应该被混同于传统的重量级的EJB容器,它们经常是庞大与笨重的,难以使用。
框架——Spring可以将简单的组件配置、组合成为复杂的应用。在Spring中,应用对象被声明式地组合,典型地是在一个XML文件里。Spring也提供了很多基础功能(事务管理、持久化框架集成等等),将应用逻辑的开发留给了你。
MVC——Spring的作用是整合,但不仅仅限于整合,Spring 框架可以被看做是一个企业解决方案级别的框架。客户端发送请求,服务器控制器(由DispatcherServlet实现的)完成请求的转发,控制器调用一个用于映射的类HandlerMapping,该类用于将请求映射到对应的处理器来处理请求。HandlerMapping 将请求映射到对应的处理器Controller(相当于Action)在Spring 当中如果写一些处理器组件,一般实现Controller 接口,在Controller 中就可以调用一些Service 或DAO 来进行数据操作 ModelAndView 用于存放从DAO 中取出的数据,还可以存放响应视图的一些数据。 如果想将处理结果返回给用户,那么在Spring 框架中还提供一个视图组件ViewResolver,该组件根据Controller 返回的标示,找到对应的视图,将响应response 返回给用户。
(以上摘选自:《百度百科》)
这一代的Spring新增了什么?
- 1:提供了基于XML配置文件管理bean。目前实现的XML配置文件功能有:
- 1.1:目前实现的标签有:my-beans、my-bean、my-properties、my-ref
- 2:新增了Spring容器的另外一个实现类:MyClassPathXmlApplicationContext。
手写Spring的生命周期
- 推断构造方法(这里我全都采用无參构造)->创建bean对象->DI依赖注入->Aware回调(BeanName和BeanFactory)->初始化前->初始化->初始化后(AOP)
- 我们的Spring并没有实现完整的生命周期,所以很多并不完善。后面应该不会继续完善这个生命周期。以上的生命周期均已实现。
项目结构图
pom.xml
<dependencies> <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> <!-- https://mvnrepository.com/artifact/org.dom4j/dom4j --> <dependency> <groupId>org.dom4j</groupId> <artifactId>dom4j</artifactId> <version>2.1.3</version> </dependency> </dependencies>
- 1:cglib:用来做动态代理。Spring的底层亦是如此
- 2:dom4j:用来操作XML文件。dom4j也可以说是目前最好用的操作XML文件的工具了,且此项目中会大量用到。
MyClassPathXmlApplicationContext底层核心
细节-1
public class MyClassPathXmlApplicationContext implements ApplicationContext {}
- 说明MyClassPathXmlApplicationContext是ApplicationContext的实现类,可以通过多态的方式创建对象
细节-2
private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap;private ConcurrentHashMap<String,Object> singletonMap; private CopyOnWriteArrayList<BeanPostProcessor> beanPostProcessors; private List<Element> elements;
- beanDefinitionMap:bean定义Map
- singletonObjects:单例池,也就是Spring的一级缓存
- beanPostProcessors:beanPostProcessors集合
- elements:root结点下一个结点的集合
细节-3
String uri=this.getClass().getResource("/spring/"+configLocation).toURI().getPath(); String scp = uri.substring(1, uri.length()); SAXReader saxReader=new SAXReader(); Document document = saxReader.read(new File(scp)); Element rootElement = document.getRootElement(); if(!rootElement.getName().equals("my-beans")){ throw new NullPointerException("标签不正确"); } ClassLoader classLoader = this.getClass().getClassLoader(); elements = rootElement.elements();
- 获取扫描xml文件,只扫描resources目录下的spring目录
- 通过SAX阅读器去加载XML配置文件,并获取该XML配置文件的root结点也就是my-beans标签
- 然后校验标签是否正确,如果标签不正确则抛出异常
- 通过当前对象的类去拿到类加载器(ClassLoader)
- 获取root结点的下一级结点的集合并且赋值给全局变量elements
细节-4
for (Element element : elements) { if(!element.getName().equals("my-bean")){ throw new NullPointerException("标签不正确"); } //beanName String beanName=null; //定义一个BeanDefinition BeanDefinition beanDefinition = new BeanDefinition(); List<Attribute> attributes = element.attributes(); for (Attribute attribute : attributes) { if(attribute.getName().equals("id")){ beanName=attribute.getValue(); }else if(attribute.getName().equals("class")){ Class<?> aClass = classLoader.loadClass(attribute.getValue()); if (BeanPostProcessor.class.isAssignableFrom(aClass)) {BeanPostProcessor obj = (BeanPostProcessor) aClass.getConstructor().newInstance();beanPostProcessors.add(obj); } beanDefinition.setType(aClass); }else if(attribute.getName().equals("scope")){ beanDefinition.setScope(attribute.getValue()); }else { throw new NullPointerException("属性不正确"); } } beanDefinitionMap.put(beanName,beanDefinition); }
- 遍历root结点的下一级结点,这个结点其实就是我们定义的一个个bean
- 校验标签的正确性,不正确则抛出异常
- 定义局部变量beanName和BeanDefinition(存储bean所属类、单例还是多例)
- 获取root结点的下一级结点的属性,并进行遍历
- 通过attribute.getValue()给beanName赋值、classLoader.loadClass(attribute.getValue())给BeanDefinition的type赋值、和beanDefinition.setScope(attribute.getValue())
- 并且判断这个类是否实现BeanPostProcessor接口,如果有则单独创建一个对象放到beanPostProcessors集合中。
- 扫描到此结束
细节-5
ConcurrentHashMap.KeySetView<String, BeanDefinition> keySet = beanDefinitionMap.keySet(); //加载入单例池 for (String beanName : keySet) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); String scope = beanDefinition.getScope(); if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){ //不做任何事 }else { if(!singletonObjects.containsKey(beanName)){ Object obj = createBean(beanName); singletonObjects.put(beanName,obj); } } }
- 遍历beanDefinitionMap,找出所有单例的BeanDefinition对象,然后将其加载入单例池。
细节-6
for (Element element : elements) { String curBeanName = element.attributeValue("id"); xxx}
- 遍历elements集合(二级标签),获取当前beanName,通过判断beanName(也就是需要创建bean的bean名)和curBeanName(当前遍历到的bean名相比较)找出需要创建的bean。
细节-7
List<Element> els = element.elements(); for (Element el : els) { if(el.getName().equals("my-properties")){ if(el.element("my-ref")!=null){ String fieldName = el.attributeValue("name"); String bn = el.element("my-ref").attributeValue("bean"); Field field = aClass.getDeclaredField(fieldName); field.setAccessible(true); if(singletonObjects.containsKey(bn)){ Object beanObj = singletonObjects.get(bn); field.set(obj,beanObj); } else{ Object createBean = createBean(bn); field.set(obj,createBean); } }else{ List<Attribute> attributes = el.attributes(); //用于保存注入的字段 String field=null; //用于保存注入的值 String val=null; for (Attribute attribute : attributes) { if(attribute.getName().equals("name")){ field=attribute.getValue(); }else if(attribute.getName().equals("value")){ val=attribute.getValue(); }else { throw new NullPointerException("请检查定义bean的属性"); } } Field fd = aClass.getDeclaredField(field); fd.setAccessible(true); String type = fd.getType().getName().toLowerCase(); if(type.equals("byte")||type.equals("java.lang.byte")){ fd.set(obj,Byte.parseByte(val)); }else if(type.equals("short")||type.equals("java.lang.short")){ fd.set(obj,Short.parseShort(val)); }else if(type.equals("int")||type.equals("java.lang.integer")){ fd.set(obj,Integer.parseInt(val)); }else if(type.equals("long")||type.equals("java.lang.long")){ fd.set(obj,Long.parseLong(val)); }else if(type.equals("double")||type.equals("java.lang.double")){ fd.set(obj,Double.parseDouble(val)); }else if(type.equals("float")||type.equals("java.lang.float")){ fd.set(obj,Float.parseFloat(val)); }else if(type.equals("char")){ fd.set(obj,val.charAt(0)); }else if(type.equals("boolean")||type.equals("java.lang.boolean")){ if(val.equals("true")){ fd.set(obj,true); }else{ fd.set(obj,false); } }else if(type.equals("java.lang.string")){ fd.set(obj,val); } else{ throw new NullPointerException("暂不支持这个字段类型"); } }}else { throw new NullPointerException("请检查注入属性的标签");} }
- 获取当前二级标签(my-bean)的第三级标签(my-properties)集合
- 遍历这个三级标签集合,判断这个标签的子标签是否是my-ref标签
- 如果含有my-ref标签的话,则通过bn去单例池里面找是否有这个对象,单例池如果有的话则用单例池的;
- 单例池如果没有则直接调用createBean方法创建对象并赋值
- 如果没有my-ref标签的话,就拿出他的name和value属性,通过这个name匹配它的实体类中字段名为这个name的字段。
- 为了防止注入报错,我们对注入的所有基本数据类型+String进行判断(变小写再判断)
- 然后接下来就是Aware回调和初始化前、初始化、初始化后、如果是单例则放入单例池的操作,这里省略了不写了。
MyClassPathXmlApplicationContext源码
package com.springframework.core.context;import com.springframework.core.constant.ScopeType;import org.dom4j.Attribute;import org.dom4j.Document;import org.dom4j.DocumentException;import org.dom4j.Element;import org.dom4j.io.SAXReader;import java.io.File;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.net.URISyntaxException;import java.net.URL;import java.util.Arrays;import java.util.List;import java.util.Objects;import java.util.Optional;import java.util.concurrent.ConcurrentHashMap;import java.util.concurrent.CopyOnWriteArrayList;import java.util.stream.Stream;/** * 纯手写ClassPathXmlApplicationContext * @author 游政杰 * TODO: 2022/3/22 *///说明MyClassPathXmlApplicationContext是ApplicationContext的实现类,可以通过多态的方式创建对象public class MyClassPathXmlApplicationContext implements ApplicationContext { //bean定义Map private ConcurrentHashMap<String,BeanDefinition> beanDefinitionMap; //单例池,也就是Spring的一级缓存 private ConcurrentHashMap<String,Object> singletonObjects; //beanPostProcessors集合 private CopyOnWriteArrayList<BeanPostProcessor> beanPostProcessors; //root结点下一个结点的集合 private List<Element> elements; //私有化构造器 private MyClassPathXmlApplicationContext(){ } public MyClassPathXmlApplicationContext(String configLocation){ this.beanDefinitionMap=new ConcurrentHashMap<>(); this.singletonObjects=new ConcurrentHashMap<>(); this.beanPostProcessors=new CopyOnWriteArrayList<>(); try { //获取扫描xml文件,只扫描resources目录下的spring目录 String uri=this.getClass().getResource("/spring/"+configLocation).toURI().getPath(); String scp = uri.substring(1, uri.length()); //通过SAX阅读器去加载XML配置文件,并获取该XML配置文件的root结点也就是my-beans标签 //然后校验标签是否正确,如果标签不正确则抛出异常 //通过当前对象的类去拿到类加载器(ClassLoader) //获取root结点的下一级结点的集合并且赋值给全局变量elements //创建SAX阅读器 SAXReader saxReader=new SAXReader(); //加载XML文件 Document document = saxReader.read(new File(scp)); //获取root结点 Element rootElement = document.getRootElement(); //校验标签的正确性 if(!rootElement.getName().equals("my-beans")){ throw new NullPointerException("标签不正确"); } //获取类加载器 ClassLoader classLoader = this.getClass().getClassLoader(); //获取root结点的下一级结点的集合 elements = rootElement.elements(); //遍历root结点的下一级结点,这个结点就是我们定义的一个个bean for (Element element : elements) { //校验标签的正确性 if(!element.getName().equals("my-bean")){ throw new NullPointerException("标签不正确"); } //beanName String beanName=null; //定义一个BeanDefinition BeanDefinition beanDefinition = new BeanDefinition(); //获取root结点的下一级结点的属性 List<Attribute> attributes = element.attributes(); //root结点的下一级结点的属性的值 for (Attribute attribute : attributes) { //分别找出id、class、scope属性 if(attribute.getName().equals("id")){ beanName=attribute.getValue(); }else if(attribute.getName().equals("class")){ Class<?> aClass = classLoader.loadClass(attribute.getValue()); //判断这个类是否实现BeanPostProcessor if (BeanPostProcessor.class.isAssignableFrom(aClass)) {BeanPostProcessor obj = (BeanPostProcessor) aClass.getConstructor().newInstance();beanPostProcessors.add(obj); } beanDefinition.setType(aClass); }else if(attribute.getName().equals("scope")){ beanDefinition.setScope(attribute.getValue()); }else { throw new NullPointerException("属性不正确"); } } beanDefinitionMap.put(beanName,beanDefinition); } //扫描结束 ConcurrentHashMap.KeySetView<String, BeanDefinition> keySet = beanDefinitionMap.keySet(); //加载入单例池 for (String beanName : keySet) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); String scope = beanDefinition.getScope(); if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){ //不做任何事 }else { if(!singletonObjects.containsKey(beanName)){ Object obj = createBean(beanName); singletonObjects.put(beanName,obj); } } } } catch (URISyntaxException e) { e.printStackTrace(); } catch (DocumentException e) { e.printStackTrace(); } catch (ClassNotFoundException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } } @Override public Object getBean(String beanName) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if(beanDefinition==null){ return null; }else { String scope = beanDefinition.getScope(); //如果是多例 if(scope!=null&&scope.equals(ScopeType.PROTOTYPE)){ return createBean(beanName); }else {//如果是单例的话先去单例池中找 Object obj = singletonObjects.get(beanName); //如果单例池没有 if(obj==null){ obj=createBean(beanName); }else {//单例池有的话直接返回obj return obj; } } } return null; } private Object createBean(String beanName) { BeanDefinition beanDefinition = beanDefinitionMap.get(beanName); if(beanDefinition==null){ throw new NullPointerException("创建bean失败");// return null; }else { try { //依赖注入,二级标签,这既是一个个bean for (Element element : elements) { //遍历获取当前beanName String curBeanName = element.attributeValue("id"); if(curBeanName.equals(beanName)){ Class<?> aClass = beanDefinition.getType(); //反射创建对象 Object obj = aClass.getConstructor().newInstance(); //注入的属性集,也就是第三级标签 List<Element> els = element.elements(); for (Element el : els) {//只有这种情况才合法,因为我们目前只支持my-properties标签注入if(el.getName().equals("my-properties")){ //判断这个标签的子标签是否是my-ref标签 if(el.element("my-ref")!=null){ String fieldName = el.attributeValue("name"); String bn = el.element("my-ref").attributeValue("bean"); Field field = aClass.getDeclaredField(fieldName); field.setAccessible(true); //通过bn去单例池里面找是否有 if(singletonObjects.containsKey(bn)){ //单例池如果有则用单例池的 Object beanObj = singletonObjects.get(bn); field.set(obj,beanObj); } else{ //直接调用createBean方法创建对象并赋值 Object createBean = createBean(bn); field.set(obj,createBean); } }else{ List<Attribute> attributes = el.attributes(); //用于保存注入的字段 String field=null; //用于保存注入的值 String val=null; for (Attribute attribute : attributes) { if(attribute.getName().equals("name")){ field=attribute.getValue(); }else if(attribute.getName().equals("value")){ val=attribute.getValue(); }else { throw new NullPointerException("请检查定义bean的属性"); } } //此时进行依赖注入,必须要是getDeclaredField方法 Field fd = aClass.getDeclaredField(field); fd.setAccessible(true); //为了防止注入报错,我们对注入的所有基本数据类型+String进行判断(变小写再判断) String type = fd.getType().getName().toLowerCase(); if(type.equals("byte")||type.equals("java.lang.byte")){ fd.set(obj,Byte.parseByte(val)); }else if(type.equals("short")||type.equals("java.lang.short")){ fd.set(obj,Short.parseShort(val)); }else if(type.equals("int")||type.equals("java.lang.integer")){ fd.set(obj,Integer.parseInt(val)); }else if(type.equals("long")||type.equals("java.lang.long")){ fd.set(obj,Long.parseLong(val)); }else if(type.equals("double")||type.equals("java.lang.double")){ fd.set(obj,Double.parseDouble(val)); }else if(type.equals("float")||type.equals("java.lang.float")){ fd.set(obj,Float.parseFloat(val)); }else if(type.equals("char")){ fd.set(obj,val.charAt(0)); }else if(type.equals("boolean")||type.equals("java.lang.boolean")){ if(val.equals("true")){ fd.set(obj,true); }else{ fd.set(obj,false); } }else if(type.equals("java.lang.string")){ fd.set(obj,val); } else{ throw new NullPointerException("暂不支持这个字段类型"); } }}else { throw new NullPointerException("请检查注入属性的标签");} } //依赖注入完成之后 //判断对象是否需要Aware回调 if(obj instanceof Aware){//再具体判断是什么回调if(obj instanceof BeanNameAware){ ((BeanNameAware) obj).setBeanName(beanName);} } //初始化前 for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {beanPostProcessor.postProcessorBeforeInitialization(obj,beanName); } //初始化 if(obj instanceof InitializingBean){((InitializingBean) obj).afterPropertiesSet(); } //初始化后 for (BeanPostProcessor beanPostProcessor : beanPostProcessors) {beanPostProcessor.postProcessorAfterInitialization(obj,beanName); } //如果是单例就放入单例池 if(beanDefinition.getScope()==null||beanDefinition.getScope().equals(ScopeType.SINGLETON)){singletonObjects.put(beanName,obj); } return obj; } } } catch (InstantiationException e) { e.printStackTrace(); } catch (IllegalAccessException e) { e.printStackTrace(); } catch (InvocationTargetException e) { e.printStackTrace(); } catch (NoSuchMethodException e) { e.printStackTrace(); } catch (NoSuchFieldException e) { e.printStackTrace(); } //如果找不到就会返回null return null; } }}
使用教程
xml配置文件
<my-beans> <my-bean id="orderBean1" class="com.springframework.core.service.impl.OrderServiceImpl" > <my-properties name="aByte" value="111"/> <my-properties name="aShort" value="222"/> <my-properties name="aInt" value="333"/> <my-properties name="aLong" value="6666"/> <my-properties name="aDouble" value="123.123"/> <my-properties name="aFloat" value="233.567"/> <my-properties name="aChar" value="c"/> <my-properties name="aBoolean" value="true"/> <my-properties name="aString" value="8大基本数据类型与String的注入测试"/> </my-bean> <my-bean id="orderBean2" class="com.springframework.core.service.impl.OrderServiceImpl" scope="prototype"> <my-properties name="aByte" value="111"/> <my-properties name="aShort" value="222"/> <my-properties name="aInt" value="333"/> <my-properties name="aLong" value="6666"/> <my-properties name="aDouble" value="123.123"/> <my-properties name="aFloat" value="233.567"/> <my-properties name="aChar" value="c"/> <my-properties name="aBoolean" value="true"/> <my-properties name="aString" value="8大基本数据类型与String的注入测试---prototype"/> </my-bean> <my-bean id="userBean1" class="com.springframework.core.service.impl.UserServiceImpl" scope="singleton"> <my-properties name="orderServiceImpl"> <my-ref bean="orderBean2"/> </my-properties> </my-bean> <my-bean id="myBeanPostProcess" class="com.springframework.core.service.impl.MyBeanPostProcess" scope="singleton"> </my-bean></my-beans>
测试类
ApplicationContext applicationContext=new MyClassPathXmlApplicationContext("spring-service.xml"); OrderService orderBean1 = (OrderService) applicationContext.getBean("orderBean1"); System.out.println(orderBean1); System.out.println(orderBean1.hashCode()); OrderService orderBean2 = (OrderService) applicationContext.getBean("orderBean2"); System.out.println(orderBean2); System.out.println(orderBean2.hashCode()); UserService userBean1 = (UserService) applicationContext.getBean("userBean1"); System.out.println(userBean1);// singleton System.out.println(applicationContext.getBean("orderBean1").hashCode()); System.out.println(applicationContext.getBean("orderBean1").hashCode()); System.out.println(applicationContext.getBean("orderBean1").hashCode()); System.out.println("----多例---"); //prototype System.out.println(applicationContext.getBean("orderBean2").hashCode()); System.out.println(applicationContext.getBean("orderBean2").hashCode()); System.out.println(applicationContext.getBean("orderBean2").hashCode());
输出结果
postProcessorBeforeInitializationpostProcessorAfterInitializationpostProcessorBeforeInitializationpostProcessorAfterInitializationpostProcessorBeforeInitialization初始化Bean...postProcessorAfterInitializationpostProcessorBeforeInitialization初始化beanpostProcessorAfterInitializationOrderServiceImpl{aByte=111, aShort=222, aInt=333, aLong=6666, aDouble=123.123, aFloat=233.567, aChar=c, aBoolean=true, aString='8大基本数据类型与String的注入测试'}1136497418postProcessorBeforeInitializationpostProcessorAfterInitializationOrderServiceImpl{aByte=111, aShort=222, aInt=333, aLong=6666, aDouble=123.123, aFloat=233.567, aChar=c, aBoolean=true, aString='8大基本数据类型与String的注入测试---prototype'}863125040UserServiceImpl{orderServiceImpl=OrderServiceImpl{aByte=111, aShort=222, aInt=333, aLong=6666, aDouble=123.123, aFloat=233.567, aChar=c, aBoolean=true, aString='8大基本数据类型与String的注入测试---prototype'}, beanName='userBean1'}113649741811364974181136497418----多例---postProcessorBeforeInitializationpostProcessorAfterInitialization949057310postProcessorBeforeInitializationpostProcessorAfterInitialization2024542466postProcessorBeforeInitializationpostProcessorAfterInitialization770189387