Spring笔记(三)(Spring整合web环境)

news2024/11/24 6:39:30

01、Spring整合web环境

1.1 Javaweb三大组件及环境特点

在Java语言范畴内,web层框架都是基于Javaweb基础组件完成的,所以有必要复习一下Javaweb组件的特点

组件作用特点
Servlet服务端小程序,负责接收客户端请求并作出响应的单例对象,默认第一次访问创建,可以通过配置指定服务器启动就创建,Servlet创建完毕会执行初始化init方法。每个Servlet有一个service方法,每次访问都会执行service方法,但是缺点是一个业务功能就需要配置一个Servlet
Filter过滤器,负责对客户端请求进行过滤操作的单例对象,服务器启动时就创建,对象创建完毕执行init方法,对客户端的请求进行过滤,符合要求的放行,不符合要求的直接响应客户端,执行过滤的核心方法doFilter
Listener监听器,负责对域对象的创建和属性变化进行监听的根据类型和作用不同,又可分为监听域对象创建销毁和域对象属性内容变化的, 根据监听的域不同,又可以分为监听Request域的,监听Session域的,监听ServletContext域的

1.2 Spring整合web环境的思路及实现

在进行Java开发时要遵循三层架构+MVC,Spring操作最核心的就是Spring容器,web层需要注入Service, service层需要注入Dao(Mapper),web层使用Servlet技术充当的话,需要在Servlet中获得Spring容器
具体操作如下:

  1. 在第二节笔记中的转账业务模块,修改pom.xml 文件的打包方式及导包:
<packaging>war</packaging>
<dependency>
    <groupId>javax.servlet</groupId>
    <artifactId>javax.servlet-api</artifactId>
    <version>4.0.1</version>
</dependency>
  1. 创建web层,servlet
@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // web层调用service层,获得accountService,现在accountService在Spring容器中
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AccountConfig.class);
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");
        accountService.transferMoney("tom","lucy",500);
        System.out.println("转账操作成功");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}
  1. 配置tomcat,部署项目,访问链接http://localhost:8080/accountServlet

测试事务

  • 模拟有异常

访问前:数据库tom:5000, lucy:5000
访问后:数据库tom:5000, lucy:5000
image.png

  • 无异常

访问前:数据库tom:5000, lucy:5000
访问后:数据库tom:4500, lucy:5500
image.png

上述方案的问题:web层代码如果都去编写创建AnnotationConfigApplicationContext的代码,
那么配置类重复被加载了, Spring容器也重复被创建了,不能每次想从容器中获得一个Bean都得先创建一次容器,这样肯定是不允许。
所以,我们现在的诉求很简单,如下:

  • ApplicationContext创建一次,配置类加载一次;
  • 最好web服务器启动时,就执行第1步操作,后续直接从容器中获取Bean使用即可;
  • ApplicationContext的引用需要在web层任何位置都可以获取到。

针对以上诉求我们给出解决思路,如下:

  • 在ServletContextListener的contextInitialized方法中执行ApplicationContext的创建。或在Servlet的init 方法中执行ApplicationContext的创建,并给Servlet的load-on-startup属性一个数字值,确保服务器启动Servlet就创建;
  • 将创建好的ApplicationContext存储到ServletContext域中,这样整个web层任何位置就都可以获取到了

实现:

  1. 在main目录下,新增webapp/WEB-INF/web.xml目录及文件
<?xml version="1.0" encoding="UTF8"?>
<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_3_1.xsd"
  version="3.1">
</web-app>
  1. 新建一个监听器ContextLoaderListener,用于创建applicationContext,并存入ServletContext域中
public class ContextLoaderListener implements ServletContextListener {
    @Override
    // 服务器启动,ServletContext创建,该方法被执行1次
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ContextLoaderListener init ....");
        // 1. 创建spring容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 2. 将容器存储到servletContext域中
        sce.getServletContext().setAttribute("applicationContext",applicationContext);
    }
}
  1. 修改AccountServlet类中applicationContext的获取方式,改为从ServletContext域中获取
package com.mem.web;

import com.mem.config.AccountConfig;
import com.mem.service.AccountService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import javax.servlet.*;
import javax.servlet.http.*;
import javax.servlet.annotation.*;
import java.io.IOException;

@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // web层调用service层
        // 从ServletContext域中获取applicationContext对象
        ApplicationContext applicationContext =
        (ApplicationContext) request.getServletContext().getAttribute("applicationContext");
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");
        accountService.transferMoney("tom","lucy",500);
        System.out.println("转账操作成功####");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}
  1. 将新建的监听器配置到web.xml中
<!--配置Listener-->
<listener>
    <listener-class>com.mem.listener.ContextLoaderListener</listener-class>
</listener>
  1. 测试,结果同上

上述方案问题描述:
在ContextLoaderListener类中,配置文件的名称和容器的名称都写死了,不太友好;
解决方法:
利用常量进行解耦

改进:

  1. 在web.xml文件中,新增全局参数
<!--定义全局参数-->
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:applicationContext.xml</param-value>
</context-param>
  1. 修改ContextLoaderListener中的代码
public class ContextLoaderListener implements ServletContextListener {
    private String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";
    @Override
    // 服务器启动,ServletContext创建,该方法被执行1次
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println("ContextLoaderListener init ....");
        ServletContext servletContext = sce.getServletContext();
        // 0. 获取contextConfigLocation配置文件的名称
        String contextConfigLocation = servletContext.getInitParameter(CONTEXT_CONFIG_LOCATION);
        contextConfigLocation = contextConfigLocation.substring("classpath:".length());
        // 1. 创建spring容器
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext(contextConfigLocation);
        // 2. 将容器存储到servletContext域中
        servletContext.setAttribute("applicationContext",applicationContext);
    }
}
  1. 新建一个工具类WebApplicationContextUtils获取applicationContext对象
public class WebApplicationContextUtils {
    public static ApplicationContext getWebApplicationContext(ServletContext servletContext){
        ApplicationContext applicationContext = (ApplicationContext) servletContext.getAttribute("applicationContext");
        return applicationContext;
    }
}
  1. 修改AccountServlet类中applicationContext的获取方式
@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // web层调用service层,从getWebApplicationContext()方法中获取applicationContext对象
        ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");
        accountService.transferMoney("tom","lucy",500);
        System.out.println("转账操作成功####");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}

1.3 Spring的web开发组件spring-web

到此,就将一开始的诉求都解决了,当然我们能想到的Spring 框架自然也会想到,Spring其实已经为我们定义 好了一个ContextLoaderListener,使用方式跟我们上面自己定义的大体一样,但是功能要比我们强百倍,所以 ,遵循Spring “拿来主义” 的精神,我们直接使用Spring提供的就可以了,开发如下:

  1. 先导入Spring-web的坐标:
<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>5.3.9</version>
</dependency>
  1. 在web.xml中去配置ContextLoaderListener,并指定配置文件的位置
<!--自定义的Listener,用于创建applicationContext容器并存入到servletContext域中-->
<!--    <listener>-->
<!--        <listener-class>com.mem.listener.ContextLoaderListener</listener-class>-->
<!--    </listener>-->
<!--官方提供的-->
<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
  1. 在Servlet中直接使用,改成官方的WebApplicationContextUtils
import org.springframework.web.context.support.WebApplicationContextUtils;

@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        // web层调用service层
        ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(request.getServletContext());
        AccountService accountService = (AccountService) applicationContext.getBean("accountService");
        accountService.transferMoney("tom","lucy",500);
        System.out.println("转账操作成功####");
    }

    @Override
    protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {

    }
}
  1. 测试,结果同上,容器名:XmlWebApplicationContext

如果核心配置类使用的是注解形式的,那么Spring容器是AnnotationConfigWebApplicationContext,如下配 置方式

    <!--配置类的方式(注解)-->
    <context-param>
        <param-name>contextClass</param-name>
        <param-value>com.mem.web.MyAnnotationConfigWebApplicationContext</param-value>
    </context-param>

    <!--配置文件的方式-->
<!--    <context-param>-->
<!--        <param-name>contextConfigLocation</param-name>-->
<!--        <param-value>classpath:applicationContext.xml</param-value>-->
<!--    </context-param>-->

两个配置方式都打开,按配置类的方式创建容器,容器名是 MyAnnotationConfigWebApplicationContext
原因:在下面源码分析中,
determineContextClass方法里面,根据是否含有contextClass变量而决定的初始化哪个容器。
String contextClassName = servletContext.getInitParameter(“contextClass”);

public class MyAnnotationConfigWebApplicationContext extends AnnotationConfigWebApplicationContext {
    public MyAnnotationConfigWebApplicationContext(){
        super();
        // 注册配置类到容器
        this.register(AccountConfig.class);
    }
}

测试:结果同上,容器名为:MyAnnotationConfigWebApplicationContext

源码分析:
目的:如何决定是xml配置文件方式还是用配置类方式创建applicationContext
入口:org.springframework.web.context.ContextLoaderListener类的contextInitialized方法

public class ContextLoaderListener extends ContextLoader implements ServletContextListener {
    public ContextLoaderListener() {
    }

    public ContextLoaderListener(WebApplicationContext context) {
        super(context);
    }

    public void contextInitialized(ServletContextEvent event) {
        // 调用父类中的initWebApplicationContext方法,所以查看ContextLoader类
        this.initWebApplicationContext(event.getServletContext());
    }

    public void contextDestroyed(ServletContextEvent event) {
        this.closeWebApplicationContext(event.getServletContext());
        ContextCleanupListener.cleanupAttributes(event.getServletContext());
    }
}
public class ContextLoader {
	public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {
        // 调用本类的createWebApplicationContext方法
    	this.context = this.createWebApplicationContext(servletContext);
    }
    protected WebApplicationContext createWebApplicationContext(ServletContext sc) {
        // 调用本类的determineContextClass方法,获取字节码对象,
        Class<?> contextClass = this.determineContextClass(sc);
        if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) {
            throw new ApplicationContextException("Custom context class [" + contextClass.getName() + "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]");
        } else {
            return (ConfigurableWebApplicationContext)BeanUtils.instantiateClass(contextClass);
        }
    }
    protected Class<?> determineContextClass(ServletContext servletContext) {
        // 判断 web.xml 文件中有没有contextClass参数,
        // 如果有,则按配置类的方式(注解) 容器名:AnnotationConfigWebApplicationContext
        // 如果没有,则按配置文件的方式 容器名:XmlWebApplicationContext
        String contextClassName = servletContext.getInitParameter("contextClass");
        if (contextClassName != null) {
            return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader());
        } else {
            contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName());
            return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader());
            
        }
    }
}

查看WebApplicationContext的实现类:
image.png

02、web层MVC框架思想与设计思路

Java程序员在开发一般都是MVC+三层架构,MVC是web开发模式,传统的Javaweb技术栈实现的MVC如下
image.png
原始Javaweb开发中,Servlet充当Controller的角色,Jsp充当View角色,JavaBean充当模型角色,后期Ajax异 步流行后,在加上现在前后端分离开发模式成熟后,View就被原始Html+Vue替代。原始Javaweb开发中, Service充当Controller有很多弊端,显而易见的有如下几个:
image.png

负责共有行为的Servlet称之为前端控制器,负责业务行为的JavaBean称之为控制器Controller
image.png
分析前端控制器基本功能如下:

  1. 具备可以映射到业务Bean的能力
  2. 具备可以解析请求参数、封装实体 等共有功能
  3. 具备响应视图及响应其他数据的功能

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1185781.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Lec13 Sleep Wake up

进程切换的流程 一个进程出于某种原因想要进入休眠状态&#xff0c;比如说出让CPU或者等待数据&#xff0c;它会先获取自己的锁&#xff1b;之后进程将自己的状态从RUNNING设置为RUNNABLE&#xff1b;之后进程调用switch函数&#xff0c;其实是调用sched函数在sched函数中再调…

Sealos 私有云正式发布,三倍性能 1/5 成本

马斯克将推特下云后可以节省 60% 成本&#xff0c;不代表你可以。 但是有了 Sealos 之后&#xff0c;你真的可以&#xff01; Sealos 私有云正式发布&#xff0c;详情地址&#xff1a;https://sealos.run/zh-Hans/self-hosting 原文链接&#xff1a;https://forum.laf.run/d/…

使用python操作数据库

一、背景 当前由于多个脚本涉及到账户登陆&#xff0c;同时账号密码存在不断修改的情况&#xff0c;为避免多处修改&#xff0c;现计划将账户信息放到数据库中&#xff0c;后续所有账号信息均从数据库中去读取。 二、本文主要结构 创建测试账户 库表中插入记录 三、代码 1、创建…

【方法】如何取消PDF文件的“打开密码”?

我们知道&#xff0c;PDF文件可以设置“打开密码”&#xff0c;保护文件不被随意打开&#xff0c;那如果后续不需要了&#xff0c;要怎么取消“打开密码”呢&#xff1f;不清楚的小伙伴可以试试小编分享的3种方法&#xff01; 方法1&#xff1a;使用PDF编辑器 PDF编辑器不仅可…

程序员远程兼职接单是骗局?索嘎~

“你还在线上接单&#xff1f;”“没被坑够&#xff1f;”“你不知道这些平台有多坑&#xff1f;”...... 相信无论是有无经验的兄弟都看到过这样的句子。那么事实果真如此吗&#xff1f;今天&#xff0c;带大家来了解一波。 常见的吐槽和问题是&#xff0c;一上来平台还没出力…

域名解析DNS:如何查询txt类型的解析记录

前言 略 查询txt类型的解析记录 使用 nslookup 命令查询。 示例&#xff1a; cmd> nslookup -qttxt _acme-challenge.mydomain.com 服务器: UnKnown Address: fe80::1非权威应答: _acme-challenge.mydomain.com text "_unitrust-dcv2311071423492fmnwb1w…

error: (-215:Assertion failed) !_src.empty() in function ‘cv::cvtColor‘

在给图片去除水印的时候&#xff0c;发现使用cv2打开图片的时候报错了&#xff0c;处理过程中没有找到图片对象&#xff0c;经过分析发现是图片的路径问题导致的&#xff0c;修改路径后未报错

网络安全(黑客)-零基础小白高效自学

1.网络安全是什么 网络安全可以基于攻击和防御视角来分类&#xff0c;我们经常听到的 “红队”、“渗透测试” 等就是研究攻击技术&#xff0c;而“蓝队”、“安全运营”、“安全运维”则研究防御技术。 2.网络安全市场 一、是市场需求量高&#xff1b; 二、则是发展相对成熟…

Yolov8部署——vs2019生成解决方案问题

Yolov8部署——vs2019生成解决方案问题 &#xff08;1&#xff09;Yolov8部署 最近开始在win10上部署Yolov8,并用tensorrt加速&#xff0c;从此贴开始记录后续遇到的部署问题。 &#xff08;2&#xff09;vs2019生成解决方案问题 报错如下图&#xff1a; NvInfer.h是Tenso…

【Unity】简单案例脚本实现 | 鼠标观察/键盘控制移动飞行/行走/碰撞检测

《Unity5实战-使用C#和Unity开发多平台游戏》第二章-构建一个让你置身3D空间的演示 鼠标观察/键盘控制移动飞行/行走/碰撞检测 Unity版本&#xff1a;2019.4.23f1c1 注意脚本名称和组件添加&#xff0c;不在文章中一一强调场景模型都是在资源商店选择的免费下载&#xff08;选…

【管理工具】CMAK安装和使用(kafka-manager)

文章目录 前言一、安装和启动1.1 安装CMAK1.2 启动cmak 二、使用CMAK2.1 添加kafka集群2.2 topic 概述2.3 broker概述2.4 其他操作 前言 一、安装和启动 1.1 安装CMAK Java环境&#xff1a;需要jdk11 $ java -version java version "11.0.5" 2019-10-15 LTS Java…

NIO 笔记(一)基础内容

【笔记来自&#xff1a;it白马】 NIO基础 **注意&#xff1a;**推荐完成JavaSE篇、JavaWeb篇的学习再开启这一部分的学习&#xff0c;如果在这之前完成了JVM篇&#xff0c;那么看起来就会比较轻松了。 在JavaSE的学习中&#xff0c;我们了解了如何使用IO进行数据传输&#xf…

写论文中的心得记录

SCI论文从每一个细节开始 说明&#xff1a;本文章旨在记录写论文过程中常用的一些写作技巧和写作公式的编写等&#xff0c;意在提高写作效率。 1、如何给数学公式编号&#xff1f; 方法1&#xff1a;简单粗暴的方式 回车后的效果如下&#xff1a; 参考这篇文章&#xff1a…

安卓TextView调整下划线颜色、与文本底部的距离

需要自定义view来实现&#xff0c;因为安卓的TextView没有提供设置颜色与间距的方法 实现效果 下划线颜色与文字不一样下划线距离文字底部的边界可调整但只能标记同一行的文字&#xff0c;也就是说下划线的start与end在同一行。还没多行的需求&#xff0c;有时间再说 packa…

RuntimeError_ Found dtype Long but expected Float

在跑模型的时候&#xff0c;出现了报错&#xff1a; RuntimeError: Found dtype Long but expected Float报错是发生在BCE loss 的部分&#xff0c;因为crossentropyloss要求batch_label必须为torch.float类型 所以需要修改一下标签&#xff0c;转为float格式就好了&#xff1…

setpci设定PCIe寄存器

PCIe寄存器信息读取、分析、配置 1.获取bdf &#xff08;bus:device.function&#xff09;地址2.根据bdf地址抓取pci信息3.结合PCIe Spec对读到的信息进行分析使用setpci修改 1.获取bdf &#xff08;bus:device.function&#xff09;地址 在Linux系统下使用命令&#xff1a; l…

web3 React dapp项目通过事件从区块链中拿到 已取消 已完成 和所有的订单数据 并存入redux中

好 上文web3通过antd 在React dapp中构建订单组件基本结构我们算是把一个基本的订单组件展示做出来了 然后 我们继续 起一下环境先 ganache 终端运行 ganache -dMetaMask 登录一下 然后 打开项目 发布一下合约 truffle migrate --reset然后 运行一下 测试脚本 转入交易所 E…

云尘靶场 --JIS-CTF-VulnUpload

重新下vpn连接的文件 还是fscan扫 访问一下13 到了/login.php 随便弱口令试试 好吧没成功&#xff0c;那目录扫描一下 那就先看一下robots.txt了 flag有点快的 抱着试一试的态度看一下admin_area 没想到源代码里面居然有 这么这么多 本来还以为密码要去爆破的&#xff0…

webrtc推拉流 srs报错:DTLS_HANG DTLS: > Hang, done=0, version=-1, arq=0

执行了./objs/srs -c conf/rtc.conf 打开了srs的推拉流网页&#xff1a; 推流 拉流 srs报错如下&#xff1a; [2023-11-08 21:55:23.489][Warn][44992][8xvf4d62][104][DTLS_HANG] DTLS: Hang, done0, version-1, arq0 观看srs日志&#xff0c;在sdp offer&#xff0c;answer…

Qlik Sense : Fetching data with Qlik Web Connectors

目录 Connecting to data sources Opening a connector Connecting to a data source Authenticating the connector Defining table parameters Using standard mode or legacy mode Standard mode Connector overview Using multi-line input parameters to fetch da…