1、最简单的mvc框架tiny,3个类,彻底0配置,0注解

时光毁灭记忆、已成空白 提交于 2020-04-07 05:31:16

今天突发奇想,弄个最简单的mvc,叫tiny,用3个类(2个类也可以FrontControl.java和Container.java)就可以实现mvc功能,彻底0配置,web.xml都不用配置。

类太少了,我不往git和svn上传了,贴代码;

FrontControl.java为一个filter,init初始化所有后缀名为Action.class的类,为action类,保存。

doFilter方法将截获到的/ty/*开头的请求(你可以自己修改下,注意有2处地方),做解析成路由,路由格式为“/类/方法”,在解析为数组,传给调用方法。

处理返回类型,有3种,void不处理;/开后,认为具体地址,如/userList.jsp;Renderer渲染器,我默认实现很多种(jsp、javascript、text、二进制、文件),你可以自己重写。

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.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 = { "/ty/*" }, 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();
            }
        }
        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;
            }
			Object result = o.getClass().getMethod(routes[1],Map.class).invoke(o,this.converter(req.getParameterMap()));
	        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;
    }
    @Override
    public void destroy() {
    }
}


初始化所有后缀为Action.class的类

Container.java

package tiny;

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>();
    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 Object getCls(String name){
		return clsMap.get(name);
	}
}


用本地线程保存servlet的环境

ContextUtil.java

package tiny;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

public final class ContextUtil {

    private static final ThreadLocal<ContextUtil> ContextThreadLocal = new ThreadLocal<ContextUtil>();

    private ServletContext context;
    private HttpServletRequest request;
    private HttpServletResponse response;

    public ServletContext getServletContext() {
        return context;
    }

    public HttpServletRequest getHttpServletRequest() {
        return request;
    }

    public HttpServletResponse getHttpServletResponse() {
        return response;
    }

    public HttpSession getHttpSession() {
        return request.getSession();
    }

    public static ContextUtil getContext() {
        return ContextThreadLocal.get();
    }

    public static void setActionContext(ServletContext context, HttpServletRequest request, HttpServletResponse response) {
    	ContextUtil ctx = new ContextUtil();
        ctx.context = context;
        ctx.request = request;
        ctx.response = response;
        ContextThreadLocal.set(ctx);
    }

    public static void removeActionContext() {
    	ContextThreadLocal.remove();
    }
}


约定规则:

1、所有方法(想变成action的方法)参数类型为map<String,String>

2、action的访问地址为

http://ip/xx/ty/类名(不带Action)/方法名,即要调用该类的这个方法。

3、类名必须为xxAction


例子,你可以返回一个jsp地址(比如以/开后),或一个渲染器

http://localhost/tiny/ty/TinyTest/hello?aa=0000

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


后期增强

如果有时间,我想在开发一个,java调用前台页面的功能,有很多都可以实现,我这里就想也弄一个最最最简单的这个功能,呵呵。


渲染器所有的类,这个是我写的渲染器。

package tiny;

import java.io.OutputStream;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class BinaryRenderer extends Renderer {

    private byte[] data;

    public byte[] getData() {
        return data;
    }

    public void setData(byte[] data) {
        this.data = data;
    }

    @Override
    public void render(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws Exception {
        response.setContentType(contentType==null ? "application/octet-stream" : contentType);
        response.setContentLength(data.length);
        OutputStream output = response.getOutputStream();
        output.write(data);
        output.flush();
    }
}
package tiny;

import java.io.BufferedInputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.io.OutputStream;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class FileRenderer extends Renderer {

    private File file;

    public FileRenderer() {
    }

    public FileRenderer(File file) {
        this.file = file;
    }

    public FileRenderer(String file) {
        this.file = new File(file);
    }

    public File getFile() {
        return file;
    }

    public void setFile(File file) {
        this.file = file;
    }

    @Override
    public void render(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws Exception {
        if (file==null || !file.isFile() || file.length()>Integer.MAX_VALUE) {
            response.sendError(HttpServletResponse.SC_NOT_FOUND);
            return;
        }
        String mime = contentType;
        if (mime==null) {
            mime = context.getMimeType(file.getName());
            if (mime==null) {
                mime = "application/octet-stream";
            }
        }
        response.setContentType(mime);
        response.setContentLength((int)file.length());
        InputStream input = null;
        try {
            input = new BufferedInputStream(new FileInputStream(file));
            OutputStream output = response.getOutputStream();
            byte[] buffer = new byte[4096];
            for (;;) {
                int n = input.read(buffer);
                if (n==(-1))
                    break;
                output.write(buffer, 0, n);
            }
            output.flush();
        }
        finally {
            if (input!=null) {
                input.close();
            }
        }
    }

}
package tiny;

public class JavaScriptRenderer extends TextRenderer {

    static final String MIME_JAVASCRIPT = "application/x-javascript";

    public JavaScriptRenderer() {
        setContentType(MIME_JAVASCRIPT);
    }

    public JavaScriptRenderer(String text) {
        super(text);
        setContentType(MIME_JAVASCRIPT);
    }

    public JavaScriptRenderer(String text, String characterEncoding) {
        super(text, characterEncoding);
        setContentType(MIME_JAVASCRIPT);
    }

}
package tiny;

import java.util.HashMap;
import java.util.Map;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class JspRenderer extends Renderer {

    private String path;
    private Map<String, Object> model;
    protected String contentType;

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }
    public JspRenderer(String path) {
        this.path = path;
        this.model = new HashMap<String, Object>();
    }

    public JspRenderer(String path, Map<String, Object> model) {
        this.path = path;
        this.model = model;
    }

    public JspRenderer(String path, String modelKey, Object modelValue) {
        this.path = path;
        this.model = new HashMap<String, Object>();
        this.model.put(modelKey, modelValue);
    }
    @Override
    public void render(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws Exception {
        Set<String> keys = model.keySet();
        for (String key : keys) {
            request.setAttribute(key, model.get(key));
        }
        request.getRequestDispatcher(path).forward(request, response);
    }
}
package tiny;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public abstract class Renderer {

    protected String contentType;

    public String getContentType() {
        return contentType;
    }

    public void setContentType(String contentType) {
        this.contentType = contentType;
    }

    public abstract void render(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws Exception;

}
package tiny;

import java.io.PrintWriter;

import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class TextRenderer extends Renderer {

    private String characterEncoding;
    private String text;

    public TextRenderer() {
    }

    public TextRenderer(String text) {
        this.text = text;
    }

    public TextRenderer(String text, String characterEncoding) {
        this.text = text;
        this.characterEncoding = characterEncoding;
    }

    public String getCharacterEncoding() {
        return characterEncoding;
    }

    public void setCharacterEncoding(String characterEncoding) {
        this.characterEncoding = characterEncoding;
    }

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    @Override
    public void render(ServletContext context, HttpServletRequest request, HttpServletResponse response) throws Exception {
        StringBuilder sb = new StringBuilder(64);
        sb.append(contentType==null ? "text/html" : contentType)
          .append(";charset=")
          .append(characterEncoding==null ? "UTF-8" : characterEncoding);
        response.setContentType(sb.toString());
        PrintWriter pw = response.getWriter();
        pw.write(text);
        pw.flush();
    }

}


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!