1、利用模板创建一个maven项目。
2、创建项目目录,架构设计如下
3、添加pom依赖,仅此一个,在
javax.servlet
javax.servlet-api
4.0.1
provided
4、 在web.xml添加路径映射,application.properties会标红,这个数据正常不用管。
Archetype Created Web Application
springmvc
com.spring.mvcframework.servlet.DispatcherServlet
contextConfigLocation
application.properties
1
springmvc
/*
5、application.properties内容就一行。
scanPackage=com.spring.demo
6、各注解实现
package com.spring.mvcframework.annotation;
import java.lang.annotation.*;
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbAutowired {
String value() default "";
}
package com.spring.mvcframework.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbController {
String value() default "";
}
package com.spring.mvcframework.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbRequestMapping {
String value() default "";
}
package com.spring.mvcframework.annotation;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbRequestParam {
String value() default "";
}
package com.spring.mvcframework.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface DwbService {
String value() default "";
}
7、DemoAction类。注意注入时要写接口,不能是实现类,不然访问会报空指针异常,即注入的service为null。
package com.spring.demo.action;
import com.spring.demo.service.DemoService;
import com.spring.mvcframework.annotation.DwbAutowired;
import com.spring.mvcframework.annotation.DwbController;
import com.spring.mvcframework.annotation.DwbRequestMapping;
import com.spring.mvcframework.annotation.DwbRequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@DwbController
@DwbRequestMapping("/demo")
public class DemoAction {
@DwbAutowired
private DemoService demoService;
@DwbRequestMapping("/get")
public void get(HttpServletRequest req, HttpServletResponse res,
@DwbRequestParam("name") String name) {
System.out.println(" 参数:====================" + name);
String result = demoService.get(name);
try {
res.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@DwbRequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse res,
@DwbRequestParam("number1") Integer number1, @DwbRequestParam("number2") Integer number2) {
try {
res.getWriter().write(number1 + "+" + number2 + "=" + (number1 + number2));
} catch (IOException e) {
e.printStackTrace();
}
}
@DwbRequestMapping("delete")
public void delete(HttpServletRequest req, HttpServletResponse res,
@DwbRequestParam("id") Integer id) {
System.out.println(" 参数:====================" + id);
String result = "id" + id + "已删除";
try {
res.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
}
8、DemoService接口
package com.spring.demo.service;
public interface DemoService {
public String get(String name);
}
9、DemoService接口实现DemoServiceImpl
package com.spring.demo.service;
import com.spring.mvcframework.annotation.DwbService;
@DwbService
public class DemoServiceImpl implements DemoService {
public String get(String name) {
return "hello" + name;
}
}
10、DispatcherServlet类,doInstance方法中有newInstance要注意servlet-api的版本,详见方法上的注释。
package com.spring.mvcframework.servlet;
import com.spring.mvcframework.annotation.DwbAutowired;
import com.spring.mvcframework.annotation.DwbController;
import com.spring.mvcframework.annotation.DwbRequestMapping;
import com.spring.mvcframework.annotation.DwbService;
import javax.servlet.ServletConfig;
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.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
public class DispatcherServlet extends HttpServlet {
private static final long serialVersionUID = 1L;
//跟web.xml中param-name的值一致
private static final String LOCATION = "contextConfigLocation";
//保存所有配置信息
private Properties p = new Properties();
//保存所有被扫描到的相关的类名
private List classNames = new ArrayList();
//核心IOC容器, 保存所有初始化的Bean
private Map ioc = new HashMap();
private Map handlerMapping = new HashMap();
public DispatcherServlet() {
super();
}
/**
* 初始化,加载配置文件
*
* @param config 配置
*/
public void init(ServletConfig config) {
//1、加载配置文件
doLoadConfig(config.getInitParameter(LOCATION));
//2、扫描所有相关的类
doScanner(p.getProperty("scanPackage"));
//3、初始化所有相关的实例,并保存到IOC容器中
doInstance();
//4、依赖注入
doAutowired();
//5、构造HandlerMapping
initHandleMapping();
//6、等待请求 匹配URL,定位方法,反射调用执行
//调用doGet方法或doPost方法
//提示信息
System.out.println("spring mvc framework is init");
}
private void doLoadConfig(String location) {
InputStream fis = null;
try {
fis = this.getClass().getClassLoader().getResourceAsStream(location);
//读取配置文件
if (null == fis){
System.out.println("扫描文件不应该为空=============");
}
p.load(fis);
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
if (null != fis) {
fis.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
private void doScanner(String packageName) {
//将所有的包路径替换为文件路径
URL url = this.getClass().getClassLoader().getResource("/" + packageName.replaceAll("\\.", "/"));
File dir = new File(url.getFile());
for (File file : dir.listFiles()) {
//如果是文件夹,继续递归
if (file.isDirectory()) {
doScanner(packageName + "." + file.getName());
} else {
classNames.add(packageName + "." + file.getName().replaceAll(".class", "").trim());
}
}
}
/**
* IOC容器的key默认是类名首字母小写,如果是自己自己设置类名,则优先使用自定义的。
*
* @param str 类名
* @return 仅转换首字母的字符串
*/
private String lowerFirstCase(String str) {
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
/**
*
* 下面的clazz.getDeclaredConstructor().newInstance()
* 在3.0.1之前使用 clazz.newInstance()
*
*/
private void doInstance() {
if (classNames.size() == 0) {
return;
}
try {
for (String classNameItem : classNames) {
Class clazz = Class.forName(classNameItem);
if (clazz.isAnnotationPresent(DwbController.class)) {
//默认首字母小写作为beanName
String beanName = lowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, clazz.getDeclaredConstructor().newInstance());
} else if (clazz.isAnnotationPresent(DwbService.class)) {
DwbService service = clazz.getAnnotation(DwbService.class);
String beanName = service.value();
//如果用户设置了名字,就用用户自己的设置
if (!"".equals(beanName.trim())) {
ioc.put(beanName, clazz.getDeclaredConstructor().newInstance());
continue;
}
//如果用户没设,就按接口类型创建一个实例
Class[] interfaces = clazz.getInterfaces();
for (Class i : interfaces) {
ioc.put(i.getName(), clazz.getDeclaredConstructor().newInstance());
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
//拿到实例对象中的所有属性
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(DwbAutowired.class)) {
continue;
}
DwbAutowired autowired = field.getAnnotation(DwbAutowired.class);
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
field.setAccessible(true);
try {
field.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
private void initHandleMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(DwbController.class)) {
continue;
}
String baseUrl = "";
//获取Controller的url配置
if (clazz.isAnnotationPresent(DwbRequestMapping.class)) {
DwbRequestMapping requestMapping = clazz.getAnnotation(DwbRequestMapping.class);
baseUrl = requestMapping.value();
}
//获取Method的url值
Method[] methods = clazz.getMethods();
for (Method method : methods) {
//没有加RequestMapping注解的直接忽略
if (!method.isAnnotationPresent(DwbRequestMapping.class)) {
continue;
}
//映射URL
DwbRequestMapping requestMapping = method.getAnnotation(DwbRequestMapping.class);
String url = ("/" +baseUrl +"/" + requestMapping.value()).replaceAll("/+", "/");
handlerMapping.put(url, method);
System.out.println("mapped " + url + "," + method);
}
}
}
public void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException {
this.doPost(req, res);
}
/**
* 业务处理
*
* @param req
* @param res
*/
public void doPost(HttpServletRequest req, HttpServletResponse res) throws IOException {
try {
//开始匹配到对应的方法
doDispatch(req, res);
} catch (Exception e) {
//如果匹配过程中出现异常,将异常值打印出去
res.getWriter().write("500 Exception, Details: \r\n"
+ Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\}]", "")
.replaceAll(",\\s", "\r\n"));
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse res) throws IOException {
if (this.handlerMapping.isEmpty()) {
return;
}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
res.getWriter().write("404 Not Found");
return;
}
Method method = this.handlerMapping.get(url);
//获取方法的参数列表
Class[] paramsTypes = method.getParameterTypes();
//获取请求的参数
Map paramterMap = req.getParameterMap();
//保存参数值
Object[] paramValues = new Object[paramsTypes.length];
//方法的参数列表
for (int i = 0; i < paramsTypes.length; i++) {
//根据参数名称做某些处理,
Class parameterType = paramsTypes[i];
if (parameterType == HttpServletRequest.class) {
//参数类型已明确,强制转类型
paramValues[i] = req;
continue;
} else if (parameterType == HttpServletResponse.class) {
paramValues[i] = res;
continue;
} else if (parameterType == String.class) {
for (Map.Entry param : paramterMap.entrySet()) {
String value = Arrays.toString(param.getValue())
.replaceAll("\\[|\\]", "")
.replaceAll(",\\s", ",");
paramValues[i] = value;
}
}
}
try {
String beanName = lowerFirstCase(method.getDeclaringClass().getSimpleName());
//利用反射机制来调用
method.invoke(this.ioc.get(beanName), paramValues);
} catch (Exception e) {
e.printStackTrace();
}
}
}
最后添加tomcat并运行,会返回404,没关系,我们没有设置默认页。在输入http://localhost:8080/demo/get?name=ddd即可看到返回数据,方法调用成功。
代码获取github:https://github.com/dwenb/springmvc-demo
本文参考:https://www.jianshu.com/p/f57db8b29be9