一、运行流程
1.用户发送请求至前端控制器DispatcherServlet
2.DispatcherServlet?收到请求调用HandlerMapping处理器映射器
3.处理器映射器根据请求url找到具体的处理器,生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet
4.DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
5.执行处理器(Controller,也叫后端控制器)
6.Controller?执行完成返回ModelAndView
7.HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet
8.DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9.ViewReslover解析后返回具体的View
10.DispatcherServlet对View进行渲染视图(即将模型数据填充至视图中)
11.DispatcherServlet响应用户
流程图:
二、Servlet
Servlet?生命周期:加载->实例化->服务->销毁。
几个重要的方法:
init():在整个servlet生命周期中,仅执行一次init()方法。是服务器装入servlet时执行的,负责初始化servlet对象。无论有多少客户机访问Servlet,都不会重复执行init()。
service():?是servlet核心,负责响应客户的请求。每当一个客户请求一个HttpServlet对象,该对象的service方法就要调用,而且传递给这个方法一个“请求”(ServletRequest)对象和一个“响应”(ServletResponse)对象作为参数。在HttpServlet中已存在Service()方法。默认的服务功能是调用与HTTP请求的方法相应的do功能。
destroy():仅执行一次,在服务器端停止且卸载servlet时执行该方法。在servlet对象退出生命周期时,负责释放占用的资源。一个Servlet在运行service()方法时可能会产生其他的线程,因此需要确认在调用destroy()方法时,这些线程已经终止或完成。
三、手写SpringMVC原理分析
1.创建一个前端控制器?ExtDispatcherServlet,拦截所有请求
2.初始化操作,重写servlet init()方法
? 2.1?扫包,将所有带有注解的类,注入到springmvc容器中,存放在Map集合中(springMvcBeans),?key为默认类名小写,value为对象。
? 2.2?将url映射和方法进行关联。
? 2.3?判断类上是否有注解,使用java反射机制循环遍历,将类初始化后存储在集合中(urlBeans)。再判断方法上是否存在注解,进行url和方法对应存储集合中(urlMethods)。
?3.处理请求,重写GET或者POST方法
? 3.1?获取请求URL,从urlBeans集合中获取对象实例,然后再调用urlMethods集合获取方法名称,使用反射机制执行
四、代码实例
//一 先定义相关注解和实体类
//Controller 注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtController {
}
//RequestMapping 注解
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ExtRequestMapping {
String value() default "";
}
//给方法 定义一个实体类
public class ExtMethod {
//方法名
private String methodName;
//用于存储 参数名与参数类型,用于从Url中获取对应类型的参数,运用到反射机制调用有参方法中
private Map<String,Class> pamas;
public String getMethodName() {
return methodName;
}
public void setMethodName(String methodName) {
this.methodName = methodName;
}
public Map<String,Class> getPamas() {
return pamas;
}
public void setPamas(Map<String,Class> pamas) {
this.pamas = pamas;
}
}
//再定义一个测试Controller
@ExtController
@ExtRequestMapping("/ext")
public class TestController {
@ExtRequestMapping("/test")
public String test(String name,int age){
System.out.println("------------name:"+name+" age:"+age);
return "index";
}
}
//二 定义中央控制器 类 DispatcherServlet
public class ExtDispatcherServlet extends HttpServlet {
//存放mvc 的bean key 默认为 类的名称(首字母小写), value 为初始化的类
private ConcurrentHashMap<String,Object> springMvcBeans=new ConcurrentHashMap<String, Object>();
//存放url与实体类的对应关系,key为url ,value 为实体类
private ConcurrentHashMap<String,Object> urlBeans=new ConcurrentHashMap<String, Object>();
//存放url 与方法的对应关系,key为Url,value为 自定义的方法类
private ConcurrentHashMap<String,ExtMethod> urlMethods=new ConcurrentHashMap<String, ExtMethod>();
@Override
public void init() throws ServletException {
try {
//获取所有的类
List<Class<?>> classes = ClassUtil.getClasses("com.mg.controller");
//初始化controller类
findClassAnnotation(classes);
//将url 映射和方法进行关联
handlerMapping();
}catch (Exception e){
e.printStackTrace();
}
}
// 2.将扫包范围所有的类,注入到springmvc容器里面,存放在Map集合中 key为默认类名小写,value 对象
private void findClassAnnotation(List<Class<?>> classes) throws IllegalAccessException, InstantiationException {
for(Class<?> classinfo:classes){
//获取ExtController 注解
ExtController annotation = classinfo.getDeclaredAnnotation(ExtController.class);
//如果不为空,则使用反射机制初始化对象
if(annotation!=null){
//获取类名,首字母小写
String className=ClassUtil.toLowerCaseFirstOne(classinfo.getSimpleName());
//将实体类存放在mvc容器中
springMvcBeans.put(className,classinfo.newInstance());
}
}
}
// 3.将url映射和方法进行关联
private void handlerMapping(){
//遍历springmvcbeans
for (Map.Entry<String,Object> mvcBean:springMvcBeans.entrySet()){
Object obj=mvcBean.getValue();
//先判断类上是否有加注解
Class<?> classinfo=obj.getClass();
//路径
String baseUrl="";
//判断类上是否有注解,若有则拼接baseUrl
ExtRequestMapping declaredAnnotation=classinfo.getDeclaredAnnotation(ExtRequestMapping.class);
if(declaredAnnotation!=null)baseUrl=declaredAnnotation.value();
//获取类的所有方法,判断方法上是否有注解
Method[] methods=classinfo.getDeclaredMethods();
for(Method method:methods){
ExtRequestMapping methodRequestMapping=method.getDeclaredAnnotation(ExtRequestMapping.class);
if(methodRequestMapping!=null){
String methodUrl = baseUrl + methodRequestMapping.value();
// 绑定URL与实体类的关系 , key:请求地址 ,vlue类
urlBeans.put(methodUrl, obj);
//定义方法实体类
ExtMethod md=new ExtMethod();
//判断方法是否 为有参方法
Class[] parameterTypes = method.getParameterTypes();
if(parameterTypes.length>0){
//若方法为有参方法,则获取 参数与参数类型对应关系
Map<String,Class> pamaMap=new HashMap<String, Class>();
String[] pamas=ParameterNameUtils.getMethodParameterNamesByAsm4(classinfo,method);
for(int i=0;i<parameterTypes.length;i++){
pamaMap.put(pamas[i],parameterTypes[i]);
}
md.setPamas(pamaMap);
}
//设置方法名,在反射机制 调用方法时用
md.setMethodName(method.getName());
// 绑定URL与方法的对应关系, key:请求地址 ,value 方法实体类
urlMethods.put(methodUrl,md);
}
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doPost(req, resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1.获取请求url地址
String requestUri=req.getRequestURI();
// 2.从Map集合中获取控制对象
Object object = urlBeans.get(requestUri);
if (object == null) {
resp.getWriter().println(" not found 404 url");
return;
}
// 3.使用url地址获取方法
ExtMethod methodName=urlMethods.get(requestUri);
if (methodName==null) {
resp.getWriter().println(" not found method");
}
// 4.使用java的反射机制调用方法
Class classinfo=object.getClass();
String resultPage="";
try {
//4.1判断方法是否为有参方法
if (methodName.getPamas().size() > 0) {
//获取各参数类型
Class[] clas = getUrlPamasClass(methodName.getPamas(),req);
//获取各参数 值
Object[] pamaObject=getUrlPamasObjects(methodName.getPamas(),req);
//利用反射机制调用有参方法
Method method1 = classinfo.getDeclaredMethod(methodName.getMethodName(), clas);
resultPage = (String) method1.invoke(object, pamaObject);
} else {
resultPage = (String) methodInvoke(object, methodName.getMethodName());
}
}catch (Exception e){
e.printStackTrace();
}
// 5.调用视图转换器渲染给页面展示
extResourceViewResolver(resultPage, req, resp);
}
//获取方法 参数的 数据类型 数组,用于反射机制调用方法
private Class[] getUrlPamasClass(Map<String,Class> pamaMap,HttpServletRequest req){
Class[] classes=new Class[pamaMap.size()];
int n = 0;
for (Map.Entry<String, Class> pama : pamaMap.entrySet()) {
classes[n] = pama.getValue();
n++;
}
return classes;
}
//动态获取URL中的参数,利用反射机制调用有参方法
private Object[] getUrlPamasObjects(Map<String,Class> pamaMap,HttpServletRequest req){
Object[] pamaObject=new Object[pamaMap.size()];
int n = 0;
for (Map.Entry<String, Class> pama : pamaMap.entrySet()) {
Class type = pama.getValue();
String obj = req.getParameter(pama.getKey());
String typeName=type.getName();
if("int".equals(typeName)||"java.lang.Integer".equals(typeName)){
pamaObject[n]=Integer.parseInt(obj);
}
if("java.lang.String".equals(typeName)){
pamaObject[n]=type.cast(obj);
}
// 最好增加8大基本类型
n++;
}
return pamaObject;
}
private void extResourceViewResolver(String pageName, HttpServletRequest req, HttpServletResponse res)
throws ServletException, IOException {
// 根路径
String prefix = "/";
String suffix = ".jsp";
req.getRequestDispatcher(prefix + pageName + suffix).forward(req, res);
}
// 通过反射机制处理调用方法获取返回
private Object methodInvoke(Object object, String methodName) {
try {
Class<? extends Object> classInfo = object.getClass();
Method method = classInfo.getMethod(methodName);
Object result = method.invoke(object);
return result;
} catch (Exception e) {
return null;
}
}
}
|