Spring AOP的应用

AOP注解方式

开启自动织入支持

在xml中开启

<!-- 开启aop注解支持 -->
<aop:aspectj-autoproxy />

<!-- 开启注解支持,同时强制指定代理机制为cglib -->
<aop:aspectj-autoproxy proxy-target-class="true" />
通过注解开启

@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.simple.say")
@EnableAspectJAutoProxy //开启对AOP相关注解的处理
public class AppByAnnotation {}

定义一个类

@Component
@Slf4j
public class SayHelloImpl implements ISayHello {

    @Override
    public void sayHello() {

        log.info("hello world!");
    }

    @Override
    public void sayHello(String name) {
        log.info("hello, " + name);
    }
}

定义切面类,添加@Aspect注解,@Component注解,因为@Aspect注解没有让Spring作为组件bean扫描的能力,所以我们需要额外添加@Component注解

/**
 * 定义一个切面,负责收集方法调用的入参、出参(返回值)
 */
@Slf4j
@Aspect     // 表示该类是一个切面
@Component  // Aspect切面首先必须也是一个普通的bean
public class MethodInOutAspect {

    // 指定该方法是一个环绕通知,通知注解的参数代表引用一个切入点表达式
    @Around("com.lanou3g.spring.GlobalPointcut.say_all_method()")
    public Object aroundM(ProceedingJoinPoint joinPoint) throws Throwable {

        // 获取连接点方法的名称
        String methodName = joinPoint.getSignature().getName();

        // 获取连接点方法的参数
        Object[] args = joinPoint.getArgs();

        log.debug("[aroundM] "+methodName+"("+ Arrays.toString(args) +") 开始执行");
        Object retuVal = joinPoint.proceed();
        log.debug("[aroundM] "+methodName+"("+ Arrays.toString(args) +") 返回值: " + retuVal);
        return retuVal;
    }
 

}

定义切入点

/**
 * 定义系统中所有用到的切入点表达式
 */
@Component
public class GlobalPointcut {

    /**
     * 通过@Pointcut注解定义切入点表达式
     * 此处表达式含义:拦截com.lanou3g.spring.simple.say包下所有类(包括子包中所有类)中的所有方法
     */
   @Pointcut("execution(* com.lanou3g.spring.simple.say..*.*(..))")
   public void say_all_method() {}

}

定义通知方法

@Slf4j
@Aspect     // 表示该类是一个切面
@Component  // Aspect切面首先必须也是一个普通的bean
public class MethodInOutAspect {

  @AfterReturning(pointcut = "com.lanou3g.spring.GlobalPointcut.say_all_method()", returning = "ret")
    public Object afterRM(Object ret) {
        log.debug("[afterRM] 返回值: " + ret);
        return ret;
    }

}

注解方式入口

/**
 * 纯注解方式入口类
 */
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.simple.say")
@EnableAspectJAutoProxy //开启对AOP相关注解的处理
public class AppByAnnotation {
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppByAnnotation.class);
        ISayHello hello = ctx.getBean(ISayHello.class);
        hello.sayHello("JinSaiSai");
    }
}

Spring AOP的应用

声明式事务

XML配置方式

配置start
<?xml version="1.0" encoding="UTF-8"?>
<!-- 通过xml配置声明式事务,我们需要添加tx命名空间 -->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!-- 通过xml方式配置Spring声明式事务 配置start -->

    <context:component-scan base-package="com.lanou3g.spring.transaction" />

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

    <bean id="teacherDao" class="com.lanou3g.spring.transaction.dao.TeacherDaoImpl">
        <property name="jdbcTemplate" ref="jdbcTemplate" />
    </bean>

配置数据源
jdbc.url=jdbc:mysql://localhost:3306/zzj100301
jdbc.driver=com.mysql.jdbc.Driver
jdbc.user=root
jdbc.password=root
jdbc.characterEncoding=utf8
<context:property-placeholder location="classpath:jdbc.properties" />
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="url" value="${jdbc.url}" />
    <property name="driverClassName" value="${jdbc.driver}" />
    <property name="username" value="${jdbc.user}" />
    <property name="password" value="${jdbc.password}" />
    <property name="connectionProperties">
        <props>
            <prop key="characterEncoding">${jdbc.characterEncoding}</prop>
        </props>
    </property>
</bean>
配置事务管理器
    <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>
配置事务AOP通知
 <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="insert*" rollback-for="ArithmeticException" />
            <tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
        </tx:attributes>
    </tx:advice>

定义AOP配置(将上面的通知和表达式组装到一起
 <aop:config>
        <aop:pointcut id="all_dao_method" expression="execution(* com.lanou3g.spring.transaction.dao.*.*(..))" />
        <aop:advisor advice-ref="txAdvice" pointcut-ref="all_dao_method" />
    </aop:config>
查询数据库中的Student表
package com.lanou3g.spring.transaction.dao;

import com.lanou3g.spring.transaction.bean.Student;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;

// 此注解和@Component作用一样, 只是含有特定的语义(一般用来标注dao层的类)
@Repository
public class StudentDaoImpl {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    /**
     * 查询student表所有数据
     * @return
     */
    public List<Student> queryAll() {
       List<Student> Students = jdbcTemplate.query("select * from Student",
               new BeanPropertyRowMapper<Student>(Student.class));
       return Students;
    }
    


XML方式入口
/**
        * 纯XML方式入口类
        */
@Slf4j
public class AppByTransactionXml {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("tx_conf.xml");
        testQuery(ctx);
       
    }

    static void testQuery(ApplicationContext ctx) {
        StudentDaoImpl studentDao = ctx.getBean(StudentDaoImpl.class);
        List<Student> studentList = studentDao.queryAll();
        for(Student student: studentList) {
            System.out.println("id: " + student.getSid()+", " +
                    "sname: " + student.getSname());
        }
    }

注解配置方式

开启注解支持
/**
 * 纯注解方式入口类
 */
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {

}
配置数据源
/**
 * 配置数据源
 */
@PropertySource("classpath:jdbc.properties")
@Component
public class MyDataSource extends DriverManagerDataSource{
    public MyDataSource(@Value("${jdbc.driver}") String driver, 
                        @Value("${jdbc.url}") String url, 
                        @Value("${jdbc.user}") String userName,
                        @Value("${jdbc.password}") String password, 
                        @Value("${jdbc.characterEncoding}") String characterEncoding) {
        super.setDriverClassName(driver);
        super.setUrl(url);
        super.setUsername(userName);
        super.setPassword(password);
        Properties conProperties = new Properties();
        conProperties.setProperty("characterEncoding", characterEncoding);
        super.setConnectionProperties(conProperties);
    }
}

配置事务管理器
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {

    /**
     * 定义JdbcTemplate对象,Spring给我们封装了所有的JDBC操作
     * @param dataSource
     * @return
     *
     * @Bean相当于xml中的
     * <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
     *         <property name="dataSource" ref="dataSource" />
     * </bean>
     */
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }

    /**
     * 定义事务管理器bean
     * @param dataSource
     * @return
     *
     * @Bean相当于xml中的
     * <bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
     *         <property name="dataSource" ref="dataSource" />
     * </bean>
     *
     */
    @Bean("txManager")
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
查询数据库中的Teacher
// 此注解和@Component作用一样, 只是含有特定的语义(一般用来标注dao层的类)
@Setter
@Getter
@Repository //@Compenent
public class TeacherDaoImpl {   //<bean id="teacherDaoImpl" class="com.lanou3g.spring.transaction.annotation.dao.TeacherDaoImpl">

    @Autowired
    private JdbcTemplate jdbcTemplate;  //<propertype name="jdbcTemplate" ref="" />

    /**
     * 查询teacher表所有数据
     * @return
     */
    // 通过@Transactional 开启事务、设置事务属性
    @Transactional(readOnly = true)
    public List<Teacher> queryAll() {
    
        int rows = jdbcTemplate.update("delete from teacher;");
        System.out.println("影响了" + rows + "行.");
        return null;
    }
注解方式入口
@Slf4j
@Configuration
@ComponentScan(basePackages = "com.lanou3g.spring.transaction.annotation")
// 开启事务相关注解支持
@EnableTransactionManagement
public class AppByTransactionAnnotation {

    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
   
    @Bean("txManager")
    public PlatformTransactionManager transactionManager(DataSource dataSource) {
        return new DataSourceTransactionManager(dataSource);
    }
    
    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppByTransactionAnnotation.class);
        testQuery(ctx);

    }

    static void testQuery(ApplicationContext ctx) {
        TeacherDaoImpl teacherDao = ctx.getBean(TeacherDaoImpl.class);
        List<Teacher> teacherList = teacherDao.queryAll();
        for(Teacher teacher : teacherList) {
            System.out.println("id: " + teacher.getId()+", tname: "
                    + teacher.getTname());
        }
    }
        
    }

Spring事务的隔离级别和传播行为

七种传播行为

PROPAGATION_REQUIRED支持当前事务,如果当前没有事务,就新建一个事务。这是最常见的选择。
PROPAGATION_SUPPORTS支持当前事务,如果当前没有事务,就以非事务方式执行。
PROPAGATION_MANDATORY支持当前事务,如果当前没有事务,就抛出异常。
PROPAGATION_REQUIRES_NEW新建事务,如果当前存在事务,把当前事务挂起
PROPAGATION_NOT_SUPPORTED以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
PROPAGATION_NEVER以非事务方式执行,如果当前存在事务,则抛出异常。
PROPAGATION_NESTED如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则进行与PROPAGATION_REQUIRED类似的操作。

常用的两个事务传播属性是1和4,即PROPAGATIONREQUIRED ,PROPAGATIONREQUIRES_NEW

< tx:advice / >默认配置如下:

  • 传播行为(propagation)是REQUIRED
  • 隔离级别(isolation level)是DEFAULT
  • 事务默认可读写(read-write)
  • 事务超时时间是数据库默认事务超时时间
  • unchecked异常RuntimeException异常触发事务回滚,checked异常Exception不会触发回滚
属性是否必须默认值描述
nameYes与事务属性关联的方法名称。支持*通配符(如, get*, handle*, on*Event
propagationNoREQUIRED事务传播行为
isolationNoDEFAULT事务隔离级别 仅当传播行为设置为 REQUIRED 或者 REQUIRES_NEW时有效.
timeoutNo-1事务超时时间(单位:秒). 仅当传播行为设置为 REQUIRED 或者 REQUIRES_NEW时有效.
read-onlyNofalse设置读写事务或者只读事务. 仅当传播行为设置为 REQUIRED或者 REQUIRES_NEW时有效.
rollback-forNo设置多个可以触发事务回滚的异常(多个用英文逗号隔开). 如,com.foo.MyBusinessException,ServletException.
no-rollback-forNo设置多个禁止触发事务回滚的异常(多个用英文逗号隔开).如,com.foo.MyBusinessException,ServletException.
 <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="insert*" rollback-for="ArithmeticException" />
            <tx:method name="query*"  propagation="REQUIRED" read-only="true" />
        </tx:attributes>
    </tx:advice>

五种隔离级别

  <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="insert*" rollback-for="ArithmeticException" />
            <tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
        </tx:attributes>
    </tx:advice>

参考上面七种传播行为

回滚事务

回滚特定异常

 <tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="insert*" rollback-for="ArithmeticException" /><!--设置回滚特定异常,可以有多个异常,用英文逗号隔开-->
            <tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
        </tx:attributes>
    </tx:advice>

不回滚特定异常

<tx:advice id="txAdvice" transaction-manager="txManager">
        <tx:attributes>
            <tx:method name="insert*" no-rollback-for="ArithmeticException" /><!--设置不回滚特定异常,可以有多个异常,用英文逗号隔开-->
            <tx:method name="query*" isolation="READ_COMMITTED" read-only="true" />
        </tx:attributes>
    </tx:advice>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值