首页 > 编程语言 >Java Web项目1:水果管理系统

Java Web项目1:水果管理系统

时间:2023-01-28 11:14:20浏览次数:70  
标签:Web Java 请求 管理系统 request 实例 方法 public String

Java Web项目1:水果管理系统

核心参考资料

Java Web教程笔记

尚硅谷JavaWeb教程

项目架构体系

单一架构技术体系

  • 视图(V):用户的操作界面+数据的动态显示
    • 前端技术:HTML/CSS/JavaScript
    • 服务器端页面模板技术:Thymeleaf
  • 控制层(C):处理请求+跳转页面
    • 服务器:Tomcat
    • 控制器:Servlet
    • 域对象:request、session、servletContext
    • 过滤器:Filter
    • 监听器:Listener
  • 业务逻辑层(M):业务逻辑计算
  • 持久化层:操作数据库(DAO语句
./images

视图设计

  • 结构:由HTML实现,负责管理网页的内容。将来网页上不管是静态还是动态的数据都是填写到HTML的标签里。
  • 表现:由CSS实现,负责管理网页内容的表现形式。比如:颜色、尺寸、位置、层级等等。也就是给数据穿上一身漂亮的衣服。
  • 行为:由JavaScript实现,负责实现网页的动态交互效果。比如:轮播图、表单验证、鼠标滑过显示下拉菜单、鼠标滑过改变背景颜色等等。

​ HTML各标签的具体用法可参照:HTML标签

这里强调一下服务器访问地址(绝对路径)的概念:

./images

CSS语法:由选择器和声明组成,CSS样式由选择器和声明组成,而声明又由属性和值组成。

./images

Java Script的具体语法可参考:JSP基础语法

控制器

Tomcat

配置文件

XML文件(eXtensible Markup Language)——可拓展标记语言。

properties文件(web.xml)

Tomcat的作用

./images

对外是连接客户端服务端的Web服务器,对内则是能够处理请求的Servlet容器

Servlet类

Servlet类的功能

  • 获取来自客户端的数据

    请求-响应执行顺序

  1. 用户发送请求,action = add;
  1. 在web.xml找到url-pattern中对应的映射
  1. 找到对应的servlet类,由对应的该类获取请求
  • 调用DAO完成对数据库的操作

使用对应的dopost()或者doget方法来调用DAO方法完成对于数据库的操作

Servlet处理乱码问题

处理http request中的中文乱码

method中默认为get方法,另一种为post方法(表单),这个请求根据http协议采取不同的内容。

tomcat8:
1)get请求不需要设置编码
2)Post请求需要设置编码
request.setCharacterEncoding("UTF-8");

Servlet中的Service方法

Service方法:

当服务器收到http request的时候,自动调用service方法

相关方法
    javax.servlet.Servlet接口:
      void init(config) - 初始化方法
      void service(request,response) - 服务方法
      void destory() - 销毁方法

    javax.servlet.GenericServlet抽象类:
      void service(request,response) - 仍然是抽象的

    javax.servlet.http.HttpServlet 抽象子类:
      void service(request,response) - 不是抽象的
	      1. String method = req.getMethod(); 获取请求的方式
	      2. 各种if判断,根据请求方式不同,决定去调用不同的do方法
	          if (method.equals("GET")) {
	              this.doGet(req,resp);
	          } else if (method.equals("HEAD")) {
	              this.doHead(req, resp);
	          } else if (method.equals("POST")) {
	              this.doPost(req, resp);
	          } else if (method.equals("PUT")) {
	      3. 在HttpServlet这个抽象类中,do方法都差不多:
	      protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
	          String protocol = req.getProtocol();
	    	  String msg = lStrings.getString("http.method_get_not_supported");
	          if (protocol.endsWith("1.1")) {
	              resp.sendError(405, msg);
	          } else {
	              resp.sendError(400, msg);
	          }
	      }

service()方法执行流程:

当有请求过来时,service方法会自动响应(其实是tomcat容器调用的) 在HttpServlet中我们会去分析请求的方式:到底是get、post、head还是delete等等 然后再决定调用的是哪个do开头的方法

Servlet的生命周期:

1) 生命周期:从出生到死亡的过程就是生命周期。

对应Servlet中的三个方法:init(),service(),destroy()
2) 默认情况下
第一次接收请求时,这个Servlet会进行实例化(调用构造方法)、初始化(调用init())、然后服务(调用service())
从第二次请求开始,每一次都是服务
当容器关闭时,其中的所有的servlet实例会被销毁,调用销毁方法
3) 通过案例我们发现

  • Servlet实例tomcat只会创建一个,所有的请求都是这个实例去响应。
  • 默认情况下,第一次请求时,tomcat才会去实例化,初始化,然后再服务.这样的好处是什么? 提高系统的启动速度 。 这样的缺点是什么? 第一次请求时,耗时较长。
  • 因此得出结论: 如果需要提高系统的启动速度,当前默认情况就是这样。如果需要提高响应速度,我们应该设置Servlet的初始化时机。

Servlet的线程安全性:

Servlet在容器中是:单例的、线程不安全的
- 单例:所有的请求都是同一个实例去响应
- 线程不安全:一个线程需要根据这个实例中的某个成员变量值去做逻辑判断。但是在中间某个时机,另一个线程改变了这个成员变量的值,从而导致第一个线程的执行路径发生了变化

  • 我们已经知道了servlet是线程不安全的,给我们的启发是: 尽量的不要在servlet中定义成员变量。如果不得不定义成员变量,那么不要去:①不要去修改成员变量的值 ②不要去根据成员变量的值做一些逻辑判断。

HTTP协议

Http 称之为 超文本传输协议
1) Http是无状态的
2) Http请求响应包含两个部分:请求和响应

  • 请求
    请求包含三个部分: 1.请求行 ; 2.请求消息头 ; 3.请求主体
    1)请求行包含是三个信息: 1. 请求的方式 ; 2.请求的URL ; 3.请求的协议(一般都是HTTP1.1)
    2)请求消息头中包含了很多客户端需要告诉服务器的信息,比如:我的浏览器型号、版本、我能接收的内容的类型、我给你发的内容的类型、内容的长度等等
    3)请求体,三种情况
    get方式,没有请求体,但是有一个queryString,比如?nation=USA&name=jim(就是URL后面那一串字符串)
    post方式,有请求体,form data
    json格式,有请求体,request payload
  • 响应
    响应也包含三本: 1. 响应行 ; 2.响应头 ; 3.响应体
    1)响应行包含三个信息:1.协议 2.响应状态码(200) 3.响应状态(ok)
    2)响应头:包含了服务器的信息;服务器发送给浏览器的信息(内容的媒体类型、编码、内容长度等)
    3)响应体:响应的实际内容(比如请求add.html页面时,响应的内容就是<form....)

具体可参考:HTTPS协议详解

由于HTTP是无状态的,因此没有办法判断两次请求是一个客户端发送过来的,还是不同客户端发送过来的,因此需要通过会话跟踪技术来解决无状态的问题。

会话跟踪技术

客户端请求,服务器响应,这就像谈话一样,这种场景被定义为一个会话。

 - 客户端第一次发请求给服务器,服务器获取session,获取不到,则创建新的,然后响应给客户端
 - 下次客户端给服务器发请求时,会把sessionID带给服务器,那么服务器就能获取到了,那么服务器就判断这一次请求和上次某次请求是同一个客户端,从而能够区分开客户端
     - 常用的API:
     request.getSession() -> 获取当前的会话,没有则创建一个新的会话
     request.getSession(true) -> 效果和不带参数相同
     request.getSession(false) -> 获取当前会话,没有则返回null,不会创建新的

     session.getId() -> 获取sessionID
     session.isNew() -> 判断当前session是否是新的
     session.getMaxInactiveInterval() -> session的非激活间隔时长,默认1800秒
     session.setMaxInactiveInterval()
     session.invalidate() -> 强制性让会话立即失效

Session保存作用域

每个客户端实例化了一个HttpSession类,同时呢,也实例化了一个xxxServlet类

- session保存作用域是和具体的某一个session对应的
- 常用的API:
  void session.setAttribute(k,v)
  Object session.getAttribute(k)
  void removeAttribute(k)

服务器端转发和客户端重定向

服务器端转发

在这里插入图片描述

服务器内部转发:request.getRequestDispatcher("...").forward(request,response);
	  -一次请求响应的过程,对于客户端而言,内部经过了多少次转发,客户端是不知道的
      - 地址栏没有变化

客户端重定向

在这里插入图片描述

客户端重定向: response.sendRedirect("....");
      - 两次请求响应的过程。客户端肯定知道请求URL有变化
      - 地址栏有变化
超链接即为重定向,从URL和request的变化中即可看出          

Web服务器中四种容器

在这里插入图片描述

四种容器的作用域

pageContext:当前页面有效 (页面跳转后无效)

request:同一次请求有效(请求转发后有效;重定向后无效)

session:同一次会话有效(关闭/切换浏览器后无效 ; 默认30分钟有效期)

appliation:全局有效 (切换浏览器 仍然有效) 服务器开着就有效,切换客户端

ServletContext接口

ServletContext作为Servlet的整体配置接口

    • 获取某个资源的真实路径:getRealPath()
    • 获取整个Web应用级别的初始化参数:getInitParameter()
    • 作为Web应用范围的域对象
      • 存入数据:setAttribute()
      • 取出数据:getAttribute()

操作

[1]配置Web应用级别的初始化参数

<!-- 配置Web应用的初始化参数 -->
    <context-param>
        <param-name>handsomeMan</param-name>
        <param-value>alsoMe</param-value>
    </context-param>

[2]获取参数

String handsomeMan = servletContext.getInitParameter("handsomeMan");
System.out.println("handsomeMan = " + handsomeMan);

动态获取上下文路径

上下文路径(context path)= /Web应用名称

使用request对象来动态获取上下文路径,这样就不用担心上下文路径变化带来的影响。

request.getContextPath()

在Thymeleaf中则用@{}表示在字符串前附加上下文路径。

${pageContext.request.contextPath} 的效果就是"/应用名" ,这里等于 /test

补充一下相对路径与绝对路径的概念

https://blog.csdn.net/GoOnDrift/article/details/106664904

Thymeleaf基础语法

Thymeleaf语法说明

Thymeleaf精准获取请求参数中的值:

<p th:text="${param.team[0]}">这里替换为请求参数的值</p>
<p th:text="${param.team[1]}">这里替换为请求参数的值</p>

持久化层(DAO)

POJO类

POJO是Plain OrdinaryJava Object的缩写,实质上可以理解为简单的实体类,顾名思义POJO类的作用是方便程序员使用数据库中的数据表,对于广大的程序员,可以很方便的将POJO类当做对象来进行使用,当然也是可以方便的调用其get,set方法。

JavaBean

(1)通常情况下,由于 Java Bean 是被容器所创建(如 Tomcat) 的,所以 Java Bean 应具有一个无参的构造器,同时方便进行反射来赋值。
(2)实现 Serializable 接口用于实现 Bean 的持久性
(3)不能被跨进程访问

BaseDAO类:通用的增删改查操作
xxxDAO接口针对某一张数据表的原子操作,使用继承与BaseDAO的方法实现
xxxDAOImpl类:重写xxxDAO接口中的所有方法

Fruit项目功能迭代过程

1.0 增删改查功能

使用HTML的事件请求设置对应的Servlet,或者通过JSP再调用Servlet。然后Servlet中调用DAO方法来操作数据库。

2.0 实现分页功能

  1. 厘清总记录条数总页数的数学关系:

    总记录条数:fruitCount 总页数:pageCount

    pageCount = (frUItCount+4)/5

  2. 控制页面显示数量

     @Override
        public List<Fruit> getFruitList(Integer pageNo) {
            return super.executeQuery("select * from t_fruit limit ?,5",(pageNo-1)*5);
        }
    
  3. 确定记录数据

    @Override
        public int getFruitCount() {
            return ((Long)super.executeComplexQuery("select count(*) from t_fruit")[0]).intValue();
        }
    

    4.在界面上实现

    	<div style="width:60%;margin-left:20%;" class="center">
    	<input type="button" value="首 页" class="btn" th:onclick="|page(1)|" th:disabled="${session.pageNo==1}"/>
    	<input type="button" value="上一页" class="btn" th:onclick="|page(${session.pageNo-1})|" th:disabled="${session.pageNo==1}"/>
    	<input type="button" value="下一页" class="btn" th:onclick="|page(${session.pageNo+1})|" th:disabled="${session.pageNo==session.pageCount}"/>
    	<input type="button" value="尾页" class="btn" th:onclick="|page(${session.pageCount})|" th:disabled="${session.pageNo==session.pageCount}"/></div>
    

    5.判断

    通过一个判断变量,既可以区分请求是由查询而来还是从点击下一页等而来。

    if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
                //说明此时是点击 查询来进入index组件的,keyword可以从查询的表单中获取
                pageNo=1 ;
        		//从表单的请求中获取keyword
                keyword = req.getParameter("keyword");
                if(StringUtil.isEmpty(keyword)){
                    keyword="";
                }
                session.setAttribute("keyword",keyword);
            }else {
                //说明此处不是通过点击查询来发送请求的,当点击下面的 页数的时候
        		//从会话中得到页数
                String pageNoStr = req.getParameter("pageNo");
                if(StringUtil.isNotEmpty(pageNoStr)){
                    pageNo = Integer.parseInt(pageNoStr);
                }
        		//从会话中获取关键词
                Object keywordObj = session.getAttribute("keyword");
                if(keywordObj!=null){
                    keyword = (String)keywordObj;
                }else{
                    keyword="";
                }
            }
    
    

3.0 Servlet优化

由于以前其实也是调用XXXServlet中的service方法,然后service()方法根据method的值判断应该调用XXXServlet中的哪一个方法,因此现在只需要重写service方法即可。

把之前的XxxServlet类都写成方法,让他们都变成FruitServlet类里边的方法,然后在FruitServlet类中重写的service方法调用

​ 现在设计一个FruitServlet类,标签为fruit.do,那么所有的.html文件的相关位置都要改为fruit.do,并且都要回传一个隐藏域变量operate

@Override
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 "add":
            add(request,response);
            break;

        case "del":
            del(request,response);
            break;

        case "edit":
            edit(request,response);
            break;

        case "update":
            update(request,response);
            break;

        default:
            throw new RuntimeException("operate值非法");

    }

}

4.0 反射的引入

由于switch case不适合实际业务的开发,因此引入反射机制来替代switch case这一结构。一句话总结,利用反射机制来调用和operate同名的方法。

//使用反射机制来调用和operate同名的方法
//获取当前类中的所有方法
Method[] methods = this.getClass().getDeclaredMethods();
for(Method m : methods){
    //获取方法名称
    String methodName = m.getName();
    if(operate.equals(methodName)){
        try {
            //找到和operate同名的方法,通过反射技术调用该方法
            m.invoke(this,request,response);
            return;
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        }

    }
}
throw new RuntimeException("operate值非法!");

5.0 dispatcherServlet的引入

由于实际业务中需要不同的业务,如水果管理系统中有水果和用户等业务,因此进一步抽象,引入核心控制器的概念。

在这里插入图片描述

  1. 解析URL
//解析字符串url
//假设url是:  http://localhost:8080/pro15/hello.do
//那么servletPath是:    /hello.do
// 我的思路是:
// 第1步: /hello.do ->   hello   或者  /fruit.do  -> fruit
// 第2步: hello -> HelloController 或者 fruit -> FruitController
String servletPath = request.getServletPath();//得到 /hello.do
servletPath = servletPath.substring(1);//处理掉斜杠
int lastDotIndex = servletPath.lastIndexOf(".do") ;//处理掉最后的.do
servletPath = servletPath.substring(0,lastDotIndex);

  1. XML配置

在XML中建立字符串fruitFruitController的映射关系

<beans>
    <!-- 这个bean标签的作用是 将来servletpath中涉及的名字对应的是fruit,那么就要FruitController这个类来处理 -->
    <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
</beans>

  1. XML文档的解析

解析XML配置文件,并最终创建一个map容器,beanMap,的技术就叫做DOM技术

DOM:Document Object Mode,文档对象模型

private Map<String,Object> beanMap = new HashMap<>();

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节点,拿到每一组<bean id="fruit" class="com.atguigu.fruit.controllers.FruitController"/>
NodeList beanNodeList = document.getElementsByTagName("bean");
for(int i = 0 ; i<beanNodeList.getLength() ; i++){
	//拿到一个bean标签
    Node beanNode = beanNodeList.item(i);
    if(beanNode.getNodeType() == Node.ELEMENT_NODE){
    	//Element有更好的API支持
        Element beanElement = (Element)beanNode ;
        String beanId =  beanElement.getAttribute("id");
        String className = beanElement.getAttribute("class");
        //最终是希望在map容器中放入字符串id以及对应的实例
        Class controllerBeanClass = Class.forName(className);
        Object beanObj = controllerBeanClass.newInstance() ;
        beanMap.put(beanId , beanObj) ;
    }
}

比如说我们想要调用FruitController,那么我们就希望从xml中解析出另个东西存到map容器中,一个是字符串id=fruit,一个是FruitController的实例.

  1. 从map容器中获取对应的Controller实例
Object controllerBeanObj = beanMap.get(servletPath);

6.0 提取视图资源处理通用代码

新的客户端重定向方式:直接根据返回值来重定向,节约了使用switch-case的时间。

//2.controller组件中的方法调用
 method.setAccessible(true);
 //得到controller组件的返回对象
 Object returnObj = method.invoke(controllerBeanObj,parameterValues);
 //3.视图处理
 String methodReturnStr = (String)returnObj ;
 if(methodReturnStr.startsWith("redirect:")){       
 //比如:  redirect:fruit.do
     String redirectStr = methodReturnStr.substring("redirect:".length());
     response.sendRedirect(redirectStr);
 }
 }

新的Thymeleaf跳转方式

//2.controller组件中的方法调用
 method.setAccessible(true);
 Object returnObj = method.invoke(controllerBeanObj,parameterValues);
 //3.视图处理
 String methodReturnStr = (String)returnObj ;
 if(methodReturnStr.startsWith("redirect:")){    //比如: redirect:fruit.do
     String redirectStr = methodReturnStr.substring("redirect:".length());
     response.sendRedirect(redirectStr);
 }else{
     super.processTemplate(methodReturnStr,request,response);   
     // 比如:  "edit"
 }

因为客户端重定向以及跳转的实现由核心控制器负责了,那么Fruit控制器中各个方法的形参也就不再需要HTTPServletResponse响应,核心控制器继承的不再是HttpServlet,而是ViewBaseServlet

解决参数不匹配问题

我们从HttpServletRequest实例request中取到的数据都是String类型,然后将他们作为实参传给了控制器的某个方法。但是这个方法需要的实参并不都是String

还是使用反射机制,获得每个形参的类型parameter.getType().getName(),依据类型进行强转。又一次说明反射机制的重要性

//从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
String typeName = parameter.getType().getName();

Object parameterObj = parameterValue ;
//如果是Integer,则强制转换
if(parameterObj!=null) {
    if ("java.lang.Integer".equals(typeName)) {
        parameterObj = Integer.parseInt(parameterValue);
    }
}

parameterValues[i] = parameterObj ;

PS:可以看出Servlet的核心就在于反射这一块。

7.0 使用ServletConfig实现类存储初始化配置参数

  1. 首先可以定义web.xml中的初始化参数
<servlet>
    <servlet-name>Demo01Servlet</servlet-name>
    <servlet-class>com.atguigu.servlet.Demo01Servlet</servlet-class>
    <init-param>
        <param-name>hello</param-name>
        <param-value>world</param-value>
    </init-param>
    <init-param>
        <param-name>uname</param-name>
        <param-value>jim</param-value>
    </init-param>
</servlet>
  1. 使用servletConfig管理类的接口

在我们重写的init方法中,通过ServletConfig接口的实现类的实例来获取初始化配置参数,换句或说ServletConfig实例是用来管理初始化配置参数的。

public class Demo01Servlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        String initValue = config.getInitParameter("hello");
        System.out.println("initValue = " + initValue);
    }
}
    
  1. 在ServletContext中实现类存储参数(IOC容器的实现)

1.配置context-param参数(全局配置参数)

<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
</context-param>
  1. 在重写的空参init方法中获取ServletContext实例中的参数
public void init() throws ServletException {
    ServletConfig config = getServletConfig();
    String initValue = config.getInitParameter("hello");
    System.out.println("initValue = " + initValue);

    ServletContext servletContext = getServletContext();
    String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
    System.out.println("contextConfigLocation = " + contextConfigLocation);
}

3.在重写的service方法中获取ServletContext实例中的参数,这就麻烦一点,必须通过HttpServletRequest实例来获取

@Override
public void service(HttpServletRequest request, HttpServletResponse response){
    request.getServletContext();
    request.getSession().getServletContext();
}

8.0 Service技术

在这里插入图片描述

由业务层负责与业务相关的特有操作(一个业务可能包含多个DAO方法),控制器依赖业务层,负责业务层调用的方法,业务层负责调用DAO组件来实现业务方法。

业务层分为FruitService接口和FruitServiceImpl实现类,由FruitServiceImpl实现类中的方法调用fruitDAO实现目标。

(引入业务层就是将以细粒度中的DAO方法转换为粗粒度的业务方法)——这样更符合我们的核心需求。

9.0 引入IOC技术

IOC技术

  1. 中央控制器的作用:

    继承了ViewBaseServlet,用于thymeleaf渲染

    1. 重写init方法,调用父类ViewBaseServlet的init方法,实例化IOC层的BeanFactory用于提供各层需要的实例
    2. 重写service方法,解析url确定调用控制器的哪一个方法,统一获取参数,视图处理
  2. IOC层的作用:

新的IOC层负责:解析applicationContext.xml配置文件,创建beanMap容器,组装bean之间的依赖关系(赋值)

IOC层的结构非常简单:(1)接口BeanFactory,用于定义一些方法(2)接口的实现类,一个作为属性的map容器,一个空参构造器,重写接口中的方法

所谓的组装,其实就是进行赋值。比如说,控制器层依赖下层的业务层,在有IOC之前,我们是直接在控制器层中实例化了业务层,将业务层实例作为控制器层的一个属性。但现在是由IOC层利用反射机制为这个属性进行赋值,这个值来自于第一步中的beanMap容器(就是由IOC容器统一管理,统一实例化)

组装实例之间的依赖关系

1.在xml中配置bean之间的依赖关系

<beans>
    <bean id="fruitDAO" class="com.atguigu.fruit.dao.impl.FruitDAOImpl"/>
    <bean id="fruitService" class="com.atguigu.fruit.service.impl.FruitServiceImpl">
        <!-- property标签用来表示属性;name表示属性名;ref表示引用其他bean的id值-->
        <property name="fruitDAO" ref="fruitDAO"/>
    </bean>
    <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController">
        <property name="fruitService" ref="fruitService"/>
    </bean>
</beans>

2.组装依赖类之间的关系

//1) 找到propertyRef对应的实例
Object refObj = beanMap.get(propertyRef);
//2) 将refObj设置到当前bean对应的实例的property属性上去
Object beanObj = beanMap.get(beanId);
Class beanClazz = beanObj.getClass();
Field propertyField = beanClazz.getDeclaredField(propertyName);
propertyField.setAccessible(true);
propertyField.set(beanObj,refObj);

FruitController依赖FruitServiceImpl

  1. 根据FruitController中的ref(fruitService),取出被依赖的实例FruitServiceImpl
  2. 根据fruit(id)取出FruitController实例
  3. 根据FruitController中的name的值拿到fruitService属性
  4. 将FruitController对象中的fruitService顺序性设置为FruitServiceImpl

10.0 引入Filter技术

在这里插入图片描述

//@WebFilter("/demo01.do")
@WebFilter("*.do")
public class Demo01Filter implements Filter {
    @Override
    public void init(FilterConfig filterConfig) throws ServletException {

    }

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        System.out.println("helloA");
        //放行
        filterChain.doFilter(servletRequest,servletResponse);
        System.out.println("helloA2");
    }

    @Override
    public void destroy() {

    }
}

不只是请求,服务器端的响应也要经过过滤器,也就是说,请求先被拦截,打印helloA,然后放行,执行servlet的service方法,响应传回给客户端之前,响应被过滤器拦截,打印helloA2,最后,响应传到客户端。

创建过滤器的步骤
Filter也属于Servlet规范

Filter开发步骤:新建类实现Filter接口,然后实现其中的三个方法:init、doFilter、destroy
配置Filter,可以用注解@WebFilter,也可以使用xml文件

Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter(“*.do”)表示拦截所有以.do结尾的请求

过滤器链
1)执行的顺序依次是: A B C demo03 C2 B2 A2
2)如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的
3)如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序

11.0 引入事务管理技术

(1)将通过DriverManager获取连接封装为一个方法
(2)从ThreadLocal实例获取连接封装为一个方法

private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();

public static final String DRIVER
public static final String URL
public static final String USER
public static final String PWD

private static Connection createConn(){
    try {
        //1.加载驱动
        Class.forName(DRIVER);
        //2.通过驱动管理器获取连接对象
        return DriverManager.getConnection(URL, USER, PWD);
    } catch (ClassNotFoundException | SQLException e) {
        e.printStackTrace();
    }
    return null ;
}

public static Connection getConn(){
    Connection conn = threadLocal.get();
    if(conn==null){
        conn =createConn();
        threadLocal.set(conn);
    }
    return threadLocal.get() ;
}

于是TransactionManager中的结构变成:

public class TransactionManager {

    //开启事务
    public static void beginTrans() throws SQLException {
        ConnUtil.getConn().setAutoCommit(false);
    }

    //提交事务
    public static void commit() throws SQLException {
         ConnUtil.getConn().commit();
    }

    //回滚事务
    public static void rollback() throws SQLException {
         ConnUtil.getConn().rollback();
    }
}

12.0 异常体系构建

//执行更新,返回影响行数
protected int executeUpdate(String sql , Object... params) {
    boolean insertFlag = false ;
    insertFlag = sql.trim().toUpperCase().startsWith("INSERT");

    conn = getConn();
    try{
        
    }catch (SQLException e){
        e.printStackTrace();
        throw new DAOException("BaseDAO executeUpdate出错了");
    }
}

13.0 添加监听器

public interface EventListener {
}

public interface ServletContextListener extends EventListener {
    void contextInitialized(ServletContextEvent var1);

    void contextDestroyed(ServletContextEvent var1);
}

//@WebListener
public class MyServletContextListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        System.out.println("Servlet上下文对象初始化动作被我监听到了....");
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {
        System.out.println("Servlet上下文对象销毁动作被我监听到了.....");
    }
}

监听器的使用:在监听器中提前实例化IOC容器

//监听上下文启动,在上下文启动的时候去创建IOC容器,然后将其保存到application作用域
//后面中央控制器再从application作用域中去获取IOC容器
@WebListener
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        //创建IOC容器
        BeanFactory beanFactory = new ClassPathXmlApplicationContext();
        //获取ServletContext对象
        ServletContext application = servletContextEvent.getServletContext();
        //将IOC容器保存到application作用域
        application.setAttribute("beanFactory",beanFactory);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

然后中央控制器再从application作用域中去获取IOC容器

private BeanFactory beanFactory ;

public DispatcherServlet(){
}

public void init() throws ServletException {
    super.init();
    //之前是在此处主动创建IOC容器的
    //现在优化为从application作用域去获取
    //beanFactory = new ClassPathXmlApplicationContext();
    ServletContext application = getServletContext();
    Object beanFactoryObj = application.getAttribute("beanFactory");
    if(beanFactoryObj!=null){
        beanFactory = (BeanFactory)beanFactoryObj ;
    }else{
        throw new RuntimeException("IOC容器获取失败!");
    }
}

ServletContext其实就是Application对象,启动时由tomcat创建。

https://blog.csdn.net/weixin_43362002/article/details/124659893

14.0 项目小结

在这里插入图片描述

项目的整体流程

  1. 首先用CharacterEncodingFilter拦截器来拦截请求,从而配置编码。
  2. 使用事务管理器开启事务,遇到错误则回退事务。
  3. 使用DispatchServlet来解析URL,并调用对应的控制器FruitController,接受到operate参数的方法,通过反射技术调用方法,同时进行重定向或视图处理。
  4. FruitController依赖FruitService中提供的方法,这里涉及到依赖注入,核心就是通过ioc容器在使用时将一个FruitService实例赋给FruitController,这样就起到解耦合的作用。
  5. 获取连接时只从ThreadLocal实例中获取,如果没有拿到,就造一个连接放到ThreadLocal实例中,然后再从ThreadLocal实例中取出数据库连接。
  6. 于是TransactionManager就可以调用ConnUtil中获取数据库连接的方法来开启事务。
  7. 事务开启后,使用FruitDAO调用BaseDAO方法实现数据库操作。

标签:Web,Java,请求,管理系统,request,实例,方法,public,String
From: https://www.cnblogs.com/robyn2022/p/17069850.html

相关文章

  • Padavan最新修复安装ONMP,自建本地entware源快速安装WEB环境,帖内付补充文件
    onmp一键安装WEB环境脚本,因为官方源文件变动的问题,导致onmp即使成功安装后很多WEB应用也用不了,缺失了常用的PHP扩展,作者自己也放弃更新了,很可惜.经过几个星期的摸索烧......
  • 嵌入 WebAssembly 运行时和实例化 WebAssembly 模块的几大要素
    ​​嵌入WebAssembly运行时和实例化WebAssembly模块的几大要素​​ 下面这段代码忽略了错误处理机制,介绍了如何在Go语言开发的宿主程序中嵌入WebAssembly.fun......
  • 如何在 Go 语言开发的宿主程序中嵌入 WebAssembly
    ​​如何在Go语言开发的宿主程序中嵌入WebAssembly​​ 在WebAssembly的官方定义中,​​forastack-basedvirtualmachine​​​这句话也值得关注,因为它引领了......
  • 读Java8函数式编程笔记03_高级集合类和收集器
    1. 方法引用1.1. 一种引用方法的轻量级语法1.1.1. 提供了一种简短的语法1.1.2. 标准语法为Classname::methodName1.2. 凡是使用Lambda表达式的地方,就可以使用1.......
  • websocket打造在线聊天室
    1.常见的网络通信协议 tcpudphttp和websocket等;http超文本传输协议,是一个无状态,无连接,单向的应用层协议,缺点是服务器不能主动的给客户端发送消息;消息只能由客......
  • java多线程基础小白指南--关键字识别(start,run,sleep,wait,join,yield)
    在学习java多线程基础上,会遇到几个关键字,理解并识别它们是掌握多线程的必备知识,下面,我将通过源码或者程序演示给出我对这几个关键字的理解,如果有不同意见,欢迎在评论区或者......
  • java安全-RMI
    Java安全-RMI1、RMI原理浅析RMI(RemoteMethodInvocation)远程方法调用,是允许运行在一个JVM中的对象调用另一个JVM中的对象方法。两台虚拟机可以是同一台宿主机的不......
  • 第25章 web层开发-用户接口开发
    目录1表单验证的初始化2自定义mobile验证器3登录逻辑完善4session机制在微服务下的问题5jsonwebtoken的认证机制6集成jwt到gin中7给url添加登录权限验证8如何解......
  • JavaScript学习笔记—正则表达式
    用来定义一个规则通过这个规则计算机可以检查一个字符串是否符合规则或者将字符串中符合规则的内容提取出来也是JS中的一个对象,所以要使用正则表达式,需要先创建正则表达......
  • java学习记录
    makrdown学习标题二级标题字体粗体斜体斜体加粗划线引用引用分割号图片可以用复制的东西超链接点击列表............