SpringMVC的核心实现
实现效果:实现在浏chrom输入地址localhost:8080/toLogin.do
进入servlet最终转发到html
页面
项目包
实现步骤
# # 一:创建@RequestMapping
注解->用于实现SpringMVC中的@RequestMapping
注解
@Retention(RUNTIME)
public @interface RequestMapping {
public String value () default "";
}
解释:
1)@Retention(RUNTIME)
表示注解在运行期间有效
2)public String value () default
中的value()
为SpringMVC中的\hello
值
例:@RequestMapping("/toLogin.do")
# # 二:创建Handler
类->方便利用java反射机制去调用一个对象的方法而设计的一个辅助类
Handler封装了:处理器实例已经对应的Method对象
public class Handler {
// obj:处理器实例
private Object obj;
// mh:方法
private Method mh;
public Object getObj() {
return obj;
}
public void setObj(Object obj) {
this.obj = obj;
}
public Method getMh() {
return mh;
}
public void setMh(Method mh) {
this.mh = mh;
}
}
解释:
1)Object:存放处理器实例
2)Method:存放处理其实例中的方法
# # 三:创建HandlerMapping
类->映射处理器->负责建立请求路径与处理器的对应关系
例:"/hello.do"
应该由HelloController
的hello
方法来处理
类设计:
成员变量:private Map<String,Handler>maps=new HashMap();
方法一·:public void process(List beans)
方法二· public Handler getHandler(String path)
1)maps用于存放请求路径和Handler的对应关系 key为请求路径 value为Handler对象
private Map<String,Handler>maps=new HashMap();
2)``HadlerMapping中核心方法
process(List beans)`:负责变量存放对应关系
// 传入DispatcherServlet中获得的List集合:List集合为处理器实例
public void process(List beans) {
// for循环获取每一个处理器实例
for(Object bean:beans) {
// 反射拿到所有方法
Method[]methods=bean.getClass().getDeclaredMethods();
// 遍历获得的方法
for(Method mh:methods) {
// 获取加在方法前的@RequestMapping注解
RequestMapping rm=mh.getDeclaredAnnotation(RequestMapping.class);
// 如果注解为空就说明没有找到合适的处理器,为空
if(rm!=null) {
// 获取注解中的value 请求路径 /*.do
String path=rm.value();
// 将method对象以及处理器实例封装到Handler对象
Handler handler=new Handler();
handler.setMh(mh);
handler.setObj(bean);
// 以请求路径作为key,以Handler对象作为value
// 将对应关系添加到map里
maps.put(path,handler);
}
}
}
System.out.println("maps:"+maps);
}
3)HadlerMapping
中get
方法getHandler(String path)
根据请求路径获取对应的处理器Handler
public Handler getHandler(String path) {
return maps.get(path);
}
# # 四:DispatcherServlet
方法也是SpringMVC
中的核心方法,负责处理分发请求(前端控制器)DispatcherServlet
为一个Servlet
成员变量:private HandlerMapping handlerMapping;
方法一:public void init() throws ServletException {}初始化方法
方法二:protected void service(HttpServletRequest request, HttpServletResponse response) {}用于分发请求
1)handlerMapping
为存储请求路径value
和处理的关系handler
private HandlerMapping handlerMapping;
2)DispatcherServlet
中核心方法也是初始化方法:用dom4j解析smartmvc.xml文件获取类全路径名,之后反射,存储Handler
已经HandlerMapping
也是一个初始化方法
public void init() throws ServletException {
try {
SAXReader saxReader=new SAXReader();
InputStream in=getClass()
.getClassLoader()
.getResourceAsStream("smartmvc.xml");
Document doc=saxReader.read(in);
// 获得根元素 <beans></beans>
Element root=doc.getRootElement();
// 获得根元素下面的所有子元素
List beans=new ArrayList();
List<Element>elements=root.elements();
for(Element ele:elements) {
// 获取处理器类名
String className=ele.attributeValue("class");
System.out.println("className:"+className);
// 将处理器实例化
Object bean=Class.forName(className);
System.out.println(bean);
beans.add(bean);
}
// 将处理器实例交给HandlerMapping来处理
handlerMapping=new HandlerMapping();
handlerMapping.process(beans);
} catch (Exception e) {
System.out.println("初始化失败:"+e);
e.printStackTrace();
}
}
2.5)smartmvc.xml
代码如下
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<!--
配置处理器,其中,class属性用于指定处理器类名
-->
<bean class="controller.HelloController"/>
<bean class="controller.LoginController"/>
</beans>
xml文件通常存储反射对象的全路径名
3)DispatcherServlet
中处理请求方法也是servlet
中的处理业务的方法
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
try {
// 现获得请求资源路径
String uri=request.getRequestURI();
System.out.println("uri:"+uri);
// 获得应用名
String contextPath=request.getContextPath();
System.out.println("contextPath:"+contextPath);
// 将请求路径中到应用名截取掉,生成请求路径
String path=uri.substring(contextPath.length());
System.out.println("path:"+path);
// 依据请求路径获取对应掉Handler对象
Handler handler=handlerMapping.getHandler(path);
if(handler==null) {
response.sendError(404);
System.out.println("没有对应到处理器");
return;
}
Object bean=handler.getObj();
Method mh=handler.getMh();
/**
* 获取处理器的方法
* 先获得处理器方法的参数类型信息
*/
Class[]types=mh.getParameterTypes();
// 获得返回值
Object rv=null;
if(types.length>0) {
// params用于存放实际参数
Object[]params=new Object[types.length];
// 依据参数类型进行相应的赋值
for(int i=0;i<types.length;i++) {
if(types[i]==HttpServletRequest.class) {
params[i]=request;
}
if(types[i]==HttpServletResponse.class) {
params[i]=response;
}
}
// 处理器方法带参数
rv=mh.invoke(bean, params);
}else {
// 处理器方法不带参数
rv=mh.invoke(bean);
}
// 获得视图名
String viewName=rv.toString();
System.out.println("viewName:"+viewName);
/*
* 处理视图名
* 如果视图名是以"redirect:"开头,则重定向,否则
* 默认转发到 "/WEB-INF/"+视图名+".jsp"
*/
if(viewName.startsWith("redirect:")) {
// 生成重定向地址
String redirectPath=contextPath+"/"+viewName.substring("redirect:".length());
// 重定向
response.sendRedirect(redirectPath);
}else {
// 生成转发地址
String forwardPath="/WEB-INF/"+viewName+".jsp";
// 转发
request.getRequestDispatcher(forwardPath).forward(request, response);
}
} catch (Exception e) {
e.printStackTrace();
throw new ServletException(e);
}
}
# # 五:编写Controller
类进行测试HelloController
public class HelloController {
@RequestMapping(value = "/hello.do")
public String hello() {
System.out.println("HelloController hello()");
// 返回视图名
return "hello";
}
}
hello.jsp
代码
<%@ page language="java" contentType="text/html; charset=UTF-8"
pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
Hello SmartMVC
</body>
</html>
测试:在Chrom地址栏中敲入localhost:8080/hello.do
就可以直接转发到hello.jsp
了完成了SpringMVC的核心实现
实现思路:根据反射的api m.invoke(obj)
m为方法 obj为类实例->通过Handler存储m和obj->HandlerMapping存储url和Handler的对应关系
最终实现(凑齐)m.invoke(obj)
的参数实现代码