AOP(Aspect-Oriented Programming,面向切面编程),通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离。从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。 AOP用来封装多个类的公共行为,将与业务无关,却被业务模块共同调用的逻辑封装起来,减少系统的重复代码。减低模块之间的耦合度。AOP 的作用就是保证开发者在不修改源代码的前提下,为系统中的业务组件添加某种通用功能。AOP 就是代理模式的典型应用。
Spring AOP 是基于 AOP 编程模式的一个框架,它能够有效的减少系统间的重复代码,达到松耦合的目的。Spring AOP 使用纯 Java 实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类植入增强的代码。有两种实现方式:基于接口的 JDK 动态代理和[基于继承的CGLIB 动态代理。
AspectJ 是一个基于 Java 语言的 AOP 框架。AspectJ ,提供了一个专门的编译器,在编译时提供横向代码的植入。
基于接口的动态代理
特点
基于子类的动态代理
特点
JDK 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。而 CGLIB 动态代理是利用 ASM 开源包,加载代理对象类的 class 文件,通过修改其字节码生成子类来处理。
JDK 动态代理只能对实现了接口的类生成代理,而不能针对类。
CGLIB 是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法。因为是继承,所以该类或方法不能声明成 final 类型。
JDK与CGLIB动态代理的性能比较
生成代理实例性能:JDK > CGLIB
代理实例运行性能:JDK > CGLIB
基于 XML 的声明式是指通过 Spring 配置文件的方式来定义切面、切入点及通知,而所有的切面和通知都必须定义在 aop:config 元素中。命名空间:
<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 ">
...
beans>
元素定义切面可以将定义好的Bean转换为切面Bean,所以在使用
之前需要先定义一个普通的spring Bean,示例
<aop:config>
<aop:aspect id="myAspect" ref="aBean">
...
aop:aspect>
aop:config>
用来定义一个切入点,当
元素作为
元素的子元素定义时,表示该切入点是全局切入点,它可被多个切面所共享;当
元素作为
元素的子元素时,表示该切入点只对当前切面有效。示例
<aop:config>
<aop:pointcut id="myPointCut"
expression="execution(* net.biancheng.service.*.*(..))"/>
aop:config>
<aop:aspect id="myAspect" ref="aBean">
<aop:before pointcut-ref="myPointCut" method="..."/>
<aop:after-returning pointcut-ref="myPointCut" method="..."/>
<aop:around pointcut-ref="myPointCut" method="..."/>
<aop:after-throwing pointcut-ref="myPointCut" method="..."/>
<aop:after pointcut-ref="myPointCut" method="..."/>
....
aop:aspect>
@AspectJ 注解有以下两种方法:
@Configuration
@EnableAspectJAutoProxy
public class Appconfig {
}
<aop:aspectj-autoproxy>
AspectJ 类和其它普通的 Bean 一样,可以有方法和字段,不同的是 AspectJ 类需要使用 @Aspect 注解,
// 要求:方法必须是private,返回值类型为void,名称自定义,没有参数
@Pointcut("execution(*net.biancheng..*.*(..))")
private void myPointCut() {
}
常用注解:
注解 | 作用 |
---|---|
@Before | 用于定义前置通知,相当于 BeforeAdvice。 |
@AfterReturning | 用于定义后置通知,相当于 AfterReturningAdvice。 |
@Around | 用于定义环绕通知,相当于MethodInterceptor。 |
@AfterThrowing | 用于定义抛出通知,相当于ThrowAdvice。 |
@After | 用于定义最终final通知,不管是否异常,该通知都会执行。 |
通知的执行顺序:前置通知–>代码块–>后置通知(AfterReturning)或异常通知–>最终通知
实例:
logger类
package com.chenrui.utils;
import org.aspectj.lang.ProceedingJoinPoint;
public class logger {
public void beforePrintLog(){
System.out.println("beforePrintLog前置通知");
}
public void afterReturningPrintLog(){
System.out.println("afterReturning");
}
public void afterThrowingPrintLog(){
System.out.println("afterThrowing");
}
public void afterPrintLog(){
System.out.println("afterPrintLog最终通知");
}
public Object aroundPrintLog(ProceedingJoinPoint proceedingJoinPoint){
Object rtValue = null;
try{
Object[] args = proceedingJoinPoint.getArgs();
System.out.println("aroundPrintLog前置通知");
rtValue = proceedingJoinPoint.proceed(args);
System.out.println("aroundPrintLog后置通知");
return rtValue;
}catch (Throwable t){
System.out.println("aroundPrintLog异常通知");
throw new RuntimeException(t);
}finally {
System.out.println("aroundPrintLog最终通知");
}
}
}
业务层
package com.chenrui.service.impl;
import com.chenrui.service.accountService;
public class accountServiceImpl implements accountService{
@Override
public void saveAccount() {
System.out.println("执行保存方法");
// int i=1/0;
}
@Override
public void updateAccount(int i) {
System.out.println("执行更新方法");
}
@Override
public void deleteAccount() {
System.out.println("执行删除方法");
}
}
bean.xml文件
<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
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="accountService" class="com.chenrui.service.impl.accountServiceImpl">bean>
<bean id="logger" class="com.chenrui.utils.logger">bean>
<aop:config>
<aop:pointcut id="pt" expression="execution(* com.chenrui.service.impl.accountServiceImpl.*(..))"/>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="beforePrintLog" pointcut-ref="pt">aop:before>
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt">aop:after-returning>
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt">aop:after-throwing>
<aop:after method="afterPrintLog" pointcut-ref="pt">aop:after>
aop:aspect>
aop:config>
beans>
测试类
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class aoptest {
@Test
public void aopTest(){
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
accountService accountService = (accountService)ac.getBean("accountService");
accountService.saveAccount();
// accountService.deleteAccount();
// accountService.updateAccount(2);
}
}
结果:
beforePrintLog前置通知
执行保存方法
afterReturning
afterPrintLog最终通知
加入环绕通知后结果:
beforePrintLog前置通知
aroundPrintLog前置通知
执行保存方法
aroundPrintLog后置通知
aroundPrintLog最终通知
afterPrintLog最终通知
afterReturning