二十一、Spring框架三之AOP(1)

本文详细介绍了AOP的概念、使用场景和解决思路,通过静态代理和动态代理技术展示了AOP在实际问题中的应用。重点讨论了Spring中的AOP,包括基于XML配置的入门案例和详细配置,以及基于注解的AOP配置,如半注解和纯注解形式。此外,还阐述了AOP的五种通知类型,并提供了完整的实例代码,帮助读者深入理解和掌握Spring AOP。
摘要由CSDN通过智能技术生成

1 初识AOP

1.1 AOP概述

1.2 AOP使用场景

在这里插入图片描述
在这里插入图片描述

1.3 解决思路分析

在这里插入图片描述
在这里插入图片描述
静态代理图:
在这里插入图片描述
缺点:每一个service对应一个代理,代码多冗余
好处:为每一个service可以定制不同的功能
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

1.4 动态代理技术

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.5 案例介绍

1 基于接口的动态代理

/*
    基于JDK的动态代理( Proxy )
    作用: 用于创建AccountService 动态代理对象的 工厂类
 */
@Component
public class ProxyAccountServiceFactory {

    //指定被代理对象(AccountServiceImpl对象)
    @Autowired
    @Qualifier("accountServiceImpl")
    private AccountService accountService;
    @Autowired
    private TransactionManager transactionManager;

    //用于创建AccountService 动态代理对象
    @Bean("proxyAccountService")
    public AccountService createAccountServiceProxy(){
        //创建AccountService 的动态代理对象
        /**
         * 参数一: 被代理对象的类加载器
         * 参数二: 被代理对象 所实现的所有接口
         * 参数三: 指定原有方法 如何进行增强
         */
        AccountService proxyAccountService = (AccountService) Proxy.newProxyInstance(
                accountService.getClass().getClassLoader(),
                accountService.getClass().getInterfaces(),
                new InvocationHandler() {
                    /**
                     * invoke 方法, 就是对原有功能方法的增强
                     * @param proxy 就是创建出来的动态代理对象的引用
                     * @param method 被代理对象 所要执行的方法
                     * @param args 被代理对象 所要执行的方法 所接收的实际参数值
                     */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        System.out.println("被代理对象 = " + accountService);
                        System.out.println("被增强的方法 = " + method.getName());
                        System.out.println("被增强的方法 所接收的实际参数值 = " + Arrays.toString(args));
                        Object rtValue = null;
                        try {
                            //开启事务(增强)
                            transactionManager.begin();
                            //调用 被代理对象 的原有方法
                            rtValue = method.invoke(accountService, args);
                            //提交事务(增强)
                            transactionManager.commit();
                        } catch (Exception e) {
                            e.printStackTrace();
                            //回滚事务(增强)
                            transactionManager.rollback();
                        } finally {
                            //释放连接(增强)
                            transactionManager.close();
                        }
                        return rtValue;
                    }
                }
        );
        return proxyAccountService;
    }
}

2 基于子类的动态代理

/*
    基于CgLib的动态代理( 第三方jar包 )
    作用: 用于创建AccountService 动态代理对象的 工厂类
 */
@Component
public class CglibAccountServiceFactory {

    //指定被代理对象(AccountServiceImpl对象)
    @Autowired
    @Qualifier("accountServiceImpl")
    private AccountService accountService;
    @Autowired
    private TransactionManager transactionManager;

    //用于创建AccountService 动态代理对象
    @Bean("cgLibAccountService")
    public AccountService createAccountServiceProxy(){
        //创建AccountService 的动态代理对象
        AccountService cgLibAccountService = (AccountService) Enhancer.create(
                accountService.getClass(),
                new MethodInterceptor() {
                    /**
                     * intercept 方法,  就是对原有功能方法的增强
                     * @param proxy 就是创建出来的动态代理对象的引用
                     * @param method 被代理对象 所要执行的方法
                     * @param objects 被代理对象 所要执行的方法 所接收的实际参数值
                     * @param methodProxy 所要执行的方法的代理对象  method.invoke()
                     */
                    @Override
                    public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        System.out.println("被代理对象 = " + accountService);
                        System.out.println("被增强的方法 = " + method.getName());
                        System.out.println("被增强的方法 所接收的实际参数值 = " + Arrays.toString(objects));
                        Object rtValue = null;
                        try {
                            //开启事务(增强)
                            transactionManager.begin();
                            //调用 被代理对象 的原有方法
                            rtValue = method.invoke(accountService, objects);
                            //提交事务(增强)
                            transactionManager.commit();
                        } catch (Exception e) {
                            e.printStackTrace();
                            //回滚事务(增强)
                            transactionManager.rollback();
                        } finally {
                            //释放连接(增强)
                            transactionManager.close();
                        }
                        return rtValue;
                    }
                });

        return cgLibAccountService;
    }
}

在这里插入图片描述

2 AOP相关概念

2.1 AOP的基础知识

在这里插入图片描述

2.2 Spring中的AOP

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3 Spring中应用AOP

3.1 基于XML配置的入门案例

1 案例介绍
在业务层方法执行之前,输出记录日志的语句

2 导入坐标

<dependencies>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.1.9.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.aspectj</groupId>
        <artifactId>aspectjweaver</artifactId>
        <version>1.9.4</version>
    </dependency>
</dependencies>

3 编写基础代码

编写账户业务层接口
	模拟账户保存的save()方法 

编写对应实现类
	实现save方法
		(前置增强:日志打印)
	
编写日志切面类,实现提供实现类的增强方法
	utils.logUtil	
	打印日志printLog()方法

4 配置Spring的IoC,配置Spring的AOP:ApplicationContext.xml

配置Service对象
配置LogUtil切面类(增强功能)对象 
配置AOP,实现了对service中的方法进行增强LogUtil
<?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
       https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--配置service 对象-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>

    <!--配置LogUtil 切面类(增强功能)对象-->
    <bean id="logUtil" class="com.itheima.utils.LogUtil"></bean>

    <!--配置AOP, 实现了对service 中的方法 进行增强(LogUtil)-->
    <aop:config>
        <!--配置切面-->
        <aop:aspect id="logUtil" ref="logUtil">
            <!--配置切入点表达式-->
            <aop:before method="printLog" pointcut="execution(public void com.itheima.service.impl.AccountServiceImpl.save())"></aop:before>
        </aop:aspect>
    </aop:config>

</beans>

5 编写测试类测试

创建Spring容器对象
获取Service对象
调用方法

3.2 基于XML的AOP配置细节

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

3.3 五种通知类型

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
注意:后置通知和异常通知不会同时执行,后置通知表示成功执行后的,异常通知表示执行异常出现的。
在这里插入图片描述
在这里插入图片描述
3.3.6 五种通知类型的完整实例
1 导入坐标
2 编写基础代码:

	接口
		三个方法
		
	接口实现类
		三个方法的实现类:
		save:保存账户
		update(int i) 更新账户
		int delete 删除账户
		
	切面类:包含增强方法

切面类举例:

/*
日志的切面类(包含了 增强方法)
 */
public class LogUtil {

    //配置 前置增强: 执行实现 在切入点方法执行前执行
    public void beforePrintLog(JoinPoint joinPoint) throws Throwable {
        //获取切入点方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("前置增强beforePrintLog = " + Arrays.toString(args));
    }

    //配置 后置增强: 执行时机 在切入点方法执行完毕后, 执行; 若切入点方法执行过程中产生了异常, 后置增强不执行
    public void afterReturningPrintLog(JoinPoint joinPoint, Object rtValue) throws Throwable {
        System.out.println("后置增强afterReturningPrintLog = " + rtValue);
    }

    //配置 异常增强: 执行时机 在切入点方法执行过程中 抛出了异常,
    public void afterThrowingPrintLog(JoinPoint joinPoint, Exception e) throws Throwable {
        System.out.println("异常增强:afterThrowingPrintLog = " + e);
    }

    //配置 最终增强: 执行时机 在切入点方法执行完毕后, 执行(无论是否有异常, 最终增强都执行)
    public void afterPrintLog(JoinPoint joinPoint) throws Throwable {
        System.out.println("最终增强:afterPrintLog");
    }

    //配置 环绕通知: 包含了(前置增强, 后置增强, 异常增强, 最终增强)
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object rtValue = null;

        try {
            //配置 前置增强:
            System.out.println("前置增强");

            //获取方法执行时, 所需的实际参数
            Object[] args = joinPoint.getArgs();
            //原有的方法执行
            rtValue = joinPoint.proceed(args);
            //配置 后置增强:
            System.out.println("后置增强");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //配置 异常增强:
            System.out.println("异常增强");
        } finally {
            //配置 最终增强:
            System.out.println("最终增强");
        }

        return rtValue;
    }
}

3 Spring配置文件:ApplicationContext.xml

配置service 
配置切面类 
配置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 http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--配置AccountService-->
    <bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>

    <!--配置切面类(增强)-->
    <bean id="logUtil" class="com.itheima.utils.LogUtil"></bean>

    <!--配置AOP-->
    <aop:config>
        <!--配置 通用的 切入点表达式-->
        <aop:pointcut id="pc" expression="execution(* com.itheima.service..*.*(..))"></aop:pointcut>
        <!--配置切面(织入)-->
        <aop:aspect id="logUtil" ref="logUtil">
            <!--配置 前置增强: 执行时机 在切入点方法执行前执行-->
            <aop:before method="beforePrintLog" pointcut-ref="pc"></aop:before>
            <!--配置 后置增强: 执行时机 在切入点方法执行完毕后, 执行; 若切入点方法执行过程中产生了异常, 后置增强不执行-->
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pc" returning="rtValue"></aop:after-returning>
            <!--配置 异常增强: 执行时机 在切入点方法执行过程中 抛出了异常, 执行-->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pc" throwing="e"></aop:after-throwing>
            <!--配置 最终增强: 执行时机 在切入点方法执行完毕后, 执行(无论是否有异常, 最终增强都执行)-->
            <aop:after method="afterPrintLog" pointcut-ref="pc"></aop:after>

            <!--配置 环绕通知: 包含了(前置增强, 后置增强, 异常增强, 最终增强)-->
            <aop:around method="around" pointcut-ref="pc"></aop:around>
        </aop:aspect>
    </aop:config>
</beans>		

4 基于注解的AOP配置

4.1 半注解的形式

1 导入坐标
2 基础代码
Service层添加@Service注解

3 编写Spring配置文件
开启SpringIoC容器的注解扫描
开启SpringAOP的注解扫描

<?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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启SpringIOC容器的注解扫描-->
    <context:component-scan base-package="com.itheima"></context:component-scan>

    <!--开启SpringAOP的注解扫描-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

</beans>

4 切面类:

	@Component:放到IoC容器中
	设置切面类@Aspect :声明它是切面类,替换原xml中配置切面类的语句:
		<bean id="logUtil" class="com.itheima.utils.LogUtil"></bean>
	
	@Pointcut配置通用的切入点表达式
	@Before引用@Pointcut修饰的方法
	。。。
	注意:增强的顺序是:前置、service业务、最终和后置(异常),其中最终居然在后置前面,如果是
	事务的操作就会出错:先释放资源在提交事务错误
	所以如果涉及到事务的操作要使用环绕增强@Around:
		它的顺序:前置 service业务 后置 最终 
/*
日志的切面类(包含了 增强方法)
 */
@Component
//设置切面类
@Aspect
public class LogUtil {

    //配置通用的切入点表达式
    @Pointcut("execution(* com.itheima.service..*.*(..))")
    public void pointcut(){}


    //配置 前置增强: 执行实现 在切入点方法执行前执行
    //@Before("pointcut()")
    public void beforePrintLog(JoinPoint joinPoint) throws Throwable {
        //获取切入点方法的参数
        Object[] args = joinPoint.getArgs();
        System.out.println("前置增强beforePrintLog = " + Arrays.toString(args));
    }

    //配置 后置增强: 执行时机 在切入点方法执行完毕后, 执行; 若切入点方法执行过程中产生了异常, 后置增强不执行
    //@AfterReturning("pointcut()")
    public void afterReturningPrintLog(JoinPoint joinPoint) throws Throwable {
        System.out.println("后置增强afterReturningPrintLog = ");
    }

    //配置 异常增强: 执行时机 在切入点方法执行过程中 抛出了异常
    //@AfterThrowing("pointcut()")
    public void afterThrowingPrintLog(JoinPoint joinPoint) throws Throwable {
        System.out.println("异常增强:afterThrowingPrintLog = ");
    }

    //配置 最终增强: 执行时机 在切入点方法执行完毕后, 执行(无论是否有异常, 最终增强都执行)
    //@After("pointcut()")
    public void afterPrintLog(JoinPoint joinPoint) throws Throwable {
        System.out.println("最终增强:afterPrintLog");
    }

    //配置 环绕通知: 包含了(前置增强, 后置增强, 异常增强, 最终增强)
    @Around("pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        Object rtValue = null;

        try {
            //配置 前置增强:
            System.out.println("前置增强");

            //获取方法执行时, 所需的实际参数
            Object[] args = joinPoint.getArgs();
            //原有的方法执行
            rtValue = joinPoint.proceed(args);
            //配置 后置增强:
            System.out.println("后置增强");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
            //配置 异常增强:
            System.out.println("异常增强");
        } finally {
            //配置 最终增强:
            System.out.println("最终增强");
        }

        return rtValue;
    }
}

4.2 纯注解的形式

创建Spring配置文件对应的java类:类上声明@Configuration
1 开启SpringIOC的注解扫描
	@ComponentScan

2 开启SpringAOC的注解扫描 
	@EnableAspectJAutoProxy
/**
 * Spring的核心配置文件
 */
@Configuration
//开启SpringIOC的注解扫描
@ComponentScan({"com.itheima"})
//开启SPringAOP的注解扫描
@EnableAspectJAutoProxy
public class SpringConfig {
}
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {SpringConfig.class})
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void save() {
        accountService.save();
    }

    @Test
    public void update() {
        accountService.update(123);
    }

    @Test
    public void delete() {
        accountService.delete();
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值