今天突发奇想,弄个最最最简单的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();
}
}
来源:oschina
链接:https://my.oschina.net/u/933274/blog/215875