返回顶部
首页 > 资讯 > 后端开发 > Python >Spring超详细讲解AOP面向切面
  • 616
分享到

Spring超详细讲解AOP面向切面

Spring面向切面SpringAOP面向切面 2022-11-13 18:11:29 616人浏览 薄情痞子

Python 官方文档:入门教程 => 点击学习

摘要

目录简介aop底层原理代理概念jdk动态代理实现spring中的AOP相关术语AspectJ实现AOP不同通知类型实现相同的切入点抽取增强类优先级完全使用注解开发说明:基于atgui

说明:基于atguigu学习笔记

简介

AOP(Aspect Oriented Programming)是一种面向切面的编程思想。不同于面向对象里的继承思想,当需要为多个不具有继承关系的对象引人同一个公共行为时,也就是把程序横向看,寻找切面,插入公共行为。

AOP目的是为了些把影响了多个类的公共行为抽取到一个可重用模块里,不通过修改源代码方式,在主干功能里面添加新功能,降低模块间的耦合度,增强代码的可操作性和可维护性。

例如,每次用户请求我们的服务接口,都要进行权限认证,看看是否登录,就可以在不改变原来接口代码的情况下,假如认证这个新功能。

Spring AOP底层使用了代理模式。下面具体了解一下。

AOP底层原理

代理概念

所谓代理,也就是让我们的代理对象持有原对象,在执行原对象目标方法的前后可以执行额外的增强代码。

代理对象需要是原对象接口的实现或原对象的子类,这样就可以在对象引用处直接替换原对象。

代理方式分静态代理和动态代理,区别在于代理对象生成方式不同

静态代理:在编译期增强,生成可见的代理class,使用代理类替换原有类进行调用。

动态代理:在运行期增强,内存中动态生成代理类,使用反射动态调用原对象方法。

在spring中使用的是JDK、CGLIB动态代理对象。

JDK动态代理:必须基于接口,即生成的代理对象是对原对象接口的实现,相当于替换了实现类,面向对象中接口可以替换实现类。

CGLIB动态代:理基于继承,即生成的代理对象是原对象的子类,面向对象中子类可以替换父类。

JDK动态代理实现

使用 JDK 动态代理,使用反射包里 java.lang.refelft.Proxy 类的 newProxyInstance 方法创建代理对象。

源码如下

@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h) {
    Objects.requireNonNull(h);
    final Class<?> caller = System.getSecurityManager() == null
                                ? null
                                : Reflection.getCallerClass();
    
    Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
    return newProxyInstance(caller, cons, h);
}

方法有三个参数:

第一参数,类加载器

第二参数,增强方法所在的类,这个类实现的接口,支持多个接口

第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分

下面以JDK动态代理为例,具体步骤。

1.创建接口,定义方法

public interface UserDao {
	public int add(int a,int b);
	public String update(String id);
}

2.创建接口实现类,实现方法

public class UserDaoImpl implements UserDao {
	@Override
	public int add(int a, int b) {
		return a+b;
	}
	@Override
	public String update(String id) {
		return id;
	} 
}

3.使用 Proxy 类创建接口代理对象

public class JDKProxy {
	public static void main(String[] args) {
		//创建接口实现类代理对象
		Class[] interfaces = {UserDao.class};
		// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
		// @Override
		// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// return null;
		// }
		// });
		UserDaoImpl userDao = new UserDaoImpl();
		UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));
		int result = dao.add(1, 2);
		System.out.println("result:"+result);
		} 
	}
	//创建代理对象代码
	class UserDaoProxy implements InvocationHandler {
		//1 把创建的是谁的代理对象,把谁传递过来
		//有参数构造传递
		private Object obj;
		public UserDaoProxy(Object obj) {
		this.obj = obj;
		}
		//增强的逻辑
		@Override
		public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		//方法之前
		System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));
		//被增强的方法执行
		Object res = method.invoke(obj, args);
		//方法之后
		System.out.println("方法之后执行...."+obj);
		return res;
	} 
}

Spring中的AOP

相关术语

1.连接点(Join point): 类里面可以被增强的方法。

2.切入点:真正被增强的方法。

3.通知:实际增强处理的逻辑。

AOP框架汇总通知分为以下几种:

  • 前置通知@Before
  • 后置通知@AfterReturning
  • 环绕通知@Around
  • 异常通知@AfterThrowing
  • 最终通知@After

4.切面:把通知应用到切入点的过程,是一个动作。

AspectJ

AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作。

基于 AspectJ 实现 AOP 操作可以有两种方式:基于xml配置文件、基于注解。

要使用AspectJ,首先要引入相关依赖:

	 <dependency>
         <groupId>org.aspectj</groupId>
          <artifactId>aspectjweaver</artifactId>
      </dependency>
      <dependency>
          <groupId>org.springframework</groupId>
          <artifactId>spring-aop</artifactId>
      </dependency>

使用AspectJ时,会寻找切入点,这时候会用到切入点表示,为了知道对哪个类里面的哪个方法进行增强。

语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )

举例 1:对 com.example.dao.BookDao 类里面的 add 进行增强
execution(* com.example.dao.BookDao.add(..))

举例 2:对 com.example.dao.BookDao 类里面的所有的方法进行增强
execution(* com.example.dao.BookDao.* (..))

举例 3:对 com.example.dao 包里面所有类,类里面所有方法进行增强
execution(* com.example.dao.*.* (..))

实现AOP

1.创建项目,引入依赖

依赖如下:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="Http://Maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>org.example</groupId>
    <artifactId>spring-demo02</artifactId>
    <version>1.0-SNAPSHOT</version>
    <properties>
        <maven.compiler.source>11</maven.compiler.source>
        <maven.compiler.target>11</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
    </dependencies>
</project>

2.创建类

创建一个自己的类,写一个要增强的方法,并使用注解管理bean

package com.example;
import org.springframework.stereotype.Component;
@Component
public class User {
    public void add () {
        System.out.println("user add method...");
    }
}

3.创建代理增强类

创建增强类,使用@Aspect注解。

在增强类里面,创建方法,让不同方法代表不同通知类型,此例创建前置通知使用@Before

package com.example;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
    @Before(value = "execution(* com.example.User.add())")
    public void before () {
        System.out.println("proxy before...");
    }
}

4.xml配置

开启注解扫描和Aspect 生成代理对象

<?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:context="http://www.springframework.org/schema/context"
       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/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">
    <!-- 开启注解扫描 -->
    <context:component-scan base-package="com.example"></context:component-scan>
    <!-- 开启 Aspect 生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

5.测试

package com.example;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AopTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext ap = new ClassPathXmlApplicationContext("bean1.xml");
        User user = ap.getBean("user", User.class);
        user.add();
    }
}

结果先输出proxy before…,再输出user add method…。说明我们的前置通知确实再被增强方法之前执行成功。

不同通知类型实现

下面把五种通知都实现看一下顺序,修改我们的代理类如下:

package com.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
    
    @Before(value = "execution(* com.example.User.add())")
    public void before () {
        System.out.println("proxy before...");
    }
    
    @AfterReturning(value = "execution(* com.example.User.add())")
    public void afterReturning() {
        System.out.println("proxy afterReturning...");
    }
    
    @After(value = "execution(* com.example.User.add())")
    public void after() {
        System.out.println("proxy after...");
    }
    
    @AfterThrowing(value = "execution(* com.example.User.add())")
    public void afterThrowing() {
        System.out.println("proxy afterThrowing...");
    }
    
    @Around(value = "execution(* com.example.User.add())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 环绕之前
        System.out.println("proxy around before...");
        proceedingJoinPoint.proceed();
        // 环绕之后
        System.out.println("proxy around after...");
    }
}

执行结果如下:

proxy around before...
proxy before...
user add method...
proxy around after...
proxy after...
proxy afterReturning...

相同的切入点抽取

上面代码可以看到我们的通知value是相同的,这时候可以抽取出来公用,改写代理类如下代码如下:

package com.example;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
@Component
@Aspect
public class UserProxy {
    @Pointcut(value = "execution(* com.example.User.add())")
    public void pointDemo() {}
    
    @Before(value = "pointDemo()")
    public void before () {
        System.out.println("proxy before...");
    }
    
    @AfterReturning(value = "pointDemo()")
    public void afterReturning() {
        System.out.println("proxy afterReturning...");
    }
    
    @After(value = "pointDemo()")
    public void after() {
        System.out.println("proxy after...");
    }
    
    @AfterThrowing(value = "pointDemo()")
    public void afterThrowing() {
        System.out.println("proxy afterThrowing...");
    }
    
    @Around(value = "pointDemo()")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        // 环绕之前
        System.out.println("proxy around before...");
        proceedingJoinPoint.proceed();
        // 环绕之后
        System.out.println("proxy around after...");
    }
}

增强类优先级

有多个增强类多同一个方法进行增强,设置增强类优先级。

在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高。

@Component
@Aspect
@Order(1)
public class UserProxy

完全使用注解开发

创建配置类,不需要创建 xml 配置文件。

package com.example;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
@Configuration
@ComponentScan(basePackages = {"com.example"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class AopConfig {
}

测试类:

package com.example;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class AopTest {
    public static void main(String[] args) {
        ApplicationContext ap = new AnnotationConfigApplicationContext(AopConfig.class);
        User user = ap.getBean(User.class);
        user.add();
    }
}

到此这篇关于Spring超详细讲解AOP面向切面的文章就介绍到这了,更多相关Spring面向切面内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: Spring超详细讲解AOP面向切面

本文链接: https://lsjlt.com/news/169497.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

猜你喜欢
  • Spring超详细讲解AOP面向切面
    目录简介AOP底层原理代理概念JDK动态代理实现Spring中的AOP相关术语AspectJ实现AOP不同通知类型实现相同的切入点抽取增强类优先级完全使用注解开发说明:基于atgui...
    99+
    2022-11-13
    Spring面向切面 Spring AOP面向切面
  • Spring超详细讲解面向对象到面向切面
    目录前言一.OOP&AOP二.AOP核心三.第一个AOP案例1.环境准备2.AOP实现步骤四.切入点表达式1.语法格式2.通配符五.AOP通知类型环境准备环绕通知1.返回后通...
    99+
    2022-11-13
    Spring 面向对象 Spring 面向切面
  • 【Spring】面向切面编程详解(AOP)
    文章目录 一、AOP概述什么是AOPAOP应用场景 二、AOP的基本术语术语介绍术语举例详解 三、AOP实例说明四、通知类型详解概述前置通知后置通知环绕通知最终通知 六、AOP实现声明式事务...
    99+
    2023-08-30
    spring java 后端
  • Spring面向切面编程AOP详情
    目录1. 面向切面编程2. AOP核心概念3. AOP的实现4. Spring 对AOP支持4.1 支持@Aspect4.2 声明一个切面4.3 声明一个切入点4.4 声明增强5. ...
    99+
    2024-04-02
  • Java aop面向切面编程(aspectJweaver)案例详解
    面向切面编程的目的就是:在不改变别人的代码的前提下,在别人代码方法执行前或后,执行(切入自己的逻辑) 准备:idea+maven+aspectjweaver-1.8.9.jar ...
    99+
    2024-04-02
  • JAVA:面向切面编程AOP
    一、定义         把某一些功能提取出来与某一对象进行隔离,提取之后可以对某哥单方面的功能进行修改和扩展         也就是把众多方法中的的所有公共代码抽取出来,放到某个地方集中管理         对业务逻辑的各个部分进行了隔离...
    99+
    2023-09-12
    java AOP
  • spring6-AOP面向切面编程
    面向切面编程AOP 1、场景模拟1.1、声明接口1.2、创建实现类1.3、创建带日志功能的实现类1.4、提出问题 2、代理模式2.1、概念2.2、静态代理2.3、动态代理2.4、测试 ...
    99+
    2023-10-21
    java 后端 spring aop 面向切面编程
  • Java的Spring AOP详细讲解
    目录什么是AOP&作用AOP的动态代理技术基于JDK的动态代理cglib动态代理AOP相关概念AOP开发明确事项需要编写的内容AOP技术实现的内容AOP 底层使用哪种代理方式...
    99+
    2024-04-02
  • Spring针对AOP详细讲解
    目录什么是Spring AOP为什么要用AOP?AOP的组成Spring AOP的实现导入依赖定义切面和切点定义通知Advice(5类)什么是Spring AOP AOP是面向切面编...
    99+
    2024-04-02
  • Spring框架AOP面向切面编程原理全面分析
    目录1.什么是AOPAOP面向切面的优势AOP需要添加的依赖2.简述AOP工作运行原理动态创建的总结:3.使用Spring创建AOP测试类Spring.xml1.什么是AOP AOP...
    99+
    2024-04-02
  • AOP面向切面编程思想。
    目录 一、AOP工作流程 1、基本概念 2、AOP工作流程  二、AOP核心配置 1、AOP切入点表达式 2、AOP通知类型 三、AOP通知获取数据 1、获取参数 2、获取返回值 3、获取异常  四、AOP事务管理 1、Spring事务简介...
    99+
    2023-09-06
    spring java 后端
  • Spring使用AspectJ的注解式实现AOP面向切面编程
    目录1、认识Spring AOP1.1 AOP的简介1.2 AOP中的概念 切入点(pointcut):2、认识AspectJ 2.1 AspectJ的简介2.2 Spring AO...
    99+
    2024-04-02
  • Spring AOP使用@Aspect注解 面向切面实现日志横切的操作
    引言: AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续...
    99+
    2024-04-02
  • Spring超详细讲解事务
    目录什么是事务事务的四个特性(ACID)Spring对事务的支持编程式事务管理声明式事务管理基于注解的声明式事务管理Spring事务管理的三个接口Spring事务属性什么是事务 一个...
    99+
    2024-04-02
  • 解析Spring中面向切面编程
    目录一、AOP——另一种编程思想1.1、什么是 AOP1.2、为什么需要 AOP1.3、AOP 实现分类二、AOP 术语三、初步认识 Spring AOP3.1、Spring AOP...
    99+
    2024-04-02
  • JavaScript面向对象编程详细讲解
    这篇文章主要介绍“JavaScript面向对象编程详细讲解”,在日常操作中,相信很多人在JavaScript面向对象编程详细讲解问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”...
    99+
    2024-04-02
  • Spring超详细讲解BeanUtils改造
    目录1.基本原理2.使用3.性能4.提醒1.基本原理 原理:https://www.jb51.net/article/252384.htm 浅拷贝:https://www.jb51....
    99+
    2024-04-02
  • java开发AOP面向切面编程入门
    目录引言不好的解决方案面向过程的解决方案使用继承解决方案使用聚合的解决方案面向切面的编程基本概念基于Spring面向切面程序实现小结引言 在实际应用场景中,我们封装一个学生的类,这个...
    99+
    2024-04-02
  • Python 面向切面编程 AOP 及装饰器
    目录什么是 AOP装饰器函数装饰器类装饰器1、函数装饰函数2、类装饰函数3、函数装饰类4、类装饰类什么是 AOP AOP,就是面向切面编程,简单的说,就是动态地将代码切入到类的指定方...
    99+
    2024-04-02
  • Java面向切面编程AOP怎么实现
    这篇文章主要介绍“Java面向切面编程AOP怎么实现”,在日常操作中,相信很多人在Java面向切面编程AOP怎么实现问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”Java面向切面编程AOP怎么实现”的疑惑有所...
    99+
    2023-06-04
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作