Spring AOP使用注解创建切面

Spring使用xml创建切面和使用AspectJ创建切面有所不同的。下面使用AspectJ来创建切面。

一、先举个栗子

所谓aop就是面向切面编程,使其他类共享相同的行为,比如日常开发中使用日志模块,总不能每次使用都new一个出来,这时候就要用到aop,我们先管他三七二十一,定义一个切面类,类中写方法,然后把方法直接搞到其他方法内部。先举个栗子,比如我们经常逛b站鬼畜区,什么Van,香蕉君啥的,哲学你们都懂得。那么,栗子来了,Van口头禅FQ,但是Banana君等等也想要说FQ,但是Banana没有自己不会说FQ,他只会跳舞和摔跤,Banana想要在跳舞和摔跤时候也说FQ,没法只能由Van来说FQ。

准备工作,先把pom写好

<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.yoyiyi</groupId>
    <artifactId>TestAOP</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
   <name>TestAOP</name>
    <url>http://maven.apache.org</url>
    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <java.version>1.8</java.version>
        <spring-framework.version>4.1.5.RELEASE</spring-framework.version>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <!-- spring aop支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <!-- aspectj支持 -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.6</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.5</version>
        </dependency>
        <!-- Spring test 支持 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring-framework.version}</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.11</version>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.3</version>
        </dependency>
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>jsr250-api</artifactId>
            <version>1.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>


二、定义切面

既然Banana不会说FQ,Van会,我们先把Van搞过来,就是定义一个切面。

@Aspect  //表明是一个切面
@Component   //表明是一个组件
public class Van {
    
    //定义公共切点,这样不用以后每一次都写一遍 切入到Banana摔跤方法 
    @Pointcut("execution(* com.yoyiyi.java.Banana.摔跤())")
    public void poincut() {
    }
    
    //前置通知     在Banan摔跤之前说FQ
    @Before("poincut()")
    public void 前置sayFQ() {
        System.out.println("前置FQ");
    }
    
    //后置通知    在Banana摔跤之后说FQ
    @After("poincut()")
    public void 后置sayFQ() {
        System.out.println("后置FQ");
    }
    
    //异常通知   Banana 摔跤发生异常说FQ 比如没体力
    @AfterThrowing("poincut()")
    public void 异常sayFQ() {
        System.out.println("异常FQ");
    }
    
    //返回后通知 Van想知道Banana跳舞之后在干啥 Van不知道,所以Object
     @AfterReturning(returning = "rvt",
            pointcut = "execution(* com.yoyiyi.java.Banana.跳舞())")
    public void 返回通知sayFQ(Object rvt) {
        System.out.println("返回通知FQ,发现蕉说" + rvt.toString());
    }

    //环绕通知   Van在Banana摔跤之前之后都说FQ
    @Around("poincut()")
    public void 环绕sayFQ(ProceedingJoinPoint joinPoint) {
        try {
            System.out.println("前面FQ");
            joinPoint.proceed();//执行方法
            System.out.println("后面FQ");
        } catch (Throwable throwable) {
            System.out.println("异常FQ");
            throwable.printStackTrace();
        }
    }

二、目标对象

这个目标对象就是Banana

@Component   //Banana组件
public class Banana {

    public void 摔跤() {
        //异常通知
        //int i = 1;
        //i=i/0;
        System.out.println("摔跤");
    }

    public String 跳舞() {
        return "我还要跳舞";
    }
}

三、装配

既然Van和Banana都有了,总有个地方要把他们弄到一起吧,b站通常在鬼畜区就可以了,也就是装配到Spring的bean中。

@Configuration            //相当于一个xml配置文件
@EnableAspectJAutoProxy  //表示开启AOP代理自动配置
@ComponentScan(basePackages = "com.yoyiyi.java")  //扫描com.yoyiyi.java 注解
public class GuiChuConfig { //配置

}

四、测试

我们写一个测试类看看效果

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = GuiChuConfig.class)  //找到鬼畜区配置类
public class TestBanana {
    //测试一个通知时候 需要把Van中其他通知注释掉
    @Autowired    自动装配
    Banana mBanana;
    @Test
    public void 前置() throws Exception {
        mBanana.摔跤();
        //控制台:
        //前置FQ
        //摔跤
    }

    @Test
    public void 后置() throws Exception {
        mBanana.摔跤();
        //控制台:
        //后置FQ
        //摔跤
    }

    @Test
    public void 环绕() throws Exception {
        mBanana.摔跤();
         //控制台:
         //前面FQ
         //摔跤
         //后面FQ
    }

    @Test
    public void 返回() throws Exception {
        mBanana.跳舞();
        //控制台:
        //返回通知FQ,发现蕉说我还要跳舞
    }

    @Test
    public void 异常() throws Exception {
        mBanana.摔跤();
        //控制台:
        //异常FQ
        //摔跤
    }

五、目标方法带有参数

举个栗子:

Banana要和Bili摔跤,所以要在摔跤方法中传入Bili大名,Van说FQ并且要知道Bili和Banana摔跤多少次。

在Banana中添加一个带有参数摔跤方法

 public void 摔跤(String name) {
        System.out.println("摔跤");
    }

在Van中添加一个前置通知

  @Before("execution(* com.yoyiyi.java.Banana.摔跤(String)) && args(name))")
  public void 带统计前置sayFQ(String name) {
        System.out.println("前置FQ");
        if ("bili".equals(name)) {
            System.out.println("统计" + 1000);
        }
    }

在测试类中添加测试方法

@Test
public void 统计() throws Exception {
    mBanana.摔跤("bili");
    //控制台:
    //前置FQ
   // 统计摔跤次数1000
    //摔跤
}

六、自定义注解

在现实开发中日志文件带有记录基本增删改的功能,但是表信息,权限等难记录就要用到自定义注解

举个栗子:

鬼畜视频是up制作,Van说FQ时候想知道这是哪个up主搞的视频,他想知道就是up主名字和年龄。

定义一个Up主注解类

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Up {
    /**
     * 姓名
     */
    String upName() default "";

    /**
     * 名字
     */
    int upAge() default 0;
}

在Banana里面摔跤方法添加注解

  @Up(upAge = 15, upName = "van")
  public void 摔跤() {
    System.out.println("摔跤");
  }

在Van里面添加

  @Before("poincut()")
    public void 前置带有up主名字和年龄sayFQ(JoinPoint joinPoint) {
        System.out.println("前置FQ");
        try {
            int upAge = getUpAge(joinPoint);
            String upName = getUpName(joinPoint);
            System.out.println("up姓名:" + upName);
            System.out.println("up年龄:" + upAge);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取Up名字
    private String getUpName(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        String upName = "";
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] types = method.getParameterTypes();
                if (types.length == arguments.length) {
                    upName = method.getAnnotation(Up.class).upName();
                    break;
                }
            }
        }
        return upName;
    }

    //获取Up年龄
    private int getUpAge(JoinPoint joinPoint) throws Exception {
        String targetName = joinPoint.getTarget().getClass().getName();
        String methodName = joinPoint.getSignature().getName();
        Object[] arguments = joinPoint.getArgs();
        Class targetClass = Class.forName(targetName);
        Method[] methods = targetClass.getMethods();
        int upAge = 0;
        for (Method method : methods) {
            if (method.getName().equals(methodName)) {
                Class[] types = method.getParameterTypes();
                if (types.length == arguments.length) {
                    upAge = method.getAnnotation(Up.class).upAge();
                    break;
                }
            }

        }
        return upAge;
    }

在测试类添加

   @Test
    public void 前置带有up主名字和年龄() throws Exception {
        mBanana.摔跤();
        //控制台:
        //前置FQ
        //up姓名:van
        //up年龄:15
        //摔跤
    }

至此,Spring AOP使用注解创建切面骚操作差不过讲完了。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,470评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,393评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,577评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,176评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,189评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,155评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,041评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,903评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,319评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,539评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,703评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,417评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,013评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,664评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,818评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,711评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,601评论 2 353

推荐阅读更多精彩内容

  • 本章内容: 面向切面编程的基本原理 通过POJO创建切面 使用@AspectJ注解 为AspectJ切面注入依赖 ...
    谢随安阅读 3,146评论 0 9
  • 上节中我们已经定义了Performance接口,他是切面中的切点的一个目标对象。那么现在就让我们使用AspectJ...
    郭之源阅读 26,242评论 1 12
  • AOP实现可分为两类(按AOP框架修改源代码的时机): 静态AOP实现:AOP框架在编译阶段对程序进行修改,即实现...
    数独题阅读 2,317评论 0 22
  • 因为失眠引起的头疼,在时隔一个多月再次侵袭了我,仿佛在警告我不要再继续这么浑浑噩噩混日子下去一般。生活跟工...
    六月半幺阅读 193评论 0 0
  • 市场趋势 目前,我国网络美食菜谱领域正处于发展期。菜谱类APP的覆盖人数在稳步上升,用户人均有效使用时间也呈不断上...
    RainaL阅读 5,932评论 2 12