Princípio de implementação spring5.x-AOP e análise de código-fonte

Acima: princípio spring5.x-listener e implementação do código-fonte


Artigos desta série:

                Princípio spring5.x-listener e implementação do código-fonte

                análise de dependência circular spring5.x-solve

                Aprendizado de código-fonte do módulo spring5.x-IOC

                Introdução ao spring5.x e seu ambiente de leitura de código-fonte Spring correspondente


O que é springAop?

Spring AOP (Programação Orientada a Aspectos) é um recurso importante da estrutura Spring, que fornece um paradigma de programação baseado em preocupações transversais. AOP permite que os desenvolvedores separem preocupações transversais (como registro em log, gerenciamento de transações, segurança, etc.) da lógica de negócios principal, resultando em melhor estrutura e capacidade de manutenção do código.

Na programação orientada a objetos tradicional, as funções geralmente estão espalhadas entre vários módulos da aplicação, como acesso a bancos de dados, tratamento de exceções, etc. Isso resulta em muito código repetitivo que é difícil de manter e estender. AOP fornece uma maneira de definir preocupações transversais de maneira declarativa e aplicá-las a vários módulos do aplicativo sem modificar o código original.

No Spring AOP, as preocupações transversais são chamadas de aspectos, e os objetos que estão sendo cortados são chamados de objetos de destino. A implementação do AOP depende do modo proxy e da tecnologia de proxy dinâmico.Spring fornece dois métodos principais de implementação do AOP:

  1. AOP baseado em proxy: esta abordagem usa proxies dinâmicos JDK ou proxies dinâmicos CGLIB para criar objetos proxy e entrelaçar aspectos em chamadas de método no objeto de destino. Se o objeto de destino implementar a interface, o proxy dinâmico JDK será usado; caso contrário, o proxy dinâmico CGLIB será usado. AOP baseado em proxy pode incorporar lógica de aspecto ao objeto de destino em tempo de execução.

  2. AOP implantado em tempo de compilação Java puro: Este método implementa AOP por meio do aprimoramento de bytecode em tempo de compilação. Ele usa anotações e compilador do AspectJ para entrelaçar aspectos no bytecode do objeto de destino durante o processo de compilação. Ao contrário do AOP baseado em proxy, o AOP incorporado em tempo de compilação Java puro não requer proxies dinâmicos em tempo de execução e, portanto, tem desempenho superior.

Spring AOP oferece suporte às seguintes funções de aspecto comuns:

  • Antes do conselho: ações executadas antes do método de destino ser chamado.

  • Conselho posterior: Ações executadas após a chamada do método de destino, independentemente de ocorrer uma exceção.

  • Depois de retornar o conselho: ações executadas após o método de destino retornar um resultado com êxito.

  • Conselho de exceção (aconselhamento após lançamento): Ações executadas após o método de destino lançar uma exceção.

  • Conselhos sobre: ​​envolve a chamada do método de destino e pode executar operações personalizadas antes e depois da chamada.

Spring AOP é um mecanismo para implementar programação orientada a aspectos, que pode ajudar os desenvolvedores a gerenciar melhor questões transversais e fornece métodos flexíveis para lidar com funções comuns, como registro, transações e segurança, melhorando a capacidade de reutilização e disponibilidade do código.

Uso básico da mola AOP

Consulte o artigo que escrevi originalmente: Spring's AOP

aprendizado de código-fonte Spring AOP

73725ee6b5bf7a7f3aa9e1c9d5cf9ef8.png

Inicialização AOP

d2951792ca3bf582edfe146fd4364dde.png

Quando o Spring é inicializado em org.springframework.context.support.AbstractApplicationContext#refresh(), há um processador de notificação de inicialização e o método a seguir será chamado para inicialização (outros são ignorados e lidos no artigo anterior)

6ae398dcd4be66d4aa0d0d6daee7b17c.png

Quando @EnableAspectJAutoProxy(proxyTargetClass = true) é anotado em uma classe de configuração personalizada ou classe de inicialização, isso indica que o projeto está habilitado para AOP.

6bb2bd46d69ec1b1a2fd944ac1777b91.png

Configure anotações de classe de inicialização:

@EnableAspectJAutoProxy

Digite @EnableAspectJAutoProxy e descubra que AspectJAutoProxyRegistrar.class é importado por meio de @import.

14216ca2e0f96590ddb69d66f7bde529.png

Localização do código: org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions

Diagrama de Classe:bc4eac3aef8a3606539faba83358f5c8.png

@Override
  public void registerBeanDefinitions(
      AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    //通过反射方式进行注入

    AopConfigUtils.registerAspectJAnnotationAutoProxyCreatorIfNecessary(registry);

    AnnotationAttributes enableAspectJAutoProxy =
        AnnotationConfigUtils.attributesFor(importingClassMetadata, EnableAspectJAutoProxy.class);
    //cglib代理
    if (enableAspectJAutoProxy.getBoolean("proxyTargetClass")) {
      AopConfigUtils.forceAutoProxyCreatorToUseClassProxying(registry);
    }
    //jdk代理(同理)
    if (enableAspectJAutoProxy.getBoolean("exposeProxy")) {
      AopConfigUtils.forceAutoProxyCreatorToExposeProxy(registry);
    }
  }

b85d3086524b4b9bca19a269e53f290e.png

Ou seja, quando o spring começar, ele usará a anotação @EnableAspectJAutoProxy para identificar se deve iniciar o aop. Se for adicionado por padrão, o aop será iniciado. A classe AspectJAutoProxyRegistrar é importada através do @import nesta anotação para inicialização , e a classe tem Um método RegisterBeanDefinitions é executado.

Então você irá inserir: org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean

exposedObject = this.initializeBean(beanName, exposedObject, mbd);

Observação: a camada superior aqui é, na verdade, AnnotationAwareAspectJAutoProxyCreator, mas a camada inferior é AbstractAutowireCapableBeanFactory. Observe o diagrama de classes abaixo:

9e9352e4a7776620d7321e97ebdd9e89.png

Local da classe: org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#initBeanFactory

Aqui está a classe de fábrica que inicializa o bean. Na verdade, ainda está atualizado aqui.

6a6aa7f745722b26e011d13fca0cacdc.png

protected void initBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        super.initBeanFactory(beanFactory);
        if (this.aspectJAdvisorFactory == null) {
            this.aspectJAdvisorFactory = new ReflectiveAspectJAdvisorFactory(beanFactory);
        }
//用于管理切面通知的构建和配置,用于实现横切关注点的处理。
        this.aspectJAdvisorsBuilder = new AnnotationAwareAspectJAutoProxyCreator.BeanFactoryAspectJAdvisorsBuilderAdapter(beanFactory, this.aspectJAdvisorFactory);
    }

Você deve prestar atenção aqui e precisará ajustar o COI posteriormente (você pode ler o artigo original se não tiver certeza se já escreveu sobre isso antes).

Localização do código: org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator#postProcessBeforeInstantiation

public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    //缓存获取key
        Object cacheKey = this.getCacheKey(beanClass, beanName);
    //为空或不包含
        if (beanName == null || !this.targetSourcedBeans.contains(beanName)) {
      //如果有有解析直接返回
            if (this.advisedBeans.containsKey(cacheKey)) {
                return null;
            }
      //如果不是基础bean就直接跳过
            if (this.isInfrastructureClass(beanClass) || this.shouldSkip(beanClass, beanName)) {
                this.advisedBeans.put(cacheKey, Boolean.FALSE);
                return null;
            }
        }
    //如果beanName不是空
        if (beanName != null) {
            TargetSource targetSource = this.getCustomTargetSource(beanClass, beanName);
      //获取目标路劲不为空 通过工理方式获取并放到代理缓存中
            if (targetSource != null) {
                this.targetSourcedBeans.add(beanName);
                Object[] specificInterceptors = this.getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
                Object proxy = this.createProxy(beanClass, beanName, specificInterceptors, targetSource);
                this.proxyTypes.put(cacheKey, proxy.getClass());
                return proxy;
            }
        }

        return null;
    }

b3845d9c1e387fe415a3aed38a831d28.png

Em seguida, insira shouldSkip.Este método é usado principalmente para determinar se um bean específico deve ser ignorado.

Usando:org.springframework.aop.aspectj.autoproxy.AspectJAwareAdvisorAutoProxyCreator#shouldSkip

protected boolean shouldSkip(Class<?> beanClass, String beanName) {
        List<Advisor> candidateAdvisors = this.findCandidateAdvisors();
        Iterator var4 = candidateAdvisors.iterator();

        Advisor advisor;
        do {
            if (!var4.hasNext()) {
                return super.shouldSkip(beanClass, beanName);
            }

            advisor = (Advisor)var4.next();
        } while(!(advisor instanceof AspectJPointcutAdvisor) || !((AbstractAspectJAdvice)advisor.getAdvice()).getAspectName().equals(beanName));

        return true;
    }

Encontre o método findAdvisorBeans por meio dos findCandidateAdvisors acima para construir os vdisors do aspecto.

Localização do código: org.springframework.aop.framework.autoproxy.BeanFactoryAdvisorRetrievalHelper#findAdvisorBeans

//获取所有通知者
public List<Advisor> findAdvisorBeans() {
    //通知者名称列表
        String[] advisorNames = null;
    //同步锁
        synchronized(this) {
      //获取缓存列表
            advisorNames = this.cachedAdvisorBeanNames;
      //如果为空
            if (advisorNames == null) {
        //通过从工厂类中去获取(这里不深入前端ioc类似)
                advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this.beanFactory, Advisor.class, true, false);
        //然后进行赋值给缓存
                this.cachedAdvisorBeanNames = advisorNames;
            }
        }
    //长度为空直接返回空
        if (advisorNames.length == 0) {
            return new LinkedList();
        } else {
      //初始化列表
            List<Advisor> advisors = new LinkedList();
            String[] var3 = advisorNames;
            int var4 = advisorNames.length;
      //从ioc容器中获取所有的bean名称
            for(int var5 = 0; var5 < var4; ++var5) {
                String name = var3[var5];
                if (this.isEligibleBean(name)) {
                    if (this.beanFactory.isCurrentlyInCreation(name)) {
                        if (logger.isDebugEnabled()) {
                            logger.debug("Skipping currently created advisor '" + name + "'");
                        }
                    } else {
                        try {
                            advisors.add(this.beanFactory.getBean(name, Advisor.class));
                        } catch (BeanCreationException var11) {
                            Throwable rootCause = var11.getMostSpecificCause();
                            if (rootCause instanceof BeanCurrentlyInCreationException) {
                                BeanCreationException bce = (BeanCreationException)rootCause;
                                String bceBeanName = bce.getBeanName();
                                if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                                    if (logger.isDebugEnabled()) {
                                        logger.debug("Skipping advisor '" + name + "' with dependency on currently created bean: " + var11.getMessage());
                                    }
                                    continue;
                                }
                            }

                            throw var11;
                        }
                    }
                }
            }

            return advisors;
        }
    }

Em seguida, insira o método isAspect.

Localização do código: org.springframework.aop.aspectj.annotation.AbstractAspectJAdvisorFactory#isAspect

public boolean isAspect(Class<?> clazz) {
        return this.hasAspectAnnotation(clazz) && !this.compiledByAjc(clazz);
    }

    private boolean hasAspectAnnotation(Class<?> clazz) {
        return AnnotationUtils.findAnnotation(clazz, Aspect.class) != null;
    }

代码位置:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisors

public List<Advisor> getAdvisors(MetadataAwareAspectInstanceFactory aspectInstanceFactory) {
    //获取aspect类
        Class<?> aspectClass = aspectInstanceFactory.getAspectMetadata().getAspectClass();
    //获取切面类的名称
        String aspectName = aspectInstanceFactory.getAspectMetadata().getAspectName();
    //校验切面类
        this.validate(aspectClass);
    //通过包装类包装
        MetadataAwareAspectInstanceFactory lazySingletonAspectInstanceFactory = new LazySingletonAspectInstanceFactoryDecorator(aspectInstanceFactory);
        List<Advisor> advisors = new ArrayList();
    //获取所有切面类 @PointCut注解的方法
        Iterator var6 = this.getAdvisorMethods(aspectClass).iterator();

        while(var6.hasNext()) {
            Method method = (Method)var6.next();
      //循环解析切面中的方法
            Advisor advisor = this.getAdvisor(method, lazySingletonAspectInstanceFactory, advisors.size(), aspectName);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        if (!advisors.isEmpty() && lazySingletonAspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Advisor instantiationAdvisor = new ReflectiveAspectJAdvisorFactory.SyntheticInstantiationAdvisor(lazySingletonAspectInstanceFactory);
            advisors.add(0, instantiationAdvisor);
        }

        Field[] var12 = aspectClass.getDeclaredFields();
        int var13 = var12.length;

        for(int var14 = 0; var14 < var13; ++var14) {
            Field field = var12[var14];
            Advisor advisor = this.getDeclareParentsAdvisor(field);
            if (advisor != null) {
                advisors.add(advisor);
            }
        }

        return advisors;
    }

O seguinte é obter a classe de aspecto através disso, é claro, está em ordem.

Usando:org.springframework.aop.aspectj.annotation.ReflectiveAspectJAdvisorFactory#getAdvisorMethods

//获取一个切面类(aspectClass)中的方法列表
private List<Method> getAdvisorMethods(Class<?> aspectClass) {
        final List<Method> methods = new ArrayList();
        ReflectionUtils.doWithMethods(aspectClass, new MethodCallback() {
            public void doWith(Method method) throws IllegalArgumentException {
                if (AnnotationUtils.getAnnotation(method, Pointcut.class) == null) {
                    methods.add(method);
                }

            }
        });
        Collections.sort(methods, METHOD_COMPARATOR);
        return methods;
    }

A ordem do início ao fim é determinada durante a inicialização, que é a ordem quando executamos: Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}

static {
        CompoundComparator<Method> comparator = new CompoundComparator();
        comparator.addComparator(new ConvertingComparator(new InstanceComparator(new Class[]{Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class}), new Converter<Method, Annotation>() {
            public Annotation convert(Method method) {
                AspectJAnnotation<?> annotation = AbstractAspectJAdvisorFactory.findAspectJAnnotationOnMethod(method);
                return annotation != null ? annotation.getAnnotation() : null;
            }
        }));
        comparator.addComparator(new ConvertingComparator(new Converter<Method, String>() {
            public String convert(Method method) {
                return method.getName();
            }
        }));
        METHOD_COMPARATOR = comparator;
    }

Através da lógica anterior, finalmente vá para este InstantiationModelAwarePointcutAdvisorImpl para criar uma implementação específica de aop.

public InstantiationModelAwarePointcutAdvisorImpl(AspectJExpressionPointcut declaredPointcut, Method aspectJAdviceMethod, AspectJAdvisorFactory aspectJAdvisorFactory, MetadataAwareAspectInstanceFactory aspectInstanceFactory, int declarationOrder, String aspectName) {
    //切面定义切点
        this.declaredPointcut = declaredPointcut;
    //切面对象
        this.declaringClass = aspectJAdviceMethod.getDeclaringClass();
    //切面方法名称
        this.methodName = aspectJAdviceMethod.getName();
    //切面参数类型
        this.parameterTypes = aspectJAdviceMethod.getParameterTypes();
    //切面方法对象
        this.aspectJAdviceMethod = aspectJAdviceMethod;
    //asperctj通知工厂
        this.aspectJAdvisorFactory = aspectJAdvisorFactory;
    //aspect实例工厂
        this.aspectInstanceFactory = aspectInstanceFactory;
    //切面具体顺序
        this.declarationOrder = declarationOrder;
    //切面名称
        this.aspectName = aspectName;
    //判断是否需要延迟加载
        if (aspectInstanceFactory.getAspectMetadata().isLazilyInstantiated()) {
            Pointcut preInstantiationPointcut = Pointcuts.union(aspectInstanceFactory.getAspectMetadata().getPerClausePointcut(), this.declaredPointcut);
            this.pointcut = new InstantiationModelAwarePointcutAdvisorImpl.PerTargetInstantiationModelPointcut(this.declaredPointcut, preInstantiationPointcut, aspectInstanceFactory);
            this.lazy = true;
        } else {
            this.pointcut = this.declaredPointcut;
            this.lazy = false;
            this.instantiatedAdvice = this.instantiateAdvice(this.declaredPointcut);
        }

    }

O que foi dito acima é, em última análise, a criação do bean.

564b845659ead900113a275d01bc6ecc.png

A inicialização light é um código tão grande (algumas partes são omitidas ~), realmente ~

proxy dinâmico

e29c6ceb366f425e768d619082cbb7e3.png

O método de proxy padrão do Spring é o proxy dinâmico JDK, mas se o objeto de destino não implementar a interface, ele mudará automaticamente para o proxy dinâmico CGLIB. Isso garante que a função AOP possa ser usada na maioria dos casos sem a necessidade de configurar manualmente o método proxy.

3492c461555ae83ee5b39a4344e1d4f1.png

Após a inicialização anterior, quando o método for chamado, o Spring terá uma cadeia de interceptação por padrão para conectar o agente e o aspecto, e combinar o agente por meio de reflexão de invocação.

Localização do código: org.springframework.aop.framework.ReflectiveMethodInvocation#proceed

//AOP 拦截器链可以按照顺序依次执行拦截器或拦截器通知的逻辑
public Object proceed() throws Throwable {
    //最后一个拦截器会进入
        if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      //返回切点
            return this.invokeJoinpoint();
        } else {
      //获取需要运行的拦截器
            Object interceptorOrInterceptionAdvice = this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
            if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
                InterceptorAndDynamicMethodMatcher dm = (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
                return dm.methodMatcher.matches(this.method, this.targetClass, this.arguments) ? dm.interceptor.invoke(this) : this.proceed();
            } else {
        //执行拦截器方法
                return ((MethodInterceptor)interceptorOrInterceptionAdvice).invoke(this);
            }
        }
    }

O método acima sempre foi executado recursivamente.ec39a62d91120810516859d1dc800c98.png

Então digite: invocar método

Localização do código: org.springframework.aop.interceptor.ExposeInvocationInterceptor#invoke

public Object invoke(MethodInvocation mi) throws Throwable {
        MethodInvocation oldInvocation = (MethodInvocation)invocation.get();
        invocation.set(mi);

        Object var3;
        try {
            var3 = mi.proceed();
        } finally {
            invocation.set(oldInvocation);
        }

        return var3;
    }

4cc800cccc03edba5f32c0cc9f13db04.png

Você pode ver que este é o interceptador que é finalmente executado, e o padrão de design da cadeia de responsabilidade é usado para fazer chamadas recursivas. A ordem é: Around.class, Before.class, After.class, AfterReturning.class, AfterThrowing.class} que é consistente com a ordem de inicialização.

Quanto aos alunos que não entendem o padrão de design da cadeia de responsabilidade, consulte: Padrões de Design - Padrões de Cadeia de Responsabilidade

afinal

    Spring AOP é relativamente difícil de depurar, especialmente vários agentes. Se você não entende este artigo, leia o artigo de referência abaixo para obter mais referências. Claro, se você puder entender, é recomendável dar uma olhada nos logs em algumas estruturas de código aberto ou em algum middleware ou componentes relacionados integrados por spring, springboot e springcloud. Vale a pena dar uma olhada. Essas lógicas de implementação e princípios são na verdade os mesmos. Claro, o que mais tenho visto em meus trabalhos públicos é a implementação de ferramentas como logs globais e interceptadores customizados como criptografia.

referência:

https://cloud.tencent.com/developer/article/1512235

https://www.cnblogs.com/FatalFlower/p/15572344.html

https://blog.csdn.net/pengjianglilive/article/details/109608986

Acho que você gosta

Origin blog.csdn.net/qq_16498553/article/details/132267836
Recomendado
Clasificación