文章目录
目录结构
方便理解,没什么用
注解
Controller注解
package cn.edu.guet.myblog.mvc.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
RequestMapping注解
package cn.edu.guet.myblog.mvc.annotaion;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping{
String value() default "";
}
配置文件
config.properties
controller.package=cn.edu.guet.myblog.controller
Configuration.java
- 扫描特定包下特定注解标注的类和特定注解标注的方法和方法上注解的值
package cn.edu.guet.myblog.mvc;
import cn.edu.guet.myblog.mvc.annotaion.Controller;
import cn.edu.guet.myblog.mvc.annotaion.RequestMapping;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.reflect.MethodUtils;
import java.io.File;
import java.lang.reflect.Method;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
/**
* @Description: 扫描特定包下特定注解标注的类和特定注解标注的方法和方法上注解的值
* @date 2021/6/27 10:34
*/
public class Configuration {
public Map<String, ControllerMapping> config() throws URISyntaxException {
Map<String, ControllerMapping> controllerMapping = new HashMap<String, ControllerMapping>();
// 读取resource目录下的config.properties文件
ResourceBundle bundle = ResourceBundle.getBundle("config");
// 拿到config.properties文件中controller.package键对应的值
String controllerPackageName = bundle.getString("controller.package");
// 把配置文件的包路径中的.替换成/ path=====cn/edu/guet/myblog/controller
String path = controllerPackageName.replace(".", "/");
System.out.println("path====="+ path);
// 拿到盘符真实路径 uri=====file:/D:/springboot-study/myblog/target/myblog-1.0-SNAPSHOT/WEB-INF/classes/cn/edu/guet/myblog/controller/
URI uri = Configuration.class.getResource("/" + path).toURI();
System.out.println("uri====="+uri.toString());
// 创建一个文件对象,获取该目录下所有的文件名
File controllerDirectory = new File(uri);
String[] controllerFileNames = controllerDirectory.list();
for (String className : controllerFileNames) {
System.out.println("className======="+className);
// 筛选出以.class结尾的java文件
if (className.endsWith(".class")) {
String fullClassName = controllerPackageName + "." + StringUtils.substringBefore(className, ".class");
// 拿到全类名 cn.edu.guet.myblog.controller.UserController
System.out.println("全类名:===="+fullClassName);
try {
// 通过全类名获取反射类对象
Class controllerClass = Class.forName(fullClassName);
// 筛选出有Controller注解的反射类对象
if (controllerClass.isAnnotationPresent(Controller.class)) {
// 拿到有RequestMapper注解标注的方法
Method methods[] = MethodUtils.getMethodsWithAnnotation(controllerClass, RequestMapping.class);
for (Method method : methods) {
System.out.println("有RequestMapper注解标注的方法======"+method.toString());
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
// 把全类名的反射对象和一个RequestMapping注解标注的方法注入到ControllerMapping属性中,两个那就两个相同的类对象映射两个方法,分别存到ControllerMapping对象中
ControllerMapping mapping=new ControllerMapping(controllerClass,method);
// 把RequestMapping注解的值(请求的url)作为键,把一个ControllerMapping对象放到该键中,一个键(url)对应一个ControllerMapping对象
controllerMapping.put(annotation.value(),mapping);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return controllerMapping;
}
}
ContextConfigListener.java
- 监听器,给服务器启动时就执行扫描包和获取值,存到上下文中
package cn.edu.guet.myblog.mvc;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
import java.net.URISyntaxException;
import java.util.Map;
/**
* @Description: 监听器
* @date 2021/6/27 10:34
*/
@WebListener()
public class ContextConfigListener implements ServletContextListener {
public ContextConfigListener() {
}
public void contextInitialized(ServletContextEvent sce) {
try {
// 启动服务器时执行,创建一个Configration对象执行它的方法并拿到返回结果
Map<String, ControllerMapping> controllerMapping = new Configuration().config();
// 把结果放到上下文(application全局对象)中
sce.getServletContext().setAttribute("cn.guet.web.controller", controllerMapping);
} catch (URISyntaxException e) {
e.printStackTrace();
}
}
public void contextDestroyed(ServletContextEvent sce) {
}
}
ControllerMapping.java
- 控制器映射类,一个RequestMapping值(也就是uri)对应一个全类名反射对象和注解标注的方法,这个类就相当于给 全类名反射对象和注解标注的方法 注入的类,它也就是值的本身
package cn.edu.guet.myblog.mvc;
import java.lang.reflect.Method;
/**
* @Description:控制器映射类,一个RequestMapping值(也就是uri)对应一个全类名反射对象和注解标注的方法
*
* 键:uri 值:一个全类名反射对象和注解标注的方法,这个类就相当于给 全类名反射对象和注解标注的方法 注入的类,它也就是值的本身
*
* @date 2021/6/27 14:15
*/
public class ControllerMapping {
// 业务控制器类实例, UserController, BookController ......
private Class<?> controllerClass;
// 业务控制器类实例的目标方法,即标注了@RequestMapping的方法实例
private Method handleMethod;
public ControllerMapping() {
}
public ControllerMapping(Class<?> controllerClass, Method handleMethod) {
this.controllerClass = controllerClass;
this.handleMethod = handleMethod;
}
public Class<?> getControllerClass() {
return controllerClass;
}
public void setControllerClass(Class<?> controllerClass) {
this.controllerClass = controllerClass;
}
public Method getHandleMethod() {
return handleMethod;
}
public void setHandleMethod(Method handleMethod) {
this.handleMethod = handleMethod;
}
@Override
public String toString() {
//UserController.login
return "控制器类: " + controllerClass.getSimpleName() + "." + handleMethod.getName();
}
}
DispatcherServlet.java
- 路由分发和方法参数注入(由用户传过来的值注入到方法参数中)
package cn.edu.guet.myblog.mvc;
import com.google.gson.GsonBuilder;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang3.ClassUtils;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Description:路由分发和方法参数注入(由用户传过来的值注入到方法参数中),设置loadOnStartup的值让服务器启动就初始化
* @date 2021/6/27 11:23
*/
@WebServlet(value = "/*",loadOnStartup = 1)
public class DispatcherServlet extends HttpServlet {
Map<String, ControllerMapping> controllerMapping;
/**
* @Description: Servlet初始化先拿到上下文中存储的controllerMapping集合,服务器启动时监听器已经存了数据
* @Param config:
* @return void
* @date 2021/6/27 11:29
*/
@Override
public void init(ServletConfig config) throws ServletException {
controllerMapping = (Map<String, ControllerMapping>) config.getServletContext().getAttribute("cn.guet.web.controller");
System.out.println(controllerMapping);
}
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
}
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// 拿到前端客户请求的路径
String uri = request.getRequestURI();
// 取到第一个 / 的索引,并以该索引截取请求字符串,也就是去掉项目名
uri = uri.substring(uri.indexOf("/", 1) + 1);
System.out.println("真实的客户请求:" + uri);
ControllerMapping mapping = null;
// 如果请求包含uri就把集合中uri对应值的对象拿出来
if (controllerMapping.containsKey(uri)) {
mapping = controllerMapping.get(uri);
}
// 拿到请求映射的控制器反射类和方法
Class controllerMappingClass = mapping.getControllerClass();
Method method = mapping.getHandleMethod();
/*
* @Description: 传参的赋值处理(前端传的参数映射到方法参数中)
*/
// 获取方法的参数类型
Class[] parameterType = method.getParameterTypes();
List<String> paramterList = new ArrayList<String>();//List的特点:有序可重复
// 获取控制器的方法的参数名称集合
Parameter[] params = method.getParameters();
for (Parameter parameter : params) {
System.out.println(parameter.getName());
paramterList.add(parameter.getName());
}
Object[] parameterValues = new Object[parameterType.length];//前端传过来的参数的值的容器
for (int i = 0; i < parameterType.length; i++) {
/*
8种基本类型,剩下的自己可以补齐八种类型
*/
// 如果时基本数据类型
if (parameterType[i].isPrimitive()) {
if (parameterType[i].getTypeName().equals("int")) {
parameterValues[i] = Integer.parseInt(request.getParameter(paramterList.get(i)));
}
/*
处理String类型
*/
} else if (ClassUtils.isAssignable(parameterType[i], String.class)) {
parameterValues[i] = request.getParameter(paramterList.get(i));
} else {
//Bean
Object pojo = parameterType[i].newInstance();
//得到请求里所有的参数:Map<参数名, value>
//获取表单里的数据
Map<String, String[]> parameterMap = request.getParameterMap();
//beanutils会自动将map里的key与bean的属性名进行反射赋值
BeanUtils.populate(pojo, parameterMap);
parameterValues[i] = pojo;
}
}
Object obj = controllerMappingClass.newInstance();
//调用方法处理请求,前端传过来的参数变成了方法的参数,执行方法后会有返回值
Object returnValue = method.invoke(obj, parameterValues);
if (returnValue != null && returnValue instanceof String) { //方法返回的是一个字符串类
String path = returnValue.toString();
if (((String) returnValue).startsWith("forward:")) {
// forward开头就是转发
request.getRequestDispatcher(StringUtils.substringAfter(path, "forward:")).forward(request, response);
} else if (((String) returnValue).startsWith("redirect:")) {
// redirect开头就是重定向
response.sendRedirect(StringUtils.substringAfter(path, "redirect:"));
}
// 如果不为空且不是String类型,就以json传输返回给前端(我们经常返回一个对象给前端)
} else if (returnValue != null && !(returnValue instanceof String)) {
response.setContentType("application/json; charset=UTF-8");
//返回的是一个bean,即客户端发送的是ajax请求,并将该bean转换成json
String json = new GsonBuilder()
.setDateFormat("yyyy-MM-dd HH:mm:ss")
.setPrettyPrinting()
.create()
.toJson(returnValue);
PrintWriter out = response.getWriter();
out.write(json);
out.flush();
out.close();
}
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}