作者微信 bishe2022

代码功能演示视频在页面下方,请先观看;如需定制开发,联系页面右侧客服
Spring 3 AOP 概念及完整示例

Custom Tab

AOP概念

AOP(Aspect Oriented Programming),即面向切面编程(也叫面向方面编程,面向方法编程)。其主要作用是,在不修改源代码的情况下给某个或者一组操作添加额外的功能。像日志记录,事务处理,权限控制等功能,都可以用AOP来“优雅”地实现,使这些额外功能和真正的业务逻辑分离开来,软件的结构将更加清晰。AOP是OOP的一个强有力的补充。

AOP术语

AOP的术语不太直观,Spring文档中也没有给一个确切的定义,所以重在理解。

Advice的类型

实现机制

Spring AOP是基于代理机制的,通过JDK Proxy和CGLIB Proxy两种方法实现代理。

如果target object没有实现任何接口,那么Spring将使用CGLIB来实现代理。CGLIB是一个开源项目,它是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。

如果target object实现了一个以上的接口,那么Spring将使用JDK Proxy来实现代理,因为Spring默认使用的就是JDK Proxy,并且JDK Proxy是基于接口的。这也是Spring提倡的面向接口编程。当然,你也可以强制使用CGLIB来进行代理,但是这样可能会造成性能上的下降。

Pointcut expression

Pointcut可以有下列方式来定义或者通过&& || 和!的方式进行组合. 

args()
@args()
execution()
this()
target()
@target()
within()
@within()
@annotation

其中execution 是用的最多的,其格式为:

ret-type-pattern,name pattern, 和 parameters pattern是必须的.

举例说明:

1) 任意公共方法的执行:

execution(public * *(..))

2) 任何一个以“set”开始的方法的执行:

execution(* set*(..))

3) AccountService 接口的任意方法的执行:

execution(* com.xyz.service.AccountService.*(..))

4) 定义在service包里的任意方法的执行:

execution(* com.xyz.service.*.*(..))

5) 定义在service包和所有子包里的任意类的任意方法的执行:

execution(* com.xyz.service..*.*(..))

6) 定义在pointcutexp包和所有子包里的JoinPointObjP2类的任意方法的执行:

execution(* com.test.spring.aop.pointcutexp..JoinPointObjP2.*(..))

7) pointcutexp包里的任意类.

within(com.test.spring.aop.pointcutexp.*)

8) pointcutexp包和所有子包里的任意类.

within(com.test.spring.aop.pointcutexp..*)

9) 实现了Intf接口的所有类,如果Intf不是接口,限定Intf单个类.

this(com.test.spring.aop.pointcutexp.Intf)

10) 带有@Transactional标注的所有类的任意方法.

@within(org.springframework.transaction.annotation.Transactional)
@target(org.springframework.transaction.annotation.Transactional)

11) 带有@Transactional标注的任意方法.

@annotation(org.springframework.transaction.annotation.Transactional)

12) 参数带有@Transactional标注的方法.

@args(org.springframework.transaction.annotation.Transactional)

13) 参数为String类型(运行是决定)的方法.

args(String)

完整示例:

首先新建一个maven项目,在项目的pom.xml中添加spring 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>cn.outofmemory</groupId>
	<artifactId>spring-aop-aspect</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-aop-aspect</name>
	<url>http://maven.apache.org</url>
	<properties>
		<spring.version>3.1.1.RELEASE</spring.version>
	</properties>
	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-core</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-aop</artifactId>
			<version>${spring.version}</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjrt</artifactId>
			<version>1.6.12</version>
		</dependency>
		<dependency>
			<groupId>org.aspectj</groupId>
			<artifactId>aspectjweaver</artifactId>
			<version>1.6.12</version>
		</dependency>
		<dependency>
			<groupId>cglib</groupId>
			<artifactId>cglib</artifactId>
			<version>2.2</version>
		</dependency>
	</dependencies></project>

在maven中我们引入了spring aop相关的依赖,aspectj相关的包有两个,cglib是必须的。

下面我们在项目中新建一个Service类PersonService,它是业务代码,是我们AOP注入的目标类:

package cn.outofmemory.spring_aop_aspect;import org.springframework.stereotype.Service;
@Servicepublic class PersonService 
{

	public void addPerson(String personName) {
		System.out.println("add person " + personName);
	}
	
	public boolean deletePerson(String personName) {
		System.out.println("delete person " + personName) ;
		return true;
	}
	
	public void editPerson(String personName) {
		System.out.println("edit person " + personName);
		throw new RuntimeException("edit person throw exception");
	}
	}

PersonService类中定义了三个方法,addPerson方法无返回值,而deletePerson方法有返回值,而editPerson方法抛出运行时的异常。

下面我们看Aspect类:

package cn.outofmemory.spring_aop_aspect;import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.
After;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.Pointcut;
import org.springframework.stereotype.Component;@Component@Aspectpublic class SimpleAspect {
	@Pointcut("execution(* cn.outofmemory.spring_aop_aspect.*Service*.*(..))")
	public void pointCut() {
	}

	@After("pointCut()")
	public void after(JoinPoint joinPoint) {
		System.out.println("after aspect executed");
	}

	@Before("pointCut()")
	public void before(JoinPoint joinPoint) {
		//如果需要这里可以取出参数进行处理
		//Object[] args = joinPoint.getArgs();
		System.out.println("before aspect executing");
	}

	@AfterReturning(pointcut = "pointCut()", returning = "returnVal")
	public void afterReturning(JoinPoint joinPoint, Object returnVal) {
		System.out.println("afterReturning executed, return result is "
				+ returnVal);
	}

	@Around("pointCut()")
	public void around(ProceedingJoinPoint pjp) throws Throwable {
		System.out.println("around start..");
		try {
			pjp.proceed();
		} catch (Throwable ex) {
			System.out.println("error in around");
			throw ex;
		}
		System.out.println("around end");
	}

	@AfterThrowing(pointcut = "pointCut()", throwing = "error")
	public void afterThrowing(JoinPoint jp, Throwable error) {
		System.out.println("error:" + error);
	}}

SimpleAspect类中的第一个方法是pointCut()方法,此方法无任何执行内容,只有一个@Pointcut的注解,其他方法都会引用这个注解中指定的pointcut表达式。

其他几个方法是各种Advice类型的方法实现,在这些方法中都可以通过JoinPoint实例来获得方法执行的上下文信息,参数信息。需要注意AfterReturning和AfterThrowing,After三种不同Advice的执行顺序。

有了spring框架和aspectj以及cglib的支持,我们只需要实现上面两个类就可以使用spring aop的功能了,下面我们看下如何在spring的配置文件中配置spring aop。

<?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:aop="http://www.springframework.org/schema/aop"       
xmlns:context="http://www.springframework.org/schema/context"       
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">    
<aop:aspectj-autoproxy />    
<context:component-scan base-package="cn.outofmemory" /> 
</beans>

注意在beans节点中需要指定aop命名空间,一家chemaLocation地址,启用aop只需要添加<aop:aspectj-autoproxy/>就可以了。<context:component-scan/>节点用来指定自动扫描bean的命名空间

最后我们要测试下aop的效果,需要新建一个App类作为程序的入口类。

package cn.outofmemory.spring_aop_aspect;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * Hello world!
 *
 */public class App {    public static void main( String[] args )    
 {    	ApplicationContext appContext = new ClassPathXmlApplicationContext("/appContext.xml");    	
 PersonService personService = appContext.getBean(PersonService.class);    	
 String personName = "Jim";    	personService.addPerson(personName);    	
 personService.deletePerson(personName);    	
 personService.editPerson(personName);    	
 ((ClassPathXmlApplicationContext)appContext).close();    
 }
 }

这个类中我们初始化了ApplicationContext,然后从中得到PersonService的实例,并执行其addPerson,deletePerson,editPerson方法,最后关闭ApplicationContext。

其执行结果如下:

before aspect executing
around start..add person Jimafter aspect executed
around endafterReturning executed, return result is null
-------------------------------------
before aspect executing
around start..delete person Jimafter aspect executed
around endafterReturning executed, return result is null
----------------------------------------
before aspect executing
around start..edit person Jimafter aspect executed
error in around
error:java.lang.RuntimeException: edit person throw exceptionException in thread ...

从上面执行结果可以看到我们要执行的Before,around,after以及around,afterReturning,afterThrowing都正常的执行了。

 

AOP 配置详解

http://blog.csdn.net/lipei1220/article/details/52152430

 

Spring AOP 完成日志记录

http://hotstrong.iteye.com/blog/1330046

http://blog.csdn.net/baidu_25958185/article/details/43764467

http://www.oschina.net/code/snippet_1159320_49213

 

Spring Aop自定义注解拦截Controller实现日志管理

http://blog.csdn.net/itnik/article/details/38542373

转载自:https://www.cnblogs.com/nsw2018/p/5881723.html




Home