文章目录
1.环境配置
1.创建maven项目
2.创建文件目录
3.导入jar包
xml
复制代码
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>sun-springmvc</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>sun-springmvc Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>3.8.1</version>
<scope>test</scope>
</dependency>
<!--servlet原生api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<!--在项目打包时不会带上这个jar-->
<scope>provided</scope>
</dependency>
<!--解析xml-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--常用工具类-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
</dependencies>
<build>
<finalName>sun-springmvc</finalName>
</build>
</project>
2.开发核心控制器
文件目录
1.流程图
2.编写核心控制器SunDispatcherServlet.java
java
复制代码
package com.Sun.sunspringmvc.servlet;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 充当中央控制器
*
* @author 孙显圣
* @version 1.0
*/
public class SunDispatcherServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
}
3.类路径下编写spring配置文件sunspringmvc.xml
4.配置中央控制器web.xml
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>
<!--配置中央控制器-->
<servlet>
<servlet-name>SunDispatcherServlet</servlet-name>
<servlet-class>com.Sun.sunspringmvc.servlet.SunDispatcherServlet</servlet-class>
<!--init---param设置spring配置文件的位置-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:sunspringmvc.xml</param-value>
</init-param>
<!--服务器启动时实例化servlet,将其放到容器中,并且调用init方法-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SunDispatcherServlet</servlet-name>
<!--拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
5.配置tomcat,完成测试
1.配置发布方式
2.配置热加载
3.修改SunDispatcherServlet.java
4.完成测试
3.完成客户端/浏览器可以请求控制层
文件目录
1.思路分析
2.编写MonsterController.java
java
复制代码
package com.Sun.controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* @author 孙显圣
* @version 1.0
*/
public class MonsterController {
public void listMonster(HttpServletRequest request, HttpServletResponse response) {
//设置mine类型
response.setContentType("text/html;charset=utf-8");
try {
PrintWriter writer = response.getWriter();
writer.write("<h1>妖怪列表信息</h1>");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
3.自定义注解
1.Controller.java
java
复制代码
package com.Sun.sunspringmvc.annotation;
import java.lang.annotation.*;
/**
* 用于标识一个Controller
*
* @author 孙显圣
* @version 1.0
*/
@Target(ElementType.TYPE) //作用于类型
@Retention(RetentionPolicy.RUNTIME) //作用范围
@Documented
public @interface Controller {
}
2.RequestMapping.java
java
复制代码
package com.Sun.sunspringmvc.annotation;
import java.lang.annotation.*;
/**
* 用于指定映射路径
*
* @author 孙显圣
* @version 1.0
*/
@Target(ElementType.METHOD) //作用于方法
@Retention(RetentionPolicy.RUNTIME) //作用范围
@Documented
public @interface RequestMapping {
}
4.自定义容器(1),在tomcat启动时读取配置文件,获取要扫描的包的工作路径
1.SunWebApplicationContext.java
java
复制代码
package com.Sun.sunspringmvc.context;
import com.Sun.sunspringmvc.xml.XmlParser;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* @author 孙显圣
* @version 1.0
*/
public class SunWebApplicationContext {
//存放所有要扫描的包下的class文件的全路径
private List<String> classFullPathList = new ArrayList<String>();
//初始化容器
public void init() {
//读取spring配置文件,获取要扫描的包的信息
String basePage = XmlParser.getBasePage("sunspringmvc.xml");
//完成对指定包的扫描
scanPage(basePage);
}
//创建方法,完成对指定包的扫描,获取所有class文件的全路径
public void scanPage(String packFullName) {
//将包的全类名中的点替换为斜杠
String packPath = packFullName.replaceAll("\\.", "/");
//通过类加载器来获取这个包的工作路径,就是获取工作路径下的类路径下的文件路径
URL resource = SunWebApplicationContext.class.getClassLoader().getResource(packPath);
System.out.println(resource);
}
}
2.修改SunDispatcherServlet.java
java
复制代码
package com.Sun.sunspringmvc.servlet;
import com.Sun.sunspringmvc.context.SunWebApplicationContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 充当中央控制器
*
* @author 孙显圣
* @version 1.0
*/
public class SunDispatcherServlet extends HttpServlet {
@Override
public void init(ServletConfig config) throws ServletException {
//初始化容器
SunWebApplicationContext sunWebApplicationContext = new SunWebApplicationContext();
sunWebApplicationContext.init();
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doGet");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
System.out.println("doPost");
}
}
3.单元测试,启动tomcat
5.自定义容器(2),在tomcat启动的时候完成对指定包的扫描
1.修改SunWebApplicationContext.java
java
复制代码
package com.Sun.sunspringmvc.context;
import com.Sun.sunspringmvc.xml.XmlParser;
import java.io.File;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
/**
* @author 孙显圣
* @version 1.0
*/
public class SunWebApplicationContext {
//存放所有要扫描的包下的class文件的全路径
private List<String> classFullPathList = new ArrayList<String>();
//初始化容器
public void init() {
//读取spring配置文件,获取要扫描的包的信息
String basePage = XmlParser.getBasePage("sunspringmvc.xml");
//初始化容器
//根据逗号进行分割,得到多个要扫描的包的全路径,遍历将里面的class文件全路径放到列表中
String[] split = basePage.split(",");
for (String packPath : split) {
scanPage(packPath);
}
}
//创建方法,完成对指定包的扫描,获取所有class文件的全路径
public void scanPage(String packFullName) {
//将包的全类名中的点替换为斜杠
String packPath = packFullName.replaceAll("\\.", "/");
//通过类加载器来获取这个包的工作路径,就是获取工作路径下的类路径下的文件路径
URL url = SunWebApplicationContext.class.getClassLoader().getResource(packPath);
//得到路径
String file = url.getFile();
//根据这个文件夹来创建一个file对象,从而遍历里面所有的class文件得到所有class文件的全路径
File packDirectory = new File(file);
if (packDirectory.isDirectory()) {
//如果是文件夹则列出里面的所有文件对象
File[] files = packDirectory.listFiles();
//遍历这些文件对象,实际上就那个包下的所有class文件对象
for (File classFile : files) {
//如果这里的文件对象还是文件夹,则进行递归扫描
if (classFile.isDirectory()) {
scanPage(packFullName + "." + classFile.getName());
} else {
//如果这里的文件对象指的都是文件,则将其放到classFullPathList中
//得到当前文件的全类名 = 包的全路径 + class文件的名字去掉.class
String classFullPath = packFullName + "." + classFile.getName().replaceAll(".class", "");
//放到列表中
classFullPathList.add(classFullPath);
}
}
}
}
}
2.debug测试
6.将自定义容器(3),符合要求的类反射创建对象,放到单例池
1.修改SunWebApplicationContext.java增加方法,添加属性
java
复制代码
//编写方法,将符合要求的类反射创建对象,并封装到单例池中
public void executeInstance(){
//遍历所有全类名
for (String classPath : classFullPathList) {
try {
//反射
Class<?> aClass = Class.forName(classPath);
//判断是否有Controller注解
if (aClass.isAnnotationPresent(Controller.class)) {
//有注解,当他是单例的,反射创建bean对象,放到单例池中,默认首字母小写
//获取类名首字母小写
String name = aClass.getSimpleName().substring(0, 1).toLowerCase() + aClass.getSimpleName().substring(1);
//放到单例池中
singleObjects.put(name, aClass.newInstance());
}
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
2.debug查看单例池
7.完成url和控制器方法映射
1.创建映射bean,SunHandler.java
java
复制代码
package com.Sun.sunspringmvc.handler;
import java.lang.reflect.Method;
/**
* 用于存放有注解的类的映射信息
*
* @author 孙显圣
* @version 1.0
*/
public class SunHandler {
private String url; //映射的url
private Object controller; //controller对象
private Method method; //方法对象,用于反射调用方法
public SunHandler(String url, Object controller, Method method) {
this.url = url;
this.controller = controller;
this.method = method;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
@Override
public String toString() {
return "SunHandler{" +
"url='" + url + '\'' +
", controller=" + controller +
", method=" + method +
'}';
}
}
2.修改中央控制器SunWebApplicationContext.java添加方法和属性
java
复制代码
//初始化映射对象列表,获取映射对象并且将其放到映射列表中
private void initHandlerMapping() {
//判断单例池是否为空
if (sunWebApplicationContext.singleObjects.isEmpty()) {
return;
}
//取出单例池里的所有对象
for (Map.Entry<String, Object> entry : sunWebApplicationContext.singleObjects.entrySet()) {
//反射
Class<?> aClass = entry.getValue().getClass();
//判断是否有colltroller注解
if (aClass.isAnnotationPresent(Controller.class)) {
//反射获取所有方法对象
Method[] declaredMethods = aClass.getDeclaredMethods();
//判断方法里是否有requestmapping注解
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
//获取这个方法的注解信息
String url = declaredMethod.getAnnotation(RequestMapping.class).value();
//将信息封装到映射bean对象中
SunHandler sunHandler = new SunHandler(url, entry.getValue(), declaredMethod);
//添加到列表中
handlers.add(sunHandler);
}
}
}
}
}
3.debug查看映射对象列表
8.完成请求分发到目标方法
1.修改SunDispatcherServlet.java,添加两个方法并在dopost中请求分发
java
复制代码
package com.Sun.sunspringmvc.servlet;
import com.Sun.sunspringmvc.annotation.Controller;
import com.Sun.sunspringmvc.annotation.RequestMapping;
import com.Sun.sunspringmvc.context.SunWebApplicationContext;
import com.Sun.sunspringmvc.handler.SunHandler;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* 充当中央控制器
*
* @author 孙显圣
* @version 1.0
*/
public class SunDispatcherServlet extends HttpServlet {
//存放所有的映射关系
private List<SunHandler> handlers = new ArrayList<SunHandler>();
private SunWebApplicationContext sunWebApplicationContext = null;
@Override
public void init(ServletConfig config) throws ServletException {
//初始化容器
sunWebApplicationContext = new SunWebApplicationContext();
sunWebApplicationContext.init();
//初始化映射列表
initHandlerMapping();
System.out.println("ss");
}
@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 {
//请求分发
executeDispatch(req, resp);
}
//初始化映射对象列表,获取映射对象并且将其放到映射列表中
private void initHandlerMapping() {
//判断单例池是否为空
if (sunWebApplicationContext.singleObjects.isEmpty()) {
return;
}
//取出单例池里的所有对象
for (Map.Entry<String, Object> entry : sunWebApplicationContext.singleObjects.entrySet()) {
//反射
Class<?> aClass = entry.getValue().getClass();
//判断是否有colltroller注解
if (aClass.isAnnotationPresent(Controller.class)) {
//反射获取所有方法对象
Method[] declaredMethods = aClass.getDeclaredMethods();
//判断方法里是否有requestmapping注解
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
//获取这个方法的注解信息
String url = declaredMethod.getAnnotation(RequestMapping.class).value();
//将信息封装到映射bean对象中
SunHandler sunHandler = new SunHandler(url, entry.getValue(), declaredMethod);
//添加到列表中
handlers.add(sunHandler);
}
}
}
}
}
//根据请求对象得到映射对象
private SunHandler getSunHandler(HttpServletRequest request) {
//获取uri: /sun-springmvc/list/monster
String requestURI = request.getRequestURI();
String contextPath = request.getServletContext().getContextPath();
//遍历映射对象列表,查看列表中是否有这个uri
for (SunHandler handler : handlers) {
//这里拼接一个上下文路径
if ((contextPath + "/" + handler.getUrl()).equals(requestURI)) {
//返回这个映射对象
return handler;
}
}
return null;
}
//请求分发
private void executeDispatch(HttpServletRequest request, HttpServletResponse response) {
//获取映射对象
SunHandler sunHandler = getSunHandler(request);
//映射对象不等于空则反射调用controller的方法
if (sunHandler != null) {
try {
sunHandler.getMethod().invoke(sunHandler.getController(), request, response);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
} else {
//当映射对象是空的时候,返回404
try {
response.getWriter().write("<h1>404 not found!</h1>");
} catch (IOException e) {
throw new RuntimeException(e);
}
}
}
}
2.单元测试
4.当前阶段完成的功能
1.初始化阶段
- tomcat服务器启动,自动装载中央控制器(servlet),调用init方法
- 初始化spring容器
- 创建spring容器实例,调用init方法
- 读取spring配置文件,得到要扫描的包的工作路径
- 扫描指定的包,获取所有class文件的全路径
- 扫描所有class文件,将包含Controller注解的类反射创建对象放到单例池中(这里假设都是单例的)
- 初始化映射对象列表
- 扫描所有单例池中的对象
- 反射获取这个对象对应类的所有方法,如果方法包含RequestMapping注解,则将这个对象,url,Method对象封装到映射对象中,并且添加到映射对象列表
2.完成请求分发
- 根据请求对象得到映射对象
- 获取请求的uri
- 遍历对象映射列表查看是否有匹配的映射对象,如果有则返回映射对象
- 请求分发
- 首先根据请求对象得到映射对象
- 如果得到了就反射调用方法
- 没有得到则返回404