使用普通类简单模拟Servlet
需求
通过使用普通类+过滤器来模拟实现前端页面向后端发送请求时Servlet的工作
步骤
配置访问路径xml
为了能够灵活的匹配到用户想要访问的路径,我们将自定义的资源类的类名、资源类的完全限定名以及默认访问的方法,保存到路径映射文件中。之后再解析此配置xml文件。
我这里简单模拟了两个资源类,怎么样看这里是不是有种似曾相识的感觉。
解析配置文件,结果放入map
自定义过滤器类,并在web.xml中配置我们的过滤器,拦截所有的请求
当服务器启动加载时,会加载过滤器,在过滤器的初始化方法中解析我们的访问配置文件:这里使用的是dom4j+Xpath解析,
public void init(FilterConfig arg0) throws ServletException {
/**
* 为了灵活的获取到用户想要访问的类,配置xml文件,配置路径名、类完全限定名、默认访问方法
* 在服务器加载、过滤器初始化时就解析配置文件
*
* 使用dom4j解析xml
* 用一个对象存储解析出的name、class、method等属性的值
* 使用map存储对象
*/
try {
SAXReader reader = new SAXReader();
//使用类加载器以输入流形式将配置文件加载
InputStream stream = Thread.currentThread().getContextClassLoader().getResourceAsStream("config.xml");
Document read = reader.read(stream);
//使用Xpath获取bean节点 '//bean'找到文档中所有bean节点,无视层级
//使用Xpath要导入jar包 jaxen
List<Element> list = read.selectNodes("//bean");
//遍历元素集合
for (Element element : list) {
Config config = new Config();
//获取到要访问的类
String name = element.attributeValue("name");
String className = element.attributeValue("class");
String method = element.attributeValue("method");
config.setName(name);
config.setClassName(className);
config.setMethod(method);
**hashMap.put(name, config);** //将要访问的资源名作为key,解析结果封装的对象作为value装入一个map中
}
} catch (DocumentException e) {
e.printStackTrace();
}
}
将解析结果封装到一个对象之中,这里我自定义了一个用于封装的对象,并且将要访问的资源名作为key,解析结果封装的对象作为value装入一个map中。我自定义的资源类其字段有 类名,类完全限定名,类方法名,类具体结构如下:
public class Config {
//类名
private String name;
//类的完全限定名,用于反射创建对象
private String className;
//类中的方法名
private String method;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getClassName() {
return className;
}
public void setClassName(String className) {
this.className = className;
}
public String getMethod() {
return method;
}
public void setMethod(String method) {
this.method = method;
}
@Override
public String toString() {
return "Config [name=" + name + ", className=" + className + ", method=" + method + "]";
}
}
访问拦截处理
当用户在前端发送请求之后,过滤器拦截到请求,在过滤器的过滤方法中,我们先将请求对象从ServletRequest转为HttpServletRequest因为我们要使用这个Http请求对象的getRequestURI方法来获取到用户的请求路径,通过字符串操作将路径中用户想要访问的资源名给提取出来。这里就只用遍历我们之前在过滤器初始化方法中使用的map中是否存在用户想要访问的资源
- 如果不存在就直接放行
- 如果存在:通过资源名在map中拿到该资源名对应的资源类的完全限定名,通过完全限定名创建对象,之后我们再从map中拿到方法名,通过之前创建的对象执行该方法完成对该资源类的访问
过滤器过滤方法:
public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain)
throws IOException, ServletException {
try {
//将请求对象转为httpServletRequest对象
HttpServletRequest request = (HttpServletRequest) req;
//获取请求路径
String uri = request.getRequestURI();
int index = uri.lastIndexOf("/");
//得到访问的类
String name = uri.substring(index+1);
//请求到map中对应的name
if (hashMap.containsKey(name)) {
//拿到对应name的完全限定名
Config c = (Config) hashMap.get(name);
String className = c.getClassName();
//通过字节码对象使用完全限定名创建对象
Class<?> clazz = Class.forName(className);
Object obj = clazz.newInstance();
//获得方法
Method method = clazz.getMethod(c.getMethod());
method.invoke(obj);
}else {
//如果map中没有对应的路径,就放行
chain.doFilter(req, resp);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (SecurityException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}