Spring 4.0 学习日记(8) ---AOP切面注解实现五种通知

写在前面 会有专门的一章来备注AOP切面各种的知识点
这里我只写通过注解实现的五种通知 其实我就是懒~~~略略略略略略

通知(Advice)类型

前置通知(Before advice):在某连接点(JoinPoint)之前执行的通知,但这个通知不能阻止连接点前的执行。ApplicationContext中在<aop:aspect>里面使用<aop:before>元素进行声明。例如,TestAspect中的doBefore方法。

后置通知(After advice):当某连接点退出的时候执行的通知(不论是正常返回还是异常退出)。ApplicationContext中在<aop:aspect>里面使用<aop:after>元素进行声明。例如,ServiceAspect中的returnAfter方法,所以Teser中调用UserService.delete抛出异常时,returnAfter方法仍然执行。

返回通知(After return advice):在某连接点正常完成后执行的通知,不包括抛出异常的情况。ApplicationContext中在里面使用元素进行声明。

环绕通知(Around advice):包围一个连接点的通知,类似Web中Servlet规范中的Filter的doFilter方法。可以在方法的调用前后完成自定义的行为,也可以选择不执行。ApplicationContext中在<aop:aspect>里面使用<aop:around>元素进行声明。例如,ServiceAspect中的around方法。

抛出异常后通知(After throwing advice):在方法抛出异常退出时执行的通知。ApplicationContext中在<aop:aspect>里面使用<aop:after-throwing>元素进行声明。例如,ServiceAspect中的returnThrow方法。

注1:可以将多个通知应用到一个目标对象上,即可以将多个切面织入到同一目标对象。
(我的实例就是多个通知注入到一个方法上 但是异常通知为啥不实现呢)

注2:以为我把所有的类写到一个包下面了 所以扫描的时候就是那一个包

注3:官网下的Spring插件有两个JAR包没 需要另外下 反正我没有 附上地址
aopalliance.jar
aspectjweaver.jar

下面是实例

interface类

package com.wow.AopMessage;

public interface AopServiceInterface {

    public String addInfo(int i,int b,int j);
}

实现接口方法的类 这里的方法带了参数是为了测试around通知的时候是否打印出了参数
getArgs():获取连接点方法运行时的入参列表

package com.wow.AopMessage;

public class AopServiceImpl implements AopServiceInterface{

    @Override
    public String addInfo(int a,int b,int c) {
        System.out.println("--------------------");
        System.out.println("实现类方法addInfo已经执行");
        System.out.println("--------------------");
        return this.getClass().getName();
    }

}

切面类

package com.wow.AopMessage;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.After;

@Aspect
public class AopMessageInfo {

    /****
     * 声明该方法是一个前置通知:在目标方法开始之前执行 
     * 
     * JoinPoint 是连接点,代表一个方法,客户端调用哪个方法,哪个方法就是连接点 通过该参数,可以获取到连接点的一些信息
     * 
     * "execution(* com.wow.AopMessage.*.*(..))"
     * 
     * 第一个* 任意修饰符和任意返回值 
     * 第二个* 该包下所有的类 
     * 第三个* 所有方法 
     * (..) 所有参数
     */
    @Before("execution(* com.wow.AopMessage.*.*(..))")
    public void Before(JoinPoint jPonit) {
        System.out.println("--------------------");
        // 获取目标类
        System.out.println("targetClass:" + jPonit.getTarget());
        // 获取连接点的参数
        System.out.println("args:" + jPonit.getArgs());
        // 获取连接点的名称
        System.out.println("methodName:" + jPonit.getSignature().getName());
        System.out.println("前置通知已完成----开启事务");
        System.out.println("--------------------");

    }

    /**
     * 返回通知:在目标方法正常结束执行后的通知 
     * 返回通知是可以访问到目标方法的返回值
     * 在后置通知后执行
     */

    @AfterReturning(value = "execution(* com.wow.AopMessage.*.*(..))", returning = "result")
    public void afterRunningMethod(JoinPoint jPonit, Object result) {
        System.out.println("--------------------");
        System.out.println("methodName:" + jPonit.getSignature().getName());
        System.out.println("The method " + jPonit.getSignature().getName()
                + " ends with the Result " + result);
        System.out.println("返回通知已完成----获取目标方法返回值"+result);
        System.out.println("--------------------");

    }

    /**
     * 
     * 后置通知:在目标方法之后(无论是否发生异常),执行的通知,
     * 在后置通知中还不能访问目标方法执行的结果。执行结果须在返回通知中访问。
     *  
     * JoinPoint
     */

    @After("execution(* com.wow.AopMessage.*.*(..))")
    public void After(JoinPoint jPonit) {
        System.out.println("--------------------");
        System.out.println("methodName:" + jPonit.getSignature().getName());
        System.out.println("后置通知已完成----提交事务");
        System.out.println("--------------------");

    }

     /**
     * 环绕通知
     *    需要携带ProceedingJoinPoint类型的参数
     * 类似于动态代理的全过程:ProceedingJoinPoint类型参数可以决定是否执行目标方法
     * 环绕通知必须有返回值,返回值就是目标方法的返回值
     * 
     */

    /**
     * org.aspectj.lang.JoinPoint接口表示目标类连接点对象.
     * org.aspectj.lang.ProceedingJoinPoint表示连接点对象,该类是JoinPoint的子接口.
     * 
     * JoinPoint 
     * java.lang.Object[] getArgs():获取连接点方法运行时的入参列表;
     * getSignature() :获取连接点的方法签名对象; 
     * java.lang.Object getTarget() :获取连接点所在的目标对象; 
     * java.lang.Object getThis() :获取代理对象本身; 
     * 
     * ProceedingJoinPoint 
     * roceedingJoinPoint继承JoinPoint子接口,它新增了两个用于执行连接点方法的方法: 
     * java.lang.Object proceed() throws java.lang.Throwable:通过反射执行目标对象的连接点处的方法; 
     * java.lang.Object proceed(java.lang.Object[] args) throws java.lang.Throwable:
     * 通过反射执行目标对象连接点处的方法,不过使用新的入参替换原来的入参。
     */

    @Around("execution(* com.wow.AopMessage.*.*(..))") 
    public Object around(ProceedingJoinPoint jPonit){  
        Object result = null;  
        String methodName = jPonit.getSignature().getName();  
        Object args = Arrays.asList(jPonit.getArgs());  
        //执行目标方法  
        try {  
            //前置通知  
            System.out.println("--------------------");
            System.out.println("Arround:The method "+methodName +" begins with "+ args);
            System.out.println("--------------------");
            result = jPonit.proceed();  
            //后置通知  
            System.out.println("--------------------");
            System.out.println("Arround:The method "+ methodName+" ends");
            System.out.println("--------------------");
        } catch (Throwable e) {  
            e.printStackTrace();  
            //异常通知  
            System.out.println("Arround:The method "+ methodName+"occurs exception:"+e);  
            //throw new RuntimeException(e);  
            //不抛出异常的话,异常就被上面抓住,执行下去,返回result,result值为null,转换为int  
        }  
        //返回通知  
        System.out.println("--------------------");
        System.out.println("Arround:The method "+ methodName+" ends with the Result "+ result);  
        System.out.println("--------------------");
        //return 方法名;  
        return result;  
    }  


    /**
     * 在目标方法出现异常时会执行的代码,
     * 可以访问到异常对象,且可以指定在出现特定异常时在执行通知代码
     * 如下面Exception ex,方法参数Exception改为其它异常可以指定出现指定异常时执行
     * 这里没有报异常  以后有时间再说吧  
     */
    @AfterThrowing(value = "execution(* com.wow.AopMessage.*.*(..))", throwing = "ex")
    public void afterThrowingMethod(JoinPoint jPonit, Exception ex) {
        System.out.println("methodName:" + jPonit.getSignature().getName());
        System.out.println("The method " + jPonit.getSignature().getName()
                + "occurs exception:" + ex);
    }
}

测试类

package com.wow.AopMessage;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AopTest {

    public static void main(String[] args) {
        ApplicationContext App = new ClassPathXmlApplicationContext("beans.xml");
        AopServiceImpl AopSer = (AopServiceImpl) App.getBean("AopService");
        AopSer.addInfo(1,2,3);
    }
}

xml配置文件 这里为了实现的aop:aspectj 表头文件有修改 主要就是AOP和Context

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

   <!-- 激活组件扫描功能,在包com.wow.AopMessage及其子包下面自动扫描通过注解配置的组件 -->        
   <context:component-scan base-package="com.wow.AopMessage"/>      

    <bean id = "AopService" class = "com.wow.AopMessage.AopServiceImpl"></bean>
    <bean id = "AopMessage" class = "com.wow.AopMessage.AopMessageInfo"></bean>

    <!-- proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。
    如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。
    如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理将起作用 -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
    <!-- 当采取默认忽略这个属性的时候发生以下错误 为啥我也不知道 等我复习到XML配置或者cglib代理的时候就知道了 动态代理除了JDK的另外一种代理
    Exception in thread "main" java.lang.ClassCastException: 
    com.sun.proxy.$Proxy7 cannot be cast to com.wow.AopMessage.AopServiceImpl
    <aop:aspectj-autoproxy/> -->
</beans>  

打印结果 其实看下每个通知的出现顺序

八月 01, 2017 11:24:37 下午 org.springframework.context.support.ClassPathXmlApplicationContext prepareRefresh
信息: Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@3b85c6ac: startup date [Tue Aug 01 23:24:37 CST 2017]; root of context hierarchy
八月 01, 2017 11:24:37 下午 org.springframework.beans.factory.xml.XmlBeanDefinitionReader loadBeanDefinitions
信息: Loading XML bean definitions from class path resource [beans.xml]
--------------------
Arround:The method addInfo begins with [1, 2, 3]
--------------------
--------------------
targetClass:com.wow.AopMessage.AopServiceImpl@4b7596bc
args:[Ljava.lang.Object;@14fb3758
methodName:addInfo
前置通知已完成----开启事务
--------------------
--------------------
实现类方法addInfo已经执行
--------------------
--------------------
Arround:The method addInfo ends
--------------------
--------------------
Arround:The method addInfo ends with the Result com.wow.AopMessage.AopServiceImpl
--------------------
--------------------
methodName:addInfo
后置通知已完成----提交事务
--------------------
--------------------
methodName:addInfo
The method addInfo ends with the Result com.wow.AopMessage.AopServiceImpl
返回通知已完成----获取目标方法返回值com.wow.AopMessage.AopServiceImpl
--------------------

恩 以上 很简单 没啥难的

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值