源码地址:https://github.com/shawntime/springmvc-tutorial/tree/master/02-simple-mvc
一个简单的MVC模型实现
自定义实现一个简单的mvc模型,使用效果如下
package com.shawntime.simplemvc.controller;
import javax.servlet.http.HttpServletRequest;
import com.shawntime.simplemvc.annotation.Controller;
import com.shawntime.simplemvc.annotation.RequestMapping;
import com.shawntime.simplemvc.annotation.RequestParameter;
import com.shawntime.simplemvc.entity.User;
/**
* Created by shma on 2017/5/21.
*/
@Controller(url = "/user")
public class LoginController {
@RequestMapping(url = "/login", method = "POST")
public String login(@RequestParameter(value = "userName")String userName,
@RequestParameter(value = "password")String password,
HttpServletRequest request) {
User user = new User();
user.setUserName(userName);
user.setPassword(password);
return "success:" + user;
}
}
定义注解
package com.shawntime.simplemvc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by shma on 2017/5/21.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String url() default "";
}
package com.shawntime.simplemvc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by shma on 2017/5/21.
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String url() default "";
String method() default "GET";
}
package com.shawntime.simplemvc.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Created by shma on 2017/5/21.
*/
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestParameter {
String value() default "";
}
定义ServletContextListener监听,启动服务时会扫描controller包下所有类和方法,生成相关映射关系保存到ServletContext中
package com.shawntime.simplemvc.listener;
import java.util.Map;
import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
/**
* Created by shma on 2017/5/21.
*/
public class ControllerHandleListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent servletContextEvent) {
ServletContext servletContext = servletContextEvent.getServletContext();
String packageName = servletContext.getInitParameter("scanPackageName");
Map<String, MappingBean> mappingBeanMap = ControllerHandler.handle(packageName);
servletContext.setAttribute("methodHandlerMap", mappingBeanMap);
}
public void contextDestroyed(ServletContextEvent servletContextEvent) {
// ----
}
}
package com.shawntime.simplemvc.listener;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.List;
import java.util.Map;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.shawntime.simplemvc.annotation.Controller;
import com.shawntime.simplemvc.annotation.RequestMapping;
import com.shawntime.simplemvc.annotation.RequestParameter;
import com.shawntime.simplemvc.utils.PackageUtil;
import org.apache.commons.collections4.CollectionUtils;
/**
* Created by shma on 2017/5/21.
*/
public final class ControllerHandler {
public static final Map<String, MappingBean> handle(String packageName) {
Map<String, MappingBean> dataMap = Maps.newHashMap();
List<String> classNameList = PackageUtil.getClassName(packageName);
if (CollectionUtils.isNotEmpty(classNameList)) {
for (String className : classNameList) {
try {
Map<String, MappingBean> mappingBean = getMappingBean(className);
dataMap.putAll(mappingBean);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return dataMap;
}
private static Map<String, MappingBean> getMappingBean(String className) throws ClassNotFoundException {
Map<String, MappingBean> resultMap = Maps.newHashMap();
Class<?> aClass = Class.forName(className);
Controller controller = aClass.getAnnotation(Controller.class);
String headerUrl = controller.url();
Method[] methods = aClass.getDeclaredMethods();
if (methods != null && methods.length > 0) {
for (Method method : methods) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String url = headerUrl + requestMapping.url();
String requestType = requestMapping.method();
List<ParamBean> paramBeans = loadParam(method);
MappingBean mappingBean = new MappingBean();
mappingBean.setUrl(className);
mappingBean.setMethodType(requestType);
mappingBean.setMethodName(method.getName());
mappingBean.setParamBeanList(paramBeans);
mappingBean.setClazz(method.getParameterTypes());
resultMap.put(url, mappingBean);
}
}
return resultMap;
}
private static List<ParamBean> loadParam(Method method) {
List<ParamBean> paramBeanList = Lists.newArrayList();
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
ParamBean paramBean = new ParamBean();
RequestParameter annotation = parameter.getAnnotation(RequestParameter.class);
if (annotation != null) {
paramBean.setParamName(annotation.value());
}
paramBean.setParamType(parameter.getType());
paramBeanList.add(paramBean);
}
return paramBeanList;
}
}
package com.shawntime.simplemvc.listener;
import java.util.List;
/**
* Created by shma on 2017/5/21.
*/
public class MappingBean {
private String url;
private String methodName;
private String methodType;
private Class<?>[] clazz;
private List<ParamBean> paramBeanList;
public Class<?>[] getClazz() {
return clazz;
}
public void setClazz(Class<?>[] clazz) {
this.clazz = clazz;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public String getMethodType() {
return methodType;
}
public void setMethodType(String methodType) {
this.methodType = methodType;
}
public List<ParamBean> getParamBeanList() {
return paramBeanList;
}
public void setParamBeanList(List<ParamBean> paramBeanList) {
this.paramBeanList = paramBeanList;
}
}
package com.shawntime.simplemvc.listener;
/**
* Created by shma on 2017/5/22.
*/
public class ParamBean {
private String paramName;
private Class<?> paramType;
public String getParamName() {
return paramName;
}
public void setParamName(String paramName) {
this.paramName = paramName;
}
public Class<?> getParamType() {
return paramType;
}
public void setParamType(Class<?> paramType) {
this.paramType = paramType;
}
}
创建DispatcherServlet调度
package com.shawntime.simplemvc.servlet;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.shawntime.simplemvc.listener.MappingBean;
import com.shawntime.simplemvc.listener.ParamBean;
import com.shawntime.simplemvc.utils.UriUtil;
/**
* Created by shma on 2017/5/22.
*/
public class DispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doParse(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doParse(req, resp);
}
private void doParse(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
Map<String, MappingBean> mappingBeanMap =
(Map<String, MappingBean>) this.getServletContext().getAttribute("methodHandlerMap");
String uri = UriUtil.getUri(req.getContextPath(), req.getRequestURI().toString());
if (uri.equals("/")) {
return;
}
MappingBean mappingBean = mappingBeanMap.get(uri);
if (mappingBean == null) {
PrintWriter writer = resp.getWriter();
writer.write("请求url不存在");
writer.flush();
}
try {
Class<?> aClass = Class.forName(mappingBean.getUrl());
Method method = aClass.getDeclaredMethod(mappingBean.getMethodName(), mappingBean.getClazz());
Object object = aClass.newInstance();
Object[] params = getParams(mappingBean.getParamBeanList(), req);
Object invoke = method.invoke(object, params);
PrintWriter writer = resp.getWriter();
writer.write(invoke.toString());
writer.flush();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private static Object[] getParams(List<ParamBean> paramBeanList, HttpServletRequest request) {
Object[] paramObj = new Object[paramBeanList.size()];
for (int i = 0; i < paramBeanList.size(); ++i) {
String paramName = paramBeanList.get(i).getParamName();
Class<?> paramType = paramBeanList.get(i).getParamType();
if (paramType.isInstance(request)) {
paramObj[i] = request;
} else {
paramObj[i] = request.getParameter(paramName);
}
}
return paramObj;
}
}
package com.shawntime.simplemvc.utils;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
public class PackageUtil {
public static void main(String[] args) {
String packageName = "com.shawntime.simplemvc.controller";
List<String> classNames = getClassName(packageName);
for (String className : classNames) {
System.out.println(className);
}
}
public static List<String> getClassName(String packageName) {
String filePath = Thread.currentThread().getContextClassLoader().getResource("").getPath()
+ packageName.replace(".", "\\");
List<String> fileNames = getClassName(filePath, null);
return fileNames;
}
private static List<String> getClassName(String filePath, List<String> className) {
List<String> myClassName = new ArrayList<String>();
File file = new File(filePath);
File[] childFiles = file.listFiles();
for (File childFile : childFiles) {
if (childFile.isDirectory()) {
myClassName.addAll(getClassName(childFile.getPath(), myClassName));
} else {
String childFilePath = childFile.getPath();
childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));
childFilePath = childFilePath.replace("\\", ".");
myClassName.add(childFilePath);
}
}
return myClassName;
}
}
package com.shawntime.simplemvc.utils;
/**
* Created by shma on 2017/5/22.
*/
public final class UriUtil {
private UriUtil() {
// -----
}
public static String getUri(String contextPath, String uri) {
if (uri.contains("?")) {
return uri.substring(contextPath.length(), uri.indexOf("?"));
} else {
return uri.substring(contextPath.length(), uri.length());
}
}
}
web.xml配置
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>scanPackageName</param-name>
<param-value>com.shawntime.simplemvc.controller</param-value>
</context-param>
<listener>
<listener-class>com.shawntime.simplemvc.listener.ControllerHandleListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>com.shawntime.simplemvc.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>scanPackageName</param-name>
<param-value>com.shawntime.simplemvc.controller</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>