在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
AOP (Aspect Orient Programming),直译过来就是 面向切面编程,AOP 是一种编程思想,是面向对象编程(OOP)的一种补充。
面向切面编程,实现在不修改源代码的情况下给程序动态统一添加额外功能的一种技术,如下图所示:
AOP可以拦截指定的方法并且对方法增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离,比如Spring的事务,通过事务的注解配置,Spring会自动在业务方法中开启、提交业务,并且在业务处理失败时,执行相应的回滚策略。
AOP 采取横向抽取机制(动态代理),取代了传统纵向继承机制的重复性代码,其应用主要体现在事务处理、日志管理、权限控制、异常处理等方面。
主要作用是分离功能性需求和非功能性需求,使开发人员可以集中处理某一个关注点或者横切逻辑,减少对业务代码的侵入,增强代码的可读性和可维护性。
简单的说,AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。
比如典型的AOP的应用场景:\
AOP可以拦截指定的方法,并且对方法增强,比如:事务、日志、权限、性能监测等增强,而且无需侵入到业务代码中,使业务与非业务处理逻辑分离。
所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的连接点。
所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。
所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。
引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field。
代理的目标对象。
是指把增强应用到目标对象来创建新的代理对象的过程。spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。
一个类被 AOP 织入增强后,就产生一个结果代理类。
是切入点和通知(引介)的结合。
通知 | 说明 |
before(前置通知) | 通知方法在目标方法调用之前执行 |
after(后置通知) | 通知方法在目标方法返回或异常后调用 |
after-returning(返回后通知) | 通知方法会在目标方法返回后调用 |
after-throwing(抛出异常通知) | 通知方法会在目标方法抛出异常后调用 |
around(环绕通知) | 通知方法会将目标方法封装起来 |
时期 | 说明 |
编译期 | 切面在目标类编译时被织入,这种方式需要特殊的编译器,Aspectj的织入编译器就是以这种方式织入切面的。 |
类加载期 | 切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器(ClassLoader),它可以在目标类引入应用之前增强目标类的字节码。 |
运行期 | 切面在应用运行的某个时期被织入,一般情况下,在织入切面时,AOP容器会为目标对象动态创建一个代理对象,Spring AOP采用的就是这种织入方式。 |
AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:
所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。
配置通知时需实现org.springframework.aop包下的一些接口
前置通知:MethodBeforeAdvice
后置通知:AfterReturningAdvice
环绕通知:MethodInterceptor
异常通知:ThrowsAdvice
1.首先导入IOC和AOP所需要的jar包
2.创建UserService类,这个类完成核心功能操作。
package com.zhao.service.impl;
import com.zhao.service.BookService;
import org.springframework.stereotype.Service;
@Service
public class BookServiceImpl implements BookService {
@Override
public int save(int n) {
System.out.println("添加");
//int a=5/0;
return 1;
}
@Override
public void del() {
System.out.println("删除");
}
@Override
public void update() {
System.out.println("修改");
}
@Override
public void find() {
System.out.println("查询");
}
}
3.创建Loger类,这个类是用来做功能的增强。
public class Loger {
public void check() {
System.out.println("前置通知/增强:执行系统的权限验证");
}
public void logPrint() {
System.out.println("后置通知/增强:执行日志的打印");
}
public void exception() {
System.out.println("异常通知/增强:做出异常的处理");
}
public void distory() {
System.out.println("最终通知/增强:资源的释放");
}
}
4.在spring的beans.xml中开始进行AOP的配置:
5.在测试类中测试UserService的方法是否被增强。
package com.zhao.servlet;
import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test01 {
@Test
public void test01(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
BookService bookService = context.getBean(BookService.class);
bookService.save(5);
}
}
测试结果:
1.环绕通知是一种提供自由灵活对核心类方法进行增强的操作手段,首先还是在beans.xml中配置环绕通知。
2.接着我们需要在增强类中完善我们环绕通知的方法,这个方法需要定义一个ProceedingJoinPoint接口对象作为方法参数,且方法返回值是Object。
3.环绕通知的实现:其实就是通过ProceedingJoinPoint的实现类对象(spring框架会创建)获取要增强的那个类的方法参数、执行方法、获得方法返回值。然后在方法执行之前的操作就是前置通知、在方法执行后的操作就是后置通知、在异常处理中的操作就是异常通知、在finally中执行的操作就是最终通知。具体如下:
/**
* 环绕通知的方法需要在参数中定义ProceedingJoinPoint接口对象,
* Spring框架会自动创建ProceedingJoinPoint接口的实现类对象进行处理
* @param pjp
* @return
*/
public Object around(ProceedingJoinPoint pjp) {
try {
//前置增强
System.out.println("环绕通知---前置增强");
//通知ProceedingJoinPoint 完成代理对象的方法调用
Object result = null;//定义返回值变量
Object[] args = pjp.getArgs();//获取参数列表
result = pjp.proceed(args);
//后置增强
System.out.println("环绕通知---后置增强");
return result;
} catch (Throwable throwable) {
//异常增强
System.out.println("环绕通知---异常增强");
throw new RuntimeException(throwable);
} finally {
//最终增强
System.out.println("环绕通知---最终增强");
}
}
1.首先按照基于XML配置的项目部署
2.首先关于定义IOC容器的bean标签使用注解替换。
3.xml配置的切面配置信息我们在增强类中使用@Aspect注解替代。
4.xml配置切入点的前置通知、后置通知、异常通知、最终通知分别使用@Before、@AfterReturning、@AfterThrowing、@After这四个注解替代,同时在注解中定义要增强那个包中那个类的那个方法,且使用表达式表示。
- @Before:前置注解
- @AfterReturning:后置注解
- @AfterThrowing:异常注解
- @After:最终注解
package com.zhao.advice;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class Loger {
@Before("execution(* *..BookServiceImpl.*(..))")
public void check() {
System.out.println("前置通知/增强:执行系统的权限验证");
}
@AfterReturning("execution(* *..BookServiceImpl.*(..))")
public void logPrint() {
System.out.println("后置通知/增强:执行日志的打印");
}
@AfterThrowing("execution(* *..BookServiceImpl.*(..))")
public void exception() {
System.out.println("异常通知/增强:做出异常的处理");
}
@After("execution(* *..BookServiceImpl.*(..))")
public void distory() {
System.out.println("最终通知/增强:资源的释放");
}
}
5.在beans.xml中配置注解的扫描,同时配置开启aop注解的支持
6.在测试类中测试注解配置的操作是否生效。
package com.zhao.servlet;
import com.zhao.service.BookService;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Test01 {
@Test
public void test01(){
ClassPathXmlApplicationContext context=new ClassPathXmlApplicationContext("spring.xml");
BookService bookService = context.getBean(BookService.class);
bookService.save(5);
}
}
测试结果: