"Se um trabalhador quiser fazer bem o seu trabalho, ele deve primeiro afiar suas ferramentas." - Confúcio, "Os Analectos de Confúcio. Lu Linggong"
Primeira página > Programação > Revelando o funcionamento interno do Spring AOP

Revelando o funcionamento interno do Spring AOP

Publicado em 2024-11-06
Navegar:355

Unveiling the Inner Workings of Spring AOP

Nesta postagem, iremos desmistificar a mecânica interna da Programação Orientada a Aspectos (AOP) na primavera. O foco estará na compreensão de como o AOP alcança funcionalidades como o registro em log, muitas vezes considerado uma forma de “mágica”. Ao percorrer uma implementação Java central, veremos como tudo gira em torno da reflexão, dos padrões de proxy e das anotações do Java, em vez de algo verdadeiramente mágico.

Pré-requisitos

  • API Java Core Proxy
  • API de reflexão
  • API de anotação

Tudo isso faz parte dos pacotes java.lang.reflect,java.lang.annotation e javassist.util.proxy.

O Mecanismo Central

No cerne do Spring AOP está o conceito de objetos proxy, interceptadores de método e reflexão. O principal participante desse padrão é o MethodHandler (ou manipulador de invocação). Este manipulador controla o comportamento do objeto proxy interceptando chamadas de método. Quando um método é invocado no proxy, ele passa pelo manipulador, onde as anotações podem ser introspectadas por meio de reflexão. Com base nas anotações aplicadas, a lógica necessária (por exemplo, registro) pode ser executada antes, depois ou na exceção.

Dividindo

  1. Objetos proxy: Esses são objetos criados dinamicamente que substituem seus objetos de negócios reais, roteando chamadas de método por meio do manipulador de método.
  2. Manipuladores de invocação: É aqui que a mágica da interceptação acontece. Usando a reflexão, o manipulador pode examinar as anotações presentes no método de destino e alterar o comportamento de acordo.
  3. Anotações personalizadas: Você pode definir anotações personalizadas, que servem como marcadores para acionar funcionalidades adicionais, como registro, verificações de segurança ou gerenciamento de transações.

Exemplo: Suponha que queremos adicionar log antes e depois de determinadas execuções de métodos. Em vez de codificar o log em todos os lugares, podemos anotar métodos com @BeforeMethod e @AfterMethod. Nosso manipulador inspeciona o método para esta anotação e adiciona a lógica de registro apropriada dinamicamente.

Abaixo estão as classes com a aparência do Controlador e do Serviço em nosso exemplo.

WorkerController.java

package edu.pk.poc.aop.controller;

import edu.pk.poc.aop.annotation.AfterMethod;
import edu.pk.poc.aop.annotation.All;
import edu.pk.poc.aop.annotation.BeforeMethod;
import edu.pk.poc.aop.helper.ProxyFactory;
import edu.pk.poc.aop.service.Worker;
import edu.pk.poc.aop.service.WorkerService;
import edu.pk.poc.aop.service.WorkerServiceImpl;

public class WorkerController {
    WorkerService workerService = ProxyFactory.createProxy(WorkerServiceImpl.class);
    /**
     * This Method 1s annotated with @BeforeMethod and @AfterMethod, So the log statements
     * will be generated before and after method call.
     */
    @BeforeMethod
    @AfterMethod
    public void engageFullTimeWorker() throws Exception {
        Worker fullTimeWorker = new Worker();
        fullTimeWorker.setName("FullTime-Worker");
        fullTimeWorker.setPartTime(false);
        fullTimeWorker.setDuration(9);
        workerService.doWork(fullTimeWorker);
    }
    /**
     * This Method is annotated with @All, So the log statements will be generated before and after method call
     * along with exception if raised.
     */
    @All
    public void engagePartTimeWorker() throws Exception {
        Worker partTimeWorker = new Worker();
        partTimeWorker.setName("PartTime-Worker");
        partTimeWorker.setPartTime(true);
        partTimeWorker.setDuration(4);
        workerService.doWork(partTimeWorker);
    }
}

WorkerServiceImpl.java

package edu.pk.poc.aop.service;

import edu.pk.poc.aop.annotation.AfterMethod;

public class WorkerServiceImpl implements WorkerService {
    /**
     * Here this method is annotated with only @AfterMethod, So only log statement
     * will be generated after method call
     */
    @AfterMethod
    @Override
    public void doWork(Worker worker) throws Exception {
        if (worker.isPartTime()) {
            throw new Exception("Part time workers are not permitted to work.");
        }
        System.out.print("A full time worker is working for "   worker.getDuration()   " hours :: ");
        for (int i = 1; i 



Classe de teste Main.java

package edu.pk.poc.aop.test;

import edu.pk.poc.aop.controller.WorkerController;
import edu.pk.poc.aop.helper.ProxyFactory;
import edu.pk.util.Logger;

public class Main {
    public static void main(String[] args) {
        WorkerController controller = ProxyFactory.createProxy(WorkerController.class);
        Logger logger = new Logger();
        try {
            System.out.println("Testing @BeforeMethod and @AfterMethod");
            System.out.println("-----------------------------------------");
            controller.engageFullTimeWorker();
            System.out.println("Testing @All");
            System.out.println("-----------------------------------------");
            controller.engagePartTimeWorker();
        } catch (Exception e) {
            logger.error("Exception caught in Main class");
        }
    }
}

Saída

Testing @BeforeMethod and @AfterMethod
-----------------------------------------
>>> Entering into edu.pk.poc.aop.controller.WorkerController.engageFullTimeWorker()
A full time worker is working for 9 hours :: * * * * * * * * 
>>> Exiting from edu.pk.poc.aop.service.WorkerServiceImpl.doWork()
>>> Exiting from edu.pk.poc.aop.controller.WorkerController.engageFullTimeWorker()
Testing @All
-----------------------------------------
>>> Entering into edu.pk.poc.aop.controller.WorkerController.engagePartTimeWorker()
>>> Exception in edu.pk.poc.aop.controller.WorkerController.engagePartTimeWorker()
Exception caught in Main class

Como funciona

Quando um método é invocado em um objeto proxy, a chamada é interceptada pelo manipulador, que usa reflexão para inspecionar todas as anotações no método de destino. Com base nessas anotações, o manipulador decide se deseja registrar a entrada/saída do método, registrar exceções ou ignorar completamente o registro.

Veja como você pode visualizá-lo:

  • Antes da execução: entrada do método de log.
  • Após a execução: saída ou sucesso do método de log.
  • Todos: Registra a entrada do método, entrada do método e em caso de exceção, se gerado. Esse comportamento dinâmico mostra que o Spring AOP aproveita as principais APIs Java em vez de empregar algum truque mágico.

Definir anotações

package edu.pk.poc.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AfterMethod {

}
package edu.pk.poc.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface BeforeMethod {

}
package edu.pk.poc.aop.annotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface All {

}

Definir fábrica de proxy

package edu.pk.poc.aop.helper;

/**
 * The {@code ProxyFactory} class is responsible for creating proxy objects using the Javassist library.
 * It allows for dynamic generation of proxies for classes or interfaces, with support for method interception.
 */
public class ProxyFactory {

    /**
     * A Javassist ProxyFactory instance used to generate proxy classes.
     */
    private static final javassist.util.proxy.ProxyFactory factory = new javassist.util.proxy.ProxyFactory();

    /**
     * Creates a proxy object for the given class or interface.
     * If the class is an interface, the proxy implements the interface.
     * If it's a concrete class, the proxy extends the class.
     *
     * @param    the type of the class or interface for which the proxy is to be created
     * @param klass the {@code Class} object representing the class or interface to proxy
     * @return a proxy instance of the specified class or interface, or {@code null} if proxy creation fails
     */
    public static  T createProxy(Class klass) {
        if (klass.isInterface())
            factory.setInterfaces(new Class[]{klass});
        else
            factory.setSuperclass(klass);
        try {
            return (T) factory.create(new Class>[0], new Object[0], new AOPLoggingMethodHandler());
        } catch (Exception e) {
            System.err.println(e.getMessage());
        }
        return null;
    }
}

Definir MethodHandler

package edu.pk.poc.aop.helper;

import edu.pk.poc.aop.annotation.AfterMethod;
import edu.pk.poc.aop.annotation.All;
import edu.pk.poc.aop.annotation.BeforeMethod;
import edu.pk.poc.aop.annotation.OnException;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import edu.pk.util.Logger;
import javassist.util.proxy.MethodHandler;

public class AOPLoggingMethodHandler implements MethodHandler {

    private static final Logger logger = new Logger();

    public Object invoke(Object self, Method thisMethod, Method proceed, Object[] args) throws Throwable {
        if (proceed != null) { // Concrete Method
            Object result = null;
            String className = resolveClassName(self);
            try {
                if (isAnnotationPresent(thisMethod, BeforeMethod.class) || isAnnotationPresent(thisMethod, All.class)) {
                    logger.info(">>> Entering into "   className   "."   thisMethod.getName()   "()");
                }
                result = proceed.invoke(self, args);
                if (isAnnotationPresent(thisMethod, AfterMethod.class) || isAnnotationPresent(thisMethod, All.class)) {
                    logger.info(">>> Exiting from "   className   "."   thisMethod.getName()   "()");
                }
            } catch (Throwable t) {
                if (isAnnotationPresent(thisMethod, OnException.class) || isAnnotationPresent(thisMethod, All.class)) {
                    logger.error(">>> Exception in "   className   "."   thisMethod.getName()   "()");
                }
                throw t;
            }
            return result;
        }
        throw new RuntimeException("Method is Abstract");
    }

    private boolean isAnnotationPresent(Method method, Class klass) {
        Annotation[] declaredAnnotationsByType = method.getAnnotationsByType(klass);
        return declaredAnnotationsByType != null && declaredAnnotationsByType.length > 0;
    }

    private String resolveClassName(Object self) {
        String className = self.getClass().getName();
        if (className.contains("_$$")) {
            className = className.substring(0, className.indexOf("_$$"));
        }
        return className;
    }
}

Conclusão

Spring AOP é uma ferramenta poderosa para questões transversais, mas não está fazendo nada revolucionário. Ele é baseado em conceitos básicos do Java, como reflexão e proxies, que estão disponíveis na própria linguagem. Ao entender isso, você pode apreciar melhor como o Spring simplifica essas mecânicas de nível inferior para conveniência do desenvolvedor.

Declaração de lançamento Este artigo foi reproduzido em: https://dev.to/prabhatkjena/unveiling-the-inner-workings-of-spring-aop-548o?1 Se houver alguma violação, entre em contato com [email protected] para excluí-la
Tutorial mais recente Mais>

Isenção de responsabilidade: Todos os recursos fornecidos são parcialmente provenientes da Internet. Se houver qualquer violação de seus direitos autorais ou outros direitos e interesses, explique os motivos detalhados e forneça prova de direitos autorais ou direitos e interesses e envie-a para o e-mail: [email protected]. Nós cuidaremos disso para você o mais rápido possível.

Copyright© 2022 湘ICP备2022001581号-3