一.SpringMVC前期准备
1.IOC基本执行原理(流程):ioc就是一个MAP(key:beanName,value:Object(反射的实例))
a.先配置包扫描路径(例:cn.com.bardream,demo->扫描demo包下所有.class类)
b.识别出被标记上的@MYController@MYService注解的类
c.通过反射实例化被标记的类后,加载到ioc容器中(就是一个map(beanName,Object(反射的实例)))
d.beanName;有两种情况,一种注解中设置了别名,就以别名为beanName,另一种没有设置别名,那就以类名首字母小写为beanName
2.DI基本执行原理(流程):通过ioc给被标记的@MYAutoWrite的属性赋值
a.遍历ioc容器中的Object,看类中字段属性上是否标记上@MYAutoWrite的注解
b.再根据类中的字段属性的类型,去ioc容器中,找到对应的实例,然后赋值给当前被标记的类
3.MVC基本执行原理(流程):通过handlerMapping,做URI与Method的映射
a.扫描被@RequestMapping标记的类,以及方法,获取注解中所定义的路径
b.用一个handlerMapping去存储,url和method的对应关系(map<key:uri,value:method(反射包中的Method类)>)
c.当请求进入时,通过请求uri 对比 handlerMapping中的key:uri,去获取,需要调用的方法
d.在组装参数,采用,method.invoke(Object,params),通过反射去调用,请求对应的方法
二.开写
1.照抄并简化@Controller @Service... 等
//@Target(ElementType.TYPE)//接口、类、枚举、注解
//@Target(ElementType.FIELD)//字段、枚举的常量
//@Target(ElementType.METHOD)//方法
//@Target(ElementType.PARAMETER)//方法参数
//@Target(ElementType.CONSTRUCTOR)//构造函数
//@Target(ElementType.LOCAL_VARIABLE)//局部变量
//@Target(ElementType.ANNOTATION_TYPE)//注解
//@Target(ElementType.PACKAGE)//包
@Target({ElementType.TYPE})
//@Retention(RetentionPolicy.SOURCE)//注解仅存在于源码中,在class字节码文件中不包含
//@Retention(RetentionPolicy.CLASS)//默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
//@Retention(RetentionPolicy.RUNTIME)//注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Retention(RetentionPolicy.RUNTIME)
//注解包含在javadoc中:
@Documented
public @interface MYController {
String value() default "";
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYService {
String value() default "";
}
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MYAutowired {
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 "";
}
2.建造controller层,service层
package cn.com.bardream.demo.controller;
import cn.com.bardream.demo.service.IDemoService;
import cn.com.bardream.myframework.annotation.MYAutowired;
import cn.com.bardream.myframework.annotation.MYController;
import cn.com.bardream.myframework.annotation.MYRequestMapping;
import cn.com.bardream.myframework.annotation.MYRequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@MYController
@MYRequestMapping("/demo")
public class DemoController {
@MYAutowired
private IDemoService demoService;
@MYRequestMapping("/query")
public void query(HttpServletRequest req, HttpServletResponse resp,
@MYRequestParam("name") String name) {
String result = "My name is " + name;
try {
resp.getWriter().write(result);
} catch (IOException e) {
e.printStackTrace();
}
}
@MYRequestMapping("/add")
public void add(HttpServletRequest req, HttpServletResponse resp,
@MYRequestParam("a") Integer a, @MYRequestParam("b") Integer b) {
try {
resp.getWriter().write(a + "+" + b + "=" + (a + b));
} catch (IOException e) {
e.printStackTrace();
}
}
@MYRequestMapping("/sub")
public void add(HttpServletRequest req, HttpServletResponse resp,
@MYRequestParam("a") Double a, @MYRequestParam("b") Double b) {
try {
resp.getWriter().write(a + "-" + b + "=" + (a - b));
} catch (IOException e) {
e.printStackTrace();
}
}
@MYRequestMapping("/remove")
public String remove(@MYRequestParam("id") Integer id) {
return "" + id;
}
}
package cn.com.bardream.demo.service;
public interface IDemoService {
String get(String name);
}
package cn.com.bardream.demo.service.impl;
import cn.com.bardream.demo.service.IDemoService;
import cn.com.bardream.myframework.annotation.MYService;
@MYService
public class DemoService implements IDemoService {
public String get(String name) {
return "My name is " + name;
}
}
3.准备配置文件:application.properties
scanPackage=cn.com.bardream.demo
4.配置web.xml文件
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>mymvc</servlet-name>
<servlet-class>cn.com.bardream.myframework.servlet.v2.MYDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mymvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
5.新建一MYDispatcherServlet继承HttpServlet,实现init() doGet() doPost() 方法
package cn.com.bardream.myframework.servlet.v2;
import cn.com.bardream.myframework.annotation.MYAutowired;
import cn.com.bardream.myframework.annotation.MYController;
import cn.com.bardream.myframework.annotation.MYRequestMapping;
import cn.com.bardream.myframework.annotation.MYRequestParam;
import cn.com.bardream.myframework.annotation.MYService;
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;
public class MYDispatcherServlet extends HttpServlet {
// 存储配置文件信息
Properties configContext = new Properties();
// 扫描到的类名存储容器
List<String> classNameContext = new ArrayList<String>();
// IOC容器 - key:beanName value:Object
Map<String, Object> ioc = new HashMap<String, Object>();
// 采用map存储 HandlerMapping key:uri value:Method
Map<String, Method> handlerMapping = new HashMap<String, Method>();
// 先进行初始化
@Override
public void init(ServletConfig config) throws ServletException {
//1、加载配置文件 -> 到configContext
doLoadConfig(config.getInitParameter("contextConfigLocation"));
//2、扫描相关的类->从configContext中获取需要扫描的包路径->classNameContext容器中
doScanner(configContext.getProperty("scanPackage"));
//3、初始化所有相关的类的实例,并且放入到IOC容器之中
doInitIOC();
//4、完成依赖注入
doAutoWrite();
//5、初始化HandlerMapping
doInitHandlerMapping();
System.out.println("MYframework is init.");
}
// 初始化HandlerMapping
private void doInitHandlerMapping() {
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
if (!entry.getValue().getClass().isAnnotationPresent(MYController.class)) {
continue;
}
MYRequestMapping annotation = entry.getValue().getClass().getAnnotation(MYRequestMapping.class);
String baseUri = annotation.value();
for (Method method : entry.getValue().getClass().getMethods()) {
if (!method.isAnnotationPresent(MYRequestMapping.class)) {
continue;
}
MYRequestMapping anMapping = method.getAnnotation(MYRequestMapping.class);
String methodUri = anMapping.value();
String url = (baseUri + "/" + methodUri).replaceAll("/+", "/");
handlerMapping.put(url, method);
}
}
}
// 将定义@AutoWrite的字段,赋值
private void doAutoWrite() {
try {
for (Map.Entry<String, Object> entry : ioc.entrySet()) {
Field[] fields = entry.getValue().getClass().getDeclaredFields();
for (Field field : fields) {
if (!field.isAnnotationPresent(MYAutowired.class)) {
continue;
}
String beanName = field.getAnnotation(MYAutowired.class).value();
if ("".equals(beanName)) {
Class<?> type = field.getType();
beanName = type.getName();
}
if (!ioc.containsKey(beanName)) {
throw new Exception("the autowrite " + beanName + " is not found!");
}
Object object = ioc.get(beanName);
// 设置私有属性赋值,权限
field.setAccessible(true);
// 这就是自动注入最后的结果
field.set(entry.getValue(), object);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 从类名容器中获取全类名
// 将进行@MYController @MYService 标记的标记类,通过反射实例化后,存入到ioc中
private void doInitIOC() {
try {
for (String className : classNameContext) {
Class<?> aClass = Class.forName(className);
// 判断类上加了@MYController 注解的
if (aClass.isAnnotationPresent(MYController.class)) {
String beanName = toLowFirstName(aClass.getSimpleName());
Object obj = aClass.newInstance();
ioc.put(beanName, obj);
// 判断类上加了@MYService 注解的
} else if (aClass.isAnnotationPresent(MYService.class)) {
String beanName = toLowFirstName(aClass.getSimpleName());
MYService annotation = aClass.getAnnotation(MYService.class);
if (annotation.value() != "") {
beanName = annotation.value();
}
Object obj = aClass.newInstance();
ioc.put(beanName, obj);
// 将Service所实现的接口的全类名,也加载进IOC容器中
// 这样后面我们使用@MYAotuwrite是,就可以根据接口类型,去获取其实现类的实例
for (Class clazz : aClass.getInterfaces()) {
if (ioc.containsKey(clazz.getName())) {
throw new Exception("the class " + clazz.getName() + " is exist!");
}
ioc.put(clazz.getName(), obj);
}
} else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
// 将类名首字母转成小写
private String toLowFirstName(String simpleName) {
char[] chars = simpleName.toCharArray();
if ('A' <= chars[0] && chars[0] <= 'Z') {
chars[0] += 32;
}
return String.valueOf(chars);
}
// 获取配置文件中的需要扫描的包路径
// 将包路径下所有的类名扫描到类名容器中
private void doScanner(String scanPackage) {
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File filePath = new File(url.getFile());
File[] files = filePath.listFiles();
for (File file : files) {
if (file.isDirectory()) {
doScanner(scanPackage + "." + file.getName());
} else {
if (!file.getName().endsWith(".class")) {
continue;
}
// 全类名
String className = scanPackage + "." + file.getName().replace(".class", "");
classNameContext.add(className);
}
}
}
// 去加载配置文件
private void doLoadConfig(String contextConfigLocation) {
InputStream inputStream = null;
try {
inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
configContext.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (inputStream != null) {
inputStream.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatcher(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatcher(req, resp);
}
// 通过Dispatcher进行请求转发以及匹配
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) {
try {
String requestURI = req.getRequestURI();
if (!handlerMapping.containsKey(requestURI)) {
resp.getWriter().write("404 not found!");
return;
}
Method method = handlerMapping.get(requestURI);
// 请求参数列表 key:paramName value:paramValue
Map<String, String[]> parameterMap = req.getParameterMap();
// 形参列表
Class<?>[] parameterTypes = method.getParameterTypes();
// 新建需要传入方法中的参数
Object[] objs = new Object[parameterTypes.length];
// 获取方法上的注解--[]参数位置,[]参数上注解数组
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
if (parameterType == HttpServletRequest.class) {
objs[i] = req;
} else if (parameterType == HttpServletResponse.class) {
objs[i] = resp;
} else {
// 获取参数上的注解数组
Annotation[] parameterAnnotation = parameterAnnotations[i];
for (Annotation annotation : parameterAnnotation) {
if (!(annotation instanceof MYRequestParam)) {
continue;
}
MYRequestParam myRequestParam = (MYRequestParam) annotation;
// myRequestParam.value()不想做非空判断了,默认有值了
String[] strings = parameterMap.get(myRequestParam.value());
objs[i] = conver(parameterType,strings);
}
}
}
Object o = ioc.get(toLowFirstName(method.getDeclaringClass().getSimpleName()));
Object result = method.invoke(o, objs);
if (result==null||result==Void.class){return;}
resp.getWriter().write(result.toString());
return;
} catch (Exception e) {
e.printStackTrace();
}
}
private Object conver(Class<?> parameterType, String[] strings) {
if (parameterType==String.class){
return Arrays.toString(strings).replaceAll("\\[|\\]","").trim().replaceAll("\\s[2]",",");
}
if (parameterType==Integer.class){
return Integer.valueOf(strings[0]);
}
if (parameterType==Double.class){
return Double.valueOf(strings[0]);
}
return null;
}
}
6.测试结果
三.总结-三大阶段-十四步
配置阶段 | 配置web.xml | DispatchServlet |
设定init-param | contextConfigLocation=classpath:application.xml | |
设定url-pattern | /* | |
配置annotation | @Controller@Service@Autowrited@RequesetParam@RequestMapping | |
初始化阶段 | 调用init()方法 | 加载配置文件 |
IOC容器初始化 | Map<String,Object> | |
扫描相关类 | scan-package = "cn.com.bardream.demo" | |
创建实例化并保存至容器 | 通过反射机制将类实例化放入IOC容器中 | |
进行DI操作 | 扫描IOC容器中的实例,给加了自动装载的属性赋值 | |
初始化HandlerMapping | 将一个URL和一个Method进行一对一的关联映射Map<String,Method> | |
运行阶段 | 调用doGet()/doPost() | Web容器调用doGet()/doPost()方法.获得request/respond对象 |
匹配HandlerMapping | 从request对象中获取用户输入的url,找到对应的Method | |
反射调用Method.invoke() | 利用反射调用方法并返回结果 | |
respond.gerWrite().write() | 将返回结果输出到浏览器 |