SpringAOP 知识点大全详细介绍切点表达式

知识点归纳

1. AOP 切入点表达式介绍和使用

2. 实例介绍通知的执行顺序

3. 基于round的的通知执行

4. 基于xml配置的spring aop

4. 基于注解和xml的方式哪个好?

AOP 切入点表达式

Spring aop支持实现AspectJ切断标识符,用于切断表达式,用得最多得是1,2,4.

1. execution 用于匹配方法连接点,用的最多的切点标识符,可以匹配到方法级别,细粒度。基于方法。

    @Before(“execution(* cn.allen.service.impl.*.*(..))”)

2. @annotation 匹配哪些方法上面存在这某个注解的。基于注解。

    @Before(“@annotation(jdk.nashorn.internal.runtime.logging.Logger)”)

3. @within只能匹配到类级别,陪陪类上面有某个注解的。

4. this 用来匹配实现了某个接口的类。基于接口。

5. args按参数进行匹配。

表达式合并&& || !,对应于与 或 非

@Before(“execution(* cn.allen.service.impl.*.*(..)) && @annotation(jdk.nashorn.internal.runtime.logging.Logger)")

Spring AOP 通知的执行顺序

执行顺序: 前置 ->后置 ->后置异常 ->后置返回

正常: @Before -> Method -> @After ->  @AfterReturning

异常:@Before -> Method -> @After -> @AfterThrowing

对与前置通知,后置通知,后置返回通知,后置异常通知的实例说明。

增加一个知识点,JoinPoint 就是我们前面提到过的链接点,在里面可以得到方法的内部的一些具体信息,如方法名字,方法的参数,具体例子如下

前置通知 @Before, 在被调用的方法之前执行。

后置通知@After,在被调用的方法之后执行

后置返回通知@AfterReturning,在被调用的方法执行即将完成,返回结构之前执行。

后置异常通@AfterThrowing,在被调用的方法发实异常时执行。

@Component
@Aspect
public class LogAspect {
    @Before("execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))")
    public void before(JoinPoint point){
        System.out.println("@Before --- "+ point.getArgs()[0] + " is the name for a candidate girl!");
    }

    @After("execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))")
    public void after(JoinPoint point){
        System.out.println("@Before --- "+ point.getSignature().getName() + " was been called just now!");
    }

    @AfterReturning(value = "execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))", returning = "returnVal")
    public void afterReturn( Object returnVal){
        XiaoJieJie xiaojiejie = (XiaoJieJie)returnVal;
        System.out.println("@AfterReturning --- "+ xiaojiejie.getName() + " have been returned !");
    }

    @AfterThrowing(value = "execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))", throwing = "errorInfo")
    public void afterException(Exception errorInfo){
        StringWriter sw = new StringWriter();
        errorInfo.printStackTrace(new PrintWriter(sw,true));
        System.out.println("@AfterThrowing --- unexpected result happens, please check !!!" + sw.getBuffer().toString() );
    }
}

被切的方法有2 个,getByage,getByName:

@Repository
public  class GetXiaoJieJieDaoImpl implements GetXiaoJieJieDao {
    public  XiaoJieJie getByAge(Integer age){
        System.out.println("--- method getByAge is been calling ---!");
        if (age==null){
            throw new RuntimeException("age is null ,please check");
        }
        return new XiaoJieJie("Lucy",age);
    }
    public  XiaoJieJie getByName(String name){
        System.out.println("--- method getByName is been calling ---!");
        return new XiaoJieJie(name,18);
    }
}

测试:

public class TestAop {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext xml = new ClassPathXmlApplicationContext("spring.xml");
        GetXiaoJieJieDao findXiaojiejie = xml.getBean(GetXiaoJieJieDao.class);
        XiaoJieJie girl = findXiaojiejie.getByName("lulu");
        girl.play();
        findXiaojiejie.getByAge(null);
    }
}

执行结果:

@Before --- lulu is the name for a candidate girl!
--- method getByName is been calling ---!
@Before --- getByName was been called just now!
@AfterReturning --- lulu have been returned !
my Master, I am lulu ,18 years old, will you play with me
@Before --- null is the name for a candidate girl!
--- method getByAge is been calling ---!
@Before --- getByAge was been called just now!
@AfterThrowing --- unexpected result happens, please check !!!java.lang.RuntimeException: age is null ,please check
	at com.allen.trainning.spring.aop.dao.impl.GetXiaoJieJieDaoImpl.getByAge(GetXiaoJieJieDaoImpl.java:13)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
	at java.lang.reflect.Method.invoke(Method.java:498)
	at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343)

上面的每个通知方法都写了自己的切点表达式,又都是一样的,这样看起来很不优雅。切点变量,这个概念可以帮我们解决这个问题,这时候就用用到切点注解了@PointCut,如下

    @Pointcut("execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))")
    public void pointCut(){
    }

 所以上面的各个通知上的切点表达式可以用pointCut()这个方法来代替。

    @Before("pointCut()")
    @After("pointCut()")
    @AfterReturning(value = "pointCut()", returning = "returnVal")
    @AfterThrowing(value = "pointCut()", throwing = "errorInfo")

环绕通知的使用@Around

环绕通知简单来说就是一个通知代替上面4个通知,具体实现如下:

@Component
@Aspect
public class LogAspectPointCut {
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinpoint) {
        String methodName = joinpoint.getSignature().getName(); //获取方法名字
        String paraValue = (String)joinpoint.getArgs()[0];//获取方法参数
        Object object = null;
        try {
            System.out.println("前置通知" + methodName+ paraValue);
            object = joinpoint.proceed();
            System.out.println("前置通知");
        } catch (Throwable throwable) {
            System.out.println("异常通知");
        } finally {
            System.out.println("返回通知");
        }
        return object;
    }
}

执行结果:

前置通知 方法名:getByName 参数:lulu
--- method getByName is been calling ---!
后置通知
返回通知
my Master, I am lulu ,18 years old, will you play with me
前置通知 方法名:getByAge 参数:null
--- method getByAge is been calling ---!
异常通知
返回通知

Process finished with exit code 0

基于XML的方式对AOP进行配置

xml里面不能用&&当中切,需要对xml进行转义&&

具体配置如下:

第一步,去掉 enable aop注解的配置

<aop:aspectj-autoproxy/>

第二步,配置对于的aopconfig

<?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:tx="http://www.springframework.org/schema/tx"
       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<context:component-scan base-package="com.allen.trainning.spring.aop"/>
    <aop:config>
        <aop:aspect ref="logAspect">
            <aop:pointcut id="logPointcut" expression="execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))"></aop:pointcut>
            <aop:before method="before" pointcut="execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))&amp;&amp;execution(* com.allen.trainning.spring.aop.dao.impl.*.*(Integer))"></aop:before>
            <aop:after method="after" pointcut-ref="logPointcut"></aop:after>
            <aop:after-returning method="afterReturn" pointcut-ref="logPointcut" returning="returnVal"></aop:after-returning>
            <aop:after-throwing method="afterException" throwing="errorInfo" pointcut-ref="logPointcut"></aop:after-throwing>
        </aop:aspect>
    </aop:config>

    <aop:config>
        <aop:aspect ref="logAspectPointCut">
            <aop:pointcut id="logPointcut" expression="execution(* com.allen.trainning.spring.aop.dao.impl.*.*(..))"></aop:pointcut>
            <aop:around method="around" pointcut-ref="logPointcut" ></aop:around>
        </aop:aspect>
    </aop:config>
</beans>

测试结果,和注解的测试结果一样, 就不贴2遍了。

注解和xml那个用呢, 当时是注解

如下, 注解可以合并自己定义的多个切点表达式,xml不行

注解用起来也比较简单方便。

面试题:

1. spring aop通知类型偶哪几个?

2. 基于注解和xml实现分别步骤是什么?

猜你喜欢

转载自blog.csdn.net/pengweismile/article/details/109849548