AOP概述
AOP 即 Aspect Oriented Program 面向切面编程,首先,在面向切面编程的思想里面,把功能分为核心业务功能和辅助功能。
- 核心业务:比如登陆,增加数据,删除数据都叫核心业务
- 辅助功能:比如性能统计,日志,事务管理等等
面向切面的编程(AOP)是一种编程范式,旨在通过允许横切关注点的分离,提高模块化。AOP提供切面来将跨越对象关注点模块化。
AOP要实现的是在我们写的代码的基础上进行一定的包装,如在方法执行前、或执行后、或是在执行中出现异常后这些地方进行拦截处理或叫做增强处理。
Aop的概念
- Pointcut:切点。一个基于正则表达式的表达式。通常一个pointcut,会选取程序中的某些我们感兴趣的执行点,或者说是程序执行点的集合。
- JoinPoint:通过pointcut选取出来的集合中的具体的一个执行点,我们就叫JoinPoint。
- Advice:通知。在选取出来的JoinPoint上要执行的操作逻辑。5种类型:环绕通知(@Around)、前置通知(@Before)、后置通知(@After)、最终通知(@AfterRunning)、异常通知(@AfterThrowing)。
- Aspect:切面。这个关注点可能会横切多个对象和模块,事务管理是横切关注点的很好的例子。由pointcut 和advice组成。
- Weaving:织入。把切面应用到目标对象来创建新的 advised 对象的过程。
AOP现有两个主要的流行框架,即SpringAOP和AspectJ。

SpringAOP和AspectJ区别:
- 织入的时期不同:SpringAop采用的动态织入,而Aspectj是静态织入。SpringAop基于动态代理来实现,默认如果使用接口的,用JDK提供的动态代理实现,如果是方法则使用CGLIB实现;而Aspectj静态织入,通过修改代码来实现,编译前、后都可以进行织入,而且类加载后也可以织入。
- 使用对象不同:SpringAOP需要依赖IOC容器来管理,并且只能作用于Spring容器中的SpringBean对象;而AspectJ可以在任何Java对象上应用通知。
- 功能上:Spring AOP的功能不如AspectJ全面。
- 性能上:Spring AOP是基于动态代理来实现的,在容器启动时需要生成代理实例,在方法调用上也会增加栈的深度,使得Spring AOP的性能不如AspectJ好。
SpringAop概述
在Spring-AOP思想里,辅助功能被定义为切面,核心业务功能和切面功能分别独立进行开发,然后把切面功能和核心业务功能 “编织” 在一起,这种可选择性的,低耦合的把切面和核心业务功能结合在一起的编程思想,就叫做切面编程(AOP)。
利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
底层原理
AOP底层使用动态代理:
- 有接口情况,使用JDK动态代理。
- 1、创建方法拦截器类(JDK动态代理实现接口InvocationHandler.invoke()),实现相应接口方法。
- 2、使用方法拦截器类,创建动态代理类(JDK动态代理使用Proxy.newProxyInstance(ClassLoader var0, Class<?>[] var1, InvocationHandler var2))。
- 3、使用动态代理类调用方法即可。
- 没有接口情况,使用CGLIB动态代理。
- 1、创建方法拦截器类(CGLIB动态代理实现接口MethodInterceptor.intercept()),实现相应接口方法。
- 2、使用方法拦截器类,创建动态代理类(CGLIB动态代理使用Enhancer.setCallback(Callback callback))。
- 3、使用动态代理类调用方法即可。
代码详见《Java-动态代理》
AspectJ概述
AspectJ全称是Eclipse AspectJ,AspectJ不是Spring的组成部分。
AspectJ是一个易用的功能强大的AOP框架,可以单独使用,也可以整合到其它框架中。一般把AspectJ和Spirng框架一起使用,Spring框架一般都是基于AspectJ实现AOP操作。
基于AspectJ实现AOP操作:
- 基于xml配置文件实现
- 基于注解方式实现(常用)
基于xml配置文件方式
引用AspectJ包

业务类 ProductService:
package springAop.AOP.service;
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
}
切面类 LoggerAspect:
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggerAspect {
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
}
配置文件applicationContext.xml:
<?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"
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">
<!-- 声明业务对象 -->
<bean name="s" class="springAop.AOP.service.ProductService" />
<!-- 声明日志切面 -->
<bean id="loggerAspect" class="springAop.AOP.aspect.LoggerAspect"/>
<!--aop:config 把业务对象与辅助功能编织在一起-->
<aop:config>
<!-- 指定切点-核心业务功能-->
<aop:pointcut id="productServiceCutpoint"
expression="execution(* springAop.AOP.service.ProductService.*(..)) "/>
<!-- 指定切面-辅助功能-->
<aop:aspect id="logAspect" ref="loggerAspect">
<aop:around pointcut-ref="productServiceCutpoint" method="log"/>
</aop:aspect>
</aop:config>
</beans>
- aop:pointcut:指定切点-核心业务功能,指定被代理的类方法。
- aop:aspect:指定切面-辅助功能,指定增强类方法以及切点。
- aop:config:把切点与切面编织在一起。
execution(* com.how2java.service.ProductService.*(…)) :
- 第一个 * :表示返回任意类型
- 第二个 * :表示任意方法
- (…) :表示参数是任意数量和任意类型
- com.how2java.service.ProductService.* :表示包名以 com.how2java.service.ProductService 开头的类的任意方法
TestSpring:
package springAop.AOP;
import configTest.AOP.service.ProductService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class TestSpring {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[] {"springAop/AOP/applicationContext.xml"});
ProductService s = (ProductService) context.getBean("s");
s.doSomeService();
}
}
运行结果:

基于注解方式(常用)
注解业务类 ProductService:
package springAop.AOP.service;
import org.springframework.stereotype.Component;
@Component("s")
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
}
}
注解切面类 LoggerAspect:
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggerAspect {
@Around(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
}
@Aspect 注解表示这是一个切面
@Component 表示这是一个bean,由Spring进行管理。
@Around(value = “execution(* springAop.AOP.service.ProductService.*(..))”) 表示对com.how2java.service.ProductService 这个类中的所有方法进行切面操作
语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )
- execution(* springAop.BookDao.add(..)):对springAop.BookDao类里面的add进行增强
- execution(* springAop.BookDao.* (..)):对springAop.BookDao类里面的所有的方法进行增强
- execution(* springAop.*.* (..)):对springAop包里面所有类,类里面所有方法进行增强
配置文件applicationContext.xml:
<?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.0.xsd">
<context:component-scan base-package="springAop.AOP.service"/>
<context:component-scan base-package="springAop.AOP.aspect"/>
<!-- 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy/>
</beans>
运行结果:

相同的切入点抽取
注解切面类 LoggerAspect:
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggerAspect {
/* 定义切点*/
@Pointcut(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void productServicePointcut(){}
@Around(value = "productServicePointcut()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("start log:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("end log:" + joinPoint.getSignature().getName());
return object;
}
}
运行结果:

@Pointcut(value = “execution(* springAop.AOP.service.ProductService.*(..))”)
public void productServicePointcut(){}
定义切点 productServicePointcut() ,通知类方法可以直接使用。也可以专门创建切点类,统一管理所有切点。
多个切面类增强一个方法
增加统计增强类ReportAspect:
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class ReportAspect {
/* 定义切点*/
@Pointcut(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void productServicePointcut(){}
@Around(value = "productServicePointcut()")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("report:" + joinPoint.getSignature().getName());
Object object = joinPoint.proceed();
System.out.println("report:" + joinPoint.getSignature().getName());
return object;
}
}
切面类 LoggerAspect增加优先级注解@Order(2):
@Order(2)
@Aspect
@Component
public class LoggerAspect {
...
}
运行结果:

在增强切面类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高。
五种通知注解
@Before: 前置通知,在方法执行之前执行。value@After: 后置通知,在方法执行之后执行。无论连接点方法执行成功还是出现异常,都将执行后置方法。value@Around: 环绕通知,围绕着方法执行。环绕通知需要携带ProceedingJoinPoint类型的参数,且环绕通知必须有返回值,返回值即为连接点方法的返回值。value@AfterRunning: 返回通知,在方法返回结果之后执行。当连接点方法成功执行后,返回通知方法才会执行。所以,返回通知方法可以拿到连接点方法执行后的结果。value、pointcut、returning@AfterThrowing: 异常通知,在方法抛出异常之后。当连接点方法执行异常,异常通知方法才会执行。value、pointcut、throwing
LoggerAspect:
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Aspect
@Component
public class LoggerAspect {
//@Before 注解表示作为前置通知
@Before(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void beforMethod(JoinPoint point){
String methodName = point.getSignature().getName();
List<Object> args = Arrays.asList(point.getArgs());
System.out.println("调用前连接点方法为:" + methodName + ",参数为:" + args);
System.out.println("Before...");
}
//@After 后置通知
@After(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void afterMethod(JoinPoint point) {
String methodName = point.getSignature().getName();
List<Object> args = Arrays.asList(point.getArgs());
System.out.println("调用前连接点方法为:" + methodName + ",参数为:" + args);
System.out.println("After...");
}
//@Around: 环绕通知
@Around(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("Around环绕之前...");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("Around环绕之后...");
}
//@AfterRunning: 返回通知
@AfterReturning(value = "execution(* springAop.AOP.service.ProductService.*(..))",
returning="result")
public void afterReturning(JoinPoint point, Object result) {
String methodName = point.getSignature().getName();
List<Object> args = Arrays.asList(point.getArgs());
System.out.println("连接点方法为:" + methodName + ",参数为:" + args + ",目标方法执行结果为:" + result);
System.out.println("AfterReturning...");
}
//@AfterThrowing: 异常通知
@AfterThrowing(value = "execution(* springAop.AOP.service.ProductService.*(..))",
throwing="ex")
public void afterThrowing(JoinPoint point, Exception ex) {
String methodName = point.getSignature().getName();
List<Object> args = Arrays.asList(point.getArgs());
System.out.println("连接点方法为:" + methodName + ",参数为:" + args + ",异常为:" + ex);
System.out.println("AfterThrowing...");
}
}
运行结果:

五种通知注解的执行顺序
正常情况
通过上面的运行结果:

可以看出正常情况的执行顺序: @Around -> @Before -> 切点方法 -> @Around -> @After -> @AfterReturning
异常情况
业务类 ProductService增加异常抛出
package springAop.AOP.service;
import org.springframework.stereotype.Component;
@Component("s")
public class ProductService {
public void doSomeService(){
System.out.println("doSomeService");
throw new NullPointerException();
}
}
运行结果:

可以看出异常情况的执行顺序: @Around -> @Before -> 切点方法 -> @After -> @AfterThrowing
多个增强类增强一个方法的执行顺序
日志切面LoggerAspect
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(2)
@Aspect
@Component
public class LoggerAspect {
//@Before 注解表示作为前置通知
@Before(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void before() {
System.out.println("[LoggerAspect]Before...");
}
//@After 后置通知
@After(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void after() {
System.out.println("[LoggerAspect]After...");
}
//@Around: 环绕通知
@Around(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("[LoggerAspect]Around环绕之前...");
//被增强的方法执行
proceedingJoinPoint.proceed();
System.out.println("[LoggerAspect]Around环绕之后...");
}
//@AfterRunning: 返回通知
@AfterReturning(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void afterReturning() {
System.out.println("[LoggerAspect]AfterReturning...");
}
//@AfterThrowing: 异常通知
@AfterThrowing(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void afterThrowing() {
System.out.println("[LoggerAspect]AfterThrowing...");
}
}
统计切面ReportAspect
package springAop.AOP.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
@Order(1)
@Aspect
@Component
public class ReportAspect {
/* 定义切点*/
@Pointcut(value = "execution(* springAop.AOP.service.ProductService.*(..))")
public void productServicePointcut(){}
//@Before 注解表示作为前置通知
@Before(value = "productServicePointcut()")
public void before() {
System.out.println("[ReportAspect]Before...");
}
//@After 后置通知
@After(value = "productServicePointcut()")
public void after() {
System.out.println("[ReportAspect]After...");
}
//@Around: 环绕通知
@Around(value = "productServicePointcut()")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("[ReportAspect]Around环绕之前...");
proceedingJoinPoint.proceed();
System.out.println("[ReportAspect]Around环绕之后...");
}
//@AfterRunning: 返回通知
@AfterReturning(value = "productServicePointcut()")
public void afterReturning() {
System.out.println("[ReportAspect]AfterReturning...");
}
//@AfterThrowing: 异常通知
@AfterThrowing(value = "productServicePointcut()")
public void afterThrowing() {
System.out.println("[ReportAspect]AfterThrowing...");
}
}
运行结果:

优先级高的拦截器先执行@Around -> @Before , 然后低优先级的拦截器的通知方法全部执行后 ,再执行高优先级拦截器的@Around -> @After -> @AfterReturning 。
完全使用注解开发
创建配置类,不再需要xml配置文件
package springAop.AOP;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"springAop.AOP"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}
@EnableAspectJAutoProxy(proxyTargetClass = true)
- proxyTargetClass = false:使用JDK的基于接口的方式进行织入的。这时候代理生成的是一个接口对象,将这个接口对象强制转换为实现该接口的一个类;
- proxyTargetClass = true: 使用cglib的动态代理方式。
小结
- AOP面向切面编程,在不修改原来代码前提下,对原逻辑进行拦截增强处理,提高模块化,降低耦合性。
- Pointcut(切点)、JoinPoint(具体执行点)、Advice(通知)、Aspect(切面)、Weaving(织入)
- @Aspect标识类是切面类
- @Around(value = “execution(…)”)标识切面方法
- @Pointcut(value = “execution(…)”)标识切点
- 多个切面类增强一个方法时,通过@Order(数字类型值)标识切面类,数字类型值越小优先级越高。
- 五种通知注解:@Before、@After、@Around、@AfterRunning、@AfterThrowing
- 完全使用注解开发:@EnableAspectJAutoProxy(proxyTargetClass = true)
1228

被折叠的 条评论
为什么被折叠?



