我们都知道 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";
@Override
public void init(ServletConfig config) throws ServletException {
// 解析 web.xml, mvc.xml
String 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);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected 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:
返回页面: