JAVA-第十部分-Spring-AOP和事务

382 阅读4分钟

写在前面

Spring-AOP

  • Aspect Orienter Programming,面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术
  • 对业务逻辑的各个部分进行隔离,使业务逻辑各部分耦合度降低,处理业务层的互相引用
  • 抽取方法,通过AOP通过配置方式进行结合,在运行期间,结合不同的业务方法
  • 底层通过动态代理进行实现,在运行期间,spring通过动态代理技术生成代理对象,代理对象执行时进行增强方法的切面介入
  • 动态代理 JDK基于接口的动态代理技术;cglib代理,基于父类的动态代理技术
  • spring根据目标类是否实现了接口来决定采用哪种动态代理方式
  • 动态代理其实就是在原来的方法上增加了新的内容,产生了一个新的子类

AOP术语

  • Target 目标对象,代理的目标对象
  • Proxy 一个类被AOP织入增强后,产生一个新的结果代理类
  • Joinpint 连接点,可以被增强的方法
  • Pointcut 切入点,真正被增强处理后的方法
  • Advice 通知/增强,增强切入点的方法
  • Aspect 切面,切点+增强/通知
  • Weaving 织入,将增强和切点结合的过程

JDK实现动态代理

  • 需要接口,接口实现类,增强类
  • 要用抽象类接收动态代理对象
//增强对象
final Advice advice = new Advice();
//目标对象
final TargetInterfaceImpl targetInterface = new TargetInterfaceImpl();
//返回动态生成的代理对象
//集成目标对象接口实例化的方法
TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
        //目标对象类加载器
        targetInterface.getClass().getClassLoader(),
        //目标对象相同的接口字节码对象数组
        targetInterface.getClass().getInterfaces(),
        new InvocationHandler() {
            // 调用代理对象的任何方法 实质执行的都是invoke方法
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                //前置增强
                advice.before();
                //执行目标方法
                Object invoke = method.invoke(targetInterface, args);
                //后置增强
                advice.after();
                return invoke;
            }
        }
);
//在save的前后集成了advice的两个方法
proxy.save();

cglib实现动态代理

  • 需要目标类,增强类
//增强对象
final Advice advice = new Advice();
//目标对象
final Target target = new Target();
//返回动态生成的代理对象
//基于cglib
//1.创建增强器
Enhancer enhancer = new Enhancer();
//2.设置父类(目标)
enhancer.setSuperclass(Target.class);
//3.设置回调
enhancer.setCallback(new MethodInterceptor() {
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        //执行前置
        advice.before();
        Object invoke = method.invoke(target, objects);
        //执行后置
        advice.after();
        return invoke;
    }
});
//4.创建代理对象
Target proxy = (Target) enhancer.create();
// 执行
proxy.save();

基础用法

  • applicationContext.xml配置,在aop的命名空间下
<!--目标对象-->
<bean id="target" class="com.java.proxy.aop.impl.TargetInterfaceImpl"></bean>
<!--切面对象-->
<bean id="myAspect" class="com.java.proxy.aop.MyAspect"></bean>
<!--配置织入,哪些方法需要被哪些增强-->
<aop:config>
    <!--声明切面-->
    <aop:aspect ref="myAspect">
        <!--切面:增强类型 通知 + 切点-->
        <aop:before method="before" pointcut="execution(public void com.java.proxy.aop.impl.TargetInterfaceImpl.save())"/>
    </aop:aspect>
</aop:config>
  • 切点表达式
excution([修饰符] 返回值类型 包名.类名.方法名(参数类型))
访问修饰符可以省略
返回值、包名、类名、方法名可以使用星号* 通配符
包名与类名之间一个点.代表当前包下的类,两个点..表示当前包及其子包下的类
参数列表可以使用两个点..表示任意个数,任意类型的参数列表
常用 execution(* com.java.aop.*.*(..)) aop包下的任意类的任意方法、任意返回值、任意参数都可以作为切点

增强/通知类型

image.png

  • 环绕通知
//ProceedingJoinPoint pjp 正在执行的连接点 切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("around - before - MyAspect");
    Object proceed = pjp.proceed(); // 切点方法
    System.out.println("around - after - MyAspect");
    return proceed;
}
<!--切面:通知 + 切点-->
<aop:around method="around" pointcut="execution(public void com.java.proxy.aop..*.*(..))"/>
<aop:before method="before" pointcut="execution(public void com.java.proxy.aop..*.*(..))"/>
<aop:after-returning method="after" pointcut="execution(public void com.java.proxy.aop..*.*(..))"/>
<aop:after-throwing method="afterThrowing" pointcut="execution(public void com.java.proxy.aop..*.*(..))"/>
<aop:after method="afterFinal" pointcut="execution(public void com.java.proxy.aop..*.*(..))"/>
  • 切点表达式抽取
<!--抽取切点表达式-->
<aop:pointcut id="myPoint" expression="execution(public void com.java.proxy.aop..*.*(..))"/>
<!--切面:通知 + 切点-->
<aop:around method="around" pointcut-ref="myPoint"/>

注解形式

  • xml.配置
<!--组件扫描-->
<contex:component-scan base-package="com.java.proxy.aopanno"/>
<!--aop自动代理-->
<aop:aspectj-autoproxy/>
  • 切面对象配置
@Component("MyAspect")
@Aspect //标注当前类是一个切面类
public class MyAspect {

    //配置前置通知
    @Before("execution(* com.java.proxy.aopanno..*.*(..))")
    public void before() {
        System.out.println("before - MyAspect");
    }
}
  • 目标对象配置
@Component("Target")
public class TargetInterfaceImpl implements TargetInterface {}

通知类型

image.png

切点表达式的抽取

//方法不用实现,作为注解的宿主
@Pointcut("execution(* com.java.proxy.aopanno..*.*(..))")
public void pointcut() {

}
  • 调用
@After("MyAspect.pointcut()")
public void afterFinal() {
    System.out.println("afterFinal");
}

@Around("pointcut()")
//ProceedingJoinPoint pjp 正在执行的连接点 切点
public Object around(ProceedingJoinPoint pjp) throws Throwable {
    System.out.println("around - before - MyAspect");
    Object proceed = pjp.proceed(); // 切点方法
    System.out.println("around - after - MyAspect");
    return proceed;
}

Spring-事务

  • AOP思想
  • 切点,业务方法;通知,事务方法
  • 配置xml
<!--配置平台事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <property name="dataSource" ref="dataSource"/>
</bean>

<!--通知 事务的增强-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    //设置事务属性信息
    <tx:attributes>
        //被增强的方法
        <tx:method name="*"/>
    </tx:attributes>
</tx:advice>
<!--事务织入-->
<aop:config>
    <aop:advisor advice-ref="txAdvice" pointcut="execution(* com.java.service.impl.*.*(..))"/>
</aop:config>

事务增强属性

<tx:method name="*"  //被增强的方法名称
isolation="DEFAULT"  //隔离级别
propagation="REQUIRED"  //传播级别
timeout="1" //超时时间
read-only="false"/> //是否只读

基于注解

  • @Transactional修饰要增强的方法
@Service("accountService")
//标注在类上,所有方法都被事务增强
@Transactional
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Override
    @Transactional(isolation = Isolation.READ_COMMITTED, propagation = Propagation.REQUIRED)
    public void transfer(String outMan, String inMan, double balance) {
        accountDao.out(outMan, balance);
        int i = 1/ 0;
        accountDao.in(inMan, balance);
    }
}
  • xml配置事务注解驱动
//configuration.java实现
@Bean("transactionManager")
public DataSourceTransactionManager getTransactionManager() {
    DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
    transactionManager.setDataSource(dataSource);
    return transactionManager;
}

<tx:annotation-driven transaction-manager="transactionManager"/>