SpringMVC介绍
- SpringMVC基于Servlet去实现的。
- Servlet是单例的,所以也是存在线程安全问题的。线程不安全。
- SpringMVC基于Servlet实现,所以也是线程不安全的。
- 判断是单例还是多例就看其构造函数执行几次,创建多个对象,构造函数调用了一次就是单例
什么是Servlet
- Java Servlet是运行在web服务器或应用服务器上的程序,他是作为来自web浏览器或者其他Http客户端的请求和HTTP服务器上的数据库或者应用程序之间的中间层。
- 使用Servlet,可以收集来自网页表单的用户输入,呈现来自数据库或者其他资源的记录,还可以动态创建网页
- Servlet是独立于平台的,用Java编写的
- Java类库的全部功能堆Servlet来说都是可用的。
Servlet的生命周期
- Servlet加载------>实例化 ------->服务 ------->销毁
- init()
在Servlet的生命周期中,仅仅执行一次init()方法,他是在服务器装入Servlet的时候进行执行的,负责初始化Servlet对象。可以配置服务器,让其在启动服务器或者客户机首次访问Servlet的时候装入Servlet。无论多少个客户机访问Servlet,都不会重复执行init()
- service()
他是Servlet的核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,还对象的Service方法就会被调用,而且传递给这个方法一个请求(ServletRequest)对象和一个响应(ServletResponse)对象作为参数。在HttpServlet中存在Service()方法。默认的服务功能与调用HTTP请求的方法相应的doXXX功能。
- destory()
仅仅执行一次,在服务器端停止且卸载Servlet时执行该方法。当Servlet对象退出生命周期的时候,负责释放占用的资源。一个Servlet在运行service()方法的时候会产生其他的线程,因此需要确认在调用destory()方法的时候,这些线程已经终止或者执行完成了。
SpringMVC的运行原理
- 核心就是DispatcherServlet
- 原理图如下所示:
- (1)用户发送请求到前端控制器DispatcherServlet
(2)DispatcherServlet收到请求调用HandlerMapping处理器映射器(就是通过请求地址找到对应的方法。)
(3)处理器映射器根据请求url找到具体的处理器,生成处理器对象以及处理器拦截器(如果有作为生成)一并返回给DisPatcherServlet。
(4)DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
(5)执行处理器(Controller)
(6)Controlelr执行完成返回ModelAndView
(7)HandlerAdapter将Controller执行结果ModelAndView返回给DispatcherServlet
(8)DispatcherServlet将ModelAndView传给viewResolver视图解析器
(9)viewResolver解析后返回具体的view
(10)DispatcherServlet对View进行渲染
(11)DispatcherServlet响应用户 - SpringMVC就是基于Servlet去实现的。
手写SpringMVC框架
- 思路:
(1)创建一个前端控制器,拦截所有的请求(SpringMVC基于Servlet实现)
(2)初始化操作,重写Servlet的init()方法
----(2.1)扫描包。找寻哪些类上有指定注解,加载到SpringIOC容器中,存放到map中,key是类名小写,value是该类对象
----(2.2)将url映射和方法进行关联
----------(2.2.1)判断类上是否有注解。使用java反射机制遍历方法,判断方法上是否有注解,有注解取出方法的url地址,存入map。key是url,value是对应方法(类名+方法)。
(3)处理请求(重写get和post方法)
---- (3.1)获取请求的url,获取类的实例结果,获取成功后,调用key获取方法名的集合,使用反射机制执行 - 这里我们可以建立三个map,一个key是类名的id(类名小写,value是对象);第二个是存储url和类之间的关系,key是请求地址,value是类的反射对象;最后一个存放url和方法之间的关系,key是url,value是方法的反射对象。
- java代码设计:
(1)代码结构图:
(2)index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<h1>这是我第一次手写springmvc.....</h1>
<h1>每特教育!!!!!</h1>
</body>
</html>
(3)web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<!-- Spring MVC 核心控制器 DispatcherServlet 配置 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>com.xiyou.ext.ExtDispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!-- 拦截所有/* 的请求,交给DispatcherServlet处理,性能最好 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
(4)自定义注解
- ExtController:
package com.xiyou.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解 功能类似@Controller
* @author zyf
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtController {
}
- ExtRequestMapping
package com.xiyou.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 自定义注解,类似于@RequestMapping
* @Author
*/
// 表示可以放在类上或者方法上
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRequestMapping {
/**
* 定义value属性,默认值是""
* @return
*/
String value() default "";
}
(5)Controller层:
package com.xiyou.controller;
import com.xiyou.annotation.ExtController;
import com.xiyou.annotation.ExtRequestMapping;
@ExtController
@ExtRequestMapping("/ext")
public class ExtIndexController {
@ExtRequestMapping("/test")
public String test() {
System.out.println("手写springmvc框架...");
return "index";
}
}
(6)工具类(反射用的,主要是用来处理找到指定包下所有的类)
package com.xiyou.utils;
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
public class ClassUtil {
/**
* 取得某个接口下所有实现这个接口的类
*/
public static List<Class> getAllClassByInterface(Class c) {
List<Class> returnClassList = null;
if (c.isInterface()) {
// 获取当前的包名
String packageName = c.getPackage().getName();
// 获取当前包下以及子包下所以的类
List<Class<?>> allClass = getClasses(packageName);
if (allClass != null) {
returnClassList = new ArrayList<Class>();
for (Class classes : allClass) {
// 判断是否是同一个接口
if (c.isAssignableFrom(classes)) {
// 本身不加入进去
if (!c.equals(classes)) {
returnClassList.add(classes);
}
}
}
}
}
return returnClassList;
}
/*
* 取得某一类所在包的所有类名 不含迭代
*/
public static String[] getPackageAllClassName(String classLocation, String packageName) {
// 将packageName分解
String[] packagePathSplit = packageName.split("[.]");
String realClassLocation = classLocation;
int packageLength = packagePathSplit.length;
for (int i = 0; i < packageLength; i++) {
realClassLocation = realClassLocation + File.separator + packagePathSplit[i];
}
File packeageDir = new File(realClassLocation);
if (packeageDir.isDirectory()) {
String[] allClassName = packeageDir.list();
return allClassName;
}
return null;
}
/**
* 从包package中获取所有的Class
*
* @param packageName
* @return
*/
public static List<Class<?>> getClasses(String packageName) {
// 第一个class类的集合
List<Class<?>> classes = new ArrayList<Class<?>>();
// 是否循环迭代
boolean recursive = true;
// 获取包的名字 并进行替换
String packageDirName = packageName.replace('.', '/');
// 定义一个枚举的集合 并进行循环来处理这个目录下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
// 循环迭代下去
while (dirs.hasMoreElements()) {
// 获取下一个元素
URL url = dirs.nextElement();
// 得到协议的名称
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服务器上
if ("file".equals(protocol)) {
// 获取包的物理路径
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式扫描整个包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath, recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定义一个JarFile
JarFile jar;
try {
// 获取jar
jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 从此jar包 得到一个枚举类
Enumeration<JarEntry> entries = jar.entries();
// 同样的进行循环迭代
while (entries.hasMoreElements()) {
// 获取jar里的一个实体 可以是目录 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/开头的
if (name.charAt(0) == '/') {
// 获取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定义的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"结尾 是一个包
if (idx != -1) {
// 获取包名 把"/"替换成"."
packageName = name.substring(0, idx).replace('/', '.');
}
// 如果可以迭代下去 并且是一个包
if ((idx != -1) || recursive) {
// 如果是一个.class文件 而且不是目录
if (name.endsWith(".class") && !entry.isDirectory()) {
// 去掉后面的".class" 获取真正的类名
String className = name.substring(packageName.length() + 1, name.length() - 6);
try {
// 添加到classes
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
/**
* 以文件的形式来获取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName, String packagePath, final boolean recursive,
List<Class<?>> classes) {
// 获取此包的目录 建立一个File
File dir = new File(packagePath);
// 如果不存在或者 也不是目录就直接返回
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 如果存在 就获取包下的所有文件 包括目录
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定义过滤规则 如果可以循环(包含子目录) 或则是以.class结尾的文件(编译好的java类文件)
public boolean accept(File file) {
return (recursive && file.isDirectory()) || (file.getName().endsWith(".class"));
}
});
// 循环所有文件
for (File file : dirfiles) {
// 如果是目录 则继续扫描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java类文件 去掉后面的.class 只留下类名
String className = file.getName().substring(0, file.getName().length() - 6);
try {
// 添加到集合中去
classes.add(Class.forName(packageName + '.' + className));
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
}
// 首字母转小写
public static String toLowerCaseFirstOne(String s) {
if (Character.isLowerCase(s.charAt(0))) {
return s;
}
else{
return (new StringBuilder()).append(Character.toLowerCase(s.charAt(0))).append(s.substring(1)).toString();
}
}
// 初始化对象
public static Object newInstance(Class<?> classInfo)
throws ClassNotFoundException, InstantiationException, IllegalAccessException {
return classInfo.newInstance();
}
}
(7)重要,自定义实现逻辑,实际上依靠servlet完成,解析注解的过程在Servlet的init()方法中
package com.xiyou.ext;
import com.xiyou.annotation.ExtController;
import com.xiyou.annotation.ExtRequestMapping;
import com.xiyou.utils.ClassUtil;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
/**
* 自定义一个Servlet
* 该Servlet用来解析自定义注解,完成注解功能
* @Author zyf
*/
public class ExtDispatcherServlet extends HttpServlet{
/**
* 该Map用来存储上面有指定注解的类的信息,key是类的首字母小写(默认的beanId)
*/
private ConcurrentHashMap<String, Object> springMvcBeans = new ConcurrentHashMap<>();
/**
* 定义map,key是映射的url(宽窄路径),value是当前类的对象
*/
private ConcurrentHashMap<String, Object> urlBeans = new ConcurrentHashMap<String, Object>();
/**
* 定义Map,key是映射的url(宽窄路径),value是当前的方法名
*/
private ConcurrentHashMap<String, String> urlMethods = new ConcurrentHashMap<String, String>();
/**
* Servlet初始化的时候会调用该方法,该方法只会调用一次
* @throws ServletException
*/
@Override
public void init() throws ServletException {
// 获取指定包下面的所有的类信息
List<Class<?>> classes = ClassUtil.getClasses("com.xiyou.controller");
try {
// 1. 判断哪些类是上面有ExtController注解的,有注解的存入Map
findClassMVCAnnotation(classes);
} catch (Exception e) {
e.printStackTrace();
}
// 2. 将宽窄路径和具体的方法和类映射起来,存入两个map中
handlerMapping();
}
/**
* 将宽窄路径进行和类以及方法的绑定,将其保存在两个定义好的map中
*/
public void handlerMapping() {
// 遍历上面有指定注解的所有的类
for (Map.Entry<String, Object> mvcBean : springMvcBeans.entrySet()) {
// 得到类的对象
Object object = mvcBean.getValue();
// 判断该对象的上面是否有@ExtRequestMapping
Class<?> classInfo = object.getClass();
ExtRequestMapping declaredAnnotation = classInfo.getDeclaredAnnotation(ExtRequestMapping.class);
// 用来保存类上面的路径
String baseUrl = "";
if (declaredAnnotation != null) {
baseUrl = declaredAnnotation.value();
}
// 判断方法上有没有指定的注解
Method[] declaredMethods = classInfo.getDeclaredMethods();
for (Method method : declaredMethods) {
// 判断方法上有无注解
ExtRequestMapping methodExtRequestMapping = method.getDeclaredAnnotation(ExtRequestMapping.class);
if (methodExtRequestMapping != null) {
// 方法上的路径
String methodUrl = methodExtRequestMapping.value();
if (StringUtils.isNotEmpty(baseUrl)) {
methodUrl = baseUrl + methodUrl;
}
// 保存两个map(一个保存类信息,一个保存方法信息)
urlBeans.put(methodUrl, object);
urlMethods.put(methodUrl, method.getName());
}
}
}
}
/**
* 找到当前类上有指定注解的类,将其保存到map中,key是类的首字母小写,value是类对象
* @param classes 传进来的是扫描指定包下的所有的类
*/
public void findClassMVCAnnotation(List<Class<?>> classes) throws InstantiationException, IllegalAccessException, ClassNotFoundException {
// 遍历判断是否有指定的注解在类的上面
for (Class<?> classInfo : classes) {
// 判断类上是否有注解
ExtController annotation = classInfo.getDeclaredAnnotation(ExtController.class);
if (annotation != null) {
// 不为空表示的是该类上有ExtController注解
String beanId = ClassUtil.toLowerCaseFirstOne(classInfo.getSimpleName());
// 实例化对象
Object object = ClassUtil.newInstance(classInfo);
// 存入map中
springMvcBeans.put(beanId, object);
}
}
}
/**
* 根据返回结果映射到指定的jsp上
* @param pageName
* @param req
* @param res
*/
public void extResourceViewResolver(String pageName, HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException {
String prefix = "/";
String suffix = ".jsp";
// 默认是转发请求
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
}
/**
* 反射调用获取方法返回结果
* @param object 类的对象
* @param methodName 方法名称
* @return
*/
public Object methodInvoke(Object object, String methodName){
try {
Class<?> classInfo = object.getClass();
// 得到对应方法名称的方法
Method method = classInfo.getMethod(methodName);
// 执行方法
Object result = method.invoke(object);
return result;
} catch (Exception e){
return null;
}
}
@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 {
// 1. 获取请求的url地址
String requestURI = req.getRequestURI();
if (StringUtils.isEmpty(requestURI)) {
return;
}
// 2. 从map中获取控制的对象
Object object = urlBeans.get(requestURI);
if (object == null) {
resp.getWriter().println(" not found 404 url");
return;
}
// 3. 从url中获取方法
String methodName = urlMethods.get(requestURI);
if (StringUtils.isEmpty(methodName)) {
resp.getWriter().println(" not found method");
}
// 4. 使用java的反射机制调用方法
String result = (String) methodInvoke(object, methodName);
// 5. 视图解析
extResourceViewResolver(result, req, resp);
}
}