我们都知道 Spring MVC 下,给类加一个 @Controller,给方法加一个 @RequestMapping,就可以完成客户端调用,访问资源,我们可以模拟一下 MVC 的实现
设计思路
- 写一个 Servlet 加载配置文件,也就是读取 Spring 的 xml
- 根据配置文件给定的目录扫描包,扫描加了 @Controller 注解的类,
- 当扫描到 @Controller 注解的类后,扫描类下面的所有方法,并判断上面是否加了 @RequestMapping 注解
- 底层定义一个集合,把符合要求的方法保存起来 Map
POM 文件
<dependencies><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><dependency><groupId>dom4j</groupId><artifactId>dom4j</artifactId><version>1.6.1</version></dependency><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.70</version></dependency></dependencies><build><finalName>web-mvc</finalName><plugins><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.2</version><configuration><port>8080</port><path>/</path></configuration></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-compiler-plugin</artifactId><version>3.3</version><configuration><source>1.8</source><target>1.8</target><compilerArgs><arg>-parameters</arg></compilerArgs></configuration></plugin></plugins></build>
核心类 DispathServlet
public class DispatcherServlet extends HttpServlet {private static Map<String, Method> methodMap = new LinkedHashMap<String, Method>();private static String prefix = "";private static String suffix = "";/** 项目根路径 */// /D:/Documents/Java/Spring/spring/web-mvc/target/classes/private static String PROJECT_ROOT_PATH;static {try {PROJECT_ROOT_PATH = URLDecoder.decode(DispatcherServlet.class.getResource("/").getPath(), "UTF-8");} catch (UnsupportedEncodingException e) {e.printStackTrace();}}private static final String XML_COMPONENT_SCAN_ELEMENT = "componentScan";private static final String XML_COMPONENT_SCAN_ELEMENT_PACKAGE_ATTR = "package";private static final String XML_VIEW_ELEMENT = "view";private static final String XML_VIEW_ELEMENT_PREFIX_ATTR = "prefix";private static final String XML_VIEW_ELEMENT_SUFFIX_ATTR = "suffix";@Overridepublic void init(ServletConfig config) throws ServletException {// 解析 web.xml, mvc.xmlString xmlPath = config.getInitParameter("xmlPath");Document document = parseXML(new File(PROJECT_ROOT_PATH + "//" + xmlPath));Element beans = document.getRootElement();Element componentScanEle = beans.element(XML_COMPONENT_SCAN_ELEMENT);String packageValue = componentScanEle.attribute(XML_COMPONENT_SCAN_ELEMENT_PACKAGE_ATTR).getValue();Element viewEle = beans.element(XML_VIEW_ELEMENT);prefix = viewEle.attribute(XML_VIEW_ELEMENT_PREFIX_ATTR).getValue();suffix = viewEle.attribute(XML_VIEW_ELEMENT_SUFFIX_ATTR).getValue();// 扫描文件doScan(new File(PROJECT_ROOT_PATH + packageValue.replace(".", File.separator)));super.init(config);}@Overrideprotected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {doPost(req, resp);}@Overrideprotected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {String requestURI = req.getRequestURI();Method method = methodMap.get(requestURI);if (method != null) {Parameter[] parameters = method.getParameters();Object[] objects = new Object[parameters.length];for (int i = 0; i < parameters.length; i++) {Parameter parameter = parameters[i];String name = parameter.getName();Class<?> type = parameter.getType();if (type.equals(String.class)) {objects[i] = req.getParameter(name);} else if (type.equals(HttpServletRequest.class)) {objects[i] = req;} else if (type.equals(HttpServletResponse.class)) {objects[i] = resp;} else if (type.getInterfaces()[0].equals(Entity.class)) {try {Object entity = type.newInstance();for (Field field : type.getDeclaredFields()) {field.setAccessible(true);String fieldName = field.getName();field.set(entity, req.getParameter(fieldName));}objects[i] = entity;} catch (Exception e) {e.printStackTrace();}}}try {Object controllerClass = method.getDeclaringClass().newInstance();Object ret = method.invoke(controllerClass, objects);Class<?> returnType = method.getReturnType();if (!returnType.equals(Void.class)) {ResponseBody responseBody = method.getAnnotation(ResponseBody.class);if (responseBody != null) {resp.setContentType("text/json;charset=UTF-8");resp.setCharacterEncoding("UTF-8");resp.getWriter().write(JSONObject.toJSONString(ret));} else {req.getRequestDispatcher(prefix + "/" + String.valueOf(ret) + suffix).forward(req, resp);}}} catch (Exception e) {e.printStackTrace();}} else {resp.setStatus(404);}}private void doScan(File file) {if (file.isDirectory()) {for (File _file : file.listFiles()) {doScan(_file);}} else {String filePath = file.getPath();String suffix = filePath.substring(filePath.lastIndexOf("."));if (suffix.equals(".class")) {String classPath = filePath.replace(new File(PROJECT_ROOT_PATH).getPath() + File.separator, "");classPath = classPath.replace(File.separator, ".");String className = classPath.substring(0, classPath.lastIndexOf("."));try {Class<?> clazz = Class.forName(className);if (clazz.isAnnotationPresent(Controller.class)) {RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);String classRequestMappingUrl = "";if (classRequestMapping != null) {classRequestMappingUrl = classRequestMapping.value();}for (Method method : clazz.getDeclaredMethods()) {RequestMapping methodAnnotation = method.getAnnotation(RequestMapping.class);if (methodAnnotation != null) {String methodRequestMappingUrl = "";methodRequestMappingUrl = methodAnnotation.value();String url = classRequestMappingUrl + methodRequestMappingUrl;System.out.println(clazz.getName() + " => " + method.getName() + " => " + url);methodMap.put(url, method);}}}} catch (ClassNotFoundException e) {e.printStackTrace();}}}}/*** 解析 XML 文件对象** @param file XML 文件*/private Document parseXML(File file) {SAXReader reader = new SAXReader();Document document = null;try {document = reader.read(file);} catch (DocumentException e) {e.printStackTrace();}return document;}}
注解类
@Target(ElementType.TYPE)@Retention(RetentionPolicy.RUNTIME)public @interface Controller {}@Target({ElementType.TYPE, ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)public @interface RequestMapping {String value() default "";}@Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface ResponseBody {}
测试类
@Controller@RequestMapping("/test")public class TestController {@ResponseBody@RequestMapping("/test.do")public Object test(String name, UserEntity userEntity, HttpServletRequest request, HttpServletResponse resp) {System.out.println(name);System.out.println(request);System.out.println(resp);System.out.println(userEntity);return userEntity;}@RequestMapping("/index.do")public Object view(HttpServletRequest request, HttpServletResponse resp) {return "index";}}
返回 JSON:
返回页面:
