基于AspectJ的AOP开发

AspectJ简介

  • AspectJ是一个基于Java语言的AOP框架
  • Spring2.0以后新增了对AspectJ切点表达式支持
  • @AspectJ是AspectJ1.5新增功能,通过JDK5注解技术,允许直接在Bean类中定义切面
  • 新版本Spring框架,建议使用AspectJ方式来开发AOP
  • 使用AspectJ需要导入Spring AOP和AspectJ相关jar包
    • 除spring四个核心包之外还需要
    • spring-aop
    • aopalliance
    • spring-aspects
    • aspectjweaver

使用AspectJ实现AOP的两种方式

  1. 注解方式
  2. XML方式

注解方式的AOP开发

环境准备
<?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" 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">

    <!-- bean definitions here -->
</beans>
@AspectJ提供不同的通知类型
  • @Before前置通知,相当于BeforeAdvice
  • @AfterReturning后置通知,相当于AfterReturningAdvice
  • @Around环绕通知,相当于MethodInterceptor
  • @AfterThrowing异常抛出通知,相当于ThrowAdvice
  • @After最终final通知,不管是否异常,该通知都会执行
  • @DeclareParents引介通知,相当于IntroductionInterceptor
在通知中通过value属性定义切点

通过execution函数,可以定义切点的方法切入

语法
execution(<访问修饰符>?<返回值类型><方法名>(<参数>)<异常>)

例子

  • 匹配所有类public方法 execution(public * *(..))
  • 匹配指定包下所有类方法 execution(* com.dao.*(..)) ==不包含子包==
  • execution(* com.dao..*(..)) ==包、子包下所有类==
  • 匹配指定类所有方法 execution(* com.service.UserService.*(..))
  • 匹配实现特定接口所有类方法 execution(* com.dao.GenericDAO==+.*(..)) ==+代表子类==
  • 匹配所有save开头的方法 execution(* save*(..))
例子
xml
<?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" 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">

    <!--开启AspectJ的注解开发,自动代理-->
    <aop:aspectj-autoproxy />
    <!--目标类-->
    <bean id="userDao" class="com.UserDao" />
    <!--定义切面-->
    <bean class="com.MyAspect" />
</beans>
目标类
package com;

public class UserDao {

    public void save(){
        System.out.println("保存");
    }

    public void delete(){
        System.out.println("删除");
    }

    public void find(){
        System.out.println("查找");
    }
    
    public void findAll(){
        System.out.println("查找所有");
    }
}
切面类
package com;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MyAspect {

    @Before(value = "execution(* com.UserDao.*(..))")
    public void before(){
        System.out.println("前置通知!!!!");
    }
}

@Before前置通知

可以在方法中传入JoinPoint对象,用来获取切点信息

@Before(value = "execution(* com.UserDao.*(..))")
public void before(JoinPoint joinPoint){
    System.out.println("前置通知!!!!"+joinPoint);
}
@AfterReturing后置通知

通过returning属性,可以定义方法返回值returning,作为参数

@AfterReturning(value = "execution(* com.UserDao.find(..))" , returning = "result")
public void afterReturning(Object result){
    System.out.println("后置通知!!!!" + result);
}
@Around环绕通知

around方法的返回值就是目标代理方法执行返回值,参数ProceedingJoinPoint可以调用拦截目标方法执行,==如果不调用ProceedingJoinPoint的proceed方法,那么目标方法就被拦截了==

@Around(value = "execution(* com.UserDao.delete(..))")
public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    System.out.println("环绕前");
    Object object = proceedingJoinPoint.proceed();
    System.out.println("环绕后");
    return object;
}
@AfterThrowing异常抛出通知

通过设置throwing属性,可以设置发生异常对象参数

@AfterThrowing(value = "execution(* com.UserDao.findAll(..))",throwing = "e")
public void afterThrowing(Throwable e){

    System.out.println("异常抛出通知"+ e.getMessage());
}
@After最终通知

无论是否出现异常,最终通知总是会被执行的

@After(value = "execution(* com.UserDao.findAll(..))")
public void after(){
    System.out.println("最终通知");
}

通过@Pointcut为切点命名

  • 在每个通知内定义切点,会造成工作量大,不易维护,对于重复的切点,可以使用@Pointcut进行定义
  • 切点方法:private void 无参数方法,方法名为切点名
  • 当通知多个切点时,可以使用||进行连接

==这里要注意aspectjweaver的版本==

@Before(value = "pointCutDelete() || pointCutSave()")
public void before(JoinPoint joinPoint){
    System.out.println("前置通知!!!!"+joinPoint);
}

//定义切点
@Pointcut(value = "execution(* com.UserDao.save(..))")
private void pointCutSave(){}

@Pointcut(value = "execution(* com.UserDao.delete(..))")
private void pointCutDelete(){}

XML方式开发AOP

<!--aop的相关配置-->
<aop:config>
    <!--配置切入点-->
    <aop:pointcut id="pointcut1" expression="execution(* com.UserDao.find(..))" />
    <!--配置AOP切面-->
    <aop:aspect ref="myAspect">
        <!--配置前置通知-->
        <aop:before method="before" pointcut-ref="pointcut1" />
    </aop:aspect>
</aop:config>
<!--后置通知以及返回值-->
<aop:after-returning method="before" pointcut-ref="pointcut1" returning="result"/>

猜你喜欢

转载自blog.csdn.net/weixin_34364135/article/details/87615814