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