Java自定義Spring配置標(biāo)簽
引言:
在Sping中,一般使用<bean>這樣的元素來(lái)配置一個(gè)bean,Spring在創(chuàng)建容器的時(shí)候會(huì)掃描這些配置,根據(jù)配置創(chuàng)建對(duì)象存放于容器中,然后我們?cè)購(gòu)娜萜髦腥〕?,或者在配置其他bean的時(shí)候作為屬性注入。使用bean配置的一個(gè)限制是我們必須遵循配置文件的XML Schema定義,這在大多數(shù)情況下不會(huì)出現(xiàn)問(wèn)題。但是在一些情況下,我們希望實(shí)現(xiàn)更為靈活的bean配置。Spring為此提供了 Custom tag Support,也稱為Extensible XML Authoring。通過(guò)這個(gè)拓展點(diǎn),我們可以靈活定制自己需要的配置格式。
例如,如果我們使用了責(zé)任鏈設(shè)計(jì)應(yīng)用程序,那么我們可能希望用下面的方式來(lái)配置責(zé)任鏈:
<chain id="orderChain" class="foo.bar"> <handler> handler1</handler> <hadnler> handler2</handler> </chain>
檔Spring創(chuàng)建容器時(shí),掃描到這樣的元素的時(shí)候,會(huì)根據(jù)我們事先的定義實(shí)例化一個(gè)責(zé)任鏈對(duì)象,并填充屬性。因此,這種特殊的<chain>標(biāo)簽可以作為<bean>標(biāo)簽以外的另一種形式。借助Spring的Custome Tag,我們完全可以實(shí)現(xiàn)這樣的bean配置。在產(chǎn)品級(jí)的應(yīng)用框架中,可以實(shí)現(xiàn)更為復(fù)雜的定制標(biāo)簽元素。作為一個(gè)入門級(jí)別的介紹,我們定義一個(gè)用于配置日期格式化的一個(gè)類SimpleDateFormat。當(dāng)然,使用傳統(tǒng)的<bean>完全夠用,我們這里只是作為例子。
一個(gè)HelloWorld例子:
定制標(biāo)簽的第一步是要定義標(biāo)簽元素的XML結(jié)構(gòu),也就是采用XSD來(lái)元素我們要定制的元素的結(jié)構(gòu)時(shí)怎樣的。
我們定義如下一個(gè)簡(jiǎn)單的XSD:
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.mycompany.com/schema/myns" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" targetNamespace="http://www.mycompany.com/schema/myns" elementFormDefault="qualified" attributeFormDefault="unqualified"> <xsd:import namespace="http://www.springframework.org/schema/beans"/> <xsd:element name="dateformat"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:attribute name="lenient" type="xsd:boolean"/> <xsd:attribute name="pattern" type="xsd:string" use="required"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element> </xsd:schema>
在這個(gè)XSD定義中,有一個(gè)標(biāo)簽叫dateformat,這就是我們用來(lái)替換bean標(biāo)簽的自定義標(biāo)簽。注意到我們導(dǎo)入了Spring本身的beans命名空間,并且在beans:identifiedType基礎(chǔ)之上定義dateformat標(biāo)簽。也就是我們這個(gè)標(biāo)簽可以像<bean>標(biāo)簽一樣擁有id屬性。同時(shí)我們?cè)黾恿藘蓚€(gè)屬性lenient和pattern。這有點(diǎn)繼承的味道。
定義完XSD之后,我們要告訴Spring遇到這樣的標(biāo)記(命名空間+元素名稱)時(shí),如何創(chuàng)建對(duì)象。Spring中,完成這個(gè)任務(wù)的是NamespaceHandler。因此我們需要提供一個(gè)NamespaceHandler實(shí)現(xiàn)來(lái)處理自定義的<dateformat>標(biāo)簽元素。
一個(gè)簡(jiǎn)單的實(shí)現(xiàn)如下:
package extensiblexml.customtag; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class MyNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("dateformat", new SimpleDateFormatBeanDefinitionParser()); } }
我們?cè)诔跏蓟椒ㄖ凶?cè)了一個(gè)Bean定義的解析器,這個(gè)解析器就是用來(lái)解析定制的配置標(biāo)簽的。
其實(shí)現(xiàn)如下:
package extensiblexml.customtag; import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser; import org.springframework.util.StringUtils; import org.w3c.dom.Element; import java.text.SimpleDateFormat; public class SimpleDateFormatBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { protected Class<SimpleDateFormat> getBeanClass(Element element) { return SimpleDateFormat.class; } @SuppressWarnings("deprecation") protected void doParse(Element element, BeanDefinitionBuilder bean) { // this will never be null since the schema explicitly requires that a value be supplied String pattern = element.getAttribute("pattern"); bean.addConstructorArg(pattern); // this however is an optional property String lenient = element.getAttribute("lenient"); if (StringUtils.hasText(lenient)) { bean.addPropertyValue("lenient", Boolean.valueOf(lenient)); } } }
這個(gè)解析器的doParse中,實(shí)現(xiàn)了解析的具體邏輯,借助Spring提供的支持類,我們可以很輕松地完成解析。以上三個(gè)文件放在同一個(gè)目錄下,即把XSD文件跟Java代碼放在同一目錄。編碼完畢之后,還需要做一些配置工作。我們必須告訴Spring我們準(zhǔn)備使用自定義的標(biāo)簽元素,告訴Spring如何解析元素,否則Spring沒(méi)那么聰明。這里需要2個(gè)配置文件,在與代碼根路徑同一級(jí)別下,床墊一個(gè)叫META-INF的文件。并在里面創(chuàng)建名為spring.handlers和spring.schemas,用于告訴Spring自定義標(biāo)簽的文檔結(jié)構(gòu)以及解析它的類。兩個(gè)文件內(nèi)容分別如下:
spring.handlers:
http\://www.mycompany.com/schema/myns=extensiblexml.customtag.MyNamespaceHandler
等號(hào)的左邊是XSD定義中的targetNamespace屬性,右邊是NamespaceHandler的全稱限定名。
spring.schemas:
http\://www.mycompany.com/schema/myns/myns.xsd=extensiblexml/customtag/myns.xsd
然后像往常一樣配置bean,作為簡(jiǎn)單的測(cè)試,我們定義一個(gè)bean:
<?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:myns="http://www.mycompany.com/schema/myns" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.mycompany.com/schema/myns http://www.mycompany.com/schema/myns/myns.xsd" > <myns:dateformat id="defaultDateFormat" pattern="yyyy-MM-dd HH:mm" lenient="true" /> </beans>
在Eclipse中,整個(gè)項(xiàng)目結(jié)構(gòu)如下圖:
最后我們寫(xiě)個(gè)測(cè)試類測(cè)試一下能否工作:
package extensiblexml.customtag; import java.text.SimpleDateFormat; import java.util.Date; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext( "beans.xml"); SimpleDateFormat format = (SimpleDateFormat) context .getBean("defaultDateFormat"); System.out.println(format.format(new Date())); } }
一切正常,輸出如下:
更實(shí)用的例子
第一個(gè)例子主要是為了舉例,在實(shí)際中用處不大,我們接著來(lái)看一個(gè)更復(fù)雜的自定義標(biāo)簽。我們自定義一個(gè)<fileList>標(biāo)簽,當(dāng)Spring掃描到這個(gè)標(biāo)簽的時(shí)候,創(chuàng)建一個(gè)指定目錄下的File類的集合。另外,可以使用<fileFilter>對(duì)該目錄的文件進(jìn)行過(guò)濾。
如下:
<core-commons:fileList id="xmlList" directory="src/extensiblexml/example"> <core-commons:fileFilter> <bean class="org.apache.commons.io.filefilter.RegexFileFilter"> <constructor-arg value=".*.java" /> </bean> </core-commons:fileFilter> </core-commons:fileList>
上面的bean定義中,我們從“src/extensible/example”目錄中篩選出java源碼文件。
使用下面的測(cè)試迭代輸出文件名:
@SuppressWarnings("unchecked") List<File> fileList = (List<File>) context.getBean("xmlList"); for (File file : fileList) { System.out.println(file.getName()); }
輸出結(jié)果如下:
根據(jù)第一個(gè)例子中的步驟,各部分配置及代碼如下:
core-commons-1.0.xsd:
<?xml version="1.0" encoding="UTF-8"?> <xsd:schema xmlns="http://www.example.com/schema/core-commons-1.0" targetNamespace="http://www.example.com/schema/core-commons-1.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" elementFormDefault="qualified" attributeFormDefault="unqualified" version="1.0"> <xsd:import namespace="http://www.springframework.org/schema/beans" schemaLocation="http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"/> <xsd:element name="fileList"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:sequence> <xsd:element ref="fileFilter" minOccurs="0" maxOccurs="1"/> <xsd:element ref="fileList" minOccurs="0" maxOccurs="unbounded"/> </xsd:sequence> <xsd:attribute name="directory" type="xsd:string"/> <xsd:attribute name="scope" type="xsd:string"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element> <xsd:element name="fileFilter"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:group ref="limitedType"/> <xsd:attribute name="scope" type="xsd:string"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element> <xsd:group name="limitedType"> <xsd:sequence> <xsd:choice minOccurs="1" maxOccurs="unbounded"> <xsd:element ref="beans:bean"/> <xsd:element ref="beans:ref"/> <xsd:element ref="beans:idref"/> <xsd:element ref="beans:value"/> <xsd:any minOccurs="0"/> </xsd:choice> </xsd:sequence> </xsd:group> </xsd:schema>
CoreNamespaceHandler.java:
package extensiblexml.example; import org.springframework.beans.factory.xml.NamespaceHandlerSupport; public class CoreNamespaceHandler extends NamespaceHandlerSupport { @Override public void init() { this.registerBeanDefinitionParser("fileList", new FileListDefinitionParser()); this.registerBeanDefinitionParser("fileFilter", new FileFilterDefinitionParser()); } }
FileListDefinitionParser.java:
public class FileListDefinitionParser extends AbstractSingleBeanDefinitionParser { /** * The bean that is created for this tag element * * @param element The tag element * @return A FileListFactoryBean */ @Override protected Class<?> getBeanClass(Element element) { return FileListFactoryBean.class; } /** * Called when the fileList tag is to be parsed * * @param element The tag element * @param ctx The context in which the parsing is occuring * @param builder The bean definitions build to use */ @Override protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) { // Set the directory property builder.addPropertyValue("directory", element.getAttribute("directory")); // Set the scope builder.setScope(element.getAttribute("scope")); // We want any parsing to occur as a child of this tag so we need to make // a new one that has this as it's owner/parent ParserContext nestedCtx = new ParserContext(ctx.getReaderContext(), ctx.getDelegate(), builder.getBeanDefinition()); // Support for filters Element exclusionElem = DomUtils.getChildElementByTagName(element, "fileFilter"); if (exclusionElem != null) { // Just make a new Parser for each one and let the parser do the work FileFilterDefinitionParser ff = new FileFilterDefinitionParser(); builder.addPropertyValue("filters", ff.parse(exclusionElem, nestedCtx)); } // Support for nested fileList List<Element> fileLists = DomUtils.getChildElementsByTagName(element, "fileList"); // Any objects that created will be placed in a ManagedList // so Spring does the bulk of the resolution work for us ManagedList<Object> nestedFiles = new ManagedList<Object>(); if (fileLists.size() > 0) { // Just make a new Parser for each one and let them do the work FileListDefinitionParser fldp = new FileListDefinitionParser(); for (Element fileListElem : fileLists) { nestedFiles.add(fldp.parse(fileListElem, nestedCtx)); } } // Support for other tags that return File (value will be converted to file) try { // Go through any other tags we may find. This does not mean we support // any tag, we support only what parseLimitedList will process NodeList nl = element.getChildNodes(); for (int i=0; i<nl.getLength(); i++) { // Parse each child tag we find in the correct scope but we // won't support custom tags at this point as it coudl destablize things DefinitionParserUtil.parseLimitedList(nestedFiles, nl.item(i), ctx, builder.getBeanDefinition(), element.getAttribute("scope"), false); } } catch (Exception e) { throw new RuntimeException(e); } // Set the nestedFiles in the properties so it is set on the FactoryBean builder.addPropertyValue("nestedFiles", nestedFiles); } public static class FileListFactoryBean implements FactoryBean<Collection<File>> { String directory; private Collection<FileFilter> filters; private Collection<File> nestedFiles; @Override public Collection<File> getObject() throws Exception { // These can be an array list because the directory will have unique's and the nested is already only unique's Collection<File> files = new ArrayList<File>(); Collection<File> results = new ArrayList<File>(0); if (directory != null) { // get all the files in the directory File dir = new File(directory); File[] dirFiles = dir.listFiles(); if (dirFiles != null) { files = Arrays.asList(dirFiles); } } // If there are any files that were created from the nested tags, // add those to the list of files if (nestedFiles != null) { files.addAll(nestedFiles); } // If there are filters we need to go through each filter // and see if the files in the list pass the filters. // If the files does not pass any one of the filters then it // will not be included in the list if (filters != null) { boolean add; for (File f : files) { add = true; for (FileFilter ff : filters) { if (!ff.accept(f)) { add = false; break; } } if (add) results.add(f); } return results; } return files; } @Override public Class<?> getObjectType() { return Collection.class; } @Override public boolean isSingleton() { return false; } public void setDirectory(String dir) { this.directory = dir; } public void setFilters(Collection<FileFilter> filters) { this.filters = filters; } /** * What we actually get from the processing of the nested tags * is a collection of files within a collection so we flatten it and * only keep the uniques */ public void setNestedFiles(Collection<Collection<File>> nestedFiles) { this.nestedFiles = new HashSet<File>(); // keep the list unique for (Collection<File> nested : nestedFiles) { this.nestedFiles.addAll(nested); } } } }
FileFilterDefinitionParser.java
public class FileFilterDefinitionParser extends AbstractSingleBeanDefinitionParser { /** * The bean that is created for this tag element * * @param element The tag element * @return A FileFilterFactoryBean */ @Override protected Class<?> getBeanClass(Element element) { return FileFilterFactoryBean.class; } /** * Called when the fileFilter tag is to be parsed * * @param element The tag element * @param ctx The context in which the parsing is occuring * @param builder The bean definitions build to use */ @Override protected void doParse(Element element, ParserContext ctx, BeanDefinitionBuilder builder) { // Set the scope builder.setScope(element.getAttribute("scope")); try { // All of the filters will eventually end up in this list // We use a 'ManagedList' and not a regular list because anything // placed in a ManagedList object will support all of Springs // functionalities and scopes for us, we dont' have to code anything // in terms of reference lookups, EL, etc ManagedList<Object> filters = new ManagedList<Object>(); // For each child node of the fileFilter tag, parse it and place it // in the filtes list NodeList nl = element.getChildNodes(); for (int i=0; i<nl.getLength(); i++) { DefinitionParserUtil.parseLimitedList(filters, nl.item(i), ctx, builder.getBeanDefinition(), element.getAttribute("scope")); } // Add the filtes to the list of properties (this is applied // to the factory beans setFilters below) builder.addPropertyValue("filters", filters); } catch (Exception e) { throw new RuntimeException(e); } } public static class FileFilterFactoryBean implements FactoryBean<Collection<FileFilter>> { private final List<FileFilter> filters = new ArrayList<FileFilter>(); @Override public Collection<FileFilter> getObject() throws Exception { return filters; } @Override public Class<?> getObjectType() { return Collection.class; } @Override public boolean isSingleton() { return false; } /** * Go through the list of filters and convert the String ones * (the ones that were set with <value> and make them NameFileFilters */ public void setFilters(Collection<Object> filterList) { for (Object o : filterList) { if (o instanceof String) { filters.add(new NameFileFilter(o.toString())); } else if (o instanceof FileFilter) { filters.add((FileFilter)o); } } } } }
DefinitionParserUtil.java:
package extensiblexml.example; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanDefinitionHolder; import org.springframework.beans.factory.support.BeanDefinitionReaderUtils; import org.springframework.beans.factory.support.DefaultListableBeanFactory; import org.springframework.beans.factory.support.ManagedList; import org.springframework.beans.factory.xml.BeanDefinitionParserDelegate; import org.springframework.beans.factory.xml.ParserContext; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; import org.w3c.dom.Element; import org.w3c.dom.Node; public class DefinitionParserUtil { /** * Parses the children of the passed in ParentNode for the following tags: * <br/> * value * ref * idref * bean * property * *custom* * <p/> * * The value tag works with Spring EL even in a Spring Batch scope="step" * * @param objects The list of resultings objects from the parsing (passed in for recursion purposes) * @param parentNode The node who's children should be parsed * @param ctx The ParserContext to use * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean * (i.e. the Bean that is the parentNode) * @param scope The scope to execute in. Checked if 'step' to provide Spring EL * support in a Spring Batch env * @throws Exception */ public static void parseLimitedList(ManagedList<Object> objects, Node node, ParserContext ctx, BeanDefinition parentBean, String scope) throws Exception { parseLimitedList(objects, node, ctx, parentBean, scope, true); } /** * Parses the children of the passed in ParentNode for the following tags: * <br/> * value * ref * idref * bean * property * *custom* * <p/> * * The value tag works with Spring EL even in a Spring Batch scope="step" * * @param objects The list of resultings objects from the parsing (passed in for recursion purposes) * @param parentNode The node who's children should be parsed * @param ctx The ParserContext to use * @param parentBean The BeanDefinition of the bean who is the parent of the parsed bean * (i.e. the Bean that is the parentNode) * @param scope The scope to execute in. Checked if 'step' to provide Spring EL * support in a Spring Batch env * @param supportCustomTags Should we support custom tags within our tags? * @throws Exception */ @SuppressWarnings("deprecation") public static void parseLimitedList(ManagedList<Object> objects, Node node, ParserContext ctx, BeanDefinition parentBean, String scope, boolean supportCustomTags) throws Exception { // Only worry about element nodes if (node.getNodeType() == Node.ELEMENT_NODE) { Element elem = (Element)node; String tagName = node.getLocalName(); if (tagName.equals("value")) { String val = node.getTextContent(); // to get around an issue with Spring Batch not parsing Spring EL // we will do it for them if (scope.equals("step") && (val.startsWith("#{") && val.endsWith("}")) && (!val.startsWith("#{jobParameters")) ) { // Set up a new EL parser ExpressionParser parser = new SpelExpressionParser(); // Parse the value Expression exp = parser.parseExpression(val.substring(2, val.length()-1)); // Place the results in the list of created objects objects.add(exp.getValue()); } else { // Otherwise, just treat it as a normal value tag objects.add(val); } } // Either of these is a just a lookup of an existing bean else if (tagName.equals("ref") || tagName.equals("idref")) { objects.add(ctx.getRegistry().getBeanDefinition(node.getTextContent())); } // We need to create the bean else if (tagName.equals("bean")) { // There is no quick little util I could find to create a bean // on the fly programmatically in Spring and still support all // Spring functionality so basically I mimic what Spring actually // does but on a smaller scale. Everything Spring allows is // still supported // Create a factory to make the bean DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory(); // Set up a parser for the bean BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext()); // Parse the bean get its information, now in a DefintionHolder BeanDefinitionHolder bh = pd.parseBeanDefinitionElement(elem, parentBean); // Register the bean will all the other beans Spring is aware of BeanDefinitionReaderUtils.registerBeanDefinition(bh, beanFactory); // Get the bean from the factory. This will allows Spring // to do all its work (EL processing, scope, etc) and give us // the actual bean itself Object bean = beanFactory.getBean(bh.getBeanName()); objects.add(bean); } /* * This is handled a bit differently in that it actually sets the property * on the parent bean for us based on the property */ else if (tagName.equals("property")) { BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext()); // This method actually set eh property on the parentBean for us so // we don't have to add anything to the objects object pd.parsePropertyElement(elem, parentBean); } else if (supportCustomTags) { // handle custom tag BeanDefinitionParserDelegate pd = new BeanDefinitionParserDelegate(ctx.getReaderContext()); BeanDefinition bd = pd.parseCustomElement(elem, parentBean); objects.add(bd); } } } }
spring.schemas
http\://www.mycompany.com/schema/myns/myns.xsd=extensiblexml/customtag/myns.xsd
http\://www.example.com/schema/core-commons-1.0.xsd=extensiblexml/example/core-commons-1.0.xsd
spring.handlers
http\://www.mycompany.com/schema/myns=extensiblexml.customtag.MyNamespaceHandler
http\://www.example.com/schema/core-commons-1.0=extensiblexml.example.CoreNamespaceHandler
小結(jié):
要自定義Spring的配置標(biāo)簽,需要一下幾個(gè)步驟:
- 使用XSD定義XML配置中標(biāo)簽元素的結(jié)構(gòu)(myns.XSD)
- 提供該XSD命名空間的處理類,它可以處理多個(gè)標(biāo)簽定義(MyNamespaceHandler.java)
- 為每個(gè)標(biāo)簽元素的定義提供解析類。(SimpleDateFormatBeanDefinitionParser.java)
- 兩個(gè)特殊文件通知Spring使用自定義標(biāo)簽元素(spring.handlers 和spring.schemas)
到此這篇關(guān)于Java自定義Spring配置標(biāo)簽的文章就介紹到這了,更多相關(guān)Java Spring配置標(biāo)簽內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
手把手教你實(shí)現(xiàn)Java第三方應(yīng)用登錄
本文主要介紹了手把手教你實(shí)現(xiàn)Java第三方應(yīng)用登錄,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2021-08-08Spring boot 路徑映射的實(shí)現(xiàn)
這篇文章主要介紹了spring boot 路徑映射的實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-11-11java 通過(guò)cmd 調(diào)用命令啟動(dòng)tomcat的操作
這篇文章主要介紹了java 通過(guò)cmd 調(diào)用命令啟動(dòng)tomcat的操作,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2020-11-11Java16 JDK安裝并設(shè)置環(huán)境變量的方法步驟
突然想起自己大學(xué)剛接觸java的時(shí)候,要下載JDK和配置環(huán)境變量,那時(shí)候我上網(wǎng)找了很多教學(xué),本文就詳細(xì)的介紹一下Java16 JDK安裝并設(shè)置環(huán)境變量,感興趣的可以了解一下2021-09-09spring boot 常見(jiàn)http請(qǐng)求url參數(shù)獲取方法
這篇文章主要介紹了spring boot 常見(jiàn)http請(qǐng)求url參數(shù)獲取,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03java高并發(fā)InterruptedException異常引發(fā)思考
這篇文章主要為大家介紹了java高并發(fā)InterruptedException異常引發(fā)思考,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-08-08SpringCloud服務(wù)的發(fā)現(xiàn)與調(diào)用詳解
在Java微服務(wù)越來(lái)越火的今天。幾乎什么公司都在搞微服務(wù)。而使用的比較多的就是Spring?Cloud技術(shù)棧。今天就來(lái)研究一下Spring?Cloud中服務(wù)發(fā)現(xiàn)與調(diào)用的基本原理2022-07-07