飞道的博客

求求你,下次面试别再问我什么是AOP了!

519人阅读  评论(0)

Aop相关阅读

阅读本文之前,需要先掌握下面3篇文章内容,不然会比较吃力。

  1. Spring系列第15篇:代理详解(java动态代理&CGLIB代理)

  2. Spring系列第30篇:jdk动态代理和cglib代理

  3. Spring系列第31篇:Aop概念详解

  4. Spring系列第32篇:AOP核心源码、原理详解

本文继续Aop。

AOP创建代理的方式主要分为2大类

手动方式

也称为手动的方式,需要通过硬编码一个个创建代理。

自动化的方式

也称为批量的方式,批量的方式用在spring环境中,通过bean后置处理器来对符合条件的bean创建代理

手动的方式基本上是采用硬编码的方式,相对来说更灵活一些,可以脱离spring环境使用,而自动化的方式主要用在spring环境中,和spring集成起来更容易一些,更强大一些。

AOP创建代理相关的类

左边的ProxyCreatorSupport下面的都是手动的方式,有3个类。

右边的AbstractAutoProxyCreator下面挂的都是自动创建代理的方式,主要有5个实现类。

手动3种方式

ProxyFactory方式

这种是硬编码的方式,可以脱离spring直接使用,用到的比较多,自动化方式创建代理中都是依靠ProxyFactory来实现的,所以这种方式的原理大家一定要了解,上篇文章中已经有介绍过了,不清楚的可以去看一下:Spring系列第32篇:AOP核心源码、原理详解

AspectJProxyFactory方式

AspectJ是一个面向切面的框架,是目前最好用,最方便的AOP框架,Spring将其集成进来了,通过Aspectj提供的一些功能实现aop代理非常方便,下篇文章将详解。

ProxyFactoryBean方式

Spring环境中给指定的bean创建代理的一种方式,本文主要介绍这个。

ProxyFactoryBean

这个类实现了一个接口FactoryBeanFactoryBean不清楚的可以看一下:Spring系列第5篇:创建bean实例这些方式你们都知道?

ProxyFactoryBean就是通过FactoryBean的方式来给指定的bean创建一个代理对象。

创建代理,有3个信息比较关键:

  1. 需要增强的功能,这个放在通知(Advice)中实现

  2. 目标对象(target):表示你需要给哪个对象进行增强

  3. 代理对象(proxy):将增强的功能和目标对象组合在一起,然后形成的一个代理对象,通过代理对象来访问目标对象,起到对目标对象增强的效果。

使用ProxyFactoryBean也是围绕着3部分来的,ProxyFactoryBean使用的步骤:


   
  1. 1.创建ProxyFactoryBean对象
  2. 2.通过ProxyFactoryBean.setTargetName设置目标对象的bean名称,目标对象是spring容器中的一个bean
  3. 3.通过ProxyFactoryBean。setInterceptorNames添加需要增强的通知
  4. 4.将ProxyFactoryBean注册到Spring容器,假设名称为proxyBean
  5. 5.从Spring查找名称为proxyBean的bean,这个bean就是生成好的代理对象

上案例。

来个类Service1


   
  1. package com.javacode2018.aop.demo8.test1;
  2. public class Service1 {
  3.     public void m1() {
  4.         System.out. println( "我是 m1 方法");
  5.     }
  6.     public void m2() {
  7.         System.out. println( "我是 m2 方法");
  8.     }
  9. }

需求

在spring容器中注册上面这个类的bean,名称为service1,通过代理的方式来对这个bean进行增强,来2个通知

一个前置通知:在调用service1中的任意方法之前,输出一条信息:准备调用xxxx方法

一个环绕通知:复制统计所有方法的耗时。

下面是代码的实现


   
  1. package com.javacode2018.aop.demo8.test1;
  2. import org.aopalliance.intercept.MethodInterceptor;
  3. import org.aopalliance.intercept.MethodInvocation;
  4. import org.springframework.aop.MethodBeforeAdvice;
  5. import org.springframework.aop.framework.ProxyFactoryBean;
  6. import org.springframework.context.annotation.Bean;
  7. import org.springframework.context.annotation.Configuration;
  8. import org.springframework.lang.Nullable;
  9. import java.lang.reflect.Method;
  10. @Configuration
  11. public class MainConfig1 {
  12.      //注册目标对象
  13.     @Bean
  14.     public Service1 service1() {
  15.          return  new Service1();
  16.     }
  17.      //注册一个前置通知
  18.     @Bean
  19.     public MethodBeforeAdvice beforeAdvice() {
  20.         MethodBeforeAdvice advice =  new MethodBeforeAdvice() {
  21.             @Override
  22.             public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  23.                 System.out. println( "准备调用:" + method);
  24.             }
  25.         };
  26.          return advice;
  27.     }
  28.      //注册一个后置通知
  29.     @Bean
  30.     public MethodInterceptor costTimeInterceptor() {
  31.         MethodInterceptor methodInterceptor =  new MethodInterceptor() {
  32.             @Override
  33.             public Object invoke(MethodInvocation invocation) throws Throwable {
  34.                 long starTime = System.nanoTime();
  35.                 Object result = invocation.proceed();
  36.                 long endTime = System.nanoTime();
  37.                 System.out. println(invocation.getMethod() +  ",耗时(纳秒):" + (endTime - starTime));
  38.                  return result;
  39.             }
  40.         };
  41.          return methodInterceptor;
  42.     }
  43.      //注册ProxyFactoryBean
  44.     @Bean
  45.     public ProxyFactoryBean service1Proxy() {
  46.          //1.创建ProxyFactoryBean
  47.         ProxyFactoryBean proxyFactoryBean =  new ProxyFactoryBean();
  48.          //2.设置目标对象的bean名称
  49.         proxyFactoryBean.setTargetName( "service1");
  50.          //3.设置拦截器的bean名称列表,此处2个(advice1和advice2)
  51.         proxyFactoryBean.setInterceptorNames( "beforeAdvice""costTimeInterceptor");
  52.          return proxyFactoryBean;
  53.     }
  54. }

下面启动spring容器,并获取代理对象


   
  1. AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig1.class);
  2. //获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
  3. Service1 bean = context.getBean( "service1Proxy", Service1.class);
  4. System.out. println( "----------------------");
  5. //调用代理的方法
  6. bean.m1();
  7. System.out. println( "----------------------");
  8. //调用代理的方法
  9. bean.m2();

运行输出


   
  1. ----------------------
  2. 准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
  3. 我是 m1 方法
  4. public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒): 8680400
  5. ----------------------
  6. 准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
  7. 我是 m2 方法
  8. public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒): 82400

从输出中可以看到,目标对象service1已经被增强了。

ProxyFactoryBean中的interceptorNames

interceptorNames用来指定拦截器的bean名称列表,常用的2种方式。

  • 批量的方式

  • 非批量的方式

批量的方式

使用方法

proxyFactoryBean.setInterceptorNames("需要匹配的bean名称*");

需要匹配的bean名称后面跟上一个*,可以用来批量的匹配,如:interceptor*,此时spring会从容器中找到下面2中类型的所有bean,bean名称以interceptor开头的将作为增强器


   
  1. org.springframework.aop.Advisor
  2. org.aopalliance.intercept.Interceptor

这个地方使用的时候需要注意,批量的方式注册的时候,如果增强器的类型不是上面2种类型的,比如下面3种类型通知,我们需要将其包装为Advisor才可以,而MethodInterceptorInterceptor类型的,可以不用包装为Advisor类型的。


   
  1. MethodBeforeAdvice(方法前置通知)
  2. AfterReturningAdvice(方法后置通知)
  3. ThrowsAdvice(异常通知)

下面来个案例感受一下。

案例

下面批量注册2个增强器。


   
  1. package com.javacode2018.aop.demo8.test2;
  2. import com.javacode2018.aop.demo8.test1.Service1;
  3. import org.aopalliance.intercept.MethodInterceptor;
  4. import org.aopalliance.intercept.MethodInvocation;
  5. import org.springframework.aop.Advisor;
  6. import org.springframework.aop.MethodBeforeAdvice;
  7. import org.springframework.aop.framework.ProxyFactoryBean;
  8. import org.springframework.aop.support.DefaultPointcutAdvisor;
  9. import org.springframework.context.annotation.Bean;
  10. import org.springframework.lang.Nullable;
  11. import java.lang.reflect.Method;
  12. public class MainConfig2 {
  13.      //注册目标对象
  14.     @Bean
  15.     public Service1 service1() {
  16.          return  new Service1();
  17.     }
  18.      //定义一个增强器:interceptor1,内部是一个前置通知,需要将其包装为Advisor类型的
  19.     @Bean
  20.     public Advisor interceptor1() {
  21.         MethodBeforeAdvice advice =  new MethodBeforeAdvice() {
  22.             @Override
  23.             public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  24.                 System.out. println( "准备调用:" + method);
  25.             }
  26.         };
  27.         DefaultPointcutAdvisor advisor =  new DefaultPointcutAdvisor();
  28.         advisor.setAdvice(advice);
  29.          return advisor;
  30.     }
  31.      //定义一个增强器:interceptor2
  32.     @Bean
  33.     public MethodInterceptor interceptor2() {
  34.         MethodInterceptor methodInterceptor =  new MethodInterceptor() {
  35.             @Override
  36.             public Object invoke(MethodInvocation invocation) throws Throwable {
  37.                 long starTime = System.nanoTime();
  38.                 Object result = invocation.proceed();
  39.                 long endTime = System.nanoTime();
  40.                 System.out. println(invocation.getMethod() +  ",耗时(纳秒):" + (endTime - starTime));
  41.                  return result;
  42.             }
  43.         };
  44.          return methodInterceptor;
  45.     }
  46.      //注册ProxyFactoryBean
  47.     @Bean
  48.     public ProxyFactoryBean service1Proxy() {
  49.          //1.创建ProxyFactoryBean
  50.         ProxyFactoryBean proxyFactoryBean =  new ProxyFactoryBean();
  51.          //2.设置目标对象的bean名称
  52.         proxyFactoryBean.setTargetName( "service1");
  53.          //3.设置拦截器的bean名称列表,此处批量注册
  54.         proxyFactoryBean.setInterceptorNames( "interceptor*");  //@1
  55.          return proxyFactoryBean;
  56.     }
  57. }

上面定义了2个增强器:

interceptor1:前置通知,包装为Advisor类型了

interceptor2:环绕通知,MethodInterceptor类型的

测试代码


   
  1. @Test
  2. public void test2() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig2.class);
  4.      //获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
  5.     Service1 bean = context.getBean( "service1Proxy", Service1.class);
  6.     System.out. println( "----------------------");
  7.      //调用代理的方法
  8.     bean.m1();
  9.     System.out. println( "----------------------");
  10.      //调用代理的方法
  11.     bean.m2();
  12. }

运行输出


   
  1. ----------------------
  2. 准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
  3. 我是 m1 方法
  4. public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒): 10326200
  5. ----------------------
  6. 准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
  7. 我是 m2 方法
  8. public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒): 52000

非批量的方式

用法

非批量的方式,需要注册多个增强器,需明确的指定多个增强器的bean名称,多个增强器按照参数中指定的顺序执行,如

proxyFactoryBean.setInterceptorNames("advice1","advice2");

advice1、advice2对应的bean类型必须为下面列表中指定的类型,类型这块比匹配的方式范围广一些


   
  1. MethodBeforeAdvice(方法前置通知)
  2. AfterReturningAdvice(方法后置通知)
  3. ThrowsAdvice(异常通知)
  4. org.aopalliance.intercept.MethodInterceptor(环绕通知)
  5. org.springframework.aop.Advisor(顾问)

下面来个案例。

案例

这次给service1来3个通知:前置、环绕、后置


   
  1. package com.javacode2018.aop.demo8.test3;
  2. import com.javacode2018.aop.demo8.test1.Service1;
  3. import org.aopalliance.intercept.MethodInterceptor;
  4. import org.aopalliance.intercept.MethodInvocation;
  5. import org.springframework.aop.AfterReturningAdvice;
  6. import org.springframework.aop.MethodBeforeAdvice;
  7. import org.springframework.aop.framework.ProxyFactoryBean;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.lang.Nullable;
  10. import java.lang.reflect.Method;
  11. public class MainConfig3 {
  12.      //注册目标对象
  13.     @Bean
  14.     public Service1 service1() {
  15.          return  new Service1();
  16.     }
  17.      //定义一个前置通知
  18.     @Bean
  19.     public MethodBeforeAdvice methodBeforeAdvice() {
  20.         MethodBeforeAdvice methodBeforeAdvice =  new MethodBeforeAdvice() {
  21.             @Override
  22.             public void before(Method method, Object[] args, @Nullable Object target) throws Throwable {
  23.                 System.out. println( "准备调用:" + method);
  24.             }
  25.         };
  26.          return methodBeforeAdvice;
  27.     }
  28.      //定义一个环绕通知
  29.     @Bean
  30.     public MethodInterceptor methodInterceptor() {
  31.         MethodInterceptor methodInterceptor =  new MethodInterceptor() {
  32.             @Override
  33.             public Object invoke(MethodInvocation invocation) throws Throwable {
  34.                 long starTime = System.nanoTime();
  35.                 Object result = invocation.proceed();
  36.                 long endTime = System.nanoTime();
  37.                 System.out. println(invocation.getMethod() +  ",耗时(纳秒):" + (endTime - starTime));
  38.                  return result;
  39.             }
  40.         };
  41.          return methodInterceptor;
  42.     }
  43.      //定义一个后置通知
  44.     @Bean
  45.     public AfterReturningAdvice afterReturningAdvice() {
  46.         AfterReturningAdvice afterReturningAdvice =  new AfterReturningAdvice() {
  47.             @Override
  48.             public void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable {
  49.                 System.out. println(method +  ",执行完毕!");
  50.             }
  51.         };
  52.          return afterReturningAdvice;
  53.     }
  54.      //注册ProxyFactoryBean
  55.     @Bean
  56.     public ProxyFactoryBean service1Proxy() {
  57.          //1.创建ProxyFactoryBean
  58.         ProxyFactoryBean proxyFactoryBean =  new ProxyFactoryBean();
  59.          //2.设置目标对象的bean名称
  60.         proxyFactoryBean.setTargetName( "service1");
  61.          //3.设置拦截器的bean名称列表,此处批量注册
  62.         proxyFactoryBean.setInterceptorNames( "methodBeforeAdvice""methodInterceptor""afterReturningAdvice");
  63.          return proxyFactoryBean;
  64.     }
  65. }

测试代码


   
  1. @Test
  2. public void test3() {
  3.     AnnotationConfigApplicationContext context =  new AnnotationConfigApplicationContext(MainConfig3.class);
  4.      //获取代理对象,代理对象bean的名称为注册ProxyFactoryBean的名称,即:service1Proxy
  5.     Service1 bean = context.getBean( "service1Proxy", Service1.class);
  6.     System.out. println( "----------------------");
  7.      //调用代理的方法
  8.     bean.m1();
  9.     System.out. println( "----------------------");
  10.      //调用代理的方法
  11.     bean.m2();
  12. }

运行输出


   
  1. ----------------------
  2. 准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m1()
  3. 我是 m1 方法
  4. public void com.javacode2018.aop.demo8.test1.Service1.m1(),执行完毕!
  5. public void com.javacode2018.aop.demo8.test1.Service1.m1(),耗时(纳秒): 12724100
  6. ----------------------
  7. 准备调用:public void com.javacode2018.aop.demo8.test1.Service1.m2()
  8. 我是 m2 方法
  9. public void com.javacode2018.aop.demo8.test1.Service1.m2(),执行完毕!
  10. public void com.javacode2018.aop.demo8.test1.Service1.m2(),耗时(纳秒): 76700

源码解析

重点在于下面这个方法

org.springframework.aop.framework.ProxyFactoryBean#getObject

源码:


   
  1. public Object getObject() throws BeansException {
  2.      //初始化advisor(拦截器)链
  3.     initializeAdvisorChain();
  4.      //是否是单例
  5.      if (isSingleton()) {
  6.          //创建单例代理对象
  7.          return getSingletonInstance();
  8.     }
  9.      else {
  10.          //创建多例代理对象
  11.          return newPrototypeInstance();
  12.     }
  13. }

initializeAdvisorChain方法,用来初始化advisor(拦截器)链,是根据interceptorNames配置,找到spring容器中符合条件的拦截器,将其放入创建aop代理的配置中


   
  1. private synchronized void initializeAdvisorChain() throws AopConfigException, BeansException {
  2.      if (!ObjectUtils.isEmpty(this.interceptorNames)) {
  3.          // 轮询 interceptorNames
  4.          for (String name : this.interceptorNames) {
  5.              //批量注册的方式:判断name是否以*结尾
  6.              if (name.endsWith(GLOBAL_SUFFIX)) {
  7.                  //@1:从容器中匹配查找匹配的增强器,将其添加到aop配置中
  8.                 addGlobalAdvisor((ListableBeanFactory) this.beanFactory,
  9.                                  name.substring( 0, name.length() - GLOBAL_SUFFIX.length()));
  10.             }
  11.              else {
  12.                  //非匹配的方式:按照name查找bean,将其包装为Advisor丢到aop配置中
  13.                 Object advice;
  14.                  //从容器中查找bean
  15.                 advice = this.beanFactory.getBean(name);
  16.                  //@2:将advice添加到拦截器列表中
  17.                 addAdvisorOnChainCreation(advice, name);
  18.             }
  19.         }
  20.     }
  21. }

@1:addGlobalAdvisor批量的方式Advisor,看一下源码,比较简单


   
  1. /**
  2.  * 添加所有全局拦截器和切入点,
  3.  * 容器中所有类型为Advisor/Interceptor的bean,bean名称prefix开头的都会将其添加到拦截器链中
  4.  */
  5. private void addGlobalAdvisor(ListableBeanFactory beanFactory, String prefix) {
  6.      //获取容器中所有类型为Advisor的bean
  7.     String[] globalAdvisorNames =
  8.         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Advisor.class);
  9.      //获取容器中所有类型为Interceptor的bean
  10.     String[] globalInterceptorNames =
  11.         BeanFactoryUtils.beanNamesForTypeIncludingAncestors(beanFactory, Interceptor.class);
  12.     List<Object> beans =  new ArrayList<>(globalAdvisorNames.length + globalInterceptorNames.length);
  13.     Map<Object, String> names =  new HashMap<>(beans.size());
  14.      for (String name : globalAdvisorNames) {
  15.         Object bean = beanFactory.getBean(name);
  16.         beans.add(bean);
  17.         names.put(bean, name);
  18.     }
  19.      for (String name : globalInterceptorNames) {
  20.         Object bean = beanFactory.getBean(name);
  21.         beans.add(bean);
  22.         names.put(bean, name);
  23.     }
  24.      //对beans进行排序,可以实现Ordered接口,排序规则:order asc
  25.     AnnotationAwareOrderComparator.sort(beans);
  26.      for (Object bean : beans) {
  27.         String name = names.get(bean);
  28.          //判断bean是否已prefix开头
  29.          if (name.startsWith(prefix)) {
  30.              //将其添加到拦截器链中
  31.             addAdvisorOnChainCreation(bean, name);
  32.         }
  33.     }
  34. }

@2:addAdvisorOnChainCreation


   
  1. private void addAdvisorOnChainCreation(Object next, String name) {
  2.      //namedBeanToAdvisor用来将bean转换为advisor
  3.     Advisor advisor = namedBeanToAdvisor(next);
  4.      //将advisor添加到拦截器链中
  5.     addAdvisor(advisor);
  6. }

namedBeanToAdvisor方法


   
  1. private AdvisorAdapterRegistry advisorAdapterRegistry =  new DefaultAdvisorAdapterRegistry();
  2. private Advisor namedBeanToAdvisor(Object next) {
  3.      //将对象包装为Advisor对象
  4.      return this.advisorAdapterRegistry.wrap(next);
  5. }

AdvisorAdapterRegistry不清楚的,看一下上一篇文章:Spring系列第32篇:AOP核心源码、原理详解

advisorAdapterRegistry#wrap方法将adviceObject包装为Advisor对象,代码如下,比较简单


   
  1. public Advisor wrap(Object adviceObject) throws UnknownAdviceTypeException {
  2.      if (adviceObject instanceof Advisor) {
  3.          return (Advisor) adviceObject;
  4.     }
  5.      if (!(adviceObject instanceof Advice)) {
  6.         throw  new UnknownAdviceTypeException(adviceObject);
  7.     }
  8.     Advice advice = (Advice) adviceObject;
  9.      if (advice instanceof MethodInterceptor) {
  10.          return  new DefaultPointcutAdvisor(advice);
  11.     }
  12.      for (AdvisorAdapter adapter : this.adapters) {
  13.          if (adapter.supportsAdvice(advice)) {
  14.              return  new DefaultPointcutAdvisor(advice);
  15.         }
  16.     }
  17.     throw  new UnknownAdviceTypeException(advice);
  18. }

总结

  1. Spring中创建代理主要分为2种:手动方式和自动化的方式

  2. 手动方式采用硬编码的方式,一次只能给一个目标对象创建代理对象,相对来说灵活一下,对开发者来说更灵活一些,通常可以独立spring环境使用;自动化的方式主要在spring环境中使用,通常是匹配的方式来为符合条件的目标bean创建代理,使用起来更简单一些

  3. 本文介绍的ProxyFactoryBean用来在spring环境中给指定的bean创建代理对象,用到的不是太多,大家可以作为了解即可

案例源码

https://gitee.com/javacode2018/spring-series

路人甲java所有案例代码以后都会放到这个上面,大家watch一下,可以持续关注动态。

Spring系列

  1. Spring系列第1篇:为何要学spring?

  2. Spring系列第2篇:控制反转(IoC)与依赖注入(DI)

  3. Spring系列第3篇:Spring容器基本使用及原理

  4. Spring系列第4篇:xml中bean定义详解(-)

  5. Spring系列第5篇:创建bean实例这些方式你们都知道?

  6. Spring系列第6篇:玩转bean scope,避免跳坑里!

  7. Spring系列第7篇:依赖注入之手动注入

  8. Spring系列第8篇:自动注入(autowire)详解,高手在于坚持

  9. Spring系列第9篇:depend-on到底是干什么的?

  10. Spring系列第10篇:primary可以解决什么问题?

  11. Spring系列第11篇:bean中的autowire-candidate又是干什么的?

  12. Spring系列第12篇:lazy-init:bean延迟初始化

  13. Spring系列第13篇:使用继承简化bean配置(abstract & parent)

  14. Spring系列第14篇:lookup-method和replaced-method比较陌生,怎么玩的?

  15. Spring系列第15篇:代理详解(Java动态代理&cglib代理)?

  16. Spring系列第16篇:深入理解java注解及spring对注解的增强(预备知识)

  17. Spring系列第17篇:@Configration和@Bean注解详解(bean批量注册)

  18. Spring系列第18篇:@ComponentScan、@ComponentScans详解(bean批量注册)

  19. Spring系列第18篇:@import详解(bean批量注册)

  20. Spring系列第20篇:@Conditional通过条件来控制bean的注册

  21. Spring系列第21篇:注解实现依赖注入(@Autowired、@Resource、@Primary、@Qulifier)

  22. Spring系列第22篇:@Scope、@DependsOn、@ImportResource、@Lazy 详解

  23. Spring系列第23篇:Bean生命周期详解

  24. Spring系列第24篇:父子容器详解

  25. Spring系列第25篇:@Value【用法、数据来源、动态刷新】

  26. Spring系列第26篇:国际化详解

  27. Spring系列第27篇:spring事件机制详解

  28. Spring系列第28篇:Bean循环依赖详解

  29. Spring系列第29篇:BeanFactory扩展(BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor)

  30. Spring系列第30篇:jdk动态代理和cglib代理

  31. Spring系列第31篇:aop概念详解

更多好文章

  1. Java高并发系列(共34篇)

  2. MySql高手系列(共27篇)

  3. Maven高手系列(共10篇)

  4. Mybatis系列(共12篇)

  5. 聊聊db和缓存一致性常见的实现方式

  6. 接口幂等性这么重要,它是什么?怎么实现?

  7. 泛型,有点难度,会让很多人懵逼,那是因为你没有看这篇文章!

世界上最好的关系是相互成就,点赞转发 感恩开心????

路人甲java

▲长按图片识别二维码关注

路人甲Java:工作10年的前阿里P7,所有文章以系列的方式呈现,带领大家成为java高手,目前已出:java高并发系列、mysql高手系列、Maven高手系列、mybatis系列、spring系列,正在连载springcloud系列,欢迎关注!


转载:https://blog.csdn.net/likun557/article/details/106990856
查看评论
* 以上用户言论只代表其个人观点,不代表本网站的观点或立场