兩小時手寫springmvc框架
這篇文章是學習咕泡學院tom老師手寫一個spring框架視訊而來,程式碼基本複製原文,稍作修改。可以幫助我們理解springmvc實現的大致原理。
1、構建maven工程,只需要匯入javax.servlet-api的依賴。另外配置,直接通過tomcat外掛來啟動專案。
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>springmvc</finalName>
<plugins>
<plugin>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-maven-plugin</artifactId>
<version>9.4.7.v20170914</version>
<configuration>
<webApp>
<contextPath>/${project.build.finalName}</contextPath>
</webApp>
<stopKey>CTRL+C</stopKey>
<stopPort>8999</stopPort>
<scanIntervalSeconds>10</scanIntervalSeconds>
<scanTargets>
<scanTarget>src/main/webapp/WEB-INF/web.xml</scanTarget>
</scanTargets>
</configuration>
</plugin>
</plugins>
</build>
專案結構:
2、編寫主要的程式碼並配置web.xml
XXAutowired.java
package com.xxx.springmvc.annotation;
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XXAutowired {
String value() default "";
}
XXController.java
package com.xxx.springmvc.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XXController {
String value() default "";
}
XXRequestMapping.java
package com.xxx.springmvc.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XXRequestMapping {
String value() default "";
}
XXRequestParam.java
package com.xxx.springmvc.annotation;
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XXRequestParam {
String value() default "";
}
XXService.java
package com.xxx.springmvc.annotation;
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface XXService {
String value() default "";
}
實體類User.java
package com.xxx.springmvc.entity;
public class User {
private String id;
private String username;
private String password;
...此處省略get set方法
@Override
public String toString() {
return "{\"id\":" + id + ", \"username\":\"" + username + "\", \"password\":\""
+ password + "\"}";
}
public User() {}
public User(String id, String username, String password) {
this.id = id;
this.username = username;
this.password = password;
}
}
UserService.java介面檔案
package com.xxx.springmvc.web.service;
import java.util.List;
import com.xxx.springmvc.entity.User;
public interface UserService {
String get(String name);
List<User> list();
}
UserServiceImpl.java
package com.xxx.springmvc.web.service.impl;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.xxx.springmvc.annotation.XXService;
import com.xxx.springmvc.entity.User;
import com.xxx.springmvc.web.service.UserService;
@XXService("userService")
public class UserServiceImpl implements UserService {
private static Map<String,User> users = new HashMap<String, User>();
static{
users.put("aa", new User("1","aaa","123456"));
users.put("bb", new User("2","bbb","123456"));
users.put("cc", new User("3","ccc","123456"));
users.put("dd", new User("4","ddd","123456"));
users.put("ee", new User("5","eee","123456"));
}
@Override
public String get(String name) {
User user = users.get(name);
if(user==null){
user = users.get("aa");
}
return user.toString();
}
@Override
public List<User> list() {
List<User> list = new ArrayList<User>();
for(Entry<String, User> entry : users.entrySet()){
list.add(entry.getValue());
}
return list;
}
}
UserController.java
package com.xxx.springmvc.web.controller;
import java.io.IOException;
import java.util.List;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xxx.springmvc.annotation.XXAutowired;
import com.xxx.springmvc.annotation.XXController;
import com.xxx.springmvc.annotation.XXRequestMapping;
import com.xxx.springmvc.annotation.XXRequestParam;
import com.xxx.springmvc.entity.User;
@XXController
@XXRequestMapping("/user")
public class UserController {
@XXAutowired
private com.xxx.springmvc.web.service.UserService userService;
@XXRequestMapping("/index")
public String index(HttpServletRequest request,HttpServletResponse response,
@XXRequestParam("name")String name) throws IOException{
String res = userService.get(name);
System.out.println(name+"=>"+res);
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(res);
return "index";
}
@XXRequestMapping("/list")
public String list(HttpServletRequest request,HttpServletResponse response)
throws IOException{
List<User> users = userService.list();
response.setContentType("application/json;charset=UTF-8");
response.getWriter().write(users.toString());
return "list";
}
}
核心類:XXDispatcherServlet.java
package com.xxx.springmvc.servlet;
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.Method;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import com.xxx.springmvc.annotation.XXAutowired;
import com.xxx.springmvc.annotation.XXController;
import com.xxx.springmvc.annotation.XXRequestMapping;
import com.xxx.springmvc.annotation.XXRequestParam;
import com.xxx.springmvc.annotation.XXService;
public class XXDispatcherServlet extends HttpServlet{
private Properties contextConfig = new Properties();
private List<String> classNames = new ArrayList<String>();
private Map<String, Object> ioc = new HashMap<String, Object>();
private List<Handler> handlerMapping = new ArrayList<Handler>();
private static final long serialVersionUID = -4943120355864715254L;
@Override
public void init(ServletConfig config) throws ServletException {
//load config
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//scan relative class
doScanner(contextConfig.getProperty("scanPackage"));
//init ioc container put relative class to it
doInstance();
//inject dependence
doAutoWired();
//init handlerMapping
initHandlerMapping();
}
private void initHandlerMapping() {
if(ioc.isEmpty())return;
for(Map.Entry<String, Object> entry:ioc.entrySet()){
Class<?> clazz = entry.getValue().getClass();
if(!clazz.isAnnotationPresent(XXController.class)){continue;}
String baseUrl = "";
if(clazz.isAnnotationPresent(XXRequestMapping.class)){
XXRequestMapping requestMapping = clazz.getAnnotation(XXRequestMapping.class);
baseUrl = requestMapping.value();
}
Method[] methods = clazz.getMethods();
for(Method method:methods){
if(!method.isAnnotationPresent(XXRequestMapping.class)){continue;}
XXRequestMapping requestMapping = method.getAnnotation(XXRequestMapping.class);
String url = (baseUrl+requestMapping.value()).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(url);
handlerMapping.add(new Handler(pattern, entry.getValue(), method));
System.out.println("mapped:"+url+"=>"+method);
}
}
}
private void doAutoWired() {
if(ioc.isEmpty())return;
for(Map.Entry<String, Object> entry:ioc.entrySet()){
//依賴注入->給加了XXAutowired註解的欄位賦值
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for(Field field:fields){
if(!field.isAnnotationPresent(XXAutowired.class)){continue;}
XXAutowired autowired = field.getAnnotation(XXAutowired.class);
String beanName = autowired.value();
if("".equals(beanName)){
beanName = field.getType().getName();
}
field.setAccessible(true);
try {
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
continue;
}
}
}
}
private void doInstance() {
if(classNames.isEmpty())return;
try {
for(String className:classNames){
Class<?> clazz = Class.forName(className);
if(clazz.isAnnotationPresent(XXController.class)){
String beanName = lowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, clazz.newInstance());
}else if(clazz.isAnnotationPresent(XXService.class)){
XXService service = clazz.getAnnotation(XXService.class);
String beanName = service.value();
if("".equals(beanName)){
beanName = lowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
Class<?>[] interfaces = clazz.getInterfaces();
for(Class<?> i:interfaces){
ioc.put(i.getName(), instance);
}
}else{
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private void doScanner(String packageName) {
URL resource =
this.getClass().getClassLoader().getResource("/"+packageName.replaceAll("\\.", "/"));
File classDir = new File(resource.getFile());
for(File classFile:classDir.listFiles()){
if(classFile.isDirectory()){
doScanner(packageName+"."+classFile.getName());
}else{
String className = (packageName+"."+classFile.getName()).replace(".class", "");
classNames.add(className);
}
}
}
private void doLoadConfig(String location) {
InputStream input = this.getClass().getClassLoader().getResourceAsStream(location);
try {
contextConfig.load(input);
} catch (IOException e) {
e.printStackTrace();
}finally{
if(input!=null){
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
this.doPost(req, res);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
doDispatcher(req, res);
}
public void doDispatcher(HttpServletRequest req,HttpServletResponse res){
try {
Handler handler = getHandler(req);
if(handler==null){
res.getWriter().write("404 not found.");
return;
}
Class<?>[] paramTypes = handler.method.getParameterTypes();
Object[] paramValues = new Object[paramTypes.length];
Map<String, String[]> params = req.getParameterMap();
for(Entry<String, String[]> param:params.entrySet()){
String value = Arrays.toString(param.getValue()).replaceAll("\\[|\\]", "");
if(!handler.paramIndexMapping.containsKey(param.getKey())){continue;}
int index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(paramTypes[index],value);
}
int reqIndex = handler.paramIndexMapping.get(HttpServletRequest.class.getName());
paramValues[reqIndex] = req;
int resIndex = handler.paramIndexMapping.get(HttpServletResponse.class.getName());
paramValues[resIndex] = res;
handler.method.invoke(handler.controller, paramValues);
} catch (Exception e) {
e.printStackTrace();
}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
}
private Object convert(Class<?> type, String value) {
if(Integer.class == type){
return Integer.valueOf(value);
}
return value;
}
private String lowerFirstCase(String str){
char[] chars = str.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
private Handler getHandler(HttpServletRequest req){
if(handlerMapping.isEmpty()){return null;}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath, "").replaceAll("/+", "/");
for(Handler handler:handlerMapping){
Matcher matcher = handler.pattern.matcher(url);
if(!matcher.matches()){continue;}
return handler;
}
return null;
}
private class Handler{
protected Object controller;
protected Method method;
protected Pattern pattern;
protected Map<String, Integer> paramIndexMapping;
protected Handler(Pattern pattern,Object controller,Method method){
this.pattern = pattern;
this.controller = controller;
this.method = method;
paramIndexMapping = new HashMap<String, Integer>();
putParamIndexMapping(method);
}
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 XXRequestParam){
String paramName = ((XXRequestParam)a).value();
if(!"".equals(paramName)){
paramIndexMapping.put(paramName, i);
}
}
}
}
Class<?>[] paramTypes = method.getParameterTypes();
for(int i=0;i<paramTypes.length;i++){
Class<?> type = paramTypes[i];
if(type == HttpServletRequest.class || type == HttpServletResponse.class){
paramIndexMapping.put(type.getName(), i);
}
}
}
}
}
配置檔案config.properties
scanPackage=com.xxx.springmvc.web
配置檔案web.xml
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>com.xxx.springmvc.servlet.XXDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>config.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>
3、執行:
控制檯列印日誌:
訪問 http://localhost:8080/springmvc/user/index?name=bb:
訪問 http://localhost:8080/springmvc/user/list:
訪問 http://localhost:8080/springmvc/user/detail 出現404:
相關文章
- 自己手寫一個SpringMVC框架(簡化)SpringMVC框架
- 手寫SpringMvc概要SpringMVC
- 《四 spring原始碼》手寫springmvc原始碼SpringMVC
- 手把手教你寫一個SpringMVC框架SpringMVC框架
- 【SpringMVC】SpringMVC搭建框架SpringMVC框架
- 高手過招「效能優化/純手寫SpringMVC框架/MySql優化/微服務」優化SpringMVC框架MySql微服務
- 手寫RPC框架RPC框架
- 手寫Spring框架Spring框架
- 手寫mybatis框架MyBatis框架
- 純手寫web框架Web框架
- 手寫迷你Spring框架Spring框架
- 手寫一個HTTP框架:兩個類實現基本的IoC功能HTTP框架
- 看年薪50W的架構師如何手寫一個SpringMVC框架(文末附視訊)架構SpringMVC框架
- 純手寫Arraylist集合框架框架
- 手寫mini版MVC框架MVC框架
- SpringMVC-08-SpringMVC層編寫SpringMVC
- SpringMVC框架簡介②SpringMVC框架
- SpringMVC框架簡介①SpringMVC框架
- SpringMVC框架入門SpringMVC框架
- 基於netty手寫RPC框架NettyRPC框架
- 手寫RPC框架(六)整合NettyRPC框架Netty
- 手寫自己的MyBatis框架-SqlSessionMyBatis框架SQLSession
- 手寫簡易版React框架React框架
- SpringMVC框架搭建環境SpringMVC框架
- 兩小時快速構建微信小程式微信小程式
- springmvc小專案SpringMVC
- 《四 spring原始碼》手寫springioc框架Spring原始碼框架
- 仿照dubbo手寫一個RPC框架RPC框架
- 手寫事件釋出訂閱框架事件框架
- 手寫開源ORM框架介紹ORM框架
- 用 vue 寫小程式,基於 mpvue 框架重寫 weuiVue框架UI
- 搭建 Spring+SpringMVC+MyBatis 框架SpringMVCMyBatis框架
- Spring+SpringMVC+MyBatis框架整合SpringMVCMyBatis框架
- 自己動手寫一個持久層框架框架
- 自己動手寫Android資料庫框架Android資料庫框架
- 手寫Spring框架,是時候擼個AOP與Bean生命週期融合了!Spring框架Bean
- 手寫Spring MVC框架(一) 實現簡易版mvc框架SpringMVC框架
- canvas手寫辨色力小遊戲Canvas遊戲