SSM-Spring-4-AOP

本文详细介绍了Spring AOP的概念,包括面向切面编程的含义、优势,以及AOP的两种底层实现:JDK动态代理和cglib动态代理。此外,还探讨了AOP的相关术语,如切点、通知等,并提供了AOP快速入门的步骤,包括切点表达式的写法和不同类型的增强。最后,文章讲解了基于注解的AOP开发,包括如何通过注解实现织入和抽取切点表达式。
摘要由CSDN通过智能技术生成

1 AOP概念

AOP为Aspect Oriented Programming的缩写,意为面向切面编程,是通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术,AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的重要内容,利用AOP可以对业务逻辑的各个部分进行隔离,从而松耦合,提高程序可用性,提高开发效率

作用:在程序运行期间,不修改源码地情况下对方法增强

优势:减少重复代码,提高开发效率,并且便于维护

2 AOP的底层实现

AOP的底层是通过Spring提供的动态代理技术实现的,在运行期间,Spring通过动态代理技术动态的生成代理对象,代理对象方法执行时进行增强功能的介入,在去调用目标对象的方法,从而完成功能的增强

常用动态代理技术:

1,JDK代理: 基于接口的动态代理技术

2,cglib代理: 基于父类的动态代理技术

在这里插入图片描述

2.1 基于JDK的动态代理

public class ProxyTest {

    public static void main(String[] args) {

        // 创建目标对象
        Target target = new Target();

        // 创建增强对象
        Advice advice = new Advice();

        // 获取动态代理对象
        TargetInterface proxy = (TargetInterface) Proxy.newProxyInstance(
            target.getClass().getClassLoader(),
            target.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                    // 前置增强
                    advice.before();

                    Object object = method.invoke(target, args);

                    // 后置增强
                    advice.after();

                    return object;
                }
            });

        proxy.save();
        
    }

}

2.2 基于cglib的动态代理

public class ProxyTest {

    public static void main(String[] args) {

        Target target = new Target();

        Advice advice = new Advice();

        // 1,创建增强器
        Enhancer enhancer = new Enhancer();

        // 2,设置父类
        enhancer.setSuperclass(Target.class);

        // 3,设置hook
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object proxy, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                // 前置增强
                advice.before();

                Object object = method.invoke(target, args);

                // 后置增强
                advice.after();

                return object;
            }
        });

        // 4,生成动态代理对象
        Target proxy = (Target) enhancer.create();

        proxy.save();

    }

}

3 AOP相关概念

3.1 AOP相关术语

Target(目标对象): 被增强的对象

Proxy(代理): 一个类被AOP增强后,就产生一个代理对象

JoinPoint(连接点): 可以被增强的方法

PointCut(切入点): 真正被增强的方法

Advice(通知/增强): 对切入点增强的方法

Aspect(切面): 切入点+增强

Weaving(织入): 将切点和增强结合的过程

3.2 AOP开发有关事项

1,需要编写的内容:

1,核心业务,即目标方法

2,编写通知

3,在配置文件中,配置切入点和增强

2,AOP技术的实现:
Spring框架会监控切入点方法的执行,一旦监控到切入点方法被运行,使用代理机制,动态创建目标对象的代理对象,根据增强类别,在代理对象的对应位置,将增强的功能织入,完成完整的代码逻辑运行

3,AOP底层使用的代理方式:
在Spring框架中,框架会根据目标类是否实现了接口来决定采用哪种动态代理方式

4 AOP快速入门

AOP开发的步骤:

1,导入AOP相关坐标

2,创建目标接口和目标类

3,创建增强类,编写增强方法

4,将目标类和增强类配置成Bean

5,在applicationContext.xml中配置织入关系

1,导入AOP相关坐标:

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

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

2,创建目标接口和目标类:
在这里插入图片描述

3,创建增强类,编写增强方法:
在这里插入图片描述

4,将目标类和增强类配置成Bean:
在这里插入图片描述

5,在applicationContext.xml中配置织入关系:
即告诉Spring哪些方法需要进行哪些增强,需要先在applicationCOntext.xml中引入aop命名空间

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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/mvc http://www.springframework.org/schema/mvc/spring-mvc.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">
            

</beans>

织入的写法:

    <!-- 织入  -->
    <aop:config>
        <!-- 声明切面对象 -->
        <aop:aspect ref="myAspect">
            <!--  将myAspect的before()作为target对象中save()的前置增强 -->
            <aop:before method="before" pointcut="execution(public void com.coisini.aop.Target.save())"></aop:before>
            <!--  将myAspect的before()作为target对象中save()的最终增强 -->
            <aop:after method="after" pointcut="execution(public void com.coisini.aop.Target.save())"></aop:after>
        </aop:aspect>
    </aop:config>

测试:
在这里插入图片描述

4.1 切点表达式的写法

表达式语法:

execution(访问修饰符 返回值类型 包名.类名.方法名(参数))

1,访问修饰符可以不写

2,返回值类型,包名,类名,方法名可以使用*代表任意

3,包名与类名之间一个点,代表当前包下的类
两个点,表示当前包及子包下的类

4,参数列表可以使用两个点表示任意参数

看如下几个切点表达式:

1,execution(public void com.coisini.aop.Target.save())
繁琐且不常用,智能增强一个方法

2,execution(void com.coisini.aop.Target.*(..))
对com.coisini.aop.Target中所有返回值为void的方法进行增强

3,execution(* com.coisini.aop.*.*(..))
对com.coisini.aop包下的所有方法进行增强,最常用

4,execution(* com.coisini.aop..*.*(..))
对com.coisini.aop包及其所有子包下的所有方法进行增强

5,execution(* *..*.*(..))
对所有方法都进行增强

4.2 增强(通知)的种类

增强的配置语法:

<aop:通知类型 method="切面类中方法名" pointcut="切点表达式"></aop:通知类型>

增强的种类:
在这里插入图片描述
编写各种增强:
在这里插入图片描述

在applicationContext.xml中织入各种增强:

    <!-- 织入  -->
    <aop:config>
        <!-- 声明切面对象 -->
        <aop:aspect ref="myAspect">
            <!-- 将myAspect的around()作为com.coisini.aop包下所有方法的环绕增强 -->
            <aop:around method="around" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:around>

            <!--  将myAspect的before()作为为com.coisini.aop包下所有方法的前置增强 -->
            <aop:before method="before" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:before>

            <!--  将myAspect的afterReturning()作为com.coisini.aop包下所有方法的后置增强 -->
            <aop:after-returning method="afterReturning" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:after-returning>

            <!--  将myAspect的before()作为com.coisini.aop包下所有方法的最终增强 -->
            <aop:after method="after" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:after>
        </aop:aspect>
    </aop:config>

测试:
在这里插入图片描述

4.3 切点表达式的抽取

    <!-- 织入  -->
    <aop:config>
        <!-- 声明切面对象 -->
        <aop:aspect ref="myAspect">
            <!-- 将myAspect的around()作为com.coisini.aop包下所有方法的环绕增强 -->
            <aop:around method="around" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:around>

            <!--  将myAspect的before()作为target对象中save()的前置增强 -->
            <aop:before method="before" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:before>

            <!--  将myAspect的afterReturning()作为com.coisini.aop包下所有方法的后置增强 -->
            <aop:after-returning method="afterReturning" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:after-returning>

            <!--  将myAspect的before()作为com.coisini.aop包下所有方法的最终增强 -->
            <aop:after method="after" pointcut="execution(* com.coisini.aop.*.*(..))"></aop:after>
        </aop:aspect>
    </aop:config>

在织入时,各种增强的切点表达式可能相同,对于相同的内容就可以抽取出来,在增强中使用pointcut-ref属性代替pointcut属性来引用抽取后的切点表达式:

   <!-- 织入  -->
    <aop:config>
        <!-- 声明切面对象 -->
        <aop:aspect ref="myAspect">
            <!--  抽取切点表达式 -->
            <aop:pointcut id="myPointcut" expression="execution(* com.coisini.aop.*.*(..))"/>
            
            <!-- 将myAspect的around()作为com.coisini.aop包下所有方法的环绕增强 -->
            <aop:around method="around" pointcut-ref="myPointcut"></aop:around>

            <!--  将myAspect的before()作为target对象中save()的前置增强 -->
            <aop:before method="before" pointcut-ref="myPointcut"></aop:before>

            <!--  将myAspect的afterReturning()作为com.coisini.aop包下所有方法的后置增强 -->
            <aop:after-returning method="afterReturning" pointcut-ref="myPointcut"></aop:after-returning>

            <!--  将myAspect的before()作为com.coisini.aop包下所有方法的最终增强 -->
            <aop:after method="after" pointcut-ref="myPointcut"></aop:after>
        </aop:aspect>
    </aop:config>

5 基于注解的AOP开发

5.1 快速入门

基于注解的AOP开发步骤:

1,创建目标接口和目标类

2,创建增强类并编写增强方法

3,将目标类和增强类配置成Bean

4,在增强类中使用注解配置织入关系

5,在applicationContext.xml中开启组件扫描和AOP的自动代理

1,创建目标接口和目标类:
在这里插入图片描述
2,创建增强类并编写增强方法:
在这里插入图片描述
3,将目标类和增强类配置成Bean:
在这里插入图片描述
4,在增强类中使用注解配置织入关系:

@Component("myAspect")
// 标注当前类是一个切面类
@Aspect
public class MyAspect {

    @Before(value = "execution(* com.coisini.aop.*.*(..))")
    public void before() {
        System.out.println("before...");
    }

    @After("execution(* com.coisini.aop.*.*(..))")
    public void after() {
        System.out.println("after...");
    }

    // 指明参数ProceedingJoinPoint 切点
    @Around("execution(* com.coisini.aop.*.*(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around-before...");

        Object object = joinPoint.proceed();

        System.out.println("around-after...");

        return object;
    }

    @AfterReturning("execution(* com.coisini.aop.*.*(..))")
    public void afterReturning() {
        System.out.println("after-returning...");
    }

}

5,在applicationContext.xml中开启组件扫描和AOP的自动代理:

	 <!-- 开启组件扫描  -->
    <context:component-scan base-package="com.coisini.aop"></context:component-scan>


    <!-- 配置AOP自动代理   -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

5.2 注解增强种类&抽取切点表达式

注解增强的种类:
在这里插入图片描述
切点表达式的抽取,抽取方法是在切面内定义方法,在该方法上使用@Pointcut注解定义切点表达式,然后在增强注解中进行引用即可:

@Component("myAspect")
// 标注当前类是一个切面类
@Aspect
public class MyAspect {

    // 定义切点表达式
    @Pointcut(value = "execution(* com.coisini.aop.*.*(..))")
    public void pointcut() {}


    @Before("MyAspect.pointcut()")
    public void before() {
        System.out.println("before...");
    }

    @After("MyAspect.pointcut()")
    public void after() {
        System.out.println("after...");
    }

    // 指明参数ProceedingJoinPoint 切点
    @Around("MyAspect.pointcut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around-before...");

        Object object = joinPoint.proceed();

        System.out.println("around-after...");

        return object;
    }

    @AfterReturning("MyAspect.pointcut()")
    public void afterReturning() {
        System.out.println("after-returning...");
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值