Spring4 How to customize @ES1en function details

  • 2020-10-23 20:57:25
  • OfStack

preface

This article mainly introduces the Spring4 custom @Value function. The Spring version 4.3.10.RELEASE is used. Let's start with the detailed introduction.

In Spring, @ES9en is very powerful. You can inject 1 configuration item, refer to Bean in the container (calling its methods), or do 1 simple operation

Here is a simple demo to demonstrate the use of @Value


import org.springframework.stereotype.Service; 
 
/** 
 *  test Bean 
 */ 
@Service("userService") 
public class UserService { 
 
 public int count() { 
  return 10; 
 } 
  
 public int max(int size) { 
  int count = count(); 
  return count > size ? count : size; 
 } 
} 

import org.springframework.beans.factory.InitializingBean; 
import org.springframework.beans.factory.annotation.Value; 
import org.springframework.stereotype.Component; 
 
@Component 
public class AppRunner implements InitializingBean { 
  
 /** 
  *  reference 1 A configuration item  
  */ 
 @Value("${app.port}") 
 private int port; 
  
 /** 
  *  Calling the container's 1 a bean Method to get the value  
  */ 
 @Value("#{userService.count()}") 
 private int userCount; 
  
 /** 
  *  Calling the container's 1 a bean , and passed in 1 Values of the configuration items as parameters  
  */ 
 @Value("#{userService.max(${app.size})}") 
 private int max; 
  
 /** 
  *  Simple operation  
  */ 
 @Value("#{${app.size} <= '12345'.length() ? ${app.size} : '12345'.length()}") 
 private int min; 
  
 // test  
 public void afterPropertiesSet() throws Exception { 
  System.out.println("port : " + port); 
  System.out.println("userCount : " + userCount); 
  System.out.println("max : " + max); 
  System.out.println("min : " + min); 
 } 
} 

app.properties


app.port=9090 
 
app.size=3 

import org.springframework.context.annotation.AnnotationConfigApplicationContext; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.PropertySource; 
 
@ComponentScan 
@PropertySource("classpath:app.properties") 
public class App { 
  
 public static void main( String[] args) { 
  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class); 
  context.close(); 
 } 
} 

Run, output the result

port : 9090

userCount : 10

max : 10

min : 3

This is the 1-like usage for injecting 1 value.

So, can I do, given an expression or a specific value, that will help me evaluate the expression? In other words, how about one @Value?

The methods are as follows:


import org.springframework.beans.factory.config.BeanExpressionContext; 
import org.springframework.beans.factory.config.BeanExpressionResolver; 
import org.springframework.beans.factory.config.ConfigurableBeanFactory; 
import org.springframework.context.expression.StandardBeanExpressionResolver; 
 
public class ValueUtil { 
 
 private static final BeanExpressionResolver resolver = new StandardBeanExpressionResolver(); 
  
 /** 
  *  parsing 1 An expression, get 1 A value  
  * @param beanFactory 
  * @param value 1 A fixed value or 1 It's an expression. If it is 1 Returns a fixed value, otherwise resolved 1 An expression that returns the parsed value  
  * @return 
  */ 
 public static Object resolveExpression(ConfigurableBeanFactory beanFactory, String value) { 
  String resolvedValue = beanFactory.resolveEmbeddedValue(value); 
   
  if (!(resolvedValue.startsWith("#{") && value.endsWith("}"))) { 
   return resolvedValue; 
  } 
   
  return resolver.evaluate(resolvedValue, new BeanExpressionContext(beanFactory, null)); 
 } 
} 

Specific USES are as follows:


import org.springframework.context.annotation.AnnotationConfigApplicationContext; 
import org.springframework.context.annotation.ComponentScan; 
import org.springframework.context.annotation.PropertySource; 
 
@ComponentScan 
@PropertySource("classpath:app.properties") 
public class App { 
  
 public static void main( String[] args) { 
  AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(App.class); 
  // To calculate 1 A specific value (non-expression)  
  System.out.println(ValueUtil.resolveExpression(context.getBeanFactory(), "1121")); 
  // implementation @Value The function of the  
  System.out.println(ValueUtil.resolveExpression(context.getBeanFactory(), "${app.port}")); 
  System.out.println(ValueUtil.resolveExpression(context.getBeanFactory(), "#{userService.count()}")); 
  System.out.println(ValueUtil.resolveExpression(context.getBeanFactory(), "#{userService.max(${app.size})}")); 
  System.out.println(ValueUtil.resolveExpression(context.getBeanFactory(), "#{${app.size} <= '12345'.length() ? ${app.size} : '12345'.length()}")); 
  context.close(); 
 } 
} 

The operation output is as follows:

1121

9090

10

10

3

We found that @Value has been implemented

And then finally, some of you might wonder, what's the use? Shouldn't I just use @Value?

For most scenarios, you do just use @Value. However, there are some special scenarios that @Value can't do

Let's say we define an annotation


@Retention(RUNTIME) 
@Target(TYPE) 
public @interface Job { 
 String cron(); 
} 

This annotation requires an cron expression. The requirement is that the user can either use an cron expression directly or support a reference to a configuration item (configuring the value to a configuration file).

For instance


@Job(cron = "0 0 12 * * ?")
@Job(cron = "${app.job.cron}")

In this case @Value won't do it, but you can use my solution above.

conclusion


Related articles: