最简单的mvc框架tiny,增加Aop功能。
增加Aop接口,使用是实现即可。
然后设置路由(访问的action)和aop的绑定信息,类似如下:
下面的意思是把路由"/TinyTest/hello/"和TestAop.class做绑定,这样执行类TinyTestAction的hello方法时,就会自动执行TestAop的before和after方法。
下面的BindingAop 的init方法,需要自己设置。系统启动时会自动读取。
1public class BindingAop { 2 public static void init(){ 3 BindingUtil.binding("/TinyTest/hello/", new Class[]{TestAop.class}); 4 BindingUtil.binding("/TinyTest/*", new Class[]{TestAop2.class}); 5 } 6}
Aop.java
1package tiny; 2 3import java.util.Map; 4 5public interface Aop { 6 void before(Map<String,String> args); 7 void after(Map<String,String> args); 8}
BindingAop.java
1package tiny; 2 3import web.servlet.async_request_war.TestAop; 4import web.servlet.async_request_war.TestAop2; 5 6public class BindingAop { 7 public static void init(){ 8 //BindingUtil.binding("/TinyTest/hello/", new Class[]{TestAop.class}); 9 //BindingUtil.binding("/TinyTest/*", new Class[]{TestAop2.class}); 10 //自己绑定 11 } 12}
BindingUtil.java
1package tiny; 2 3 4public final class BindingUtil { 5 public static void binding(String route,Class[] cls){ 6 if(Container.aops.get(route) != null){ 7 new Exception(route+" 重复"); 8 }else{ 9 Container.aops.put(route,cls); 10 } 11 } 12}
修改后的Container.java
1package tiny; 2 3import static java.lang.System.out; 4 5import java.io.File; 6import java.net.URL; 7import java.util.HashMap; 8import java.util.Map; 9public class Container { 10 private static Map<String,Object> clsMap = new HashMap<String,Object>(); 11 static Map<String,Class[]> aops =new HashMap<String,Class[]>(); 12 private static Map<String,Aop[]> reqAops =new HashMap<String,Aop[]>(); 13 public static void init() throws ClassNotFoundException, InstantiationException, IllegalAccessException{ 14 ClassLoader cld = Thread.currentThread().getContextClassLoader(); 15 URL resource = cld.getResource("/"); 16 File dirs = new File(resource.getFile()); 17 findAction(dirs,""); 18 } 19 20 private static void findAction(File dirs,String basePack) 21 throws ClassNotFoundException, InstantiationException, IllegalAccessException { 22 File[] childs = dirs.listFiles(); 23 for (int i = 0; i < childs.length; i++) { 24 String packPath =basePack+childs[i].getName()+"."; 25 if (childs[i].isDirectory()) { 26 findAction(childs[i],packPath); 27 } else { 28 String className = childs[i].getName(); 29 if (className.endsWith("Action.class")) { 30 packPath=packPath.replace(".class.", ""); 31 Object o = Class.forName(packPath).newInstance(); 32 String clsName = o.getClass().getSimpleName().substring(0,o.getClass().getSimpleName().lastIndexOf("Action")); 33 if(clsMap.get(clsName) != null){ 34 new IllegalAccessException(clsName+" class 重复"); 35 }else{ 36 clsMap.put(clsName, o); 37 } 38 } 39 } 40 } 41 } 42 43 44 public static Aop[] getBeforeBinding(String[] route,String key) throws InstantiationException, IllegalAccessException{ 45 Aop[] reqAop = null; 46 47 Class[] aops1 = aops.get("/"+route[0]+"/"+route[1]+"/"); 48 Class[] aops2 = aops.get("/"+route[0]+"/*"); 49 Class[] aops3 = aops.get("/*"); 50 int aopNum1 = aops1 !=null?aops1.length:0; 51 int aopNum2 = aops2 != null?aops2.length:0; 52 int aopNum3 = aops3 !=null?aops3.length:0; 53 if(aopNum3+aopNum2+aopNum1 !=0){ 54 Class[] allAop = new Class[aopNum3+aopNum2+aopNum1]; 55 56 if(aopNum3>0){ 57 System.arraycopy(aops3,0,allAop,0,aops3.length); 58 } 59 if(aopNum2>0){ 60 System.arraycopy(aops2,0,allAop,aopNum3,aops2.length); 61 } 62 if(aopNum1>0){ 63 System.arraycopy(aops1,0,allAop,aopNum2+aopNum3,aops1.length); 64 } 65 reqAop = new Aop[allAop.length]; 66 for(int a=0;a<allAop.length;a++){ 67 reqAop[a] = (Aop)allAop[a].newInstance(); 68 } 69 reqAops.put(key, reqAop); 70 } 71 return reqAop; 72 } 73 74 public static Aop[] getAfterBinding(String key){ 75 return reqAops.get(key); 76 } 77 78 public static void clearReqAops(String key){ 79 reqAops.remove(key); 80 } 81 82 public static Object getCls(String name){ 83 return clsMap.get(name); 84 } 85 86}
修稿后的FrontControl.java
1package tiny; 2 3import java.io.IOException; 4import java.lang.reflect.InvocationTargetException; 5import java.util.Arrays; 6import java.util.HashMap; 7import java.util.Map; 8import java.util.UUID; 9import java.util.concurrent.atomic.AtomicBoolean; 10 11import javax.servlet.Filter; 12import javax.servlet.FilterChain; 13import javax.servlet.FilterConfig; 14import javax.servlet.ServletContext; 15import javax.servlet.ServletException; 16import javax.servlet.ServletRequest; 17import javax.servlet.ServletResponse; 18import javax.servlet.annotation.WebFilter; 19import javax.servlet.http.HttpServletRequest; 20import javax.servlet.http.HttpServletResponse; 21 22//@WebFilter(urlPatterns = { "/demoAsyncLink" }, asyncSupported = true) 23@WebFilter(urlPatterns = { "/ty/*" }) 24public class FrontControl implements Filter{ 25 26 private AtomicBoolean initialized = new AtomicBoolean(); 27 private ServletContext servletContext; 28 29 @Override 30 public void init(final FilterConfig config) throws ServletException{ 31 try { 32 if (initialized.compareAndSet(false, true)) { 33 this.servletContext = config.getServletContext(); 34 Container.init(); 35 BindingAop.init(); 36 } 37 } 38 catch (Exception e) { 39 throw new ServletException("FrontControl init failed.", e); 40 } 41 } 42 43 @Override 44 public void doFilter(ServletRequest request, ServletResponse response,FilterChain chain) throws ServletException, IOException{ 45 HttpServletRequest req = (HttpServletRequest) request; 46 HttpServletResponse res = (HttpServletResponse) response; 47 ContextUtil.setActionContext(servletContext, req, res); 48 try { 49 String[] routes = valid(req); 50 if(routes == null){ 51 chain.doFilter(request, response); 52 return; 53 } 54 Object o = Container.getCls(routes[0]); 55 if(o == null){ 56 chain.doFilter(request, response); 57 return; 58 } 59 60 Map<String,String> args = this.converter(req.getParameterMap()); 61 62 String key = UUID.randomUUID().toString(); 63 this.before(routes,args,key); 64 65 Object result = o.getClass().getMethod(routes[1],Map.class).invoke(o,args); 66 67 this.after(args,key); 68 69 Container.clearReqAops(key); 70 71 if (result==null){ 72 return; 73 } 74 if (result instanceof Renderer) { 75 Renderer r = (Renderer) result; 76 r.render(this.servletContext, req, res); 77 return; 78 } 79 if (result instanceof String) { 80 String s = (String) result; 81 if (s.startsWith("/")) { 82 request.getRequestDispatcher(s).forward(request, response); 83 return; 84 }else{ 85 response.getWriter().print(result); 86 } 87 } 88 89 } catch (IllegalAccessException e) { 90 e.printStackTrace(); 91 } catch (IllegalArgumentException e) { 92 e.printStackTrace(); 93 } catch (SecurityException e) { 94 e.printStackTrace(); 95 } catch (InvocationTargetException e) { 96 e.printStackTrace(); 97 } catch (NoSuchMethodException e) { 98 e.printStackTrace(); 99 } catch (Exception e) { 100 e.printStackTrace(); 101 } 102 } 103 private Map<String,String> converter(Map<String,String[]> args){ 104 if(args == null){ 105 return null; 106 } 107 Map<String,String> params = new HashMap<String,String>(); 108 for(String key : args.keySet()){ 109 params.put(key, Arrays.toString(args.get(key)).replaceAll("[\\[\\]\\s,]", "")); 110 } 111 return params; 112 } 113 private String[] valid(HttpServletRequest req){ 114 String uri = req.getRequestURI(); 115 String path = req.getContextPath(); 116 if (path != null){ 117 uri = uri.substring(path.length()); 118 }else{ 119 return null; 120 } 121 String[] routes = uri.substring(uri.indexOf("/ty/")+4).split("/"); 122 if(routes == null || routes.length<2){ 123 return null; 124 } 125 return routes; 126 } 127 //aop before 128 private void before(String[] route,Map<String,String> args,String key) throws InstantiationException, IllegalAccessException, IllegalArgumentException, SecurityException, InvocationTargetException, NoSuchMethodException{ 129 Aop[] aops = Container.getBeforeBinding(route, key); 130 if(aops != null){ 131 for(int a=0;a<aops.length;a++){ 132 aops[a].getClass().getMethod("before",Map.class).invoke(aops[a],args); 133 } 134 } 135 } 136 //aop after 137 private void after(Map<String,String> args,String key) throws IllegalArgumentException, SecurityException, IllegalAccessException, InvocationTargetException, NoSuchMethodException{ 138 Aop[] aops = Container.getAfterBinding(key); 139 if(aops != null){ 140 for(int a=0;a<aops.length;a++){ 141 aops[a].getClass().getMethod("after",Map.class).invoke(aops[a],args); 142 } 143 } 144 } 145 146 @Override 147 public void destroy() { 148 } 149}
测试类代码
TinyTestAction.java
1package web.servlet.async_request_war; 2 3import java.util.Map; 4 5import tiny.ContextUtil; 6 7public class TinyTestAction { 8 public void hello(Map<String,String> args){ 9 10 System.out.println("aa:"+args.get("aa")); 11 System.out.println("访问时间1:"+System.currentTimeMillis()); 12 //ContextUtil.getContext().getXXX; 13 } 14}
TestAop.java
1package web.servlet.async_request_war; 2 3import java.util.Map; 4 5import tiny.Aop; 6 7public class TestAop implements Aop { 8 9 @Override 10 public void before(Map<String,String> args){ 11 System.out.println(this.getClass().getName()+".before"); 12 } 13 14 @Override 15 public void after(Map<String,String> args){ 16 System.out.println(this.getClass().getName()+".after"); 17 } 18}
TestAop2.java
1package web.servlet.async_request_war; 2 3import java.util.Map; 4 5import tiny.Aop; 6 7public class TestAop2 implements Aop { 8 9 @Override 10 public void before(Map<String,String> args){ 11 System.out.println(this.getClass().getName()+".before"); 12 } 13 14 @Override 15 public void after(Map<String,String> args){ 16 System.out.println(this.getClass().getName()+".after"); 17 } 18}
测试执行结果截图:


总结
我们用最少的类实现了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天时间,做的比较粗糙,欢迎大家指导一下。提高下我自己。