Spring组件开发模式支持SPEL表达式

 更新时间:2018年12月24日 15:03:11   作者:isea533  
今天小编就为大家分享一篇关于Spring组件开发模式支持SPEL表达式,小编觉得内容挺不错的,现在分享给大家,具有很好的参考价值,需要的朋友一起跟随小编来看看吧

本文是一个 Spring 扩展支持 SPEL 的简单模式,方便第三方通过 Spring 提供额外功能。

简化版方式

这种方式可以在任何能获取ApplicationContext 的地方使用。还可以提取一个方法处理动态 SPEL 表达式。

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.*;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.annotation.AnnotationUtils;
import java.lang.reflect.Method;
/**
 * 针对 Spring 实现某些特殊逻辑时,支持 SPEL 表达式
 * @author liuzh
 */
public class SpelUtil implements ApplicationContextAware {
  /**
   * 通过 ApplicationContext 处理时
   * @param applicationContext
   * @throws BeansException
   */
  @Override
  public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    if (applicationContext instanceof ConfigurableApplicationContext) {
      ConfigurableApplicationContext context = (ConfigurableApplicationContext)applicationContext;
      ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
      StandardBeanExpressionResolver expressionResolver = new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader());
      for (String definitionName : applicationContext.getBeanDefinitionNames()) {
        BeanDefinition definition = beanFactory.getBeanDefinition(definitionName);
        Scope scope = (definition != null ? beanFactory.getRegisteredScope(definition.getScope()) : null);
        //根据自己逻辑处理
        //例如获取 bean
        Object bean = applicationContext.getBean(definitionName);
        //获取实际类型
        Class<?> targetClass = AopUtils.getTargetClass(bean);
        //获取所有方法
        for (Method method : targetClass.getDeclaredMethods()) {
          //获取自定义的注解(Bean是个例子)
          Bean annotation = AnnotationUtils.findAnnotation(method, Bean.class);
          //假设下面的 value 支持 SPEL
          for (String val : annotation.value()) {
            //解析 ${} 方式的值
            val = beanFactory.resolveEmbeddedValue(val);
            //解析 SPEL 表达式
            Object value = expressionResolver.evaluate(val, new BeanExpressionContext(beanFactory, scope));
            //TODO 其他逻辑
          }
        }
      }
    }
  }
}

上面是完全针对ApplicationContext的,下面是更推荐的一种用法。

推荐方式

import org.springframework.aop.support.AopUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanClassLoaderAware;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.config.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.expression.StandardBeanExpressionResolver;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.util.ReflectionUtils;
/**
 * 针对 Spring 实现某些特殊逻辑时,支持 SPEL 表达式
 * @author liuzh
 */
public class SpelUtil2 implements BeanPostProcessor, BeanFactoryAware, BeanClassLoaderAware {
  private BeanFactory beanFactory;
  private BeanExpressionResolver resolver;
  private BeanExpressionContext expressionContext;
  /**
   * 解析 SPEL
   * @param value
   * @return
   */
  private Object resolveExpression(String value){
    String resolvedValue = resolve(value);
    if (!(resolvedValue.startsWith("#{") && value.endsWith("}"))) {
      return resolvedValue;
    }
    return this.resolver.evaluate(resolvedValue, this.expressionContext);
  }
  /**
   * 解析 ${}
   * @param value
   * @return
   */
  private String resolve(String value){
    if (this.beanFactory != null && this.beanFactory instanceof ConfigurableBeanFactory) {
      return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value);
    }
    return value;
  }
  @Override
  public void setBeanClassLoader(ClassLoader classLoader) {
    this.resolver = new StandardBeanExpressionResolver(classLoader);
  }
  @Override
  public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
    this.beanFactory = beanFactory;
    if(beanFactory instanceof ConfigurableListableBeanFactory){
      this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver();
      this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null);
    }
  }
  @Override
  public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    return bean;
  }
  /**
   * 对 bean 的后置处理
   * @param bean
   * @param beanName
   * @return
   * @throws BeansException
   */
  @Override
  public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    //获取实际类型
    Class<?> targetClass = AopUtils.getTargetClass(bean);
    //获取所有方法
    ReflectionUtils.doWithMethods(targetClass, method -> {
      //获取自定义的注解(Bean是个例子)
      Bean annotation = AnnotationUtils.findAnnotation(method, Bean.class);
      //假设下面的 value 支持 SPEL
      for (String val : annotation.value()) {
        //解析表达式
        Object value = resolveExpression(val);
        //TODO 其他逻辑
      }
    }, method -> {
      //TODO 过滤方法
      return true;
    });
    return null;
  }
}

这种方式利用了 Spring 生命周期的几个接口来获取需要用到的对象。

Spring 生命周期调用顺序

扩展 Spring 我们必须了解这个顺序,否则就没法正确的使用各中对象。

完整的初始化方法及其标准顺序是:

  • BeanNameAware 的 setBeanName 方法
  • BeanClassLoaderAware 的 setBeanClassLoader 方法
  • BeanFactoryAware 的 setBeanFactory 方法
  • EnvironmentAware 的 setEnvironment 方法
  • EmbeddedValueResolverAware 的 setEmbeddedValueResolver 方法
  • ResourceLoaderAware 的 setResourceLoader 方法 (仅在应用程序上下文中运行时适用)
  • ApplicationEventPublisherAware 的 setApplicationEventPublisher 方法 (仅在应用程序上下文中运行时适用)
  • MessageSourceAware 的 setMessageSource 方法 (仅在应用程序上下文中运行时适用)
  • ApplicationContextAware 的 setApplicationContext 方法 (仅在应用程序上下文中运行时适用)
  • ServletContextAware 的 setServletContext 方法 (仅在Web应用程序上下文中运行时适用)
  • BeanPostProcessors 的 postProcessBeforeInitialization 方法
  • InitializingBean 的 afterPropertiesSet 方法
  • 自定义初始化方法
  • BeanPostProcessors 的 postProcessAfterInitialization 方法

关闭bean工厂时,以下生命周期方法适用:

  • DestructionAwareBeanPostProcessors 的 postProcessBeforeDestruction 方法
  • DisposableBean 的 destroy 方法
  • 自定义销毁方法

参考:https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/beans/factory/BeanFactory.html

灵活运用

利用上述模式可以实现很多便捷的操作。

Spring 中,使用类似模式的地方有:

  • @Value 注解支持 SPEL(和 ${})
  • @Cache 相关的注解(支持 SPEL)
  • @EventListener 注解
  • @RabbitListener 注解

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对脚本之家的支持。如果你想了解更多相关内容请查看下面相关链接

相关文章

  • Java如何根据根据IP地址获取省市

    Java如何根据根据IP地址获取省市

    这篇文章主要为大家详细介绍了两种在Java中获取IP地址对应地理位置信息的方法,分别是使用ip2region库和GeoIP库,感兴趣的小伙伴可以了解下
    2025-01-01
  • springboot读取自定义配置文件节点的方法

    springboot读取自定义配置文件节点的方法

    这篇文章主要介绍了springboot读取自定义配置文件节点的方法,本文给大家介绍的非常不错,具有一定的参考借鉴价值 ,需要的朋友可以参考下
    2018-05-05
  • Java jar打包工具使用方法步骤解析

    Java jar打包工具使用方法步骤解析

    这篇文章主要介绍了Java jar打包工具使用方法步骤解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-10-10
  • Springboot2以代码的方式统一配置Jackson教程

    Springboot2以代码的方式统一配置Jackson教程

    这篇文章主要介绍了Springboot2以代码的方式统一配置Jackson教程,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-11-11
  • 面试官:怎么做JDK8的垃圾收集器的调优(面试常问)

    面试官:怎么做JDK8的垃圾收集器的调优(面试常问)

    这篇文章主要介绍了面试官:怎么做JDK8的垃圾收集器的调优,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-08-08
  • 详解maven的setting配置文件中mirror和repository的区别

    详解maven的setting配置文件中mirror和repository的区别

    这篇文章主要介绍了详解maven的setting配置文件中mirror和repository的区别,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-12-12
  • SpringSecurity集成图片验证码的详细过程

    SpringSecurity集成图片验证码的详细过程

    SpringSecurity是通过过滤器链来完成的,接下来的验证码,可以尝试创建一个过滤器放到Security的过滤器链中,在自定义的过滤器中比较验证码,本文通过实例代码介绍SpringSecurity集成图片验证码的详细过程,感兴趣的朋友一起看看吧
    2023-12-12
  • SpringBoot部署到腾讯云的实现示例

    SpringBoot部署到腾讯云的实现示例

    记录一下自己第一次部署springboot项目,本文主要介绍了SpringBoot部署到腾讯云的实现示例,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-08-08
  • tdesign的文件上传功能实现(微信小程序+idea的springboot)

    tdesign的文件上传功能实现(微信小程序+idea的springboot)

    这篇文章主要介绍了tdesign的文件上传(微信小程序+idea的springboot)的相关知识,本文通过图文实例代码相结合给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧
    2023-09-09
  • Java设计模式:组合模式

    Java设计模式:组合模式

    这篇文章主要介绍了快速理解Java设计模式中的组合模式,具有一定参考价值,需要的朋友可以了解下,希望能够给你带来帮助
    2021-09-09

最新评论