十、AspectJ开发

AspectJ是一个基于Java语言的AOP框架,它提供了强大的AOP功能。

10.1 基于xml的声明式AspectJ

基于xml的声明式AspectJ是通过写满了文件来定义切面、切入点以及通知,所有的切面、切入点和通知都必须定义在aop:config元素内。

spring切面失效 spring配置切面使用什么元素_spring切面失效

注意:Spring配置文件中的beans元素下可以包含多个aop:config元素,一个aop:config元素又可以包含属性和子元素,其子元素包括pointcut、advisor和aspect。在配置时,这3个子元素必须按照此顺序来定义!

配置文件的步骤和相关属性

  1. 配置切面:使用aop:aspect元素,通常先定义好一个bean,然后使用ref属性引用即可。

性名称

描述

id

用于定义该切面的唯一标识名称

ef

用于引用普通的Spring Bean

  1. 配置切入点:使用aop:pointcut元素来定义。当aop:pointcut元素作为aop:config元素的子元素定义时,标识该切入点是全局切入,它可被多个切面所共享;当aop:pointcut元素作为aop:aspect元素的子元素时,表示该切入点只对当前切面有效。

性名称

描述

id

用于指定切入点的唯一标识名称

expression

用于指定切入点关联的切入点表达式

  1. 配置通知:使用aop:aspect的子元素配置5种常用通知,这5个子元素不支持使用子元素,但是在使用时可以指定一些属性:

属性名称

描述

ointcut

该属性用于指定一个切入点表达式,Spring将在匹配该表达式的连接点时织入该通知

pointcut-ref

该属性指定一个已经存在的切入点名称,如配置代码中的myPointCut。通常pointcut和pointcut-ref两个属性只需要使用其中之一

method

该属性指定一个方法名,指定将切面Bean中的该方法转换为增强处理

throwing

该属性只对after-throwing元素有效,它用于指定一个形参名,异常通知方法可以通过该形参访问目标方法所抛出的异常

returning

该属性只对after-returning元素有效,它用于指定一个形参名,后置通知方法可以通过该形参访问目标方法的返回值

基于通知函数的AspectJ

通知类MyAdvice.java

package cn.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
public class MyAdvice {
    public void mybefore(){
        System.out.println("我的前置");
    }
    public void myafter(){
        System.out.println("即使出现异常也执行的后置通知");
    }
    public void myAftering(){
        System.out.println("出现异常就不执行的后置通知");
    }
    public Object myAround(ProceedingJoinPoint p) throws Throwable{
        System.out.println("函数执行之前");
        //唤醒环绕的函数
        Object obj=p.proceed();
        System.out.println("函数执行之后");
        return obj;
    }
}

配置文件MyAdvice.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:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.2.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd">
    <!-- 基于通知函数的AsceptJ -->
    <!-- 配置目标对象 -->
    <bean id="book1" class="cn.action.BookServiceImp"/>
    <!-- 配置通知类 -->
    <bean id="myadvice" class="cn.aop.MyAdvice"/>

    <aop:config>
        <!--配置切入面 (在哪个方法的哪里调用该方法)-->
        <!-- 配置切面 -->
        <aop:aspect ref="myadvice">
            <!-- 配置切入点 (要使用哪个方法)-->
            <aop:pointcut expression="execution(* cn.action.BookServiceImp.*(..))" id="mypoint"/>
            <!-- 配置切面的切入方式  前置-->
            <aop:before method="mybefore" pointcut-ref="mypoint"/>
            <!-- 配置切面的切入方式  后置1-->
            <aop:after method="myafter" pointcut-ref="mypoint"/>
            <!-- 配置切面的切入方式  后置2-->
            <aop:after-returning method="myAftering" pointcut-ref="mypoint"/>
            <!-- 配置切面的切入方式  环绕-->
            <aop:before method="myAround" pointcut-ref="mypoint"/>
        </aop:aspect>
    </aop:config>
</beans>

测试类test.java

package cn.test;
import cn.action.BookService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test3 {
    public static void main(String[] args) {
        //1.加载bean工厂
        BeanFactory bf=new ClassPathXmlApplicationContext("cn/MyAdvice.xml");
        //2.通过代理方式获得bean
        BookService bs=(BookService) bf.getBean("book1");
        //3.测试
        bs.buy();
        bs.buyJava();
        bs.buyJavaEE();
        bs.buyJsp();
        bs.comment();
    }
}

10.2 基于注解的声明式AspectJ

AspectJ框架为AOP实现提供了一套注解,用以取代Spring配置文件中实现AOP功能所配置的臃肿代码。

注解名称

描述

@Aspect

用于定义一个切面

@Pointcut

用于定义切入点表达式,在使用时还需定义一个包含名字和任意参数的方法签名来表示切入点名称。实际上,这个方法签名就是一个返回值为void,且方法体为空的普通的方法

@Before

用于定义前置通知,相当于BeforeAdvice。在使用时,通常需要指定一个value属性值,该属性值用于指定一个切入点表达式(可以是已有的切入点,也可以直接电仪切入点表达式)

@AfterReturning

用于定义后置通知,相当于AfterReturningAdvice。在使用时可以指定pointcut/value和returning属性,其中pointcut/value这两个属性的作用一样,都用于指定切入点表达式,returning属性值用于表Advice方法中可定义与此同名的形参,该形参可用于访问目标方法的返回值

@Around

用于定义环绕通知,相当于MethodInterceptor。在使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点

@AfterThrowing

用于定义异常通知来处理程序中未处理的异常,相当于ThrowAdvice。在使用时可指定pointcut/value和throwing属性。其中pointcut/value用于指定切入点表达式,而throwing属性值用于指定一个形参名来表示Advice方法中可定义与此同名的形参,该形参可用于访问目标方法跑出的异常

@After

用于定义最终final通知,不管是否异常,该通知都会执行。使用时需要指定一个value属性,该属性用于指定该通知被植入的切入点

@DeclareParents

用于定义引介通知,相当于IntroductionInterceptor(不要求掌握)

基于通知函数的AspectJ注解方式

通知类MyAdvice.java

package cn.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAdvice {
    //如何来配置切入点
    @Pointcut("execution(* com.qhj.service.BookServiceImp.*(..))")
    public void myPointCut(){}

    @Pointcut("execution(* com.qhj.service.BookServiceImp.buy*(..))")
    public void myPointCutBuy(){}


    //配置前置函数
    @Before("myPointCut()")
    public void mybefore(){
        System.out.println("我的前置");
    }
    //配置前置函数1
    @After("myPointCut()")
    public void myafter(){
        System.out.println("即使出现异常也执行的后置通知");
    }
    //配置前置函数2
    @AfterReturning("myPointCut()")
    public void myAftering(){
        System.out.println("出现异常就不执行的后置通知");
    }
    //配置环绕通知
    @Around("myPointCut()")
    public Object myAround(ProceedingJoinPoint p) throws Throwable{
        System.out.println("函数执行之前");
        //唤醒环绕的函数
        Object obj=p.proceed();
        System.out.println("函数执行之后");
        return obj;
    }
}

配置文件MyAdvice.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"
       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="cn.*"/>
    <!--启动基于注解的声明式AspectJ支持-->
    <aop:aspectj-autoproxy/>
</beans>

测试类test.java

package cn.test;
import cn.action.BookService;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class test3 {
    public static void main(String[] args) {
        //1.加载bean工厂
        BeanFactory bf=new ClassPathXmlApplicationContext("cn/MyAdvice.xml");
        //2.通过代理方式获得bean
        BookService bs=(BookService) bf.getBean("book1");
        //3.测试
        bs.buy();
        bs.buyJava();
        bs.buyJavaEE();
        bs.buyJsp();
        bs.comment();
    }
}