Spring MVC provides a mechanism for extending XML to write custom XML beans, such as the Dubbo Framework, which enables a lot of Dubbo beans, such as <dubbo:application>, <dubbo:r Egistry> and so on, as long as the installation of the standard extension mode to implement the configuration.
What is the point of extending a custom bean
Assuming we're going to use an open source framework or a set of APIs, we certainly want the following two points:
Ease of use, which is simple to configure, as few places as possible.
Encapsulation, simple invocation, that is, the higher the high-level package, less exposure to the underlying implementation
Based on the above two points, let's say we want to implement a custom feature that can be implemented with existing Spring configuration items, but it's possible to configure more content and possibly add code assistance. Leads to logical dispersion and inconvenience to maintenance.
So we're using a way to extend the Spring configuration to encapsulate some of the custom complex features to minimize the configuration.
Steps to implement a custom extension
This example simply demonstrates that the function is simple, that is to implement a configurable parameter of the Hacker bean, and then provide a ToString () method, input parameter information.
We finally implemented the bean configuration as follows:
<kite:hacker id= "hacker" Name= "Moon" language= "Chinese" age= "" "ishide=" true "/>
Make a comparison with Spring's own configuration, for example
<context:component-scan base-package= "Com.ebanghu" ></context:component-scan>
1. Implement the Custom bean class, name it Hacker, and overload the ToString () method in the method, enter the property name, and the code is as follows:
Package kite.lab.spring.config;/** * Hacker * @author Fengzheng */public class Hacker {private String name; Private String age; Private String language; Private Boolean ishide; Public String GetName () {return name; } public void SetName (String name) {this.name = name; } public String Getage () {return age; public void Setage (String age) {this.age = age; } public String GetLanguage () {return language; } public void SetLanguage (String language) {this.language = language; } public boolean ishide () {return ishide; The public void Sethide (Boolean hide) {ishide = hide; } @Override Public String toString () {StringBuilder builder = new StringBuilder (); Builder.append ("======================\n"); Builder.append (String.Format ("Hacker's name is:%s \ n", This.getname ())); Builder.append (String.Format ("Hacker's age is:%s \ n", This.getage ())); Builder.append (String.Format ("Hacker's language is:%s \ n", This.getlanguage ())); Builder.append (String.Format ("Hacker's status is:%s \ n", This.ishide ())); Builder.append ("======================\n"); return builder.tostring (); }}
2, write the XSD schema property description file, named Hacker.xsd, here to put it in the project resources directory in the Meta-inf directory (the location can be determined by itself), it can be understood as: This file is corresponding to the entity class just created as an XML structure Description, which reads as follows:
<?xml version= "1.0" encoding= "UTF-8"? ><xsd:schema xmlns= "Http://code.fengzheng.com/schema/kite" xmlns: Xsd= "Http://www.w3.org/2001/XMLSchema" xmlns:beans= "Http://www.springframework.org/schema/beans" tar Getnamespace= "Http://code.fengzheng.com/schema/kite" elementformdefault= "qualified" attributeformdef ault= "Unqualified" > <xsd:import namespace= "Http://www.springframework.org/schema/beans"/> <xsd: ComplexType name= "Hacktype" > <xsd:complexContent> <xsd:extension base= "Beans:identifiedtype" > <xsd:attribute name= "name" type= "xsd:string" use= "required" > <xsd:annotati On> <xsd:documentation> <! [cdata[the name of hacker]]> </xsd:documentation> </xsd:annotation > </xsd:attribute> <xsd:aTtribute name= "Age" type= "Xsd:int" use= "optional" default= "0" > <xsd:annotation> <xsd:documentation><! [cdata[the age of Hacker.] ></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name= "Language" type= "xsd:string" use= "optional" > <xsd:annotation> <xsd:documentation><! [cdata[the language of hacker.] ></xsd:documentation> </xsd:annotation> </xsd:attribute> <xsd:attribute name= "Ishide" type= "Xsd:boolean" use= "optional" > <xsd:annotation> <xsd:documentation><! [cdata[the status of Hacker.]] ></xsd:documentation> </xsd:annotation> </xsd:attribute> </xsd:extension> </xsd:complexcontent> </xsd:complexType> <xsd:element name= "hacker" type= "Hacktype" > <xsd:anno Tation> <xsd:documentation><! [cdata[the hacker config]]></xsd:documentation> </xsd:annotation> </xsd:element></xsd: Schema>
Note the above
Xmlns= "Http://code.fengzheng.com/schema/kite" and targetnamespace= "Http://code.fengzheng.com/schema/kite"
There's a place to use in a minute.
3, the implementation of the Namespacehandler class, the code is as follows:
Package Kite.lab.spring.config;import Org.slf4j.logger;import Org.slf4j.loggerfactory;import org.springframework.beans.factory.xml.namespacehandlersupport;/** * Hacknamespacehandler * @author Fengzheng * * public class Hacknamespacehandler extends Namespacehandlersupport { private static final Logger Logger = Loggerfactor Y.getlogger (hacknamespacehandler.class); @Override public void init () { Logger.info ("Init method for executing Hacknamespacehandler"); Registerbeandefinitionparser ("Hacker", New Hackbeandefinitionparser (Hacker.class)); Logger.info ("Register"hacker"definition Converter succeeded");} }
This kind of functionality is very simple, that is, inheriting the Namespacehandlersupport class, and overloading the Init method, calling the Registerbeandefinitionparser method, where the first parameter, hacker, is the spring The name to be used in the configuration file, that is, <kite:hacker> hacker here;
The second argument is the next one.
4, the implementation of the Beandefinitionparser class, the role of this class is simply the first step to implement the class and Spring XML life of the bean to do the association, to implement the attribute injection, to see the code:
Package Kite.lab.spring.config;import Org.slf4j.logger;import Org.slf4j.loggerfactory;import Org.springframework.beans.mutablepropertyvalues;import org.springframework.beans.factory.config.BeanDefinition; Import Org.springframework.beans.factory.support.beandefinitionregistry;import Org.springframework.beans.factory.support.rootbeandefinition;import Org.springframework.beans.factory.xml.beandefinitionparser;import Org.springframework.beans.factory.xml.parsercontext;import Org.w3c.dom.element;import Java.lang.reflect.Field; Import Java.lang.reflect.method;import java.lang.reflect.parameter;/** * hackbeandefinitionparser * * @author Fengzheng */public class Hackbeandefinitionparser implements Beandefinitionparser {private static final Logger Logger = Loggerfactory.getlogger (Hackbeandefinitionparser.class); Private final class<?> Beanclass; Public Hackbeandefinitionparser (class<?> beanclass) {this.beanclass = Beanclass; } @Override Public BeandefinitiOn parse (element element, ParserContext ParserContext) {Logger.info ("Parse method to enter Hckbeandefinitionparser"); try {String id = element.getattribute ("id"); Rootbeandefinition rootbeandefinition = new Rootbeandefinition (); Rootbeandefinition.setbeanclass (Beanclass); Rootbeandefinition.setlazyinit (FALSE); You must register before you can implement injection Parsercontext.getregistry (). Registerbeandefinition (ID, rootbeandefinition); String name = Element.getattribute ("name"); String age = Element.getattribute ("Age"); String language = Element.getattribute ("language"); String ishide = Element.getattribute ("Ishide"); Mutablepropertyvalues PVs = Rootbeandefinition.getpropertyvalues (); Pvs.add ("name", name); Pvs.add ("Age", integer.valueof (age)); Pvs.add ("language", language); Pvs.add ("Hide", ishide.equals (null)? False:Boolean.valueOf (Ishide)); return rootbeandefinition; } catch (Exception e) {e.printstacktrace (); } return null; }}
This class implements self-Beandefinitionparser and overloads the Parse method, which has two parameters, and the first element can be understood as the entity corresponding to the bean of the Spring XML configuration, by Element.getattribute The ParserContext method can get the configured parameter values, and the second parameter, which can be understood as the interface object provided by Spring, implements the injection of the registered bean.
By rootbeandefinition the Getpropertyvalues method of the Entity object, you get the property kv collection of the custom bean, and then add the property value like it.
Note: The key in the KV collection is not the attribute name in the entity class, but the parameter name of the setter method for the property, for example, if the Boolean parameter is named is, the parameter of the corresponding setter method is removed when the setter method is automatically generated using the editor, and the following String to do hump naming rules processing. Of course, if you want to avoid, you can write your own setter method.
5. Registering handler and XSD schema
Spring specifies two XML registration files and specifies that the two files must be in the Meta-inf directory under the project resource Directory, and that the file name and format be fixed.
Spring.handlers for registering the third-step implementation of the Handler class
The contents are as follows:
Http\://code.fengzheng.com/schema/kite=kite.lab.spring.config.hacknamespacehandler
This is a key-value pair form, preceded by a namespace, the first step has been mentioned, here is used, after the equal sign is the full class name of the Handler class. Note the colon before the escape character
Spring.schemas used to register the XSD file in the second step
The contents are as follows:
Http\://code.fengzheng.com/schema/kite/kite.xsd=meta-inf/hacker.xsd
The equals sign is preceded by the XSD path of the declaration, followed by the actual XSD path.
6. Use in Spring configuration file
<?xml version= "1.0" encoding= "UTF-8"? ><beans xmlns= "Http://www.springframework.org/schema/beans" Xmlns:xsi= "Http://www.w3.org/2001/XMLSchema-instance" xmlns:kite= "Http://code.fengzheng.com/schema/kite" xsi:schemalocation= " Http://www.springframework.org/schema/beans http://www.springframework.org/ Schema/beans/spring-beans.xsd http://code.fengzheng.com/schema/kite http://code.fengzheng.com/schema/ Kite/kite.xsd "> <kite:hacker id=" hacker "Name=" Moon "language=" 中文版 "age=" ishide= "true"/></ Beans>
Notice that the namespace was introduced earlierxmlns:kite="http://code.fengzheng.com/schema/kite”,后面指定了 xsd 文件位置
http://code.fengzheng.com/schema/kite http://code.fengzheng.com/schema/kite/kite.xsd
7. Testing
How to get a configuration file directly test
public static void Main (string[] args) { ApplicationContext ac = new Classpathxmlapplicationcontext (" Application.xml "); Hacker Hacker = (Hacker) ac.getbean ("Hacker"); System.out.println (Hacker.tostring ()); }
Using the Springjunit4classrunner test
@RunWith (Springjunit4classrunner.class) @ContextConfiguration (locations = {"Classpath:application.xml"}) public Class Hacktest { @Resource (name = "Hacker") private hacker hacker; @Test public void PropertyTest () { System.out.println (hacker.tostring ());} }
Test results
This article is just a brief description of the implementation steps, the specific responsibility for the operation can refer to Dubbo, code in the Dubbo-config-spring module, of course, can also read the spring source, such as view <context:component-scan> implementation, in The spring-context-version number module.
The ancient Kite "the public number" Gushidefengzheng
Spring implements the extension of a custom bean