作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
Spring学习之旅(二) AOP(面向切面编程)的使用

Custom Tab

      Spring提供的通知有以下几种:

        1.前向通知,在调用目标方法前执行,使用@Before注解

        2.返回通知,在调用目标方法无异常返回后执行,使用@AfterReturning注解

        3.异常返回通知,在调用目标方法抛出异常后执行,使用@AfterThrowing注解

        4.后向通知,在调用目标方法返回后执行,无论是正常返回还是抛出异常,使用@After注解

        5.环绕通知,在调用目标方法前后都会进行通知,使用@Around注解

        本篇基于上一篇的示例,给出一个新的场景,有一名计算机老师教学生如何点击鼠标选择一个图标,如何点击鼠标弹出一个上下文菜单。我们先看一下我们的Teacher类。

package com.yjp.spring.study.beans;  
  
@Component  
public class Teacher {  
      
    public void teachSelectAnIcon() {  
        System.out.println("请在一个图标上点击鼠标左键");  
    }  
      
    public void selectAnIconSuccess() {  
        System.out.println("做得好!你选中了一个图标");  
    }  
      
    public void teachShowContextMenu() {  
        System.out.println("请点击鼠标右键");  
    }  
      
    public void showContextMenuSuccess() {  
        System.out.println("做得好!弹出了一个上下文菜单");  
    }  
      
}

可以看到,Teacher是一个Spring Bean(用@Component注解),如果不使用AOP,我们需要在Computer中注入依赖,来使用teacher bean。

package com.yjp.spring.study.beans;  
  
import org.springframework.beans.factory.annotation.Autowired;  
import org.springframework.beans.factory.annotation.Qualifier;  
import org.springframework.stereotype.Component;  
  
@Component  
public class Computer {  
      
    private Mouse mouse;  
      
    @Autowired  
    private Teacher teacher;  
      
    @Autowired  
    public Computer(@Qualifier("blackMouse") Mouse mouse) {  
        this.mouse = mouse;  
    }  
      
    public void selectAnIcon() {  
        teacher.teachSelectAnIcon();  
        mouse.clickLeft();  
        teacher.selectAnIconSuccess();  
    }  
      
    public void contextMenu() {  
        teacher.teachShowContextMenu();  
        mouse.clickRight();  
        teacher.showContextMenuSuccess();  
    }  
}

可以看到,我们需要用teacher的方法"拥抱"Computer,两者不分你我,融合得很好,但是,有一些问题,teacher和computer本就是独立的,为什么必须要求computer持有一个teacher的引用,我们就没有办法让teacher在它该出场的时候再出场吗?当然可以,这时候我们使用AOP的时候来了。我们先删除上面代码中的teacher相关的逻辑,恢复到一开始Computer的状态。


        首先,启用我们的AOP,在我们的配置类上加入@EnableAspectJAutoProxy注解

@Configuration  
@EnableAspectJAutoProxy  
@ComponentScan({"com.yjp.spring.study.beans"})  
public class ComputerConfig {  
}

这样就启用了Spring的AOP,然后为Teacher类添加注解

package com.yjp.spring.study.beans;  
  
import org.aspectj.lang.annotation.After;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.springframework.stereotype.Component;  
  
@Aspect  
@Component  
public class Teacher {  
      
    @Before("execution(* com.yjp.spring.study.beans.Computer.selectAnIcon(..))")  
    public void teachSelectAnIcon() {  
        System.out.println("请在一个图标上点击鼠标左键");  
    }  
      
    @After("execution(* com.yjp.spring.study.beans.Computer.selectAnIcon(..))")  
    public void selectAnIconSuccess() {  
        System.out.println("做得好!你选中了一个图标");  
    }  
      
    @Before("execution(* com.yjp.spring.study.beans.Computer.contextMenu(..))")  
    public void teachShowContextMenu() {  
        System.out.println("请点击鼠标右键");  
    }  
      
    @After("execution(* com.yjp.spring.study.beans.Computer.contextMenu(..))")  
    public void showContextMenuSuccess() {  
        System.out.println("做得好!弹出了一个上下文菜单");  
    }  
      
}

首先,Teacher有@Component注解,说明它是一个Bean,之后添加了@Aspect注解,该注解说明Teacher这个Bean要作为切面使用,这里注意@Aspect注解是aspectjrt包中定义的,不是Spring的注解,需要加入依赖。然后,将几个方法使用@Before和@After进行注解,注解的参数是一个excution表达式,一般结构是


返回值类型 完整类名.方法名(参数)

其中可以用* 代表任意类型,..代表任意参数

再次运行程序,会看到如下打印

请在一个图标上点击鼠标左键
BlackMouse Left Clicked!
做得好!你选中了一个图标
请点击鼠标右键
BlackMouse Right Clicked!
做得好!弹出了一个上下文菜单

        运行成功,结果还不错,但是有一点还不太好,我们是不是可以环绕通知,把对应的方法包裹起来,当然可以,在Teacher类删除之前的方法,添加新的方法和注解,如下所示:

[java] view plain copy

package com.yjp.spring.study.beans;  
  
import org.aspectj.lang.ProceedingJoinPoint;  
import org.aspectj.lang.annotation.After;  
import org.aspectj.lang.annotation.Around;  
import org.aspectj.lang.annotation.Aspect;  
import org.aspectj.lang.annotation.Before;  
import org.springframework.stereotype.Component;  
  
@Aspect  
@Component  
public class Teacher {  
      
    @Around("execution(* com.yjp.spring.study.beans.Computer.selectAnIcon(..))")  
    public void selectAnIconAround(ProceedingJoinPoint jp) {  
        try {  
            System.out.println("环绕通知 请在一个图标上点击鼠标左键");  
            jp.proceed();  
            System.out.println("环绕通知 做得好!你选中了一个图标");  
        } catch (Throwable e) {  
            e.printStackTrace();  
        }  
    }  
      
    @Around("execution(* com.yjp.spring.study.beans.Computer.contextMenu(..))")  
    public void showContextMenuAround(ProceedingJoinPoint jp) {  
        try {  
            System.out.println("环绕通知 请点击鼠标右键");  
            jp.proceed();  
            System.out.println("环绕通知 弹出了一个上下文菜单");  
        } catch (Throwable e) {  
            e.printStackTrace();  
        }  
    }  
}

这样就有了环绕通知,环绕通知的差别在于,使用@Around对方法进行注解,环绕通知方法有一个参数ProceedingJpinPoint,字面意思就是连接点,连接点是我们调用被环绕方法的途径,jp.proceed()之前的内容,在方法调用前执行,之后的内容,在方法调用之后执行。运行结果如下:


环绕通知 请在一个图标上点击鼠标左键
BlackMouse Left Clicked!
环绕通知 做得好!你选中了一个图标
环绕通知 请点击鼠标右键
BlackMouse Right Clicked!
环绕通知 弹出了一个上下文菜单

        Spring使用AOP就是如此简单,当然,AOP还可以处理带有参数的方法,还能配合其他一些注解,限制AOP作用的条件,这些可以在使用中慢慢学习。下面给出目前的pom.xml配置供参考。

<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.yjp.spring</groupId>  
  <artifactId>study</artifactId>  
  <version>0.0.1-SNAPSHOT</version>  
  <packaging>jar</packaging>  
  
  <name>study</name>  
  <url>http://maven.apache.org</url>  
  
  <properties>  
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>  
    <java.version>1.8</java.version>  
    <junit.version>4.12</junit.version>  
    <spring.version>4.3.4.RELEASE</spring.version>  
    <aspectjrt.version>1.8.9</aspectjrt.version>  
  </properties>  
  
  <dependencies>  
    <dependency>  
        <groupId>junit</groupId>  
        <artifactId>junit</artifactId>  
        <version>${junit.version}</version>  
        <scope>test</scope>  
    </dependency>  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-context</artifactId>  
        <version>${spring.version}</version>  
    </dependency>  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-test</artifactId>  
        <version>${spring.version}</version>  
    </dependency>  
    <dependency>  
        <groupId>org.springframework</groupId>  
        <artifactId>spring-aspects</artifactId>  
        <version>${spring.version}</version>  
    </dependency>  
    <dependency>  
        <groupId>org.aspectj</groupId>  
        <artifactId>aspectjrt</artifactId>  
        <version>${aspectjrt.version}</version>  
    </dependency>  
  </dependencies>  
</project>

转载自:http://blog.csdn.net/yjp19871013/article/details/53733387

Java Socket编程(一) Socket编程原理及基本概念

http://wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=501

 Java Socket编程(三) 并发服务器

http://wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=503

Java Socket编程(四) 异步服务器

http://wisdomdd.cn/Wisdom/resource/articleDetail.htm?resourceId=504


Home