AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
aop中的几个概念
- Aspect: a modularization of a concern that cuts across multiple classes. Transaction management is a good example of a crosscutting concern in enterprise Java applications. In Spring AOP, aspects are implemented using regular classes (the schema-based approach) or regular classes annotated with the
@Aspect
annotation (the@AspectJ
style). - Join point: a point during the execution of a program, such as the execution of a method or the handling of an exception. In Spring AOP, a join point always represents a method execution.
- Advice: action taken by an aspect at a particular join point. Different types of advice include "around," "before" and "after" advice. (Advice types are discussed below.) Many AOP frameworks, including Spring, model an advice as an interceptor, maintaining a chain of interceptors around the join point.
- Pointcut: a predicate that matches join points. Advice is associated with a pointcut expression and runs at any join point matched by the pointcut (for example, the execution of a method with a certain name). The concept of join points as matched by pointcut expressions is central to AOP, and Spring uses the AspectJ pointcut expression language by default.
- Introduction: declaring additional methods or fields on behalf of a type. Spring AOP allows you to introduce new interfaces (and a corresponding implementation) to any advised object. For example, you could use an introduction to make a bean implement an
IsModified
interface, to simplify caching. (An introduction is known as an inter-type declaration in the AspectJ community.) - Target object: object being advised by one or more aspects. Also referred to as the advised object. Since Spring AOP is implemented using runtime proxies, this object will always be a proxied object.
- AOP proxy: an object created by the AOP framework in order to implement the aspect contracts (advise method executions and so on). In the Spring Framework, an AOP proxy will be a JDK dynamic proxy or a CGLIB proxy.
- Weaving: linking aspects with other application types or objects to create an advised object. This can be done at compile time (using the AspectJ compiler, for example), load time, or at runtime. Spring AOP, like other pure Java AOP frameworks, performs weaving at runtime.
通知的类型:
- Before advice(前置通知): Advice that executes before a join point, but which does not have the ability to prevent execution flow proceeding to the join point (unless it throws an exception).
- After returning advice(返回通知): Advice to be executed after a join point completes normally: for example, if a method returns without throwing an exception.
- After throwing advice(异常通知): Advice to be executed if a method exits by throwing an exception.
- After (finally) advice(后置通知): Advice to be executed regardless of the means by which a join point exits (normal or exceptional return).
- Around advice(环绕通知): Advice that surrounds a join point such as a method invocation. This is the most powerful kind of advice. Around advice can perform custom behavior before and after the method invocation. It is also responsible for choosing whether to proceed to the join point or to shortcut the advised method execution by returning its own return value or throwing an exception.
在spring中要实现aop编程有两种方式:
|-通过@AspectJ注解的方式
|-通过配置文件的方式
※在实现aop编程的时候,spring依赖于aspectjweaver.jar,在spring3.0之后都需要自己单独下载
1、首先来看看配置文件的方式实现aop编程:
1 package com.fuwh.service; 2 3 public interface StuService { 4 5 public abstract void addStudent(String name); 6 }
package com.fuwh.service.impl; import com.fuwh.service.StuService; public class StuServiceImpl implements StuService { @Override public void addStudent(String name) { // TODO Auto-generated method stub System.out.println("添加学生:"+name); // return "返回值"; } }
1 package com.fuwh.advice; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.ProceedingJoinPoint; 5 6 //定义一个切面类 7 public class StuServiceAspect { 8 9 //定义前置通知执行的方法 10 public void doBefore(JoinPoint jp){ 11 System.out.println("前置通知 --->>>开始添加学生"+jp.getArgs()[0]); 12 } 13 14 //定义前置通知执行的方法2 15 public void doBefore2(JoinPoint jp){ 16 System.out.println("前置通知2 --->>>开始添加学生"+jp.getArgs()[0]); 17 } 18 19 //定义后置通知执行的方法 20 public void doAfter(JoinPoint jp){ 21 System.out.println("后置通知 --->>>结束添加学生"+jp.getArgs()[0]); 22 } 23 24 /** 25 * 定义环绕通知执行的方法 26 * 环绕通知的第一个参数一定是ProceedingJoinPoint类型的 27 * 调用prceed()方法的时候才执行元方法 28 */ 29 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ 30 System.out.println("环绕通知 --->>>添加学生前"); 31 Object retVal=pjp.proceed(); 32 System.out.println("环绕通知 --->>>添加学生后:"+retVal); 33 return retVal; 34 } 35 36 //定义返回通知执行的方法 37 public void doReturn(JoinPoint jp) { 38 System.out.println("返回通知 --->>>"); 39 } 40 41 //定义异常通知执行的方法 42 public void doAfterThrowing(JoinPoint jp,Throwable ex) { 43 System.out.println("异常通知 --->>>"+ex.getMessage()); 44 } 45 }
1 package com.fuwh.test; 2 3 import org.junit.Before; 4 import org.springframework.context.ApplicationContext; 5 import org.springframework.context.support.ClassPathXmlApplicationContext; 6 import com.fuwh.service.StuService; 7 8 //Junit测试类 9 public class Test { 10 11 private ApplicationContext ac; 12 13 @Before 14 public void setUp() throws Exception { 15 ac=new ClassPathXmlApplicationContext("beans.xml"); 16 } 17 18 @org.junit.Test 19 public void test() { 20 StuService ss=(StuService)ac.getBean("stuServiceImpl"); 21 ss.addStudent("老傅"); 22 } 23 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/aop 9 http://www.springframework.org/schema/aop/spring-aop.xsd"> 10 11 <bean id="stuServiceImpl" class="com.fuwh.service.impl.StuServiceImpl"></bean> 12 <bean id="stuServiceAspect" class="com.fuwh.advice.StuServiceAspect"></bean> 13 14 <aop:config> 15 <!-- 16 aop:aspect:定义切面 17 aop:pointcut:定义切点 expression:定义切点在执行某方法的时候触发 18 aop:before:定义前置通知 pointcut-ref/pointcut 指定执行该通知的切点,有两种定义方式(行内和引用) 19 aop:after:定义后置通知 20 aop:around:定义环绕通知 21 aop:after-returning:定义返回通知 22 aop:after-throwing:定义异常通知 23 --> 24 <aop:aspect ref="stuServiceAspect"> 25 <aop:pointcut id="addStuPt" expression="execution(* com.fuwh.service.*.*(..))" /> 26 <aop:before method="doBefore" pointcut-ref="addStuPt"/> 27 <aop:before method="doBefore2" pointcut="execution(* com.fuwh.service.*.*Dog(..))"/> 28 <aop:after method="doAfter" pointcut="execution(* com.fuwh.service.*.*(..))"/> 29 <aop:around method="doAround" pointcut="execution(* com.fuwh.service.*.*(..))"/> 30 <aop:after-returning method="doReturn" pointcut="execution(* com.fuwh.service.*.*(..))"/> 31 <aop:after-throwing method="doAfterThrowing" pointcut="execution(* com.fuwh.service.*.*(..))" throwing="ex"/> 32 </aop:aspect> 33 </aop:config> 34 35 </beans>
执行结果:
前置通知 --->>>开始添加学生老傅
环绕通知 --->>>添加学生前
添加学生:老傅
返回通知 --->>>
环绕通知 --->>>添加学生后:null
后置通知 --->>>结束添加学生老傅
2、在来看看@AspectJ注解的方式
1 package com.fuwh.service; 2 3 //定义一个接口 4 public interface StuService { 5 6 public abstract void addStudent(String name); 7 }
1 package com.fuwh.service.impl; 2 3 import com.fuwh.service.StuService; 4 5 ///实现接口类 6 public class StuServiceImpl implements StuService { 7 8 @Override 9 public void addStudent(String name) { 10 // TODO Auto-generated method stub 11 System.out.println("添加学生:"+name); 12 } 13 14 }
1 package com.fuwh.advice; 2 3 import org.aspectj.lang.JoinPoint; 4 import org.aspectj.lang.ProceedingJoinPoint; 5 import org.aspectj.lang.annotation.After; 6 import org.aspectj.lang.annotation.AfterReturning; 7 import org.aspectj.lang.annotation.AfterThrowing; 8 import org.aspectj.lang.annotation.Around; 9 import org.aspectj.lang.annotation.Aspect; 10 import org.aspectj.lang.annotation.Before; 11 import org.aspectj.lang.annotation.Pointcut; 12 import org.springframework.context.annotation.Configuration; 13 import org.springframework.context.annotation.EnableAspectJAutoProxy; 14 import org.springframework.stereotype.Component; 15 16 17 @SuppressWarnings("unused") 18 @Component 19 @Aspect 20 public class StuServiceAspect { 21 22 //定义一个空的方法,只为定义Pointcut用,需要注意的是方法的返回值必须为void 23 @Pointcut("execution(* com.fuwh.service.*.*(..))") 24 public void doNothing(){} 25 26 //定义前置通知,指向doNothing()所定义的Pointcut 27 @Before("doNothing()") 28 public void doBefore(JoinPoint jp){ 29 System.out.println("前置通知 --->>>开始添加学生"+jp.getArgs()[0]); 30 } 31 32 //定义后置通知 33 @After("doNothing()") 34 public void doAfter(JoinPoint jp){ 35 System.out.println("后置通知 --->>>结束添加学生"+jp.getArgs()[0]); 36 } 37 38 //定义环绕通知,使用in-place(原型)的方式定义Pointcut 39 @Around("execution(* com.fuwh.service.*.*(..))") 40 public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ 41 System.out.println("环绕通知 --->>>添加学生前"); 42 Object retVal=pjp.proceed(); 43 System.out.println("环绕通知 --->>>添加学生后:"+retVal); 44 return retVal; 45 } 46 47 @AfterReturning("execution(* com.fuwh.service.*.*(..))") 48 public void doReturn(JoinPoint jp) { 49 System.out.println("返回通知 --->>>"); 50 } 51 52 @AfterThrowing(pointcut="execution(* com.fuwh.service.*.*(..))",throwing="ex") 53 public void doAfterThrowing(JoinPoint jp,Throwable ex) { 54 System.out.println("异常通知 --->>>"+ex.getMessage()); 55 } 56 }
1 <?xml version="1.0" encoding="UTF-8"?> 2 3 <beans xmlns="http://www.springframework.org/schema/beans" 4 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 5 xmlns:aop="http://www.springframework.org/schema/aop" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/aop 9 http://www.springframework.org/schema/aop/spring-aop.xsd"> 10 11 <bean id="stuServiceImpl" class="com.fuwh.service.impl.StuServiceImpl"></bean> 12 <bean id="stuServiceAspect" class="com.fuwh.advice.StuServiceAspect"></bean> 13 <!-- 声明启动自动代理 --> 14 <aop:aspectj-autoproxy/> 15 16 </beans>
执行结果
环绕通知 --->>>添加学生前
前置通知 --->>>开始添加学生fuwh
添加学生:fuwh
环绕通知 --->>>添加学生后:null
后置通知 --->>>结束添加学生fuwh
返回通知 --->>>
浅析Pointcut定义格式:AspectJ
execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern)
throws-pattern?)
后续中.....