nutz源码 mvc 之 url与controller 映射

mvc框架的一个重要的作用就是根据用户的url请求,来调用相应的方法。

 

首先自然是对url进行解析了,这里有两种方法一是采用filter方式,另一种则是servlet方式。

 

采用servlet方式的需要在web.xml进行如下配置:

 

 

<servlet>
        <servlet-name>nutServlet</servlet-name>
        <servlet-class>org.nutz.mvc.NutServlet</servlet-class>
        <init-param>
            <param-name>modules</param-name>
            <param-value>org.nutz.controller.HelloController</param-value>
        </init-param>
    </servlet>

    <servlet-mapping>
        <servlet-name>nutServlet</servlet-name>
        <url-pattern>/*</url-pattern>
    </servlet-mapping>

 

这样请求过来后就得先经过NutServlet了。

 

主要的方法是init和service。init进行初始化,将定义好的controller和url的映射关系保存起来,然后在service方法中就可以根据request的path来获取相应的controller的方法处理了。

 

 

NutServlet init方法

 

public void init() throws ServletException {
		if (log.isInfoEnabled()) {
			URL me = Thread.currentThread()
							.getContextClassLoader()
							.getResource(NutServlet.class.getName().replace('.', '/') + ".class");
			log.infof("Nutz Version : %s in %s", Nutz.version(), me);
		}
		config = new ServletNutConfig(getServletConfig());
		Loading ing = Inits.init(config, false);
		urls = ing.getUrls();
		ok = true;
	}

 

NutServlet service方法

 

protected void service(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		if (null == urls) {
			if (log.isErrorEnabled())
				log.error("!!!This servlet is destroyed!!! Noting to do!!!");
			return;
		}

		Mvcs.updateRequestAttributes(req);
		String path = Mvcs.getRequestPath(req);

		if (log.isInfoEnabled())
			log.info("HttpServletRequest path = " + path);

		// get Url and invoke it
		ActionInvoking ing = urls.get(path);
		if (null == ing || null == ing.getInvoker())
			resp.setStatus(404);
		else
			ing.invoke(config.getServletContext(), req, resp);
	}
 

 

代码中可以看到contoller和url对应的关系保存到了UrlMap urls对象中。init的关键方法在

 

 

Loading ing = Inits.init(config, false);
urls = ing.getUrls();

 

Inits 的init方法的基本思想就是遍历所有的controller的方法,再根据At注解中定义的响应url来建立url和controller中各个方法的关联关系。

 

这里有个问题就是nutz必须要个maincontroller,其他的controller必须在这个controller里定义,系统才能找到。在上面的web.xml中helloController就是。我们公司的mvc框架则采用其他的方式来寻找系统中的Controller,就是通过一个properties将所有的Controller类定义到里面,init里面遍历这个properties就行了。

 

Inits的init方法:

 

 

......
Class<? extends Loading> loadingType;
			LoadingBy lb = mainModule.getAnnotation(LoadingBy.class);
			if (null != lb)
				loadingType = lb.value();
			else
				loadingType = DefaultLoading.class;
			if (log.isDebugEnabled())
				log.debug("Loading by " + loadingType);
			// Here, we load all Nutz.Mvc configuration
			Loading ing = Mirror.me(loadingType).born();
			ing.load(config, mainModule);
........
 

我们没有写loading的实现,就只得使用defaultLoading了。 进入DefaultLoading类。

 

在这里我们又看到了UrlMap urls变量了,组装好这个变量,NutServlet的 urls就能用了。

 

DefaultLoading 的load方法 还是干了很多事的

 

 

this.mainModule = mainModule;
			createContent(config);
			if (log.isDebugEnabled())
				log.debug("Loading configuration...");
			loadIoc(config);
			loadSubModules(config);
			loadLocalization(config);
			setupServer(config);
			saveResult2Context(config);

 这里主要看loadSubModules。

 

 

protected void loadSubModules(NutConfig config) throws Throwable {
		Views vms = mainModule.getAnnotation(Views.class);

		// Prepare view makers
		List<ViewMaker> makers = new ArrayList<ViewMaker>();
		if (null != vms)
			for (Class<? extends ViewMaker> type : vms.value())
				makers.add(type.newInstance());
		makers.add(new DefaultViewMaker());// 优先使用用户自定义

		// Load modules
		if (log.isDebugEnabled())
			log.debugf("MainModule: <%s>", mainModule.getName());

		urls = makeUrlMap(config, context, mainModule);
		Set<Class<?>> moduleSet = new HashSet<Class<?>>();

		// Add default module
		moduleSet.add(mainModule);

		// Then try to load sub-modules
		Modules modules = mainModule.getAnnotation(Modules.class);
		Class<?>[] moduleRefers;
		if (null == modules || null == modules.value() || modules.value().length == 0)
			moduleRefers = new Class<?>[]{mainModule};
		else
			moduleRefers = modules.value();

		// 扫描所有的
		boolean isNeedScanSubPackages = null == modules ? false : modules.scanPackage();
		for (Class<?> module : moduleRefers) {
			// 扫描这个类同包,以及所有子包的类
			if (isNeedScanSubPackages) {
				if (log.isDebugEnabled())
					log.debugf(" > scan '%s'", module.getPackage().getName());
				List<Class<?>> subs = Scans.me().scanPackage(module);
				for (Class<?> sub : subs) {
					if (isModule(sub)) {
						if (log.isDebugEnabled())
							log.debugf("   >> add '%s'", sub.getName());
						moduleSet.add(sub);
					} else if (log.isTraceEnabled()) {
						log.tracef("   >> ignore '%s'", sub.getName());
					}
				}
			}
			// 仅仅加载自己
			else {
				if (isModule(module)) {
					if (log.isDebugEnabled())
						log.debugf(" > add '%s'", module.getName());
					moduleSet.add(module);
				} else if (log.isTraceEnabled()) {
					log.tracef(" > ignore '%s'", module.getName());
				}
			}
		}

		for (Class<?> module : moduleSet) {
			if (log.isDebugEnabled())
				log.debugf("Module: <%s>", module.getName());

			urls.add(makers, module);
		}
		config.setAttributeIgnoreNull(UrlMap.class.getName(), urls);
	}
 

程序先生成个UrlMap,然后就挨个扫描,把所有的Controller找到,然后再每个Controller单独处理,其实就是读At注解,把要处理的url和method关联起来,这个是在urls.add(makers, module);的时候做的。

里面的代码还是挺烦的,看到NutServlet 的service方法里面的ActionInvoking 对象了吗,urls 添加 module的时候就会为moudule中所有有At注解的方法,生成个这个类型的对象,并和At注解里面定义的path关联起来。

 

Nutz 的Mvc框架大体流程就是这样了。个人觉得还是有些复杂,如果做的更轻量点的话,可以这么做:

 

直接将url分成两个部分moudle和method。

比如url为如下格式:http://www.test.com/home/index.do

 

这样的url会调用HomeController(module)的index(method)来处理。这样path对应于方法的映射关系很简单,一个HashMap就搞定,而且也符合约定优先配置的设计思想。

 

你可能感兴趣的:(mvc,框架,Web,xml,servlet)