Webx命名规范

Web请求响应


当Webx Framework接收到一个来自WEB的请求以后,实际上它主要做了两件事:
首先,它会增强request、response、session的功能,并把它们打包成更易使用的RequestContext对象。
其次,它会调用相应子应用的pipeline,用它来做进一步的处理。
假如在上面的过程中出现异常,则会触发Webx Framework处理异常的过程。
此外,Webx Framework还提供了一组辅助开发的功能,例如查看环境变量,查看schema等。这些功能只在开发模式生效,生产模式下自动关闭。

public class WebxFrameworkFilter extends FilterBean {private final Logger log = LoggerFactory.getLogger(this.getClass());private String parentContextAttribute;private WebxComponents components;private RequestURIFilter excludeFilter;private RequestURIFilter passthruFilter;private String internalPathPrefix;...protected final void init() throws Exception {WebApplicationContext parentContext = this.findParentContext();if(parentContext instanceof WebxComponentsContext) {this.components = ((WebxComponentsContext)parentContext).getWebxComponents();WebxConfiguration rootController = this.components.getParentWebxConfiguration();if(rootController != null) {this.internalPathPrefix = rootController.getInternalPathPrefix();this.internalPathPrefix = FileUtil.normalizeAbsolutePath(this.internalPathPrefix, true);}}WebxRootController rootController1 = this.components.getWebxRootController();if(this.passthruFilter != null) {if(rootController1 instanceof PassThruSupportable) {((PassThruSupportable)rootController1).setPassthruFilter(this.passthruFilter);} else {...}}}...@Overrideprotected void doFilter(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws IOException, ServletException {String path = getResourcePath(request);if (isExcluded(path)) {...return;} else {...}try {// 获取WebxRootController并且对request进行处理getWebxComponents().getWebxRootController().service(request, response, chain);} catch (IOException e) {throw e;} catch (ServletException e) {throw e;} catch (Exception e) {throw new ServletException(e);}}..
}

WebxRootController为接口,有一个实现了部分函数的抽象类AbstractWebxRootController

    public final void service(HttpServletRequest request, HttpServletResponse response, FilterChain chain)throws Exception {RequestContext requestContext = null;try {requestContext = assertNotNull(getRequestContext(request, response), "could not get requestContext");// 如果请求已经结束,则不执行进一步的处理。例如,当requestContext已经被重定向了,则立即结束请求的处理。if (isRequestFinished(requestContext)) {return;}// 请求未结束,则继续处理...request = requestContext.getRequest();response = requestContext.getResponse();// 如果是一个内部请求,则执行内部请求// webx内部请求,如报错,开发者模式页面if (handleInternalRequest(request, response)) {return;}// 如果不是内部的请求,并且没有被passthru,则执行handleRequest// handleRequest抽象方法if (isRequestPassedThru(request) || !handleRequest(requestContext)) {// 如果请求被passthru,或者handleRequest返回false(即pipeline放弃请求),// 则调用filter chain,将控制交还给servlet engine。giveUpControl(requestContext, chain);}} catch (Throwable e) {handleException(requestContext, request, response, e);} finally {commitRequest(requestContext);}}
package com.alibaba.citrus.webx.impl;import javax.servlet.http.HttpServletRequest;
import com.alibaba.citrus.service.requestcontext.RequestContext;
import com.alibaba.citrus.util.ServletUtil;
import com.alibaba.citrus.webx.WebxComponent;
import com.alibaba.citrus.webx.support.AbstractWebxRootController;
import com.alibaba.citrus.webx.util.WebxUtil;public class WebxRootControllerImpl extends AbstractWebxRootController {// 处理外部请求@Overrideprotected boolean handleRequest(RequestContext requestContext) throws Exception {// WebxFrameWorkFilter封装的request+response+sessionHttpServletRequest request = requestContext.getRequest();// Servlet mapping有两种匹配方式:前缀匹配和后缀匹配。// 对于前缀匹配,例如:/servlet/aaa/bbb,servlet path为/servlet,path info为/aaa/bbb// 对于前缀匹配,当mapping pattern为/*时,/aaa/bbb,servlet path为"",path info为/aaa/bbb// 对于后缀匹配,例如:/aaa/bbb.html,servlet path为/aaa/bbb.html,path info为null//// 对于前缀匹配,取其pathInfo;对于后缀匹配,取其servletPath。String path = ServletUtil.getResourcePath(request);// 再根据path查找componentWebxComponent component = getComponents().findMatchedComponent(path);boolean served = false;if (component != null) {try {WebxUtil.setCurrentComponent(request, component);served = component.getWebxController().service(requestContext);} finally {WebxUtil.setCurrentComponent(request, null);}}return served;}
}
public WebxComponent findMatchedComponent(String path) {if (!path.startsWith("/")) {path = "/" + path;}WebxComponent defaultComponent = getDefaultComponent();WebxComponent matched = null;// 前缀匹配componentPath。// 子应用for (WebxComponent component : this) {if (component == defaultComponent) {continue;}String componentPath = component.getComponentPath();if (!path.startsWith(componentPath)) {continue;}// path刚好等于componentPath,或者path以componentPath/为前缀if (path.length() == componentPath.length() || path.charAt(componentPath.length()) == '/') {matched = component;break;}}// fallback to default componentif (matched == null) {matched = defaultComponent;}return matched;}
public class PipelineImpl extends AbstractService<Pipeline> implements Pipeline {private Valve[] valves;private String  label;.../** 实现<code>PipelineContext</code>。 */private final class PipelineContextImpl implements PipelineContext, PipelineInvocationHandle {private final Logger log = getLogger();private final PipelineContext parentContext;private final int             level;private int executedIndex  = -1;private int executingIndex = -1;private boolean             broken;private Map<String, Object> attributes;public PipelineContextImpl(PipelineContext parentContext) {this.parentContext = parentContext;if (parentContext == null) {this.level = 1;} else {this.level = parentContext.level() + 1;}}public int level() {return level;}public int index() {return executingIndex + 1;}public int findLabel(String label) throws LabelNotDefinedException {label = assertNotNull(trimToNull(label), "no label");boolean isTop = TOP_LABEL.equals(label);if (isTop && parentContext == null) {return 0;} else if (label.equals(getLabel())) {return 0;} else if (parentContext != null) {return parentContext.findLabel(label) + 1;} else {throw new LabelNotDefinedException("Could not find pipeline or sub-pipeline with label \"" + label+ "\" in the pipeline invocation stack");}}public void invokeNext() {assertInitialized();if (broken) {return;}try {executingIndex++;if (executingIndex <= executedIndex) {throw new IllegalStateException(descCurrentValve() + " has already been invoked: "+ valves[executingIndex]);}executedIndex++;if (executingIndex < valves.length) {Valve valve = valves[executingIndex];try {if (log.isTraceEnabled()) {log.trace("Entering {}: {}", descCurrentValve(), valve);}valve.invoke(this);} catch (PipelineException e) {throw e;} catch (Exception e) {throw new PipelineException("Failed to invoke " + descCurrentValve() + ": " + valve, e);} finally {if (log.isTraceEnabled()) {log.trace("...Exited {}: {}", descCurrentValve(), valve);}}if (executedIndex < valves.length && executedIndex == executingIndex) {if (log.isTraceEnabled()) {log.trace("{} execution was interrupted by {}: {}", new Object[] { descCurrentPipeline(),descCurrentValve(), valve });}}} else {if (log.isTraceEnabled()) {log.trace("{} reaches its end.", descCurrentPipeline());}}} finally {executingIndex--;}}public void breakPipeline(int levels) {assertTrue(levels >= 0 && levels < level, "invalid break levels: %d, should be in range of [0, %d)",levels, level);broken = true;if (levels > 0) {parentContext.breakPipeline(levels - 1);}}public void breakPipeline(String label) throws LabelNotDefinedException {breakPipeline(findLabel(label));}public boolean isBroken() {return broken;}public boolean isFinished() {return !broken && executedIndex >= valves.length;}public void invoke() throws IllegalStateException {assertTrue(!isBroken(), ILLEGAL_STATE, "cannot reinvoke a broken pipeline");executingIndex = executedIndex = -1;invokeNext();}public Object getAttribute(String key) {Object value = null;if (attributes != null) {value = attributes.get(key);}if (value == null && parentContext != null) {value = parentContext.getAttribute(key);}return value == NULL_PLACEHOLDER ? null : value;}public void setAttribute(String key, Object value) {if (attributes == null) {attributes = createHashMap();}attributes.put(key, defaultIfNull(value, NULL_PLACEHOLDER));}@Overridepublic String toString() {return "Executing Pipeline " + descCurrentValve();}private String descCurrentPipeline() {return "Pipeline[level " + level() + "]";}private String descCurrentValve() {return "Valve[#" + index() + "/" + valves.length + ", level " + level() + "]";}}}

页面布局


Screen,代表页面的主体。
Layout,代表页面的布局。
Control,代表嵌在screen和layout中的页面片段。

处理页面的基本流程

Webx Turbine的处理流程被定义在pipeline中。Webx Framework没有规定Pipeline的内容,但Webx Turbine却定义了一系列valves。

下面是一个Webx Turbine推荐的pipeline配置:

<services:pipeline xmlns="http://www.alibaba.com/schema/services/pipeline/valves"><!-- 初始化turbine rundata,并在pipelineContext中设置可能会用到的对象(如rundata、utils),以便valve取得。 --><prepareForTurbine /><!-- 设置日志系统的上下文,支持把当前请求的详情打印在日志中。 --><setLoggingContext /><!-- 分析URL,取得target。 --><analyzeURL homepage="homepage" /><!-- 检查csrf token,防止csrf攻击和重复提交。假如request和session中的token不匹配,则出错,或显示expired页面。 --><checkCsrfToken /><loop><choose><when><!-- 执行带模板的screen,默认有layout。 --><pl-conditions:target-extension-condition extension="null, vm, jsp" /><performAction /><performTemplateScreen /><renderTemplate /></when><when><!-- 执行不带模板的screen,默认无layout。 --><pl-conditions:target-extension-condition extension="do" /><performAction /><performScreen /></when><otherwise><!-- 将控制交还给servlet engine。 --><exit /></otherwise></choose><!-- 假如rundata.setRedirectTarget()被设置,则循环,否则退出循环。 --><breakUnlessTargetRedirected /></loop></services:pipeline>

假设用户以URL:http://localhost:8081/来访问Webx应用。域名和端口不重要,取决于应用服务器的配置,这里假设为localhost:8081。Webx Framework的处理流程,从WebxFrameworkFilter接收请求,并且一路顺利到达pipeline。然后Pipeline开始依次执行它的valves。(下面的描述略过一些相对次要的步骤。)
- 分析URL

public void invoke(PipelineContext pipelineContext) throws Exception {TurbineRunDataInternal rundata = (TurbineRunDataInternal) getTurbineRunData(request);String target = null;// 取得target,并转换成统一的内部后缀名。String pathInfo = ServletUtil.getResourcePath(rundata.getRequest()).substring(component.getComponentPath().length());if ("/".equals(pathInfo)) {pathInfo = getHomepage();}// 注意,必须将pathInfo转换成camelCase。int lastSlashIndex = pathInfo.lastIndexOf("/");if (lastSlashIndex >= 0) {pathInfo = pathInfo.substring(0, lastSlashIndex) + "/"+ StringUtil.toCamelCase(pathInfo.substring(lastSlashIndex + 1));} else {pathInfo = StringUtil.toCamelCase(pathInfo);}target = mappingRuleService.getMappedName(EXTENSION_INPUT, pathInfo);rundata.setTarget(target);// 取得actionString action = StringUtil.toCamelCase(trimToNull(rundata.getParameters().getString(actionParam)));action = mappingRuleService.getMappedName(ACTION_MODULE, action);rundata.setAction(action);// 取得actionEventString actionEvent = ActionEventUtil.getEventName(rundata.getRequest());rundata.setActionEvent(actionEvent);pipelineContext.invokeNext();}

分析URL的目的是取得target。由于用户访问的URL中并没有提供path信息,通常被理解为:用户想要访问“主页”。AnalyzeURL valve提供了一个可选的参数“homepage”,即是在这种情况下起作用 —— http://localhost:8081/对应的target为“homepage”。
需要注意的是,target不代表模板名,也不代表类名。Target只是一个抽象的概念 —— 当前页面需要达成的目标。Target可能被后续的valves解释成模板名、类名或者其它东西。
进入 - 多重分支
很明显,“homepage”满足了第一个所附带的条件:,意思是target的后缀不存在(null)或为“jsp”或为“vm”。
- 执行action

public void invoke(PipelineContext pipelineContext) throws Exception {TurbineRunData rundata = getTurbineRunData(request);// 检查重定向标志,如果是重定向,则不需要将页面输出。if (!rundata.isRedirected()) {String action = rundata.getAction();// 如果找到action,则执行之。if (!StringUtil.isEmpty(action)) {String actionKey = "_action_" + action;// 防止重复执行同一个action。if (rundata.getRequest().getAttribute(actionKey) == null) {rundata.getRequest().setAttribute(actionKey, "executed");try {moduleLoaderService.getModule(ACTION_MODULE, action).execute();} catch (ModuleLoaderException e) {throw new PipelineException("Could not load action module: " + action, e);} catch (Exception e) {throw new PipelineException("Failed to execute action module", e);}}}}pipelineContext.invokeNext();}

和其它框架中的action概念不同,在Webx Turbine中,action是用来处理用户提交的表单的。
因为本次请求未提供action参数,所以跳过该步骤。
- 查找并执行screen。
继承于performScreen

public void invoke(PipelineContext pipelineContext) throws Exception {TurbineRunData rundata = getTurbineRunData(request);// 检查重定向标志,如果是重定向,则不需要将页面输出。if (!rundata.isRedirected()) {setContentType(rundata);Object result = null;try {result = performScreenModule(rundata);} finally {setOutputValue(pipelineContext, result);}}pipelineContext.invokeNext();}

这里要用到一个规则:target映射成screen module类名的规则。
假设target为xxx/yyy/zzz,那么Webx Turbine会依次查找下面的

screen模块:
screen.xxx.yyy.Zzz,
screen.xxx.yyy.Default,
screen.xxx.Default,
screen.Default。

本次请求的target为homepage,因此它会尝试查找screen.Homepage和screen.Default这两个类。
如果找到screen类,Webx Turbine就会执行它。Screen类的功能,通常是读取数据库,然后把模板所需要的对象放到context中。
如果找不到,也没关系 —— 这就是“页面优先”:像homepage这样的主页,通常没有业务逻辑,因此不需要screen类,只需要有模板就可以了。
- 渲染模板

    public void invoke(PipelineContext pipelineContext) throws Exception {TurbineRunDataInternal rundata = (TurbineRunDataInternal) getTurbineRunData(request);String target = assertNotNull(rundata.getTarget(), "Target was not specified");// 检查重定向标志,如果是重定向,则不需要将页面输出。if (!rundata.isRedirected()) {Context context = rundata.getContext();renderTemplate(getScreenTemplate(target), context, rundata);// layout可被禁用。if (rundata.isLayoutEnabled() && bufferedRequestContext.isBuffering()) {String layoutTemplateOverride = rundata.getLayoutTemplateOverride();if (layoutTemplateOverride != null) {target = layoutTemplateOverride;}String layoutTemplate = getLayoutTemplate(target);if (templateService.exists(layoutTemplate)) {String screenContent = defaultIfNull(bufferedRequestContext.popCharBuffer(), EMPTY_STRING);context.put(SCREEN_PLACEHOLDER_KEY, screenContent);renderTemplate(layoutTemplate, context, rundata);}}}pipelineContext.invokeNext();}

这里用到两个规则:target映射成screen template,以及target映射成layout template。
假设target为xxx/yyy/zzz,那么Webx Turbine会查找下面的screen模板:/templates/screen/xxx/yyy/zzz。Screen模板如果未找到,就会报404 Not Found错误。 找到screen模板以后,Webx Turbine还会试着查找下面的layout模板:
/templates/layout/xxx/yyy/zzz
/templates/layout/xxx/yyy/default
/templates/layout/xxx/default
/templates/layout/default
Layout模板如果找不到,就直接渲染screen模板;如果存在,则把渲染screen模板后的结果,嵌入到layout模板中。
Layout模板和screen模板中,都可以调用control。每个页面只有一个screen,却可以有任意多个controls。
- 内部重定向
在screen和action中,可以进行“内部重定向”。内部重定向实质上就是由实施的 —— 如果没有重定向标记,就退出;否则循环到标签。
和外部重定向不同,外部重定向是向浏览器返回一个302或303 response,其中包含Location header,浏览器看到这样的response以后,就会发出第二个请求。而内部重定向发生在pipeline内部,浏览器并不了解内部重定向。

WebxApplicationContext:

WebxApplicationContext 继承于Spring的XmlWebApplicationContext:

/*** 用于webx框架的application context。* <ul>* <li>扩展了Spring的* {@link org.springframework.web.context.support.XmlWebApplicationContext}* ,添加了SpringExt的支持,包括configuration point以及resource loading扩展。</li>* <li>修改了默认的配置文件名:<code>/WEB-INF/webx-*.xml</code>。</li>* </ul>** @author Michael Zhou*/
public class WebxApplicationContext extends ResourceLoadingXmlWebApplicationContext {/*** 取得默认的Spring配置文件名。* <ul>* <li>Root context默认配置文件为<code>/WEB-INF/webx.xml</code>;</li>* <li>假设component名称为<code>"test"</code> ,其默认的配置文件是* <code>/WEB-INF/webx-test.xml</code>。</li>* </ul>*/@Overrideprotected String[] getDefaultConfigLocations() {if (getNamespace() != null) {return new String[] { WEBX_COMPONENT_CONFIGURATION_LOCATION_PATTERN.replace("*", getNamespace()) };} else {return new String[] { WEBX_CONFIGURATION_LOCATION };}}
}

相对于Spring上下文中以bean为单位进行加载,webx的” 级联组件 “结构是一大特色:

webx将一个web应用分解成多个小应用模块:app1,app2等。将一个大的应用变成若干个小应用模块,并且配置文件相互隔离。
所有小应用模块共享一个Spring Root Context( 根容器 ),对应 webx.xml ,根容器的bean可以被注入到子容器中;反过来不行。
每个小应用独享一个Spring Sub Context( 子容器 ),对应 webx-*.xml ,子容器之间相互隔离,不可相互注入bean。
需要注意的是,即使web应用很简单,也至少配置一个子容器。

public void refresh() throws BeansException, IllegalStateException {//refresh过程是同步的,线程安全synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.//容器启动的预先准备,设置startup date等prepareRefresh();// Tell the subclass to refresh the internal bean factory.//创建BeanFactory,如果已存在就先销毁。BeanFactory装载BeanDefinitionConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.//配置BeanFactory的标准上下文特性prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.//在bean开始装载后,提供一个修改BeanFactory的接口postProcessBeanFactory(beanFactory);// Invoke factory processors registered as beans in the context.//调用postBeanBeanFactoryinvokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.//注册BeanPostProcessor,可以再bean初始化前后定制一些功能registerBeanPostProcessors(beanFactory);// Initialize message source for this context.//初始化消息源initMessageSource();// Initialize event multicaster for this context.//初始化事件监听器集合initApplicationEventMulticaster();// Initialize other special beans in specific context subclasses.//用于扩展实现一些特殊的bean的初始化,默认什么都不做onRefresh();// Check for listener beans and register them.//注册监听器registerListeners();// Instantiate all remaining (non-lazy-init) singletons.//初始化其余非延迟加载的beanfinishBeanFactoryInitialization(beanFactory);// Last step: publish corresponding event.//最后一步:调用onRefresh方法,并发布ContextRefreshedEvent事件finishRefresh();}catch (BeansException ex) {...}}}
public class WebxComponentsContext extends WebxApplicationContext {private WebxComponentsLoader componentsLoader;public WebxComponentsLoader getLoader() {return assertNotNull(componentsLoader, ILLEGAL_STATE, "no WebxComponentsLoader set");}public void setLoader(WebxComponentsLoader loader) {this.componentsLoader = loader;}/** 取得所有的components。 */public WebxComponents getWebxComponents() {return getLoader().getWebxComponents();}@Overrideprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {super.postProcessBeanFactory(beanFactory);// 初始化WebxComponentsCreator的BeangetLoader().postProcessBeanFactory(beanFactory);}@Overrideprotected void finishRefresh() {super.finishRefresh();// getLoader().finishRefresh();}/** 在创建子容器时,给parent一个设置子context的机会。 */protected void setupComponentContext(WebxComponentContext componentContext) {}
}

Webx重写postProcessBeanFactory方法:

 public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {// 由于初始化components依赖于webxConfiguration,而webxConfiguration可能需要由PropertyPlaceholderConfigurer来处理,// 此外,其它有一些BeanFactoryPostProcessors会用到components,// 因此components必须在PropertyPlaceholderConfigurer之后初始化,并在其它BeanFactoryPostProcessors之前初始化。//// 下面创建的WebxComponentsCreator辅助类就是用来确保这个初始化顺序:// 1. PriorityOrdered - PropertyPlaceholderConfigurer// 2. Ordered - WebxComponentsCreator// 3. 普通 - 其它BeanFactoryPostProcessorsBeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(WebxComponentsCreator.class);builder.addConstructorArgValue(this);BeanDefinition componentsCreator = builder.getBeanDefinition();componentsCreator.setAutowireCandidate(false);BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;String name = SpringExtUtil.generateBeanName(WebxComponentsCreator.class.getName(), registry);registry.registerBeanDefinition(name, componentsCreator);}
    /** 初始化components。 */private WebxComponentsImpl createComponents(WebxConfiguration parentConfiguration,ConfigurableListableBeanFactory beanFactory) {ComponentsConfig componentsConfig = getComponentsConfig(parentConfiguration);// 假如isAutoDiscoverComponents==true,试图自动发现componentsMap<String, String> componentNamesAndLocations = findComponents(componentsConfig, getServletContext());// 取得特别指定的componentsMap<String, ComponentConfig> specifiedComponents = componentsConfig.getComponents();// 实际要初始化的comonents,为上述两种来源的并集Set<String> componentNames = createTreeSet();componentNames.addAll(componentNamesAndLocations.keySet());componentNames.addAll(specifiedComponents.keySet());// 创建root controllerWebxRootController rootController = componentsConfig.getRootController();if (rootController == null) {rootController = (WebxRootController) BeanUtils.instantiateClass(componentsConfig.getRootControllerClass());}// 创建并将components对象置入resolvable dependencies,以便注入到需要的bean中WebxComponentsImpl components = new WebxComponentsImpl(componentsContext,componentsConfig.getDefaultComponent(), rootController, parentConfiguration);beanFactory.registerResolvableDependency(WebxComponents.class, components);// 初始化每个componentfor (String componentName : componentNames) {ComponentConfig componentConfig = specifiedComponents.get(componentName);String componentPath = null;WebxController controller = null;if (componentConfig != null) {componentPath = componentConfig.getPath();controller = componentConfig.getController();}if (controller == null) {controller = (WebxController) BeanUtils.instantiateClass(componentsConfig.getDefaultControllerClass());}WebxComponentImpl component = new WebxComponentImpl(components, componentName, componentPath,componentName.equals(componentsConfig.getDefaultComponent()), controller,getWebxConfigurationName());components.addComponent(component);prepareComponent(component, componentNamesAndLocations.get(componentName));}return components;}

webx重写finishRefresh方法:

/** 初始化所有components。 */
public void finishRefresh() {components.getWebxRootController().onFinishedProcessContext();for (WebxComponent component : components) {logInBothServletAndLoggingSystem("Initializing Spring sub WebApplicationContext: " + component.getName());WebxComponentContext wcc = (WebxComponentContext) component.getApplicationContext();WebxController controller = component.getWebxController();wcc.refresh();controller.onFinishedProcessContext();}logInBothServletAndLoggingSystem("WebxComponents: initialization completed");
}

Webx mvc 源码相关推荐

  1. asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证

    asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型的绑定和验证 原文:asp.net mvc源码分析-DefaultModelBinder 自定义的普通数据类型 ...

  2. ASP.NET Core MVC 源码学习:MVC 启动流程详解

    前言 在 上一篇 文章中,我们学习了 ASP.NET Core MVC 的路由模块,那么在本篇文章中,主要是对 ASP.NET Core MVC 启动流程的一个学习. ASP.NET Core 是新一 ...

  3. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)(转)

    阅读目录 一.MVC原理解析 1.MVC原理 二.HttpHandler 1.HttpHandler.IHttpHandler.MvcHandler的说明 2.IHttpHandler解析 3.Mvc ...

  4. Spring MVC源码——Servlet WebApplicationContext

    上一篇笔记(Spring MVC源码--Root WebApplicationContext)中记录了下 Root WebApplicationContext 的初始化代码.这一篇来看 Servlet ...

  5. Spring MVC源码——Root WebApplicationContext

    Spring MVC源码--Root WebApplicationContext 打算开始读一些框架的源码,先拿 Spring MVC 练练手,欢迎点击这里访问我的源码注释, SpringMVC官方文 ...

  6. MVC系列——MVC源码学习:打造自己的MVC框架(一:核心原理)

    前言:最近一段时间在学习MVC源码,说实话,研读源码真是一个痛苦的过程,好多晦涩的语法搞得人晕晕乎乎.这两天算是理解了一小部分,这里先记录下来,也给需要的园友一个参考,奈何博主技术有限,如有理解不妥之 ...

  7. ASP.NET MVC源码现在可以下载了(翻译)

    上个月我在blog里介绍了ASP.NET MVC Roadmap,两个周以前我们发布了ASP.NET Preview 2 Release,ASP.NET组的Phil Haack写了一篇很好的blog介 ...

  8. asp.net mvc源码分析-Action篇 Action的执行

    接着上篇 asp.net mvc源码分析-Action篇 DefaultModelBinder 我们已经获取的了Action的参数,有前面的内容我们知道Action的调用时在ControllerAct ...

  9. asp.net mvc源码分析-Action篇 DefaultModelBinder

    接着上篇 asp.net mvc源码分析-Controller篇 ValueProvider 现在我们来看看ModelBindingContext这个对象. ModelBindingContext b ...

  10. Spring MVC源码解析——HandlerMapping(处理器映射器)

    Sping MVC 源码解析--HandlerMapping处理器映射器 1. 什么是HandlerMapping 2. HandlerMapping 2.1 HandlerMapping初始化 2. ...

最新文章

  1. sed替换每行最后一个字符
  2. r语言导出html改不了名,请问如何进行数据框列的重命名?
  3. Java多线程编程模式实战指南(三):Two-phase Termination模式--转载
  4. Apache Kafka-通过concurrency实现并发消费
  5. 无用的设计模式之装饰者模式
  6. Python——assert(断言函数)
  7. 因为此网站使用了 hsts_长春定制小程序服务,网站设计市场价格
  8. python数组去重函数_Python科学计算库Numpy之 数组操作
  9. 剑指offer之矩形覆盖问题
  10. html jwt权限控制,SpringBoot+SpringSecurity+JWT实RESTfulAPI权限控制
  11. 关于html中table表格tr,td的高度和宽度
  12. caffe的caffe.proto
  13. codeproject 调试技巧 学习笔记
  14. 无源三端口器件特性分析-《微波工程》
  15. 无所不能的NumPy:我用它弹奏出了CD音质的吉他名曲“爱的罗曼史”
  16. java8中数据类型_Java 8中 基本数据类型
  17. 电源管理允许此设备唤醒计算机怎么关掉,允许计算机关闭此设备以节省电量灰色 | MOS86...
  18. 9款别出心裁的jQuery插件
  19. 小米10 MIUI11 安卓10安装面具获取root权限
  20. 洛谷-P1598- 垂直直方图

热门文章

  1. 软件开发工作量/费用估算
  2. 传感器实训心得体会_传感器心得体会范文
  3. 程序员代码面试指南(左程云著)java学习笔记
  4. 安卓暗黑模式软件_程序员欢呼!微软 GitHub 安卓版 App 发布预览:支持暗黑模式...
  5. 华三交换机ping大包命令_cmd如何ping大包
  6. php寻仙记,【网站搭建】寻仙记+天空之城两款文字游戏PHP源码
  7. Snowflake id生成器
  8. 如何在 Web 前端做 3D 音效处理
  9. win安装soapui
  10. Java贪吃蛇游戏开发