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

作者:isea533 时间:2023-09-05 11:53:31 

本文是一个 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 注解

来源:https://blog.csdn.net/isea533/article/details/84100428

标签:spring,spel,表达式
0
投稿

猜你喜欢

  • Java设计模式之java命令模式详解

    2023-11-13 16:00:36
  • Android Path绘制贝塞尔曲线实现QQ拖拽泡泡

    2023-03-16 07:57:24
  • 使用android隐藏api实现亮度调节的方法

    2022-10-16 23:37:36
  • android工程下不能运行java main程序的解决方法

    2023-06-23 21:54:08
  • springboot对接微信支付的完整流程(附前后端代码)

    2021-11-12 15:08:42
  • 深入解析Java的设计模式编程中的模板方法模式

    2023-11-01 13:31:14
  • SpringBoot多种自定义错误页面方式小结

    2021-12-12 00:56:03
  • Springboot项目引入druid安装部署使用教程

    2023-07-28 01:39:25
  • Spring Aop 源码增强获取分享

    2023-06-22 21:59:22
  • Android实现点击AlertDialog上按钮时不关闭对话框的方法

    2022-07-28 07:39:51
  • Map集合之HashMap的使用及说明

    2022-07-23 15:56:56
  • Android中Textview和图片同行显示(文字超出用省略号,图片自动靠右边)

    2023-02-25 04:52:37
  • 详解Spring中使用@within与@target的区别

    2022-11-18 17:58:24
  • 浅谈Android轻量级的数据缓存框架RxCache

    2023-12-22 14:01:30
  • Intellij Idea中进行Mybatis逆向工程的实现

    2021-06-05 00:08:53
  • Android Notification使用方法详解

    2023-12-14 13:03:17
  • list集合去除重复对象的实现

    2022-10-16 23:02:42
  • SpringBoot中定位切点的两种常用方法

    2023-09-26 08:45:17
  • 深入解析Java中ThreadLocal线程类的作用和用法

    2022-01-12 09:03:15
  • c#中CAD文件读取实例

    2023-07-23 19:37:59
  • asp之家 软件编程 m.aspxhome.com