📜  如何以编程方式生成 spring XMP 配置文件?

📅  最后修改于: 2021-10-22 03:23:34             🧑  作者: Mango

问题陈述

众所周知,在 Spring 应用程序中,我们通过“ApplicationContext”提供配置信息。 spring框架提供了多个类来实现这个接口,帮助我们在应用中使用配置信息,ClassPathXmlApplicationContext就是其中之一。通常在这种情况下,所有 bean 定义都配置在一个单独的 xml 文件中,通常称为“application-context.xml”。
在较大的项目结构中,最佳实践是保留多个 spring 配置文件,以便于维护和模块化。在这种情况下,最好的做法是将所有 Spring bean 配置文件组织到一个 XML 文件中,然后将整个文件导入其中。此外,在应用程序上下文中,我们加载这个单个 xml 文件。

现在如果要创建的配置文件数量过大怎么办?比如说,接近 100 或更多?
现在这是一个单调和蜗牛般的工作!是时候找到一条路径来自动化这个过程了。
让我们考虑一个场景,我们在外部源中拥有所有必需的数据,例如,Excel 工作表。现在我们需要从sheet中读取数据,并自发生成近100个spring配置文件。

如何自动化这个过程:JAXB 来救援。

JAXB 有一个绑定编译器。 JAXB XJC 模式绑定编译器将源 XML 模式转换或绑定到Java编程语言中的一组 JAXB 内容类。

  1. 在要生成的xml中查找需要引用的xml schema-http://www.springframework.org/schema/beans/spring-beans-3.0.xsd。
  2. 在浏览器中点击这个 url 来获取 xsd。现在将其保存在某个位置(如 spring.xsd)。
  3. 下载 jaxbri 包并设置环境路径变量。如果是 maven 项目,包括 jaxb-ri 和 jaxb-core 的依赖项。
  4. 从 spring.xsd 所在的文件夹中执行命令 xjc.exe spring.xsd。
    这将生成与 xsd 关联的类。

如果在执行命令时出现“属性已定义”错误,需要做什么?

很可能会收到一个异常,显示“属性已经定义”,如下面的屏幕截图所示。如果是这样,可以通过创建绑定文件来解决该问题,以覆盖引发异常的属性。
例外

绑定文件
春天25

使用以下命令解析架构并生成类:

xjc -b spring25.xjb -verbose -xmlschema
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd

生成的类
组织

现在创建一个 Bean 实例并对其进行编组。这将生成具有所需输出的 xml 文件。

示例代码
注意:- 测试类是在 org\springframework\schema\beans 下创建的。

package org.springframework.schema.beans;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.URISyntaxException;
import java.util.ArrayList;
  
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.namespace.QName;
  
import org.springframework.util.CollectionUtils;
  
import com.hrb.leap.bean.SpringConfigGenerator;
  
public class Test 
{
    public static void main(String[] args) throws JAXBException 
    {
        ObjectFactory factory = new ObjectFactory();
        JAXBContext context = JAXBContext.newInstance("org.springframework.schema.beans");
          
        /*
        * Create the root element 'Beans'
        * Set the required schema properties
        */
        Beans rootElement = factory.createBeans();
        rootElement.getOtherAttributes().put(new QName
        ("xmlns:xsi"), "http://www.w3.org/2001/XMLSchema-instance");
        rootElement.getOtherAttributes().put(new QName
        ("xsi:schemaLocation"), 
        "http://www.springframework.org/schema/beans spring-beans-3.2.xsd");
          
        /*
        * How to import resources
        * How to define list of reference beans
        */
        java.util.List resources = new ArrayList<>();
        resources.add("ResourceOne");
        resources.add("ResourceTwo");
        resources.add("ResourceThree");
          
        resources.forEach(resourceName -> 
        {
            Import importResource = new Import();
            importResource.setResource(resourceName+".xml");
            rootElement.getImportOrAliasOrBean().add(importResource);
        });
          
        Bean bean = factory.createBean();
        bean.setId("id");
        bean.setClazz("java.util.ArrayList");
        if (!CollectionUtils.isEmpty(resources)) 
        {
            ConstructorArg constructorArgs = factory.createConstructorArg();
            org.springframework.schema.beans.List listOfResources =
                    new org.springframework.schema.beans.List();
              
            resources.forEach(referenceFormName -> 
            {
                Ref refBean = new Ref();
                refBean.setBean(referenceFormName);
                listOfResources.getBeanOrRefOrIdref().add(refBean);
            });
            constructorArgs.setList(listOfResources);
            bean.getMetaOrConstructorArgOrProperty().add(constructorArgs);
        }
        rootElement.getImportOrAliasOrBean().add(bean);
          
        /*
        * Sample bean definition to show how to store
        list of values as a property
        */
            Bean simpleBean = factory.createBean();
            simpleBean.setId("SimpleBean");
            simpleBean.setClazz("com.packagename.ClassName");
              
            PropertyType firstProperty= factory.createPropertyType();
            firstProperty.setName("listOfValuesDemo");
              
            java.util.List listOfValues = new ArrayList<>();
            listOfValues.add("ValueOne");
            listOfValues.add("ValueTwo");
            listOfValues.add("ValueThree");
              
            org.springframework.schema.beans.List listToStoreValues 
                                = new org.springframework.schema.beans.List();
            listOfValues.forEach(name ->
            {
                Value value = factory.createValue();
                value.getContent().add(name);
                listToStoreValues.getBeanOrRefOrIdref().add(value);
            });
              
            firstProperty.setList(listToStoreValues);
              
            //Add the property to the bean.
            simpleBean.getMetaOrConstructorArgOrProperty().add
                            (factory.createProperty(firstProperty));
              
            // Add the bean under the root element 'beans'
            rootElement.getImportOrAliasOrBean().add(simpleBean);
              
            Marshaller marshaller = context.createMarshaller();
            marshaller.setProperty("jaxb.formatted.output",Boolean.TRUE);
            createXmlConfiguration(marshaller , "output", rootElement);
    }
      
    /*
    * Method create the xml under the 'src/main/resources' 
    folder in the project.
    */
    public static void createXmlConfiguration (Marshaller marshaller ,
                                String fileName, Beans rootElement) 
                                {
        try
        {
            java.net.URL url = SpringConfigGenerator.class.getResource("/");
            File fullPathToSubfolder = new File(url.toURI()).getAbsoluteFile();
            String projectFolder = 
                        fullPathToSubfolder.getAbsolutePath().split("target")[0];
              
            // TODO - Destination folder to be configured
            File outputFile = new File(projectFolder 
                            + "src/main/resources/" + "/"+fileName+".xml");
          
            if (!outputFile.exists()) 
            {
                outputFile.createNewFile();
            }
            OutputStream os = new FileOutputStream(outputFile);
            marshaller.marshal(rootElement, os);
        } 
          
        catch (URISyntaxException uriException) 
        {
              
        } 
          
        catch (IOException ioException) 
        {
              
        } 
          
        catch (JAXBException jaxbException) 
        {
              
        }
          
    }
}

输出
输出

按照以下步骤查看示例输出:

  1. 在浏览器中点击架构 url 以获取 xsd。现在将其保存在某个位置(例如:- spring.xsd)。
  2. 下载 jaxbri 包并设置环境路径变量。
  3. 设置 jaxb 库的类路径和当前路径(点),如示例(在 IDE 中执行此命令提示符或等号)
    set classpath=C:\folder_name\jaxb-ri\lib;.;
  4. 解压缩附加到文件夹下运行的命令(从 cmd 中的解压缩文件夹)后,编译“测试”类。
    javac org\springframework\schema\beans\Test.java
  5. 最后运行程序。
    java org.springframework.schema.beans.Test

    注意:- 如果’createXmlConfiguration(marshaller, “output”, rootElement);’,输出将打印在控制台中替换为“marshaller.marshal(rootElement, System.out);”

优点

  1. 工作量大大减少。一个人可能至少需要 20-25 天来配置接近 85 个文件(考虑 9 小时/天),这将占 225 小时。上述工具只需不到 3 秒即可完成。
  2. 如果以后有小的配置改动,我们可以单独拿具体的文件进行修正。

替代方法
如果配置文件的数量非常少,我们可以尝试动态加载包含所有 bean 定义的上下文,而无需生成物理 xml 文件。这可以通过以编程方式加载 spring 上下文并在需要时运行应用程序来实现。

缺点

  1. 内存消耗高
  2. 性能低
  3. 数据源中的小错误会产生很大的影响。
  4. 在生产环境中动态运行有风险。
  5. 如果数据源几乎保持不变,则不是一种建议的方法
  6. 将很难排除故障。