pom.xml
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!--servlet-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.10</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>UTF-8</encoding>
<!--告訴編譯器,保存形參的值-->
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<!--這裏可以修改端口-->
<port>8089</port>
<path>/</path>
<server>tomcat7</server>
</configuration>
</plugin>
</plugins>
</build>
配置文件
scanPackage=com.liu.mvc.demo
自定義註解
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutoWired {
String value() default "";
}
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
String value() default "";
}
@Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
String value() default "";
}
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
String value() default "";
}
Handler類用記url與method的綁定
package com.liu.mvc.framework.handler;
import java.io.Serializable;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern;
/**
* @Description
* @ClassName Handler
* @Author 劉楠
* @date 2020.07.02
*/
public class Handler implements Serializable {
private static final long serialVersionUID = 1060649943862035180L;
/**
* 控制器
*/
private Object controller;
/**
* 方法
*/
private Method method;
/**
* 匹配
*/
private Pattern pattern;
/**
* 請求url
*/
private String requestUrl;
/**
* 參數的順序 key是參數名 value是第幾個參數
*/
private Map<String, Integer> paramIndexMapping ;
public Handler() {
}
public Handler(Object controller, Method method, Pattern pattern, String requestUrl) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.requestUrl = requestUrl;
this.paramIndexMapping = new HashMap<>();
}
public Object getController() {
return controller;
}
public void setController(Object controller) {
this.controller = controller;
}
public Method getMethod() {
return method;
}
public void setMethod(Method method) {
this.method = method;
}
public Pattern getPattern() {
return pattern;
}
public void setPattern(Pattern pattern) {
this.pattern = pattern;
}
public String getRequestUrl() {
return requestUrl;
}
public void setRequestUrl(String requestUrl) {
this.requestUrl = requestUrl;
}
public Map<String, Integer> getParamIndexMapping() {
return paramIndexMapping;
}
public void setParamIndexMapping(Map<String, Integer> paramIndexMapping) {
this.paramIndexMapping = paramIndexMapping;
}
public void add(String paramName,Integer index){
paramIndexMapping.put(paramName,index);
}
}
自定義MyDispatcherServlet
package com.liu.mvc.framework.servlet;
import com.liu.mvc.framework.annotation.MyAutoWired;
import com.liu.mvc.framework.annotation.MyController;
import com.liu.mvc.framework.annotation.MyRequestMapping;
import com.liu.mvc.framework.annotation.MyService;
import com.liu.mvc.framework.handler.Handler;
import org.apache.commons.lang3.StringUtils;
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.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Description
* @ClassName MyDispatcherServlet
* @Author 劉楠
* @date 2020.07.02
*/
public class MyDispatcherServlet extends HttpServlet {
private Properties props = new Properties();
//緩存類的全類名
private List<String> classNames = new ArrayList<>();
//實現化Bean對象
private Map<String, Object> iocMap = new ConcurrentHashMap<>();
//HandlerMapping
// private Map<String, Handler> handlerMapping = new HashMap<>();;
private List<Handler> handlerMapping = new ArrayList<>();;
@Override
public void init(ServletConfig config) throws ServletException {
//0.從web.xml Servelt中init-param獲取
String contextConfigLocation = config.getInitParameter("contextConfigLocation");
//1.加載配置文件 properties
doLoadConfig(contextConfigLocation);
//2.掃描有相關注解的類
doScan(props.getProperty("scanPackage"));
//3.初始化Bean(IOC容器)
doInstance();
//4. 實現對象之間依賴注入
doAutoWire();
//5.初始SpringMVC相關組件如HandlerMapping處理器映射器 url method 綁定關係
initHandlerMapping();
System.out.println("MyMVC初始化完批");
//6.處理請求
}
private void doLoadConfig(String contextConfigLocation) {
//讀成注
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
//加載
try {
props.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 掃描包
*
* @param basePackage
*/
public void doScan(String basePackage) {
String classPath = Thread.currentThread().getContextClassLoader().getResource("").getPath();
String basePackagePath = basePackage.replaceAll("\\.", "/");
String scanPath = classPath + basePackagePath;
File pack = new File(scanPath);
File[] files = pack.listFiles();
for (File file : files) {
if (file.isDirectory()) {
//遞歸
doScan(basePackage + "." + file.getName());
} else if (file.getName().endsWith(".class")) {
//文件class文件 全類名
String className = basePackage + "." + file.getName().replaceAll(".class", "");
classNames.add(className);
}
}
}
/**
* 實例化Bean對象
* 基於類的全類名和反射創建對象
*/
private void doInstance() {
if (classNames.size() == 0) {
return;
}
try {
for (String className : classNames) {
//com.liu.mvc.demo.controller.DemoController
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(MyController.class)) {
//是個Controller 類的首字母小寫保存
String simpleName = clazz.getSimpleName();
//首字母小寫
String lowerFirstSimpleName = lowerFirst(simpleName);
//實現化對象
Object instance = clazz.newInstance();
iocMap.put(lowerFirstSimpleName, instance);
} else if (clazz.isAnnotationPresent(MyService.class)) {
//是Service
MyService annotation = clazz.getAnnotation(MyService.class);
Object newInstance = clazz.newInstance();
String beanName = annotation.value();
if (!"".equals(beanName)) {
iocMap.put(beanName, newInstance);
} else {
beanName = lowerFirst(clazz.getSimpleName());
iocMap.put(beanName, newInstance);
}
/**
* 將接口也放一份
*/
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> anInterface : interfaces) {
//以接口的全類名
iocMap.put(anInterface.getName(), newInstance);
}
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
public String lowerFirst(String str) {
char[] chars = str.toCharArray();
if ('A' <= chars[0] && chars[0] <= 'Z') {
chars[0] += 32;
}
return String.valueOf(chars);
}
/**
* 實現Bean之間的依賴注入
*/
private void doAutoWire() {
if (iocMap.isEmpty()) {
return;
}
try {
/**
* 遍歷容器中的所有對象 判斷屬性有沒有MyAutoWire註解
*/
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
/**
* 對象實例
*/
Object entryValue = entry.getValue();
//獲取所有字段
Field[] fields = entryValue.getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(MyAutoWired.class)) {
continue;
}
//有註解
MyAutoWired annotation = field.getAnnotation(MyAutoWired.class);
String beanName = annotation.value();
if ("".equals(beanName.trim())) {
//獲取類型再獲取名稱
beanName = field.getType().getName();
}
//開啓賦值
field.setAccessible(true);
field.set(entryValue, iocMap.get(beanName));
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
/**
* 實現key-value url-method映射關係
*/
private void initHandlerMapping() {
if(iocMap.isEmpty()){
return;
}
try {
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
Object value = entry.getValue();
Class<?> clazz = value.getClass();
//判斷是不是MyController繼續
if(!clazz.isAnnotationPresent(MyController.class)){
continue;
}
String baseUrl="";
if(clazz.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = clazz.getAnnotation(MyRequestMapping.class);
baseUrl=annotation.value();
}
//獲取所有方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if(!method.isAnnotationPresent(MyRequestMapping.class)){
//沒有註解不處理
continue;
}
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
String methodUrl =annotation.value();
String url = baseUrl+methodUrl;
Pattern pattern = Pattern.compile(url);
Handler handler =new Handler(value, method,pattern , url);
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
if(parameter.getType() ==HttpServletRequest.class || parameter.getType()==HttpServletResponse.class) {
handler.add(parameter.getType().getSimpleName(),i);
}else{
handler.add(parameter.getName(), i);
}
}
handlerMapping.add(handler);
}
}
}catch (Exception e){
e.printStackTrace();
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//請求的uri
Handler handler= getHandler(req);
if(handler==null){
resp.getWriter().write("404 not found");
}
//參數類型數組
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
//創建數組 並與方法中的參數順序保持一致
Object[] args = new Object[parameterTypes.length];
//
Map<String, Integer> paramIndexMapping = handler.getParamIndexMapping();
/**
* 獲取請求的參數 填充普通參數
*/
Map<String, String[]> parameterMap = req.getParameterMap();
for(Map.Entry<String,String[]> entry:parameterMap.entrySet()){
String entryKey = entry.getKey();
String[] entryValue = entry.getValue();
//遍歷所有參數
String join = StringUtils.join(entryValue, ",");
//如果也方法中的參數匹配上就填充
if(! paramIndexMapping.containsKey(entryKey)){
continue;
}
//有這個參數 找到這個索引
Integer index = paramIndexMapping.get(entryKey);
//賦值
args[index]=join;
}
//填充request與response參數
Integer requestIndex = paramIndexMapping.get(HttpServletRequest.class.getSimpleName());
args[requestIndex]=req;
Integer responseIndex = paramIndexMapping.get(HttpServletResponse.class.getSimpleName());
args[responseIndex]=resp;
try {
handler.getMethod().invoke(handler.getController(),args );
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
private Handler getHandler(HttpServletRequest req) {
if(handlerMapping.isEmpty()){
return null;
}
String requestURI = req.getRequestURI();
Optional<Handler> handler = handlerMapping.stream().filter(h -> {
Matcher matcher = h.getPattern().matcher(requestURI);
return matcher.matches();
}).findFirst();
if(handler.isPresent()){
return handler.get();
}
return null;
}
}
web.xml
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1"
metadata-complete="false">
<servlet>
<servlet-name>mvcServelt</servlet-name>
<servlet-class>com.liu.mvc.framework.servlet.MyDispatcherServlet</servlet-class>
<!--初始參數-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>springmvc.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvcServelt</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>index.html</welcome-file>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.js</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.html</url-pattern>
<url-pattern>*.htm</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.gif</url-pattern>
<url-pattern>*.ico</url-pattern>
</servlet-mapping>
</web-app>
Controller
@MyController
@MyRequestMapping("/demo")
public class DemoController {
@MyAutoWired
private IDemoServcie demoServcie;
@MyRequestMapping("/query")
public String query(HttpServletRequest request, HttpServletResponse response, String name){
String s = demoServcie.get(name);
return s;
}
}
service
public interface IDemoServcie {
String get(String name);
}
@MyService("demoService")
public class DemoServcieImpl implements IDemoServcie {
@Override
public String get(String name) {
System.err.println("DemoServcieImpl: "+name);
return name;
}
}
完成
https://gitee.com/null_631_9084/myhomework/tree/master/stage01-springmvc/springmvc-task02