有时候,要通过自定义XML配置文件来实现一些特定的功能。这里通过例子来说明。
1、说明
首先,看部分spring加载bean文件的源码:
spring-beans-5.0.6.RELEASE.jar!/org/springframework/beans/factory/xml/PluggableSchemaResolver.class
:
public class PluggableSchemaResolver implements EntityResolver {
public static final String DEFAULT_SCHEMA_MAPPINGS_LOCATION = "META-INF/spring.schemas";
private static final Log logger = LogFactory.getLog(PluggableSchemaResolver.class);
spring-beans-5.0.6.RELEASE.jar!/org/springframework/beans/factory/xml/DefaultNamespaceHandlerResolver.class
:
public class DefaultNamespaceHandlerResolver implements NamespaceHandlerResolver {
public static final String DEFAULT_HANDLER_MAPPINGS_LOCATION = "META-INF/spring.handlers";
protected final Log logger;
@Nullable
可以看出,spring在加载xml文件的时候,会默认读取配置文件META-INF/spring.schemas
和META-INF/spring.handlers
。这样,我们就可以在这两个文件添加我们自定义的xml文件格式和xml文件解析处理器。
2、解析自定义xml配置文件
2.1 创建一个基本的Springboot工程
新建一个Springboot工程,pom如下。
SelfDefineXmlTrial/pom.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.lfqy.springboot</groupId>
<artifactId>SelfDefineXmlTrial</artifactId>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.2.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
</project>
然后,新建一个用于测试controller。
com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController
:
package com.lfqy.springboot.selfdefxml.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* Created by chengxia on 2022/5/7.
*/
@RestController
@RequestMapping("/selfdefxml")
public class SelfDefXmlController {
@RequestMapping("/hello")
public String hello(){
return "Hello world!";
}
}
最后,创建一个Springboot的启动类。
com.lfqy.springboot.selfdefxml.SelfDefXmlApplication
:
package com.lfqy.springboot.selfdefxml;
import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SelfDefXmlApplication {
public static void main(String[] args) {
SpringApplication.run(SelfDefXmlApplication.class);
System.out.println("The application has been started ....");
}
}
运行启动之后,浏览器访问http://localhost:8080/selfdefxml/hello
效果如下:
2.2 添加自定义的xml并解析
2.2.1 添加自定义xml格式说明和处理器配置
修改前面提到的配置文件META-INF/spring.schemas
、META-INF/spring.handlers
,添加xml格式说明。
META-INF/spring.schemas
:
http\://www.mmpp.com/schema/selfdef.xsd=META-INF/selfdef.xsd
META-INF/spring.handlers
:
http\://www.mmpp.com/schema/selfdef=com.lfqy.springboot.selfdefxml.selxmlparse.UserNamespaceHandler
添加xml格式说明配置文件。
META-INF/selfdef.xsd
:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema"
xmlns:tns="http://www.mmpp.com/schema/selfdef"
targetNamespace="http://www.mmpp.com/schema/selfdef"
elementFormDefault="qualified">
<element name="user">
<complexType> <attribute name="id" type="string"/>
<attribute name="name" type="string"/>
<attribute name="address" type="string"/>
</complexType> </element>
</schema>
2.2.2 添加自定义xml格式处理器
添加自定义xml格式处理器类。
com.lfqy.springboot.selfdefxml.selxmlparse.UserNamespaceHandler
:
package com.lfqy.springboot.selfdefxml.selxmlparse;
import org.springframework.beans.factory.xml.NamespaceHandlerSupport;
/**
* Created by chengxia on 2022/7/3.
*/
public class UserNamespaceHandler extends NamespaceHandlerSupport {
@Override
public void init() {
registerBeanDefinitionParser("user", new UserBeanDefinitionParser());
}
}
新增xml格式解析类。
com.lfqy.springboot.selfdefxml.selxmlparse.UserBeanDefinitionParser
:
package com.lfqy.springboot.selfdefxml.selxmlparse;
import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
import org.springframework.util.StringUtils;
import org.w3c.dom.Element;
/**
* Created by chengxia on 2022/7/3.
*/
public class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
@Override
protected Class<?> getBeanClass(Element element) {
return User.class;
}
@Override
protected void doParse(Element element, BeanDefinitionBuilder builder) {
String name = element.getAttribute("name");
String address = element.getAttribute("address");
if (StringUtils.hasText(name)) {
builder.addPropertyValue("name", name);
}
if (StringUtils.hasText(address)) {
builder.addPropertyValue("address", address);
}
}
}
2.2.3 添加自定义的bean类
新增自定义xml对应的bean类。
com.lfqy.springboot.selfdefxml.beans.User
:
package com.lfqy.springboot.selfdefxml.beans;
/**
* Created by chengxia on 2022/7/3.
*/
public class User {
private String name;
private String address;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
2.2.4 修改启动类读取自定义配置文件
添加自定义xml配置文件读取的相关逻辑。
com.lfqy.springboot.selfdefxml.SelfDefXmlApplication
:
package com.lfqy.springboot.selfdefxml;
import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
public class SelfDefXmlApplication {
public static void main(String[] args) {
SpringApplication.run(SelfDefXmlApplication.class);
System.out.println("The application has been started ....");
ApplicationContext ac = new ClassPathXmlApplicationContext("SelfDefBeans.xml");
User user = (User)ac.getBean("user");
System.out.println(user.getName());
System.out.println(user.getAddress());
}
}
2.3 启动和运行
到这里,编码就完成了,工程的目录结构如下。
运行之后,控制台输出如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2022-07-03 23:54:48.457 INFO 3667 --- [ main] c.l.s.selfdefxml.SelfDefXmlApplication : Starting SelfDefXmlApplication on ChengdeMBP with PID 3667 (/Users/chengxia/Developer/TrialProject/SpringBoot/SelfDefineXmlTrial/target/classes started by chengxia in /Users/chengxia/Developer/TrialProject)
2022-07-03 23:54:48.461 INFO 3667 --- [ main] c.l.s.selfdefxml.SelfDefXmlApplication : No active profile set, falling back to default profiles: default
2022-07-03 23:54:48.628 INFO 3667 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@45f45fa1: startup date [Sun Jul 03 23:54:48 CST 2022]; root of context hierarchy
2022-07-03 23:54:51.084 INFO 3667 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-07-03 23:54:51.121 INFO 3667 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-07-03 23:54:51.121 INFO 3667 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.31
2022-07-03 23:54:51.140 INFO 3667 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/chengxia/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2022-07-03 23:54:51.273 INFO 3667 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-07-03 23:54:51.273 INFO 3667 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 2656 ms
2022-07-03 23:54:51.492 INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2022-07-03 23:54:51.497 INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2022-07-03 23:54:51.498 INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2022-07-03 23:54:51.498 INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2022-07-03 23:54:51.498 INFO 3667 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2022-07-03 23:54:51.675 INFO 3667 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-03 23:54:52.034 INFO 3667 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@45f45fa1: startup date [Sun Jul 03 23:54:48 CST 2022]; root of context hierarchy
2022-07-03 23:54:52.153 INFO 3667 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/selfdefxml/hello]}" onto public java.lang.String com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController.hello()
2022-07-03 23:54:52.158 INFO 3667 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2022-07-03 23:54:52.158 INFO 3667 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2022-07-03 23:54:52.190 INFO 3667 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-03 23:54:52.191 INFO 3667 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-03 23:54:52.448 INFO 3667 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2022-07-03 23:54:52.519 INFO 3667 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-07-03 23:54:52.526 INFO 3667 --- [ main] c.l.s.selfdefxml.SelfDefXmlApplication : Started SelfDefXmlApplication in 4.696 seconds (JVM running for 5.555)
The application has been started ....
2022-07-03 23:54:52.532 INFO 3667 --- [ main] o.s.c.s.ClassPathXmlApplicationContext : Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7cd1ac19: startup date [Sun Jul 03 23:54:52 CST 2022]; root of context hierarchy
2022-07-03 23:54:52.537 INFO 3667 --- [ main] o.s.b.f.xml.XmlBeanDefinitionReader : Loading XML bean definitions from class path resource [SelfDefBeans.xml]
Paopao
Beijing
3、实现xml配置文件启动时自动加载
这里,通过实现一个启动时自动初始化的一个servlet来实现。
3.1 新增一个servlet实现类
com.lfqy.springboot.selfdefxml.servlet.StartupServlet
:
package com.lfqy.springboot.selfdefxml.servlet;
import com.lfqy.springboot.selfdefxml.beans.User;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
/**
* Created by chengxia on 2022/7/3.
*/
public class StartupServlet extends HttpServlet {
private static final long serialVersionUID = 8823372937923792739L;
public StartupServlet(){}
public void init(ServletConfig sc) throws ServletException{
super.init(sc);
//load the self-deine beans
ApplicationContext ac = new ClassPathXmlApplicationContext("SelfDefBeans.xml");
User user = (User)ac.getBean("user");
System.out.println(user.getName());
System.out.println(user.getAddress());
}
}
3.2 在启动类中添加servlet加载逻辑
在启动时加载servlet,为了方便区分,这里新写一个启动类。
com.lfqy.springboot.selfdefxml.SelfDefXmlLoadOnStartupApplication
package com.lfqy.springboot.selfdefxml;
import com.lfqy.springboot.selfdefxml.beans.User;
import com.lfqy.springboot.selfdefxml.servlet.StartupServlet;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletComponentScan;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.support.ClassPathXmlApplicationContext;
@SpringBootApplication
@ServletComponentScan(basePackages = "com.lfqy.springboot.selfdefxml.servlet") //servlet扫描包路径
public class SelfDefXmlLoadOnStartupApplication {
@Bean
public ServletRegistrationBean getServletRunOnStartup(){
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StartupServlet());
//设置Servlet在服务器启动时初始化(执行init方法),如果传入的参数是负数或者没有配置,那么这个Servlet会在第一次被请求时初始化
servletRegistrationBean.setLoadOnStartup(1);
//这里随便指定servlet关联度的url,如果不指定,那么默认所有的请求都会到这儿,会有各种奇奇怪怪的问题
servletRegistrationBean.addUrlMappings("/runOnStartup");
return servletRegistrationBean;
}
public static void main(String[] args) {
SpringApplication.run(SelfDefXmlLoadOnStartupApplication.class);
System.out.println("The application has been started ....");
}
}
3.3 启动和运行
到这里,编码已经完成,工程的目录结构如下:
运行之后,控制台输出如下:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.0.2.RELEASE)
2022-07-04 00:13:45.346 INFO 3789 --- [ main] l.s.s.SelfDefXmlLoadOnStartupApplication : Starting SelfDefXmlLoadOnStartupApplication on ChengdeMBP with PID 3789 (/Users/chengxia/Developer/TrialProject/SpringBoot/SelfDefineXmlTrial/target/classes started by chengxia in /Users/chengxia/Developer/TrialProject)
2022-07-04 00:13:45.350 INFO 3789 --- [ main] l.s.s.SelfDefXmlLoadOnStartupApplication : No active profile set, falling back to default profiles: default
2022-07-04 00:13:45.399 INFO 3789 --- [ main] ConfigServletWebServerApplicationContext : Refreshing org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@18a70f16: startup date [Mon Jul 04 00:13:45 CST 2022]; root of context hierarchy
2022-07-04 00:13:47.116 INFO 3789 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8080 (http)
2022-07-04 00:13:47.155 INFO 3789 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2022-07-04 00:13:47.156 INFO 3789 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet Engine: Apache Tomcat/8.5.31
2022-07-04 00:13:47.165 INFO 3789 --- [ost-startStop-1] o.a.catalina.core.AprLifecycleListener : The APR based Apache Tomcat Native library which allows optimal performance in production environments was not found on the java.library.path: [/Users/chengxia/Library/Java/Extensions:/Library/Java/Extensions:/Network/Library/Java/Extensions:/System/Library/Java/Extensions:/usr/lib/java:.]
2022-07-04 00:13:47.244 INFO 3789 --- [ost-startStop-1] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2022-07-04 00:13:47.244 INFO 3789 --- [ost-startStop-1] o.s.web.context.ContextLoader : Root WebApplicationContext: initialization completed in 1848 ms
2022-07-04 00:13:47.390 INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet startupServlet mapped to [/runOnStartup]
2022-07-04 00:13:47.391 INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.ServletRegistrationBean : Servlet dispatcherServlet mapped to [/]
2022-07-04 00:13:47.394 INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'characterEncodingFilter' to: [/*]
2022-07-04 00:13:47.395 INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'hiddenHttpMethodFilter' to: [/*]
2022-07-04 00:13:47.395 INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'httpPutFormContentFilter' to: [/*]
2022-07-04 00:13:47.395 INFO 3789 --- [ost-startStop-1] o.s.b.w.servlet.FilterRegistrationBean : Mapping filter: 'requestContextFilter' to: [/*]
2022-07-04 00:13:47.539 INFO 3789 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**/favicon.ico] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-04 00:13:47.765 INFO 3789 --- [ main] s.w.s.m.m.a.RequestMappingHandlerAdapter : Looking for @ControllerAdvice: org.springframework.boot.web.servlet.context.AnnotationConfigServletWebServerApplicationContext@18a70f16: startup date [Mon Jul 04 00:13:45 CST 2022]; root of context hierarchy
2022-07-04 00:13:47.824 INFO 3789 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/selfdefxml/hello]}" onto public java.lang.String com.lfqy.springboot.selfdefxml.controller.SelfDefXmlController.hello()
2022-07-04 00:13:47.828 INFO 3789 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error]}" onto public org.springframework.http.ResponseEntity<java.util.Map<java.lang.String, java.lang.Object>> org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.error(javax.servlet.http.HttpServletRequest)
2022-07-04 00:13:47.830 INFO 3789 --- [ main] s.w.s.m.m.a.RequestMappingHandlerMapping : Mapped "{[/error],produces=[text/html]}" onto public org.springframework.web.servlet.ModelAndView org.springframework.boot.autoconfigure.web.servlet.error.BasicErrorController.errorHtml(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
2022-07-04 00:13:47.854 INFO 3789 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/webjars/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-04 00:13:47.854 INFO 3789 --- [ main] o.s.w.s.handler.SimpleUrlHandlerMapping : Mapped URL path [/**] onto handler of type [class org.springframework.web.servlet.resource.ResourceHttpRequestHandler]
2022-07-04 00:13:47.991 INFO 3789 --- [ main] o.s.j.e.a.AnnotationMBeanExporter : Registering beans for JMX exposure on startup
2022-07-04 00:13:48.086 INFO 3789 --- [ main] o.s.c.s.ClassPathXmlApplicationContext : Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@790174f2: startup date [Mon Jul 04 00:13:48 CST 2022]; root of context hierarchy
2022-07-04 00:13:48.090 INFO 3789 --- [ main] o.s.b.f.xml.XmlBeanDefinitionReader : Loading XML bean definitions from class path resource [SelfDefBeans.xml]
Paopao
Beijing
2022-07-04 00:13:48.190 INFO 3789 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 8080 (http) with context path ''
2022-07-04 00:13:48.193 INFO 3789 --- [ main] l.s.s.SelfDefXmlLoadOnStartupApplication : Started SelfDefXmlLoadOnStartupApplication in 3.506 seconds (JVM running for 4.179)
The application has been started ....