Spring配置切面的几种方式

1、实现MethodBeforeAdvice等接口

pom.xml添加spring核心依赖:

<dependencies>
    <!-- https://mvnrepository.com/artifact/org.springframework/spring-context -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
</dependencies>

在Spring中,org.springframework.aop包下有四个接口,分别是MethodBeforeAdvice(前置通知)、AfterReturningAdvice(后置通知)、MethodInterceptor(环绕通知)、ThrowsAdvice(异常通知),其中,前三个接口都有对应的实现方法,分别实现后就可以在对应的通知方法中添加功能,但是ThrowsAdvice异常通知没有实现方法,所以需要自定义一个方法,不过对方法名有规定,必须写成afterThrowing,代码如下:

定义一个切面类UserServiceAdvice:

public class UserServiceAspect implements MethodBeforeAdvice, AfterReturningAdvice,
        MethodInterceptor, ThrowsAdvice {

    /**
     * 后置通知
     * @param o
     * @param method
     * @param objects
     * @param o1
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("后置通知");
    }

    /**
     * 前置通知
     * @param method
     * @param objects
     * @param o
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("前置通知。。。");
    }

    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("环绕通知前。。。");
        Object returnVal = methodInvocation.proceed();
        System.out.println("环绕通知后。。。");
        return returnVal;
    }

    /**
     * 异常通知,参照MethodBeforeAdvice,
     * 该方法的方法名必须叫做afterThrowing,
     * method,args,target这三个参数可以省略,要么全部声明
     * Exception必须保留
     * @param e
     */
    public void afterThrowing(Exception e){
        System.out.println("产生了异常:"+e.getMessage());
    }
}

在resources目录下创建一个Spring配置文件applicationContext.xml:

在这个配置文件中主要是做对bean的配置和对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 id="userService" class="edu.nf.ch12.service.impl.UserServiceImpl" />
    <!-- 切面 -->
    <bean id="userServiceAspect" class="edu.nf.ch12.service.aspect.UserServiceAspect" />
    <!-- aop配置, -->
    <aop:config proxy-target-class="false">
        <!-- 配置切入点,使用aspectJ的切入点表达式,
         表达式语法:
         1、execution(访问修饰符 方法返回值 包名.类名.方法名(参数类型))
            execution是切入到方法级别的
         2、within(访问修饰符 方法返回值 包名.类名)
            within是切入到类级别的
         说明:访问修饰符可以省略,
         方法返回值、包名、类名、方法名、可以使用*号进行统配,
         方法参数可以指定参数的类型,也可以使用".."来标识任意类型和个数的参数
         例如:execution(* edu.nf.ch12.service.impl.*.*(..))
         表示edu.nf.ch12.service.impl包下的所有类,以及任意返回值类和任意参数类型和个数的方法都会匹配-->
        <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch12.service.impl.UserServiceImpl.say(..))"/>
        <!-- 配置通知器(Advisor),其实就是切面
         advice-ref引用上面定义的切面的id
         pointcut-ref引用上面定义的切入点的id-->
        <aop:advisor advice-ref="userServiceAspect" pointcut-ref="myPointcut"/>
        <!-- 当有多个切面,但是又不想共用一个切入点表达式的时候,那么使用pointcut属性来重新制定切入点表达式 -->
        <aop:advisor advice-ref="demoAspect" pointcut="execution(* edu.nf.ch12.service.impl.UserServiceImpl.run())"/>
    </aop:config>
</beans>

2、使用AspectJ配置,不实现任何接口

(1)使用配置文件配置切面

当使用AspectJ配置时,不需要实现任何接口,因为对这些通知的方法都是在配置文件中配置和绑定的。

创建一个切面类:

public class UserServiceAspect {

    /**
     * 前置通知
     * @param jp 连接点,通过这个连接点可以获取目标方法
     */
    public void before(JoinPoint jp){
        System.out.println("前置通知,目标方法参数:" + jp.getArgs()[0]);
    }

    /**
     * 环绕通知
     * @param pjp 连接点,可以获取目标方法参数以及方法信息以及调用目标方法等等
     */
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕通知前。。。");
        //获取目标方法的的Method对象
        MethodSignature ms = (MethodSignature)pjp.getSignature();
        Method method = ms.getMethod();
        System.out.println("当前调用的目标方法:" + method.getName());
        //调用目标方法
        Object returnVal = pjp.proceed();
        System.out.println("环绕通知后。。。");
        return returnVal;
    }

    /**
     * 后置通知
     * @param returnVal 目标方法的返回值
     */
    public void afterReturning(String returnVal){
        System.out.println("后置通知,返回参数:" + returnVal);
    }

    /**
     * 异常通知
     * @param e 目标方法产生的异常对象
     */
    public void afterThrowing(Throwable e){
        System.out.println("异常通知,异常信息:" + e.getMessage());
    }

    /**
     * 最终通知
     */
    public void after(){
        System.out.println("最终通知");
    }
}

在resources目录中创建一个Spring配置文件applicationContext.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">

    <bean id="userService" class="edu.nf.ch13.service.impl.UserServiceImpl"/>
    <!-- 定义切面 -->
    <bean id="userServiceAspect" class="edu.nf.ch13.service.aspect.UserServiceAspect"/>

    <!-- 配置AOP,基于AspectJ -->
    <aop:config>
        <aop:pointcut id="myPointcut" expression="execution(* edu.nf.ch13.service.impl.UserServiceImpl.*(..))"/>
        <!-- 装配切面,ref引用上面配置的切面的id -->
        <aop:aspect ref="userServiceAspect">
            <!-- 装配通知,method对应通知的方法名,pointcut-ref引用上面定义的切入点的id
             如果不同的通知想使用不同的切入点,那么使用pointcut属性进行自定义 -->
            <!-- 前置通知 -->
            <aop:before method="before" pointcut-ref="myPointcut"/>
            <!-- 环绕通知 -->
            <aop:around method="around" pointcut-ref="myPointcut"/>
            <!-- 后置通知,returning属性指定后置通知方法的参数名(参数名称要一致) -->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointcut" returning="returnVal"/>
            <!-- 异常通知,throwing属性指定异常通知方法的参数名(名称要一致) -->
            <aop:after-throwing method="afterThrowing" pointcut-ref="myPointcut" throwing="e"/>
            <!-- 最终通知 -->
            <aop:after method="after" pointcut-ref="myPointcut"/>
        </aop:aspect>
    </aop:config>
</beans>

(2)使用注解和配置类配置切面

在上面的applicationContext.xml配置文件中,对基于AspectJ的aop配置完全可以使用注解来配置,

在切面类中:

/**
 * AspectJ提供了所有通知注解,这些注解都有一个value属性,
 * 属性指定的是@Pointcut注解所标注的方法名,即pointcut(),
 * 如:@Around("pointcut()")
 * 如果不同通知想使用不同的切入点那么可以直接在value属性中自定义
 * 如:@Before("execution(* edu.nf.ch14.service.impl.UserServiceImpl.say())")
 */
@Component
/**
 * @Aspect注解标识当前类为一个切面类
 */
@Aspect
public class UserServiceAspect extends AbstractAsPect {

    /**
     * 声明一个切入点,@Pointcut声明在一个方法上,在这里将这个方法放在了AbstractAspect这个类中
     */
//    @Pointcut("execution(* edu.nf.ch15.service.impl.UserServiceImpl.*(..))")
//    public void pointcut(){
//
//    }

    /**
     * 前置通知
     * @param jp 连接点,通过这个连接点可以获取目标方法
     * pointcut()指定的是切入点注解所标注的方法名
     * @Before注解的value属性指定的是切入点注解所标注的方法名,
     * 结果不同通知想使用不同的切入点
     */
    @Before("execution(* edu.nf.ch15.service.impl.UserServiceImpl.say())")
    public void before(JoinPoint jp){
        System.out.println("前置通知,目标方法参数:" + jp.getArgs()[0]);
    }

    /**
     * 环绕通知
     * @param pjp 连接点,可以获取目标方法参数以及方法信息以及调用目标方法等等
     */
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("环绕通知前。。。");
        //获取目标方法的的Method对象
        MethodSignature ms = (MethodSignature)pjp.getSignature();
        Method method = ms.getMethod();
        System.out.println("当前调用的目标方法:" + method.getName());
        //调用目标方法
        Object returnVal = pjp.proceed();
        System.out.println("环绕通知后。。。");
        return returnVal;
    }

    /**
     * 后置通知
     * @param returnVal 目标方法的返回值
     */
    @AfterReturning(value = "pointcut()", returning = "returnVal")
    public void afterReturning(String returnVal){
        System.out.println("后置通知,返回参数:" + returnVal);
    }

    /**
     * 异常通知
     * @param e 目标方法产生的异常对象
     */
    @AfterThrowing(value = "pointcut()", throwing = "e")
    public void afterThrowing(Throwable e){
        System.out.println("异常通知,异常信息:" + e.getMessage());
    }

    /**
     * 最终通知
     */
    @After("pointcut()")
    public void after(){
        System.out.println("最终通知");
    }
}

创建一个配置类,可以做到0配置,

SpringConfig:

@Configuration
@ComponentScan("edu.nf.ch15")
/**
 * 启用AspectJ注解处理器,等同于xml中的<aop:aspectj-autoproxy/>
 */
@EnableAspectJAutoProxy
public class SpringConfig {
}

 

 

 

猜你喜欢

转载自www.cnblogs.com/zhangcaihua/p/12930900.html