五 基于注解的AOP开发


5.1 快速入门



步骤分析



  • 1. 创建java项目,导入AOP相关坐标
  • 2. 创建目标接口和目标实现类(定义切入点)
  • 3. 创建通知类(定义通知)
  • 4. 将目标类和通知类对象创建权交给spring
  • 5. 在通知类中使用注解配置织入关系,升级为切面类
  • 6. 在配置文件中开启组件扫描和 AOP 的自动代理
  • 7. 编写测试代码



5.1.1 创建 java 项目,导入 AOP 相关坐标



<dependencies>
        <!--导入spring的context坐标,context依赖aop-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <!-- aspectj的织入 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!--spring整合junit-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

5.1.2 创建目标接口和目标实现类

public interface AccountService {
    public void transfer();
}
public class AccountServiceImpl implements AccountService {
    @Override
    public void transfer() {
        System.out.println("转账方法执行了");
    }
}

5.1.3 创建通知类

/*
* 通知类
* */
public class MyAdvice {
    public void before(){
        System.out.println("前置通知执行了");
    }
}

5.1.4 将目标类和通知类对象创建权交给spring

@Service 
public class AccountServiceImpl implements AccountService {} 

@Component 
public class MyAdvice {}

5.1.5 在通知类中使用注解配置织入关系,升级为切面类

/*
* 通知类
* */
@Component
@Aspect // 升级为切面类:配置切入点和通知的关系
public class MyAdvice {

    @Before("execution(* com.lagou..*.*(..))")
    public void before(){
        System.out.println("前置通知执行了");
    }
}

5.1.6 在配置文件中开启组件扫描和 AOP 的自动代理

<!--组件扫描--> 
<context:component-scan base-package="com.lagou"/> 
<!--aop的自动代理--> 
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

5.1.7 编写测试代码

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testTransfer(){
        accountService.transfer();
    }
}

5.2 注解配置AOP详解



5.2.1 切点表达式



 



切点表达式的抽取



/*
* 通知类
* */
@Component
@Aspect // 升级为切面类:配置切入点和通知的关系
public class MyAdvice {

    @Pointcut("execution(* com.lagou..*.*(..))")
    public void myPoint(){

    }

    @Before("MyAdvice.myPoint()")
    public void before(){
        System.out.println("前置通知执行了");
    }
}

5.2.2 通知类型



通知的配置语法: @ 通知注解 (“ 切点表达式 ")



名称

标签

说明

前置通知

@Before

用于配置前置通知。指定增强的方法在切入点方法之前执行

后置通知

@AfterReturning

用于配置后置通知。指定增强的方法在切入点方法之后执行

异常通知

@AfterThrowing

用于配置异常通知。指定增强的方法出现异常后执行

最终通知



 

@After

用于配置最终通知。无论切入点方法执行时是否有异常,都会执行

环绕通知



 

@Around

用于配置环绕通知。开发者可以手动控制增强代码在什么时候执行



注意:



  • 当前四个通知组合在一起时,执行顺序如下:
  • @Before -> @After -> @AfterReturning(如果有异常:@AfterThrowing)

5.2.3 纯注解配置



@Configuration
@ComponentScan("com.lagou")
@EnableAspectJAutoProxy // 开启AOP的自动代理 替代了<aop:aspectj-autoproxy>标签
public class SpringConfig {

}



5.3 知识小结

  • * 使用@Aspect注解,标注切面类
  • * 使用@Before等注解,标注通知方法
  • * 使用@Pointcut注解,抽取切点表达式
  • * 配置aop自动代理 <aop:aspectj-autoproxy/> 或 @EnableAspectJAutoProxy

六 AOP优化转账案例



依然使用前面的转账案例,将两个代理工厂对象直接删除!改为 spring 的 aop 思想来实现



6.1 xml配置实现



1 )配置文件



<?xml version="1.0" encoding="UTF-8"?> 
<beans xmlns="http://www.springframework.org/schema/beans"     
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 
    xmlns:aop="http://www.springframework.org/schema/aop" 
    xmlns:context="http://www.springframework.org/schema/context" 
    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"> 

        <!--开启组件扫描-->
        <context:component-scan base-package="com.lagou"/> 
        <!--开启AOP注解支持--> 
        <aop:aspectj-autoproxy/> 
        <!--加载jdbc配置文件--> 
        <context:property-placeholder location="classpath:jdbc.properties"/> 
        <!--把数据库连接池交给IOC容器--> 
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> 
            <property name="driverClassName" value="${jdbc.driver}"></property> 
            <property name="url" value="${jdbc.url}"></property> 
            <property name="username" value="${jdbc.username}"></property> 
            <property name="password" value="${jdbc.password}"></property> 
        </bean> 
        <!--把QueryRunner交给IOC容器--> 
        <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner"> 
            <constructor-arg name="ds" ref="dataSource"></constructor-arg> 
        </bean>
</beans>



2 )事务管理器(通知)



/*
* 事务管理器工具类:包含:开启事务,提交事务,回滚事务,释放资源
* 通知类
* */
@Component
@Aspect // 表名该类为切面类
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    /*
    * 开启事务
    * */
    public void beginTransaction(){
        // 获取connection对象
        Connection connection = connectionUtils.getThreadConnection();
        try {
            // 开启了一个手动事务
            connection.setAutoCommit(false);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /*
    * 提交事务
    * */
    public void commit(){
        // 获取connection对象
        Connection connection = connectionUtils.getThreadConnection();
        try {
            connection.commit();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /*
    * 回滚事务
    * */
    public void rollback(){
        // 获取connection对象
        Connection connection = connectionUtils.getThreadConnection();
        try {
            connection.rollback();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /*
    * 释放资源
    * */
    public void release(){
        // 获取connection对象
        Connection connection = connectionUtils.getThreadConnection();

        try {
            // 将手动事务改回成自动提交事务
            connection.setAutoCommit(true);
            // 将连接归还到连接池
            connectionUtils.getThreadConnection().close();
            // 解除线程绑定
            connectionUtils.removeThreadConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}



6.2 注解配置实现



1 )配置文件



<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
       http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd
">

    <!--开启注解扫描-->
    <context:component-scan base-package="com.lagou"/>
    <!--引入properties-->
    <context:property-placeholder location="classpath:jdbc.properties"/>
    <!--配置DataSource-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <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>
    <!--配置QueryRunner-->
    <bean id="queryRunner" class="org.apache.commons.dbutils.QueryRunner">
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!--开启AOP的自动代理-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <!--AOP配置-->
    <!--<aop:config>
        <!–切点表达式–>
        <aop:pointcut id="myPointcut" expression="execution(* com.lagou.service.impl.AccountServiceImpl.*(..))"/>
        <!–切面配置–>
        <aop:aspect ref="transactionManager">
            <aop:before method="beginTransaction" pointcut-ref="myPointcut"/>
            <aop:after-returning method="commit" pointcut-ref="myPointcut"/>
            <aop:after-throwing method="rollback" pointcut-ref="myPointcut"/>
            <aop:after method="release" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>-->
</beans>



2 )事务管理器(通知)



/*
* 事务管理器工具类:包含:开启事务,提交事务,回滚事务,释放资源
* 通知类
* */
@Component
@Aspect // 表名该类为切面类
public class TransactionManager {

    @Autowired
    private ConnectionUtils connectionUtils;

    @Around("execution(* com.lagou.service.impl.AccountServiceImpl.*(..))")
    public Object around(ProceedingJoinPoint pjp) throws SQLException {
        Object proceed = null;
        try {
            // 开启手动事务
            connectionUtils.getThreadConnection().setAutoCommit(false);
            // 切入点方法执行
            proceed = pjp.proceed();
            // 手动提交事务
            connectionUtils.getThreadConnection().commit();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            // 手动回滚事务
            connectionUtils.getThreadConnection().rollback();
        } finally {
            // 释放资源
            connectionUtils.getThreadConnection().setAutoCommit(true);
            connectionUtils.getThreadConnection().close();
            connectionUtils.removeThreadConnection();
        }
        return null;
    }
}