AOP
AOP
一、AOP概念
AOP(Aspect Oriented Programming, 面向切面的编程): 通过预编译方式和运行期间动态代理实现程序功能的唯一维护。
AOP是OOP的延续,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序可重用性,同时提高开发效率。
作用:
实现并维护业务逻辑;
在运行期间,对方法进行增强
优势:
耦合度降低
较少重复代码
提高开发效率
维护方便
AOP的相关术语:
a)Joinpoint 连接点:配拦截的方法
b)Pointcut 切入点:被拦截的方法中,需要增强的方法
c)Advice 增强/通知:拦截到连接点之后,要做的增强方法的事情
分为:前置通知,后置通知,异常通知,最终通知和环绕通知
d)Aspect 切面:切入点和通知的结合
e)Target 目标对象:代理的对象
f)Proxy 代理类:被代理对象被AOP增强后,产生一个代理对象
g)Weaving 织入:Spring通过动态代理,把增强应用到目标对象,创建代理对象的过程
h)Introduction 引介:在不修改代码的前提下,为运行期代理类动态的添加一些方法或域
AOP编程:
开发阶段:
a)编写业务代码
b)将公用代码抽取出来,制作成通知。(一般开发完在做)
c)在配置文件中,声明切入点和通知间的关系,即切面
运行阶段:Spring框架监控切入点方法的执行,一旦监控到切入点方法被执行,使用动态代理,创建目标对象的被代理对象,并根据通知类型,在切入点方法的对应位置将通知织入,从而完成增强方法,逻辑控制,简化代码。
二、声明式AOP
声明式AOP使用步骤:
1.引入jar包
org.springframework.spring-context.5.2.9.RELEASE
org.aspectj.aspectjweaver.1.9.6:切入点表达式
2.编写业务类AccountService和通知类Logger,Logger是AccountService的通知类。
3.配置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:context="http://www.springframework.org/schema/context"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="com.spring.nine"/>
<!--配置AOP-->
<aop:config>
<!--配置切面-->
<aop:aspect id="logAop" ref="logger">
<!--切入点-->
<aop:pointcut id="accountServicePointcut" expression="execution(public void com.spring.nine.service.AccountService9.updateAccount(*))"/>
<!--通知-->
<aop:before method="preLog" pointcut="execution(* com.spring.nine.service.AccountService9Impl.updateAccount(*))"/>
<aop:after-returning method="afterLog" pointcut-ref="accountServicePointcut"/>
<aop:after-throwing method="throwLog" pointcut-ref="accountServicePointcut"/>
<aop:after method="finalLog" pointcut-ref="accountServicePointcut"/>
<aop:around method="aroundLog" pointcut="execution(* com.spring.nine.service.AccountService9.saveAccount())"/>
</aop:aspect>
</aop:config>
</beans>
五种通知标签:
1.<aop:before>:前置通知。切入点执行之前
2.<aop:after-returning>:后置通知。切入点执行之后
3.<aop:after-throwing>:异常通知。如果切入点执行异常,执行。
4.<aop:after>: 最终通知。无论切入点是否正常执行,都会在最后执行。
5.<aop:arount>: 环绕通知,在环绕方法中,Spring传入切入点相关的类对象ProceedingJoinPoint,通过此对象实现切入点的方法。还可以在环绕方法内定义其他四种类型的通知。
属性:
method: 要结合的通知类的方法
pointcut=”execute(表达式)”: 通过表达式指定要增强的类的方法。
环绕通知的方法定义:
public Object aroundLog(ProceedingJoinPoint joinPoint)
{
Object returnValue=null;
try{
Object [] args=joinPoint.getArgs();
preLog(); //前置通知
returnValue=joinPoint.proceed(args); //切入点方法的执行
afterLog(); //后置通知
} catch (Throwable throwable) {
throwLog(); //异常通知
throwable.printStackTrace();
}finally {
finalLog(); //最终通知
}
return returnValue;
}
切入点表达式:用于匹配切入点
1.引用:
a)<aop:通知 method=“”,pointcut=”execution(表达式)”>
b)定义:<aop:pointcut id="表达式标识" expression="execution(表达式)"/>
定义在<aop:aspect>标签内,只能在此切面内使用
定义在<aop:config>标签内,所有切面都能使用
引用:<aop:通知 method=“”, pointcut-ref=”表达式标识” >
2.表达式:[修饰符] 返回值类型 包名.类名.方法名(参数列表)
例如:public void com.spring.nine.Account.saveAccount(int id)
a)修饰符可以省略
b)参数列表:基本数据类型直接写,如int
类写包名.类名:java.lang.String
*代表一个任意参数类型
..代表任意个数,任意类型的参数
c)返回值:*代表任意返回值。如 * com.spring.nine.Account.saveAccount(int)
d)包:
*代表任意包。 如:public void *.*.*.Account.saveAccount(int)
..代表当前包及其子包。 如:void com..Account.saveAccount(int)
e)类:*代表任意类。 如:void *..*.saveAccount(int)
f)全通配:* *..*.*(..)
g)常用表达式:* com.spring.nine.service.*.*(..),对业务层方法进行增强。
三、注解AOP
使用步骤:
1.导入jar包
2.配置注解所需的xml文件
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
3.用注解实现AOP
常用注解:
@Aspect:代表当前类时一个切面类
@Pointcut(“execution(表达式)”): 该方法的名字代表一个切入点表达式
@Pointcut("execution(* com.spring.ten.service.AccountServiceImpl.*(..))")
private void serviceExecution(){}
@Before(“execution(表达式)/ 表达式方法名()”): 当前方法是前置通知
例如:Before(“execution(* com.itheima.service.impl.*.*(..)))
例如:Before(“pt1()”) //注意,一定要带括号
@AfterReturning(“”):当前方法是后置通知
@AfterThrowing(“”): 当前方法是异常通知
@After(“”): 当前方法是最终通知
@Around(“”): 当前方法是环绕通知
@Pointcut匹配多个表达式:
在多个表达式之间使用 ||(or), &&(and),!实现逻辑控制
<aop:pointcut id="pointcut" expression="(execution(* com.one.dao.*.find*(..))) or (execution(* com.one.dao.*.query*(..)))"/>
利用AOP实现事务管理:
@Component("transactionManager")
@Aspect
public class TransactionManager {
@Resource(name = "connectionUtils")
private ConnectionUtils connectionUtils;
@Pointcut("execution(* com.spring.ten.service.AccountServiceImpl.*(..))")
private void serviceExecution(){}
@Before("serviceExecution()")
public void begin()
{
try {
connectionUtils.getConnection().setAutoCommit(false);
System.out.println("事务开始...");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@AfterReturning("serviceExecution()")
public void commit()
{
try {
connectionUtils.getConnection().commit();
System.out.println("提交...");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@AfterThrowing("serviceExecution()")
public void rollBack()
{
try {
connectionUtils.getConnection().rollback();
System.out.println("事务回滚...");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
@After("serviceExecution()")
public void release()
{
try {
connectionUtils.getConnection().close();
connectionUtils.removeConnection();
System.out.println("释放...");
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
AOP存在的问题:
无论是xml形式还是注解的形式,最终通知和后置通知的执行顺序有问题。最终通知有时会比后置通知提前执行。
解决方案:使用环绕通知,不会出现通知执行顺序错误的问题。