一、Servlet最初使用方法
一个请求对应一个servlet,但是这样存在的问题是servlet会越来越多。
二、优化1(合并成一个servlet)
将一系列的请求都对应一个servlet,通过一个operate的值来决定调用servlet中的哪个方法,使用switch,但是随着项目的业务规模扩大,会有很多servlet,这样就会充斥着大量的switch,效率低下。
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
switch (operate){
case "index":index(request,response);break;
case "edit":edit(request,response);break;
case "add":add(request,response);break;
case "del":del(request,response);break;
case "update":update(request,response);break;
default:throw new RuntimeException("operate非法");
}
}
三、优化2(反射)
在servlet中使用反射技术,规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法相应,如果找不到对应方法则抛出异常,但是还是存在一定问题:每个servlet都存在类似的反射方法。
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
//获取当前类所有方法
Method[] methods = this.getClass().getDeclaredMethods();
for (Method m :methods){
//获取方法名称
String methodName = m.getName();
if(operate.equals(methodName)){
//找到方法,通过反射调用
try {
m.invoke(this,request,response);
return;
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
} catch (InvocationTargetException e) {
throw new RuntimeException(e);
}
}
}
throw new RuntimeException("operate非法");
}
四、优化3(dispatcherServlet)
设计中央控制器:dispatcherServlet。
多个servlet改为controller(去掉注解),由dispatcher统一控制;
所以需要先根据请求的网址/xxx找到对应的controller,再调用其中的方法。
1、根据url定位到能够处理这个请求的controller组件
新建DispatcherServlet类
1)从url中提取servletPath:/fruit.do -> fruit;
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setCharacterEncoding("utf-8");
//例url:http://localhost:8080/1/hello.do
//那么servletPath是:/hello.do
String servletPath = request.getServletPath();
//1、使用截取,获得hello
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do");
servletPath = servletPath.substring(0,lastDotIndex);
2)根据fruit找到对应的组件:FruitController,这个对应的依据存储在applicationContext.xml中,通过DOM技术解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有Controller组件;
以下方法解析xml文件,根据bean节点找到id和class,使用Map集合保存。
private Map<String,Object> beanMap = new HashMap<>();
public void init() throws ServletException {
super.init();
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
//1、创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
//2、创建DocumentBuilder对象
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//3、创建Document对象
Document document = documentBuilder.parse(inputStream);
//4、获取所有bean节点
NodeList beanNodeList = document.getElementsByTagName("bean");
for(int i = 0;i<beanNodeList.getLength();i++){
Node beanNode = beanNodeList.item(i);
if(beanNode.getNodeType() == Node.ELEMENT_NODE){
Element beanElement = (Element) beanNode;
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
Object beanObj = Class.forName(className).newInstance();
beanMap.put(beanId,beanObj);
}
}
} catch (ParserConfigurationException e) {
throw new RuntimeException(e);
} catch (IOException e) {
throw new RuntimeException(e);
} catch (SAXException e) {
throw new RuntimeException(e);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
applicationContext.xml文件:
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!-- bean作用:servletPath中涉及的名字对应的是fruit,那么就要用FruitController来处理-->
<bean id="fruit" class="com.fruit.controllers.FruitController"/>
</beans>
3)根据获取到的operate的值定位到FruitController中需要的方法。
先拿到所有的方法,再遍历找到和operate相同的方法
//2、根据hello对应到HelloController
Object controllerBeanObj = beanMap.get(servletPath);
String operate = request.getParameter("operate");
if(StringUtil.isEmpty(operate)){
operate = "index";
}
try {
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for (Method method : methods) {
if (operate.equals(method.getName())) {
2、调用Controller组件中的方法
由以前在每个方法里获取值,优化为传入参数;
由以前在每个方法里进行服务器重定向或跳转页面,优化为返回String值,后面进行视图处理。
1)获取参数
获取即将要调用的方法的参数信息:Parameter[] parameters = method.getParameters();
通过parameter.getName()获取参数的名称;
准备Object[] parameterValues 数组用来存放对应参数的参数值;
需要考虑参数类型问题,需要做类型转化工作,parameter.getType()获取参数类型。
//1、统一获取请求参数
//获取当前方法参数,返回参数数组
Parameter[] parameters = method.getParameters();
//承载参数值
Object[] parameterValues = new Object[parameters.length];
for(int i = 0;i<parameterValues.length;i++){
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
if("request".equals(parameterName)){
parameterValues[i] = request;
}else if("request".equals(parameterName)){
parameterValues[i] = response;
}else if("session".equals(parameterName)){
parameterValues[i] = request.getSession();
}else{
String parameterValue = request.getParameter(parameterName);
String typeName = parameter.getType().getName();
Object parameterObj = parameterValue;
if(parameterObj!=null){
if("java.lang.Integer".equals(typeName)){
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i] = parameterObj;
}
}
2)执行方法
//2、controller组件方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj,parameterValues);
3)视图处理
//3、视图处理
String methodReturnStr = (String) returnObj;
if (methodReturnStr.startsWith("redirect:")) {
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
} else {
super.processTemplate(methodReturnStr, request, response);
}
}
}
// else {
// throw new RuntimeException("operate非法");
// }
}catch (InvocationTargetException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
理解servlet优化对以后学习mvc会有帮助。