JavaWeb-Servlet优化02(MVC思想)
1. class.forName()
作用是按参数中指定的字符串形式的类名去搜索并加载相应的类,如果该类字节码已经被加载过,则返回代表该字节码的Class实例对象,否则,按类加载器的委托机制去搜索和加载该类,如果所有的类加载器都无法加载到该类,即抛出ClassNotFoundException。加载完这个Class字节码后,接着就可以使用Class字节码的newInstance方法去创建该类的实例对象了。
A a = (A)Class.forName("pacage.A").newInstance();
等价于
A a = new A();
Class.forName(xxx.xx.xx) 返回的是一个类
Class.forName(xxx.xx.xx);的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。
有时候,我们程序中所有使用的具体类名在设计时(即开发时)无法确定,只有程序运行的时候才能确定,这时候就需要使用Class.forName去动态加载该类,这个类名通常是在配置文件中配置的。
2. 优化处理-提取返回值
将FruitController中的返回值
(如:response.sendRedirect 改为 return “redirect:fruit.do”
将 super.processTemplate("edit",request,response); 改为 return "edit";)
private String delete(HttpServletRequest request) throws ServletException{
String fidstr = request.getParameter("fid");
int fid = Integer.parseInt(fidstr);
FruitDAO fruitDAO = new FruitDAOImpl();
boolean b = fruitDAO.deleteFruitById(fid);
if(b){
System.out.println("删除成功!");
}
else{
System.out.println("删除失败!");
}
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
// 将 super.processTemplate("edit",request,response); 改为 return "edit";
在 DispatcherServlet中的设置
//DispatcherServlet
try {
Method method = controllerBeanObj.getClass().getDeclaredMethod(operate,HttpServletRequest.class);
if(method!=null){
//controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj, request);
//视图处理
String methodReturnStr = (String)returnObj;
if(methodReturnStr.startsWith("redirect")){ //如果是以redirect开头的 比如:return "redirect:fruit.do";
String redirectStr = methodReturnStr.substring("redirect:".length()); //截取剩下 fruit.do
response.sendRedirect(redirectStr); //跳转到 fruit.do
}
else{
super.processTemplate(methodReturnStr,request,response); // 比如:return "index";
}
}else{
throw new RuntimeException("operate值非法!");
}
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
3. 优化处理-提取参数
在 DispatcherServlet中获取参数
try {
//获取所有的方法
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
//参数是由自己的方法决定
for(Method method :methods){
if(operate.equals(method.getName())){
//统一获取请求参数
//获取当前方法的参数,返回参数数组
Parameter[] parameters = method.getParameters();
Object[] parameterValues = new Object[parameters.length];//用来存放参数的值
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
//如果参数是request、response、session不是使用请求获取参数
if("request".equals(parameterName))
{
parameterValues[i] = request;
}
else if("response".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)){ //guo
parameterObj = Integer.parseInt(parameterValue);
}
}
parameterValues[i]=parameterObj;
}
}
//controller组件中的方法调用
method.setAccessible(true);
Object returnObj = method.invoke(controllerBeanObj, parameterValues);
//视图处理
String methodReturnStr = (String)returnObj;
if(methodReturnStr.startsWith("redirect")){ //如果是以redirect开头的 比如:return "redirect:fruit.do";
String redirectStr = methodReturnStr.substring("redirect:".length()); //截取剩下 fruit.do
response.sendRedirect(redirectStr); //跳转到 fruit.do
}
else{
super.processTemplate(methodReturnStr,request,response); // 比如:return "index";
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
FruitController中传入参数
public class FruitController {
private FruitDAO fruitDAO = new FruitDAOImpl();
private String index( String oper,String keyword,Integer pageNo,HttpServletRequest request) {
HttpSession session=request.getSession();
if(pageNo == null){
pageNo = 1;
}
if(StringUtil.isNotEmpty(oper)&&"search".equals(oper)){
pageNo=1;
if(StringUtil.isEmpty(keyword)){
keyword = "";
}
session.setAttribute("keyword",keyword);
}else{
Object keywordobj = session.getAttribute("keyword");
if(keywordobj!=null){
keyword = (String)keywordobj;
}else{
keyword="";
}
}
session.setAttribute("pageNo",pageNo);
FruitDAO fruitDAO=new FruitDAOImpl();
List<Fruit> fruitList = fruitDAO.getListbypageNo(keyword, pageNo);
session.setAttribute("fruitList", fruitList);
int count = fruitDAO.getCount(keyword);
int pageCount = count/5+1;
session.setAttribute("pageCount", pageCount);
return "index";
}
private String add(Integer fid,String fname, Integer price ,Integer fcount,String remark) {
FruitDAO fruitDAO = new FruitDAOImpl();
boolean b = fruitDAO.addFruits(new Fruit(fid,fname,price,fcount,remark));
if(b){
System.out.println("添加成功");
}
else{
System.out.println("添加失败");
}
return "redirect:fruit.do";
}
private String delete(Integer fid) {
FruitDAO fruitDAO = new FruitDAOImpl();
boolean b = fruitDAO.deleteFruitById(fid);
if(b){
System.out.println("删除成功!");
}
else{
System.out.println("删除失败!");
}
return "redirect:fruit.do";
}
private String edit(Integer fid,HttpServletRequest request) {
FruitDAO fruitDAO1=new FruitDAOImpl();
Fruit fruit1 = new Fruit();
fruit1= fruitDAO1.getFruitById(fid);
request.setAttribute("fruit",fruit1);
return "edit";
}
private String Update(Integer fid ,String fname,Integer price,Integer fcount,String remark) {
FruitDAO fruitDAO=new FruitDAOImpl();
boolean b = fruitDAO.updateFruitById(new Fruit(fid, fname, price, fcount, remark));
if(b){
System.out.println("修改成功");
}
else{
System.out.println("修改失败");
}
return "redirect:fruit.do";
}
}
小结:
1. 一个请求对应一个Servlet,(/index ----> IndexServlet、/update ---->UpdateServlet)servlet太多了
2. 优化一:把一些列的请求都对应一个Servlet:IndexServlet/AddServlet/EditServlet/DelServlet/UpdateServlet -> 合并成FruitServlet
在FruitServlet中有add方法、update方法、index方法、delete方法等等,在界面中添加一个 name="operate",value="add/update..."的隐藏文本框,
通过operate的值来决定调用FruitServlet中的哪一个方法 使用的是switch-case
switch (operate){
case "index":
Index(request,response);
break;
}
3.优化二:Servlet中充斥着大量的switch-case,若业务规模扩大,那么会有很多的Servlet,也就意味着会有很多的switch-case,会造成代码冗余
因此,我们在servlet中使用了反射技术,规定operate的值和方法名一致,那么接收到operate的值是什么就表明我们需要调用对应的方法进行响应,
如果找不到对应的方法,则抛异常。
Method[] methods = this.getClass().getDeclaredMethods(); //获取当前类中所有的方法
for(Method m : methods){ //遍历
String name = m.getName(); //获得方法名称
if(operate.equals(name)){ //若方法名称与operate的值一致
try {
m.invoke(this,request,response); //反射技术,跳转到对应的方法中
return;
} catch (Exception e)
{ e.printStackTrace(); }
throw new RuntimeException("operate不合法!"); //若不一致,则不合法
}
4. 优化三:在优化二中,若业务较大,涉及到的servlet不止一个(如还有Userservlet、Productservlet、Fruitservlet、Orderservlet....),则每一个servlet中都有类似的反射技术的代码。因此继续抽取,设计了中央控制器类:DispatcherServlet。
DispatcherServlet这个类的工作分为两大部分:
(1)根据url定位到能够处理这个请求的controller组件:
1) 从url中提取servletPath : /fruit.do -> fruit
提取方法:request.getServletPath(); 通过substring()截取得到fruit
2) 根据fruit找到对应的组件:FruitController , 这个对应的依据我们存储在applicationContext.xml中
<bean id="fruit" class="com.fruit.controllers.FruitController/>
通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件
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");
Class controllerBeanClass = Class.forName(className);
Object beanObj = controllerBeanClass.newInstance() ;
beanMap.put(beanId , beanObj) ;
3) 根据获取到的operate的值定位到我们FruitController中需要调用的方法
Object controllerBeanObj = beanMap.get(servletPath);
(2)调用Controller组件中的方法:
1) 获取参数
获取即将要调用的方法的参数签名信息:
Parameter[] parameters = method.getParameters(); 通过parameter.getName()获取参数的名称;准备了Object[] parameterValues 这个数组用来存放对应参数的参数值,需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型
2) 执行方法
Object returnObj = method.invoke(controllerBean , parameterValues);
3) 视图处理
String methodReturnStr = (String)returnObj;
if(methodReturnStr.startsWith("redirect")){ //如果是以redirect开头的 比如:return "redirect:fruit.do";
String redirectStr = methodReturnStr.substring("redirect:".length()); //截取剩下 fruit.do
response.sendRedirect(redirectStr); //跳转到 fruit.do
} else{
super.processTemplate(methodReturnStr,request,response); // 比如:return "index";
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 开源Multi-agent AI智能体框架aevatar.ai,欢迎大家贡献代码
· Manus重磅发布:全球首款通用AI代理技术深度解析与实战指南
· 被坑几百块钱后,我竟然真的恢复了删除的微信聊天记录!