# mini-spring **Repository Path**: hyp_344/mini-spring ## Basic Information - **Project Name**: mini-spring - **Description**: No description available - **Primary Language**: Unknown - **License**: Apache-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 2 - **Forks**: 0 - **Created**: 2023-11-29 - **Last Updated**: 2025-04-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: Java, Spring, Spring-MVC ## README # mini-spring ## 介绍 简版Spring ### 程序入口 #### 为了目录结构层次更清晰,spring中的每个模块都单独写在了不同的目录下,但他们不是完全隔离的,比如aop目录会使用到ioc里的东西 #### 1.IOC入口 -> com.ypan.mini.ioc.test目录下的Test1类,运行main方法可以看到简版IOC的注入逻辑 #### 2.JDBC入口 -> com.ypan.mini.jdbc.test类 #### 3.MVC入口 -> com.ypan.mini.web.tomcat.StartTomcat启动此类 会运行一个tomcat容器并且会使用servlet的listener机制把ioc容器启动并注入 #### 4.AOP入口 -> 启动Tomcat后,这个接口http://localhost:8080/testaop(接口定义在com.ypan.mini.web.test.HelloWorldBean类中)用于测试aop是否生效,相关类实现在com.ypan.mini.aop下,配置类写在application.xml文件中 测试一个简单的http请求,启动Tomcat之后,在浏览器输入http://localhost:8080/test ### 使用手册 #### @Autowired注解的使用 1.在ioc的配置文件中加入声明信息 src/main/resources/ioc/applicationContext.xml 2.在某个bean使用@Autowired注解注入你配置的bean ### 原理分析 #### @Autowired注解原理 ioc容器启动时会加载解析配置文件成为一个beanDefinition放入容器 当解析到这个属性上有@Autowired注解的时候,会调用getBean方法获取,如果获取不到则根据beanDefinition重新new出来放进ioc容器里 #### @Required注解原理 todo #### IOC容器启动流程 1. 通过ClassPathXmlApplicationContext类加载类路径下applicationContext.xml配置文件(里面是一些bean的配置元信息beanName、class、属性参数、构造器参数等等) 2. 通过dom4j提供的API把applicationContext.xml配置文件里的信息封装成beanDefinition对象并通过一个容器(Map maps)保存在程序中 如果此bean不是懒加载则直接调用getBean()方法创建实例
如果是懒加载则等待整个context注入完成后通过refresh()方法一次性创建出所有的bean
目前我们程序里把懒加载全都设置为true,因为目前程序还不完善 如果懒加载设置为false当封装第一个beanDefinition对象后就开始创建bean实例
如果这个bean有引用类型的属性,则会递归创建这个引用类型的实例,但是我们程序才读入一个beanDefinition对象,并没有这个引用类型的beanDefinition对象
就会发生空指针的报错
3. 解析完配置文件后,开始调用refresh()方法,refresh()方法有如下步骤: 3.1 postProcessBeanFactory(configurableListableBeanFactory)
3.2 registerBeanPostProcessors(configurableListableBeanFactory)
注册beanPostProcessors,目前我们写了两个beanPostProcessor: AutowiredAnnotationBeanPostProcessor:注解@Autowired的逻辑,标在一个属性上会自动注入该属性对象 RequireAnnotationBeanPostProcessor:注解@Required的逻辑,当一个属性标有该注解时,会检验该注解能否为空
3.3 initApplicationEventPublisher() 初始化事件发布者
3.4 onRefresh() 底层会调用beanFactory的refresh()方法,方法里会循环遍历beanDefinitionNames,循环调用getBean(beanDefinitionName)方法 这里就是创建bean实例的逻辑了 创建bean的详细逻辑为:
3.4.1 尝试从容器中获取该bean,如果没有则从毛坯容器中获取该bean,如果还没有则走新建bean的逻辑
3.4.2 先创建毛胚bean实例 使用构造器(如果beanDefinition中有参构造器则使用有参构造器创建bean实例,否则使用无参构造器实例化) 实例化bean 把通过构造器实例化的bean放到毛坯容器中,接着完善该bean的属性,如果有属性是引用类型,则递归创建bean 到这里一个Bean就创建好了,放入容器中。
3.4.3 接着调用applyBeanPostProcessorsBeforeInitialization(singleton, beanName)方法,这个就是@Autowire注解生效的逻辑
3.4.4 如果该beanDefinition中有初始化方法则调用invokeInitMethod(beanDefinition, singleton)
3.4.5 调用applyBeanPostProcessorsAfterInitialization(singleton, beanName)方法后置处理器,这个就是我们@Require注解生效的逻辑 到这里Bean已经创建完成了 如果标有@Required注解的属性还没满足我们的要求,那么就要抛出异常
3.5 registerListeners() 注册事件监听者
3.6 finishRefresh() 完成ioc容器的启动,这里会发布一个事件publishEvent(new ContextRefreshEvent("Context Refreshed...")); 事件监听着收到此事件后,就会把该信息打印到控制台上
#### web容器启动流程 MVC 的基本思路是屏蔽 Servlet 的概念,让程序员主要写业务逻辑代码。浏览器访问的 URL 通过映射机制找到实际的业务逻辑方法。 按照 Servlet 规范,可以通过 Filter 拦截,也可以通过 Servlet 拦截。MiniSpring 的实现过程中,模仿 Spring MVC 通过 Servlet 拦截所有请求, 处理映射关系,调用业务逻辑代码,处理返回值回递给浏览器。程序员写的业务逻辑程序,也叫做 Bean。 当 Servlet 服务器如 Tomcat 启动的时候,要遵守下面的时序。 1. 在启动 Web 项目时,Tomcat 会读取 web.xml 中的 comtext-param 节点,获取这个 Web 应用的全局参数。 2. Tomcat 创建一个 ServletContext 实例,是全局有效的。 3. 将 context-param 的参数转换为键值对,存储在 ServletContext 里。 4. 创建 listener 中定义的监听类的实例,按照规定 Listener 要继承自 ServletContextListener。监听器初始化方法是 contextInitialized(ServletContextEvent event)。初始化方法中可以通过 event.getServletContext().getInitParameter(“name”) 方法获得上下文环境中的键值对。 5. 当 Tomcat 完成启动,也就是 contextInitialized 方法完成后,再对 Filter 过滤器进行初始化。 6. servlet 初始化:有一个参数 load-on-startup,它为正数的值越小优先级越高,会自动启动,如果为负数或未指定这个参数,会在 servlet 被调用时再进行初始化。init-param 是一个 servlet 整个范围之内有效的参数,在 servlet 类的 init() 方法中通过 this.getInitParameter(″param1″) 方法获得。 **Web环境启动ioc容器原理分析**
由上述服务器启动过程我们知道,我们把 web.xml 文件里定义的元素加载过程简单归总一下:先获取全局的参数 context-param 来创建上下文,之后如果配置文件里定义了 Listener,那服务器会先启动它们,之后是 Filter,最后是 Servlet。 因此我们可以利用这个时序,把容器的启动放到 Web 应用的 Listener 中。Spring MVC 就是这么设计的,它按照这个规范, 用 ContextLoaderListener 来启动容器。我们也模仿它同样来实现这样一个Listener。
**ContextLoaderListener监听器实现分析** 1. 从web.xml配置文件获取ioc容器配置文件路径,并使用mvc容器专用的应用程序上下文类加载(点击此类发现其实还是继承了ioc容器的上下文类) 2.