首页 > 其他分享 >spring 基础2

spring 基础2

时间:2022-12-28 09:34:01浏览次数:64  
标签:事务 spring void 配置 基础 class 通知 public

方便复习,转载于尚硅谷:https://www.yuque.com/tmfl/spring/of3mbz#3e4ec32a

5、Spring的AOP

5.1、AOP的相关概念

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。

AOP是OOP的延续,是软件开发的一个热点,也是Spring框架的一个重要内容,是函数式编程的一种衍生类型。

简单的说,就是把我们程序重复的代码抽取出来,在需要执行的时候,使用动态代理技术,在不修改源码的基础上,对我们的方法进行增强。

在Spring中,框架会根据目标是否实现了接口来决定采用哪种动态代理方式。

  • Joinpoint(连接点):所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

  • Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义。 (即被代理类中的所有方法都是连接点,但只有真正被代理增强的方法才是切入点)。

  • Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。通知的类型:前置通知、后置通知、异常通知、最终通知、环绕通知。

  • Introduction(引介):引介是一种特殊的通知,在不修改类代码的前提下, Introduction可为在运行期为类动态的添加一些方法或Field。

  • Target(目标对象):代理的目标对象。

  • Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入。

  • Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。

  • Aspect(切面):是切入点和通知(引介)的结合。

5.2、基于xml的aop配置示例

  1. 引入需要的pom依赖
<!-- spring核心包 -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<!-- 切入点表达式解析依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>
  1. 编写bean.xml配置文件(需要引入xmlns:aop约束)
<?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"
       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">

    <bean id="accountService" class="com.study.service.impl.AccountServiceImpl">
    </bean>

    <bean id="logger" class="com.study.util.Logger">
    </bean>

    <aop:config>
        <aop:aspect id="logAdvice" ref="logger" >
            <aop:before method="printLog" 
                        pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
            </aop:before>
        </aop:aspect>
    </aop:config>

</beans>

配置aop的步骤:

1. 被代理的类的bean:accountService
2. 通知的bean:logger
3. 使用<aon:config>标签表明开始aop的配置
4. 使用<aop:aspect>标签表明开始配置切面
	- id属性:给切面提供一个唯一的标识
	- ref属性:指定通知类bean的id
5. 在<aop:aspect>标签的内部使用对应标签来配置通知的类型
<aop:before>标签:配置前置通知
- method属性:用于指定Logger类中哪个方法是前置通知
- pointcut属性:用于指定切入点表达式,该表达式含义是对业务层哪些方法进行增强

切入点表达式的写法:

  • 关键字:execution(表达式)
  • 表达式:
    • 完整写法:访问修饰符 返回值 包名.包名.包名...包名.类名.方法名(参数列表)
      public void com.study.service.impl.AccountServiceImpl.SaveAccount()
    • 参数列表
      基本类型直接写名称:int
      引用类型写包名.类名: java.lang.String
      public void com.study.service.impl.AccountServiceImpl.SaveAccount(int)

全通配写法:* ...*(..)

  • 访问修饰符可以省略
    void com.study.service.impl.AccountServiceImpl.SaveAccount()
  • 返回值可以使用通配符* 表示任意类型返回值
  • com.study.service.impl.AccountServiceImpl.SaveAccount()
  • 包名可以使用通配符表示任意包。但是有几级包,就需要写几个
  • ....AccountServiceImpl.SaveAccount()
  • 包名可以使用..表示当前包及其子包
  • *..AccountServiceImpl.SaveAccount()
  • 类名可以使用通配符*表示任意类
  • ...SaveAccount()
  • 方法名可以使用通配符*表示任意方法
  • ...*()
  • 参数列表可以使用通配符*表示任意类型参数,但是必须有参数
  • ...()
  • 参数列表可以使用通配符..表示有无参数均可,有参数可以是任意类型
  • ...*(..)
  • 全通配写法:* ...*(..)

5.3、spring四种通知(不含环绕通知)的xml配置

四种通知对应的xml标签:

  • 前置通知:<aop:before>
    在切入点方法执行之前执行
  • 后置通知:<aop:after-returning>
    在切入点方法正常执行之后执行。
  • 异常通知:<aop:after-throwing>
    在切入点方法执行产生异常之后执行。
  • 最终通知:<aop:after>
    无论切入点方法是否正常执行,它都会在其后面执行。
    每次执行时,后置通知和异常通知永远只能执行一个。
    示例:
<aop:config>
        <aop:aspect id="logAdvice" ref="logger" >
            <!-- 前置通知: 在切入点方法执行之前执行 -->
            <aop:before 
                        method="beforePrintLog"
                        pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
            </aop:before>
            <!-- 后置通知: 在切入点方法正常执行之后执行。(每次执行时,后置通知和异常通知永远只能执行一个) -->
            <aop:after-returning 
                                 method="afterReturningPrintLog" 
                                 pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
            </aop:after-returning>
            <!-- 异常通知:在切入点方法执行产生异常之后执行 -->
            <aop:after-throwing 
                                method="afterThrowingPrintLog" 
                                pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
            </aop:after-throwing>
            <!-- 最终通知: 无论切入点方法是否正常执行它都会在其后面执行 -->
            <aop:after 
                       method="afterPrintLog" 
                       pointcut="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())">
            </aop:after>
        </aop:aspect>
    </aop:config>

5.4、xml中通用化切入点表达式配置

多次使用的切入点表达式可以配置成一个通用化切入点表达式,在需要的地方进行引用。

使用<aop:pointcut>配置通用化切入点表达式,在通知的标签中使用pointcut-ref属性进行引入。

<aop:pointcut>标签可以写在<aop:aspect>标签内部,此时只有当前切面可以使用。

<aop:pointcut>标签也可以写在<aop:aspect>标签外部,此时就变成了所有切面可以使用。

aop的xml约束要求:aop:pointcut写在aop:aspect标签外部时,必须写在aop:aspect标签上方。
示例:

<aop:config>
    <aop:aspect id="logAdvice" ref="logger" >
        <aop:before method="beforePrintLog" pointcut-ref="pt1">
        </aop:before>
        <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1">
        </aop:after-returning>
        <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1">
        </aop:after-throwing>
        <aop:after method="afterPrintLog" pointcut-ref="pt1">
        </aop:after>

        <aop:pointcut id="pt1" 
                      expression="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())"/>

    </aop:aspect>
</aop:config>

5.5、xml中环绕通知的配置和写法

它是Spring框架为我们提供的一种可以在代码中手动控制增强方法合适执行的方式。

Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。

示例:

<bean id="accountService" class="com.study.service.impl.AccountServiceImpl">
</bean>
<bean id="logger" class="com.study.util.Logger">
</bean>

<aop:config>
    <aop:aspect id="logAdvice" ref="logger" >
        <aop:pointcut id="pt1" 
                      expression="execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())"/>        
        <aop:around method="aroundPrintLog" pointcut-ref="pt1"> </aop:around>
    </aop:aspect>
</aop:config>

环绕通知方法:

public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint) {
    Object resultValue = null;
    try {
        Object[] args = proceedingJoinPoint.getArgs(); // 获取方法传入的参数
        System.out.println("前置通知.....");
        resultValue = proceedingJoinPoint.proceed(args);  // 调用切入点方法,获取返回值
        System.out.println("后置通知.......");
        return resultValue;
    } catch (Throwable e) {  // 此处需要cache的异常类型需要为Throwable
        System.out.println("异常通知........");
        throw new RuntimeException(e);
    } finally {
        System.out.println("最终通知......");
    }
}

5.6、基于注解的AOP配置

切面类注解:

  • @Aspect
    声明当前类是一个切面类。
    另外,切面类还需使用@Component注解声明是一个bean

切入点注解:

  • @Pointcut("execution(表达式)")
    作用于方法上。

示例:

@Pointcut("execution(public void com.study.service.impl.AccountServiceImpl.saveAccount())")
private void pt1() {}

通知的注解:

  • @Before("切入点方法名()")
    前置通知
  • @AfterReturning("切入点方法名()")
    后置通知
  • @AfterThrowing("切入点方法名()")
    异常通知
  • @After("切入点方法名()")
    最终通知
  • @Around("切入点方法名()")
    环绕通知

示例:

@Before("pt1()")
public void beforePrintLog() {
    System.out.println("前置通知...");
}

Spring5.0.2版本,使用注解方式配置aop时,后置通知/异常通知 会在 最终通知 执行之后才执行。

使用注解配置AOP,需在xml或配置类中配置开启注解aop的支持。

xml开启注解aop支持:

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

配置类开启注解aop支持:

@Configuration
@ComponentScan("com.study")
@EnableAspectJAutoProxy  // 开启注解aop支持
public class SpringConfiguration {
}

6、Spring中的JdbcTemplate

6.1、JdbcTemplate概述

用于和数据库交互的,实现对表的CRUD操作。
它是Spring框架提供的一个对象,是对原始Jdbc API对象的简单封装。Spring框架为我们提供了很多的操作模板类。

操作关系型数据库的:JdbcTemplate、HibernateTemplate

操作noSql数据库的:RedisTemplate

操作消息队列的:JmsTemplate

JdbcTemplate在spring-jdbc-5.0.2.RELEASE.jar中,我们在导包的时候,除了要导入这个jar包,还需要导入和事务相关的spring-tx-5.0.2.RELEASE.jar。

用法示例:

导入依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-tx</artifactId>
    <version>5.0.2.RELEASE</version>
</dependency>
<dependency>
    <groupId>com.oracle</groupId>
    <artifactId>ojdbc6</artifactId>
    <version>12.1.0.1-atlassian-hosted</version>
</dependency>

编写测试方法:

public class JdbcTemplateDemo1 {
    public static void main(String[] args) {

        // 准备数据源: Spring内置数据源
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("oracle.jdbc.driver.OracleDriver");
        dataSource.setUrl("jdbc:oracle:thin:@localhost:1521/orcl");
        dataSource.setUsername("springTest");
        dataSource.setPassword("tiger");

        // 创建JdbcTemplate对象
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        jdbcTemplate.execute("insert into account(id, name, money) values ('04', 'dddd', 200)");
    }
}

6.2、JdbcTemplate的CRUD操作

6.2.1、使用IoC管理JdbcTemplate

在bean.xml中配置bean:

<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <property name="dataSource" ref="dataSource"></property>
</bean>

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
    </property>
    <property name="url" value="jdbc:oracle:thin:@localhost:1521/orcl">
    </property>
    <property name="username" value="springTest"></property>
    <property name="password" value="tiger"></property>
</bean>

6.2.2、新增

示例代码:

jdbcTemplate.update("insert into account (id, name, money) values (?, ?, ?)", "06", "ffff", 1000);

6.2.3、更新

示例代码:

jdbcTemplate.update("update account set name=?, money=? where id=?",  "ffff", 800, "06");

6.2.4、删除

示例代码:

jdbcTemplate.update("delete from account where id=?", "06");

6.2.5、查询获取List

自定义数据库实体类的属性和数据库结果集映射。
示例代码:

public class JdbcTemplateDemo3 {
    public static void main(String[] args) {
        List<Account> accounts = 
            jdbcTemplate.query("select * from account where money > ?", 
                               new AccountRowMapper(), 300);
        accounts.forEach(System.out::println);
    }
}
class AccountRowMapper implements RowMapper<Account> {

    /**
     * 把结果集的数据封装到Account中,然后由Spring把每个Account加到集合中
     */
    @Override
    public Account mapRow(ResultSet resultSet, int i) throws SQLException {
        Account account = new Account();
        account.setId(resultSet.getString("id"));
        account.setName(resultSet.getString("name"));
        account.setMoney(resultSet.getFloat("money"));
        return account;
    }
}

使用Spring自带的BeanPropertyRowMapper进行映射
示例代码:

List<Account> accounts = 
    jdbcTemplate.query("select * from account where money > ?", 
                       new BeanPropertyRowMapper<Account>(Account.class), 300);
accounts.forEach(System.out::println);

6.2.6、查询一个

获取到多个后,判断是否为空,获取第一个
示例代码:

List<Account> accounts = 
    jdbcTemplate.query("select * from account where id = ?", 
           				new BeanPropertyRowMapper<Account>(Account.class), "03");
System.out.println(accounts.isEmpty()? "无结果" : accounts.get(0));

6.2.7、查询一个值(聚合函数)

使用聚合函数返回一行一列,但不加group by 子句
示例代码:

int count = 
    jdbcTemplate.queryForObject("select count(*) from account where money > ?",
                                Integer.class, 200f);
System.out.println(count);

6.3、编写JdbcDaoSupport去除dao层重复代码

Spring中原生就提供了JdbcDaoSupport类,下面的示例只是演示JdbcDaoSupport的实现原理。
原来的dao层每一个实现类内部都需要注入JdbcTemplate,可以编写一个父类JdbcDaoSupport让dao层实现类都继承该类。
JdbcDaoSupport.java

public class JdbcDaoSupport {
    private JdbcTemplate jdbcTemplate;

    // 只注入dataSource也可以get到jdbcTemplate对象
    public void setDataSource(DataSource dataSource) {
        jdbcTemplate = createJdbcTemplate(dataSource);
    }
    private JdbcTemplate createJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    public JdbcTemplate getJdbcTemplate() {
        return jdbcTemplate;
    }

    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
}

AccountDaoImpl.java

public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {

    @Override
    public Account findAccountById(String id) {
        List<Account> accounts = super.getJdbcTemplate().
            query("select * from account where id=?", 
                  new BeanPropertyRowMapper<>(Account.class), id);
        return accounts.isEmpty() ? null : accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        // 使用super.getJdbcTemplate()获取到jdbcTemplate对象
        super.getJdbcTemplate().
            update("update account set name=?, money=? where id=?",
                   account.getName(), account.getMoney(), account.getId());
    }
}

在xml中配置注入dataSource:

<bean id="accountDao" class="com.study.dao.impl.AccountDaoImpl">
    <property name="dataSource" ref="dataSource">
    </property>
</bean>

<bean id="dataSource" 
      class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver">
    </property>
    <property name="url" value="jdbc:oracle:thin:@localhost:1521/orcl">
    </property>
    <property name="username" value="springTest"></property>
    <property name="password" value="tiger"></property>
</bean>

7、Spring的声明式事务控制

事务的接口spring-tx-5.0.2.RELEASE.jar中。

Spring的事务控制都是基于AOP的,它既可以使用编程的方式实现,也可以使用配置的方式实现。

7.1、Spring中的事务控制API

7.1.1、PlatformTransactionManager

PlatformTransactionManager接口提供事务操作的方法,包含3个具体的操作:

  • 获取事务状态信息
    TransactionStatus getTransaction(TransactionDefinition definition)
  • 提交事务
    void commit(TransactionStatus status)
  • 回滚事务
    void rollback(TransactionStatus status)

常用的实现类:
org.springframework.jdbc.datasource.DataSourceTransactionManager:使用Spring JDBC或iBatis进行持久化数据时使用。

org.springframework.orm.hibernate5.HibernateTransactionManager:使用Hibernate版本进行持久化数据时使用。

7.1.2、TransactionDefinition

它是事务的定义信息对象,里面有如下方法:

  • String getName()
    获取事务对象名称
  • int getIsolationLevel()
    设置事务隔离级别
  • int getPropagationBehavior()
    获取事务传播行为
  • int getTimeout()
    获取事务超时时间
  • boolean isReadOnly()
    获取事务是否只读

事务隔离级别反映事务提交并发访问时的处理态度

  • ISOLATION_DEFAULT
    默认级别,采用数据库的隔离级别

  • ISOLATION_READ_UNCOMMITTED
    可以读取未提交的数据

  • ISOLATION_READ_COMMITED
    只能读取已提交的数据,解决脏读问题

  • ISOLCATION_REPEATABLE_READ
    是否读取其他事务提交修改后的数据,解决不可重复读问题

  • ISOLATION_SERIALIZABLE
    是否读取其他事务提交添加后的数据,解决幻影读问题。

事务传播行为:

  • REQUIRED
    如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)

  • SUPPORTS
    支持当前事务,如果当前没有事务,就以非事务方式进行(没有事务)

  • MANDATORY
    使用当前的事务,如果当前没有事务,就抛出异常

  • REQUERS_NEW
    新建事务,如果当前在事务中,就把当前事务挂起

  • NOT_SUPPORTED
    以非事务方式执行操作,如果当前存在事务,就把当前事务挂起

  • NEVER
    以非事务方式运行,如果当前存在事务,抛出异常

  • NESTED
    如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作。

7.1.3、TransactionStatus

此接口提供的是事务具体的运行状态。
TransactionStatus接口描述了某个时间点上事务对象的状态信息,包含6个具体操作:

  • void flush()
    刷新事务

  • boolean hasSavepoint()
    获取是否存在存储点(按步提交回滚,类似游戏存档)

  • boolean isCompleted()
    获取事务是否完成

  • boolean isNewTransaction()
    获取事务是否为新事务

  • boolean isRollbackOnly()
    获取事务是否回滚

  • void setRollbackOnly()
    设置事务回滚

7.2、基于xml的声明式事务配置

编写service层代码,里面不手动加事务代码:

public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao;
    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }
    
    @Override
    public Account findAccountById(String id) {
        return accountDao.findAccountById(id);
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        Account sourceAccount = accountDao.findAccountByName(sourceName);
        Account targetAccount = accountDao.findAccountByName(targetName);
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        targetAccount.setMoney(targetAccount.getMoney() + money);
        accountDao.updateAccount(sourceAccount);
        // int i = 1 / 0;
        accountDao.updateAccount(targetAccount);
    }
}

在xml配置的步骤:

  1. 配置事务管理器的bean
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource">
    </property>
</bean>
  1. 配置事务通知
使用<tx:advice>配置事务通知。
<tx:advice>标签属性: 
  - id 
给事务通知起一个唯一标识
  - transaction-manager 
给事务通知提供一个事务管理器引用

示例: 
<tx:advice id="txAdvice" transaction-manager="transactionManager">
</tx:advice>

3.配置<aop:config>中通用切入点表达式

<aop:config>
    <!-- 配置切入点表达式 -->
    <aop:pointcut id="pt1" 
                  expression="execution(* com.study.service.impl.*.*(..))"/>
</aop:config>
  1. 在<aop:config>标签中使用<aop:advisor>标签建立切入点表达式和事务通知的对应关系
<aop:config>
    <aop:pointcut id="pt1" 
                  expression="execution(* com.study.service.impl.*.*(..))"/>
    <!-- 建立切入点表达式和事务通知的对应关系 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1">
    </aop:advisor>
</aop:config>
  1. <tx:advice>标签中使用<tx:attributes><tx:method>标签配置事务的属性,对某些aop切入的某些方法指定事务配置。
    <tx:method>标签的属性:
  • name
    具体的方法名或者带有通配符的表达式。
    例如:get
    表示所有方法名以get开头的方法
  • isolation
    指定事务的隔离级别(默认是DEFAULT,表示使用数据库的默认隔离级别)
  • paropagation
    用于指定事务的传播行为,一般增删改使用REQUIRED,查询方法使用SUPPORTS(默认值是REQUIRED,表示一定会有事务)
  • read-only
    用于指定事务是否只读,只有查询方法才能设置true。(默认值是false)
  • timeout
    用于指定事务的超时时间,以秒为单位。(默认值是-1,表示永不超时)
  • rollback-for
    用于指定一个异常。当产生该异常时,事务回滚;产生其他异常时,事务不回滚。
    (没有默认值,表示任何异常都回滚)
  • no-rollback-for
    用于指定一个异常。当产生该异常时,事务不回滚;产生其他异常时,事务回滚。
    (没有默认值,表示任何异常都回滚)
    示例:
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
        <!-- transfer方法的配置 -->
        <tx:method name="transfer" propagation="REQUIRED" read-only="false"/>
        <!-- 以find开头的方法的配置 -->
        <tx:method name="find*" propagation="SUPPORTS" read-only="true">
        </tx:method>

        <!-- 剩余的所有方法的配置 -->
     <tx:method name="*" propagation="REQUIRED" read-only="false">
        </tx:method>
    </tx:attributes>
</tx:advice>

7.3、基于注解的声明式事务配置

需要引入的xml约束:xmlns:context、xmlns:tx

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">
</beans>

配置步骤:

  1. 配置事务管理器
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"></property>
</bean>
  1. 使用<tx:annotation-driven>标签开启spring对注解事务的支持
    标签属性:
  • transaction-manager
    事务管理器的bean
<tx:annotation-driven transaction-manager="transactionManager">
</tx:annotation-driven>
  1. 在需要事务支持的地方使用@Transactional注解
    该注解可用于类、方法上,指定该类中所有切入点方法或类中某一个切入点方法的事务属性配置。

该注解有propagation、isolation、readOnly等属性,可对事务的属性进行配置。

如果需要手动回滚,可以使用TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();

例如:

@Service("accountService")
@Transactional
public class AccountServiceImpl implements IAccountService {
}

7.4、纯注解配置的声明式事务配置

  1. 在resources中创建数据库连接配置文件:jdbcConfig.properties
jdbc.driver=oracle.jdbc.driver.OracleDriver
jdbc.userName=springTest
jdbc.password=tiger
jdbc.url=jdbc:oracle:thin:@localhost:1521/orcl
  1. 创建数据库连接配置类
public class JdbcConfiguration {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.userName}")
    private String userName;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.password}")
    private String password;
    
    @Bean("jdbcTemplate")
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    @Bean("dataSource")
    public DataSource getDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUsername(userName);
        dataSource.setPassword(password);
        dataSource.setUrl(url);
        return dataSource;
    }
}

3.创建事务配置类

public class TransactionConfiguration {

    @Bean("transactionManager")
    public PlatformTransactionManager createTransactionManager(
        DataSource datasource) {
        return new DataSourceTransactionManager(datasource);
    }
}

4.创建总配置类

@Configuration
@ComponentScan("com.study")
@PropertySource("jdbcConfig.properties")
@EnableTransactionManagement  // 开启事务支持
@Import({JdbcConfiguration.class, TransactionConfiguration.class})
public class SpringConfiguaration {
}

8、Spring的编程式事务控制

  1. 配置事务管理器
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource">
    </property>
</bean>
  1. 配置事务模板对象
<bean id="transactionTemplate" 
      class="org.springframework.transaction.support.TransactionTemplate">
    <property name="transactionManager" ref="transactionManager">
    </property>
</bean>
  1. 在业务层实现类的代码中加入事务模板对象
public class AccountServiceImpl implements IAccountService {

    private TransactionTemplate transactionTemplate;
    private IAccountDao accountDao;
    
    public void setTransactionTemplate(TransactionTemplate transactionTemplate) {
        this.transactionTemplate = transactionTemplate;
    }
    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public Account findAccountById(String id) {
        // 带有返回值的事务模板
        return transactionTemplate.execute(new TransactionCallback<Account>() {
            @Override
            public Account doInTransaction(TransactionStatus transactionStatus) {
                return accountDao.findAccountById(id);
            }
        });
    }

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        // 在事务模板中创建TransactionCallbace对象,把原来的业务逻辑放入该对象的doInTransaction方法中
        transactionTemplate.execute(new TransactionCallback<Object>() {
            @Override
            public Object doInTransaction(TransactionStatus transactionStatus) {
                Account sourceAccount = accountDao.findAccountByName(sourceName);
                Account targetAccount = accountDao.findAccountByName(targetName);
                sourceAccount.setMoney(sourceAccount.getMoney() - money);
                targetAccount.setMoney(targetAccount.getMoney() + money);
                accountDao.updateAccount(sourceAccount);
                // int i = 1 / 0;
                accountDao.updateAccount(targetAccount);
                return null;
            }
        });
    }
}

9、Spring如何在web应用中使用

9.1、需要额外导入的jar包

  • spring-web-5.0.2.RELEASE.jar
  • spring-webmvc-5.0.2.RELEASE.jar

9.2、配置文件

同spring在java应用的配置文件一样

9.3、如何创建IOC容器

  • java应用中在main方法中直接创建
  • web应用应该在web应用被服务器加载时就创建IOC容器

在ServletContextListener#contextInitialized(ServletContextEvent sce)方法中创建IOC容器。
创建IOC容器后,可以把其放在ServletContext(即application域)的一个属性中,供web应用的其他组件访问IOC容器。
Spring配置文件的名字和位置也应该设置为可配置的。将其配置在当前Web应用的初始化参数中较为合适。

9.4、示例代码

  1. 导入pom依赖
<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>servlet-api</artifactId>
        <version>2.5</version>
        <scope>provided</scope>
    </dependency>
    <dependency>
        <groupId>javax.servlet.jsp</groupId>
        <artifactId>jsp-api</artifactId>
        <version>2.0</version>
    </dependency>
</dependencies>

2.编写监听器,实现ServletContextListener接口

public class SpringServletContextListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        // 获取配置文件的名称
        ServletContext servletContext = servletContextEvent.getServletContext();
        String configLocation = servletContext.getInitParameter("configLocation");

        // 获取IoC容器,并配置到application域中
        ApplicationContext ctx = 
            new ClassPathXmlApplicationContext(configLocation);
        servletContext.setAttribute("ApplicationContext", ctx);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

3.编写测试的servlet

public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 从application域对象得到IoC容器的引用
        ServletContext servletContext = getServletContext();
        ApplicationContext ctx = (ApplicationContext) servletContext.getAttribute("ApplicationContext");
        // 从IoC容器中获取Bean
        Person person = ctx.getBean(Person.class);
        person.hello();
    }
}
  1. 配置web.xml
<web-app>
  <!-- 配置spring配置文件的名称 -->
  <context-param>
    <param-name>configLocation</param-name>
    <param-value>applicationContext.xml</param-value>
  </context-param>

    <!-- 配置自定义的Listener -->
  <listener>
    <listener-class>com.study.listeners.SpringServletContextListener</listener-class>
  </listener>

    <!-- 测试servlet -->
  <servlet>
    <servlet-name>testServlet</servlet-name>
    <servlet-class>com.study.servlets.TestServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>testServlet</servlet-name>
    <url-pattern>/test</url-pattern>
  </servlet-mapping>
</web-app>
  1. 配置spring的配置文件:applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 配置bean的唯一标识和全限定类名 -->
    <bean id="person" class="com.study.beans.Person">
        <property name="username" value="张三" />
    </bean>
</beans>
  1. 编写javaBean类和测试jsp页面

9.5、Spring提供的Listener

web.xml的配置

<web-app>
  <display-name>Archetype Created Web Application</display-name>
  <!-- 配置spring配置文件的名称 -->
  <context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:applicationContext.xml</param-value>
  </context-param>

  <listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>

  <servlet>
    <servlet-name>testServlet</servlet-name>
    <servlet-class>com.study.servlets.TestServlet</servlet-class>
  </servlet>
  <servlet-mapping>
    <servlet-name>testServlet</servlet-name>
    <url-pattern>/test</url-pattern>
  </servlet-mapping>
</web-app>

在servlet中使用IoC容器

public class TestServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 从application域对象得到IoC容器的引用
        ServletContext servletContext = getServletContext();
        ApplicationContext ctx = 
            WebApplicationContextUtils.getWebApplicationContext(servletContext);
        // 从IoC容器中获取Bean
        Person person = ctx.getBean(Person.class);
        person.hello();
    }
}

标签:事务,spring,void,配置,基础,class,通知,public
From: https://www.cnblogs.com/cc-boy/p/17009432.html

相关文章

  • Spring Cloud Alibaba之Nacos
    目录结构:一、SpringCloudAlibaba简介二、使用版本情况三、什么是Nacos?四、Nacos运行环境部署五、Nacos注册中心六、Nacos命名空间和分组......
  • Spring学习笔记 - 第三章 - AOP与Spring事务
    原文地址:Spring学习笔记-第三章-AOP与Spring事务Spring学习笔记全系列传送门:Spring学习笔记-第一章-IoC(控制反转)、IoC容器、Bean的实例化与生命周期、DI(依......
  • tomcat7运行springboot war包项目启动报错
    报错的log如下:2022-12-2801:11:22.701WARN39756---[localhost-startStop-1]ConfigServletWebServerApplicationContext:Exceptionencounteredduringcontext......
  • Spring IOC官方文档学习笔记(五)之bean的作用域
    1.Bean的作用域(1)Bean的作用域即Bean实例的作用范围,Spring支持6种bean的作用域,其中4种只能在web环境中使用,具体如下作用域描述singleton默认作用域,采用单例......
  • 信息学赛培 | 01 基础数据结构与算法回顾
    导读信息学能够有助于孩子未来工作发展,提升孩子的综合能力。这一期课,我们继续深入学习数据结构和算法,学的更深,学的更多!在此之前,我们需要先掌握好上一期的课程,打好基础,再深入......
  • Java学习笔记----线程基础
    线程线程,进程可进一步细化为线程,是一个程序内部的一条执行路径线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器,线程切换的开销小线程的创建与启动Java......
  • 应用基础-在Hexo、Hugo博客框架中使用Gitalk基于Github上仓库项目的issue无后端服务评
    关注「WeiyiGeek」公众号本章目录:0x00Gitalk-基于Github的评论系统1.使用Gitalk进行Github的Oauth认证无法跨域获取Token问题解决办法1.快速介绍2.安装部署3.使用实践n.......
  • 每天一点基础K8S--K8S中的configmap
    ConfigMap1、背景为了将配置数据和代码解耦,可以将变化的配置文件单独保存为configmap,而不用每次都修改代码。ConfigMap是一种API对象,用来将非机密性的数据保存到键......
  • 1006.Django模型基础01
    一、Django的ORM简介1.ORM概念:对象关系映射(ObjectRelationalMapping);2.ORM优势:不用直接编写SQL代码,只需像操作对象一样从数据库操作数据。 django模型映射关系模型......
  • 基于springboot+mybatis+mysql+html实现校园宿舍管理系统
    @目录一、系统简介二、系统主要功能界面三、其它系统四、源码下载一、系统简介本系统功能模块主要分为:信息浏览浏览功能、宿舍打卡浏览功能、学生提交信息功能、宿舍搜索......