1、利用模板創建一個maven項目。
2、創建項目目錄,架構設計如下
3、添加pom依賴,僅此一個,在<dependencies></dependencies>中 添加,其他爲初始化默認數據。
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
4、 在web.xml添加路徑映射,application.properties會標紅,這個數據正常不用管。
<!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>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.spring.mvcframework.servlet.DispatcherServlet</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>springmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
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<String> classNames = new ArrayList<String>();
//核心IOC容器, 保存所有初始化的Bean
private Map<String, Object> ioc = new HashMap<String, Object>();
private Map<String, Method> handlerMapping = new HashMap<String, Method>();
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<String, Object> 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<String, Object> 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<String, String[]> 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<String, String[]> 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