MVC架构探究及其源码实现(3)-WebApplicationContext

简介:

博学,切问,近思--詹子知 (https://jameszhan.github.io)

直接利用web.xml去配置和定义我们的对象组件显然是不灵活和不方便扩展的,由于我们系统中将会需要配置很多个不同的对象资源,比如控制器,View对象,HandlerMapping对象等等,如何对它们进行管理,如何能让我们的前端控制器访问和利用到到它们便是我们不得不面对的问题。还好,现在有了Spring,现在很多流行的MVC框架都支持使用Spring对自己容器里的对象资源进行管理。尽管Spring千好万好,我们这里还是决定不使用它,而是自己来写一个对象容器来管理我们的相关资源,这样我们不仅可以了解对象资源配置管理的细节,还可以顺带学习一下Spring等IOC容器的实现原理。当然,我们这里的实现方案将会尽可能的简单。
如下便是我们的WebApplicationContext类的实现,它能够自动查找WEB-INF路径下所有以config结尾的xml文件,并把其中定义的对象抽取出来,放到Application作用域中,由于我们这里只是个Sample,不需要考虑太多的并发的情况,所有对象的类型,我们都是用Singleton,也就是定义的每个对象都为所有的请求和Servlet共享。 WebApplicationContext中还定义了一个很有用的方法,beansOfType(Class<T>),该方法用于查找出系统中定义的所有的属于当前类型的所有对象资源。package com.google.mvc.web.context; import java.io.InputStream; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Locale; import java.util.Map; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import javax.servlet.ServletContext; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.parsers.ParserConfigurationException; import org.apache.log4j.Logger; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; public class WebApplicationContext { private static final Logger LOGGER = Logger.getLogger(WebApplicationContext.class); private static final String WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".CONTEXT"; private Map<String, Object> cacheMap; private ServletContext servletContext; private DocumentBuilder builder; public WebApplicationContext(ServletContext servletContext) { this.servletContext = servletContext; DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); try { builder = factory.newDocumentBuilder(); } catch (ParserConfigurationException e) { LOGGER.error("Can't load dom builder", e); } } public void init() { ServletContext context = getServletContext(); Set<?> set = context.getResourcePaths("/WEB-INF"); Object map = servletContext.getAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE); if (map != null) { cacheMap = (Map<String, Object>) map; } else { cacheMap = new ConcurrentHashMap<String, Object>(); servletContext.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, cacheMap); for (Object o : set) { String path = (String) o; if (path.endsWith("config.xml")) { try { loadResource(servletContext.getResourceAsStream(path)); } catch (Exception ex) { LOGGER.error("Can't load resource " + path); } } } } } private void loadResource(InputStream resource) throws Exception{ Document doc = builder.parse(resource); Element root = doc.getDocumentElement(); NodeList nodeList = root.getElementsByTagName("bean"); for(int i = 0; i < nodeList.getLength(); i++){ Element el = (Element)nodeList.item(i); String id = el.getAttribute("id"); String className = el.getAttribute("class"); Class<?> clazz = this.getClass().getClassLoader().loadClass(className); Object o = createBean(id, clazz); NodeList propertyList = el.getElementsByTagName("property"); for(int j = 0; j < propertyList.getLength(); j++){ Element prop = (Element)propertyList.item(j); String methodName = getMethodName(prop.getAttribute("name")); Method m = clazz.getMethod(methodName, String.class); String property = prop.getAttribute("value"); Object dependObject = cacheMap.get(property); if(dependObject != null){ m.invoke(o, dependObject); } else { m.invoke(o, property); } } cacheMap.put(id, o); } } protected String getMethodName(String methodName){ StringBuilder sb = new StringBuilder(); sb.append("set"); sb.append(methodName.substring(0, 1).toUpperCase(Locale.US)); sb.append(methodName.substring(1)); return sb.toString(); } public Object createBean(Class<?> clazz) throws Exception{ return createBean(clazz.getCanonicalName(), clazz); } public Object createBean(String name, Class<?> clazz) throws Exception{ Object o = cacheMap.get(name); if(o == null){ o = clazz.newInstance(); if(o instanceof WebApplicationContextAware){ ((WebApplicationContextAware)o).setWebApplicationContext(this); } cacheMap.put(name, o); } LOGGER.info(name + "=" + clazz.getCanonicalName()); return o; } public Object getBean(String beanName){ return servletContext.getAttribute(beanName); } public ServletContext getServletContext() { return servletContext; } public void setServletContext(ServletContext servletContext) { this.servletContext = servletContext; } public <T> Map<String, T> beansOfType(Class<T> clazz){ Map<String, T> map = new HashMap<String, T>(); for(String key : cacheMap.keySet()){ Object o = cacheMap.get(key); if(clazz.isAssignableFrom(o.getClass())){ map.put(key, (T)o); } } return map; } } 我们再来看一下*.config.xml文件里对象资源的定义示例:<?xml version="1.0" encoding="UTF-8"?> <beans> <bean id="ControllerAdapter" class="com.google.mvc.web.servlet.mvc.ControllerHandlerAdapter" /> <bean id="HttpRequestAdapter" class="com.google.mvc.web.servlet.mvc.HttpRequestHandlerAdapter" /> <bean id="ViewResolver" class="com.google.mvc.web.servlet.mvc.DefaultViewResolver"> <property name="viewClass" value="com.google.mvc.web.servlet.mvc.InternalResourceView"/> <property name="prefix" value="/WEB-INF/"/> <property name="suffix" value=".jsp"/> </bean> <bean id="login.do" class="com.google.mvc.web.sample.LoginController" /> <bean id="hello.do" class="com.google.mvc.web.sample.HelloController" /> <bean id="404" class="com.google.mvc.web.servlet.mvc.HandlerFor404" /> </beans> 如果哪个对象资源需要在运行过程中使用到WebApplicationContext的资源及方法,只需实现接口WebApplicationContextAware即可,一旦实现了该接口,当前的WebApplicationContext会被自动的注入到此对象资源中。package com.google.mvc.web.context; public interface WebApplicationContextAware { void setWebApplicationContext(WebApplicationContext wac); }

相关文章:

  1. MVC架构探究及其源码实现(1)-理论基础
  2. MVC架构探究及其源码实现(2)-核心组件定义
  3. MVC架构探究及其源码实现(4)-前端控制器
  4. MVC架构探究及其源码实现(5)-相关组件实现
  5. MVC架构探究及其源码实现(6)-简单示例

目录
相关文章
|
12天前
|
JSON JavaScript 前端开发
Vue3源码架构简析及Monorepo流程构建
【10月更文挑战第12天】Vue3源码架构简析及Monorepo流程构建
Vue3源码架构简析及Monorepo流程构建
|
3天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
3月前
|
监控 网络协议 Java
Tomcat源码解析】整体架构组成及核心组件
Tomcat,原名Catalina,是一款优雅轻盈的Web服务器,自4.x版本起扩展了JSP、EL等功能,超越了单纯的Servlet容器范畴。Servlet是Sun公司为Java编程Web应用制定的规范,Tomcat作为Servlet容器,负责构建Request与Response对象,并执行业务逻辑。
Tomcat源码解析】整体架构组成及核心组件
|
3月前
|
设计模式 前端开发 数据库
哇塞!Rails 的 MVC 架构也太牛了吧!快来看看这令人惊叹的编程魔法,开启新世界大门!
【8月更文挑战第31天】《Rails中的MVC架构解析》介绍了Ruby on Rails框架核心的MVC设计模式,通过模型(Model)、视图(View)和控制器(Controller)三部分分离应用逻辑,利用Active Record进行数据库操作,ERB模板渲染视图,以及控制器处理用户请求与业务逻辑,使代码更易维护和扩展,提升团队开发效率。
66 0
|
2月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
335 37
|
19天前
|
Java Spring
Spring底层架构源码解析(三)
Spring底层架构源码解析(三)
|
19天前
|
XML Java 数据格式
Spring底层架构源码解析(二)
Spring底层架构源码解析(二)
独家直播|DB-GPT架构设计与源码解读(第一期)
🚀 DB-GPT首期源码解读系列上线啦! 10.8 晚7点,与DB-GPT项目发起人陈发强一起,深入探索DB-GPT的架构设计与源码解读。 🔎 直播看点: ● 架构全剖析:从设计思考到架构逻辑,全面剖析DB-GPT。 ● 源码速度解读:多模型管理、智能体、RAG、AWEL等核心模块一网打尽。 ● 项目作者面对面:陈发强,蚂蚁集团DB-GPT开源项目发起人,分享实战经验与洞见。 ● 有问必答:围绕DB-GPT的使用问题有问必答,线上帮你解issue! 👉 立即扫码预约,与DB-GPT作者零距离交流!
|
2月前
|
设计模式 前端开发 数据库
理解mvc架构
mvc架构
23 4
|
3月前
|
设计模式 存储 前端开发
MVC革命:如何用一个设计模式重塑你的应用架构,让代码重构变得戏剧性地简单!
【8月更文挑战第22天】自定义MVC(Model-View-Controller)设计模式将应用分为模型、视图和控制器三个核心组件,实现关注点分离,提升代码可维护性和扩展性。模型管理数据和业务逻辑,视图负责数据显示与用户交互,控制器处理用户输入并协调模型与视图。通过示例代码展示了基本的MVC框架实现,可根据需求扩展定制。MVC模式灵活性强,支持单元测试与多人协作,但需注意避免控制器过度复杂化。
39 1