AOP
(Aspect-Ocriented Programming) 面向切口编程,它与 OOP
(Object-Oriented Programming 面向对象编程) 相辅相成,提供了 与OOP 不同的抽象软件结构的视角
由来:
因为采用 OOP 处理日志记录等操作不仅增加了开发者的工作量,而且提高了升级维护的困难。为了解决此类问题,AOP思想应运而生。
AOP采取横向抽取机制,即将分散在各个方法中的重复代码提取出来,然后在程序编译或运行阶段将这些抽取出来的代码应用到需要执行的地方。
这种横向抽取机制采用传统的OOP是无法办到的,因为OOP实现了是父子关系的纵向重用
AOP不是OOP的替代品,而是OOP的补充,它们相辅相成
AOP术语
通知类型
动态代理
JDK动态代理
CGLIB动态代理
使用 ProxyFactoryBean
创建代理
#
开发 AspectJ
基于XML配置开发 AspectJ
依赖 jar 包
spring-aspects-5.0.2.RELEASE.jar
aspectjweaver-1.8.13.jar
是AspectJ 框架提供的规范包
可以去 maven Repository 官网下载
元素名称 | 用途 |
---|---|
<aop:config> | 在配置文件<beans> 可以包含多个该元素 开发AspectJ的顶层配置元素 |
<aop:aspect> | <aop:config> 的子元素 配置(定义一个切面) 属性 ref 指定切面的定义 |
<aop:pointcut> | <aop:aspect> 的子元素 配置切入点 属性 expression 指定通知增强哪些方法 |
aop:before | <aop:aspect> 的子元素 配置前置通知 属性 method 指定前置通知方法 属性 pointcut-ref 指定关联的切入点 |
<aop:after-returning> | <aop:aspect> 的子元素 配置后置返回通知 属性 method 指定后置返回通知 属性 pintcut-ref 指定关联的切入点 |
<aop:around> | <aop:aspect> 的子元素 配置环绕通知 属性 method 指定环绕通知方法 属性 pintcut-ref 指定关联的切入点 |
<aop:after-throwing> | <aop:aspect> 的子元素 配置异常通知 属性 method 指定异常通知方法 属性 pintcut-ref 指定关联的切入点,没有 异常发生时将不会执行 |
<aop:after> | <aop:aspect> 的子元素 配置后置(最终)通知 属性 method 指定后置最终通知方法 属性 pintcut-ref 指定关联的切入点 |
<aop:declare-parents> | 给通知引入新的额外接口,增强功能 |
<aop:config>
<aop:aspect ref=""> </aop:aspect>
<aop:point expression=""> </aop:point>
<aop:before method="" pointcut-ref=""></aop:before>
<aop:after-returning method="" pointcut-ref=""></aop:after-returning>
<aop:around method="" pointcut-ref=""></aop:around>
<aop:after-throwing method="" pointcut-ref=""></aop:after-throwing>
<aop:after method="" pointcut-ref=""></aop:after>
</aop:config>
package aspectj.xml;
/*切面类,在此类中编写各种类型的通知*/
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class MyAspect {
/*前置通知,使用JoinPoint接口作为参数获取目标对象信息*/
public void before(JoinPoint js){
System.out.println("前置通知,模拟权限控制");
System.out.println("目标对象: " + js.getTarget() + ",被增强处理的方法: " + js.getSignature().getName());
}
/*后置返回通知*/
public void afterReturning(JoinPoint js){
System.out.println("后置返回通知,模拟删除临时文件");
System.out.println("目标对象: " + js.getTarget() + ",被增强处理的方法: " + js.getSignature().getName());
}
/*环绕通知
* ProceedingJoinPoint 是JoinPoint的子接口,代表可以执行的目标方法
* 返回的类型必须是 Object
* 必须一个参数是 ProceedingJoinPoint 类型
* 必须 throws Throwable
* */
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕开始:执行目标方法前,模拟开启事务");
Object obj = pjp.proceed();
System.out.println("环绕结束:执行目标方法后,模拟关闭事务");
return obj;
}
/*异常通知*/
public void except(Throwable e){
System.out.println("异常通知: " + "程序执行异常 " + e.getMessage());
}
/*后置(最终)通知*/
public void after(){
System.out.println("最终通知:模拟释放资源");
}
}
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.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--定义目标对象-->
<bean id="testDao" class="dynamic.jdk.TestDaoImpl"/>
<!--定义切面-->
<bean id="myAspect" class="aspectj.xml.MyAspect"/>
<!--AOP配置-->
<aop:config>
<!--配置切面-->
<aop:aspect ref="myAspect">
<!--配置切入点,通知增强哪些方法-->
<aop:pointcut id="myPointCut" expression="execution(* dynamic.jdk.*.*(..))"/>
<!--将通知与切入点关联-->
<!--关联前置通知-->
<aop:before method="before" pointcut-ref="myPointCut"/>
<!--关联后置返回通知,在目标方法成功后执行-->
<aop:after-returning method="afterReturning" pointcut-ref="myPointCut"/>
<!--关联环绕通知-->
<aop:around method="around" pointcut-ref="myPointCut"/>
<!--关联异常通知,没有异常发生时将不会执行增强,throwing属性设置通知的第二个参数名称-->
<aop:after-throwing method="except" pointcut-ref="myPointCut" throwing="e"/>
<!--关联后置(最终)通知,不管目标方法是否成功都要执行-->
<aop:after method="after" pointcut-ref="myPointCut"/>
</aop:aspect>
</aop:config>
</beans>
expression="execution(* dynamic.jdk.*.*(..))"
是定义切入点表达式
意思:是匹配 dynamic.jdk 包中任意方法的执行
第一个 *
表示的是返回类型,使用 *
代表所有类型
第二个 *
表示的是类名,使用 *
代表匹配包中所有类
第三个 *
表示的是方法,使用 *
表示所有方法
(…) 表示方法的参数,其中 … 表示任意参数
package aspectj.xml.test;
import dynamic.jdk.TestDao;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class XMLAspectJTest {
public static void main(String[] args) {
ApplicationContext appCon = new ClassPathXmlApplicationContext("applicationContext.xml");
TestDao testDaoAdvice = (TestDao) appCon.getBean("testDao");
testDaoAdvice.save();
}
}
基于注解开发 AspectJ
注解名称 | 描述 |
---|---|
@Aspect | 用于定义一个切面,注解在切面类上 |
@Pointcut | 用于定义切入点表达式,在使用时需要定义一个切入点方法 该方法是一个返回值 void 且方法体为空的普通方法 |
@Before | 用于定义前置通知 在使用时通常为其指定 value 属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@AfterReturning | 用于定义后置返回通知 在使用时通常为其指定 value 属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@Around | 用于定义环绕通知 在使用时通常为其指定 value 属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@AfterThrowing | 用于定义异常通知 在使用时通常为其指定 value 属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 另外,还有一个 throwing 属性用于访问目标方法抛出的异常,该属性值与异常通知方法中同名的形参一致 |
@After | 用于定义后置(最终)通知 在使用时通常为其指定 value 属性值,该值可以是已有的切入点,也可以直接定义切入点表达式 |
@Repository()
将目标类标识为bean
context:component-scan base-package=""
扫描需要的包,使注解生效
<aop:aspect-autoproxy/>
启动基于注解的AspectJ支持