前面手写过MyBatis、Spring简易框架,今天就来写一下SpringMVC简易版框架。所谓简易版,就是肯定不像源码那些封装完整,就是实现一下基本功能,帮助更好理解底层原理
介绍
Spring MVC 是 Spring 提供的一个基于 MVC 设计模式的轻量级 Web 开发框架,本质上相当于 Servlet。
Spring MVC 角色划分清晰,分工明细。由于 Spring MVC 本身就是 Spring 框架的一部分,可以说和 Spring 框架是无缝集成。性能方面具有先天的优越性,是当今业界最主流的 Web 开发框架,最热门的开发技能。
仓库地址: gitee.com/uuuwhh/Spri…
开始实现
整体架构
新建web工程
引入依赖pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<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/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.springmvc</groupId>
<artifactId>SpringMVC-Write</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>SpringMVC-Write Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.7</maven.compiler.source>
<maven.compiler.target>1.7</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>RELEASE</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.5</version>
</dependency>
</dependencies>
<build>
<finalName>SpringMVC-Write</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>8</source>
<target>8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
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>
<!--springmvc的核心控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.springmvc.servlet.DispatcherServlet</servlet-class>
<!--springmvc的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--web服务器一旦启动,Servlet就会实例化创建对象,进行初始化(预备创建对象)-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
springmvc.xml
<?xml version="1.0" encoding="utf-8" ?>
<beans>
<!-- 加载控制器所在的包 -->
<component-scan base-package="com.ithong.controller,com.ithong.service"></component-scan>
</beans>
自定义异常类
package com.springmvc.exception;
public class ContextException extends RuntimeException{
public ContextException(String message) {
super(message);
}
public ContextException(Throwable cause) {
super(cause);
}
protected ContextException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) {
super(message, cause, enableSuppression, writableStackTrace);
}
@Override
public String getMessage() {
return super.getMessage();
}
}
配置SpringMVC核心的控制器
swevlet容器需要在应用项目启动时,给应用项目初始化一个ServletContext作为公共环境容器存放公共信息,ServletContext中的信息都是由容器提供的。
WebApplicationContext中包含contextConfigLocation和ioc容器
ublic class WebApplicationContext {
// classpath:springmvc.xml
String contextConfigLocation;
List<String> classNameList = new ArrayList<String>();
// Spring的IOC容器
public Map<String,Object> iocMap = new ConcurrentHashMap<String,Object>();
public WebApplicationContext(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
}
SpringMVC的核心就是DispatcherServlet,DispatcherServlet实质也是一个HttpServlet。DispatcherSevlet负责将请求分发,所有的请求都有经过它来统一分发。
SpringMVC请求处理流程如下
用户向服务器发送请求,请求会到DispatcherServlet,DispatcherServlet 对请求URL进行解析,得到请求资源标识符(URI),然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括一个Handler处理器对象、多个HandlerInterceptor拦截器对象),最后以HandlerExecutionChain对象的形式返回。
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter。提取Request中的模型数据,填充Handler入参,开始执行Handler(Controller)。 在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作:
- HttpMessageConveter: 将请求消息(如Json、xml等数据)转换成一个对象,将对象转换为指定的响应信息
- 数据转换:对请求消息进行数据转换。如String转换成Integer、Double等
- 数据格式化:对请求消息进行数据格式化。 如将字符串转换成格式化数字或格式化日期等
- 数据验证: 验证数据的有效性(长度、格式等),验证结果存储到BindingResult或Error中 DispatcherServlet
DispatcherServlet类的主要职责有四个
- 1、
Servlet初始化的时候,读取初始化的参数classpath:springmvc.xml - 2、创建
Spring容器 - 3、初始化
Spring容器 - 4、初始化请求映射
/user/query---->Controller---->method---->paramter
public class DispatcherServlet extends HttpServlet {
private WebApplicationContext webApplicationContext;
// 存储URL和对象的方法映射关系
List<MyHandler> handlerList = new ArrayList<MyHandler>();
@Override
public void init() throws ServletException {
// 1.Servlet初始化的时候,读取初始化的参数 classpath:springmvc.xml
String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
// 2.创建Spring容器
webApplicationContext = new WebApplicationContext(contextConfigLocation);
// 3.初始化Spring容器
webApplicationContext.refresh();
// 4、初始化请求映射 /user/query ----> Controller ----> method ----> paramter
initHandleMapping();
System.out.println("请求地址和控制器方法映射关系"+handlerList);
}
}
业务层和控制层
UserController
package com.ithong.controller;
import com.ithong.entity.User;
import com.ithong.service.UserService;
import com.springmvc.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
@Controller
public class UserController {
@AutoWired
UserService userService;
@RequestMapping("/user/query")
public String findUsers(HttpServletRequest request, HttpServletResponse response,@RequestParam("name") String name){
response.setContentType("text/html;charset=utf-8");
// try {
// List<User> users = userService.findUsers(name);
// PrintWriter out = response.getWriter();
// out.print("<h1>SpringMvc控制器:"+name+"</h1>");
// } catch (IOException e) {
// e.printStackTrace();
// }
String userMessage = userService.getUserMessage(name);
request.setAttribute("userMessage",userMessage);
// 转发到user.jsp SpringMvc默认就是转发
return "forward:/user.jsp";
}
@RequestMapping("/user/queryjson")
@ResponseBoby // 返回值的自动转为JSON数据
public List<User> queryUsers(HttpServletRequest request,HttpServletResponse response,String name){
return userService.findUsers(name);
}
}
实体类User
package com.ithong.entity;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String name;
private String pass;
}
接口UserService
package com.ithong.service;
import com.ithong.entity.User;
import java.util.List;
public interface UserService {
public List<User> findUsers(String name);
public String getUserMessage(String name);
}
UserServiceImpl
package com.ithong.service.impl;
import com.ithong.entity.User;
import com.ithong.service.UserService;
import com.springmvc.annotation.Service;
import java.util.ArrayList;
import java.util.List;
@Service
public class UserServiceImpl implements UserService {
@Override
public List<User> findUsers(String name) {
List<User> users = new ArrayList<>();
users.add(new User(1,"老王","123"));
users.add(new User(2,"小王","123"));
return users;
}
@Override
public String getUserMessage(String name) {
return "我是getUserMessage方法"+name;
}
}
自定义注解
AutoWired
package com.springmvc.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
@Documented
public @interface AutoWired {
String value() default "";
}
Controller
package com.springmvc.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Controller {
}
RequestMapping
package com.springmvc.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface RequestMapping {
String value() default "";
}
RequestParam
package com.springmvc.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.PARAMETER)
@Documented
public @interface RequestParam {
String value() default "";
}
ResponseBoby
package com.springmvc.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface ResponseBoby {
}
Service
package com.springmvc.annotation;
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface Service {
String value() default "";
}
创建spring容器
Dom4j解析springmvc
就是拿到springmvc.xml里的component-scan的base-package属性的值,就是com.ithong.controller,com.ithong.service
XmlPaser
package com.springmvc.xml;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
import java.net.URL;
public class XmlPaser {
public static String getbasePackage(String xml) {
try {
SAXReader saxReader = new SAXReader();
InputStream inputStream = XmlPaser.class.getClassLoader().getResourceAsStream(xml);
// XML文档对象
Document document = saxReader.read(inputStream);
Element rootElement = document.getRootElement();
Element componentScan = rootElement.element("component-scan");
Attribute attribute = componentScan.attribute("base-package");
String basePackage = attribute.getText();
return basePackage;
} catch (DocumentException e) {
e.printStackTrace();
}
return "";
}
}
扫描SpringMvc中的类
根据上面拿到的值去遍历文件查找类
/**
* 扫描包
*/
public void excuteScanPackage(String pack){
URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\.", "/"));
String path = url.getFile();
File dir = new File(path);
for (File f : dir.listFiles()) {
if (f.isDirectory()){
// 当前是一个文件目录 com.ithong.service.impl
excuteScanPackage(pack+"."+f.getName());
}else {
// 文件目录
String className = pack + "." + f.getName().replaceAll(".class","");
classNameList.add(className);
}
}
}
执行结果下下
扫描之后的内容是:[com.ithong.controller.UserController, com.ithong.service.impl.UserServiceImpl, com.ithong.service.UserService]
实例化Spring容器中对象并放入IOC容器
当然,SpringIOC容器比这复杂点,这里是直接通过反射把类上有Service、Controller注解的类实例放入IOC容器 执行结果如下
SpringIoC容器中对象{userController=com.ithong.controller.UserController@5c344a6f, userService=com.ithong.service.impl.UserServiceImpl@6ee6dab9}
/**
* 实例化Spring容器中bean对象
*/
private void excuteInstance() {
if (classNameList.size() == 0){
// 没有扫描到需要实例化的类
throw new ContextException("没有要实例化的class");
}
try {
for (String className : classNameList) {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Controller.class)){
// 控制层的类 com.ithong.controller.UserController
// userController 控制层对象的名字
String beanName = clazz.getSimpleName().substring(0,1).toLowerCase() + clazz.getSimpleName().substring(1);
iocMap.put(beanName,clazz.newInstance());
}else if (clazz.isAnnotationPresent(Service.class)){
// com.ithong.service.impl.UserServiceImpl
Service serviceAnnotation = clazz.getAnnotation(Service.class);
String beanName = serviceAnnotation.value();
if ("".equals(beanName)){
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
String beanName1 = anInterface.getSimpleName().substring(0,1).toLowerCase() + anInterface.getSimpleName().substring(1);
iocMap.put(beanName1,clazz.newInstance());
}
}else {
iocMap.put(beanName,clazz.newInstance());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
实现Spring容器中对象的注入
实际就是扫描类的成员变量有没有AutoWired注解需要依赖注入的
/**
* 实现Spring容器中对象依赖注入
*/
private void executeAutoWired() {
try {
if (iocMap.isEmpty()){
throw new ContextException("没有要初始化的bean对象");
}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
String key = entry.getKey();
Object bean = entry.getValue();
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
AutoWired autoWiredAnnotation = field.getAnnotation(AutoWired.class);
String beanName = autoWiredAnnotation.value();
if ("".equals(beanName)){
Class<?> type = field.getType();
beanName = type.getSimpleName().substring(0,1).toLowerCase() + type.getSimpleName().substring(1);
}
field.setAccessible(true);
// 属性注入 调用反射 给属性赋值
field.set(bean,iocMap.get(beanName));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
初始化请求映射
通过对请求的处理,将控制层@RequestMapping注解的路径地址与对应类和方法一一映射。
首先建立一个实例对象将映射属性存入
MyHandler
package com.springmvc.handler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyHandler {
private String url;
private Object controller;
private Method method;
}
建立一个集合存储URL和对象的方法映射关系实例
// 存储URL和对象的方法映射关系
List<MyHandler> handlerList = new ArrayList<MyHandler>();
通过扫描IOC容器中带有RequestMapping注解方法
/**
* 初始化请求映射
*/
private void initHandleMapping() {
// 判断IOCmap中是否有bean对象
if (webApplicationContext.iocMap.isEmpty()){
throw new ContextException("Spring容器为空");
}
for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (clazz.isAnnotationPresent(Controller.class)){
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMappingAnnotation = declaredMethod.getAnnotation(RequestMapping.class);
// /user/query
String url = requestMappingAnnotation.value();
MyHandler handler = new MyHandler(url, entry.getValue(), declaredMethod);
handlerList.add(handler);
}
}
}
}
}
反射控制器方法名
public List<String> getParameterName(Method method){
List<String> list = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
list.add(parameter.getName());
}
return list;
}
分发用户请求、控制器方法接收参数
当用户请求数据时,调用DispatcherServlet下doPost方法,在该方法内对请求的分发进行处理。
根据请求地址获取Handler对象
public MyHandler getHandler(HttpServletRequest req) {
// /user/query
String requestURI = req.getRequestURI();
for (MyHandler myHandler : handlerList) {
if (myHandler.getUrl().equals(requestURI)) {
return myHandler;
}
}
return null;
}
此处参数传值方法并不优雅,可优化之处
根据@RequestParam注解在参数上的位置拿到值,反射调用方法后可通过返回值结果。
/**
* 判断控制器方法的参数是否有requestParam注解,且找到对应value值 如果找到 返回
* @param method
* @param name
* @return
*/
public int hasRequestParam(Method method,String name) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter p = parameters[i];
boolean b = p.isAnnotationPresent(RequestParam.class);
if (b) {
RequestParam requestParam = p.getAnnotation(RequestParam.class);
String requestParamValue = requestParam.value();
if (name.equals(requestParamValue)) {
return i;
}
}
}
return -1;
}
分发过程中通过反射调用方法返回类型或@RequestBoby注解判断返回数据格式
此处也可以配置视图解析器返回页面
// 请求分发 处理
private void executeDispatch(HttpServletRequest req, HttpServletResponse resp) {
MyHandler handler = getHandler(req);
try {
if (handler == null){
resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
}else {
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
// 定义一个参数的数组
Object[] params = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
params[i] = req;
}else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
params[i] = resp;
}
}
// 获取请求中的参数集合
Map<String, String[]> parameterMap = req.getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String name = entry.getKey();
Object value = entry.getValue()[0];
System.out.println("请求参数:"+name+"-----"+value);
int index = hasRequestParam(handler.getMethod(), name);
if (index != -1) {
params[index] = value;
}else {
List<String> names = getParameterName(handler.getMethod());
System.out.println(names);
}
// 后期有待优化
params[2] = value;
}
// 调用控制中的方法
Object result = handler.getMethod().invoke(handler.getController(), parameterTypes);
if (result instanceof String){
// 跳转到jsp
String viewName = (String) result;
if (viewName.contains(":")){
// "forward:/user.jsp"
String viewType = viewName.split(":")[0];
String viewPage = viewName.split(":")[1];
if (viewType.equals("")){
req.getRequestDispatcher(viewPage).forward(req,resp);
}else {
resp.sendRedirect(viewPage);
}
}else {
// 默认就转发
req.getRequestDispatcher(viewName).forward(req,resp);
}
}else {
// 返回JSON数据
Method method = handler.getMethod();
if (method.isAnnotationPresent(ResponseBoby.class)){
// 把返回值调用JSON转换工具转为JSON字符串
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(result);
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.print(json);
out.flush();
out.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
user.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>User.jsp</title>
</head>
<body>
<h1>${requestScope.userMessage}</h1>
</body>
</html>
结果如下图,至此SpringMvc简易框架功能已全部实现
最后贴一下核心控制器
DispatcherServlet和WebApplicationContext完整代码
DispatcherServlet
package com.springmvc.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.RequestMapping;
import com.springmvc.annotation.RequestParam;
import com.springmvc.annotation.ResponseBoby;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.exception.ContextException;
import com.springmvc.handler.MyHandler;
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.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* SpringMvc核心控制器
*/
public class DispatcherServlet extends HttpServlet {
private WebApplicationContext webApplicationContext;
// 存储URL和对象的方法映射关系
List<MyHandler> handlerList = new ArrayList<MyHandler>();
@Override
public void init() throws ServletException {
// 1.Servlet初始化的时候,读取初始化的参数 classpath:springmvc.xml
String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
// 2.创建Spring容器
webApplicationContext = new WebApplicationContext(contextConfigLocation);
// 3.初始化Spring容器
webApplicationContext.refresh();
// 4、初始化请求映射 /user/query ----> Controller ----> method ----> paramter
initHandleMapping();
System.out.println("请求地址和控制器方法映射关系"+handlerList);
}
/**
* 初始化请求映射
*/
private void initHandleMapping() {
// 判断IOCmap中是否有bean对象
if (webApplicationContext.iocMap.isEmpty()){
throw new ContextException("Spring容器为空");
}
for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (clazz.isAnnotationPresent(Controller.class)){
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)){
RequestMapping requestMappingAnnotation = declaredMethod.getAnnotation(RequestMapping.class);
// /user/query
String url = requestMappingAnnotation.value();
MyHandler handler = new MyHandler(url, entry.getValue(), declaredMethod);
handlerList.add(handler);
}
}
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 请求的分发处理
executeDispatch(req,resp);
}
// 请求分发 处理
private void executeDispatch(HttpServletRequest req, HttpServletResponse resp) {
MyHandler handler = getHandler(req);
try {
if (handler == null){
resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
}else {
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
// 定义一个参数的数组
Object[] params = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
params[i] = req;
}else if ("HttpServletResponse".equals(parameterType.getSimpleName())){
params[i] = resp;
}
}
// 获取请求中的参数集合
Map<String, String[]> parameterMap = req.getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String name = entry.getKey();
Object value = entry.getValue()[0];
System.out.println("请求参数:"+name+"-----"+value);
int index = hasRequestParam(handler.getMethod(), name);
if (index != -1) {
params[index] = value;
}else {
List<String> names = getParameterName(handler.getMethod());
System.out.println(names);
}
// 后期有待优化
params[2] = value;
}
// 调用控制中的方法
Object result = handler.getMethod().invoke(handler.getController(), parameterTypes);
if (result instanceof String){
// 跳转到jsp
String viewName = (String) result;
if (viewName.contains(":")){
// "forward:/user.jsp"
String viewType = viewName.split(":")[0];
String viewPage = viewName.split(":")[1];
if (viewType.equals("")){
req.getRequestDispatcher(viewPage).forward(req,resp);
}else {
resp.sendRedirect(viewPage);
}
}else {
// 默认就转发
req.getRequestDispatcher(viewName).forward(req,resp);
}
}else {
// 返回JSON数据
Method method = handler.getMethod();
if (method.isAnnotationPresent(ResponseBoby.class)){
// 把返回值调用JSON转换工具转为JSON字符串
ObjectMapper mapper = new ObjectMapper();
String json = mapper.writeValueAsString(result);
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.print(json);
out.flush();
out.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据请求地址获取Handler对象
* @param req
* @return
*/
public MyHandler getHandler(HttpServletRequest req) {
// /user/query
String requestURI = req.getRequestURI();
for (MyHandler myHandler : handlerList) {
if (myHandler.getUrl().equals(requestURI)) {
return myHandler;
}
}
return null;
}
/**
* 判断控制器方法的参数是否有requestParam注解,且找到对应value值 如果找到 返回
* @param method
* @param name
* @return
*/
public int hasRequestParam(Method method,String name) {
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter p = parameters[i];
boolean b = p.isAnnotationPresent(RequestParam.class);
if (b) {
RequestParam requestParam = p.getAnnotation(RequestParam.class);
String requestParamValue = requestParam.value();
if (name.equals(requestParamValue)) {
return i;
}
}
}
return -1;
}
// 反射控制器方法名
public List<String> getParameterName(Method method){
List<String> list = new ArrayList<>();
Parameter[] parameters = method.getParameters();
for (Parameter parameter : parameters) {
list.add(parameter.getName());
}
return list;
}
}
WebApplicationContext
package com.springmvc.context;
import com.springmvc.annotation.AutoWired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.Service;
import com.springmvc.exception.ContextException;
import com.springmvc.xml.XmlPaser;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
public class WebApplicationContext {
// classpath:springmvc.xml
String contextConfigLocation;
List<String> classNameList = new ArrayList<String>();
// Spring的IOC容器
public Map<String,Object> iocMap = new ConcurrentHashMap<String,Object>();
public WebApplicationContext(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
/**
* 初始化Spring容器
*/
public void refresh(){
// 1.解析springmvc.xml DOM4J springmvc.xml
String basePackage = XmlPaser.getbasePackage(contextConfigLocation.split(":")[1]);
String[] basePackages = basePackage.split(",");
if (basePackages.length > 0){
for (String pack : basePackages) {
// com.ithong.controller com.ithong.service ---> /com/ithong/controller
excuteScanPackage(pack);
}
}
// com.ithong.controller.UserController, com.ithong.service.impl.UserServiceImpl, com.ithong.service.UserService
System.out.println("扫描之后的内容是:"+classNameList);
// 实例化Spring容器中的bean
excuteInstance();
// IOC容器中对象
System.out.println("SpringIoC容器中对象"+iocMap);
// 实现Spring容器中对象的注入
executeAutoWired();
}
/**
* 实现Spring容器中对象依赖注入
*/
private void executeAutoWired() {
try {
if (iocMap.isEmpty()){
throw new ContextException("没有要初始化的bean对象");
}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
String key = entry.getKey();
Object bean = entry.getValue();
Field[] fields = bean.getClass().getDeclaredFields();
for (Field field : fields) {
AutoWired autoWiredAnnotation = field.getAnnotation(AutoWired.class);
String beanName = autoWiredAnnotation.value();
if ("".equals(beanName)){
Class<?> type = field.getType();
beanName = type.getSimpleName().substring(0,1).toLowerCase() + type.getSimpleName().substring(1);
}
field.setAccessible(true);
// 属性注入 调用反射 给属性赋值
field.set(bean,iocMap.get(beanName));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 实例化Spring容器中bean对象
*/
private void excuteInstance() {
if (classNameList.size() == 0){
// 没有扫描到需要实例化的类
throw new ContextException("没有要实例化的class");
}
try {
for (String className : classNameList) {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Controller.class)){
// 控制层的类 com.ithong.controller.UserController
// userController 控制层对象的名字
String beanName = clazz.getSimpleName().substring(0,1).toLowerCase() + clazz.getSimpleName().substring(1);
iocMap.put(beanName,clazz.newInstance());
}else if (clazz.isAnnotationPresent(Service.class)){
// com.ithong.service.impl.UserServiceImpl
Service serviceAnnotation = clazz.getAnnotation(Service.class);
String beanName = serviceAnnotation.value();
if ("".equals(beanName)){
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
String beanName1 = anInterface.getSimpleName().substring(0,1).toLowerCase() + anInterface.getSimpleName().substring(1);
iocMap.put(beanName1,clazz.newInstance());
}
}else {
iocMap.put(beanName,clazz.newInstance());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 扫描包
*/
public void excuteScanPackage(String pack){
URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\.", "/"));
String path = url.getFile();
File dir = new File(path);
for (File f : dir.listFiles()) {
if (f.isDirectory()){
// 当前是一个文件目录 com.ithong.service.impl
excuteScanPackage(pack+"."+f.getName());
}else {
// 文件目录
String className = pack + "." + f.getName().replaceAll(".class","");
classNameList.add(className);
}
}
}
}