spring import placeholder

Under normal circumstances we use in the Spring configuration file <import>label it is such <import resource="other-beans.xml">. If we need to use placeholders for dynamic loading of spring bean configuration files based on the configuration file you will need to be configured in the following manner.

<context:property-placeholder location="classpath*:config.properties" />

<import resource="classpath:spring-db-${env}.xml" />

Wherein the placeholder envvalue by the configuration file config.propertiesto obtain the. If you use the above wording will get an error when starting the project.

Look at spring parse importthe source code labels we can see:

DefaultBeanDefinitionDocumentReader.java

    private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
        if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) {
            importBeanDefinitionResource(ele);
        }
        else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) {
            processAliasRegistration(ele);
        }
        else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) {
            processBeanDefinition(ele, delegate);
        }
        else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) {
            // recurse
            doRegisterBeanDefinitions(ele);
        }
    }

Spring can be seen by calling the importBeanDefinitionResource()parse method importtag. And then analyzed by the logic of the following resourceproperties:

// Resolve system properties: e.g. "${user.dir}"
location = getReaderContext().getEnvironment().resolveRequiredPlaceholders(location);

It means to resolve placeholders system properties. And we have adopted to replace the placeholder spring bean be used usually by Spring iocspreading BeanFactoryPostProcessorsubclasses PropertyPlaceholderConfigurerto achieve. But for importresolution have not yet reached that step. So we need to iocbe loaded before properitesthe file.

1, -Dkey = value added system parameters

The simplest is the time to start the service by -Dproperty=valuesetting the system 属性名/值对, run the application on top of this jvm available System.getProperty("property")to give value for value. If the value has a space, you need to use the value enclosed in double quotation marks, as -Dname="space string". This parameter is typically used to set the global variable level system, such as a profile path, so that the properties are accessible from anywhere in the program.

Tomcat start at boot time by adding -Denv=devit. The same we can use Spring's extensions to add parameters.

2、initPropertySources

As we all know, Spring is to load the root of the container by contextInitialized ContextLoaderListener. Its parent ContextLoader we can see the following logic.

ContextLoader.java

    protected void configureAndRefreshWebApplicationContext(ConfigurableWebApplicationContext wac, ServletContext sc) {
        if (ObjectUtils.identityToString(wac).equals(wac.getId())) {
            // The application context id is still set to its original default value
            // -> assign a more useful id based on available information
            String idParam = sc.getInitParameter(CONTEXT_ID_PARAM);
            if (idParam != null) {
                wac.setId(idParam);
            }
            else {
                // Generate default id...
                wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX +
                        ObjectUtils.getDisplayString(sc.getContextPath()));
            }
        }

        wac.setServletContext(sc);
        String configLocationParam = sc.getInitParameter(CONFIG_LOCATION_PARAM);
        if (configLocationParam != null) {
            wac.setConfigLocation(configLocationParam);
        }

        // The wac environment's #initPropertySources will be called in any case when the context
        // is refreshed; do it eagerly here to ensure servlet property sources are in place for
        // use in any post-processing or initialization that occurs below prior to #refresh
        ConfigurableEnvironment env = wac.getEnvironment();
        if (env instanceof ConfigurableWebEnvironment) {
            ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);
        }

        customizeContext(sc, wac);
        wac.refresh();
    }

The above ((ConfigurableWebEnvironment) env).initPropertySources(sc, null);will eventually call to WebApplicationContextUtils of initServletPropertySources method.

    public static void initServletPropertySources(
            MutablePropertySources propertySources, ServletContext servletContext, ServletConfig servletConfig) {

        Assert.notNull(propertySources, "propertySources must not be null");
        if (servletContext != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) &&
                propertySources.get(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
            propertySources.replace(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME,
                    new ServletContextPropertySource(StandardServletEnvironment.SERVLET_CONTEXT_PROPERTY_SOURCE_NAME, servletContext));
        }
        if (servletConfig != null && propertySources.contains(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) &&
                propertySources.get(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME) instanceof StubPropertySource) {
            propertySources.replace(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME,
                    new ServletConfigPropertySource(StandardServletEnvironment.SERVLET_CONFIG_PROPERTY_SOURCE_NAME, servletConfig));
        }
    }

It may be set by the value of the placeholder import ServletContext initialization parameters and initialization parameters ServletConfig.

2.1 ServletContext initialization parameters

If the container is defined if Spring Spring MVC profile in which the root containers, only, to the initial configuration of ServletContext initialization parameter. Because the root containers are initialized by the container ContextLoaderListener, ServletConfig is in the range Servlet. ServletContext initialization parameters may be configured by the following configuration.

<context-param>
    <param-name>env</param-name>
    <param-value>dev</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:application-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

2.2 ServletConfig initialization parameters

If the container is defined if Spring Spring MVC profile which container serlvet. That can be through the ServletContext initialization parameters but also by the initialization parameter ServletConfig. Therefore, the parameters can be performed in the following two ways:

<context-param>
    <param-name>env</param-name>
    <param-value>dev</param-value>
</context-param>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:application-context.xml</param-value>
</context-param>
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

There placeholder import classpath:application-context.xmlfile

or

 <servlet>
     <servlet-name>dispatcher</servlet-name>
     <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
     <init-param>
         <param-name>contextConfigLocation</param-name>
         <param-value>classpath:dispatcher-servlet.xml</param-value>
     </init-param>
     <init-param>
         <param-name>env</param-name>
         <param-value>dev</param-value>
     </init-param>
     <load-on-startup>1</load-on-startup>
 </servlet>

There placeholder import classpath:dispatcher-servlet.xmlfile

3, custom ApplicationContextInitializer

After calling ConfigurableWebEnvironment # initPropertySources, Spring containers before initialization ( wac.refresh()calls) customizeContext(sc, wac)the containers custom logic processing.

    protected void customizeContext(ServletContext sc, ConfigurableWebApplicationContext wac) {
        List<Class<ApplicationContextInitializer<ConfigurableApplicationContext>>> initializerClasses =
                determineContextInitializerClasses(sc);

        for (Class<ApplicationContextInitializer<ConfigurableApplicationContext>> initializerClass : initializerClasses) {
            Class<?> initializerContextClass =
                    GenericTypeResolver.resolveTypeArgument(initializerClass, ApplicationContextInitializer.class);
            if (initializerContextClass != null && !initializerContextClass.isInstance(wac)) {
                throw new ApplicationContextException(String.format(
                        "Could not apply context initializer [%s] since its generic parameter [%s] " +
                        "is not assignable from the type of application context used by this " +
                        "context loader: [%s]", initializerClass.getName(), initializerContextClass.getName(),
                        wac.getClass().getName()));
            }
            this.contextInitializers.add(BeanUtils.instantiateClass(initializerClass));
        }

        AnnotationAwareOrderComparator.sort(this.contextInitializers);
        for (ApplicationContextInitializer<ConfigurableApplicationContext> initializer : this.contextInitializers) {
            initializer.initialize(wac);
        }
    }

It logic is acquired web.xmldocument which is to or and to type the full name of the class that implements the interface. Before spring container will call its initialization callback method to customize the container logic.
<context-param>
<param-name>globalInitializerClassescontextInitializerClasses<param-value>ApplicationContextInitializerinitialize()

This interface provides ApplicationContextInitializer by implementing spring. In its callback interface initialize()to add properties file properties to the container. Then parse import label to replace the placeholder when he can get the value in the configuration file from the container.

3.1 interface to achieve ApplicationContextInitializer

Achieve ApplicationContextInitializer interface calls initialize()to add the properties file properties to the container.


public class CustomerApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext>{
    private static Logger logger = LoggerFactory.getLogger(CustomerApplicationContextInitializer.class);
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        ResourcePropertySource propertySource = null;
        try {
            propertySource = new ResourcePropertySource("classpath:config.properties");
        } catch (IOException e) {
            logger.error("config.properties is not exists");
        }
        applicationContext.getEnvironment().getPropertySources().addFirst(propertySource);
    }
}

3.2 Configuration attribute context-param

Add the following context-param attribute web.xml container enhanced. Because customizeContext(sc, wac)it will call all contextInitializerClassesthe initialize()methods.

<context-param>
    <param-name>globalInitializerClasses</param-name>
    <param-value>cn.carlzone.spring.initializer.CustomerApplicationContextInitializer</param-value>
</context-param>

or

<context-param>
    <param-name>contextInitializerClasses</param-name>
    <param-value>cn.carlzone.spring.initializer.CustomerApplicationContextInitializer</param-value>
</context-param>

Then placeholder envvalue by the configuration file config.propertiesto obtain. The spring load the appropriate configuration file to the project.

Reference article:

发布了173 篇原创文章 · 获赞 221 · 访问量 70万+

Guess you like

Origin blog.csdn.net/u012410733/article/details/81322763