(三十七)Spring框架详细概述大家庭第三集(AOP、Spring事务、实现转账功能Mybatis与Spring业务集成)

Spring(三)

一.AOP
  AOP思想
  AOP术语
  AspectJ
  AOP实现
    1.使用XML配置
    2.使用AOP注解配置

二.Spring对事务支持
   1.使用XMl配置事务
   2.使用注解配置事务

三.集成
   需求,模拟转账功能,配合事务
    1.使用XML配置
    2.使用注解配置

四.原理解读
  批量配置Mapper对象

模板方法设计模式:把一些相同操作定义父类中,把不同操作定义子类中,一般可以覆盖的方法都以do开头


AOP


AOP思想

在开发中,我们会给业务方法增加日志功能,事务控制等,如果业务类过多,不便于维护系统,OOP的继承可以消除重复,但是还是会在纵向的增加功能方法的调用代码不遵循开闭原则,所以有了AOP思想。

AOP,面向切面编程技术,把业务方法中与业务方法无关的操作抽离到不同对象中,使用动态代理组合起来,动态为类增加功能。

AOP本质是使用动态代理,AOP会根据我给的条件实现invocationHandle接口并重写invoke(),还会使用动态代理API帮你把代理类及对象创建好

public class XxxinvocationHandler implements InvocationHandler{
    //AOP实现会根据我们给的条件从此额invoke方法
    public void invoke(..){}
}
//使用动态代理API帮我们把代理类以及对象创建好
public class $Proxy数值 extends Proxy implements..{}


AOP术语

  • Joinpoint:连接点,想加功能的方法
  • Pointcut:切入点,多个连接点集合的表达式,开发者知道哪里想加功能,但是别人AOP实现不知道(在哪里加),可以通过切入点告诉它(where)
  • Advice:增强,什么时候加(when),加具体什么功能(what)
  • Aspect:切面:切入点+增强(Pointcut+Advice),就是where,when,what的集合
  • Target:被代理的目标对象
  • Weaving:织入,把增强(Advice)加到 目标对象(Target)后,创建代理类(Proxy)的过程
  • Proxy:一个类被AOP织入后增强后,产生的代理类


AspectJ

AspectJ 是一个面向切面的框架,它扩展了Java 语言(即使用 Java 对 AOP 进行了实现)。

切入点语法通配符

execution(* cn.wolfcode.ssm.service.impl.*ServiceImpl.*(..)) //注意第一个星符号有空格

配置依赖

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.13</version>
</dependency>


AOP实现


使用XML配置

给业务方法添加模拟事务功能

  • service层
public interface IUserService {
    void save(String username,String password);
}
----------------------------------------
public class UserServiceImpl implements IUserService {
    @Override
    public void save(String username, String password) {
     System.out.println("保存了"+username+""+password);
    }
}
  • 模拟事务管理类 tx
//封装事务相关的操作,
public class MyTransactionManager {
    public void begin(){
        System.out.println("开启事务");
    }
    public void commit(){
        System.out.println("提交事务");
    }
    public void rollback(){
        System.out.println("回滚事务");
    }
}
  • 配置applicationContext.xml
//头省略
    <!--配置事务管理器-->
    <bean id="tx" class="cn.k.tx.MyTransactionManager"/>

    <!--配置真实对象或者业务对象-->
    <bean id="userService" class="cn.k.service.impl.UserServiceImpl"/>

    <!--配置AOP一定记住是否配置三要素 where when what-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect ref="tx">
            <!--配置切入点where * cn.k.service.impl.*ServiceImpl.*(..)-->
            <aop:pointcut id="txPointcut" expression="execution(* cn.k.service.impl.*ServiceImpl.*(..))"/>
            <!--切入点表示到的方法,在方法执行之前调用 tx.begin()-->
            <aop:before pointcut-ref="txPointcut" method="begin"/>
            <!--切入点表示到的方法,在方法正常执行完调用 tx.commit()-->
            <aop:before pointcut-ref="txPointcut" method="commit"/>
            <!--切入点表示到的方法,在方法执行抛出异常调用 tx.rollback()-->
            <aop:before pointcut-ref="txPointcut" method="rollback"/>
        </aop:aspect>
    </aop:config>
  • 测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class UserServiceTest {
    @Autowired
    private IUserService userService;
    @Test
    public void save() {
    System.out.println(userService.getClass());
     userService.save("qqq","465");
    }
}

注意:使用AOP相当于拿到了代理对象,把AOP去掉拿到了真实对象


使用AOP注解配置
  • 修改applicationContext.xml配置文件
<!--配置事务管理器-->
<!--配置真实对象或者业务对象-->
<!--IoC DI注解解析
让spring帮我们创建事务管理器对象,真实对象或者业务对象
    -->
    <context:component-scan base-package="cn.k"/>
    <!--AOP注解解析器-->
    <aop:aspectj-autoproxy/>
  • 修改事务管理类
@Component @Aspect
public class MyTransactionManager {
    @Pointcut("execution(* cn.k.service.impl.*ServiceImpl.*(..))")
    public void txPointcut() {}

    @Before("txPointcut()")
    public void begin() {
        System.out.println("开启事务");}

    @AfterReturning("txPointcut()")
    public void commit() {
        System.out.println("提交事务");}

    @AfterThrowing("txPointcut()")
    public void rollback() {
        System.out.println("回滚事务");}}

这里使用的是JDK代理,如果要使用CGLIB,修改为

<aop:aspectj-autoproxy proxy-target-class="true"/>


Spring对事务支持

Spring事务管理器,不同持久化技术的不同事务管理器

  • JDBC和Mybatis 使用DataSourceTransactionManager
  • Hibernate 使用 HibernateTransactionManager
  • JPA 使用 JpaTransactionManager。

事务增强的属性配置

常用 :

read-only:若为 true,开启一个只读事务,只读事务的性能较高,但是不能在只读事务中操作 DML

Spring默认情况下,业务方法会抛出RuntimeException 及其子类的异常才会去回滚,抛出 Exception 和 Thowable 的异常不会回滚,如果是自定义,一般都会继承运行时异常


使用XMl配置事务

<!--配置事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
    <!--定义事务增强when what when看不出来-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--针对方法的差异化配置-->
        <tx:attributes>
            <!--Xxx*前代表会匹配Xxx*开头的-->
            <tx:method name="select*" read-only="true"/>
            <tx:method name="query*" read-only="true"/>
            <tx:method name="list*" read-only="true"/>
            <tx:method name="count*" read-only="true"/>
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
    <!--
    切入点表示的到方法执行之前,调用transactionManager 提交事务方法getTransaction
    切入点表示的到方法执行正常完,调用transactionManager 提交事务方法commit
    切入点表示的到方法执行抛出异常时,调用transactionManager 提交事务方法rollback
    -->
    <!--配置AOP-->
    <aop:config>
        <!--配置切入点 where-->
        <aop:pointcut id="txPointcut" expression="execution(* cn.k.service.impl.*ServiceImpl.*(..))"/>
        <!--关联切入点和增强,构成切面-->
        <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
    </aop:config>


使用注解配置事务

  • 给业务类贴上注解
// 贴事务注解
@Transactional
public class XxxServiceImpl implements IXxxService {}
  • 修改配置
<!--配置事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>

<!--配置事务注解解析器-->
    <tx:annotation-driven/>

Transactional 注解

  • 贴业务类或业务接口上,事务的配置是通用与整个类或接口的的方法;
  • 贴业务方法上,即方法上的的事务的配置仅限于被贴的方法;
  • 同时存在时,后者覆盖前者;

若要使用 CGLIB 动态代理,则修改事务注解解析器的配置

<tx:annotation-driven proxy-target-class="true" transactionmanager="transactionManager"/>


集成

使用框架,集成MyBatis和业务层,将业务对象,Mapper对象等都交给Spring容器管理

注意打包方式是war(因为要建WEB项目),我们建立一个maven项目


需求,模拟转账功能,配合事务

这里思路:

集成Mybatis和业务层,首先需要定义mapper接口,和mapper.xml文件,db.properties文件完成持久层文件基本定义,其他交给Spring容器.

  1. 首先我们需要再applicationContext.xml 关联db.properties
  2. 我们需要获取数据源对象
  3. 获取完数据源对象,我们需要获取SqlSessionFactory对象,并设置数据源,设置别名(一般都整个别名)
  4. 获取SqlSessionFactory对象的目的是为了拿到mapper对象(mapper接口的代理对象可能会有多个,所有使用批量获取)
  5. 拿到mapper对象,我们就可以配置业务对象
  6. 配置事务管理器对象,指定数据源
  7. 配置事务注解解析器

使用XML配置
  • 定义实体类
@Data
public class Account {
    private Long id;
    private BigDecimal balance;}
  • 定义mapper接口
public interface AccountMapper {
    void addBalance(@Param("inId") Long inId, @Param("amount") BigDecimal amount);
    void subtractBalance(@Param("outId") Long outId, @Param("amount") BigDecimal amount);
}
  • 编写mapper.xml文件
<mapper namespace="cn.k.mapper.AccountMapper">
    <update id="addBalance">
    update account set balance=balance +#{amount} where id=#{inId}
    </update>
    <update id="subtractBalance">
        update account set balance=balance -#{amount} where id=#{outId}
    </update>
</mapper>
  • 编写service层
接口
    public interface IAccountService {
    //转账
    void transfer(Long outId, Long inId, BigDecimal amount);
}
----------------------------------
实现类
public class AccountServiceImpl implements IAccountService {
    private AccountMapper accountMapper;

       @Autowired
    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;}
    @Override
    public void transfer(Long outId, Long inId, BigDecimal amount) {
        accountMapper.subtractBalance(outId,amount);
        //int i=1/0;
        accountMapper.addBalance(inId,amount); }}
  • 这里主要写applicationContext.xml配置文件
1. <!--关联db.properties-->
    <context:property-placeholder location="classpath:db.properties"/>

    <!--配置数据源对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

2.<!--配置sqlSessionFactory对象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--设置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--设置别名-->
        <property name="typeAliasesPackage" value="cn.k.domain"/>
        <!--关联Mapper XML 把mapper接口路径与resources路径下的mapper.xml建立相同层级就可以省略不写,建立相同层级字节码文件是编译在一起的-->
    </bean>

3.<!--批量配置Mapper 对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定Mapper接口所在包-->
        <property name="basePackage" value="cn.k.mapper"/>
    </bean>
4.<!--配置业务对象 AccountService对象-->
    <context:component-scan base-package="cn.k.service.impl"/>

5.<!--配置事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--指定数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    
   6. <!--定义事务增强when what when看不出来-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--针对方法的差异化配置-->
        <tx:attributes> 
            <tx:method name="*"/>
        </tx:attributes>
    </tx:advice>
 7.<!--配置AOP-->
    <aop:config>
        <!--配置切入点 where-->
        <aop:pointcut id="txPointcut" expression="execution(* cn.k.service.impl.*ServiceImpl.*(..))"/>
        <!--关联切入点和增强,构成切面-->
        <aop:advisor pointcut-ref="txPointcut" advice-ref="txAdvice" />
    </aop:config>

编写测试类(省略…)


使用注解配置
  • 修改service实现类
@Service
@Transactional
public class AccountServiceImpl implements IAccountService {
    private AccountMapper accountMapper;
       @Autowired
    public void setAccountMapper(AccountMapper accountMapper) {
        this.accountMapper = accountMapper;}
    @Override
    public void transfer(Long outId, Long inId, BigDecimal amount) {
        accountMapper.subtractBalance(outId,amount);
        accountMapper.addBalance(inId,amount);}}
  • 修改applicationContext.xml配置文件
   1.<!--关联db.properties-->
    <context:property-placeholder location="classpath:db.properties"/>

   2. <!--配置数据源对象-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
          init-method="init" destroy-method="close">
        <property name="driverClassName" value="${jdbc.driverClassName}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

   3. <!--配置sqlSessionFactory对象-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <!--设置数据源-->
        <property name="dataSource" ref="dataSource"/>
        <!--设置别名-->
        <property name="typeAliasesPackage" value="cn.k.domain"/>
        <!--关联Mapper XML-->
    </bean>

   4. <!--批量配置Mapper 对象-->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <!--指定Mapper接口所在包-->
        <property name="basePackage" value="cn.k.mapper"/>
    </bean>

    5.<!--配置业务对象 AccountService对象-->
    <context:component-scan base-package="cn.k.service.impl"/>

    6.<!--配置事务管理器对象-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     <!--指定数据源-->
        <property name="dataSource" ref="dataSource"/>
    </bean>
   
    7.<!--配置事务注解解析器-->
    <tx:annotation-driven/>
</beans>


原理解读


批量配置Mapper对象

  1. 在指定包路径下获取所有Mapper接口对象,Class[] clzes;

  2. 根据类型到容器中获取对应的对象SqlSessionFactory factory =(SqlSessionFactory)容器对象.getBean(SqlSessionFactory.class)

  3. for(Class clz :clzes){
      factory.openSession().getMapper(clz) //把方法返回对象存到容器中,名称对应接口类名首字母小写
        }
    

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值