Spring AOP(思想)以及AspectJ框架(重点)

文章目录

    • 一、动态代理
    • 二、AOP面向切面编程
    • 三、AspectJ框架
    • 四、代码演示

一、动态代理

1、动态代理(实现方式

(1)jdk动态代理,使用jdk中的Proxy,Method,InvocaitonHanderl创建代理对象。
jdk动态代理要求目标类必须实现接口

(2)cglib动态代理:第三方的工具库创建代理对象,原理是继承。
通过继承目标类,创建子类。子类就是代理对象。要求目标类不能是final的(类不能继承),方法也不能是final的(方法不能重写)

2、(1)当我们业务层使用实现接口的方式时,会默认采用 JDK 的动态代理形式;

(2)当我们业务层没有使用实现接口的方式时,会默认采用cglib 的动态代理(这个的执行效率会高一些)

(3)那我们可以既实现接口又想使用 cglib 的动态代理形式的吗?
答案是可以,在xml文件中添加以下配置并在pom.xml中引入依赖(强制使用 cglib 动态代理):

 <aop:config proxy-target-class="true"></aop:config>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

这里有一个报错,就是要理解以上一点,不然可能会报这样的错

3、动态代理的作用

(1)在目标类源代码不改变的情况下,增加功能。
(2)减少代码的重复
(3)专注业务逻辑代码
(4)解耦合,让你的业务功能和日志、事务等非业务功能分离。

二、AOP面向切面编程

1、AOP (Aspect Orient Programning) 面向切面编程

(1)基于动态代理的,可以使用jdk、cglib两种代理方式。
AOP就是动态代理的规范化,把动态代理的实现步骤,方式都定义好了,让开发人员用一种统一的方式,使用动态代理。

(2)Aspect:切面,给你的目标类增加的功能,就是切面。像用的日志,事务都是切面;
切面的特点:一般都是非业务方法,独立使用的;

Orient:面向(对着);
Programming:编程。

oop:面向对象编程

2、怎么理解面向切面编程?

(1)需要在分析项目功能时,找出切面
(2)合理的安排切面的执行时间(在目标方法前,还是目标方法后);
(3)合理的安排切面执行的位置(在哪个类,哪个方法增加增强功能)。

3、术语:
(1)Aspect:切面,表示增强的功能,就是一堆代码,完成某个一个功能。非业务功能;
常见的切面功能有日志、事务、统计信息、参数检查、权限验证;
(2) JoinPoint:连接点,连接业务方法和切面的位置。就某类中 的业务方法;
(3) Pointcut :切入点,指多个连接点方法的集合。多个方法;
(4)目标对象:给哪个类的方法增 加功能,这个类就是 目标对象;
(5)Advice:通知,通知表示切面功能执行的时间。

4、说一个切面有三个关键的要素:
(1)切面的功能代码,切面干什么?就是对原有功能的增强,实现解耦合
(2)切面的执行位置,使用Pointcut表示切面执行的位置
(3)切面的执行时间,使用Advice表示时间,在目标方法之前,还是目标方法之后。

5、AOP的实现
aop是一个规范,是动态代理的一个规范化也是一个标准

aop的技术实现框架:

(1)spring: spring在内部实现了aop规范,能做aop的工作。
spring主要在事务处理时使用aop,我们项目开发中很少使用spring的aop实现。因为spring的aop比较笨重。

(2)AspectJ:一个开源的专门做aop的框架。spring框架中集成了AspectJ框架,通过spring就能使用AspectJ的功能。
Spring AOP(思想)以及AspectJ框架(重点)_第1张图片

三、AspectJ框架

1、AspectJ框架实现aop有两种方式:

(1)使用xml的配置文件:配置全局事务
(2)使用注解(推荐使用,开发中常用),我们在项目中要做aop功能,一般都使用注解,AspectJ有5个注解。

2、AspectJ框架的便用
切面的执行时间,这个执行时间在规范中叫做Advice(通知,增强)
在AspectJ框架中使用注解表示的(也可以使用xml配置文件中的标签)

AspectJ有5个注解(演示示例中都有的)
(1) @Before:前置通知(增强方法之前执行)
(2) @AfterReturning:后置通知(返回通知)(处理返回值,在方法及返回值之后执行)
(3) @Around:环绕通知(最先执行,功能最强大,通常用于事务,有固定的参数,相当于动态代理中的Method方法)
(4) @AfterThrowing:异常通知(只有在代码出现异常的时候,才会执行)
(5) @After:最终通知(相当于finally)
(6) @Pointcut:定义和管理切入点,可以实现复用(相当于是给对应的切入点起别名)

执行顺序:环绕通知调用方法前的代码 > 前置通知 > 被增强方法 > 后置通知(返回通知) > 最终通知 > 环绕通知调用方法之后的代码,这个不刻意去记忆(你经常用就会记住,但是我用了很久才算清晰的记住了)

下面我就来说一下:使用AspectJ框架实现aop

使用aop:目的是给已经存在的一些类和方法,增加额外的功能。前提是不改变原来的类的代码

3、使用aspectj实现aop的基本步骤:
(1)新建maven项目
(2)加入依赖
① spring依赖
② aspectj依赖

③junit单元测试

                   <!--JUint单元测试(非必须)-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
                    <!--aspectj依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>
                    <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.8.RELEASE</version>
        </dependency>

(3)创建目标类:接口和他的实现类。要做的是给类中的方法增加功能

(4)创建切面类:普通类(主要步骤)
①在类的上面加入@Aspect注解,标识这是一个代理类。
②在类中定义方法,方法就是切面要执行的功能代码,在增强方法的上面加入aspectj中的通知注解, 例如@Before前置通知,需要指定切入点表达式execution( )

(5)创建spring的配置文件:声明对象,把对象交给容器统一管理声明对象你可以使用注解或者xml配置文件bean标签(主要步骤)
①声明目标对象
②声明切面类对象
③声明aspectj框架中的自动代理生成器标签。自动代理生成器:用来完成代理对象的自动创建功能的。

Spring AOP(思想)以及AspectJ框架(重点)_第2张图片

(6)创建测试类,从spring容器中获取目标对象(实际就是代理对象)。通过代理执行方法, 实现aop的功能增强。

4、其中还有切入点表达式:就是标识是对哪些类的哪些方法进行增强

格式:execution(访问权限 、方法返回值、方法声明(参数)、异常类型)
Spring AOP(思想)以及AspectJ框架(重点)_第3张图片
Spring AOP(思想)以及AspectJ框架(重点)_第4张图片
Spring AOP(思想)以及AspectJ框架(重点)_第5张图片

四、代码演示

1、目录结构:
Spring AOP(思想)以及AspectJ框架(重点)_第6张图片
2、applicationContext.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:util="http://www.springframework.org/schema/util"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/util  http://www.springframework.org/schema/util/spring-util.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
                           http://www.springframework.org/schema/tx  http://www.springframework.org/schema/tx/spring-tx.xsd">

    <!--     开启组件扫描(全包扫描的方式)-->
   <context:component-scan base-package="com.AspectJ"></context:component-scan>

    <!--开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <aop:config proxy-target-class="true"></aop:config>
    
</beans>

3、UserService接口

package com.AspectJ.service;

public interface UserService {
     
    void test();
}

4、UserServiceImpl实现类

package com.AspectJ.service;

import org.springframework.stereotype.Component;

@Component("userServiceImpl")
public class UserServiceImpl implements UserService {
     

    public void test() {
     
        System.out.println("user...");
    }
}

5、UserProxy代理类

package com.AspectJ.Proxy;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
//增强的类 (1、2、3、4、5表示通知执行的先后顺序)

@Component//实现类的对象
@Aspect   //生成代理对象
public class UserProxy {
     

    //相同切入点的抽取(之后可以直接调用这个方法在相应的表达式中,例如以下的后置通知和环绕通知)
    @Pointcut(value = "execution(* com.AspectJ.service.UserServiceImpl.test(..))")
    public void pointDemo() {
     
    //这里无需任何代码
    }
    //2、前置通知:在User类里的方法执行之前先执行
    @Before(value = "execution(* com.AspectJ.service.UserServiceImpl.test(..))")//切入点表达式
    public void before() {
     
        System.out.println("before...");
    }
    //4、最终通知:在方法之后执行  (相当于finally)
    @After(value = "execution(* com.AspectJ.service.UserServiceImpl.test(..))")
    public void after() {
     
        System.out.println("after...");
    }
    //3、后置通知(返回通知):在方法之后并且在返回值之后执行,处理返回值
    @AfterReturning(value = "pointDemo()")
    public void afterReturning() {
     
        System.out.println("afterReturning...");
        //int a = 10 / 0;
        //System.out.println(a);
    }
    //5、异常通知(只有在代码出现异常的时候,才会执行)
    @AfterThrowing(value = "execution(* com.AspectJ.service.UserServiceImpl.test(..))")
    public void afterThrowing() {
     
        System.out.println("afterThrowing...");
    }
    //1、环绕通知(最先执行,功能最强大,通常用于事务,有固定的参数,相当于动态代理中的Method方法)
    @Around(value = "pointDemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
     
        System.out.println("环绕之前执行");
        //被增强的方法执行
        proceedingJoinPoint.proceed();
        //一以下的代码最后执行
        System.out.println("环绕之后执行");
    }
}

6、UserServiceTest测试类

import com.AspectJ.service.UserServiceImpl;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class UserServiceTest {
     
    @Test
    public void testUserService() {
     
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        UserServiceImpl userServiceImpl = (UserServiceImpl) context.getBean("userServiceImpl");

        userServiceImpl.test();
    }
}

7、测试结果
Spring AOP(思想)以及AspectJ框架(重点)_第7张图片
有用点个关注,手留余香!

你可能感兴趣的:(Spring)