AOP是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程,为开发者提供了一种描述横切面关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化,它能够将那些与业务无关却为业务模块所共同调用的逻辑或责任,例如事故处理、日志管理、权限控制等,封装起来减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。
正如上图所示,AOP切面编程可以使得某些和系统业务无关却又要被业务模块所共同调用的逻辑和责任如:操作日志、安全监测、事物处理等等,和业务模块分离开,降低系统的耦合度。
AOP带来的好处:
1.降低模块的耦合度。
2.是系统容易扩展。
3.更好的代码复用。
AOP中的概念:
1.Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象。
2.joinpoint(连接点):所谓连接点是指那些被拦截的点,在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器。
3.pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义。
4.Advice(通知):所谓通知是指拦截到joinpoint之后要做的事情就是通知,通知分为前置通知、后置通知、异常通知、最终通知、环绕通知、
5.Target(目标对象):代理的目标对象
6.Weave(织入):指将aspect应用到target对象并导致Proxy对象创建的过程称为织入。
7.Introduction(引入):在不修改类代码的前提下:Introduction可以在运行期为类动态的添加一些方法或Field。
AOP中类及方法的注解
1.Aspect(切面):通过@Aspect来对拦截的自定义类进行注解,如果要交给Spring管理还要使用@Component
2.pointcut(切入点):切入点是在自定义类中进行声明的私有的方法,使用@Pointcut注解,如:
@Pointcut("execution(*com.lixue.service.impl.UserServiceImpl.*(..))")
注:
第一个*表示任意的返回类型,如果又返回值应该使用原始类型,如返回String那么应该是java.lang.String。
第二个*表示该类下所有的方法。
(..)表示方法中的任意参数。
3.前置通知:@Before("切入点的方法名() && args(参数名)")进行注解
4.后置通知:@AfterReturning(pointcut="切入点的方法名称()",returning="参数名称")进行注解
5.例外通知:@AfterThrowing("切入点的方法名称()")进行注解
6.最终通知:@After("切入点的方法名称()")记性注解
7.环绕通知:@Around("切入点的方法名称()")进行注解,这个通知很重要,一般情况我们只使用这个通知进行操作。
使用AOP编程,除了Spring导入的包以外还需要导入的包:
aopalliance-1.0.jar
aspectjrt.jar
aspectjweaver.jar
cglib-nodep-2.1_3.jar
spring-aop-3.2.0.M2.jar
Spring的配置文件也需要做一些修改:
<?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:c="http://www.springframework.org/schema/c" xmlns:cache="http://www.springframework.org/schema/cache" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:jee="http://www.springframework.org/schema/jee" xmlns:lang="http://www.springframework.org/schema/lang" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:p="http://www.springframework.org/schema/p" xmlns:task="http://www.springframework.org/schema/task" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/cache http://www.springframework.org/schema/cache/spring-cache.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc.xsd http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd http://www.springframework.org/schema/lang http://www.springframework.org/schema/lang/spring-lang.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/task http://www.springframework.org/schema/task/spring-task.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"> <!-- AOP面向切面编程 --> <aop:aspectj-autoproxy proxy-target-class="true" /> </beans>
AOP编程案例:
/** * 在AOP编程中,前置通知被拦截后还是可以进行相关操作 * 后置通知和最终通知是执行过后才拦截,所以这三种通知都不太符合现实(我们可以完全不适用这三个通知)。 * 最终我们选择在环绕通知中处理业务逻辑 * @author Liao */ @Aspect @Component//因为要把它交给Spring管理,所以要使用@Component注解 public class AOPLogInterceptor { /** * 切入点 * 第一个*表示任意的返回类型,后面的有一个空格,不加空格的话会报错 * ..表示com.lixue.action包及其所有子包 * 第二个*表示所有的类 * 第三个*表示该类下所有的方法 * (..)表示方法中的任意参数 */ @Pointcut("execution(* com.lixue.dom.web.action..*.*(..))") public void actionMethod(){ } /** * 前置通知 */ @Before("actionMethod() && args()") public void doBefore(){ System.out.println("前置通知"); } /** * 后置通知 * @param name */ @AfterReturning(pointcut="actionMethod()",returning="name") public void doAfterReturning(String name){ System.out.println("后置通知:" + name); } /** * 异常通知 */ @AfterThrowing("actionMethod()") public void doAfterThrowing(){ System.out.println("异常通知"); } /** * 最终通知 */ @After("actionMethod()") public void doAfter(){ System.out.println("最终通知"); } /** * 环绕通知 * 这个注解的功能是actionMethod()方法执行后,就执行, * @return * @throws Throwable */ @Around("actionMethod()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知:" + pjp.getSignature().getName()); return pjp.proceed(); } }
注:光看概念很难理解什么是切入点什么是通知,从这个例子中我们就可以清晰的理解,所谓的切入点就是你要拦截的某一个范围(包及其子包),通知就是执行被拦截包及其子包中方法的不同状态的一些记录(比如说开始之前、执行之后、出现异常等等)。
Spring提供了基于XML配置的方式声明切面,把上面示例中的所有注解去掉(@Component不要去掉,这个注解还要被扫描到),如下:
@Component//因为要把它交给Spring管理,所以要使用@Component注解 public class AOPLogInterceptor { /** * 切入点 */ public void actionMethod(){ } /** * 前置通知 */ public void doBefore(){ System.out.println("前置通知"); } /** * 后置通知 * @param name */ public void doAfterReturning(){ System.out.println("后置通知"); } /** * 异常通知 */ public void doAfterThrowing(){ System.out.println("异常通知"); } /** * 最终通知 */ public void doAfter(){ System.out.println("最终通知"); } /** * 环绕通知 * 这个注解的功能是actionMethod()方法执行后,就执行, * @return * @throws Throwable */ public Object doAround(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("环绕通知:" + pjp.getSignature().getName()); return pjp.proceed(); } }
在XML文件中配置:
<bean id="logInterceptor" class="com.lixue.web.interceptor.AOPLogInterceptor" /> <aop:config> <!-- 定义切面 --> <aop:aspect id="myAop" ref="logInterceptor"> <!-- 定义切入点 --> <aop:pointcut id="actionMethod" expression="execution(* com.lixue.dom.web.action..*.*(..))" /> <!-- 定义前置通知 --> <aop:before pointcut-ref="actionMethod" method="doBefore" /> <!-- 定义后置通知 --> <aop:after-returning pointcut-ref="actionMethod" method="doAfterReturning" /> <!-- 定义环绕通知 --> <aop:around pointcut-ref="actionMethod" method="doAround" /> <!-- 定义异常通知 --> <aop:after-throwing pointcut-ref="actionMethod" method="doAfterThrowing" /> <!-- 定义最终通知 --> <aop:after pointcut-ref="actionMethod" method="doAfter" /> </aop:aspect> </aop:config>
常见的指示符: