zhongliwen 2020-06-13
1.添加依赖
<dependencies>
<!--springmvc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
</dependencies>2.springmvc.xml配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd ">
<!-- 下面是配置扫描包的位置,包名为com.ework.upms.server,也就是说,我们的试图解析器应该放在com.ework.upms.server包下. -->
<context:component-scan base-package="com.lusai"/>
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 前缀,我们的视图文件应该放到/WEB-INF/views/目录下,这里我们需要在WEB-INF下面创建view文件夹 -->
<property name="prefix" value="/WEB-INF/views/" />
<!-- 设置后缀为.jsp -->
<property name="suffix" value=".jsp" />
</bean>
</beans>3.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>
<!-- Spring的配置-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springmvc.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- springMVC的核心控制器 -->
<servlet>
<servlet-name>springMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>4.控制层
package com.lusai;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class IndexController {
@RequestMapping("/hello")
@ResponseBody
public String hello() {
return "hello";
}
}5.测试http://localhost:8080/springmvcDemo/hello
1、配置web.xml,加载自定义的DispatcherServlet。
2、初始化阶段,在DispatcherServlet类中,实现下面几个步骤:
3、运行阶段,每一次请求将会调用doGet或doPost方法,它会根据url请求去HandlerMapping中匹配到对应的Method,然后利用反射机制调用Controller中的url对应的方法,并得到结果返回。
开始模拟
1.修改maven依赖,注释springmvc依赖,添加servlet和dom4j依赖
<!--springmvc-->
<!--<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>-->
<!--dom4j-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>2.修改web.xml
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.zzw.cn.springmvc.dispathcer.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>自定义DispatcherServlet
package com.zzw.cn.springmvc.dispathcer;
import com.zzw.cn.springmvc.annoation.AnController;
import com.zzw.cn.springmvc.annoation.AnRequestMapping;
import com.zzw.cn.utils.ClassUtils;
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.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @author Simple
* @date 14:34 2019/8/26
* @description 手写springmvc框架流程
* <p>
* 思路:自定义DispatcherServlet
* 1.包扫描获取包下面所有的类
* 2.初始化包下面所有的类
* 3.初始化HandlerMapping方法,将url和方法对应上
* 4.实现HttpServlet 重写dopost方法
*/
public class DispatcherServlet extends HttpServlet {
//springmvc 容器存放bean
private ConcurrentHashMap<String, Object> mvcBeans = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, Object> mvcBeanUrl = new ConcurrentHashMap<>();
private ConcurrentHashMap<String, String> mvcMethodUrl = new ConcurrentHashMap<>();
@Override
public void init(ServletConfig config) {
String packagePath = "com.zzw.cn.springmvc";
//1.进行报扫描获取当前包下面所有的类
List<Class<?>> classes = comscanPackage(packagePath);
try {
//2.初始化springmvcbean
initSpringMvcBean(classes);
} catch (Exception e) {
e.printStackTrace();
}
//3.将请求地址和方法进行映射
initHandMapping(mvcBeans);
}
public List<Class<?>> comscanPackage(String packagePath) {
List<Class<?>> classes = ClassUtils.getClasses(packagePath);
return classes;
}
/**
* 初始化sprignbean
*
* @param classes
* @throws Exception
*/
public void initSpringMvcBean(List<Class<?>> classes) throws Exception {
if (classes.size() == 0 || null == classes) {
throw new Exception("包扫描后的classes为null");
}
for (Class<?> aClass : classes) {
//获取被自定义注解的controller将其初始化到自定义sprignmvc容器中
AnController declaredAnnotation = aClass.getDeclaredAnnotation(AnController.class);
if (declaredAnnotation != null) {
//获取类的名字
String beanid = lowerFirstCapse(aClass.getSimpleName());
//获取对象
Object beanObj = aClass.newInstance();
//放入sprign容器
mvcBeans.put(beanid, beanObj);
}
}
}
/**
* 初始化HandlerMapping方法
*
* @param mvcBeans
*/
public void initHandMapping(ConcurrentHashMap<String, Object> mvcBeans) {
//遍历springmvc 获取注入的对象值
for (Map.Entry<String, Object> entry : mvcBeans.entrySet()) {
Object objValue = entry.getValue();
Class<?> aClass = objValue.getClass();
//获取当前类 判断是否有自定义的requestMapping注解
String mappingUrl = null;
AnRequestMapping anRequestMapping = aClass.getDeclaredAnnotation(AnRequestMapping.class);
if (anRequestMapping != null) {
mappingUrl = anRequestMapping.value();
}
//获取当前类所有方法,判断方法上是否有注解
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method method : declaredMethods) {
AnRequestMapping methodDeclaredAnnotation = method.getDeclaredAnnotation(AnRequestMapping.class);
if (methodDeclaredAnnotation != null) {
String methodUrl = methodDeclaredAnnotation.value();
mvcBeanUrl.put(mappingUrl + methodUrl, objValue);
mvcMethodUrl.put(mappingUrl + methodUrl, method.getName());
}
}
}
}
/**
* @param str
* @return 类名首字母小写
*/
public static String lowerFirstCapse(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doServelt(req, resp);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
private void doServelt(HttpServletRequest req, HttpServletResponse resp) throws IOException, NoSuchMethodException, InvocationTargetException, IllegalAccessException, ServletException {
//获取请求地址
String requestUrl = req.getRequestURI();
//查找地址所对应bean
Object object = mvcBeanUrl.get(requestUrl);
if (object == null) {
resp.getWriter().println("sorry http is not exit 404");
return;
}
//获取请求的方法
String methodName = mvcMethodUrl.get(requestUrl);
if (methodName == null) {
resp.getWriter().println("sorry method is not exit 404");
return;
}
//通过构反射执行方法
Class<?> aClass = object.getClass();
Method method = aClass.getMethod(methodName);
String invoke = (String) method.invoke(object);
// 获取后缀信息
String suffix = ".jsp";
// 页面目录地址
String prefix = "/";
req.getRequestDispatcher(prefix + invoke + suffix).forward(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
}ClassUtils工具类封装
package com.zzw.cn.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 ClassUtils {
/**
* 从包package中获取所有的Class
*
* @param pack
* @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类文件)
@Override
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();
}
}
}
}
}自定义注解类AnController
package com.zzw.cn.springmvc.annoation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author Simple
* @date 14:06 2019/8/27
* @description
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface AnController {
}自定义注解类AnRequestMapping
package com.zzw.cn.springmvc.annoation;
import java.lang.annotation.*;
/**
* @author Simple
* @date 14:07 2019/8/27
* @description
*/
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AnRequestMapping {
String value() default "";
}HelloWorld类
package com.zzw.cn.springmvc.controller;
import com.zzw.cn.springmvc.annoation.AnController;
import com.zzw.cn.springmvc.annoation.AnRequestMapping;
/**
* @author Simple
* @date 15:15 2019/8/27
* @description
*/
@AnController
@AnRequestMapping(value = "/hello")
public class HelloWorld {
@AnRequestMapping("/method")
public String method(){
return "index";
}
}index.jsp
<html> <body> <h2>Hello World!</h2> </body> </html>
访问地址:http://localhost:8080/hello/method
成功结果:
