天天看點

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")) {

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);

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) {

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);

相關文章:

mvc架構探究及其源碼實作(1)-理論基礎

mvc架構探究及其源碼實作(2)-核心元件定義

mvc架構探究及其源碼實作(4)-前端控制器

mvc架構探究及其源碼實作(5)-相關元件實作

mvc架構探究及其源碼實作(6)-簡單示例