spring aop
作者:互联网
1. AOP
aspect oriented programming,面向切面编程。通过预编译方式和运行期动态代理实现程序功能的技术。把程序重复的代码抽取出来,在需要执行的时候通过动态代理的方式,在不修改源码的情况下,对已有的方法进行增强。
①Joinpoint 连接点:
被拦截到的点。spring中指方法(spring只支持方法类型的连接点)
②Pointcut 切入点:
会被拦截并增强的连接点
③Advice 通知/增强:
拦截到连接点后要做的事
通知类型(根据和invoke方法的前后顺序区分):前置通知、后置通知、异常通知(catch里面的)、最终通知(finally里面的)、环绕通知
④Introduction 引介
在不修改现有的类的情况下,向现有的类添加新属性和新方法
⑤Target 目标对象
被代理对象
⑥Weaving 织入
把增强部分应用到目标对象来创建新的代理的过程:①编译期织入(需要特殊的编译器,AspectJ) ②类加载期织入(需要特殊的类加载器,Aspectj 5) ③运行期织入(运行时为目标对象动态地创建一个代理对象,在代理对象里面执行目标对象的方法)
spring采用动态代理在运行期织入 ; AspectJ采用编译期织入和类装载器织入
⑦Proxy 代理
一个类被AOP织入增强后,产生一个代理类
⑧Aspect 切面
切入点和通知的结合
2. spring中的aop
spring内部会根据配置或注解创建代理工厂,我们只需要做好配置即可。
例:增强代码为日志输出
2.1 xml配置
被增强接口及其实现类:
package com.xt.service; public interface IAccountService { void saveAccount(); void updateAccount(int i); int deleteAccount(); }
package com.xt.service.impl; import com.xt.service.IAccountService; public class AccountServiceImpl implements IAccountService { @Override public void saveAccount() { System.out.println("保存"); } @Override public void updateAccount(int i) { System.out.println("更新"); } @Override public int deleteAccount() { System.out.println("删除"); return 10; } }
增强类/通知类:一个打印日志的类
package com.xt.utils; public class Logger { public void printLog(){ System.out.println("---------输出日志-----------"); } public void printLog2(){ System.out.println("--------又一个日志输出---------"); } }
bean的xml也要加入aop命名空间标志:
<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"> </beans>
(1)准备一个被增强类,这里是一个普通的类
<bean id="accountService" class="com.xt.service.impl.AccountServiceImpl"></bean>
(2)将通知类/增强代码用bean配置
<bean id="logger" class="com.xt.utils.Logger"></bean>
(3)声明aop配置 <aop:config>
①配置切入点表达式 <aop:pointcut>
对哪些类的哪些方法进行增强
id:切入点表达式唯一标识
expression:定义切入点表达式 execution(访问修饰符 返回值 包名.类名.方法名(参数列表))
②配置切面 <aop:aspect>
id:切面唯一标识
ref:引用配置好的通知类/增强的bean的id
在<aop:aspect>内部配置通知类型
<aop:before> 前置通知
<aop:after-returning> 后置通知,和异常通知只能有一个执行
<aop:after-throwing> 异常通知
<aop:after> 最终通知
<aop:around> 环绕通知
属性:这几个属性就建立了普通类和通知类之间的关系
method:指定通知类中的方法名称
pointcut-ref:指定切入点表达式的引用,即<aop:pointcut>的id
pointcut:不引用切入点表达式,直接定义切入点表达式
则上面例子的bean写为:
<aop:config> <aop:pointcut id="pc1" expression="execution(public void com.xt.service.impl.AccountServiceImpl.updateAccount(int))" /> <aop:aspect id="logAdvice" ref="logger"> <aop:before method="printLog" pointcut="execution(public void com.xt.service.impl.AccountServiceImpl.saveAccount())"> </aop:before> <aop:before method="printLog2" pointcut-ref="pc1"> </aop:before> </aop:aspect> </aop:config>
调用测试:
ApplicationContext ac = new ClassPathXmlApplicationContext("beanaop.xml"); IAccountService accountService = ac.getBean("accountService", IAccountService.class); accountService.updateAccount(10); accountService.saveAccount(); System.out.println(accountService.getClass()); //class com.sun.proxy.$Proxy3 基于接口的代理
两个注意的地方:
①如果被增强类没有接口,则spring框架自动使用基于 CGLIB的动态代理来增强代码
System.out.println(accountService.getClass()); //class com.xt.service.impl.AccountServiceImpl$$EnhancerBySpringCGLIB$$72e1fb9e
②配置切入点表达式的时候,原方法参数为int,如果切入点表达式里面写 Integer,则不会代理成功
切入点表达式:修饰符 返回值 包名.类名.方法名(参数)
修饰符可省略
返回值:可用 * 表示任意返回值
包名: 可用 * 表示任意包,有多级包时要写多个 *
可用 .. 表示当前包及其子包
类名: 可用 * 表示任意类
方法名: 可用 * 表示任意方法
参数列表: 可用 * 表示任意数据类型,但必须有参数
可用 .. 表示有无参数都可以,参数可为任意类型
2.2 注解配置
其实xml配置里面重要的就两个:①aop:aspect关联切面类 ②aop:before等指定切入点表达式(切面类具体的方法(method属性)和被增强类方法(pointcut)的关联)
注解:在切面类上加 @Aspect ; 在切面类的方法上加注解 @Before("execution(xxx)")
注解API:
@Aspect 指定一个类为通知类
@Pointcut("execution(xxx)") 指定切入点表达式,然后可用如下的注解引用
@Before("pointcut()") 前置通知
@After("pointcut()") 后置通知,最终通知,始终会执行
@AfterReturning("pointcut()") 目标方法后执行,异常不执行
@AfterThrowing("pointcut()") 异常通知
@Around("pointcut()") 环绕通知,环绕目标方法执行
标签:spring,切入点,aop,代理,pointcut,通知,public,表达式 来源: https://www.cnblogs.com/taoXiang/p/13053851.html