通过手写来实现SpringMvc三层模型,spring的Ioc控制反转,Di依赖注入的功能,主要分为配置阶段,初始化阶段和运行阶段这三个阶段。
1.配置web.xml,设定init-param和param-name , 设置url-parttern,方法的过滤路径为 /*。
Archetype Created Web Application
gpmvc
com.alan.mvcframework.v2.servlet.MyDispatchServlet
contextConfigLocation
application.properties
1
gpmvc
/*
2.创建 application.properties ,配置包扫描的路径。
scanPackage=com.alan.demo
3. 创建自己的注解文件。
import java.lang.annotation.*;
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPController {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {
String value() default "";
}
import java.lang.annotation.*;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPService {
String value() default "";
}
4.引入 servlet-api 的依赖jar包
javax.servlet
servlet-api
${servlet.api.version}
provided
创建一个类继承 HttpServlet,重写 doGet ,doPost ,init 方法 。
1.获取包扫描的配置信息。
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
//存入一个property里面
contextConfig.load(is);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
2.扫描包下的所有类,将类名全路径名称存入数组中。
private void doScanner(String scanPackage) {
//获取classLoader下面该路径的url,由/拼接
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
//获取File文件
File classPath = new File(url.getFile());
//循环文件路径,获取下面的子文件(文件架)
for (File file : classPath.listFiles()) {
//如果是文件夹,递归
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
//如果是文件,判断是否是class文件
if (!file.getName().endsWith(".class")) {
continue;
}
//去掉后缀,将文件的全路径名存入数组
classNames.add(scanPackage + "." + file.getName().replace(".class", ""));
}
}
}
3.扫描到的类实例化。
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
try {
//循环扫描的类名称
for (String className : classNames) {
//反射获取Class
Class> clazz = Class.forName(className);
//如果注解是controller
if (clazz.isAnnotationPresent(MyController.class)) {
//获取实例
Object instance = clazz.newInstance();
//获取类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
//将类名作为key,类实例做为value存入map(ioc容器)
ioc.put(beanName, instance);
//注解是service
} else if (clazz.isAnnotationPresent(MyService.class)) {
//获取注解内的名称
String beanName = clazz.getAnnotation(MyService.class).value();
//注解内没有名称,类名为clazz的首字母小写类名
if ("".equals(beanName.trim())) {
beanName = toLowerFirstCase(clazz.getSimpleName());
}
//获取内的实例
Object instance = clazz.newInstance();
//将类名作为key,类实例做为value存入map(ioc容器)
ioc.put(beanName, instance);
//循环获取class的所有接口
for (Class> i : clazz.getInterfaces()) {
//如果接口有多个实现类,报异常
if (ioc.containsKey(i.getName())) {
throw new Exception("The " + i.getName() + " is exists!!");
}
//否则将接口作为key,实例作为value存入map
ioc.put(i.getName(), instance);
}
//没有注解,跳出,下一个
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
4.将扫描到的类的所有属性进行依赖注入。
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
//循环ioc容器
for (Map.Entry entry : ioc.entrySet()) {
//获取value的所有的 private/protected/default/public 修饰字段
for (Field field : entry.getValue().getClass().getDeclaredFields()) {
//没有MyAutowired的注解不管
if (!field.isAnnotationPresent(MyAutowired.class)) {
continue;
}
MyAutowired autowired = field.getAnnotation(MyAutowired.class);
String beanName = autowired.value().trim();
//如果注解内没有value,beanName就为字段的类型
if ("".equals(beanName.trim())) {
beanName = field.getType().getName();
}
//暴力访问
field.setAccessible(true);
try {
//将这个字段注入到这个类中
field.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
5. 获取类的方法和方法的url参数,存入handlerMapping中。
private void doInitHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
//循环ioc容器
for (Map.Entry entry : ioc.entrySet()) {
//获取value(Object)的class
Class> clazz = entry.getValue().getClass();
//如果这个类没有controller注解,不管它
if (!clazz.isAnnotationPresent(MyController.class)) {
continue;
}
//获取controller上面的url
String baseUrl = "";
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
baseUrl = requestMapping.value();
}
//获取class的所有方法
for (Method method : clazz.getMethods()) {
//没有MyRequestMapping注解的方法不管
if (!method.isAnnotationPresent(MyRequestMapping.class)) {
continue;
}
MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
//将url作为key,方法作为value存入handlerMapping中
handlerMapping.put(url, method);
System.out.println("Map :" + url + "," + method);
}
}
}
1.根据 请求url 从 handlerMapping 获取方法,2. 获取请求参数,3. 调用请求。
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
resp.getWriter().write("404 not found!!");
return;
}
Map params = req.getParameterMap();
//根据url获取方法
Method method = this.handlerMapping.get(url);
//获取形参列表
Class>[] parameterTypes = method.getParameterTypes();
//获取参数类型
Object[] paramValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class paramterType = parameterTypes[i];
if (paramterType == HttpServletRequest.class) {
paramValues[i] = req;
} else if (paramterType == HttpServletResponse.class) {
paramValues[i] = resp;
} else if (paramterType == String.class) {
//通过运行时的状态去拿到你
Annotation[][] pa = method.getParameterAnnotations();
for (int j = 0; j < pa.length; j++) {
for (Annotation a : pa[i]) {
if (a instanceof MyRequestParam) {
String paramName = ((MyRequestParam) a).value();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(params.get(paramName))
.replaceAll("\\[|\\]", "")
.replaceAll("\\s+", ",");
paramValues[i] = value;
}
}
}
}
}
}
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
//反射调用方法
method.invoke(ioc.get(beanName), paramValues);
}
最后来结合整体思路来看看总体代码:
import com.alan.mvcframework.annotation.*;
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.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
public class MyDispatchServlet extends HttpServlet {
private Properties contextConfig = new Properties();
//存扫描的类
private List classNames = new ArrayList();
//IOC容器
private Map ioc = new HashMap();
//handlerMapping存url和method
private Map handlerMapping = new HashMap();
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//委派,根据url去找到一个对应的Method,然后通过response返回
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exception,Detail :" + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!this.handlerMapping.containsKey(url)) {
resp.getWriter().write("404 not found!!");
return;
}
Map params = req.getParameterMap();
//根据url获取方法
Method method = this.handlerMapping.get(url);
//获取形参列表
Class>[] parameterTypes = method.getParameterTypes();
//获取参数类型
Object[] paramValues = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class paramterType = parameterTypes[i];
if (paramterType == HttpServletRequest.class) {
paramValues[i] = req;
} else if (paramterType == HttpServletResponse.class) {
paramValues[i] = resp;
} else if (paramterType == String.class) {
//通过运行时的状态去拿到你
Annotation[][] pa = method.getParameterAnnotations();
for (int j = 0; j < pa.length; j++) {
for (Annotation a : pa[i]) {
if (a instanceof MyRequestParam) {
String paramName = ((MyRequestParam) a).value();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(params.get(paramName))
.replaceAll("\\[|\\]", "")
.replaceAll("\\s+", ",");
paramValues[i] = value;
}
}
}
}
}
}
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
//反射调用方法
method.invoke(ioc.get(beanName), paramValues);
}
@Override
public void init(ServletConfig config) throws ServletException {
// 加载配置文件,获取web.xml里面init-param的param-name,作为参数传递
doLoadConfig(config.getInitParameter("contextConfigLocation"));
// 扫描所有的类,传入配置的扫描包路径
doScanner(contextConfig.getProperty("scanPackage"));
// 将类的实例存入IOC容器
doInstance();
//DI 依赖注入
doAutowired();
//初始化handlerMapping
doInitHandlerMapping();
}
private void doInitHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
//循环ioc容器
for (Map.Entry entry : ioc.entrySet()) {
//获取value(Object)的class
Class> clazz = entry.getValue().getClass();
//如果这个类没有controller注解,不管它
if (!clazz.isAnnotationPresent(MyController.class)) {
continue;
}
//获取controller上面的url
String baseUrl = "";
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping requestMapping = clazz.getAnnotation(MyRequestMapping.class);
baseUrl = requestMapping.value();
}
//获取class的所有方法
for (Method method : clazz.getMethods()) {
//没有MyRequestMapping注解的方法不管
if (!method.isAnnotationPresent(MyRequestMapping.class)) {
continue;
}
MyRequestMapping requestMapping = method.getAnnotation(MyRequestMapping.class);
String url = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
//将url作为key,方法作为value存入handlerMapping中
handlerMapping.put(url, method);
System.out.println("Map :" + url + "," + method);
}
}
}
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
//循环ioc容器
for (Map.Entry entry : ioc.entrySet()) {
//获取value的所有的 private/protected/default/public 修饰字段
for (Field field : entry.getValue().getClass().getDeclaredFields()) {
//没有MyAutowired的注解不管
if (!field.isAnnotationPresent(MyAutowired.class)) {
continue;
}
MyAutowired autowired = field.getAnnotation(MyAutowired.class);
String beanName = autowired.value().trim();
//如果注解内没有value,beanName就为字段的类型
if ("".equals(beanName.trim())) {
beanName = field.getType().getName();
}
//暴力访问
field.setAccessible(true);
try {
//将这个字段注入到这个类中
field.set(entry.getValue(), ioc.get(beanName));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
private void doInstance() {
if (classNames.isEmpty()) {
return;
}
try {
//循环扫描的类名称
for (String className : classNames) {
//反射获取Class
Class> clazz = Class.forName(className);
//如果注解是controller
if (clazz.isAnnotationPresent(MyController.class)) {
//获取实例
Object instance = clazz.newInstance();
//获取类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
//将类名作为key,类实例做为value存入map(ioc容器)
ioc.put(beanName, instance);
//注解是service
} else if (clazz.isAnnotationPresent(MyService.class)) {
//获取注解内的名称
String beanName = clazz.getAnnotation(MyService.class).value();
//注解内没有名称,类名为clazz的首字母小写类名
if ("".equals(beanName.trim())) {
beanName = toLowerFirstCase(clazz.getSimpleName());
}
//获取内的实例
Object instance = clazz.newInstance();
//将类名作为key,类实例做为value存入map(ioc容器)
ioc.put(beanName, instance);
//循环获取class的所有接口
for (Class> i : clazz.getInterfaces()) {
//如果接口有多个实现类,报异常
if (ioc.containsKey(i.getName())) {
throw new Exception("The " + i.getName() + " is exists!!");
}
//否则将接口作为key,实例作为value存入map
ioc.put(i.getName(), instance);
}
//没有注解,跳出,下一个
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
private void doScanner(String scanPackage) {
//获取classLoader下面该路径的url,由/拼接
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
//获取File文件
File classPath = new File(url.getFile());
//循环文件路径,获取下面的子文件(文件架)
for (File file : classPath.listFiles()) {
//如果是文件夹,递归
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
//如果是文件,判断是否是class文件
if (!file.getName().endsWith(".class")) {
continue;
}
//去掉后缀,将文件的全路径名存入数组
classNames.add(scanPackage + "." + file.getName().replace(".class", ""));
}
}
}
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
//存入一个property里面
contextConfig.load(is);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}