10.AOP配置

65 阅读3分钟

在Spring的AOP配置中,也和IOC配置一样,支持3类配置方式:

1、使用XML配置

2、使用XML+注解组合配置

3、使用纯注解配置

XML模式


/**
 * 通知.增强
 * 就是将目标方法拦截
 * 有五种通知增强
 * 1、前置通知,在目标方法执行之前执行
 * 2、后置通知,在目标方法执行之后执行
 * 3、返回通知,拿到目标方法的返回值之后执行
 * 4、异常通知,目标方法执行抛出异常的时候执行
 * 5、环绕通知,集大成者
 */
public class LogAdvice {
    /**
     * 这是个环绕通知,环绕目标方法
     * @param pjp
     * @return
     */
    public Object around(ProceedingJoinPoint pjp){
        Object proceed;
        try {
            //这句相当于是前置通知
            long startTime = System.currentTimeMillis();
            //这句话其实就是执行目标方法
            proceed = pjp.proceed();
            long endTime = System.currentTimeMillis();
        } catch (Throwable e) {
            //这里就是异常通知
            throw new RuntimeException(e);
        }
        return proceed;
    }


    /**
     * 前置通知
     */
    public void before(JoinPoint jp){
        //获取目标方法的名字
        String name = jp.getSignature().getName();
        System.out.println(name+"方法开始执行了");
    }
    /**
     * 后置通知
     */
    public void after(JoinPoint jp){
        //获取目标方法的名字
        String name = jp.getSignature().getName();
        System.out.println(name+"方法执行结束了");
    }

    /**
     * 返回通知执行的时候,实际上已经知道目标方法的返回值是什么
     * 这个方法的第二个参数,就是目标方法的返回值
     * 注意方法的第二个参数,这个参数只有在和目标方法的返回值类型相匹配的时候,当前方法才会触发
     * @param jp
     */
    public void afterReturning(JoinPoint jp,Object val){
        String name = jp.getSignature().getName();
        System.out.println(name+"方法执行返回"+val);
    }

    /**
     * 这个是目标方法抛出异常的时候会被触发
     * 当抛出异常的时候,我们希望能够在这个方法中得知发生了什么
     * 注意异常类型
     * @param jp
     */
    public void afterThrowing(JoinPoint jp,Throwable t){
        String name = jp.getSignature().getName();
        System.out.println(name+"方法执行抛出"+ t.getMessage());
    }

然后在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 https://www.springframework.org/schema/aop/spring-aop.xsd">

    <bean class="org.luckboy.demo.ICalculatorImpl" id="calculator"/>
<!-- 向Spring 容器注册通知Bean    -->
    <bean class="org.luckboy.demo.LogAdvice" id="logAdvice"/>

<!--  配置AOP
  1、 向Spring 容器注册通知Bean
  2、注册切面(切点+通知),切点实际上就相当于是拦截规则
  -->
    <aop:config>
<!--       这个地方就是配置切点的,也就是配置拦截规则,切点可以配置多个 -->
<!--        expression 就是表达式,也就是拦截的规则
最常用的拦截规则两种:1、通过注解去标记被拦截的方法 2、通过表达式
这个就表示拦截ICalculatorImpl中名为add且有两个int类型参数,且返回值为int的方法
execution(int org.luckboy.demo.ICalculatorImpl.add(int,int))

返回值 写成* ,第一个值表示返回值任意,第二个表示方法名任意, ..表示参数类型任意
-->
        <aop:pointcut id="pc1" expression="execution(* org.luckboy.demo.ICalculatorImpl.*(..))"/>
<!--        ref 表示logAdice 这个bean 是我们的通知bean-->
        <aop:aspect ref="logAdvice">
<!--            指定前置通知,也就是说明前置通知的方法是哪个  -->
            <aop:before method="before" pointcut-ref="pc1"/>
            <aop:after method="after" pointcut-ref="pc1"/>
<!--            下面两种通知就需要额外指定另外一个参数含义-->
            <aop:after-returning method="afterReturning" pointcut-ref="pc1" returning="val"/>
            <aop:after-throwing method="afterThrowing" pointcut-ref="pc1" throwing="t"/>
            <aop:around method="around" pointcut-ref="pc1"/>
        </aop:aspect>

    </aop:config>

</beans>

最后,从Spring容器中去获取代理对象:

public class Demo01 {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
            ICalculator calculator = ctx.getBean(ICalculator.class);

            //我们向Spring 容器注册的ICalculatorImpl,现在已经被自动代理了,所以Spring容器中现在是不存在ICalculatorImpl
            calculator.add(3,4);
        }
}