小黑子—spring:第四章 事务控制与整合web环境

news2025/7/26 8:15:23

spring入门4.0

  • 四 小黑子基于AOP的声明式事务控制
    • 1. Spring事务编程概述
    • 2. 搭建测试环境
    • 3. 基于XML声明式事务控制
      • 3.1 入门操作
      • 3.2 声明式事务控制详解
    • 4. 基于注解的声明式事务控制
  • 五 小黑子用spring进行web环境整合
    • 5. JavaWeb三大组件作用及其特点
    • 6. Spring整合web环境的思路及实现
      • 6.1 模拟ContextLoaderListener
    • 7. Spring的Web开发组件spring-web
    • web层MVC框架思想与设计思路

四 小黑子基于AOP的声明式事务控制

1. Spring事务编程概述

事务是开发中必不可少的东西,使用JDBC开发时,我们使用connnection对事务进行控制,使用MyBatis时,我们使用SqlSession对事务进行控制,缺点显而易见,当我们切换数据库访问技术时,事务控制的方式总会变化,Spring 就将这些技术基础上,提供了统一的控制事务的接口。Spring的事务分为:编程式事务控制和声明式事务控制

事务控制方式解释
编程式事务控制Spring提供了事务控制的类和方法,使用编码的方式对业务代码进行事务控制,事务控制代码和业务操作代码耦合到了一起,开发中不使用
声明式事务控制Spring将事务控制的代码封装,对外提供了Xml和注解配置方式,通过配置的方式完成事务的控制,可以

Spring事务编程相关的类主要有如下三个:

事务控制相关类解释
平台事务管理器 PlatformTransactionManager是一个接口标准,实现类都具备事务提交、回滚和获得事务对象的功能,不同持久层框架可能会有不同实现方案
事务定义 TransactionDefinition封装事务的隔离级别、传播行为、过期时间等属性信息
事务状态 TransactionStatus存储当前事务的状态信息,如果事务是否提交、是否回滚、是否有回滚点等

虽然编程式事务控制我们不学习,但是编程式事务控制对应的这些类我们需要了解一下,因为我们在通过配置的方式进行声明式事务控制时也会看到这些类的影子

2. 搭建测试环境

搭建一个转账的环境,dao层一个转出钱的方法,一个转入钱的方法service层一个转账业务方法,内部分别调
用dao层转出钱和转入钱的方法,准备工作如下:

  • 数据库准备一个账户表tb_account;
    在这里插入图片描述

  • dao层准备一个AccountMapper,包括incrMoney和decrMoney两个方法;

public interface AccountMapper {
    /*
     *   加钱
     * */
    @Update("update tb_account set money = money+#{money} where account_name = #{accountName}")
    public void incrMoney(@Param("accountName") String accountName,@Param("money") Integer money);
    /*
     *   减钱
     * */
    @Update("update tb_account set money = money-#{money} where account_name = #{accountName}")
    public void decrMoney(@Param("accountName") String accountName,@Param("money") Integer money);
    
}
  • service层准备一个transferMoney方法,分别调用incrMoney和decrMoney方法;
package com.itheima.service;

public interface AccountService {
    void transferMoney(String outAccount,String inAccount,Integer money);
}

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;

    @Override
    public void transferMoney(String outAccount, String inAccount, Integer money) {
        accountMapper.decrMoney(outAccount,money);
        accountMapper.incrMoney(inAccount,money);
    }
}

  • 在applicationContext文件中进行Bean的管理配置;
jdbc.driver = com.mysql.cj.jdbc.Driver
jdbc.url = jdbc:mysql://localhost:3306/mybatis
jdbc.username = root
jdbc.password = root
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!--    组件扫描-->
    <context:component-scan base-package="com.itheima"/>
<!--    加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--    配置数据源信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--    配置SqlSessionFactoryBean,作用将SqlSessionFactory储存导spring容器中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    作用扫描指定的包,产生Mapper对象储存导spring容器当中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.mapper"></property>
    </bean>

</beans>
  • 测试正常转账与异常转账
public class AccountTest {
    @Test
    public void accountTest1(){
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = app.getBean(AccountService.class);
        accountService.transferMoney("tom","lucy",500);
    }
}

在这里插入图片描述
在这里插入图片描述

3. 基于XML声明式事务控制

3.1 入门操作

结合上面我们学习的AOP的技术,很容易就可以想到,可以使用AOP对Service的方法进行事务的增强。

  • 目标类:自定义的AccountServicelmpl,内部的方法是切点
  • 通知类: Spring提供的,通知方法已经定义好,只需要配置即可

我们分析:

  • 通知类是Spring提供的,需要导入Spring事务的相关的坐标;
  • 配置目标类AccountServicelmpl;
  • 使用advisor标签配置切面。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
<!--    组件扫描-->
    <context:component-scan base-package="com.itheima"/>
<!--    加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--    配置数据源信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--    配置SqlSessionFactoryBean,作用将SqlSessionFactory储存导spring容器中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    作用扫描指定的包,产生Mapper对象储存导spring容器当中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.mapper"></property>
    </bean>


<!--    配置平台事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    配置spring提供好的advice-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>

<!--    事务增强的aop-->
    <aop:config>
<!--        配置切点表达式-->
        <aop:pointcut id="txPonitcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<!--        配置植入关系 通知advice-ref引入spring提供好的-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPonitcut"></aop:advisor>
    </aop:config>
</beans>

3.2 声明式事务控制详解

<!--事务增强的aop-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
   <tx:attributes>
       <!--
           配置不同的方法的事务属性
           name:方法名称   *代表通配符
           isolaton:控制事务的隔离级别
           timeout:超时时间 默认-1 单位是秒
                例:设置为3 则若3秒内事务没有提交则系统回自动返回
           read-only:是否只读  查询操作设置为只读,其他的为false
           propagation:事务的传播行为 解决业务方法调用业务方法(事务嵌套问题)
       -->
       <tx:method name="*" isolation="READ_COMMITTED" propagation="" timeout="3" read-only="false"/>
       <tx:method name="registAccount"/>
       <!--在一般开发中都是从上往下匹配,有匹配成功的就不会往下匹配了-->
       <tx:method name="add*"/>
       <tx:method name="update*"/>
       <tx:method name="delete*"/>
       <tx:method name="select*"/>
       <tx:method name="*"/>
   </tx:attributes>
</tx:advice>
  • isolation属性:指定事务的隔离级别,事务并发存在三大问题:脏读、不可重复读、幻读/虚读。可以通过设置事务的隔离级别来保证并发问题的出现,常用的是READ_COMMITTEDREPEATABLE_READ
isolation属性解释
DEFAULT默认隔离级别,取决于当前数据库隔离级别,例如MySQL默认隔离级别是REPEATABLE_READ
READ_UNCOMMITTEDA事务可以读取到B事务尚未提交的事务记录,不能解决任何并发问题,安全性最低,性能最高
READ_COMMITTEDA事务只能读取到其他事务已经提交的记录,不能读取到未提交的记录。可以解决脏读问题,但是不能解决不可重复读和幻读
REPEATABLE_READA事务多次从数据库读取某条记录结果一致,可以解决不可重复读,不可以解决幻读
SERIALIZABLE串行化,可以解决任何并发问题,安全性最高,但是性能最低
  • read-only属性:设置当前的只读状态,如果是查询则设置为true,可以提高查询性能,如果是更新(增删改)操作则设置为false

    <!--一般查询相关的业务操作都会设置只读模式-->
    <tx:method name="select*" read-only="true"/>
    <tx:method name="find*" read-only="false"/>
    
  • timeout属性:设置事务执行的超时时间,单位是秒,如果超过该时间限制但事务还没有完成,则自动回滚事务,不在继续执行。默认值是-1,即没有超时时间限制

    <!--设置查询操作的超时时间是3秒-->
    <tx:method name="select*" read-only="true" timeout="3" />
    
  • propagation属性:设置事务的传播行为,主要解决是A方法调用B方法时,事务的传播方式问题的,例如:使用单方的事务,还是A和B都使用自己的事务等。事务的传播行为有如下七种属性值可配置:

事务传播行为解释
REQUIRED (默认值)A调用B,B需要事务,如果A有事务B就加入A的事务中,如果A没有事务,B就自己创建一个事务
REQUIRED_NEWA调用B,B需要新事务,如果A有事务就挂起,B自己创建一个新的事务
SUPPORTSA调用B,B有无事务无所谓,A有事务就加入到A事务中,A无事务B就以非事务方式执行
NOT_SUPPORTSA调用B,B以无事务方式执行,A如有事务则挂起
NEVERA调用B,B以无事务方式执行,A如有事务则抛出异常
MANDATORYA调用B,B要加入A的事务中,如果A无事务就抛出异常
NESTEDA调用B, B创建一个新事务,A有事务就作为嵌套事务存在,A没事务就以创建的新事务执行

声明式事务控制原理刨析

4. 基于注解的声明式事务控制

其中,name属性名称指定哪个方法要进行哪些事务的属性配置,此处
有尽要分的是切点表达式指定的方法与此处指定的方法的区别?切点表达式,是过滤哪些方法可以进行事务增强;事务属性信息的name,是指定哪个方法要进行哪些事务属性的配置
在这里插入图片描述

@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountMapper accountMapper;

    @Override
    //那个方法需要加事务,就用注解@Transactional
    @Transactional(isolation = Isolation.READ_COMMITTED,propagation = Propagation.REQUIRED)
    public void transferMoney(String outAccount, String inAccount, Integer money) {
        accountMapper.decrMoney(outAccount,money);
        int i= 1/0;
        accountMapper.incrMoney(inAccount,money);
    }

    public void registAccount(){}

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
<!--    组件扫描-->
    <context:component-scan base-package="com.itheima"/>
<!--    加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--    配置数据源信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--    配置SqlSessionFactoryBean,作用将SqlSessionFactory储存导spring容器中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    作用扫描指定的包,产生Mapper对象储存导spring容器当中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.mapper"></property>
    </bean>


<!--    配置平台事务管理器-->
<!--    id=transactionManager如果不写的这个名字用自定义话,下面tx就必须要用到transaction-manager属性来接收自定义名-->
    <bean id="transactionManager1" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    
    <tx:annotation-driven transaction-manager="transactionManager1"/>
</beans>
  • 对以上采用全注解的方式进行修改:
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@MapperScan("com.itheima.mapper")
@EnableTransactionManagement//相当于<tx:annotation-driven transaction-manager="transactionManager"/>
public class SpringConfig {

    @Bean
    public DataSource dataSource(
            @Value("${jdbc.driver}") String driver,
            @Value("${jdbc.url}") String url,
            @Value("${jdbc.username}") String username,
            @Value("${jdbc.password}") String password
    ){
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);

        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public DataSourceTransactionManager transactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

五 小黑子用spring进行web环境整合

5. JavaWeb三大组件作用及其特点

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

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

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

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

@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //web层调用service层,获得AccountService,accountService存在applicationContext中
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        AccountService accountService = app.getBean(AccountService.class);
        accountService.transferMoney("tom","lucy",500);
    }

}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd
       http://www.springframework.org/schema/context
       https://www.springframework.org/schema/context/spring-context.xsd">
<!--    组件扫描-->
    <context:component-scan base-package="com.itheima"/>
<!--    加载properties文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--    配置数据源信息-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"></property>
        <property name="url" value="${jdbc.url}"></property>
        <property name="username" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    <!--    配置SqlSessionFactoryBean,作用将SqlSessionFactory储存导spring容器中-->
    <bean class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--    作用扫描指定的包,产生Mapper对象储存导spring容器当中-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.itheima.mapper"></property>
    </bean>


<!--    配置平台事务管理器-->
<!--    id=transactionManager如果不写的这个名字用自定义话,下面tx就必须要用到transaction-manager属性来接收自定义名-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

<!--    配置spring提供好的advice-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
<!--    事务增强的aop-->
    <aop:config>
<!--        配置切点表达式-->
        <aop:pointcut id="txPonitcut" expression="execution(* com.itheima.service.impl.*.*(..))"/>
<!--        配置植入关系 通知advice-ref引入spring提供好的-->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPonitcut"></aop:advisor>
    </aop:config>

<!--    事务的自动代理(注解驱动)-->
<!--    <tx:annotation-driven transaction-manager="transactionManager"/>-->
</beans>

编译的时候把config.SpringConfig里面的spring注解给去掉,然后用的是IDEA的Maven插件运行tomcat7
在这里插入图片描述
在这里插入图片描述

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

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

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

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

6.1 模拟ContextLoaderListener

Listener的代码

public class ContextLoaderListener implements ServletContextListener {
   private  String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    @Override
    public void contextInitialized(ServletContextEvent sce) {
        System.out.println();
        ServletContext servletContext = sce.getServletContext();
        //0. 获取ContextLoaderListener域中
        String contextConfigLocation = servletContext.getInitParameter(CONTEXT_CONFIG_LOCATION);
        //1.创建spring容器
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.将容器储存到servletContext域中
        servletContext.setAttribute("applicationContext",app);
    }
}

WebApplicationContextUtils

package com.itheima.web;

import org.springframework.context.ApplicationContext;

import javax.servlet.ServletContext;

public class WebApplicationContextUtils {

    public static ApplicationContext getWebApplicationContext(ServletContext servletContext){
        ApplicationContext applicationContext =(ApplicationContext) servletContext.getAttribute("applicationContext");
        return applicationContext;
    }
}

Servlet/web层的代码

@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //web层调用service层,获得AccountService,accountService存在applicationContext中
        //从servletContext域中去applicationContext
//        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ServletContext servletContext = request.getServletContext();
//        ApplicationContext app = (ApplicationContext) servletContext.getAttribute("applicationContext");
        ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        AccountService accountService = app.getBean(AccountService.class);
        accountService.transferMoney("tom","lucy",500);
    }

}

webapp.WEB-INF下的web.xml:

<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
         version="4.0">
    <!--定义全局参数-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!--配置Listener-->
    <listener>
        <listener-class>com.itheima.listener.ContextLoaderListener</listener-class>
<!--        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>-->
    </listener>
</web-app>

在这里插入图片描述

7. Spring的Web开发组件spring-web

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

先导入Spring-web的坐标:

<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-web</artifactId>
	<version>5.3.7</version>
</dependency>

web.xml

<?xml version="1.0" encoding="UTF-8"?>
<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_4_0.xsd"
         version="4.0">
    <!--定义全局参数-->
    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:applicationContext.xml</param-value>
    </context-param>

    <!--配置Listener-->
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
    </listener>
</web-app>
@WebServlet(urlPatterns = "/accountServlet")
public class AccountServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        //web层调用service层,获得AccountService,accountService存在applicationContext中
        //从servletContext域中去applicationContext
//        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        ServletContext servletContext = request.getServletContext();
//        ApplicationContext app = (ApplicationContext) servletContext.getAttribute("applicationContext");
        ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
        AccountService accountService = app.getBean(AccountService.class);
        accountService.transferMoney("tom","lucy",500);
    }

}

扩展-核心配置类方式如何配置

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

Java程序员在开发一般都是MVC+三层架构,MVC是web开发模式,传统的Javaweb技术栈实现的MVC如下
在这里插入图片描述

原始Javaweb开发中,Servlet充当Controller的角色,Jsp充当View角色,JavaBean充当模型角色,后期Ajax异步流行后,在加上现在前后端分离开发模式成熟后,View就被原始Html+Vue替代。原始Javaweb开发中,Service充当Controller有很多弊端,显而易见的有如下几个:

Servlet作为Controller的问题解决思路和方案
每个业务功能请求都对应一个Servlet根据业务模块去划分Controller
每个Servlet的业务操作太繁琐将通用的行为,功能进行抽取封装
Servlet获得Spring容器的组件只能通过客户端代码去获取,不能优雅的整合通过spring的扩展点,去封装一个框架,从原有的Servlet完全接手过来web层的业务

负责共有行为的Servlet称之为前端控制器,负责业务行为的JavaBean称之为控制器Controller
在这里插入图片描述

分析前端控制器基本功能如下:

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

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

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

相关文章

Java作业一

编写程序实现如下界面效果&#xff1a; import java.util.Scanner;public class Test01 {public static void main(String[] args) {String name;Scanner input new Scanner(System.in);System.out.println("请输入您的姓名&#xff1a;");name input.nextLine();S…

算法学习打卡day39|动态规划:62.不同路径 、 63. 不同路径 II

62.不同路径 力扣题目链接 题目描述&#xff1a; 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish” &#xff09;。…

Python图像处理【15】基于非锐化掩码锐化图像

基于非锐化掩码锐化图像 0. 前言1. 使用 scikit-image filters 模块执行非锐化掩码2. 使用 PIL ImageFilter 模块执行非锐化掩码3. 使用 SimpleITK 执行拉普拉斯锐化4. 使用 OpenCV 实现非锐化掩码小结系列链接 0. 前言 非锐化滤波器是一个简单的锐化算子&#xff0c;通过从原…

ArcGIS制作土地利用现状图

一、现状图成果 土地利用现状图是规划制图中一种基本的图件,基于ArcGIS制作的现状图如下所示: 二、现状图制作 1. 数据准备 (1)土地利用数据 (2)地形图 (3)影像地图 (4)其他数据 2. 符号化 加载现状地块数据。 双击地块图层,切换到符号系统,选择类别下的唯…

kubernetes-控制器

目录 一、replicaset 二、deployment 1、版本迭代 2、回滚 3、滚动更新策略 4、暂停与恢复 三、daemonset 四、statefulset 五、job 六、cronjob 一、replicaset ReplicaSet用于保证指定数量的 Pod 副本一直运行 vim rs-example.ymlapiVersion: apps/v1 kind: Replic…

Chat2DB Web版 服务器端安装部署启动流程

目录 前言安装部署过程下载安装包下载JDK17启动登录 可能出现的报错启动报错Java版本低flyway的bean创建失败登录失败无法执行二进制文件 前言 Chat2DB是款数据库连接工具&#xff0c;主要就是使用JDBC做数据库连接&#xff0c;外加多对接了几个聊天AI网站&#xff0c;把数据库…

敏捷开发框架Scrum-概述

如果你是一个程序员&#xff0c;可能会觉得这是个程序开发框架。我开始也是这样认为的。后来学习了PMP、敏捷后&#xff0c;才知道Scrum是一个用于管理团队工作的敏捷框架。Scrum可以理解成一个团队在一段时间里完成工作的方式。这里的一段时间通常很短&#xff0c;一到两周&am…

【好书推荐】ChatGPT 驱动软件开发:AI 在软件研发全流程中的革新与实践

前言 计算机技术的发展和互联网的普及&#xff0c;使信息处理和传输变得更加高效&#xff0c;极大地改变了金融、商业、教育、娱乐等领域的运作方式。数据分析、人工智能和云计算等新兴技术&#xff0c;也在不断地影响和改变着各个行业。 如今&#xff0c;我们正在见证人工智能…

vue 中methods方法为啥不能用箭头函数

vue用了很久了&#xff0c;但是又返回来看这个神秘的问题 这里理解不了&#xff1a;问题一&#xff1a;为啥他的上层就是script了呀&#xff0c;箭头函数没有作用域&#xff1f; 问题二&#xff1a;箭头函数没有作用域&#xff0c;但是外层的methods&#xff0c;{} 花括号不是…

518抽奖软件,可从Excel~Word~Pdf~网页导入名单

518抽奖软件简介 518抽奖软件&#xff0c;518我要发&#xff0c;超好用的年会抽奖软件&#xff0c;简约设计风格。 包含文字号码抽奖、照片抽奖两种模式&#xff0c;支持姓名抽奖、号码抽奖、数字抽奖、照片抽奖。(www.518cj.net) 从Excel、WPS表格导入 整列&#xff1a; 用鼠…

Linux C语言进阶-D1二维数组

数据类型 数组名[常量表达式][常量表达式] 定义方式&#xff1a;声明时列数不能省略&#xff0c;行数可以 由于内存是一维的&#xff0c;所以数组时顺序存放的&#xff1a;按行顺序排放 行名a[0]、a[1]、a[2]可以称为一维数组名 验证行名就是一维数组名&#xff1a; 1、数组名…

0基础学习PyFlink——时间滑动窗口(Sliding Time Windows)

在《0基础学习PyFlink——时间滚动窗口(Tumbling Time Windows)》我们介绍了不会有重复数据的时间滚动窗口。本节我们将介绍存在重复计算数据的时间滑动窗口。 关于滑动窗口&#xff0c;可以先看下《0基础学习PyFlink——个数滑动窗口&#xff08;Sliding Count Windows&#x…

【热带气旋】基本介绍:定义、标准、结构等

热带气旋基本介绍 热带气旋&#xff08;Tropical Cyclone, TC&#xff09;1 热带气旋定义2 热带气旋标准2.1 热带低压&#xff08;Tropical Depression&#xff09;2.2 热带风暴&#xff08;Tropical storm&#xff09;2.3 强热带风暴&#xff08;Severe tropical storm&#x…

2023 DevFest 开发者大会 | 精彩进行时!

DevFest 是由全球各地的谷歌开发者社区 (Google Developer Groups&#xff0c;GDG) 主导的&#xff0c;为期数月的系列 Google 技术交流活动。DevFest 为参与者提供一个与 Google 员工、GDE 谷歌开发者专家、社区 KOL、行业开发者和问题解决者面对面交流的机会。 今年&#xff…

[SUCTF 2019]EasySQL 1

题目环境&#xff1a; 把你的旗子给我&#xff0c;我会告诉你旗子是不是对的。 判断注入类型1回显结果 不是字符型SQL注入 1回显结果 数字型SQL注入 查所有数据库,采用堆叠注入1;show databases;查看所有数据表1;show tables;尝试爆Flag数据表的字段1;show columns from Flag; …

机器人仿真-gazebo学习笔记(3)URDF和机器人模型

1.URDF简介 URDF(统一机器人麦哦书格式)是ROS中的重要机器人模型描述格式&#xff0c;ROS提供了URDF文件的c解析器&#xff0c;可以解析URDF文件中使用XML格式的机器人模型。 urdf - ROS Wiki 自己查阅ros官方对URDF的介绍其实会强于大部分网上流传的文章。 1.URDF文件常用的…

电脑硬件坏了,如何维修?

在电子设备日益普及的今天&#xff0c;电脑已成为很多人生活和工作中不可或缺的工具&#xff0c;然而在使用过程中很容易遇见电脑故障之类的问题&#xff0c;这些问题十有八九来自硬件&#xff0c;那么针对电脑硬件问题&#xff0c;该如何维修&#xff1f; 一般来说&#xff0c…

深入探究Vue.js生命周期及其应用场景

当谈到Vue.js的生命周期时&#xff0c;我们指的是组件在创建、更新和销毁过程中发生的一系列事件。了解Vue的生命周期对于开发人员来说是至关重要的&#xff0c;因为它们提供了一个机会来执行特定任务&#xff0c;并在不同的阶段处理组件。 Vue的生命周期可以分为八个不同的阶…

项目管理-挣值管理例题-使用SV进度偏差和CV成本偏差来判断进度和成本是否合适

基础概念介绍 CV和SV的计算公式 在财务分析中&#xff0c;常常会用到CV和SV这两个指标。CV是成本偏差&#xff0c;SV是进度偏差。它们的计算公式如下&#xff1a; CV EV - AC SV EV - PV 其中&#xff0c;EV是挣值&#xff0c;AC是实际成本&#xff0c;PV是计划价值。 …

Android Studio中配置Git

安装Git 在安装Android Studio之前&#xff0c;需要先安装Git。可以从Git官网下载并安装Git&#xff1a;https://git-scm.com/downloads 在Android Studio中配置Git 在Android Studio中&#xff0c;依次点击“File” -> “Settings”&#xff0c;在弹出的窗口中选择“Ver…