<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
version="2.4">
<display-name>Web Application</display-name>
<servlet>
<servlet-name>pablo_mvc</servlet-name>
<servlet-class>com.bj.summary.spring_mvc.servlet.DispatchServlet</servlet-class>
<init-param>
<!--指定配置文件名字-->
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>pablo_mvc</servlet-name>
<url-pattern>/*
application.properties
scanPackage=com.bj.summary.spring_mvc.controller,com.bj.summary.spring_mvc.service
注解(扫描时根据注解做不同处理)
/**
* @Authror PABLO
* @Date 2022/5/4 13:59
* @Desc
*/
@Target({ElementType.FIELD}) //字段(属性)注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_Autowired {
String value() default "";
}
/**
* @Authror PABLO
* @Date 2022/5/4 13:57
* @Desc Controller
*/
@Target({ElementType.TYPE}) //类注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_Controller {
String value() default "";
}
/**
* @Authror PABLO
* @Date 2022/5/4 14:00
* @Desc
*/
@Target({ElementType.TYPE, ElementType.METHOD})//类或方法,即controller和具体method
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_RequestMapping {
String value() default "";
}
/**
* @Authror PABLO
* @Date 2022/5/4 14:01
* @Desc
*/
@Target({ElementType.PARAMETER})//参数注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_RequestParam {
String value() default "";
}
/**
* @Authror PABLO
* @Date 2022/5/4 14:01
* @Desc
*/
@Target({ElementType.TYPE}) //类注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PABLO_Service {
String value() default "";
}
Controller
/**
* @Author PABLO
* @Date 2022/5/4 15:32
* @Desc
*/
@PABLO_Controller
@PABLO_RequestMapping("/pablo")
public class TestController {
@PABLO_Autowired
private TestService testService;
@PABLO_RequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse resp, @PABLO_RequestParam("a") Integer a, @PABLO_RequestParam("b") Integer b) {
try {
resp.getWriter().write(a + "+" + b + "=" + (testService.add(a,b)));
} catch (IOException e) {
e.printStackTrace();
}
}
}
Service
/**
* @Authror PABLO
* @Date 2022/5/4 15:33
* @Desc
*/
public interface TestService {
int add(int a, int b);
}
ServiceImpl
/**
* @Author PABLO
* @Date 2022/5/4 15:34
* @Desc
*/
@PABLO_Service
public class TestServiceImpl implements TestService {
@Override
public int add(int a, int b) {
return a + b;
}
}
servlet实现
package com.bj.summary.spring_mvc.servlet;
import com.bj.summary.spring_mvc.annotation.*;
import lombok.SneakyThrows;
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.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author PABLO
* @Date 2022/5/4 13:49
* @Desc 继承HttpServlet实现servlet规范
* 项目启动入口
*/
public class DispatchServlet extends HttpServlet {
//根据contextConfigLocation获得properties文件路径
/*
*
contextConfigLocation
application.properties
* */
private final static String LOCATION = "contextConfigLocation";
//配置文件key
private final static String propertiesKey = "scanPackage";
//根据key获取配置文件内容
/*
* scanPackage=com.bj.summary.spring_mvc.controller,com.bj.summary.spring_mvc.service
* */
private Properties properties = new Properties();
//存放扫描后得到的class文件名称
private List<String> classNames = new ArrayList<String>();
//IOC容器
private Map<String, Object> ioc = new HashMap<String, Object>();
//保存所有的Url和方法的映射关系
private List<Handler> handlerMapping = new ArrayList<Handler>();
/**
* @Description: 初始化操作
* @Author: PABLO
* @Date: 2022/5/4 13:50
* @Params: [config]
* @Return: void
**/
@Override
public void init(ServletConfig config) throws ServletException {
//初始化步骤
//加载配置文件properties
doLoadConfig(config.getInitParameter(LOCATION));
//扫描配置文件properties中定义的所有类
doScannerConfig((String) properties.get(propertiesKey));
//初始化所有的类,并实例化,保存IOC容器中
doInstanceAndSaveIocContainer();
//依赖注入,初始化对象
doInitializationInstance();
//构造handlerMapping
doBuildHandlerMapping();
}
private void doBuildHandlerMapping() {
if (Objects.isNull(ioc) || ioc.size() == 0) return;
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
//只针对处理器
if (!clazz.isAnnotationPresent(PABLO_Controller.class)) {
continue;
}
String url = "";
//获取Controller的url配置
if (clazz.isAnnotationPresent(PABLO_RequestMapping.class)) {
PABLO_RequestMapping requestMapping = clazz.getAnnotation(PABLO_RequestMapping.class);
url = requestMapping.value();
}
//获取Method的url配置
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if (!method.isAnnotationPresent(PABLO_RequestMapping.class)) {
continue;
}
//映射URL controller路径+方法对应路径
PABLO_RequestMapping requestMapping = method.getAnnotation(PABLO_RequestMapping.class);
///pablo/add 类注解路径+某方法路径
String regex = ("/" + url + requestMapping.value()).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
handlerMapping.add(new Handler(pattern, entry.getValue(), method));
System.out.println("mapping URL路径" + regex + ",对应方法" + method);
}
}
}
private void doInitializationInstance() {
if (Objects.isNull(ioc) || ioc.size() == 0) return;
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
//拿到实例对象中的所有属性(任何修飾符)
Field[] fields = entry.getValue().getClass().getDeclaredFields();
//遍历该类中的所有字段
for (Field item : fields
) {
//过滤不需要自动装配的属性
if (!item.isAnnotationPresent(PABLO_Autowired.class)) {
continue;
}
//获取所有需要装配的字段
PABLO_Autowired autowired = item.getAnnotation(PABLO_Autowired.class);
String beanName = autowired.value().trim();
//未设置对象名
if ("".equals(beanName)) {
//获取默认类名
beanName = item.getType().getName();
}
item.setAccessible(true); //设置私有属性的访问权限
try {
//entry.getValue()是【实例化后的某个类对象】 如testController
//item是该类中的属性
//ioc.get(beanName),beanName是key,得到该类型对应的【具体实例化对象】
//给某个类型的某个属性真实的赋值,建立实例化后对象的 组合关系,这是符号引用-->直接引用
item.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
private void doInstanceAndSaveIocContainer() {
if (Objects.isNull(classNames) || classNames.size() == 0) return;
try {
//获取每个类上注解
for (String item : classNames
) {
//根据完全限定名加载到内存
Class<?> clazz = Class.forName(item);
//根据类注解类型进行不同操作
if (clazz.isAnnotationPresent((PABLO_Controller.class))) {
//默认将类首字母小写作为beanName
String beanName = lowerFirst(clazz.getSimpleName());
//key testController小写首字母类名
//value 实例化后的对象
ioc.put(beanName, clazz.newInstance());
} else if (clazz.isAnnotationPresent(PABLO_Service.class)) {
PABLO_Service service = clazz.getAnnotation(PABLO_Service.class);
String beanName = service.value();
//用户自定义对象名优先
if (!"".equals(beanName.trim())) {
ioc.put(beanName, clazz.newInstance());
continue;
}
//如果自己没设,就按接口类型创建一个实例
//走到这里 一定是某个类上有service注解,并且为自定义名称
//key 接口
//value 接口实现类实例对象
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> i : interfaces) {
ioc.put(i.getName(), clazz.newInstance());
}
} else {
//可扩展其他类注解如@Respository....
//类上没注解的略过
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void doScannerConfig(String packageNames) {
//包可能有多个,数组处理
String[] packageNameArr = packageNames.split(",");
for (String item : packageNameArr
) {
//处理每个包,将包路径转为文件路径 .-->/
URL url = this.getClass().getClassLoader().getResource("/" + item.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
//如果是文件夹,继续递归
if (file.isDirectory()) {
doScannerConfig(item + "." + file.getName());
} else {
//E:\IDEALocation\giant-gator\target\classes\com\bj\summary\spring_mvc\controller\TestController.class
classNames.add(item + "." + file.getName().replace(".class", "").trim());
}
}
}
}
private void doLoadConfig(String location) {
InputStream fis = null;
try {
fis = this.getClass().getClassLoader().getResourceAsStream(location);
//读取配置文件
properties.load(fis);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != fis) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private String lowerFirst(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//get调用post
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req, resp); //开始始匹配到对应的方方法
} catch (Exception e) {
//如果匹配过程出现异常,将异常信息打印出去
resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
}
}
@SneakyThrows
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) {
try { //通过请求获取对应handler,在构建handler时就已经指定 某个请求->某个方法(某些参数)
Handler handler = getHandler(req);
if (handler == null) {
//如果没有匹配上,返回404错误
resp.getWriter().write("404 Not Found");
return;
}
//获取方法的参数列表 都是类型的完全限定名如java.lang.Integer java.lang.String...
Class<?>[] paramTypes = handler.method.getParameterTypes();
//保存所有需要自动赋值的参数值
Object[] paramValues = new Object[paramTypes.length];
//得到方法参数名称和对应的值 key=a参数名称 value=[具体入参值]
Map<String, String[]> params = req.getParameterMap();
//将实际入参值填充!!!
for (Map.Entry<String, String[]> param : params.entrySet()) {
//参数带中括号,去中括号
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "").replaceAll(",\\s", ",");
//通过参数名称找参数下标,如果找到匹配的对象,则开始填充参数值
if (!handler.paramIndexMapping.containsKey(param.getKey())) {
continue;
}
int index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(paramTypes[index], value);
}
//设置方法中的request和response对象
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = req;
int respIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[respIndex] = resp;
handler.method.invoke(handler.controller, paramValues);
} catch (InvocationTargetException e) {
e.getTargetException().printStackTrace();
}
}
//可采用策略,如map
private Object convert(Class<?> paramType, String value) {
if (Integer.class == paramType) {
return Integer.valueOf(value);
}
return value;
}
private Handler getHandler(HttpServletRequest req) throws Exception {
if (handlerMapping.isEmpty()) {
return null;
}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
//根据请求路径匹配mapping
for (Handler handler : handlerMapping) {
try {
Matcher matcher = handler.pattern.matcher(url);
//如果没有匹配上继续下一个匹配
if (!matcher.matches()) {
continue;
}
return handler;
} catch (Exception e) {
throw e;
}
}
return null;
}
/**
* @Author PABLO
* @Date 2022/5/4 13:49
* @Desc Handler记录Controller中的RequestMapping和Method的对应关系
* 这里是在集合中绑定一对一关系,method一定只属于一个controller,一个controller可有N个method
*/
private class Handler {
protected Object controller; //保存方法对应的实例
protected Method method; //保存映射的方法
protected Pattern pattern;
// key是注解上的value,value是该参数的下标,记录某个value的参数值,对应的下标
//如add(@PABLO_RequestParam("a") Integer a, @PABLO_RequestParam("b") Integer b)
//key=a value=0
//key=b value=1
protected Map<String, Integer> paramIndexMapping;
/**
* 构造一个Handler基本的参数
*
* @param controller
* @param method
*/
protected Handler(Pattern pattern, Object controller, Method method) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
paramIndexMapping = new HashMap<String, Integer>();
putParamIndexMapping(method);
}
//将某个方法和该方法上的参数绑定,具体入参在前,req和resp在后
private void putParamIndexMapping(Method method) {
//提取方法中加了注解的参数
//一个参数可能有多个注解
Annotation[][] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation a : pa[i]) {
if (a instanceof PABLO_RequestParam) {
String paramName = ((PABLO_RequestParam) a).value();
//这里只有该注解中的value值不为null才行
if (!"".equals(paramName.trim())) {
//只有标记了该注解的参数才会被识别
paramIndexMapping.put(paramName, i);
}
}
}
}
//提取方法中的request和response参数
Class<?>[] paramsTypes = method.getParameterTypes();
for (int i = 0; i < paramsTypes.length; i++) {
Class<?> type = paramsTypes[i];
if (type == HttpServletRequest.class ||
type == HttpServletResponse.class) {
paramIndexMapping.put(type.getName(), i);
}
}
}
}
}