?
目录
1.自定义SpringMVC框架初级版本
2.自定义SpringMVC框架终极版本
1.自定义SpringMVC框架初级版本
-
创建@RequestMapping注解
-
注解的作用主要是为了给servlet里面的方法做映射的。 -
注解一般是作用于方法上,并且保留到运行的时候还要存在于字节码 RequestMapping
1. 这是一个注解,它要打在模块化的Servlet的方法上。
2. 用于表示当什么样的请求来的时候,就执行这个方法
3.要明确内容:
3.1 注解打在方法上
3.2 注解要保留到运行阶段,
3.3 这个注解不是空注解,所以它需要一个value属性,用来写地址: @RequestMapping("/aa/bb") BaseServlet这是一个总管类,专门用来抓请求,抓尾巴带 .do的请求
1. 它必须是一个Servlet,它才能抓请求。
2. 抓到请求之后,要对这个请求进行处理,然后去调用对应模块类的方法,所以它需要重写service方法
2.1 从请求地址里面截取出来后面的映射路径
localhost:82/user/reigster.do ===== /user/register
2.2 扫描这个包下的所有类com.jcli.servlet03 ,得到这个包下的所有类的字节码
2.3 遍历每一个字节码,然后得到类中的所有方法
2.4 遍历每一个方法,然后取出方法身上的注解 @RequestMapping的内容
2.5 判断如果有这个注解的话,就把注解里面的value属性的值(注解里面的字符串)给取出来
2.6 和我们在2.1 截取出来的字符串进行比较,如果一样,就表示找到这个方法了。
2.7 就让这个方法执行即可。用反射来调用即可!
-
创建UserController (UserServlet) , 定义方法, -
在这个方法上面添加@RequestMapping注解 -
创建DispatcherServlet (BaseServlet)继承HttpServlet, 路径配置 *.do -
在DispatcherServlet的 重写的service()方法里面
//1.获得请求的URI和项目部署路径, 截取获得映路径 eg: /user/login
//2.扫描某个包里面的所有类的字节码对象集合List
//3.遍历字节码对象集合List
//4.获得类里面的所有的Method
//5.遍历所有的Method
//6.获得method上面的RequestMapping注解 获得注解的value属性值
//7.判断value属性值是否和获得映路径一致, 一致 就调用method
存在问题:
-
在总的控制器里面扫描指定包的时候,扫描的有点多了。 -
等来了请求再去解析,再去反射创建对象调用 ---> 影响处理请求的速度的 -
扫描的包名写死了 ?
2.自定义SpringMVC框架终极版本
-
在项目部署的时候,就马上启动扫描的工作了。 a. 把扫描的工作提前放到servlet的init方法去做 b. 让这个init方法调用的时机再提前一些,提前到项目发布的时候就执行。 c. 设置 <load-on-startup> 1</load-on-startup> -
DispatcherServlet注册的时候,不要使用注解来注册了,而是使用xml来注册, 在xml里面注册的时候,就可以配置servlet的初始化参数,用户指定扫描具体那个包。 <init-param> -
扫描得到包下的所有类了之后,不是每一个类我们都要查看它的所有方法有没有requestMapping这个注解
-
只看类上有没有一个注解@Controller , 这个注解是自定义的注解。 -
谁身上有这个注解,我们就解析这个类里面的所有方法。
-
在init方法里面完成扫描的工作之后,需要使用一个Map集合来进行映射关系,也就是完成扫描工作之后,使用一个map集合来包装 映射的地址和controller的对象。 map集合里面就包装了请求地址和调用方法的一个关系。 KEY : 请求地址 , value : 包装的javaBean。 class MethodBean{ private Method method; //具体的方法对象 private Object obj; //调用这个方法要用的实例对象 } Map<String , MethodBean> map ; MethodBean mb = new MethodBean(方法对象 , 类的实例对象); map.put("/user/register" , mb); -
在请求来的时候,在service里面获取请求的地址 -
截获请求的地址了之后,就可以直接问map要方法来调用。 MethodBean mb = map.get("/user/register"); Method m = mb.getMethod(); m.invoke(mb.getObj() , req , resp);
package com.jcli.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//打在类上
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
package com.jcli.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
//打在方法上
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value();
}
package com.jcli.controller;
import com.jcli.annotation.Controller;
import com.jcli.annotation.RequestMapping;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
//可以处理请求~!
@Controller
public class UserController {
@RequestMapping("/user/register")
public void register(HttpServletRequest request, HttpServletResponse response){
System.out.println("执行了UserController的register方法~!");
}
}
package com.jcli.bean;
import java.lang.reflect.Method;
//主要是用来封装 被调用的方法和 调用这个方法用到的实例对象
public class MethodBean {
private Method m ; //要执行的方法对象
private Object o ; // 要调用上面方法用到的实例对象
public MethodBean() {
}
public MethodBean(Method m, Object o) {
this.m = m;
this.o = o;
}
public Method getM() {
return m;
}
public void setM(Method m) {
this.m = m;
}
public Object getO() {
return o;
}
public void setO(Object o) {
this.o = o;
}
}
?
package com.jcli.utils;
import java.io.File;
import java.io.FileFilter;
import java.net.URL;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
/**
* @Description:扫描包下的类
*/
public class ClassScannerUtils {
/**
* 获得包下面的所有的class
* @param
* @return List包含所有class的实例
*/
public static List<Class<?>> getClasssFromPackage(String packageName) {
List clazzs = new ArrayList<>();
// 是否循环搜索子包
boolean recursive = true;
// 包名对应的路径名称
String packageDirName = packageName.replace('.', '/');
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(packageDirName);
while (dirs.hasMoreElements()) {
URL url = dirs.nextElement();
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
findClassInPackageByFile(packageName, filePath, recursive, clazzs);
}
}
} catch (Exception e) {
e.printStackTrace();
}
return clazzs;
}
/**
* 在package对应的路径下找到所有的class
*/
public static void findClassInPackageByFile(String packageName, String filePath, final boolean recursive,
List<Class<?>> clazzs) {
File dir = new File(filePath);
if (!dir.exists() || !dir.isDirectory()) {
return;
}
// 在给定的目录下找到所有的文件,并且进行条件过滤
File[] dirFiles = dir.listFiles(new FileFilter() {
public boolean accept(File file) {
boolean acceptDir = recursive && file.isDirectory();// 接受dir目录
boolean acceptClass = file.getName().endsWith("class");// 接受class文件
return acceptDir || acceptClass;
}
});
for (File file : dirFiles) {
if (file.isDirectory()) {
findClassInPackageByFile(packageName + "." + file.getName(), file.getAbsolutePath(), recursive, clazzs);
} else {
String className = file.getName().substring(0, file.getName().length() - 6);
try {
clazzs.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + "." + className));
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
注册:
1. Servlet注册时候要用xml 注册, 不要使用注解测试
2. 需要让这个Servlet初始化的时机更提前一些,所以需要写上 <load-on-startup>1</load-on-startup>
3. 需要在注册的时候,提供一个初始化参数,这个初始化参数就是用来设置扫描哪个包!
<init-param>
<param-name>packageName</param-name>
<param-value>com.itheima.servlet</param-value>
</init-param>
4. 也只抓尾巴带 .do的请求
init :
1. 扫描包,找类,找方法,把注解的内容给分解出来。
2. 就得到了什么样的地址会执行什么类的什么方法,可以使用一个JavaBean和Map集合来封装他们
class MethodBean{
private Method m; //这个方法
private Object o; // 调用这个方法要用的对象
}
map.put(映射地址 ,MethodBean对象 );
service :
1. 截取地址, /user/register
2. 拿着这个地址,去问map要东西:
package com.jcli.servlet;
import com.jcli.annotation.Controller;
import com.jcli.annotation.RequestMapping;
import com.jcli.bean.MethodBean;
import com.jcli.utils.ClassScannerUtils;
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.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
//这是一个Servlet,专门用来抓尾巴带 .do的请求。
public class DispatherServlet extends HttpServlet {
private Map<String, MethodBean> map = new HashMap<>();
@Override
public void init(ServletConfig config) throws ServletException {
try {
//1.获取初始化参数
String packageName = config.getInitParameter("packageName");
// 2.遍历包下所有类
List<Class<?>> classList = ClassScannerUtils.getClasssFromPackage(packageName);
// 3.遍历每一个字节
for (Class<?> clazz : classList) {
// 4.看字节码是否有注解@Controller
boolean flag = clazz.isAnnotationPresent(Controller.class);
if (flag) {
// 5.得到类的所有方法
Method[] methods = clazz.getMethods();
// 6.遍历每一个方法
for (Method method : methods) {
// 7.取出方法上的注解
RequestMapping annotation = method.getAnnotation(RequestMapping.class);
// 8.判断有没有注解
if (annotation != null) {
// 9.取出来注解的内容
String value = annotation.value();
// 10.封装方法和对象到javaBean
MethodBean methodBean = new MethodBean(method, clazz.newInstance());
// 11.封装到集合
map.put(value, methodBean);
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
//1.获取地址
String uri = req.getRequestURI();
String path = uri.substring(0, uri.lastIndexOf("."));
// 2.拿着地址去问map集合要对象
MethodBean methodBean = map.get(path);
// 3.找到则执行
if (methodBean!=null){
Method m = methodBean.getM();
Object o = methodBean.getO();
m.invoke(o,req,req);
}else {
System.out.println("没有找到要执行的方法:" + path);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--注册DispatcherServlet-->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>com.jcli.servlet.DispatherServlet</servlet-class>
<!--通过初始化参数的方式,告诉servlet,要扫描哪个包-->
<init-param>
<param-name>packageName</param-name>
<param-value>com.jcli.controller</param-value>
</init-param>
<!--项目启动,就执行这个类的初始化方法-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
?
?
?
|