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:
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.
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
Inicialização AOP
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)
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.
Configure anotações de classe de inicialização:
@EnableAspectJAutoProxy
Digite @EnableAspectJAutoProxy e descubra que AspectJAutoProxyRegistrar.class é importado por meio de @import.
Localização do código: org.springframework.context.annotation.AspectJAutoProxyRegistrar#registerBeanDefinitions
Diagrama de Classe:
@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);
}
}
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:
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.
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;
}
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.
A inicialização light é um código tão grande (algumas partes são omitidas ~), realmente ~
proxy dinâmico
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.
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.
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;
}
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