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
猜你喜欢
- 本文实例讲述了Android编程开发之TextView单击链接弹出Activity的方法。分享给大家供大家参考,具体如下:话不多说直接上码:
- 自定义TypeHandler映射JSON类型为List1. 实体类这里只展示需要映射的字段,分别在所需映射的字段和实体类上添加注解。&nbs
- idea spring Initializr创建项目勾选项目所需要的依赖pom.xml文件会加载勾选的依赖,也可以不勾选后面通过自己常用的p
- 引言:关于java IO流的操作是非常常见的,基本上每个项目都会用到,每次遇到都是去网上找一找就行了,屡试不爽。上次突然一个同事问了我jav
- 背景产品想对多次快速点击做一下优化,想要的效果就是双击不会打开多次但是从开发角度来说,我可以用kotlin的拓展方法来调整这个,但是之前的历
- Android基础教程数据存储之文件存储将数据存储到文件中并读取数据1、新建FilePersistenceTest项目,并修改activit
- 首先先简单的说一下其3大特性的定义:封装:隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读和修改的访问级别。将抽象得到的数据和
- 相关知识:Java中三种简单注解介绍和代码实例一、作用用 @Deprecated注解的程序元素,不鼓励程序员使用这样的元素,通常是因为它很危
- 什么是ByteBuddyByteBuddy是一个java的运行时代码生成库,他可以帮助你以字节码的方式动态修改java类的代码。为什么需要B
- 本文实例讲述了Java Web项目部署在Tomcat运行出错与解决方法。分享给大家供大家参考,具体如下:1、在部署Java Web项目的过程
- 一 应用规划: ※ 确定功能。 ※ 必须的界面及界面跳转的流程。
- 协议做如下规定:规定数据协议:序列号 长度 状态字 数据长度 数据1 &n
- 一、什么是iText?在企业的信息系统中,报表处理一直占比较重要的作用,iText是一种生成PDF报表的Java组件。通过在服务器端使用Js
- 背景:在Android中按照数据保存的方式,可以分为如下几种Content Provider (用的SQLite实现),SQLite,Sha
- 需求: 给定一个URL地址, 例如: http://www.cncounter.com/tools/shorturl.php, 解析对应的I
- 首先对于一个SpringBoot工程来说,最明显的标志的就是 @SpringBootApplication它标记了这是一个SpringBoo
- 背景实际开发中,常常需要将比较复杂的 JSON 字符串转换为对应的 Java 对象。这里记录下解决方案。如下所示,是入侵事件检测得到的 JS
- 1. pom.xml文件配置<?xml version="1.0" encoding="UTF-8&qu
- 基本介绍数据回显:模型数据导向视图(模型数据 ---> Controller ---> 视图)说明:SpringMVC在调用方法
- 如图所示的效果相信大家都不陌生,我们可以使用很多种方法去实现此效果,这里自己采用CountDownTimer定时器简单封装下此效果,方便我们