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形式还是注解的形式,最终通知和后置通知的执行顺序有问题。最终通知有时会比后置通知提前执行。
解决方案:使用环绕通知,不会出现通知执行顺序错误的问题。
posted @ 2021-12-30 20:26  言思宁  阅读(484)  评论(0编辑  收藏  举报