欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

AOP

程序员文章站 2022-07-12 14:29:08
...

AOP

AOP相关术语

Joinpoint(连接点):

所谓连接点是指那些被拦截到的点,在Spring中,这些点指的是方法,因为Spring只支持方法类型的连接点。

Pointtout(切入点):

所谓切入点是指我们要对那哪些Jointpoint进行拦截的定义。

Advice(通知/增强):

所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

Introduction(引介):

引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期为类动态地添加一些方法或Field.

Target(目标对象):

代理的目标对象。

Weaving(织入):

是指把增强应用到目标对象来创建新的代理对象的过程。

Spring采用动态代理织入,而Aspect采用编译期织入和类装载期织入。

Proxy(代理):

一个类被AOP织入增强后,就产生一个结果代理类。

Aspect(切面):

是切入点和通知(引介)的结合。

AOP

自定义的日志类

类中的方法也就是需要在增强方法中切入的方法

public class Logger{
    public void beforePrintLog(){
        System.out.println("前置通知方法执行");
	}
    public void afterReturningPrintLog(){
        System.out.println("后置通知方法执行");
	}
    public void afterThrowingPrintLog(){
        System.out.println("异常通知方法执行");
	}
    public void afterPrintLog(){
        System.out.println("最终通知方法执行");
	}
}

AOP在xml中配置:

需要导入依赖

<dependency>
	<groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.7</version>
</dependency>

该依赖是用来支持切入点表达式

例:execution(* com.muzi.service.impl..(…))

aop名称空间:

<beans xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="                      http://www.springframework.org/schema/aop   http://springframework.org/schema/ao/spring-aop.xsd">
</beans>
<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.muzi.service.impl.AccountServiceImpl"></bean>
<!--配置Logger自定义日志类-->
<bean id="logger" class="com.muzi.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
	<!--配置切面-->
    <aop:aspect id="logAdvice" ref="logger">
    	<!--配置前置通知:在切入点方法执行之前执行-->
        <aop:before method="beforeprintLog" pointcut="execution(* com.muzi.service.impl.*.*(..))"></aop:before>
        <!--配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
        <aop:after-returning method="afterReturningPrintLog" pointcut="execution(* com.muzi.service.impl.*.*(..))"></aop:after-returning>
        <!--配置异常通知:在切入点方法执行产生异常之后执行,它和后置通知永远只能执行一个-->
        <aop:after-throwing method="afterThrowingPrintLog" pointcut="execution(* com.muzi.service.impl.*.*(..))"></aop:after-throwing>
        <!--配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
        <aop:after method="afterPrintLog" pointcut="execution(* com.muzi.service.impl.*.*(..))"></aop:after>
    </aop:aspect>
</aop:config>

还可以进一步省略每个切入点的pointcut属性:

<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.muzi.service.impl.AccountServiceImpl"></bean>
<!--配置Logger自定义日志类-->
<bean id="logger" class="com.muzi.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
	<!--配置切面-->
    <aop:aspect id="logAdvice" ref="logger">
    	<!--配置前置通知:在切入点方法执行之前执行-->
        <aop:before method="beforeprintLog" pointcut-ref="pt1"></aop:before>
        <!--配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
        <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>
        <!--配置异常通知:在切入点方法执行产生异常之后执行,它和后置通知永远只能执行一个-->
        <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
        <!--配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
        <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
        <!--配置切入点表达式,id属性用于指定表达式的唯一标识,expression属性用于指定表达式内容。此标签写在aop:aspect标签内部只能当前切面使用。它还可以写在aop:aspect外面,此时变成了所有切面可用-->
        <aop:pointcut id="pt1" expression="excution(* com.muzi.service.impl.*.*(..))"></aop:pointcut>
    </aop:aspect>
</aop:config>

或者把aop:pointcut写在所有切面之前,被所有切面所共享:

<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.muzi.service.impl.AccountServiceImpl"></bean>
<!--配置Logger自定义日志类-->
<bean id="logger" class="com.muzi.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
    <!--它还可以写在aop:aspect外面,此时变成了所有切面可用-->
    <aop:pointcut id="pt1" expression="excution(* com.muzi.service.impl.*.*(..))"></aop:pointcut>
	<!--配置切面-->
    <aop:aspect id="logAdvice" ref="logger">
    	<!--配置前置通知:在切入点方法执行之前执行-->
        <aop:before method="beforeprintLog" pointcut-ref="pt1"></aop:before>
        <!--配置后置通知:在切入点方法正常执行之后执行,它和异常通知永远只能执行一个-->
        <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"></aop:after-returning>
        <!--配置异常通知:在切入点方法执行产生异常之后执行,它和后置通知永远只能执行一个-->
        <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1"></aop:after-throwing>
        <!--配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
        <aop:after method="afterPrintLog" pointcut-ref="pt1"></aop:after>
    </aop:aspect>
</aop:config>

配置环绕通知:

需要提前在自定义日志类中添加该方法:

public Object aroundPrintLog(ProceedingJoinPoint pjp){
        Object rtValue=null;
        try{
            //得到方法执行所需要的参数
            Object[] args=pjp.getArgs();
            System.out.println("Logger类中的aroundPointLog方法开始记录日志了。。。前置");
            //明确调用业务层方法(切入点方法)
            rtValue=pjp.proceed(args);
            System.out.println("Logger类中的aroundPointLog方法开始记录日志了。。。后置");
            return rtValue;   
        }catch(Throwable t){
            System.out.println("Logger类中的aroundPointLog方法开始记录日志了。。。异常");
        }finally{
            System.out.println("Logger类中的aroundPointLog方法开始记录日志了。。。最终");
        }
    }
<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.muzi.service.impl.AccountServiceImpl"></bean>
<!--配置Logger自定义日志类-->
<bean id="logger" class="com.muzi.utils.Logger"></bean>
<!--配置AOP-->
<aop:config>
    <!--它还可以写在aop:aspect外面,此时变成了所有切面可用-->
    <aop:pointcut id="pt1" expression="excution(* com.muzi.service.impl.*.*(..))"></aop:pointcut>
	<!--配置切面-->
    <aop:aspect id="logAdvice" ref="logger">
        <!--配置环绕通知: 
        <aop:around method="aroundPointLog" pointcut-ref="pt1"></aop:around>
    </aop:aspect>
</aop:config>

Spring基于AOP的自定义事务控制

1.在xml中配置自定义事务控制

提前写自定义的事务类:

public class TransactionManager{
    @Autowired
    private ConnectionUtils connectionUtils;
    public void beginTransaction(){
        try{
            connectionUtils.getThreadConnectio().setAutoCommit(false);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    public void commit(){
        try{
            connectionUtils.getThreadConnction().commit();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    publc void rollback(){
        try{
            connectionUtils.getThreadConnection().rollback();
        }catch(Excepton e){
            e.printStackTrace();
        }
    }
    public void release(){
        try{
            connectionUtils.getThreadConnection().close();
            conectionUtils.removeConnection();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    
}
<!--配置spring的IOC,把service对象配置进来-->
<bean id="accountService" class="com.muzi.service.impl.AccountServiceImpl"></bean>
<!--配置自定义事务管理器类-->
<bean id="txManager" class="com.muzi.utils.TransactionManager"></bean>
<!--配置AOP-->
<aop:config>
    <!--配置通用的切入点表达式-->
    <aop:pointcut id="pt1" expression="excution(* com.muzi.service.impl.*.*(..))"></aop:pointcut>
	<!--配置切面-->
    <aop:aspect id="logAdvice" ref="logger">
    	<!--配置前置通知:开启事务-->
        <aop:before method="beginTransaction" pointcut-ref="pt1"></aop:before>
        <!--配置后置通知:提交事务-->
        <aop:after-returning method="commit" pointcut-ref="pt1"></aop:after-returning>
        <!--配置异常通知:回滚事务-->
        <aop:after-throwing method="rollback" pointcut-ref="pt1"></aop:after-throwing>
        <!--配置最终通知:释放连接-->
        <aop:after method="release" pointcut-ref="pt1"></aop:after>
    </aop:aspect>
</aop:config>

2.使用AOP注解的自定义事务控制

推荐使用环绕通知!!!

@Conponent("txManager")
@Aspect
public class TransactionManager{
    @Autowired
    private ConnectionUtils connectionUtils;
    @Pointcut("execution(* com.muzi.service.impl.*.*(..))")
    private void pt1(){}
    @Before("pt1()")
    public void beginTransaction(){
        try{
            connectionUtils.getThreadConnectio().setAutoCommit(false);
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    @AfterReturning("pt1()")
    public void commit(){
        try{
            connectionUtils.getThreadConnction().commit();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    @AfterThrowing("pt1()")
    publc void rollback(){
        try{
            connectionUtils.getThreadConnection().rollback();
        }catch(Excepton e){
            e.printStackTrace();
        }
    }
    @After("pt1()")
    public void release(){
        try{
            connectionUtils.getThreadConnection().close();
            conectionUtils.removeConnection();
        }catch(Exception e){
            e.printStackTrace();
        }
    }
    @Arround("pt1()")
    public Object arroundAdvice(ProceedingJoinPoint pjp){
        try{
            Object[] args=pjp.getArgs();
            this.beginTransaction();
            rtValue=pjp.proceed(args);
            this.commit();
            return rtValue;
        }catch(Throwable e){
            this.rollback;
            throw new RuntimeExcetion(e);
        }finally{
            this.release();
        }
    }
        
    }
}

Spring中基于XML的声明式事务控制配置

spring中基于XML的声明式事务配置步骤
1、配置事务管理器
2、配置事务的通知
此时我们徐亚导入事务的约束 tx名称空间和约束,同时也需要aop的 使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
3、配置AOP中的通用切入点表达式
4、建立事务通知和切入点表达式的对应关系
5、配置事务的属性
是在事务的通知tx:advice标签的内部

<!--spring中基于XML的声明式事务配置步骤
	1、配置事务管理器
	2、配置事务的通知
			此时我们徐亚导入事务的约束  tx名称空间和约束,同时也需要aop的   使用tx:advice标签配置事务通知
属性:
id:给事务通知起一个唯一标识
transaction-manager:给事务通知提供一个事务管理器引用
	3、配置AOP中的通用切入点表达式
	4、建立事务通知和切入点表达式的对应关系
	5、配置事务的属性
			是在事务的通知tx:advice标签的内部
-->
<!--配置事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManagr">
	<property name="dataSource" ref="dataSource"></property>
</bean>
<!--配置事务的通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
	<!--配置事务的属性
		isolation:用于指定事务的隔离级别。
		no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚,没有默认值,表示任何事务都回滚。
		propagation:用于指定事务的传播行为,默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以用SUPPORTS。
		read-only:用于指定事务是否只读,只有查询方法才能设置为true,默认值是false,表示读写。
		rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他事务时,事务不回滚,没有默认值,表示任何事务都回滚。
		timeout:用于指定事务的超时时间,默认值是-1,表示永不超时,如果指定了数值,以秒为单位。
-->
    <tx:attributes>
    	<tx:method name="*" propagation="REQUIRED" read-only="false"/>
        <tx:method name="find" propagation="SUPPORTS" read-only="true"></tx:method>
    </tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
	<!--配置切入点表达式-->
    <aop:pointcut id="pt1" expression="excution(* com.mzi.service.impl.*.*(..))"></aop:pointcut>
    <!--建立切入点表达式和事务通知的对应关系-->
    <aop:advisor advice-ref="txAdvice" point-ref="pt1"></aop:advisor>
</aop:config>

Spring中基于注解的声明式事务控制配置

spring中基于注解的声明式事务配置步骤

1、开启事务管理器

2、开启Spring对事务注解的支持

在主配置类上加@EnableTransactionManagement或者在xml中配置:

<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3、在需要事务支持的地方使用@Transactional注解

@Transactional
public class AccountServiceImp implements IAccountService{
    
}