JavaEE学习之Spring AOP
2014-08-17 22:31 ps_zw 阅读(728) 评论(0) 编辑 收藏 举报一、基本概念
AOP——Aspect-Oriented Programming,面向切面编程,它是spring框架的一个重要组成部分。一般的业务逻辑都有先后关系,我们可以理解为纵向关系,而AOP关注的是横向关系,每一个关注点可以理解为一个横切面。例如我们的大部分代码都会涉及到日志记录,很多的数据库操作都会涉及到事务的创建和提交。那么从横向关注这些逻辑,他们都一个个的切面。
AOP技术的具体实现,可以通过动态代理技术或者是在程序编译期间进行静态的"织入"方式。AOP经常使用的场景包括:日志记录,事务管理,异常处理,安全控制等方面。Spring 中 AOP 代理由 Spring 的 IoC 容器负责生成、管理,其依赖关系也由 IoC 容器负责管理。在spring中可以仅通过配置文件实现AOP,也可以使用注解实现。
AOP相关概念:
- Aspect(方面,切面):系统中需要实现的那些交叉功能,是系统模块化的一个切面,或领域。如日志记录。
- Joinpoint(连接点):应用程序执行过程中,插入切面的地点,可以是方法调用,异常抛出,或者要修改的字段。
- Advice(通知):切面的实际实现,他通知系统新的行为。AOP通知大致上包括:前置通知(Before),环绕通知(Around),后置通知(After Returning),异常通知(Throws Advice) .。
- Pointcut(切入点):定义了将通知应用到哪一个连接点。本质上是一个捕获连接点的结构。在AOP中,可以定义一个point cut,来捕获相关方法的调用。
- Introduce(引入):为对象引入附加的方法或属性,从而达到修改对象结构的目的。
- 目标对象:被通知的对象,既可以是你编写的类,也可以是第三方类。
- 代理:将通知应用到目标对象后创建的对象,应用系统的其他部分不用为了支持代理对象而改变
- Weaving(编织,织入):将切面应用到目标对象从而创建一个新代理对象的过程。织入发生在目标对象生命周期的多个点上:编译器,类装载期,运行期。
二、依赖jar包
本文主要学习spring aop相关使用,所以需要的jar主要包括spring相关jar包和aop相关jar包,具体pom.xml文件如下:
1 <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"> 2 <modelVersion>4.0.0</modelVersion> 3 <groupId>wzhang</groupId> 4 <artifactId>spring-aop</artifactId> 5 <version>0.0.1-SNAPSHOT</version> 6 <name>spring-aop</name> 7 <description>Hello World</description> 8 <dependencies> 9 <dependency> 10 <groupId>org.springframework</groupId> 11 <artifactId>spring-aop</artifactId> 12 <version>3.2.3.RELEASE</version> 13 </dependency> 14 <dependency> 15 <groupId>org.springframework</groupId> 16 <artifactId>spring-beans</artifactId> 17 <version>3.2.3.RELEASE</version> 18 </dependency> 19 <dependency> 20 <groupId>org.springframework</groupId> 21 <artifactId>spring-context</artifactId> 22 <version>3.2.3.RELEASE</version> 23 </dependency> 24 <dependency> 25 <groupId>org.springframework</groupId> 26 <artifactId>spring-core</artifactId> 27 <version>3.2.3.RELEASE</version> 28 </dependency> 29 <dependency> 30 <groupId>org.springframework</groupId> 31 <artifactId>spring-expression</artifactId> 32 <version>3.2.3.RELEASE</version> 33 </dependency> 34 <dependency> 35 <groupId>aopalliance</groupId> 36 <artifactId>aopalliance</artifactId> 37 <version>1.0</version> 38 </dependency> 39 <dependency> 40 <groupId>log4j</groupId> 41 <artifactId>log4j</artifactId> 42 <version>1.2.17</version> 43 </dependency> 44 <dependency> 45 <groupId>org.aspectj</groupId> 46 <artifactId>aspectjrt</artifactId> 47 <version>1.8.2</version> 48 </dependency> 49 <dependency> 50 <groupId>org.aspectj</groupId> 51 <artifactId>aspectjweaver</artifactId> 52 <version>1.8.2</version> 53 </dependency> 54 </dependencies> 55 </project>
三、具体实现
任何技术都是为业务服务的,AOP也不例外。下面以经典的计算器业务为例,简单介绍下AOP的应用。
1.利用maven创建一个simple project,并添加相关依赖。
2.在src/main/resources目录下创建spring配置文件:applicationContext.xml,先放着,用的时候再去配置。
(***************上面两步都是准备工作,下面才是具体实现*********************)
3.定义业务类
定义接口:ICalculate,并提供int add(int,int)方法,再定义一个实现类CalculateImpl:
1 /************** ICalculate接口 **************/ 2 3 package com.wzhang.service; 4 5 public interface ICalculate { 6 7 /** 8 * add 加运算 9 * @return 两数和 10 */ 11 int add(int a,int b); 12 } 13 14 /********* ICalculate 的实现类 **************/ 15 16 package com.wzhang.service.impl; 17 18 import com.wzhang.service.ICalculate; 19 20 public class CalculateImpl implements ICalculate { 21 22 public int add(int a, int b) { 23 return a+b; 24 } 25 }
4.spring 实现AOP有两种方式,配置文件和注解方式。这里先通过配置文件实现方法调用前的权限检查:
1)定义类SecurityAspect,并提供方法checkAuthority.代码如下:
1 package com.wzhang.aspect; 2 /** 3 * 安全检查切面(方面) 4 */ 5 public class SecurityAspect { 6 7 /** 8 * 权限检查 9 */ 10 public void checkAuthority(){ 11 System.out.println("方法调用前,先检查权限!"); 12 } 13 }
2) 在applicationContext.xml中配置AOP:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context-3.2.xsd 9 http://www.springframework.org/schema/tx 10 http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> 13 14 <!-- 扫描com.wzhang下的所有包 --> 15 <context:component-scan base-package="com.wzhang" /> 16 17 <!-- 定义业务类 --> 18 <bean id="calculate" class="com.wzhang.service.impl.CalculateImpl" /> 19 20 <!-- 定义一个安全控制切面 --> 21 <bean id="authority" class="com.wzhang.aspect.SecurityAspect" /> 22 23 <!-- 通过配置文件实现 --> 24 <aop:config> 25 <!-- 定义切面 --> 26 <aop:aspect id="security" ref="authority"> 27 <!-- 指定切入点 --> 28 <aop:pointcut expression="execution(* com.wzhang.service.*.*(..))" 29 id="securityPointcut" /> 30 <!-- 配置前置通知 --> 31 <aop:before method="checkAuthority" pointcut-ref="securityPointcut" /> 32 <!-- 还可以配置环绕通知,后置通知,异常通知 --> 33 </aop:aspect> 34 </aop:config> 35 </beans>
注意事项:
- 需要配置AOP相关的schema;
- 在<aop:config></aop:config>节点中配置切面,也就是我们之前定义的处理类;
- 指定切入点,使用切入点表达式。
- 通知(Advice),可以通过pointcut-ref指定已定义切入点;要定义内置切入点,可将 pointcut-ref 属性替换为 pointcut 属性。
经过以上几步,使用配置文件实现的主体代码就完成了,剩下的就是测试了。我们这里和用注解实现AOP一起测试
5. 通过注解实现方法调用前后的日志记录
1)通过注解实现AOP需要在配置文件中配置: <aop:aspectj-autoproxy />节点,启动对@AspectJ注解的支持。配置如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context-3.2.xsd 9 http://www.springframework.org/schema/tx 10 http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> 13 14 <!-- 扫描com.wzhang下的所有包 --> 15 <context:component-scan base-package="com.wzhang" /> 16 17 <!-- 自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。(启动对@AspectJ注解的支持) --> 18 <aop:aspectj-autoproxy /> 19 </beans>
2)定义切面。代码如下:
1 package com.wzhang.aspect; 2 3 import org.aspectj.lang.annotation.AfterReturning; 4 import org.aspectj.lang.annotation.Aspect; 5 import org.aspectj.lang.annotation.Before; 6 import org.aspectj.lang.annotation.Pointcut; 7 8 /** 9 * 记录日志切面 10 * @author wzhang 11 * 12 */ 13 @Aspect 14 public class LogAspect { 15 16 /** 17 * 前置切入点, 18 */ 19 @Pointcut("execution(* com.wzhang.service.*.*(..))") 20 public void beforePointcut(){} 21 22 /** 23 * 后置通知,日志记录 24 */ 25 @AfterReturning("execution(* com.wzhang.service.*.*(..))") 26 public void log(){ 27 System.out.println("方法调用后,记录操作日志!"); 28 } 29 30 /** 31 * 前置通知 32 */ 33 @Before("beforePointcut()") 34 public void beforeLog(){ 35 System.out.println("使用@Pointcut注解,实现方法调用前记录操作日志!"); 36 } 37 38 }
3)配置bean:<bean id="log" class="com.wzhang.aspect.LogAspect" />,最终配置(包括第4步的配置)如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" 4 xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" 5 xsi:schemaLocation="http://www.springframework.org/schema/beans 6 http://www.springframework.org/schema/beans/spring-beans-3.2.xsd 7 http://www.springframework.org/schema/context 8 http://www.springframework.org/schema/context/spring-context-3.2.xsd 9 http://www.springframework.org/schema/tx 10 http://www.springframework.org/schema/tx/spring-tx-3.2.xsd 11 http://www.springframework.org/schema/aop 12 http://www.springframework.org/schema/aop/spring-aop-3.2.xsd"> 13 14 <!-- 扫描com.wzhang下的所有包 --> 15 <context:component-scan base-package="com.wzhang" /> 16 17 <!-- 定义业务类 --> 18 <bean id="calculate" class="com.wzhang.service.impl.CalculateImpl" /> 19 20 <!-- 定义一个安全控制切面 --> 21 <bean id="authority" class="com.wzhang.aspect.SecurityAspect" /> 22 23 <bean id="log" class="com.wzhang.aspect.LogAspect" /> 24 25 <!-- 通过配置文件实现 --> 26 <aop:config> 27 <!-- 定义切面 --> 28 <aop:aspect id="security" ref="authority"> 29 <!-- 指定切入点 --> 30 <aop:pointcut expression="execution(* com.wzhang.service.*.*(..))" 31 id="securityPointcut" /> 32 <!-- 配置前置通知 --> 33 <aop:before method="checkAuthority" pointcut-ref="securityPointcut" /> 34 <!-- 还可以配置环绕通知,后置通知,异常通知 --> 35 </aop:aspect> 36 </aop:config> 37 38 39 <!-- 自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。(启动对@AspectJ注解的支持) --> 40 <aop:aspectj-autoproxy /> 41 </beans>
几点说明:
通过@Aspect注解声明切面;
通过@Before,@Around,@AfterReturning,@AfterThrowing等定义通知,可以通过表达式execution(xxx)定义切入点;
还可以在方法上@Pointcut定义切入点,方便同类中其他方法使用此处配置的切入点。
6.测试,在src/test/java目录下创建测试类:AppTest.代码如下:
1 package com.wzhang.test; 2 3 import org.junit.Assert; 4 import org.junit.Test; 5 import org.springframework.context.ApplicationContext; 6 import org.springframework.context.support.ClassPathXmlApplicationContext; 7 8 import com.wzhang.domain.UserBean; 9 import com.wzhang.service.ICalculate; 10 import com.wzhang.service.IUser; 11 12 public class AopTest { 13 14 static ApplicationContext ctx; 15 static { 16 ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); 17 } 18 19 @Test 20 public void calculateTest(){ 21 ICalculate cal = (ICalculate) ctx.getBean("calculate"); 22 int result = cal.add(5, 6); 23 Assert.assertEquals(11, result); 24 } 25 }
运行测试,输入以下结果:
第一行说明使用配置文件实现AOP成功,第二行说明使用@Pointcut注解实现前置通知成功。
四、切入点表达式的相关说明
1.切入点表达式中:"*"-匹配所有字符;".."-用于匹配多个包,多个参数;"+"-表示类及其子类;
2.切入点表达式支持逻辑运算符:&&,||,!;
3.使用execution用于匹配子表达式;
4.AOP的注解不仅仅局限于例子中的几个,还有@args,@within,@this,@target等注解,需要我们深入研究。
示例代码下载:spring-aop.zip