Spring AOP和事务管理

一.AOP简介

1.概念 : AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统 一维护的一种技术。

2.作用:在程序运行期间,在不修改源码的情况下对方法进行功能增强

3.优势:减少重复代码,提高开发效率,并且便于维护

4.底层:实际上,AOP的底层是通过Spring提供的的动态代理技术实现的。在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强。

二.AOP核心概念

1.通知(Advice): 指拦截到Joinpoint之后要做的事, 即对切入点增强的内容

2.切入点(Pointcut): 需要增加该功能的方法, 必须定义成切入点

3.连接点(JoinPoint): 可以增加该功能的方法, 即程序中任意位置的方法, 连接点包含着切入点

4.切面(Aspect): 连接通知和切入点之间的桥梁, 描述两者之间的关系

5.目标对象(Target):代理的目标对象

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

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

8.引入(Introduction):用来给一个类型声明额外的方法或属性(也被称为连接类型声明(inter-type declaration))。Spring允许引入新的接口(以及一个对应的实现)到任何被代理的对象。例如,你可以使用引入来使一个bean实现IsModified接口,以便简化缓存机制

三.AOP工作流程

1.Spring容器启动

2.读取所有切面配置中的切入点

3.初始化bean, 判定bean对应的类中的方法是否匹配到任意切入点 匹配失败, 创建对象 匹配成功, 创建原始对象(目标对象)的代理对象 System.out.println(bookDao.getClass()) 出来的结果是代理对象class com.sun.proxy.$Proxy20

4.获取bean执行方法 获取bean, 调用方法并执行, 完成操作 获取的bean是代理对象时, 根据代理对象的运行模式运行原始方法与增强的内容, 完成操作

四.基于XML的APO开发

1、切点表达式的写法

语法  execution([修饰符] 返回值类型 包名.类名.方法名(参数))

(1)访问修饰符可以省略 (2)返回值类型、包名、类名、方法名可以使用*号代表任意 (3)包名与类名之间一个点,代表当前包下的类,两个点,表示当前包下的类 (4)参数列表可以使用两个点…表示任意个数,任意类型

例:

execution(public void com.itcast.aop.Tartget.save())
execution(void com.itcast.aop.Tartget.(…))–任意参数的所有方法
execution( com.itcast.aop..(…))
execution(* com.itcast.aop….(…))
execution(* ….*(…))

2、通知类型

配置语法:

<aop:通知类型 method=“切面类总方法名” pointcut=”切点表达式“></aop:通知类型 >
通知说明
before(前置通知)通知方法在目标方法调用之前执行
after(后置通知)通知方法在目标方法返回或异常后调用。相当于finally块的内容
afterReturning(返回后通知)通知方法会在目标方法返回后调用
afterThrowing(抛出异常通知)通知方法在目标方法抛出异常后调用
around(环绕通知)通知方法会将目标方法封装起来

3.操作步骤

(1)导入AOP的相关坐标

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.0.3.RELEASE</version>
</dependency>
<!--aspectj框架-->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.13</version>
</dependency>

(2)创建目标接口和目标类

public interface TargetInterface {
​
    public void save();
​
}
​
public class Tartget implements TargetInterface {
    public void save() {
        System.out.println("save running.....");
    }
}

(3)创建切面类

public class MyAspect {
​
    public void before(){
        System.out.println("前置增强Aspect.....");
    }
    
    public void after(){
        System.out.println("后置增强Aspect.....");
    }
​
}

(4)将目标类和切面类的对象创建权交给spring

<!--目标对象-->
<bean id="target" class="com.itcast.aop.Tartget"></bean>
​
<!--切面对象-->
<bean id="myAspect" class="com.itcast.aop.MyAspect" ></bean>

(5)在applicationContext.xml中配置织入关系

<!--织入,告诉spring框架,哪些方法(切点)需要进行哪些增强(前置、后置。。。。)-->
<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
        <!--切面,切点,通知-->
        <aop:before method="before" pointcut="execution(public void com.itcast.aop.Tartget.save())"></aop:before>
    </aop:aspect>
</aop:config>

(6)测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class AOPTest {
    @Autowired
    private TargetInterface targetInterface;
​
​
    @Test
    public void test(){
        targetInterface.save();
    }
​
}

五.基于注解的AOP开发

(1)创建接口和实现类

public interface TargetInt {
​
public void save();
​
}
​
@Component("target")
public class TargetAnno implements TargetInt{
    public void save() {
        System.out.println("save running......");
    }
}

(2)创建切面类

@Component("myAspect")
//标注当前类是一个切面类
@Aspect
​
public class MyAspect {
    //定义切点表达式
    @Pointcut("execution(public void com.itcast.anno.*.*(..))")
    public void pointcut()
    }
​
@Before(value="MyAspect.pointcut()")
public void before(){
    System.out.println("前置增强Aspect.....");
}
​
@AfterReturning(value = "execution(public void com.itcast.anno..*.*(..))")
public void afterReturing(){
    System.out.println("后置增强Aspect.....");
}
​
//正在执行的连接点
@Around("execution(public void com.itcast.anno..*.*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("环绕前增强");
    //切入方法
    Object proceed = pjp.proceed();
    System.out.println("后置增强");
    return proceed;
}
​
//异常抛出
@AfterThrowing("execution(public void com.itcast.anno..*.*(..))")
public void afterThrowing(){
    System.out.println("异常抛出增强.....");
}
​
//最终增强
@After("execution(public void com.itcast.anno..*.*(..))")
public void after(){
    System.out.println("最终增强.....");
}
​
}

(3)将目标类和切面类的对象创建权交给spring,并在配置文件中开启组件扫描和AOP的自动代理

@Configuration
@ComponentScan(basePackages = {"com.itcast.anno"})
@EnableAspectJAutoProxy //AOP自动代理
public class AnnoConfig {
}

(4)测试

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = AnnoConfig.class)
public class AnnoTest {
    @Autowired
    private TargetInt targetInterface;
​
​
    @Test
    public void test(){
        targetInterface.save();
    }
​
}
 

六.事务管理

1.概念

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。事务的结束有两种,当事务中的所以步骤全部成功执行时,事务提交。如果其中一个步骤失败,将发生回滚操作,撤消撤消之前到事务开始时的所以操作。

2.事务的ACID原则

事务具有4个基本特性:原子性、一致性、隔离性、持久性。也就是我们常说的ACID原则。

  • 原子性(Atomicity):

    一个事务已经是一个不可再分割的工作单位。事务中的全部操作要么都做;要么都不做

    例如:A和B两个人一共1000元,A给B转账100元,A付款100元,B收款100元,
         A的付款行为和B的收款行为要么都成功,要么都失败
  • 一致性(Consistency):

    事务的执行使数据从一个状态转换为另一个状态,但是对于整个数据的完整性保持稳定。

    例如:A和B两个人一共1000元,无论A,B两人互相转账多少次,A和B两个人总额都应该是1000元
  • 隔离性(Isolation):

    事务允许多个用户对同一个数据进行并发访问,而不破坏数据的正确性和完整性。同时,并行事务的修改必须与其他并行事务的修改相互独立。

    例如:万达影院有《叶问4》电影票100张,允许所有人同时去淘票票上购票,当第100张电影票被A,B,C3人同时购买,如果A拿到第100张电影票,但是还在犹豫要不要付钱,则B,C必须等待A的决定,如果A决定付钱,B.C就无法抢到票,如果A超时不付钱,则这第100张电影票回归票池,从新分配。
  • 持久性(Durability):

    一个事务一旦提交,它对数据库中数据的改变会永久存储起来。其他操作不会对它产生影响

    例如:万达影院有《叶问4》电影票100张,100张电影票销售完毕,对于每个购买者来说,他的购买记录已经产生,即使退票,他之前的购买记录也不会消失。

3.事务隔离级别

隔离级别自上而下级别越来越高,性能越来越低,隔离级别分别是:

  • 读未提交:一个事务还没提交时,它做的变更就能被别的事务看到。

  • 读已提交:一个事务提交之后,它做的变更才会被其他事务看到。

  • 可重复读(默认):一个事务执行过程中看到的数据,总是跟这个事务在启动时看到的数据是一致的。当然在可重复读隔离级别下,未提交变更对其他事务也是不可见的。

  • 串行化:“写”会加“写锁”,“读”会加“读锁”。当出现读写锁冲突的时候,后访问的事务必须等前一个事务执行完成,才能继续执行。

事务隔离级别脏读不可重复读幻读
读未提交(read-uncommitted)
不可重复读(read-committed)
可重复读(repeatable-read)
串行化(serializable)
3.1 引发的问题
  • 脏读 脏读是指一个事务读取了另一个事务未提交的数据

  • 不可重复读 不可重复读是指一个事务对同一数据的读取结果前后不一致。脏读和不可重复读的区别在于:前者读取的是事务未提交的脏数据,后者读取的是事务已经提交的数据,只不过因为数据被其他事务修改过导致前后两次读取的结果不一样

  • 幻读 幻读是指事务读取某个范围的数据时,因为其他事务的操作导致前后两次读取的结果不一致。幻读和不可重复读的区别在于,不可重复读是针对确定的某一行数据而言,而幻读是针对不确定的多行数据。因而幻读通常出现在带有查询条件的范围查询中

    4.事务的管理方式

  • 编程式事务管理 :编程式事务管理是通过编写代码实现的事务管理。可以根据需求规定事务从哪里开始,到哪里结束,拥有很高的灵活性。但是这种方式,会使业务代码与事务规则高度耦合,难以维护,因此我们很少使用这种方式对事务进行管理。

  • 声明式事务管理: 采用声明的方式来处理事务,可以通过 2 种方式实现,分别是 XML和注解方式。Spring 在进行声明式事务管理时,底层使用了 AOP 。事务管理不侵入开发的组件。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值