Fondation de printemps--AOP

Notion AOP

Dans l'industrie du logiciel, AOP est l'abréviation de Aspect Oriented Programming, ce qui signifie : programmation orientée aspect, une technologie qui réalise une maintenance unifiée des fonctions du programme grâce à la pré-compilation et à des agents dynamiques pendant l'exécution. AOP est une continuation de la POO, un point chaud du développement logiciel, un contenu important dans le framework Spring et un paradigme dérivé de la programmation fonctionnelle. AOP peut être utilisé pour isoler diverses parties de la logique métier, réduisant ainsi le degré de couplage entre les différentes parties de la logique métier, améliorant la réutilisabilité des programmes et améliorant l'efficacité du développement en même temps.

Avantages et fonctions de l'AOP

effet:

  • Ne pas modifier le code source pendant l'exécution du programme, et renforcer les méthodes existantes

Avantage:

  • réduire la duplication de code
    • Améliorer l'efficacité du développement
    • entretien facile

Méthode de mise en œuvre : technologie de proxy dynamique

Citation

Réflexion :
Nous créons un projet Spring qui se connecte à la base de données avec des opérations d'ajout, de suppression, de modification et de requête de base. L'ajout, la suppression, la modification et la requête de base exécutent également une instruction SQL, mais si plusieurs instructions SQL sont exécutées en une seule opération, et une exception se produit au milieu, vous constaterez que l'instruction sql avant l'exception est exécutée, mais l'instruction sql après l'exception n'est pas exécutée, ce qui entraîne des erreurs de données.
Par exemple, lorsque nous mettons en œuvre des virements bancaires, nous pouvons interroger les informations de l'expéditeur et du destinataire. Après avoir envoyé l'argent, c'est-à-dire après que le montant de l'argent de l'expéditeur est réduit du montant correspondant, une exception se produit et le montant du récepteur n'augmente pas. Comment gérer cela ?
Solution :
la base de données contient des transactions. Pensez-vous que la transaction ne s'ouvre pas lorsque vous effectuez des ajouts, des suppressions, des modifications et des requêtes ? La réponse est non, car les données ne seront pas soumises avec succès si la transaction n'est pas ouverte. Lorsque nous exécutons l'instruction sql, elle ouvre une transaction par défaut.
Et pour notre opération de transfert d'argent, il y a plusieurs instructions SQL, et il lancera plusieurs transactions au lieu de planifier l'ensemble comme une seule transaction.
La solution consiste à contrôler le basculement de la transaction et à soumettre nous-mêmes l'opération de restauration.
Démarrez la transaction avant chaque exécution de l'instruction sql, validez la transaction après l'exécution et annulez si une erreur se produit.

Optimisation supplémentaire :
si nous opérons selon la méthode ci-dessus, nous devons ouvrir et valider des transactions pour chaque instruction SQL, ce qui rendra notre code gonflé, et nous devons traiter ces codes répétés de manière uniforme. La solution consiste à utiliser des proxies dynamiques pour améliorer ces méthodes sans modifier le code source.
L'AOP de Spring utilise la technologie de proxy dynamique pour renforcer la méthode.

Termes liés à l'AOP

Joinpoint(连接点):   
  所谓连接点是指那些被拦截到的点。在 spring 中,这些点指的是方法,因为 spring 只支持方法类型的 连接点。 
Pointcut(切入点):   
  所谓切入点是指我们要对哪些 Joinpoint 进行拦截的定义。 
Advice(通知/增强):   
  所谓通知是指拦截到 Joinpoint 之后所要做的事情就是通知。   
通知的类型:
前置通知,后置通知,异常通知,最终通知,环绕通知。 
Introduction(引介):   
  引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方 法或 Field。 
Target(目标对象):   
  代理的目标对象。 
Weaving(织入):   
  是指把增强应用到目标对象来创建新的代理对象的过程。   
spring 采用动态代理织入,而 AspectJ 采用编译期织入和类装载期织入。 
Proxy(代理):   
  一个类被 AOP 织入增强后,就产生一个结果代理类。 
Aspect(切面):  
   是切入点和通知(引介)的结合。 

Basé sur la configuration XMLAOP

Simuler l'interface de la couche métier IAccountSerivice.java

public interface IAccountService {
    
    
    void saveAccount();

    void updateAccount(int i);

    int deleteAccount();
}

Classe d'implémentation

public class AccountServiceImpl implements IAccountService {
    
    

    @Override
    public void saveAccount() {
    
    
        System.out.println("保存了用户");
    }

    @Override
    public void updateAccount(int i) {
    
    
        System.out.println("更新了用户 "+ i);
    }

    @Override
    public int deleteAccount() {
    
    
        System.out.println("删除了用户");
        return 0;
    }
}

Classe de notification AOP

public class Logger {
    
    
    private void BeforePrintLogger() {
    
    
        System.out.println("执行了前置通知方法");
    }
    private void AfterReturningPrintLogger() {
    
    
        System.out.println("执行了后置通知方法");
    }
    private void ExThrowPrintLogger() {
    
    
        System.out.println("执行了异常通知方法");
    }
    private void AfterPrintLogger() {
    
    
        System.out.println("执行了最终通知方法");
    }
 /**
     * 环绕通知
     * 问题:
     *      当我们配置了环绕通知之后,切入点方法没有执行,而通知方法执行了。
     * 分析:
     *      通过对比动态代理中的环绕通知代码,发现动态代理的环绕通知有明确的切入点方法调用,而我们的代码中没有。
     * 解决:
     *      Spring框架为我们提供了一个接口:ProceedingJoinPoint。该接口有一个方法proceed(),此方法就相当于明确调用切入点方法。
     *      该接口可以作为环绕通知的方法参数,在程序执行时,spring框架会为我们提供该接口的实现类供我们使用。
     *
     * spring中的环绕通知:
     *      它是spring框架为我们提供的一种可以在代码中手动控制增强方法何时执行的方式。
     */
    private Object AroundPrintLogger(ProceedingJoinPoint prj) {
    
    
        Object rtValue = null;
        try {
    
    
            Object[] args = prj.getArgs(); // 得到方法执行所需的参数
            System.out.println("前置通知");
            rtValue = prj.proceed(args); // 明确使用业务层方法(切入点方法)
            System.out.println("后置通知");
            return rtValue;
        } catch (Throwable t) {
    
    
            System.out.println("异常通知");
            throw new RuntimeException(t);
        } finally {
    
    
            System.out.println("最终通知");
        }
    }
}

Configurer AOP et importer les données
bean.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: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
        http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 配置service -->
    <bean id="service" class="org.example.service.impl.AccountServiceImpl"></bean>

    <!-- Spring中基于XML的AOP配置步骤
        1、把通知Bean交给spring来管理
        2、使用AOP:config标签来表明AOP开始配置
        3、使用AOP:aspect标签来配置切面
            id属性:是给切面提供一个唯一标识
            ref属性:是指定通知类bean的Id。
        4、在aop:aspect标签的内部使用对应标签来配置通知的类型
               我们现在示例是让printLog方法在切入点方法执行之前之前:所以是前置通知
               aop:before:表示配置前置通知
                    method属性:用于指定Logger类中哪个方法是前置通知
                    pointcut属性:用于指定切入点表达式,该表达式的含义指的是对业务层中哪些方法增强

            切入点表达式的写法:
                关键字:execution(表达式)
                表达式:
                    访问修饰符  返回值  包名.包名.包名...类名.方法名(参数列表)
                标准的表达式写法:
                    public void com.itheima.service.impl.AccountServiceImpl.saveAccount()
                访问修饰符可以省略
                    void com.itheima.service.impl.AccountServiceImpl.saveAccount()
                返回值可以使用通配符,表示任意返回值
                    * com.itheima.service.impl.AccountServiceImpl.saveAccount()
                包名可以使用通配符,表示任意包。但是有几级包,就需要写几个*.
                    * *.*.*.*.AccountServiceImpl.saveAccount())
                包名可以使用..表示当前包及其子包
                    * *..AccountServiceImpl.saveAccount()
                类名和方法名都可以使用*来实现通配
                    * *..*.*()
                参数列表:
                    可以直接写数据类型:
                        基本类型直接写名称           int
                        引用类型写包名.类名的方式   java.lang.String
                    可以使用通配符表示任意类型,但是必须有参数
                    可以使用..表示有无参数均可,有参数可以是任意类型
                全通配写法:
                    * *..*.*(..)

                实际开发中切入点表达式的通常写法:
                    切到业务层实现类下的所有方法
                        * com.itheima.service.impl.*.*(..)
    -->

    <!-- 配置Logger -->
    <bean id="logger" class="org.example.utils.Logger"></bean>

    <!-- 配置AOP -->
    <aop:config>
        <!-- 配置切面 -->
        <aop:aspect id = "logAdvice" ref="logger">
            <!-- 配置切入点信息,提前配置好,后面可以直接使用,让代码简略
            如果写aop:aspect外部,则需要写在其前面,可以供后面各个切面使用
            -->
            <aop:pointcut id="pt" expression="execution(* org.example.service.impl.*.*(..))"/>
            <!-- 配置通知类型,并且建立通知方法和切入点的关联 ,无论切入点方法是否正常执行,它都会执行 -->
            <aop:before method="BeforePrintLogger" pointcut-ref="pt"></aop:before>
            <!-- 后置通知,它和异常通知只能执行一个 -->
            <aop:after-returning method="AfterReturningPrintLogger" pointcut="execution(* org.example.service.impl.*.*(..))"></aop:after-returning>
            <!-- 异常通知,它和后置通知只能执行一个-->
            <aop:after-throwing method="ExThrowPrintLogger" pointcut="execution(* org.example.service.impl.*.*(..))"></aop:after-throwing>
            <!-- 最终通知,无论切入点方法是否正常执行,它都会执行 -->
            <aop:after method="AfterPrintLogger" pointcut="execution(* org.example.service.impl.*.*(..))"></aop:after>
             <!-- 环绕通知,使用环绕通知则不必再使用其他通知 -->
            <aop:around method="AroundPrintLogger" pointcut-ref="pt"></aop:around>
        </aop:aspect>

    </aop:config>
</beans>

test

  @Test
    public void test() {
    
    
        // 1、获取容器
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        // 2、获取对象
        IAccountService as = (IAccountService) ac.getBean("service");
        // 3、执行方法
        as.saveAccount();
//        as.deleteAccount();
    }

Sortie
insérez la description de l'image ici
Reportez-vous au contrôle de transaction suivant implémenté en conjonction avec AOP : Implémentation du contrôle de transaction

Je suppose que tu aimes

Origine blog.csdn.net/qq_44660367/article/details/108367103
conseillé
Classement