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 />
<bean />
<bean >
<property name="viewClass" value="com.google.mvc.web.servlet.mvc.InternalResourceView"/>
<property name="prefix" value="/WEB-INF/"/>
<property name="suffix" value=".jsp"/>
</bean>
<bean />
<bean />
<bean />
</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)-簡單示例
最後更新:2017-04-02 04:01:42