Spring学习之旅(二) AOP(面向切面编程)的使用

辛苦堆砌,转载请注明出处,谢谢!

        上一篇说了Spring的依赖注入,今天再看看Spring的AOP,牵扯的AOP的理论知识,大家可以搜索一些文章了解一下,这里不做过多解释,本文主要介绍使用Spring如何实现AOP。

        Spring的AOP只限于对方法进行拦截,如果有其他需求,可以考虑使用AspectJ。

        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类删除之前的方法,添加新的方法和注解,如下所示:

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>

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值