AOP 和 OOP 的对比

发布于 2023-02-06 12:39:43 字数 5696 浏览 78 评论 0

OOP 是自上而下(自左而右)的单维度代码逻辑,在随着工程扩大,业务复杂度变高,演进的过程中会出现如日志,权限,效率检测,事务管理等横切(纵切)问题。在 OOP 的概念中这些横切(纵切)代码会分散在各个业务侧,造成这些逻辑难以维护。而 AOP 编程思想就是把这些横切(纵切)逻辑和关键业务进行分离,从而达到主业务逻辑和辅助性业务逻辑解耦,实现高内聚,低耦合的设计思想。提高了开发效率和代码的复用性。

AOP 的底层实现

JDK 动态代理(代理类有接口默认采用动态代理)

CGLIB 代理(代理类没有接口默认采用 CGLIB 代理)

但是不是 绝对的,可以使用 @EnableAspectJAutoProxy(proxyTargetClass = true) 强制采用 CGLIB。

问题

编译期织入还是运行期织入?运行时

sping 初始化织入还是 getBean 织入?初始化,在IOC容器中的对象初始化的时候就执行了织入。IOC 容器名字 singletonObjects,其本质是要给线程安全的HashMap。(具体织入是在 singletonFactory.getObject() 时候织入)

springIOC从容器中拿到一个对象后,进行各种判断,判断其是否要使用代理(AbstractAutowireCapableBeanFactory查找exposedObject = this.initializeBean),是在initializeBean方法中完成代理。里面具体过程如下

AbstractAutoProxyCreator.java -> AopProxy
其中AopProxy有两个实现
CglibAopProxy
JdkDynamicAopProxy
那么如何识别用哪个实现呢,在DefaultAopProxyFactory中的createAopProxy中进行了判断,进行了好几个判断条件。
其中config.isProxyTargetClass默认为false,此时直接使用JDK动态代理。
但是config.isProxyTargetClass是可以配置的,在@Configuration标注的文件上开启@EnableAspectJAutoProxy(proxyTargetClass = true)

跟中容器对象初始化的方法:大胆假设,小心求证,使用条件断点法。

SpringAOP 和 AspectJ 的关系

AOP 是编程目标

SpringAOP 是 AOP 的实现方式。

AspectJ 是 AOP 的实现方式。

SpringAOP 在 2.5 中的编程风格不是很好,之后的版本借鉴了 AspectJ 的编程风格实现 AOP。

官方文档中说明 SpringAOP 提供了两种编程风格

AspectJSupport,使用 AspectJ 注解

Schema-basedAOPSupport,使用 xml 配置

SpringAOP 中的概念(spring 中的 aop 作用为方法级别)

Aspect(切面):PointCut,JoinPoint,Advice 定义的地方(文件)就是切面,切点一定要交予 Spring 管理(@Component)

PointCut(切点):连接点的集合 (相对于连接点,切点可看作链接点的集合表)

public void save(){
    // before logger.log();

    save

    // after logger.log();
}

public void modify(){
    // before logger.log();

    modify

    // after logger.log();
}

public void delete(){
    // before logger.log();

    delete

    // after logger.log();
}

以上各个类,方法中的所有logger.log()加起来称之为切点

JoinPoint(连接点):目标对象中的方法 (相对与切点,连接点可看作表中的一个记录),

public void save(){
    // before logger.log();

    save

    // after logger.log();
}
以上的looger.log()和save结合的地方就称之为连接点

Join Point 类型

  1. execution:限定方法的方方面面,如访问修饰,返回值,参数等等。
  2. within:限定的粒度为类,再次往下限定,则运行报错。
  3. args:限定参数类型和数量,和包名类名无关,只要能够对上参数,则包名什么的无所谓是匹配
  4. this:从容器中取出的对象是限定的对象实例(代理类和限定匹配)(可以在CGLIB中起作用)
  5. target:从容器中取出的对象代理的目标对象是限定的对象实例(代理对象和限定匹配)
  6. annotation:限定到方法上的注解 注意,以上类型的限定参数都支持@ 都支持逻辑运算符

参数:JoinPoint 对象,ProceedingJoinPoint 增强的 JoinPoint 可以增强环绕,可以动态替换参数,调用方法等。

Weaving(织入):把代理逻辑加入到目标对象上的过程叫织入

Advice

public void save() throwing Exception { // after throwing advice
    // before logger.log(); // before(前置通知)
    try{
        save
    } catch(){
        // after throwing advice
    } finally{
        // after(final) advice
    }
    // after logger.log(); // after returen advice(后置通知)
}

around advice

Target

public class Controller {  // 目标对象
    public void save(){ // 连接点
        logger.log(); // 代理对象,代理逻辑
        save
    }
}

Spring 中开启 AspectJ 支持

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-ataspectj

@Configuration
@EnableAspectJAutoProxy
public class AppConfig {

}

或者
<aop:aspectj-autoproxy/>

声明一个切面

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-at-aspectj

<bean class="org.xyz.NotVeryUsefulAspect">
    <!-- configure properties of the aspect here -->
</bean>
或者
package org.xyz;
import org.aspectj.lang.annotation.Aspect;

@Aspect
public class NotVeryUsefulAspect {

}

声明一个切点

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts

@Pointcut("execution(* transfer(..))")// the pointcut expression // 切点是一个表,包含了springEL表达式,每个表达式是一个连接点
private void anyOldTransfer() {}// the pointcut signature

常用切点表达式

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-pointcuts-examples

声明一个通知

https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/core.html#aop-advice

package com.xyz.someapp;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class SystemArchitecture {
    // 如果有如下切点
    @Pointcut("execution(* com.xyz.someapp.dao.*.*(..))")
    public void dataAccessOperation() {}

    // 则可以搭配如下通知,通知中的表达式是切点方法的全路径限定
    @Before("com.xyz.someapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheckBefore() {
        // ...
    }

    // 则可以搭配如下通知,通知中的表达式是切点方法的全路径限定
    @After("com.xyz.someapp.SystemArchitecture.dataAccessOperation()")
    public void doAccessCheckAfter() {
        // ...
    }
}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。
列表为空,暂无数据

关于作者

宛菡

暂无简介

0 文章
0 评论
22 人气
更多

推荐作者

qq_z5gHLI

文章 0 评论 0

圈圈圆圆圈圈

文章 0 评论 0

alipaysp_h2Vbo4sv6k

文章 0 评论 0

初见你

文章 0 评论 0

清风无影

文章 0 评论 0

云胡

文章 0 评论 0

    我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
    原文