30、最簡單的mvc框架tiny,增長Aop

最簡單的mvc框架tiny,增長Aop功能。 css

增長Aop接口,使用是實現便可。 java

而後設置路由(訪問的action)和aop的綁定信息,相似以下: web

下面的意思是把路由"/TinyTest/hello/"和TestAop.class作綁定,這樣執行類TinyTestAction的hello方法時,就會自動執行TestAop的before和after方法。 mvc

下面的BindingAop 的init方法,須要本身設置。系統啓動時會自動讀取。 框架

public class BindingAop {
   public static void init(){
	   BindingUtil.binding("/TinyTest/hello/", new Class[]{TestAop.class});
	   BindingUtil.binding("/TinyTest/*", new Class[]{TestAop2.class});
   }
}


Aop.java dom

package tiny;

import java.util.Map;

public interface Aop {
    void before(Map<String,String> args);
    void after(Map<String,String> args);
}


BindingAop.java async

package tiny;

import web.servlet.async_request_war.TestAop;
import web.servlet.async_request_war.TestAop2;

public class BindingAop {
   public static void init(){
	   //BindingUtil.binding("/TinyTest/hello/", new Class[]{TestAop.class});
	   //BindingUtil.binding("/TinyTest/*", new Class[]{TestAop2.class});
	   //本身綁定
   }
}


BindingUtil.java ide

package tiny;


public final class BindingUtil {
	public static void binding(String route,Class[] cls){
		if(Container.aops.get(route) != null){
			new Exception(route+" 重複");
		}else{
			Container.aops.put(route,cls);
		}
	}
}


修改後的Container.java 測試

package tiny;

import static java.lang.System.out;

import java.io.File;
import java.net.URL;
import java.util.HashMap;
import java.util.Map;
public class Container {
    private static Map<String,Object> clsMap = new HashMap<String,Object>();
    static Map<String,Class[]> aops =new HashMap<String,Class[]>();
    private static Map<String,Aop[]> reqAops =new HashMap<String,Aop[]>();
    public static void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException{
		ClassLoader cld = Thread.currentThread().getContextClassLoader();
		URL resource = cld.getResource("/");
		File dirs = new File(resource.getFile());
		findAction(dirs,"");
    }

	private static void findAction(File dirs,String basePack)
	throws ClassNotFoundException, InstantiationException, IllegalAccessException {
		File[] childs = dirs.listFiles();
		for (int i = 0; i < childs.length; i++) {
			String packPath =basePack+childs[i].getName()+".";
			if (childs[i].isDirectory()) {
				findAction(childs[i],packPath);
			} else {
				String className = childs[i].getName();
				if (className.endsWith("Action.class")) {
					packPath=packPath.replace(".class.", "");
					Object o = Class.forName(packPath).newInstance();
					String clsName = o.getClass().getSimpleName().substring(0,o.getClass().getSimpleName().lastIndexOf("Action"));
					if(clsMap.get(clsName) != null){
						new IllegalAccessException(clsName+" class 重複");
					}else{
						clsMap.put(clsName, o);
					}
				}
			}
		}
	}
	
	
	public static Aop[] getBeforeBinding(String[] route,String key) throws InstantiationException, IllegalAccessException{
		Aop[] reqAop = null;
		
		Class[] aops1 = aops.get("/"+route[0]+"/"+route[1]+"/");
    	Class[] aops2 = aops.get("/"+route[0]+"/*");
    	Class[] aops3 = aops.get("/*");
    	int aopNum1 = aops1 !=null?aops1.length:0;
    	int aopNum2 = aops2 != null?aops2.length:0;
    	int aopNum3 = aops3 !=null?aops3.length:0;
    	if(aopNum3+aopNum2+aopNum1 !=0){
    		Class[] allAop = new Class[aopNum3+aopNum2+aopNum1];

    		if(aopNum3>0){
    			System.arraycopy(aops3,0,allAop,0,aops3.length);
    		}
    		if(aopNum2>0){
    			System.arraycopy(aops2,0,allAop,aopNum3,aops2.length);
    		}
    		if(aopNum1>0){
    			System.arraycopy(aops1,0,allAop,aopNum2+aopNum3,aops1.length);
    		}
    		reqAop = new Aop[allAop.length];
    		for(int a=0;a<allAop.length;a++){
    			reqAop[a] = (Aop)allAop[a].newInstance();
    		}
    		reqAops.put(key, reqAop);
    	}
    	return reqAop;
	}
	
	public static Aop[] getAfterBinding(String key){
		return reqAops.get(key);
	}
	
	public static void clearReqAops(String key){
		reqAops.remove(key);
	}
	
	public static Object getCls(String name){
		return clsMap.get(name);
	}
	
}


修稿後的FrontControl.java this

package tiny;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.atomic.AtomicBoolean;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

//@WebFilter(urlPatterns = { "/demoAsyncLink" }, asyncSupported = true)
@WebFilter(urlPatterns = { "/ty/*" })
public class FrontControl implements Filter{
	
	private AtomicBoolean initialized = new AtomicBoolean();
	private ServletContext servletContext;

	@Override
    public void init(final FilterConfig config) throws ServletException{
        try {
            if (initialized.compareAndSet(false, true)) {
            	this.servletContext = config.getServletContext();
                Container.init();
                BindingAop.init();
            }
        }
        catch (Exception e) {
            throw new ServletException("FrontControl init failed.", e);
        }
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws ServletException, IOException{
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse res = (HttpServletResponse) response;
        ContextUtil.setActionContext(servletContext, req, res);
        try {
            String[] routes = valid(req);
            if(routes == null){
            	chain.doFilter(request, response);
            	return;
            }
            Object o = Container.getCls(routes[0]);
            if(o == null){
            	chain.doFilter(request, response);
            	return;
            }
            
            Map<String,String> args = this.converter(req.getParameterMap());
            
            String key = UUID.randomUUID().toString();
            this.before(routes,args,key);
            
			Object result = o.getClass().getMethod(routes[1],Map.class).invoke(o,args);
			
			this.after(args,key);
			
			Container.clearReqAops(key);
			
	        if (result==null){
	            return;
	        }
	        if (result instanceof Renderer) {
	            Renderer r = (Renderer) result;
	            r.render(this.servletContext, req, res);
	            return;
	        }
	        if (result instanceof String) {
	            String s = (String) result;
	            if (s.startsWith("/")) {
	                request.getRequestDispatcher(s).forward(request, response);
	                return;
	            }else{
	            	response.getWriter().print(result);
	            }
	        }
			
		} catch (IllegalAccessException e) {
			e.printStackTrace();
		} catch (IllegalArgumentException e) {
			e.printStackTrace();
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (InvocationTargetException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		} catch (Exception e) {
			e.printStackTrace();
		}
    }
    private Map<String,String> converter(Map<String,String[]> args){
    	if(args == null){
    		return null;
    	}
        Map<String,String> params = new HashMap<String,String>();
        for(String key : args.keySet()){
        	params.put(key, Arrays.toString(args.get(key)).replaceAll("[\\[\\]\\s,]", ""));
        }
        return params;
    }
    private String[] valid(HttpServletRequest req){
        String uri = req.getRequestURI();
        String path = req.getContextPath();
        if (path != null){
        	uri = uri.substring(path.length());
        }else{
        	return null;
        }
        String[] routes = uri.substring(uri.indexOf("/ty/")+4).split("/");
        if(routes == null || routes.length<2){
        	return null;
        }
        return routes;
    }
    //aop before
    private void before(String[] route,Map<String,String> args,String key) throws InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{
    	Aop[] aops = Container.getBeforeBinding(route, key);
    	if(aops != null){
    		for(int a=0;a<aops.length;a++){
    			aops[a].getClass().getMethod("before",Map.class).invoke(aops[a],args);
    		}
    	}
    }
    //aop after
    private void after(Map<String,String> args,String key) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{
    	Aop[] aops = Container.getAfterBinding(key);
    	if(aops != null){
    		for(int a=0;a<aops.length;a++){
    			aops[a].getClass().getMethod("after",Map.class).invoke(aops[a],args);
    		}
    	}
    }
    
    @Override
    public void destroy() {
    }
}


測試類代碼

TinyTestAction.java

package web.servlet.async_request_war;

import java.util.Map;

import tiny.ContextUtil;

public class TinyTestAction {
	public void hello(Map<String,String> args){
		
		System.out.println("aa:"+args.get("aa"));
		System.out.println("訪問時間1:"+System.currentTimeMillis());
		//ContextUtil.getContext().getXXX;
	}
}


TestAop.java

package web.servlet.async_request_war;

import java.util.Map;

import tiny.Aop;

public class TestAop implements Aop {

	@Override
	public void before(Map<String,String> args){
		System.out.println(this.getClass().getName()+".before");
	}

	@Override
	public void after(Map<String,String> args){
		System.out.println(this.getClass().getName()+".after");
	}
}


TestAop2.java

package web.servlet.async_request_war;

import java.util.Map;

import tiny.Aop;

public class TestAop2 implements Aop {

	@Override
	public void before(Map<String,String> args){
		System.out.println(this.getClass().getName()+".before");
	}

	@Override
	public void after(Map<String,String> args){
		System.out.println(this.getClass().getName()+".after");
	}
}


測試執行結果截圖:


總結

咱們用最少的類實現了mvc功能,其實應該叫相似mvc功能的模板,更合適。呵呵。

主要用到的就是filter來攔截用戶請求,而後統一處理。servlet也能夠實現,不過filter有個好處是不用本身處理靜態文件的請求。(eternal框架本身處理了靜態請求如css、js)。

0配置,0註解,之前0配置必須是使用註解的,我們使用了servlet3.0的註解方式配置(tiny自身),這樣使用者,根本就不用配置。

aop的實現,更簡單,就是匹配攔截到的路由和用戶本身綁定的,就比較。而後,執行action前置性before,執行action後,執行after,並傳遞參數。

昨天忘記說了,Renderer渲染器,是能夠攜帶數據的,你們一看就明白了。還有就是得使用jee6,由於用到了servlet3.0。

還有就是要實現一個java調用js的功能,相似dwr的,呵呵,實現這個功能後我們在增長一個js文件,tiny就是全部的文件了。


花了1天時間,作的比較粗糙,歡迎你們指導一下。提升下我本身。

相關文章
相關標籤/搜索