功能介绍
基于servlet完成mvc基本功能
1)@RequestMapping的请求路径映射handler
2)@Service,@controller与@Autowire,对象管理与注入
1、结果展示
2、引入依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.68</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.8.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
<plugin><!-- 编译插件 配置编译java文件时保留方法参数名称(默认不保留方法参数名),以便通过反射调用方法-->
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<compilerArgument>-parameters</compilerArgument>
<encoding>UTF-8</encoding>
<source>11</source>
<target>11</target>
</configuration>
</plugin>
</plugins>
</build>
3、注解开发
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Autowired {
String value() default "";
}
---------
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
String value() default "";
}
-------
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value() default "";
}
------
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
String value() default "";
}
4、web.xml配置
<!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>
<servlet>
<servlet-name>dispatcherServlet</servlet-name><!-- 此处的前端控制器为自己手动编写的dispatcherServlet -->
<servlet-class>com.goschen.mvcframework.DispatcherServlet</servlet-class>
<init-param>
<param-name>configPath</param-name><!-- 设置配置文件,此处用的properties文件 -->
<param-value>config.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
5、编写DispatcherServlet核心逻辑
1)DispatcherServlet继承自HttpServlet并在init中完成初始化逻辑,在service中完成url匹配handler并实现调用的逻辑
2)初始化逻辑主要包括(扫描包路径并存储全限定类名, 解析注解并是实例化bean,处理依赖关系,处理映射关系)
import com.goschen.annotation.Autowired;
import com.goschen.annotation.Controller;
import com.goschen.annotation.RequestMapping;
import com.goschen.annotation.Service;
import com.goschen.pojo.Handler;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
public class DispatcherServlet extends HttpServlet {
// 用于管理实例化的Bean对象
Map<String, Object> ioc = new HashMap<>();
// 扫描到的类全限定类名
List<String> classNameList = new ArrayList<>();
// 维护url与Handler的映射关系
Map<String, Handler> handlerMappingMap = new HashMap<>();
@Override
public void init(ServletConfig config){
try {
// 扫描路径并存储全限定类名
doScan(config);
// 解析注解,并是实例化bean
doResolveComponent();
//处理依赖关系
doAutoWired();
//处理映射关系
doHandlerMapping();
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) {
try {
Object result = null;
String requestURI = req.getRequestURI();
req.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
Handler handler = handlerMappingMap.get(requestURI);
if(handler!=null){
Object controller = handler.getController();
Method method = handler.getMethod();
Map<String, Integer> paramMapping = handler.getParamMapping();
Object[] paramArr = new Object[paramMapping.size()];
Integer requestIndex = paramMapping.get("request");
Integer responseIndex = paramMapping.get("response");
if(requestIndex!=null){
paramArr[requestIndex] = req;
}
if(responseIndex!=null){
paramArr[responseIndex] = resp;
}
Map<String, String[]> apiParameterMap = req.getParameterMap();
for (Map.Entry<String, String[]> paramEntry : apiParameterMap.entrySet()) {
String paramName = paramEntry.getKey();
String[] paramValueArr = paramEntry.getValue();
Integer paramIndex = paramMapping.get(paramName);
paramArr[paramIndex] = new String(paramValueArr[0].getBytes("ISO8859-1"),"UTF-8");
}
result = method.invoke(controller, paramArr);
}else {
result = "404 NOT FOUNT";
}
resp.getWriter().write(String.valueOf(result));
}catch (Exception e){
try {
resp.getWriter().write("500 SERVER INTERNAL ERROR");
} catch (IOException ioException) {}
}
}
/** 处理映射关系*/
private void doHandlerMapping() {
for (Map.Entry<String, Object> singletonMap : ioc.entrySet()) {
Class<?> aClass = singletonMap.getValue().getClass();
if(aClass.isAnnotationPresent(RequestMapping.class)){
RequestMapping handlerRequestMapping = aClass.getAnnotation(RequestMapping.class);
String rootUrl = handlerRequestMapping.value();
Method[] declaredMethods = aClass.getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
declaredMethod.setAccessible(true);
RequestMapping methodRequestMapping = declaredMethod.getAnnotation(RequestMapping.class);
if(methodRequestMapping != null){
Handler handler = new Handler();
String methodUrl = methodRequestMapping.value();
Parameter[] parameters = declaredMethod.getParameters();
Map<String,Integer> paramMapping = new HashMap<>();
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String name = parameter.getName();
paramMapping.put(name,i);
}
handler.setUrl(rootUrl+methodUrl);
handler.setController(singletonMap.getValue());
handler.setMethod(declaredMethod);
handler.setParamMapping(paramMapping);
handlerMappingMap.put(handler.getUrl(),handler);
}
}
}
}
}
/** 处理依赖关系*/
private void doAutoWired() throws IllegalAccessException {
for (Map.Entry<String, Object> singletonObjectMap : ioc.entrySet()) {
Object singletonObject = singletonObjectMap.getValue();
Field[] declaredFields = singletonObject.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
declaredField.setAccessible(true);
Autowired autowired = declaredField.getAnnotation(Autowired.class);
if(autowired != null){
String value = autowired.value();
if("".equals(value)){
value = declaredField.getName();
}
Object o = ioc.get(value);
if(o != null){
declaredField.set(singletonObject,o);
}
}
}
}
}
/** 解析注解注解,并是实例化bean*/
private void doResolveComponent() throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
for (String referenceStr : classNameList) {
String value = null;
Class<?> aClass = Class.forName(referenceStr);
if(aClass.isAnnotationPresent(Service.class)){
Service service = aClass.getAnnotation(Service.class);
value = service.value();
}
if(aClass.isAnnotationPresent(Controller.class)){
Controller service = aClass.getAnnotation(Controller.class);
value = service.value();
}
if(value != null){
if("".equals(value)){
value = lowerCaseFirstChar(aClass.getSimpleName());
}
Object o = aClass.getConstructor().newInstance();
ioc.put(value.trim(),o);
}
}
}
/** 扫描路径并存储全限定类名*/
private void doScan(ServletConfig config) throws IOException {
String configPath = config.getInitParameter("configPath");
InputStream resourceAsStream = DispatcherServlet.class.getClassLoader().getResourceAsStream(configPath);
Properties properties = new Properties();
properties.load(resourceAsStream);
String componentScan = properties.getProperty("componentScan");
String absolutePath = DispatcherServlet.class.getClassLoader().getResource("").getPath();
File rooFile = new File(absolutePath+componentScan);
recursionFile(rooFile);
}
/** 递归文件扫描*/
private void recursionFile(File rooFile){
if(rooFile.isDirectory()){
File[] files = rooFile.listFiles();
for (File file : files) {
recursionFile(file);
}
}else if(rooFile.getName().endsWith("class")){
String classAbsolutePath = rooFile.getAbsolutePath();
classNameList.add(classAbsolutePath.substring(classAbsolutePath.indexOf("classes")+8,classAbsolutePath.length() - 6).replace("\\","."));
}
}
/** 小写首字母*/
private String lowerCaseFirstChar(String name){
return name.substring(0,1).toLowerCase()+name.substring(1);
}
}
@Data
public class Handler {
private String url;
private Object controller;
private Method method;
private Map<String,Integer> paramMapping = new HashMap<>();
}