最简单的mvc框架tiny,增加Aop功能。
增加Aop接口,使用是实现即可。
然后设置路由(访问的action)和aop的绑定信息,类似如下:
下面的意思是把路由"/TinyTest/hello/"和TestAop.class做绑定,这样执行类TinyTestAction的hello方法时,就会自动执行TestAop的before和after方法。
下面的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
package tiny;
import java.util.Map;
public interface Aop {
void before(Map<String,String> args);
void after(Map<String,String> args);
}
BindingAop.java
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
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
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天时间,做的比较粗糙,欢迎大家指导一下。提高下我自己。