【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)(上)

简介: 【小家Spring】Spring AOP的核心类:AbstractAdvisorAutoProxy自动代理创建器深度剖析(AnnotationAwareAspectJAutoProxyCreator)(上)

前言


上篇博文:

【小家Spring】面向切面编程之—Spring AOP的原理讲解以及源码分析(Cannot find current proxy: Set ‘exposeProxy’ property on )

已经刚刚结合实例,介绍了Spring AOP的过程以及对源码进行了逐步分析~


但是这中间当时事忽略了整个Spring AOP最为核心的一个类:自动代理创建器。


本文将主要以AnnotationAwareAspectJAutoProxyCreator为引线,好好聊聊自动代理创建器是做什么的,具体做了什么,以及Spring为我们提供的其它几个Creator:DefaultAdvisorAutoProxyCreator,InfrastructureAdvisorAutoProxyCreator和AspectJAwareAdvisorAutoProxyCreator,有何区别和对应的实用场景


image.png

可以看出来,所有的创建器都是AbstractAutoProxyCreator该抽象类的子类~


image.png


当系统比较复杂,或者中需要进行aop进行织入的bean较多时,简单采用ProxyFacotryBean无疑会增加很多工作量(因为每个Bean都得手动写一个)。另外由于要从ProxyFactoryBean获得代理对象,也会使应用和Spring之间的耦合度增加,并且很多时候可维护性不强(我个人认为耦合这个不是最主要的原因,毕竟现在Spring已成为实际上的规范了)。


譬如,我们需要给bean名字满足“*service”的bean每个方法增加事务功能,如果采用ProxyFacotryBean必须为每个以service结尾的bean增加一个配置(工作量巨大),若需要修改,这个后果是灾难性的。


这样的情况下,自动代理的方式就能发挥它巨大的优势了,牛刀小试一把,上面案例只需下面一个类就搞定了(这里以功能最弱的BeanNameAutoProxyCreator为例):

<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
  <property name="beanNames"><value>*service</value></property>
  <property name="interceptorNames">
    <list>
      <!-- 这里可以写具体的Interceptor的BeanName -->
      <value>myInterceptor</value>
    </list>
  </property>
</bean>


Spring中自动创建代理器(分类)


在内部,Spring使用BeanPostProcessor让自动生成代理。基于BeanPostProcessor的自动代理创建器的实现类,将根据一些规则在容器实例化Bean时为匹配的Bean生成代理实例。代理创建器可以分为三类:


  1. 基于Bean配置名规则的自动代理生成器:允许为一组特定配置名的Bean自动创建代理实例的代理创建器,实现类为BeanNameAutoProxyCreator
  2. 基于Advisor匹配机制的自动代理创建器它会对容器中的所有Advisor进行扫描,自动将这些切面应用到匹配的Bean中,实现类是DefaultAdvisorAutoProxyCreator(它也支持前缀匹配)
  3. 基于Bean中AspectJ注解的自动代理生成器:为包含AspectJ注解的切入的Bean自动创建代理实例


AbstractAutoProxyCreator


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
    ...
}


首先它继承自:ProxyProcessorSupport:这个类应该很熟了,我们之前介绍过的三大创建代理对象的工厂ProxyFactoryBean、ProxyFactory、AspectJProxyFactory都是继承自此类的

参考:【小家Spring】面向切面编程Spring AOP创建代理的方式:ProxyFactoryBean、ProxyFactory、AspectJProxyFactory(JDK Proxy和CGLIB)


这里还是简单的科普一下吧:

// 显然,它是个ProxyConfig 拥有AOP的基本配置
public class ProxyProcessorSupport extends ProxyConfig implements Ordered, BeanClassLoaderAware, AopInfrastructureBean {
  ...
  // 这是它最重要的一个方法:就是把该bean所有的实现的接口 都作用在ProxyFactory 上 当然是有过滤得
  protected void evaluateProxyInterfaces(Class<?> beanClass, ProxyFactory proxyFactory) {
    // 拿到该类所有实现的接口们~~~~
    Class<?>[] targetInterfaces = ClassUtils.getAllInterfacesForClass(beanClass, getProxyClassLoader());
    // 标记:是否存在“有问题的”代理接口  默认是false
    boolean hasReasonableProxyInterface = false;
    for (Class<?> ifc : targetInterfaces) {
      //判断这些接口是否是“有问题的”:既我们需要处理的
      if (!isConfigurationCallbackInterface(ifc) && !isInternalLanguageInterface(ifc) &&
          ifc.getMethods().length > 0) {
        hasReasonableProxyInterface = true;
        break;
      }
    }
    // 说明除开哪些接口外,但凡有一个有用的接口,就add进去(这样就会采用JDK的动态代理了)
    if (hasReasonableProxyInterface) {
      for (Class<?> ifc : targetInterfaces) {
        proxyFactory.addInterface(ifc);
      }
    }
    // 否则直接采用CGLIB
    else {
      proxyFactory.setProxyTargetClass(true);
    }
  }
  // InitializingBean...Aware接口的子接口等等这些回调性质的接口
  protected boolean isConfigurationCallbackInterface(Class<?> ifc) {
    return (InitializingBean.class == ifc || DisposableBean.class == ifc || Closeable.class == ifc ||
        AutoCloseable.class == ifc || ObjectUtils.containsElement(ifc.getInterfaces(), Aware.class));
  }
  // 接口名称为这些的  也就是spring aop自己的东西
  protected boolean isInternalLanguageInterface(Class<?> ifc) {
    return (ifc.getName().equals("groovy.lang.GroovyObject") ||
        ifc.getName().endsWith(".cglib.proxy.Factory") ||
        ifc.getName().endsWith(".bytebuddy.MockAccess"));
  }
}


AbstractAutoProxyCreator是对自动代理创建器的一个抽象实现。最重要的是,它实现了SmartInstantiationAwareBeanPostProcessor接口,因此会介入到Spring IoC容器Bean实例化的过程,因此由此为入口进行展开~


SmartInstantiationAwareBeanPostProcessor继承自InstantiationAwareBeanPostProcessor继承自BeanPostProcessor 所以它需要实现的方法有很多~~~

这里重点提一个方法(如下图):在bean被crate之前,先会执行所有的InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation,谁第一个返回了不为null的Bean,后面就都不会执行了 。然后会再执行BeanPostProcessor#postProcessAfterInitialization 就完事了


image.png


按照执行顺序,我们来看看AbstractAutoProxyCreator的实现:


public abstract class AbstractAutoProxyCreator extends ProxyProcessorSupport
    implements SmartInstantiationAwareBeanPostProcessor, BeanFactoryAware {
  // 实现类就是我们熟悉的它: DefaultAdvisorAdapterRegistry
  private AdvisorAdapterRegistry advisorAdapterRegistry = GlobalAdvisorAdapterRegistry.getInstance(); 
  // 目标源的创建器。它有一个方法getTargetSource(Class<?> beanClass, String beanName)
  // 两个实现类:QuickTargetSourceCreator和LazyInitTargetSourceCreator
  // 它的具体使用 后面有详解
  @Nullable
  private TargetSourceCreator[] customTargetSourceCreators;
  @Nullable
  private BeanFactory beanFactory;
  ...
  private final Set<String> targetSourcedBeans = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
  private final Set<Object> earlyProxyReferences = Collections.newSetFromMap(new ConcurrentHashMap<>(16));
  private final Map<Object, Class<?>> proxyTypes = new ConcurrentHashMap<>(16);
  private final Map<Object, Boolean> advisedBeans = new ConcurrentHashMap<>(256);
  ...
  // 可以自己指定Registry 
  public void setAdvisorAdapterRegistry(AdvisorAdapterRegistry advisorAdapterRegistry) {
    this.advisorAdapterRegistry = advisorAdapterRegistry;
  }
  // 可议指定多个
  public void setCustomTargetSourceCreators(TargetSourceCreator... targetSourceCreators) {
    this.customTargetSourceCreators = targetSourceCreators;
  }
  // 通用拦截器得名字。These must be bean names in the current factory
  // 这些Bean必须在当前容器内存在的~~~
  public void setInterceptorNames(String... interceptorNames) {
    this.interceptorNames = interceptorNames;
  }
  //Set whether the common interceptors should be applied before bean-specific ones
  // 默认值是true
  public void setApplyCommonInterceptorsFirst(boolean applyCommonInterceptorsFirst) {
    this.applyCommonInterceptorsFirst = applyCommonInterceptorsFirst;
  }
  //===========下面是关于BeanPostProcessor的一些实现方法============
  // getBeanNamesForType()的时候会根据每个BeanName去匹配类型合适的Bean,这里不例外,也会帮忙在proxyTypes找一下
  @Override
  @Nullable
  public Class<?> predictBeanType(Class<?> beanClass, String beanName) {
    if (this.proxyTypes.isEmpty()) {
      return null;
    }
    Object cacheKey = getCacheKey(beanClass, beanName);
    return this.proxyTypes.get(cacheKey);
  }
  // getEarlyBeanReference()它是为了解决单例bean之间的循环依赖问题,提前将代理对象暴露出去
  @Override
  public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
    Object cacheKey = getCacheKey(bean.getClass(), beanName);
    if (!this.earlyProxyReferences.contains(cacheKey)) {
      this.earlyProxyReferences.add(cacheKey);
    }
    return wrapIfNecessary(bean, beanName, cacheKey);
  }
  // 不做构造函数检测,返回null 让用空构造初始化吧
  @Override
  @Nullable
  public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
    return null;
  }
  // 这个很重要,在Bean实例化之前,先给一个机会,看看缓存里有木有,有就直接返回得了
  // 简单的说:其主要目的在于如果用户使用了自定义的TargetSource对象,则直接使用该对象生成目标对象,而不会使用Spring的默认逻辑生成目标对象
  // (并且这里会判断各个切面逻辑是否可以应用到当前bean上)
  @Override
  public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
    Object cacheKey = getCacheKey(beanClass, beanName);
    // beanName无效或者targetSourcedBeans里不包含此Bean
    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {
      //advisedBeans:已经被通知了的(被代理了的)Bean~~~~  如果在这里面  也返回null 
      if (this.advisedBeans.containsKey(cacheKey)) {
        return null;
      }
      // isInfrastructureClass:Advice、Pointcut、Advisor、AopInfrastructureBean的子类,表示是框架所属的Bean
      // shouldSkip:默认都是返回false的。AspectJAwareAdvisorAutoProxyCreator重写此方法:只要存在一个Advisor   ((AspectJPointcutAdvisor) advisor).getAspectName().equals(beanName)成立  就返回true
      if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {  
        // 所以这里会把我们所有的Advice、Pointcut、Advisor、AopInfrastructureBean等Bean都装进来
        this.advisedBeans.put(cacheKey, Boolean.FALSE);
        return null;
      }
    }
    //到这,只有在TargetSource中没有进行缓存,并且应该被切面逻辑环绕,但是目前还未生成代理对象的bean才会通过此方法
    // Create proxy here if we have a custom TargetSource.
    // 如果我们有TargetSourceCreator,这里就会创建一个代理对象
    // getCustomTargetSource逻辑:存在TargetSourceCreator  并且 beanFactory.containsBean(beanName)  然后遍历所有的TargetSourceCreator,调用getTargetSource谁先创建不为null就终止
    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);
    // 若创建好了这个代理对象,继续进一步的操作:::
    if (targetSource != null) {
      if (StringUtils.hasLength(beanName)) {
          // 缓存起来
        this.targetSourcedBeans.add(beanName);
      }
      //getAdvicesAndAdvisorsForBean:方法判断当前bean是否需要进行代理,若需要则返回满足条件的Advice或者Advisor集合
      // 这个方法由子类实现,AbstractAdvisorAutoProxyCreator和BeanNameAutoProxyCreator  代表中两种不同的代理方式
      Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);
      // 顾名思义,就是根据目标对象创建代理对象的核心逻辑了 下面详解
      Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);
      // 把创建好的代理  缓存~~~
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
    return null;
  }
  // 这个方法也很重要,若我们自己要实现一个TargetSourceCreator ,就可议实现我们自定义的逻辑了
  // 这里条件苛刻:customTargetSourceCreators 必须不为null
  // 并且容器内还必须有这个Bean:beanFactory.containsBean(beanName)    备注:此BeanName指的即将需要被代理得BeanName,而不是TargetSourceCreator 的BeanName
  //下面会介绍我们自己自定义一个TargetSourceCreator 来实现我们自己的逻辑
  @Nullable
  protected TargetSource getCustomTargetSource(Class<?> beanClass, String beanName) {
    // We can't create fancy target sources for directly registered singletons.
    if (this.customTargetSourceCreators != null &&
        this.beanFactory != null && this.beanFactory.containsBean(beanName)) {
      for (TargetSourceCreator tsc : this.customTargetSourceCreators) {
        TargetSource ts = tsc.getTargetSource(beanClass, beanName);
        if (ts != null) {
          // Found a matching TargetSource.
          if (logger.isDebugEnabled()) {
            logger.debug("TargetSourceCreator [" + tsc +
                " found custom TargetSource for bean with name '" + beanName + "'");
          }
          return ts;
        }
      }
    }
    // No custom TargetSource found.
    return null;
  }
  // 这三个方法,没做什么动作~~
  @Override
  public boolean postProcessAfterInstantiation(Object bean, String beanName) {
    return true;
  }
  @Override
  public PropertyValues postProcessPropertyValues(
      PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    return pvs;
  }
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) {
    return bean;
  }
  // 代理是通过AbstractAutoProxyCreator中的postProcessAfterInitialization()创建的
  // 因此这个方法是蛮重要的,主要是wrapIfNecessary()方法会特别的重要
  // earlyProxyReferences缓存:该缓存用于保存已经创建过代理对象的cachekey,**避免重复创建**
  @Override
  public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) throws BeansException {
    if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      if (!this.earlyProxyReferences.contains(cacheKey)) {
        return wrapIfNecessary(bean, beanName, cacheKey);
      }
    }
    return bean;
  }
  // ============wrapIfNecessary方法==============
  protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
    // 若此Bean已经在targetSourcedBeans里,说明已经被代理过,那就直接返回即可
    // (postProcessBeforeInstantiation()中成功创建的代理对象都会将beanName加入到targetSourceBeans中)
    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
    }
    // 如果该Bean基础框架Bean或者免代理得Bean,那也不处理
    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
    }
    // 逻辑同上,对于实现了Advice,Advisor,AopInfrastructureBean接口的bean,都认为是spring aop的基础框架类,不能对他们创建代理对象,
    // 同时子类也可以覆盖shouldSkip方法来指定不对哪些bean进行代理
    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
    }
    // Create proxy if we have advice.
    // getAdvicesAndAdvisorsForBean该方法由子类实现,如国有Advice切面切进去了,我们就要给他代理
    //根据getAdvicesAndAdvisorsForBean()方法的具体实现的不同,AbstractAutoProxyCreator又分成了两类自动代理机制
    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
    // 需要代理,那就进来给它创建一个代理对象吧
    if (specificInterceptors != DO_NOT_PROXY) {
      // 缓存起来,赋值为true,说明此key是被代理了的
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      // 创建这个代理对象
      Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      // 创建好后缓存起来  避免重复创建
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
    }
    // 不需要代理,也把这种不需要代理的对象给与缓存起来  赋值为false
    this.advisedBeans.put(cacheKey, Boolean.FALSE);
    return bean;
  }
  // 创建代理对象  specificInterceptors:作用在这个Bean上的增强器们
  // 这里需要注意的地方:入参是targetSource  而不是target
  // 所以最终代理的是  ``每次AOP代理处理方法调用时,目标实例都会用到TargetSource实现``
  protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {
    if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
    }
    // 这个我们非常熟悉了ProxyFactory   创建代理对象的三大方式之一~~~
    ProxyFactory proxyFactory = new ProxyFactory();
    // 复制当前类的相关配置,因为当前类它也是个ProxyConfig
    proxyFactory.copyFrom(this);
    // 看看是否是基于类的代理(CGLIB),若表面上是基于接口的代理  我们还需要进一步去检测
    if (!proxyFactory.isProxyTargetClass()) {
      // shouldProxyTargetClass方法用于判断是否应该使用targetClass类而不是接口来进行代理
      // 默认实现为和该bean定义是否属性值preserveTargetClass为true有关。默认情况下都不会有此属性值的~~~~~
      if (shouldProxyTargetClass(beanClass, beanName)) {
        proxyFactory.setProxyTargetClass(true);
      }
      // 到此处,上面说了,就是把这个类实现的接口们,都放进proxyFactory(当然也会处理一些特殊的接口~~~不算数的)
      else {
        evaluateProxyInterfaces(beanClass, proxyFactory);
      }
    }
    // buildAdvisors:整理合并得到最终的advisors (毕竟interceptorNames还指定了一些拦截器的)
    // 至于调用的先后顺序,通过applyCommonInterceptorsFirst参数可以进行设置,若applyCommonInterceptorsFirst为true,interceptorNames属性指定的Advisor优先调用。默认为true
    Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
    // 添加进工厂里
    proxyFactory.addAdvisors(advisors);
    // 把targetSource放进去  TargetSource的实现方式有多种 后面会介绍
    proxyFactory.setTargetSource(targetSource);
    // 这个方法是交给子类的,子类可以继续去定制此proxyFactory(Spring内部并没有搭理它)
    customizeProxyFactory(proxyFactory);
    // 沿用this得freezeProxy的属性值
    proxyFactory.setFrozen(this.freezeProxy);
    // 设置preFiltered的属性值,默认是false。子类:AbstractAdvisorAutoProxyCreator修改为true
    // preFiltered:字段意思为:是否已为特定目标类筛选Advisor
    // 这个字段和DefaultAdvisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice获取所有的Advisor有关
    //CglibAopProxy和JdkDynamicAopProxy都会调用此方法,然后递归执行所有的Advisor的
    if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
    }
    // getProxyClassLoader():调用者可议指定  否则为:ClassUtils.getDefaultClassLoader()
    return proxyFactory.getProxy(getProxyClassLoader());
  }
  // 下面,只剩一个重要的方法:buildAdvisors()没有解释了
  protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
    // 解析interceptorNames而来得Advisor数组~~~
    Advisor[] commonInterceptors = resolveInterceptorNames();
    // 注意:此处用得事Object
    List<Object> allInterceptors = new ArrayList<>();
    if (specificInterceptors != null) {
      allInterceptors.addAll(Arrays.asList(specificInterceptors));
      // 若解析它来的有内容
      if (commonInterceptors.length > 0) {
        // 放在头部  也就是最上面
        if (this.applyCommonInterceptorsFirst) {
          allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
        }
        // 放在末尾
        else {
          allInterceptors.addAll(Arrays.asList(commonInterceptors));
        }
      }
    }
    // 把每一个Advisor都用advisorAdapterRegistry.wrap()包装一下~~~~
    // 注意wrap方法,默认只支持那三种类型的Advice转换为Advisor的~~~
    Advisor[] advisors = new Advisor[allInterceptors.size()];
    for (int i = 0; i < allInterceptors.size(); i++) {
      advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
    }
    return advisors;
  }
  // 处理interceptorNames,去容器内找出来
  private Advisor[] resolveInterceptorNames() {
    BeanFactory bf = this.beanFactory;
    ConfigurableBeanFactory cbf = (bf instanceof ConfigurableBeanFactory ? (ConfigurableBeanFactory) bf : null);
    List<Advisor> advisors = new ArrayList<>();
    for (String beanName : this.interceptorNames) {
      // 排除一些情况:此工厂不是ConfigurableBeanFactory或者该Bean不在创建中
      if (cbf == null || !cbf.isCurrentlyInCreation(beanName)) {
        Assert.state(bf != null, "BeanFactory required for resolving interceptor names");
        // 拿到这个Bean,然后使用advisorAdapterRegistry把它适配一下即可~~~
        Object next = bf.getBean(beanName);
        advisors.add(this.advisorAdapterRegistry.wrap(next));
      }
    }
    return advisors.toArray(new Advisor[0]);
  }
}


相关文章
|
7天前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
9天前
|
设计模式 Java Spring
spring源码设计模式分析-代理设计模式(二)
spring源码设计模式分析-代理设计模式(二)
|
21天前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
2月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
23 0
Spring高手之路22——AOP切面类的封装与解析
|
2月前
|
Java Spring XML
掌握面向切面编程的秘密武器:Spring AOP 让你的代码优雅转身,横切关注点再也不是难题!
【8月更文挑战第31天】面向切面编程(AOP)通过切面封装横切关注点,如日志记录、事务管理等,使业务逻辑更清晰。Spring AOP提供强大工具,无需在业务代码中硬编码这些功能。本文将深入探讨Spring AOP的概念、工作原理及实际应用,展示如何通过基于注解的配置创建切面,优化代码结构并提高可维护性。通过示例说明如何定义切面类、通知方法及其应用时机,实现方法调用前后的日志记录,展示AOP在分离关注点和添加新功能方面的优势。
38 0
|
7天前
|
SQL 监控 druid
springboot-druid数据源的配置方式及配置后台监控-自定义和导入stater(推荐-简单方便使用)两种方式配置druid数据源
这篇文章介绍了如何在Spring Boot项目中配置和监控Druid数据源,包括自定义配置和使用Spring Boot Starter两种方法。
|
2月前
|
缓存 Java Maven
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
Java本地高性能缓存实践问题之SpringBoot中引入Caffeine作为缓存库的问题如何解决
|
3月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
3月前
|
Java 应用服务中间件 开发者
Java面试题:解释Spring Boot的优势及其自动配置原理
Java面试题:解释Spring Boot的优势及其自动配置原理
101 0
|
7天前
|
XML Java 关系型数据库
springboot 集成 mybatis-plus 代码生成器
本文介绍了如何在Spring Boot项目中集成MyBatis-Plus代码生成器,包括导入相关依赖坐标、配置快速代码生成器以及自定义代码生成器模板的步骤和代码示例,旨在提高开发效率,快速生成Entity、Mapper、Mapper XML、Service、Controller等代码。
springboot 集成 mybatis-plus 代码生成器
下一篇
无影云桌面