大体思路
1.配置阶段
- 配置 web.xml
- 配置 application.properties 配置文件,主要配置需要扫描的包路径
- 配置 Init-pattern
- 配置 @Autowired,@Controller 等注解
2、初始化阶段
- 调用 init() 方法,加载配置文件
- IOC 容器进行初始化 :Map<String,Object>
- 根据配置对包中类进行扫描
- 创建类实例
- 实现 DI 依赖注入
- HandleMapping 容器初始化 : Map<String,Method>
- 路径和方法进行映射
3、执行阶段
- doGet()/doPost()
- 在 HandleMapping 中匹配路径
- 利用反射调用 invoke() 执行
- response 返回结果
简单代码
配置阶段
- web.xml
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>myServlet</servlet-name>
<servlet-class>servlet.MyServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>myServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
配置 servlet-class 时,所写路径的类,要继承 HttpServlet
- application.properties
scanPackage = com.stg.myspring
- annotioned
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface GPAutowired {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyController {
String value() default "";
}
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestMapping {
String value() default "";
}
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyRequestParam {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MyService {
String value() default "";
}
初始化阶段
// 存储 Config 配置
Properties config = new Properties();
// 包下面所有的类型
List<String> classNameList = new ArrayList<String>();
// IOC 容器
Map<String, Object> iocMap = new HashMap<String, Object>();
// handleMapping 容器
Map<String, Method> handleMap = new HashMap<String, Method>();
- 加载 web.xml
/**
* 加载配置文件
*
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
InputStream webInput = null;
try {
// 通过 classLoader 进行加载
webInput = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
config.load(webInput);
} catch (Exception e) {
e.printStackTrace();
}
}
- 扫描包路径
private void doScanPackage(String scanPackage) {
// 磁盘存储是 "/" 所以将 "." 进行替换
URL url = this.getClass().getClassLoader().getResource(scanPackage
.replaceAll("\\.", "/"));
File classFile = new File(url.getFile());
for (File file : classFile.listFiles()) {
if (file.isDirectory()) {
// 如果 file 是文件夹,进行递归
this.doScanPackage(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
String className = (scanPackage + "." + file.getName()).replaceAll(".class", "");
// 将包下类名进行存储
classNameList.add(className);
}
}
}
- 初始化 IOC 容器
private void doInitIOCMap() {
for (String className : classNameList) {
try {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(MyController.class)) {
Object object = clazz.newInstance();
String beanName = this.toLowerName(clazz.getSimpleName());
iocMap.put(beanName, object);
} else if (clazz.isAnnotationPresent(MyService.class)) {
String beanName = "";
beanName = this.toLowerName(clazz.getSimpleName());
String annoName = clazz.getAnnotation(MyService.class).value();
if (!"".equals(annoName)) {
beanName = annoName;
}
Object object = clazz.newInstance();
iocMap.put(beanName, object);
for (Class<?> i : clazz.getInterfaces()) {
if (iocMap.containsKey(i.getName())) {
throw new Exception("The beanName is exists!!");
}
iocMap.put(i.getName(), object);
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
}
}
- 注入
private void autoDI() {
if (iocMap.isEmpty()) {
return;
}
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
// 获取到所有的属性
Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
for (Field field : declaredFields) {
if (!field.isAnnotationPresent(MyAutowired.class)) {
continue;
} else {
String beanName = "";
beanName = field.getAnnotation(MyAutowired.class).value().trim();
if ("".equals(beanName)) {
// 按类型注入
beanName = field.getType().getName();
}
field.setAccessible(true);
try {
field.set(entry.getValue(), iocMap.get(beanName));
} catch (Exception e) {
e.printStackTrace();
continue;
}
}
}
}
}
- 初始化 HandleMapping
private void doUrlAndMethod() {
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (clazz.isAnnotationPresent(MyRequestMapping.class)) {
String path = clazz.getAnnotation(MyRequestMapping.class).value().trim();
Method[] methods = clazz.getMethods();
for (Method method : methods) {
String methodPath = "";
if (method.isAnnotationPresent(MyRequestMapping.class)) {
methodPath = method.getAnnotation(MyRequestMapping.class).value().trim();
}
if (!"".equals(methodPath)) {
String handleMethod = "/" + path + "/" + methodPath;
handleMap.put(handleMethod.replaceAll("/+", "/"), method);
}
}
}
}
}
执行阶段
/**
* 进行路径匹配,执行方法
*
* @param req
* @param resp
*/
private void invokeMethod(HttpServletRequest req, HttpServletResponse resp) throws IOException, InvocationTargetException, IllegalAccessException {
// 绝对路径
String url = req.getRequestURI();
// 相对路径
String contextPath = req.getContextPath();
url = url.replaceAll(contextPath, "").replaceAll("/+", "/");
if (!handleMap.containsKey(url)) {
resp.getWriter().println("404 Page not found");
} else {
Method method = handleMap.get(url);
//获取方法的形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
//保存请求的url参数列表
Map<String, String[]> parameterMap = req.getParameterMap();
//保存赋值参数的位置
Object[] paramValues = new Object[parameterTypes.length];
//按根据参数位置动态赋值
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 a : pa[i]) {
if (a instanceof GPRequestParam) {
String paramName = ((GPRequestParam) a).value();
if (!"".equals(paramName.trim())) {
String value = Arrays.toString(parameterMap.get(paramName))
.replaceAll("\\[|\\]", "")
.replaceAll("\\s", ",");
paramValues[i] = value;
}
}
}
}
}
}
//投机取巧的方式
//通过反射拿到method所在class,拿到class之后还是拿到class的名称
//再调用toLowerFirstCase获得beanName
String beanName = this.toLowerName(method.getDeclaringClass().getSimpleName());
method.invoke(iocMap.get(beanName), new Object[]{req, resp, parameterMap.get("name")[0]});
}
}