SpringMVC平时用得多,但其底层原理并没具体了解。整个HTTP请求过程,有几个重要的类或方法。
DispatcherServlet接收请求
Controller注解
RequestMapping注解
ViewResolver视图解释器
现在用代码手动实现这些类或注解,更好的了解整个过程。
1、pom文件依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!-- dom4j -->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
2、创建注解 MyController,用来模拟注解 Controller
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyController {
//该注解模拟Controller注解
String value() default "";
//@interface关键字用来定义注解
//注解「注解」的注解被称作 元注解
//注解和接口类似,内部可以定义常量和方法,
//但是方法有限制,不能有参数,返回值只能是基本类型、字符串、Class、枚举、数组,可以包含默认值
//元注解有四种:
//1、@Target
// -ElementType.PACKAGE 作用与包,-ElementType.TYPE 作用于 类、接口、注解、枚举,
// -ElementType.METHOD 作用于方法 ,-ElementType.FIELD 作用于属性
}
3、创建 MyRequestMapping 注解,模拟 RequestMapping
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE,ElementType.METHOD})
public @interface MyRequestMapping {
//该注解模拟RequestMapping,作用于类和方法
String value() default "";
}
4、创建 MyDispatcherServlet ,模拟 DispatcherServlet
public class MyDispatcherServlet extends HttpServlet {
//模拟ioc容器,用来存储 Controller 对象
private Map<String, Object> iocContainer = new HashMap<>();
private Map<String, Method> handlerMapping = new HashMap<>();
//自定视图解析器
private MyViewResolver myViewResolver;
@Override
public void init(ServletConfig servletConfig) throws ServletException {
super.init();
//扫描Controller,识别 MyController 注解的类,创建实例对象,并保存到 map 中
scanController(servletConfig);
initHandlerMapping();
}
}
5、扫描 Controller,识别 MyController 注解的类,创建实例对象,并保存到 map 中
/**
* 扫描 controller
* @param servletConfig
*/
public void scanController(ServletConfig servletConfig){
SAXReader saxReader = new SAXReader();
try {
Document document = saxReader.read("./src/main/resources/spring-mvc.xml");
Element root = document.getRootElement();
Iterator iterator = root.elementIterator();
while (iterator.hasNext()){
Element ele = (Element) iterator.next();
if(ele.getName().equals("component-scan")){
String packageName = ele.attributeValue("base-package");
//获取包下面所有类名,这个 类名 字符串是拼接了前面包名的
List<String> list = getClassNames(packageName);
for(String str:list){
Class clazz = Class.forName(str);
//判断类上是否有MyController注解
if(clazz.isAnnotationPresent(MyController.class)){
//获取Controller类上 MyRequestMapping 注解,如果没有 MyRequestMapping 注解呢?
MyRequestMapping requestMapping = (MyRequestMapping) clazz.getAnnotation(MyRequestMapping.class);
//获取MyRequestMapping注解的值
String value = requestMapping.value().substring(1);
iocContainer.put(value,clazz.newInstance());
}
}
}
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
}
}
6、将 Controller类 中 Method 与其 MyRequestMapping 注解关联,然后保存到 handlerMapping 中
/**
* 将 Controller类 中 Method 与其 MyRequestMapping 注解关联,然后保存到 handlerMapping 中
*/
private void initHandlerMapping() {
//首先遍历保存的Controller,也就是加了MyController注解的类
for(String key : iocContainer.keySet()){
//获取到当前类
Class clazz = iocContainer.get(key).getClass();
//获取类中所有方法
Method[] methods = clazz.getMethods();
for(Method method : methods){
//判断方法上是否有 MyRequestMapping 注解
if(method.isAnnotationPresent(MyRequestMapping.class)){
//如果有,获取该注解
MyRequestMapping myRequestMapping = method.getAnnotation(MyRequestMapping.class);
//获取该注解的值
String value = myRequestMapping.value().substring(1);
//保存该方法
handlerMapping.put(value, method);
}
}
}
}
7、创建自定义的视图解释器
public class MyViewResolver {
private String prefix;
private String suffix;
public String getPrefix() {
return prefix;
}
public String getSuffix() {
return suffix;
}
public void setPrefix(String prefix) {
this.prefix = prefix;
}
public void setSuffix(String suffix) {
this.suffix = suffix;
}
}
8、spring-mvc.xml文件内容
<context:component-scan base-package="com.guozh.mvc"/>
<!-- 配置视图解析器 -->
<bean class="com.guozh.mvc.MyViewResolver">
<property name="prefix" value="/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
9、实例化自定义的视图解释器,主要用来接收 method 的返回值,然后返回物理视图,完成页面跳转
/**
* 实例化自定义的视图解释器,主要用来接收 method 的返回值,然后返回物理视图,完成页面跳转
*/
public void initViewResolver(){
//实例化某个对象,如同通过IoC获取对象,底层代码实现是一样的
SAXReader saxReader = new SAXReader();
try {
//读取 spring-mvc 文件
Document document = saxReader.read("./src/main/resources/spring-mvc.xml");
Element root = document.getRootElement();
Iterator iterator = root.elementIterator();
if(iterator.hasNext()){
Element element = (Element) iterator.next();
if(element.getName().equals("bean")){
String className = element.attributeValue("class");
Class clazz = Class.forName(className);
Object object = clazz.newInstance();
Method prefixMethod = clazz.getMethod("setPrefix", String.class);
Method suffixMethod = clazz.getMethod("setSuffix",String.class);
//xml bean 中的属性
Iterator beanIter = element.elementIterator();
//获取 property 值
Map<String,String> propertyMap = new HashMap<String,String>();
while (beanIter.hasNext()){
Element beanEle = (Element) beanIter.next();
String name = beanEle.attributeValue("name");
String value = beanEle.attributeValue("value");
propertyMap.put(name, value);
}
for(String str : propertyMap.keySet()){
if(str.equals("prefix")){
prefixMethod.invoke(object, propertyMap.get(str));
}
if(str.equals("suffix")){
suffixMethod.invoke(object, propertyMap.get(str));
}
}
myViewResolver = (MyViewResolver) object;
}
}
} catch (DocumentException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
以上代码笔记内容来自付费专栏:案例上手 Spring 全家桶
PS:并没有透露关键内容,纯粹是零碎笔记。如果侵犯版权,请联系我。
本文由老郭种树原创,转载请注明:https://guozh.net/springmvc-demo/