spring源码是一个困扰我很久的问题,这里面水又深有浑,跟一会就蒙圈了,好不容易找到了一点思路,时间一久,概念又模糊了,然后再去跟源码的时候又是一个恶心的循环。万般无奈,高仿一个spring来帮助下次快速梳理springIOC源码;
那就从第一次接触spring来梳理吧:
public static void main(String[] args) {ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(l");UserInfo userInfo = (UserInfo) Bean("userInfo");System.out.Name());}
第一次接触spring,就听说,对象不用new,直接从容器中拿就可以了,我想大多数人心中都会有一个这样的概念吧。就如上代码,spring
加载完xml
文件之后,直接getBean
方法就可以获取对象了,省去了new UserInfo()
。但是new ClassPathXmlApplicationContext(l")
又帮我们做了什么呢?
至此,对于如上3行代码进行高仿袖珍版springIOC容器,只是最简单的springIOC容器,如有不对或者不严谨的地方,请大家即使指出,我会尽快修改,免得误导别人。
既然高仿就要像一点,第一步要创建心脏 BeanFactory
了。然后核心接口一定要有的。
/*** Created by yangman on 2020/5/8.* 精简版本也要高仿的像一点 BeanFactory 不能少。*/
public interface BeanFactory {// 来两个核心方法得了,其他的暂时先不搞了。Object getBean(String name) throws IllegalAccessException, InstantiationException, NoSuchFieldException;<T> T getBean(String name, @Nullable Class<T> requiredType) throws IllegalAccessException, NoSuchFieldException, InstantiationException, NoSuchMethodException, InvocationTargetException;
}
接着创建子接口,HierarchicalBeanFactory
,ListableBeanFactory
,ApplicationContext
但是前两个用的不多,注重说ApplicationContext
吧。
网上对于ApplicationContext
接口有一个这样的说法,就是说如果比喻BeanFactory
是心脏,则ApplicationContext
就是躯干。由于这里只是高仿,大部分功能是用不到,所以不写了。
/*** Created by yangman on 2020/5/8.* beanFactory只有一些简单的,核心的功能,* ApplicationContext的接口就是一个全面化的接口,集结所有功能为一身。* 1.可以装配管理bean* 2.源码中实现了ResourcePatternResolver->ResourceLoader 接口,可以加载资源文件* 3.可以进行国际化处理等等*/
public interface ApplicationContext extends HierarchicalBeanFactory,ListableBeanFactory {}
接口有了,进行实现吧。创建ClassPathXmlApplicationContext
实现ApplicationContext
接口,注:spring源码中ClassPathXmlApplicationContext
类不是直接实现的ApplicationContext
接口。
public class ClassPathXmlApplicationContext implements ApplicationContext {public ClassPathXmlApplicationContext(String configLocation) {this(new String[] {configLocation});}public ClassPathXmlApplicationContext(String[] configLocations) {// 源码中做了相应的处理, 这里进行简化figLocations = configLocations;synchronized (this.startupShutdownMonitor) {try{// 读取配置文件Document document = loadConfig();//生成BeanDefinitioncreateBeanDefinition(document);// IOC注入IocAutoWrite();}catch (Exception ex){ex.printStackTrace();}}}
}
通过loadConfig
方法进行处理文件路径,并且加载配置。
应该还记得classpath
吧,在源码中Resources 可见真正的解析过程。
private Document loadConfig() throws ParserConfigurationException, IOException, SAXException {DocumentBuilderFactory factory = wInstance();DocumentBuilder docBuilder = wDocumentBuilder();// 这里其实是一个数组,只是为了演示,这里就不循环处理了。InputStream inputStream = FileInputStream(configLocations[0]);// 获取doc对象Document document= docBuilder.parse(inputStream);return document;}// 获取并处理文件路径private InputStream getFileInputStream(String configLocation) {// 真正的源码中spring 有对classpath* 和 classpath 这两个开头做了处理 Resources 可见int prefixEnd = (configLocation.startsWith("war:") ? configLocation.indexOf("*/") + 1 :configLocation.indexOf(':') + 1);// Class().getClassLoader().getResourceAsStream 这个函数寻找文件的起点是JAVA项目编译之后的根目录 ,也就是/target/class目录啦InputStream inputStream = Class().getClassLoader().getResourceAsStream(configLocation);return inputStream;}
可能有的小伙伴对与BeanDefinition
这个东西有点陌生,BeanDefinition
是用来存放bean的元数据的,比如bean的类名,属性,scope等信息。但是就疑问了?这些东西配置文件中(xml)原本就有呀,何必在存一份呢?这里可以打个比方,BeanDefinition
有如手机配件,bean
是手机,手机配件从工厂进行加工出来,需要进行检测才能放到手机上,xml中的信息就是刚刚加工出来的手机配件,BeanDefinition
就是检验合格的手机配件。xml
中的信息加载到BeanDefinition
中,才可以人为xml
配置的信息被成功解析。
xml文件读取之后,接下进行验证xml文件,并且初始化到BeanDefinition
中了。
通过createBeanDefinition
初始化BeanDefinition
。
BeanDefinition
是一个接口,这里用到AbstractBeanDefinition
进行实现,现在存储xml文件内容
/*** Created by likuo on 2020/5/9.* 高仿spring AbstractBeanDefinition.java*/
public class AbstractBeanDefinition implements BeanDefinition {// 类private volatile Object beanClass;// bean名称,用来.getBean()中的值private String beanName;// bean的属性值 主要用来存放 <property name="name" value="aa"></property> 这一段private MutablePropertyValues propertyValues;public Object getBeanClass() {return beanClass;}public void setBeanClass(Object beanClass) {this.beanClass = beanClass;}public String getBeanName() {return beanName;}public void setBeanName(String beanName) {this.beanName = beanName;}public MutablePropertyValues getPropertyValues() {return propertyValues;}public void setPropertyValues(MutablePropertyValues propertyValues) {this.propertyValues = propertyValues;}
}
主要完成从xml读取,并且生成BeanDefinition
,BeanDefinition中存入类名,bean名称,和属性值,相当于把配置文件完全读取到内存中。
// 源码中就比较复杂, 这里就从简了哈。
private void createBeanDefinition(Document document) throws ClassNotFoundException {// 获取根节点Element documentElement = DocumentElement();//这个方法名字也是高仿的,下载源码搜索就能搜索到此方法doRegisterBeanDefinitions(documentElement);
}
xml文件中有很多种标签,,,等,这里进行解析。
private void doRegisterBeanDefinitions(Element documentElement) throws ClassNotFoundException {// 看到这两个判断就豁然开朗了吧,此内容高仿 DefaultBeanDefinitionDocumentReader.parseDefaultElement方法,有兴趣的看一下哈。// 如果是<beans> 则解析下子元素if(NESTED_BEANS_ELEMENT.NodeName())){NodeList childNodes = ChildNodes();for(int i = 0 ;i&Length();i++){Node item = childNodes.item(i);if(item instanceof Element) {// beans下面应该是bean,这里进行循环解析doRegisterBeanDefinitions((Element)item);}}}else if(BEAN_ELEMENT.NodeName())){AbstractBeanDefinition beanDefinition = new AbstractBeanDefinition();// 解析id属性String beanName = Attribute(BeanDefinitionParserDelegate.ID_ATTRIBUTE);// 解析class属性String className = Attribute(BeanDefinitionParserDelegate.CLASS_ATTRIBUTE);// 设置值beanDefinition.setBeanClass(Class.forName(className));beanDefinition.setBeanName(beanName);// 如果有设置了值 property,则进行解析NodeList childNodes = ChildNodes();if(childNodes!=null){List<PropertyValue> propertyValueList = new ArrayList<>();for(int i = 0 ;i&Length();i++){Node item = childNodes.item(i);if(item instanceof Element) {Element property = (Element)item;if(BeanDefinitionParserDelegate.PROPERTY_ELEMENT.NodeName())){String propertyName = Attribute(BeanDefinitionParserDelegate.NAME_ATTRIBUTE);String propertyValue = Attribute(BeanDefinitionParserDelegate.VALUE_ATTRIBUTE);propertyValueList.add(new PropertyValue(propertyName,propertyValue));}}}beanDefinition.setPropertyValues(new MutablePropertyValues(propertyValueList));}// 保存beanDefinitionbeanDefinitionMap.put(beanName,beanDefinition);// 获取的beanName放到集合中beanDefinitionNames.add(beanName);}}
通过IocAutoWrite()
进行IOC注入,主要是通过BeanDefinition转换成bean对象,并且放到map中。原本bean是一个代理对象,但是这里就从简,直接就放真实对象了。
// 这里进行注入private void IocAutoWrite() throws IllegalAccessException, NoSuchFieldException, InstantiationException {System.out.println("进行注入");for(String beanName:beanDefinitionNames){Bean(beanName);}}
@Overridepublic Object getBean(String name) {// 如果存在则直接返回ainsKey(name)){(name);}else{Object o = null;try {o = doCreateBean(name);} catch (IllegalAccessException e) {e.printStackTrace();} catch (InstantiationException e) {e.printStackTrace();} catch (NoSuchFieldException e) {e.printStackTrace();} catch (NoSuchMethodException e) {e.printStackTrace();} catch (InvocationTargetException e) {e.printStackTrace();}return o;}}
在spring中,一般do开头的方法都需要重点的看下, 里面会实现很多逻辑。这里也高仿真一下。
// 高仿 DefaultListableBeanFactoryprivate volatile List<String> beanDefinitionNames = new ArrayList<>(256);//高仿DefaultSingletonBeanRegistry 用来存放单例的对象private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256); private Object doCreateBean(String name) throws IllegalAccessException, InstantiationException, NoSuchFieldException, NoSuchMethodException, InvocationTargetException {// 现在只处理单例情况AbstractBeanDefinition beanDefinition = (name);// 这次不说代理的情况,原本是使用cglib进行代理,这里简化下// 创建对象Class clazz = (Class) BeanClass();Object o = wInstance();// 设置属性 注意, 真实spring并不是这么设置的。。PropertyValues()!=null){List<PropertyValue> propertyValueList = PropertyValues().getPropertyValueList();for(PropertyValue propertyValue:propertyValueList){// 获取set方法的名称String setMethod = Name());// 通过调用set方法进行赋值// Method method = Method(setMethod,String.class);method.invoke(Value());}}// 用来存放单例bean的mapsingletonObjects.put(name,o);return o;}
至此IOC注入完成。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns=""xmlns:xsi=""xmlns:p=""xsi:schemaLocation="://www.springframework/schema/beans/spring-beans.xsd"><bean id="userInfo" class="ity.UserInfo"><property name="name" value="张三"></property></bean>
</beans>
创建xml文件
public class UserInfo {public Long id;public String name;public Long getId() {return id;}public void setId(Long id) {this.id = id;}public String getName() {return name;}public void setName(String name) {this.name = name;}
}
public class Test {public static void main(String[] args) {ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext(l");UserInfo userInfo = (UserInfo) Bean("userInfo");System.out.Name());}
}
输出:
加载配置文件
初始化BeanDefinition
进行注入
张三
git地址:
本文发布于:2024-01-31 05:00:01,感谢您对本站的认可!
本文链接:https://www.4u4v.net/it/170664840425712.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
留言与评论(共有 0 条评论) |