3.spring-aop

Spring AOP

1. AOP简介:

AOP全称为Aspect Oriented Programming,表示面向切面编程

在这里插入图片描述

由此可以得出,切面是一种将那些与业务无关,但业务模块都需要使用的功能封装起来的技术。这样便于减少系统的重复代码,降低模块之间的耦合度。

2. AOP基本术语

连接点(Joinpoint)

连接点就是被拦截到的程序执行点,因为Spring只支持方法类型的连接点,所以在Spring中连接点就是被拦截到的方法。连接点由两个信息确定:

  • 方法( 表示程序执行点,即在哪个目标方法)
  • 相对点(表示方位,即目标方法的什么位置,比如调用前,后等)

切入点(Pointcut)

切入点是对连接点进行拦截的条件定义,切入点表达式如何和连接点匹配是AOP的核心,Spring缺省使用AspectJ切入点语法。 一般认为,所有的方法都可以认为是连接点,但是我们并不希望在所有的方法上都添加通知,而切入点的作用就是提供一组规则来匹配连接点,给满足规则的连接点添加通知。

通知、增强 (Advice)

可以为切入点添加额外功能,分为:前置通知、后置通知、异常通知、环绕通知等。

目标对象 (Target)

目标对象指将要被增强的对象,即包含主业务逻辑的类对象。或者说是被一个或者多个切面所通知的对象。

织入 (Weaving)

织入是将切面和业务逻辑对象连接起来, 并创建通知代理的过程。织入可以在编译时,类加载时和运行时完成。在编译时进行织入就是静态代理,而在运行时进行织入则是动态代理

代理 (Proxy)

被AOP织入通知后,产生的结果类。

切面 (Aspect)

切面是一个横切关注点的模块化,一个切面能够包含同一个类型的不同增强方法,比如说事务处理和日志处理可以理解为两个切面。切面由切入点和通知组成,它既包含了横切逻辑的定义,也包括了切入点的定义。 Spring AOP就是负责实施切面的框架,它将切面所定义的横切逻辑织入到切面所指定的连接点中。

3. AOP应用

AOP应用场景有许多,最典型的应用场景就是日志和事务。

依赖:spring aop是依赖于spring ioc的,aop的通知类都需要放在spring的ioc容器中。

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.11</version>
</dependency>

3.1 创建业务层

aop是针对业务层做的增强处理。

public interface UserService {
    int delete(String name);
}

public class UserServiceImpl implements UserService {
    @Override
    public int delete(String name) {
        System.out.println("这句话是执行删除业务");
//        int i=1/0;
        return 0;
    }
}

3.2. 配置业务层

AOP 功能的实现是基于 IOC 的,因此,业务层对象应该纳入 IOC 容器来管理。

<?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
">

    <!--业务层对象放入spring的ioc容器中-->
    <bean id="userService" class="com.dream.spring.aop.service.impl.UserServiceImpl"></bean>
    
</beans>

3.3 创建通知类

aop中的通知分为五种:前置通知,后置通知,异常抛出通知,环绕通知,最终通知。

这些通知的执行顺序如下:

try {
  	//前置通知
    
    //---业务执行--------
    
    //后置通知
} catch (Throwable t){
    //异常抛出通知
} finally {
    //最终通知
}

前置通知类(实现MethodBeforeAdvice接口):

在业务层方法执行之前执行。

import org.springframework.aop.MethodBeforeAdvice;

public class BeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        //获取方法名
        String methodName = method.getName();
        //获取类名
        String className = method.getDeclaringClass().getName();
        System.out.println("这是前置通知:"+methodName+"----"+className+"参数:"+ Arrays.toString(args));
    }
}

后置通知类(实现AfterReturningAdvice接口):

在业务层方法执行结束后执行

import org.springframework.aop.AfterReturningAdvice;

public class AfterAdvice implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        //获取方法名
        String methodName = method.getName();
        //获取类名
        String className = method.getDeclaringClass().getName();
        System.out.println("这是后置通知:"+methodName+"----"+className+"参数:"+ Arrays.toString(args));
    }
}

异常抛出通知类(实现ThrowsAdvice接口):

在业务层执行方法抛出异常时执行。

import org.springframework.aop.ThrowsAdvice;

public class ExceptionAdvice implements ThrowsAdvice {
    /**
     * ThrowsAdvice接口没有实现任何的方法,但是需要自己使用特定的方法才能生效
     */
    public void afterThrowing(Method method, Object[] args, Object target, Exception ex){
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();
        System.out.println("执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + ex.getMessage());
    }
}

最终通知类(实现AfterAdvice接口)

在业务层方法执行最终才执行,无论是否抛出异常。

注意:AfterAdvice是spring aop中的接口。

import org.springframework.aop.AfterAdvice;

public class FinallyAdvice implements AfterAdvice {
    @Override
    protected void finalize() throws Throwable {
        System.out.println("最终通知");
    }
}

环绕通知类(实现MethodInterceptor接口):

包含前置、后置和异常通知和最终通知。

public class AroundAdvice implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Method method = invocation.getMethod(); //获取被拦截的方法对象
        Object[] args = invocation.getArguments();//获取方法的参数
        Object target = invocation.getThis(); //获取代理对象
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();
        
        try {
            System.out.println("前置:准备执行方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args));
            //此处执行业务层的方法
            Object returnValue = method.invoke(target, args);
            
            System.out.println("后置:执行完方法:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",得到返回值:" + returnValue);
            //返回执行结果
            return returnValue;
        } catch (Throwable t){
            System.out.println("异常抛出:执行方法时:" + className + "." + methodName + ",参数:" + Arrays.toString(args) + ",发生了异常:" + t.getMessage());
            throw t;
        } finally {
            System.out.println("最终通知");
        }
    }
}

3.4 配置通知

这些通知类创建完成,还需要在xml配置文件中配置相关信息才能使用(装入ioc容器)

<?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="userService" class="com.dream.spring.aop.service.impl.UserServiceImpl"></bean>

    <!--这是配置 前置通知 对象-->
    <bean id="before" class="com.dream.spring.aop.advice.BeforeAdvice"/>
    <!--这是配置 后置通知 对象-->
    <bean id="after" class="com.dream.spring.aop.advice.AfterAdvice"/>
    <!--这是配置 异常通知 对象-->
    <bean id="exception" class="com.dream.spring.aop.advice.ExceptionAdvice"/>
    <!--这是配置 最终通知 对象-->
    <bean id="finally" class="com.dream.spring.aop.advice.FinallyAdvice"/>
	<!--这是配置 环绕通知 对象-->
    <bean id="around" class="com.dream.spring.aop.interceptor.AroundAdvice"/>
    <aop:config>
        <!--配置切入点-->
        <!--
			pointcut表示切点,也就是通知会在哪些位置触发
            expression表示切点表达式,切点表达式必须是execution(), execution()方法中的参数必须配置到方法上
            第一个 * 表示任意访问修饰符
            com.dream.spring.aop.service.. 最后的两个..表示service包下面的所有子包中的类
            *(..) 表示任意方法, 如果()中没有..,则表示不带参数的方法;有,就表示带任意参数

			execution(* com.dream.spring.aop.service..*(..))
            表示service包下的所有子包的所有类的所有方法,都作为切入点
		-->
        <aop:pointcut id="pc" expression="execution(* com.dream.spring.aop.service..*(..))"/>
        <!--这里是配置前置通知器-->
        <!--该通知器的bean id引用、切入点引用、-->
        <aop:advisor advice-ref="before" pointcut-ref="pc"/>

        <!--这里是配置后置通知器-->
        <!--该通知器的bean id引用、切入点引用、-->
        <aop:advisor advice-ref="after" pointcut-ref="pc"/>

        <!--这里是配置异常通知器-->
        <!--该通知器的bean id引用、切入点引用、-->
        <aop:advisor advice-ref="exception" pointcut-ref="pc"/>
        
        <!--这里是配置最终通知器-->
        <aop:advisor advice-ref="finally" pointcut-ref="pc"/>
        
        <!--这里是配置异常通知器-->
        <aop:advisor advice-ref="around" pointcut-ref="pc"/>
        
    </aop:config>
</beans>

配置完成在调用service层方法时,就能够触发这些通知器。

3.5 测试

@Test
public void userDeleteTest(){
    //加载applicationContext.xml配置文件
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
    UserService userService = applicationContext.getBean("userService", UserService.class);

    userService.delete("dengshuo");
}

下一章介绍 aop的框架 AspectJ和java中的代理模式

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值