spring框架(下)

一、AOP

1.问题提出

在实际开发中我们可能同时需要编写业务核心代码、事务处理代码和日志记录代码,通常的做法, 例如在dao层除了在执行数据库操作,还需其他一些操作,如 日志等。 卸载dao层 将业务代码与非业务代码 交织在一块,不利于代码维护。

    public void insertAdmin(int id,int num){
        //核心业务代码
        System.out.println("保存管理");
    }

    public void saveLog(){
        //入日志代码
        System.out.println("");
    }

2.AOP概述

oop是面向对象编程,是对整个程序的全局的一种架构设计。

aop是oop的补充,将程序中的非业务代码进行抽取,将非业务代码与业务代码相隔离,通过一个代理对象在业务代码中调用非业务代码,提供业务灵活性 提高开发效率

aop这一思想,核心就是使用一个代理对象,在中间帮忙调用,为程序添加功能。

aop思想不是spring特有的,是java中的一种代理思想。

在这里插入图片描述

3.AOP基本概念

连接点(JoinPoint):类中可以被增强的方法,就成为连接点,如方法A 方法B

切入点(pointcut):类中有许多方法可以被增强,但实际中只用 方法A被增强了,那么方法A就被称为切入点**(实际实现的连接点)**

通知(Advice):通知是指一个切面在特定的连接点要做的事情**(增强的功能)**,通知分为 前置通知、后置通知、最终通知、异常通知、环绕通知

切面(Aspect):把通知添加到切入点的整个过程称为切面

目标(target):代理的目标对象, 连接点 切入点所在的类,真正做这件事的类

代理(Proxy):向目标对象应用通知时,创建的代理对象–帮我们使用通知的类

二、springAOP实现

前置通知:在方法执行之前 被调用

后置通知:在方法正常的执行完成后 被调用

最终通知:无论是否出现异常,都会被调用

异常通知:方法中 出现异常时 才调用

环绕通知:环绕通知围绕在连接点前后,比如一个方法调用的前后。这是最强大的通知类型,能在方法调用前后自定义一些操作。

1.aspectj xml配置实现

1.1下载AOP相关jar

        <!-- aspectj       -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.2.RELEASE</version>
        </dependency>

1.2 xml文件中配置

<!--aop配置-->
<bean id="commonUtil" class="com.ffyc.springpro.util.CommonUtil"></bean>
<!--  配置通知和切入点之间的关系 -->
<aop:config>
	<!--  *表示返回值任意    ..表示任意参数-->
    <aop:pointcut id="insertAdmin" expression="execution(* com.ffyc.springpro.dao.*.*(..))"/>
    <aop:aspect ref="commonUtil">
        
    <!-- 前置通知,在方法执行之前 被调用-->
    <!--<aop:before method="saveLog" pointcut-ref="insertAdmin"></aop:before>-->

    <!-- 后置通知  在方法正常的执行完成后 被调用-->
    <!--aop:after-returning method="saveLog" pointcut-ref="insertAdmin"></aop:after-returning>-->
                        
    <!--  最终通知  无论是否出现异常,都会被调用-->
    <!-- <aop:after method="saveLog" pointcut-ref="insertAdmin"></aop:after> -->

    <!--异常通知   方法中 出现异常时 才调用-->
    <!--<aop:after-throwing method="saveLog" pointcut-ref="insertAdmin" throwing="e"></aop:after-throwing>-->

     <!--   环绕通知  -->
     <aop:around method="saveLog" pointcut-ref="insertAdmin"></aop:around>
                        
                </aop:aspect>
        </aop:config>

在这里插入图片描述

2.注解方式实现

2.1 启动AspectJ支持

在spring.xml中

<!--        //启动 AspectJ 支持:-->      
<aop:aspectj-autoproxy />

2.2定义通知

@Component
@Aspect
public class CommonUtil {

    //@Before("execution(* com.ffyc.springpro.dao.*.*(..))")
    //@After("execution(* com.ffyc.springpro.dao.*.*(..))")
    //@AfterReturning("execution(* com.ffyc.springpro.dao.*.*(..))")
   // @AfterThrowing(value ="execution(* com.ffyc.springpro.dao.*.*(..))",throwing = "e")
   // public void saveLog(JoinPoint joinPoint,Throwable e){
    /*public void saveLog(JoinPoint joinPoint,Throwable e){
        Object []objects=joinPoint.getArgs();
        System.out.println(Arrays.toString(objects));
        System.out.println("时间------->"+new Date());
    }*/

    //------------------环绕通知------------------------
    @Around("execution(* com.ffyc.springpro.dao.*.*(..))")
    public void saveLog(ProceedingJoinPoint proceedingJoinPoint){
        try {
            System.out.println("前置通知");
            proceedingJoinPoint.proceed();
            System.out.println("后置通知");
        }catch (Throwable e){
            System.out.println("异常通知"+e.getMessage());
        }
        System.out.println("最终通知");
    }

}

3.测试

@Test
    public void test3(){
    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
    AdminService adminService=applicationContext.getBean("adminService",AdminService.class);
    adminService.saveAdmin();
    adminService.updateAdmin();
    }

前置通知
		保存管理
后置通知
最终通知

前置通知
		修改管理
后置通知
最终通知

4.面向切片编程

​ 将程序中的 非业务代码进行抽取,在不修改业务代码的前提下,为其添加功能。

​ 面向切片编程思想底层时为目标类,创建一个代理对象,让代理对象调用目标类中方法,咋爱代理对象调用时,可以额外调用其他方法(增强的方法,通知)。

​ 提高代码复用性,灵活性,提高开发效率, 降低业务代码与非业务代码耦合性。

案例: 保存日志、 事务管理、权限认证、同一处理异常

三、事务管理

1.什么是事务

数据库事务,是属于数据库中的功能

事务可以看做由对数据库若干操作组成的一个操作单元。

一次操作执行多条sql,要保证原子性,要么都执行成功,要么都执行失败;

事务是一系列动作,他们综合在一起才是一个完整的工作单元,这些动作必须全部完成,如果有一个失败的话,事务就会回滚到最开始的状态,从而保证数据满足一致性的要求;

经典案例: 银行转账

首先A的银行卡减去500元钱;然后B的银行卡多500元钱。这两个步骤必须是要么都执行要么都不执行。不管哪一个步骤失败了以后,整个转账过程都能回滚。

​ A:-500

​ B:+500

2.spring事务管理

编程式事务管理

​ 在项目中很少使用,这种方式需要注入一个事务管理对象 TransactionTemplate ,需要在代码中手动提交和回滚。

声明式事务管理

​ 管理建立在 AOP 基础上,本质是对方法前后进行拦截,所以声明式

事务是方法级别的。只需要在事务管理中,运行的方法 添加注解标签声明即可

3.注解实现

开启事务管理

       <!-- 配置 spring 事务管理类, 并注入数据源 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
                <property name="dataSource" ref="dataSource"></property>
        </bean>

        <!-- 开启注解事务管理 -->
        <tx:annotation-driven transaction-manager="transactionManager"/>

在需要事务管理的方法或者类(把注解加到类上面,该类中的所有方法都添加事务管理)上面添加 @Transactional注解即可

@Service(value = "adminService")
@Transactional
public class AdminService {
    @Autowired
    AdminDao adminDao;

    public void zhuanzhang(){
        adminDao.sub();
        System.out.println(10/0);
        adminDao.add();
    }

dao层两台哦sql,我们将注解标签添加在service层,这样在遇到异常不会进行事务提交, 如果在dao层,sql语句不会同时执行,会导致一整个事务不完全提交, 转账只转了,收不到。

    public void add(){
        //spring  jdbc默认自动提交事务
        jdbcTemplate.update("update admin set money=money-500 where id= 1");
    }

    public void sub(){
        jdbcTemplate.update("update admin set money=money+500 where id=2");
    }

4.声明式事务失效

1.@Transactional注解 作用在非 public修饰的方法

    @Transactional
     void zhuanzhang(){
        adminDao.sub();
        System.out.println(10/0);
        adminDao.add();
    }

2.异常被catch捕获,代理对象认为方法没有异常,都会执行

    public void zhuanzhang(){
        adminDao.sub();
        try {
            System.out.println(10/0);
        }catch (Exception e){
            
        }
        adminDao.add();
    }
// 出现异常 两部操作都会执行

3.默认情况下方法出现编译期异常 是失效的,我们可以修改配置 将注解标签中的值改为:

@Transactional(rollbackFor = Exception.class)

4.@Transactional 事务传播行为设置错误

5.数据库引擎 不支持事务

6.同一个类中,使用非代理对象调用其他的方法

四、事务传播行为

1.什么是事务传播行为?

传播,即至少有两个东西,单体不存在传播行为。

事务传播行为 指的是当一个事务方法被另一个事务方法调用时,这个事务方法如何进行。 事务传播行为我spring框架独有的事务增强特性,它不属于事务提供方数据库行为。

例如: 方法A 事务方法 调用 方法B, 方法B是继续在 方法A的事务中运行,还是开启一个新事物运行, 就是由方法B的事务传播决定。

2.spring 事务传播

propagation = Propagation.REQUIRED

A方法没有事务,调用B方法, B会自己创建一个事务,在事务中运行,B方法出现异常,不影响A方法
A方法有十五,调用B方法, B 方法会加入A方法, 两者合二为一,任意一方异常,会影响双方
propagation = Propagation.REQUIRES_NEW
A方法没有事务,调用B方法, B会自己创建一个事务,在事务中运行,B方法出现异常,不影响A方法
A方法有事务,调用B方法, B方法会自己创建一个事务,将A方法挂起,A方法出现异常 不影响B

3.测试事务传播

    @Test
    public void test(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        AdminService adminService=applicationContext.getBean("adminService",AdminService.class);
        adminService.saveAdmin();
    }

dao


    public void saveLog(){
        jdbcTemplate.update("insert into log(opertime) values(now())");
    }
    
    public void insertAdmin(){
        //核心业务代码
        jdbcTemplate.update("insert into admin(account,password)values('bbb','666')");
    }

service

    @Transactional(propagation = Propagation.REQUIRED)   
public void savelogg(){

        logDao.saveLog();
        System.out.println(10/0);
    }


    public void saveAdmin(){
        adminDao.insertAdmin();
        logService.savelogg();
    }

在调用的方法中添加了异常, adminDao.insertAdmin()执行不受影响。

当为 admin中添加事务后,都不会执行。

五、spring集成mybatis

1.导入mybatis jar包

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.1</version>
        </dependency>

2.配置 sqlSessionFactory

    <!--配置sqsSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"></property>
        <property name="configLocation" value="classpath:mybatis.xml"></property>
        <property name="mapperLocations" value="classpath:mappers/*Mapper.xml">
        </property>
    </bean>

3.指定生成接口代理

    <!-- 配置为接口生成代理对象-->
    <bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ffyc.ssm.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">
        </property>
    </bean>

正常使用mybatis即可

</bean>

## 3.指定生成接口代理

```xml
    <!-- 配置为接口生成代理对象-->
    <bean id="mapperFactory" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.ffyc.ssm.dao"></property>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory">
        </property>
    </bean>

正常使用mybatis即可

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值