springAOP 的实现方式
三种
纯 xml 方式, xml + 注解, 纯注解方式.
Spring 实现 AOP 思想使的是动态代理技术
默认情况下, Spring 会根据被代理对象是否实现接来选择使 JDK 还是 CGLIB. 当被代理对象没有实现
任何接时, Spring 会选择 CGLIB. 当被代理对象实现了接, Spring 会选择 JDK 官的代理技术, 不过
我们可以通过配置的式, 让 Spring 强制使 CGLIB.
接下来我们开始实现 aop,
需求是: 横切逻辑代码是打印志, 希望把打印志的逻辑织到标法的特定位置 (service 层 transfer 法)
纯 xml 方式
引入 aop 相关的 jar 包
- <dependency>
- <groupId>org.springframework</groupId>
- <artifactId>spring-aop</artifactId>
- <version>5.1.12.RELEASE</version>
- </dependency>
- <dependency>
- <groupId>org.aspectj</groupId>
- <artifactId>aspectjweaver</artifactId>
- <version>1.9.4</version>
- </dependency>
TransferServiceImpl.java 文件:
- package com.lagou.edu.service.impl;
- import com.lagou.edu.dao.AccountDao;
- import com.lagou.edu.pojo.Account;
- import com.lagou.edu.service.TransferService;
- import com.lagou.edu.utils.ConnectionUtils;
- import com.lagou.edu.utils.TransactionManager;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.beans.factory.annotation.Qualifier;
- import org.springframework.context.annotation.ImportResource;
- import org.springframework.stereotype.Component;
- import org.springframework.stereotype.Service;
- /**
- * @author 应癫
- */
- @Service("transferService")
- public class TransferServiceImpl implements TransferService {
- // 最佳状态
- // @Autowired 按照类型注入 , 如果按照类型无法唯一锁定对象, 可以结合 @Qualifier 指定具体的 id
- @Autowired
- @Qualifier("accountDao")
- private AccountDao accountDao;
- @Override
- public void transfer(String fromCardNo, String toCardNo, int money) throws Exception {
- /*try{
- // 开启事务 (关闭事务的自动提交)
- TransactionManager.getInstance().beginTransaction();*/
- System.out.println("执行转账业务逻辑");
- Account from = accountDao.queryAccountByCardNo(fromCardNo);
- Account to = accountDao.queryAccountByCardNo(toCardNo);
- from.setMoney(from.getMoney()-money);
- to.setMoney(to.getMoney()+money);
- accountDao.updateAccountByCardNo(to);
- //int c = 1/0;
- accountDao.updateAccountByCardNo(from);
- }
- }
打印日志 Util:
- package com.lagou.edu.utils;
- /**
- * @author 应癫
- */
- public class LogUtils {
- /**
- * 业务逻辑开始之前执行
- */
- public void beforeMethod(JoinPoint joinPoint) {
- Object[] args = joinPoint.getArgs();
- for (int i = 0; i <args.length; i++) {
- Object arg = args[i];
- System.out.println(arg);
- }
- System.out.println("业务逻辑开始执行之前执行.......");
- }
- /**
- * 业务逻辑结束时执行 (无论异常与否)
- */
- public void afterMethod() {
- System.out.println("业务逻辑结束时执行, 无论异常与否都执行.......");
- }
- /**
- * 异常时时执行
- */
- public void exceptionMethod() {
- System.out.println("异常时执行.......");
- }
- /**
- * 业务逻辑正常时执行
- */
- public void successMethod(Object retVal) {
- System.out.println("业务逻辑正常时执行.......");
- }
- }
- public Object arroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
- System.out.println("环绕通知中的 beforemethod....");
- Object result = null;
- try{
- // 控制原有业务逻辑是否执行
- // result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
- }catch(Exception e) {
- System.out.println("环绕通知中的 exceptionmethod....");
- }finally {
- System.out.println("环绕通知中的 after method....");
- }
- return result;
- }
applicationContext.xml
- <!-- 进行 aop 相关的 xml 配置, 配置 aop 的过程其实就是把 aop 相关术语落地 -->
- <!-- 横切逻辑 bean-->
- <bean id="logUtils" class="com.lagou.edu.utils.LogUtils"></bean>
- <!-- 使用 config 标签表明开始 aop 配置, 在内部配置切面 aspect-->
- <!--aspect = 切入点 (锁定方法) + 方位点 (锁定方法中的特殊时机)+ 横切逻辑 -->
- <aop:config>
- <aop:aspect id="logAspect" ref="logUtils">
- <!-- 切入点锁定我们感兴趣的方法, 使用 aspectj 语法表达式 -->
- <!--.. 参数中的两个点表示可以有参数, 也可以没有参数, 有的话也可以是任意类型, 参数中的 * 表示参数可以是任意类型, 但必须有参数. -->
- <!-- 包名中的.. 两个点表示中间可以是任意层 -->
- <!--<aop:pointcut id="pt1" expression="execution(* *..*.*(..))"/>-->
- <aop:pointcut id="pt1" expression="execution(public void com.lagou.edu.service.impl.TransferServiceImpl.transfer(java.lang.String,java.lang.String,int))"/>
- <!-- <aop:pointcut id="pt1" expression="execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..))"/>
- -->
- <!-- 方位信息, pointcut-ref 关联切入点 -->
- <!--aop:before 前置通知 / 增强 -->
- <aop:before method="beforeMethod" pointcut-ref="pt1"/>
- <!--aop:after, 最终通知, 无论如何都执行 -->
- <!--aop:after-returnning, 正常执行通知, retValue 是接受方法的返回值的 -->
- <aop:after-returning method="successMethod" returning="retValue"/>
- <!--aop:after-throwing, 异常通知 -->
- <aop:around method="arroundMethod" pointcut-ref="pt1"/>
- </aop:aspect>
- </aop:config>-->
测试:
- /**
- * 测试 xml aop
- */
- @Test
- public void testXmlAop() throws Exception {
- ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
- TransferService transferService = applicationContext.getBean(TransferService.class);
- transferService.transfer("6029621011000","6029621011001",100);
- }
环绕通知不和前置及后置通知一起使用, 因为环绕通知可以实现前置和后置的功能, 并且可以控制原有业务逻辑是否执行, 非常强大.
xml + 注解方式
将上面纯 xml 方式改为注解方式
将 applicationContext.xml 中的内容取掉, 改为类中添加注解:
- package com.lagou.edu.utils;
- import org.aspectj.lang.JoinPoint;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.*;
- import org.springframework.stereotype.Component;
- /**
- * @author 应癫
- */
- @Component
- @Aspect
- public class LogUtils {
- @Pointcut("execution(* com.lagou.edu.service.impl.TransferServiceImpl.*(..))")
- public void pt1(){
- }
- /**
- * 业务逻辑开始之前执行
- */
- @Before("pt1()")
- public void beforeMethod(JoinPoint joinPoint) {
- Object[] args = joinPoint.getArgs();
- for (int i = 0; i <args.length; i++) {
- Object arg = args[i];
- System.out.println(arg);
- }
- System.out.println("业务逻辑开始执行之前执行.......");
- }
- /**
- * 业务逻辑结束时执行 (无论异常与否)
- */
- @After("pt1()")
- public void afterMethod() {
- System.out.println("业务逻辑结束时执行, 无论异常与否都执行.......");
- }
- /**
- * 异常时时执行
- */
- @AfterThrowing("pt1()")
- public void exceptionMethod() {
- System.out.println("异常时执行.......");
- }
- /**
- * 业务逻辑正常时执行
- */
- @AfterReturning(value = "pt1()",returning = "retVal")
- public void successMethod(Object retVal) {
- System.out.println("业务逻辑正常时执行.......");
- }
- /**
- * 环绕通知
- *
- */
- /*@Around("pt1()")*/
- public Object arroundMethod(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
- System.out.println("环绕通知中的 beforemethod....");
- Object result = null;
- try{
- // 控制原有业务逻辑是否执行
- // result = proceedingJoinPoint.proceed(proceedingJoinPoint.getArgs());
- }catch(Exception e) {
- System.out.println("环绕通知中的 exceptionmethod....");
- }finally {
- System.out.println("环绕通知中的 after method....");
- }
- return result;
- }
- }
在 application.xml 中配置注解驱动:
- <!-- 开启 aop 注解驱动
- proxy-target-class:true 强制使用 cglib
- -->
- <aop:aspectj-autoproxy/>
纯注解模式
我们只需要替换掉 xml + 注解模式中的注解驱动的部分即可,
将
- <!-- 开启 aop 注解驱动
- proxy-target-class:true 强制使用 cglib
- -->
- <aop:aspectj-autoproxy/>
改为 @EnableAspectJAutoProxy // 开启 spring 对注解 AOP 的持, 在项目中添加到任意个配置类上即可.
欢迎访问:
微信公众号 (程序员资料站):code_data
来源: https://www.cnblogs.com/liuyj-top/p/13346206.html