0
点赞
收藏
分享

微信扫一扫

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 我们必须了解这个顺序,否则就没法正确的使用各中对象。

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

  1. ​BeanNameAware​​​ 的​​setBeanName​​ 方法
  2. ​BeanClassLoaderAware​​​ 的​​setBeanClassLoader​​ 方法
  3. ​BeanFactoryAware​​​ 的​​setBeanFactory​​ 方法
  4. ​EnvironmentAware​​​ 的​​setEnvironment​​ 方法
  5. ​EmbeddedValueResolverAware​​​ 的​​setEmbeddedValueResolver​​ 方法
  6. ​ResourceLoaderAware​​​ 的​​setResourceLoader​​ 方法 (仅在应用程序上下文中运行时适用)
  7. ​ApplicationEventPublisherAware​​​ 的​​setApplicationEventPublisher​​ 方法 (仅在应用程序上下文中运行时适用)
  8. ​MessageSourceAware​​​ 的​​setMessageSource​​ 方法 (仅在应用程序上下文中运行时适用)
  9. ​ApplicationContextAware​​​ 的​​setApplicationContext​​ 方法 (仅在应用程序上下文中运行时适用)
  10. ​ServletContextAware​​​ 的​​setServletContext​​ 方法 (仅在Web应用程序上下文中运行时适用)
  11. ​BeanPostProcessors​​​ 的​​postProcessBeforeInitialization​​ 方法
  12. ​InitializingBean​​​ 的​​afterPropertiesSet​​ 方法
  13. 自定义初始化方法
  14. ​BeanPostProcessors​​​ 的​​postProcessAfterInitialization​​ 方法

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

  1. ​DestructionAwareBeanPostProcessors​​​ 的​​postProcessBeforeDestruction​​ 方法
  2. ​DisposableBean​​​ 的​​destroy​​ 方法
  3. 自定义销毁方法

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

灵活运用

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

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

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


举报

相关推荐

0 条评论