一,注解类
GPAutowired
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: baifan
* @date: 2021/2/26
*/
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {
String value() default "";
}
GPController
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: baifan
* @date: 2021/2/26
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPController {
String value() default "";
}
GPRequestMapping
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: baifan
* @date: 2021/2/26
*/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestMapping {
String value() default "";
}
GPRequestParam
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: baifan
* @date: 2021/2/26
*/
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPRequestParam {
String value() default "";
}
GPService
package com.gupaoedu.mvcframework.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @author: baifan
* @date: 2021/2/26
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPService {
String value() default "";
}
最最最重要的 GPDispatcherServlet
package com.gupaoedu.mvcframework.v1.servlet;
import com.gupaoedu.mvcframework.annotation.GPAutowired;
import com.gupaoedu.mvcframework.annotation.GPController;
import com.gupaoedu.mvcframework.annotation.GPRequestMapping;
import com.gupaoedu.mvcframework.annotation.GPRequestParam;
import com.gupaoedu.mvcframework.annotation.GPService;
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.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author: baifan
* @date: 2021/2/26
*/
public class GPDispatcherServlet extends HttpServlet {
private static final long serialVersionUID = -4242345888463705196L;
//private Map mapping = 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 {
try {
doDispatch(req, resp);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500 Exection,Detail :" + Arrays.toString(e.getStackTrace()));
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
Handler handler = getHandler(req);
if (handler == null) {
//if (!this.handlerMapping.containsKey(url)) {
resp.getWriter().write("404 Not Found!!");
return;
}
//获取方法
Method method = handler.getMethod();
//Method method = (Method) this.handlerMapping.get(url);
Map params = req.getParameterMap();
//获取方法的形参列表
Class>[] parameterTypes = method.getParameterTypes();
//保存请求的url参数列表
Map parameterMap = req.getParameterMap();
//保存赋值参数的位置
Object[] paramValues = new Object[parameterTypes.length];
//根据参数位置动态赋值
for (Map.Entry param : parameterMap.entrySet()) {
String value = Arrays.toString(param.getValue())
.replaceAll("\\[|\\]", "")
.replaceAll("\\s", "");
if (handler.paramIndexMapping.containsKey(param.getKey())) {
continue;
}
int index = handler.paramIndexMapping.get(param.getKey());
paramValues[index] = convert(parameterTypes[index], value);
}
for (int i = 0; i < parameterTypes.length; i++) {
Class> parameterType = parameterTypes[i];
if (parameterType == HttpServletRequest.class) {
paramValues[i] = req;
continue;
} else if (parameterType == HttpServletResponse.class) {
paramValues[i] = resp;
continue;
} else if (parameterType == String.class) {
//提取方法中加了注解的参数
Annotation[][] pa = method.getParameterAnnotations();
for (int j = 0; j < pa.length; j++) {
for (Annotation annotation : pa[i]) {
if (annotation instanceof GPRequestParam) {
String paramName = ((GPRequestParam) annotation).value();
if (!paramName.trim().equals("")) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "")
.replaceAll("\\s", "");
paramValues[i] = value;
}
}
}
}
}
}
//委派模式体现
//第一个参数:方法所在的实例
//第二个参数:调用时所需要的实参
String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
method.invoke(ioc.get(beanName), new Object[]{req, resp, params.get("name")[0]});
}
//url传过来的参数都是String类型的, 由于HTTP基于字符串协议
//只需要把String转换为任意类型
private Object convert(Class> parameterType, String value) {
if (Integer.class == parameterType) {
return Integer.valueOf(value);
}
//如果还有 double或者其他类型的参数,继续加if
//这时候,我们应该想到策略模式了
//在这里暂时不实现,希望“小伙伴们”自己实现
return null;
}
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;
}
//保存application.properties配置文件中的内容
private Properties contextConfig = new Properties();
//保存扫描的所有的类名
private List classNames = new ArrayList();
//IoC容器,为了简化程序暂时不考虑 ConcurrentHashMap
private Map ioc = new HashMap();
//保存url和Method的对应关系
private List handlerMapping = new ArrayList();
@Override
public void init(ServletConfig config) throws ServletException {
/**
* 2.0版本, 对1.0进行了封装
*/
//1,加载配置文件
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2,扫描相关的类
doScanner(contextConfig.getProperty("scanPackage"));
//3,初始化扫描到的类, 并且将它们放入到IoC容器中
doInstance();
//4,完成依赖注入
doAutowired();
//5,初始化 HandlerMapping
initHandlerMapping();
System.out.println("GP Spring framework is init.");
/**
* 1.0 版本
*/
/*InputStream is = null;
try {
Properties configContext = new Properties();
is = this.getClass().getClassLoader().getResourceAsStream(config.getInitParameter("contextConfigLocation"));
configContext.load(is);
String scanPackage = configContext.getProperty("scanPackage");
doScanner(scanPackage);
for (String className : mapping.keySet()) {
Class> clazz = Class.forName(className);
//A.isAnnotationPresent(B.class) 注释B是否在此A上
if (clazz.isAnnotationPresent(GPController.class)) {
mapping.put(className, clazz.newInstance());
String baseUrl = "";
if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value();
}
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(GPRequestMapping.class)) {
continue;
}
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
String url = (baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
mapping.put(url, method);
System.out.println("Mapped " + url + "," + method);
}
} else if (clazz.isAnnotationPresent(GPService.class)) {
GPService service = clazz.getAnnotation(GPService.class);
String beanName = service.value();
if ("".equals(beanName)) {
beanName = clazz.getName();
}
Object instance = clazz.newInstance();
mapping.put(beanName, instance);
for (Class> i : clazz.getInterfaces()) {
mapping.put(i.getName(), instance);
}
} else {
continue;
}
}
for (Object object : mapping.values()) {
if (object == null) {
continue;
}
Class> clazz = object.getClass();
if (clazz.isAnnotationPresent(GPController.class)) {
Field[] fields = clazz.getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(GPAutowired.class)) {
continue;
}
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
String beanName = autowired.value();
if ("".equals(beanName)) {
beanName = field.getType().getName();
}
field.setAccessible(true);
field.set(mapping.get(clazz.getName()), mapping.get(beanName));
}
}
}
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
System.out.println("GP MVC Framework is init");*/
}
/**
* 初始化 HandlderMapping 这就是策略模式的应用案例
*/
private void initHandlerMapping() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
Class> clazz = entry.getValue().getClass();
if (!clazz.isAnnotationPresent(GPController.class)) {
continue;
}
//保存写在类上面的@GPRequestMapping("/demo")
String baseUrl = "";
if (clazz.isAnnotationPresent(GPRequestMapping.class)) {
GPRequestMapping requestMapping = clazz.getAnnotation(GPRequestMapping.class);
baseUrl = requestMapping.value();
}
//默认获取所有的public类型的方法
Method[] methods = clazz.getMethods();
for (Method method : methods) {
if (!method.isAnnotationPresent(GPRequestMapping.class)) {
continue;
}
GPRequestMapping requestMapping = method.getAnnotation(GPRequestMapping.class);
String regex = ("/" + baseUrl + "/" + requestMapping.value()).replaceAll("/+", "/");
Pattern pattern = Pattern.compile(regex);
handlerMapping.add(new Handler(entry.getValue(), method, pattern));
System.out.println("Mapped " + regex + "," + method);
}
}
}
private void doAutowired() {
if (ioc.isEmpty()) {
return;
}
for (Map.Entry entry : ioc.entrySet()) {
//获取所有的字段,包括 private、 protected、 default类型的
//正常来说,普通的OOP编程只能获得 public类型的字段
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(GPAutowired.class)) {
continue;
}
GPAutowired autowired = field.getAnnotation(GPAutowired.class);
//如果用户没有自定义 beanName,默认就根据类型注入
//这个地方省去了对类名首字母小写的情况的判断,这个作为课后作业请“小伙伴们”自己去实现
String beanName = autowired.value().trim();
if ("".equals(beanName)) {
//获得接口的类型,作为key,稍后用这个key到IoC容器中取值
beanName = field.getType().getName();
}
//如果是 public以外的类型,只要加了@Autowired注解都要强制赋值
//反射中反射中叫作暴力访问
field.setAccessible(true);
try {
field.set(entry.getValue(), ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
private void doInstance() {
//初始化, 为DI做准备
if (classNames.isEmpty()) {
return;
}
for (String className : classNames) {
try {
Class> clazz = Class.forName(className);
//什么样的类才需要初始化呢?
//加了注解的类才初始化,怎么判断?
//为了简化代码逻辑,主要体会设计思想,只用@Contro1ler和@Service举例,
//@Componment等就不一一举例了
if (clazz.isAnnotationPresent(GPController.class)) {
Object instance = clazz.newInstance();
//Spring 默认类名首字母小写
String beanName = toLowerFirstCase(clazz.getSimpleName());
ioc.put(beanName, instance);
} else if (clazz.isAnnotationPresent(GPService.class)) {
//1, 自定义的beanName
GPService service = clazz.getAnnotation(GPService.class);
String beanName = service.value();
//2, 默认类名首字母小写
if ("".equals(beanName.trim())) {
beanName = toLowerFirstCase(clazz.getSimpleName());
}
Object instance = clazz.newInstance();
ioc.put(beanName, instance);
//3, 根据类型自动赋值, 投机取巧, 实际不是的
for (Class> i : clazz.getInterfaces()) {
if (ioc.containsKey(i.getName())) {
throw new Exception("THe " + i.getName() + " is exists!!");
}
ioc.put(i.getName(), instance);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
private String toLowerFirstCase(String simpleName) {
char[] chars = simpleName.toCharArray();
//之所以要做加法,是因为大、小写字母的 ASCII码相差32
//而且大写字母的ASCII码要小于小写字母的 ASCII码
//在Java中,对char做算术运算实际上就是对ASCI码做算术运算
chars[0] += 32;
return String.valueOf(chars);
}
/**
* 扫描相关的类
*
* @param scanPackage
*/
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classPath = new File(url.getFile());
for (File file : classPath.listFiles()) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
String className = (scanPackage + "." + file.getName().replace(".class", ""));
classNames.add(className);
}
}
}
/**
* 加载配置文件
* //直接通过类路径找到 Spring主配置文件所在的路径
* //并且将其读取出来放到 Properties对象中
* //相当于将 scanPackage=com.gupaoedu.demo保存到了内存中
*
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
/*private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classDir = new File(url.getFile());
for (File file : classDir.listFiles()) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
String clazzName = (scanPackage + "." + file.getName().replace(".class", ""));
mapping.put(clazzName, null);
}
}
}*/
/**
* 记录Controller中的RequeMapping和Method的对应关系
*/
private class Handler {
protected Object controller; //保存方法对应的实例
protected Method method; //保存映射的方法
protected Pattern pattern;
protected Map paramIndexMapping; //参数顺序
/**
* 构造一个Handler的基本参数
*
* @param controller
* @param method
* @param pattern
* @param paramIndexMapping
*/
public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.paramIndexMapping = new HashMap();
putParamIndexMapping(method);
}
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 Map getParamIndexMapping() {
return paramIndexMapping;
}
public void setParamIndexMapping(Map paramIndexMapping) {
this.paramIndexMapping = paramIndexMapping;
}
private void putParamIndexMapping(Method method) {
//提取方法中加了注解的参数
Annotation[][] pa = method.getParameterAnnotations();
for (int i = 0; i < pa.length; i++) {
for (Annotation annotation : pa[i]) {
if (annotation instanceof GPRequestParam) {
String paramName = ((GPRequestParam) annotation).value();
if (!paramName.trim().equals("")) {
//todo 值得学习,将参数或者其他的任意的数据标索引
paramIndexMapping.put(paramName, i);
}
}
}
}
//提取方法中的request和response参数
Class>[] parameterTypes = method.getParameterTypes();
for (int i = 0; i < parameterTypes.length; i++) {
Class> parameterType = parameterTypes[i];
if (parameterType == HttpServletRequest.class ||
parameterType == HttpServletResponse.class) {
paramIndexMapping.put(parameterType.getName(), i);
}
}
}
}
}