Series of articles
@TOC
preface
Much of the power of the Spring framework comes from the fact that it provides a complete set of extension points, which are available online in great detail. Add some custom operations before, during, and after the initialization or instantiation of Environment, container, Meta, and Bean to change or enhance the logic of the framework. Common extensions include implementing xxxInitializer, xxxProcessor, and xxxAware interfaces, such as the early XML integration of Dubbo, and annotations or SPI-based metaINF /spring.factories. For example, the starter of SpringBoot.
In order to deepen the understanding of friends, we assume that a business scenario: we need in the application. The properties/application. The custom configuration variables to get IP in the yml, used to generate different log file name. Random is built-in to SpringBoot and myVar is extended by us.
my.log.prefix=monitor_${myVar.ip}
#my.log.prefix=mopnitor_${myVar.yyyyMMddHHmmss}
#my.log.prefix=monitor _${random.int(10)}
Copy the code
I. Main points of this paper
We have integrated Apollo and hosted the configuration files on the Apollo remote server. As you can see, Apollo has injected the remote configuration into the SpringBoot container context, which is an Environment extension. In this article, we will emulate the processing logic of springBoot’s built-in Random variable to extend our custom configuration variable. A complete catalog of articles in the series
- Springboot expand
- Springboot custom configuration variables
- Springboot Application Configuration file obtains the service IP address
- Springboot Application configuration file to obtain the current time + formatting
- EnvironmentPostProcessor is configured externally
- Logback Reads the SpringBoot configuration
Second, development environment
- JDK 1.8
- Maven 3.6.2
- Springboot 2.4.3
- idea 2020
Iii. Project transformation
Add a meta-INF /spring.factories file to the resources directory to make the Spring framework aware of the new SPI.
org.springframework.boot.env.EnvironmentPostProcessor=\
com.mmc.lesson.envdemo.support.MyValuePropertySourceEnvironmentPostProcessor
Copy the code
2, write MyValuePropertySourceEnvironmentPostProcessor. Java, realize EnvironmentPostProcessor, Order interface, This gives higher priority to the injection of our custom configuration before the Spring container refresh.
public class MyValuePropertySourceEnvironmentPostProcessor implements EnvironmentPostProcessor.Ordered {
private Log logger;
/** * empty. */
public MyValuePropertySourceEnvironmentPostProcessor(a) {
logger = LogFactory.getLog(MyValuePropertySourceEnvironmentPostProcessor.class);
}
/ * * * init. * /
public MyValuePropertySourceEnvironmentPostProcessor(Log logger) {
this.logger = logger;
}
@Override
public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) {
MyValuePropertySource.addToEnvironment(environment, this.logger);
}
@Override
public int getOrder(a) {
return Ordered.HIGHEST_PRECEDENCE + 2; }}Copy the code
3, write MyValuePropertySource. Java, defined in our application. Yml/application. The properties of the variable ${myVar} prefix.
public class MyValuePropertySource extends PropertySource<MyLogValue> {
/**
* Name of the random {@link PropertySource}.
*/
public static final String MY_PROPERTY_SOURCE_NAME = "myVar";
private static final String PREFIX = "myVar.";
private static final Log logger = LogFactory.getLog(MyValuePropertySource.class);
/** * MyValuePropertySource. */
public MyValuePropertySource(a) {
this(MY_PROPERTY_SOURCE_NAME);
}
/** * MyValuePropertySource. */
public MyValuePropertySource(String name) {
super(name, new MyLogValue());
}
/** * adds a custom expression to the context. */
public static void addToEnvironment(ConfigurableEnvironment environment, Log logger) { MutablePropertySources sources = environment.getPropertySources(); PropertySource<? > existing = sources.get(MY_PROPERTY_SOURCE_NAME);if(existing ! =null) {
logger.trace("RandomValuePropertySource already present");
return;
}
MyValuePropertySource randomSource = new MyValuePropertySource(MY_PROPERTY_SOURCE_NAME);
if(sources.get(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME) ! =null) {
sources.addAfter(StandardEnvironment.SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, randomSource);
} else {
sources.addLast(randomSource);
}
logger.trace("MyValuePropertySource add to Environment");
}
@Override
public Object getProperty(String name) {
if(! name.startsWith(PREFIX)) {return null;
}
logger.trace(LogMessage.format("Generating property for '%s'", name));
return getValue(name.substring(PREFIX.length()));
}
/** * Currently, only IP. */ is supported
private Object getValue(String type) {
if (type.equalsIgnoreCase("ip")) {
return getSource().getIp();
}
return null; }}Copy the code
4, write myLogValue. Java, implement ${myvar. IP} value logic.
@Data
public class MyLogValue {
/** * Get the local Ip address. */
public String getIp(a) {
returnIpUtil.getLocalIP(); }}class IpUtil {
/** * get the local IP address, return only one. */
static String getLocalIP(a) {
String sIP = "";
InetAddress ip = null;
try {
// If Windows is used
if (isWindowsOS()) {
ip = InetAddress.getLocalHost();
// If the OS is Linux
} else {
boolean bFindIP = false;
Enumeration<NetworkInterface> netInterfaces = NetworkInterface.getNetworkInterfaces();
while (netInterfaces.hasMoreElements()) {
if (bFindIP) {
break;
}
NetworkInterface ni = netInterfaces.nextElement();
if(ni.isLoopback() || ni.isVirtual() || ! ni.isUp()) {continue;
}
// ---------- In certain cases, you can use ni.getName
// Traverses all IP addresses
Enumeration<InetAddress> ips = ni.getInetAddresses();
while (ips.hasMoreElements()) {
ip = ips.nextElement();
if(ip.isSiteLocalAddress() && ! ip.isLoopbackAddress()// 127. All lookback addresses start with lookback addresses
&& !ip.getHostAddress().contains(":")) {
bFindIP = true;
break;
}
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
if (null! = ip) { sIP = ip.getHostAddress(); }return sIP;
}
/** * Check whether the OS is Windows. */
private static boolean isWindowsOS(a) {
boolean isWindowsOS = false;
String osName = System.getProperty("os.name");
if (osName.toLowerCase().contains("windows")) {
isWindowsOS = true;
}
returnisWindowsOS; }}Copy the code
Four, run it
1. Modify the logback-spring. XML configuration.
<springProperty scope="context" name="log.path" source="logging.file.path"/>
<springProperty scope="context" name="monitor.file.prefix" source="my.log.prefix"/>
<appender name="MONITOR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<! -- The path and name of the log file being recorded -->
<file>${log.path}/${monitor.file.prefix}.log</file>
<! Log file output format -->
<encoder>
<pattern>[%d{yyyy-MM-dd HH:mm:ss.SSS}] [%thread] [%level] [%logger{50}:%L] - %msg%n
</pattern>
<charset>UTF-8</charset>
</encoder>
<! -- Logger scroll policy, by date, by size -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<! -- Daily log archive path and format -->
<fileNamePattern>${log.path}/monitor/monitor-%d{yyyy-MM-dd}.%i.zip</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>500MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<! -- Log file retention days -->
<maxHistory>7</maxHistory>
<totalSizeCap>7GB</totalSizeCap>
<cleanHistoryOnStart>true</cleanHistoryOnStart>
</rollingPolicy>
</appender>
Copy the code
2. Change application.properties and add our custom variable myvar. IP.
# log path
logging.file.path=./logs
# Log name prefix
my.log.prefix=monitor_${myVar.ip}
Copy the code
3. Write test cases.
@SpringBootTest
class EnvDemoApplicationTests {
@Value("${my.log.prefix}")
private String prefix;
@Test
void contextLoads(a) {
System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -"); System.out.println(prefix); }}Copy the code
4. Run it, as expected! The value of myvar. IP can be obtained normally.
Five, the summary
At this point, we have simply implemented the functionality that extends the Spring framework. ${myvar.time.YYYYMMDD} = ${myvar.time.yyyYMmDD} = ${myvar.time.yyyYMmDD} = ${myVar. Springboot Extension – Customizing logging Components
Add me to exchange learning!