zoukankan      html  css  js  c++  java
  • 第三章 最小化SpringXml 配置

    自动装配(autowiring):有助于减少甚至消除<property>元素和<constructor-arg>元素,让spring自动识别如何装配Bean的依赖关系。

    自动检测(autodiscovery):比自动装配更进一步,让spring能够自动识别哪些类需要被装配成sping Bean ,从而减少对<bean>元素的使用。

    3.1 自动装配Bean属性

    3.1.1 4种类型的自动装配

      byName——把与Bean的属性具有相同的名字(或者ID)的其他Bean自动装配到Bean对应的属性中。如果没有跟属性的名字像匹配的Bean,则该属性不进行装配。

      byType——把与Bean的属性具有相同类型的其他Bean自动装配到Bean的对应属性中,若果没有跟属性的类型相匹配的bean,则该属性不被装配。

      constructor——把与Bean的构造器入参具有相同类型的其他Bean自动装配到Bean构造器的对应参数中。

      autodetect——首先尝试使用constructor进行自动装配,如果失败,再尝试使用byType进行自动装配。

      byName自动装配:

     1 <!-- byName自动装配
     2          缺点:若是有多个音乐家需要装配instrument属性,则他们就会公用一个Saxopbone(即多个bean的属性被同一个bean赋值)
     3      -->
     4     <!-- 先在容器中装个乐器对象 -->
     5      <bean id="instrument" class="com.springinaction.springidol.Saxophone"></bean>
     6     <!--现在为音乐家kenny自动装配上面instrument的乐器-->
     7     <bean id="kenny" -->
     8         class="com.springinaction.springidol.Instrumentalist"
     9         autowire="byName">
    10         <property name="song" value="演员——薛之谦"></property>    
    11     </bean>
    12     
    13     <bean id="kenny1" 
    14         class="com.springinaction.springidol.Instrumentalist"
    15         autowire="byName">
    16         <property name="song" value="演员1——薛之谦"></property>    
    17     </bean> 
    View Code

      byType自动装配,容器中若是有同个类型的多个bean,自动装配的时候,会出抛出异常(就像选择性综合征,多了不知选哪个了)NoUniqueBeanDefinitionException(不唯一bean定义异常,翻译的可能不准确):

     1 <!-- byType装配 
     2         有两个乐器:kenny bean就不知道要识别那个乐器装配给自己,
     3         会抛出异常NoUniqueBeanDefinitionException(非唯一bean定义异常-个人翻译,可能不正确),
     4         但是下面会出现提示:
     5         org.springframework.beans.factory.UnsatisfiedDependencyException: 
     6             Error creating bean with name 'kenny' defined in class path resource [spring/springbean.xml]: Unsatisfied dependency expressed through bean property 'instrument': No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0; nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0
     7         Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0
     8     -->
     9     <!-- Saxophone类型的bean -->
    10      <bean class="com.springinaction.springidol.Saxophone"></bean>
    11     <!--Guitar类型的bean-->
    12     <bean class="com.springinaction.springidol.Guitar"></bean>
    13     
    14     <bean id="kenny" 
    15         class="com.springinaction.springidol.Instrumentalist"
    16         autowire="byType">
    17         <property name="song" value="演员——薛之谦"></property>    
    18     </bean> 
    View Code

      constructor自动装配:

    1 <!-- constructor 自动装配
    2         这个要求PoeticJuggler类中有一个构造器的参数是Sonnet29(他是--实现Poem的实现类)类型的
    3      -->
    4     <bean class="com.springinaction.springidol.Sonnet29"></bean>
    5 
    6     <bean id="duke" 
    7         class="com.springinaction.springidol.PoeticJuggler"
    8         autowire="constructor"/>
    View Code

       autodetect混合装配,就不介绍了。

      注:在测试代码的时候,遇到了如下的SAXParseException异常:
    Caused by: org.xml.sax.SAXParseException; lineNumber: 48; columnNumber: 45; 注释中不允许出现字符串 "--"。
        at ;
      这个是由于springbean.xml中的注释<!-- 这里是注释 -->除了开头和结尾可以有"--"外,里面不能有第三个"--",不如:<!-- 这里是是--注释 -->就会报上面的错误,这个一看就明白了。

    3.1.2 默认自动装配

      若果需要为Spring应用上下文中的额每一个Bean(或者其中大多数)配置相同的autowire属性,那么可以要求spring为它所创建的所有Bean引用相同的自动装配策略来简化配置

    3.2 使用注解装配

      使用注解方式允许更细粒度的自动装配,我们可以选择性标注某一个属性来对其应用自动装配。spring容器默认禁用注解装配,所以,在使用基于注解的自动装配,我们需要在spring配置中启用它。最简单的启用方式是使用spring的context命名空间配置中的<context:annotation-config>元素:

      spring的context命名空间: xmlns:context="http://www.springframework.org/schema/context";

       xsi:schemaLocation的值中要加:http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd;这两个东西(好像是约束,不知是啥)

      spring3支持几种不同的用于自动装配的注解:

    • spring自带的@Autowierd注解;
    • JSR-330的@Inject注解;
    • JSR-250的@Resource注解;

    3.2.1 使用@Autowired

    @Autowired是:org.springframework.beans.factory.annotation包下的Autowired接口如下:

      

      @Autowired的装配代码,比如,在Instrumentalist类型的setInstrument上标注@Autowired:

    1         //注入乐器
    2     @Autowired
    3     public void setInstrument(Instrument instrument) {
    4         this.instrument = instrument;
    5     }    
    View Code

      在springbean.xml中:

    1 <!-- Saxophone类型的bean -->
    2     <!-- <bean class="com.springinaction.springidol.Saxophone"></bean> -->
    3     <!--Guitar类型的bean(Instrument 乐器)-->
    4     <bean class="com.springinaction.springidol.Guitar"></bean> 
    5     
    6     <bean id="kenny" 
    7         class="com.springinaction.springidol.Instrumentalist">
    8         <property name="song" value="演员——薛之谦"></property>    
    9     </bean>
    View Code

      测试结果,这时容器中只有一个乐器类型的bean:

    1 //测试注解@Autowired
    2     @Test
    3     public void testAutowired() throws Exception {
    4         
    5         Instrumentalist kenny = (Instrumentalist) ac.getBean("kenny");
    6         kenny.perform();
    7         kenny.getInstrument().play();
    8         
    9     }
    View Code

       当springbean.xml中有两个乐器类型的bean时,会抛异常BeanCreationException,还是bean不唯一的问题:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'kenny': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.springinaction.springidol.Instrumentalist.setInstrument(com.springinaction.springidol.Instrument); nested exception is org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] is defined: expected single matching bean but found 2: com.springinaction.springidol.Saxophone#0,com.springinaction.springidol.Guitar#0:

    1 <!-- Saxophone类型的bean,下面两个bean同时存在的时候,会抛异常NoUniqueBeanDefinitionException -->
    2     <bean class="com.springinaction.springidol.Saxophone"></bean>
    3     <!--Guitar类型的bean(Instrument 乐器)-->
    4     <bean class="com.springinaction.springidol.Guitar"></bean> 
    5     
    6     <bean id="kenny" 
    7         class="com.springinaction.springidol.Instrumentalist">
    8         <property name="song" value="演员——薛之谦"></property>    
    9     </bean>
    View Code

      上面就抛出异常;

      @Autowired可以在构造器上面和属性上面(这里标注了就可以把setter方法删掉了)都可以标注;

      当容器中没有自动装配的bean时,会抛出 NoSuchBeanDefinitionException(没有这样的bean定义异常):
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire method: public void com.springinaction.springidol.Instrumentalist.setInstrument(com.springinaction.springidol.Instrument); nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.springinaction.springidol.Instrument] found for
      这时候@Autowired(required=false)这样装配,自动装配可选,容器中没有改类型的bean,instrument值会是空:
       @Autowired(required=false)
         private Instrument instrument;

      这时候抛异常会抛NullPointException;

      若是@Autowired注解用在构造器上时,只有一个构造器上required设置为true,其他使用@Autowired注解所标注的构造器只能将required属性设置为false。

    为解决上述问题,可以使用注解@Qualifier("guitar"),配置如下,这种配置和第二章中的<property name='instrument' ref='guitar'>原理其实应该差不多(个人觉得):

    1         @Autowired(required=false)
    2     @Qualifier("guitar")
    3     private Instrument instrument;    
    View Code
    1 <bean id="saxophone" class="com.springinaction.springidol.Saxophone"></bean> 
    2     <!--Guitar类型的bean(Instrument 乐器)-->
    3     <bean id="guitar" class="com.springinaction.springidol.Guitar"></bean> 
    4     
    5     <bean id="kenny" 
    6         class="com.springinaction.springidol.Instrumentalist">
    7         <property name="song" value="演员——薛之谦"></property>    
    8     </bean>
    View Code

      还可以如下进行配置:

    1 @Autowired(required=false)
    2 //@Qualifier("guitar")
    3 private Instrument instrument;
    View Code
     1 package com.springinaction.springidol;
     2 
     3 import org.springframework.beans.factory.annotation.Qualifier;
     4 
     5 /**
     6  * 
     7  * @ClassName: Guitar 
     8  * @Description: 乐器:吉他
     9  * @author mao
    10  * @date 2017年3月19日 下午8:15:44 
    11  *
    12  */
    13 @Qualifier("stringed")
    14 public class Guitar implements Instrument {
    15     
    16     public Guitar(){
    17         
    18     }
    19     
    20     public void play() {
    21         System.out.println("guitar guitar guitar");
    22     }
    23 
    24 }
    View Code
    1 <bean class="com.springinaction.springidol.Guitar">
    2     <qualifier value="stringed"></qualifier>
    3 </bean> 
    4     
    5 <bean id="kenny" 
    6     class="com.springinaction.springidol.Instrumentalist">
    7     <property name="song" value="演员——薛之谦"></property>    
    8 </bean>
    View Code

    测试了一下<qualifier value="stringed"></qualifier>这个有没有都可以哎,我用的spring版本是spring4.2.9的,难道版本高了,功能也自动升级了,不是很明白:

     创建自定义的限定器(Qualifiler)(这个感觉好吊的样子,因为看不懂,感觉像是自己创建了个自定义的注解)

     首先创建一个接口:

     1 package com.springinaction.springidol;
     2 
     3 import java.lang.annotation.ElementType;
     4 import java.lang.annotation.Retention;
     5 import java.lang.annotation.RetentionPolicy;
     6 import java.lang.annotation.Target;
     7 
     8 import org.springframework.beans.factory.annotation.Qualifier;
     9 
    10 @Target({ElementType.FIELD,ElementType.PARAMETER,ElementType.TYPE})
    11 @Retention(RetentionPolicy.RUNTIME)
    12 @Qualifier
    13 public @interface StringedInstrument {
    14 
    15 }
    View Code
     1 package com.springinaction.springidol;
     2 
     3 
     4 /**
     5  * 
     6  * @ClassName: Guitar 
     7  * @Description: 乐器:吉他
     8  * @author mao
     9  * @date 2017年3月19日 下午8:15:44 
    10  *
    11  */
    12 @StringedInstrument//好嘛?这应该是自定义的注解吧
    13 public class Guitar implements Instrument {
    14     
    15     public Guitar(){
    16         
    17     }
    18     
    19     public void play() {
    20         System.out.println("guitar guitar guitar");
    21     }
    22 
    23 }
    View Code
    1 @Autowired
    2 //@Qualifier("guitar")
    3 @StringedInstrument
    4 private Instrument instrument;
    View Code
    1 <bean class="com.springinaction.springidol.Guitar">
    2     <!-- <qualifier value="stringed"></qualifier> 这一行有没有不影响 -->
    3 </bean> 
    4     
    5 <bean id="kenny" 
    6     class="com.springinaction.springidol.Instrumentalist">
    7     <property name="song" value="演员——薛之谦"></property>    
    8 </bean>
    View Code
    1 //测试注解@Autowired @StringedInstrument
    2     @Test
    3     public void testAutowired() throws Exception {
    4         
    5         Instrumentalist kenny = (Instrumentalist) ac.getBean("kenny");
    6         kenny.perform();
    7         kenny.getInstrument().play();
    8         
    9     }
    View Code

    如果有多个乐器类被@StringedInstrument标注了,还需要再进行细粒度的控制,感觉这个太麻烦!要自定义很多个限定器!

    3.2.2 借助@Inject实现基于标准的自动装配

    JSR-330是一种依赖注入规范,更常见的叫法at inject

    这个要用到新的jar文件,javax.inject,所以pom.xml文件中需要引入该jar依赖;

    1 <!-- JSR-330的标准注解 -->
    2  <dependency>
    3       <groupId>javax.inject</groupId>
    4       <artifactId>javax.inject</artifactId>
    5       <version>1</version>
    6   </dependency>
    View Code

    这个jar内容如下,总共就这些类:

    @Inject注解和@Autowired一样,可以用来自动装配属性、方法和构造器;与@Autowired不同的是,@Inject没有required属性。

    @Inject他也可以限定,用@Name,@Autowired用的@Qualifier:

    1 @Inject
    2 @Named("guitar")
    3 private Instrument instrument;
    View Code

    基本用法和@Autowired差不多,@Name和@Qualifier区别:前者是通过Bean的ID来表示可选择的Bean,后者是帮助我们缩小匹配Bean的选择范围(目前没有感觉到太大的差一性);

    3.2.3 在注解注入中使用表达式

    Spring3.0引入了@Value,可以装配String类型的值和基本类型的值,例如。

    @Value("Eruption")

    private String song;

    @Value与SpEL表达式配合,才能显示他的魔力(第二章中的SpEL表达式);

    3.3 自动检测Bean

      <context:annotation-config/>需要显示定义<bean>,用<context:annotation-scan>允许spring自动检测Bean和定义Bean。为了配置Spring自动检测,需要使用<context:conponent-scan>元素代替<context:annotation-config>元素,元素会扫描指定的包及其所有子包,并查处自动注册的Spring Bean的类。

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:context="http://www.springframework.org/schema/context"
     5     xmlns:p="http://www.springframework.org/schema/p"
     6     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     7         http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     8         http://www.springframework.org/schema/context
     9         http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    10      <context:component-scan 
    11         base-package="com.springinaction.springidol">            </context:component-scan>
    12 </beans>
    View Code

    3.3.1 为自动检测标注Bean

      <context:componment-scan>查找使用构造型(stereotype)注解所标注的类:

    • @Component——通用的构造型注解,标识该类为Spring组件
    • @Constroller——标识将该类定义为SpringMVC controller
    • @Repository——标识将该类定义为数据仓库
    • @Service——标识将该类定义为服务
     1 package com.springinaction.springidol;
     2 
     3 import org.springframework.stereotype.Component;
     4 
     5 
     6 /**
     7  * 
     8  * @ClassName: Guitar 
     9  * @Description: 乐器:吉他
    10  * @author mao
    11  * @date 2017年3月19日 下午8:15:44 
    12  *
    13  */
    14 //自动将该类注册为Spring Bean。Bean的ID默认无限定类名。在这种场景下,Guitar Bean的ID为guitar
    15 @Component
    16 public class Guitar implements Instrument {
    17     
    18     public Guitar(){
    19         
    20     }
    21     
    22     public void play() {
    23         System.out.println("guitar guitar guitar");
    24     }
    25 
    26 }
    View Code
     1 package com.springinaction.springidol;
     2 
     3 import javax.inject.Inject;
     4 import javax.inject.Named;
     5 
     6 import org.springframework.beans.factory.annotation.Value;
     7 import org.springframework.stereotype.Component;
     8 
     9 
    10 
    11 /**
    12  * 
    13 * @ClassName: Instrumentalist 
    14 * @Description: 一个有天赋的音乐家
    15 * @author mao
    16 * @date 2017年3月19日 下午7:15:17 
    17 *
    18  */
    19 //将该类注册到Spring容器中,显示的为其命名为eddie。即ID为eddie
    20 @Component("eddie")
    21 public class Instrumentalist implements Performer {
    22     
    23     @Value("演员--薛之谦")
    24     private String song;
    25     
    26     @Inject
    27     @Named("guitar")
    28     private Instrument instrument;
    29     
    30     //注入乐器
    31     public void setInstrument(Instrument instrument) {
    32         this.instrument = instrument;
    33     }
    34     public Instrument getInstrument() {
    35         return instrument;
    36     }
    37     //注入歌曲
    38     public void setSong(String song) {
    39         this.song = song;
    40     }
    41     public String getSong() {
    42         return song;
    43     }
    44     
    45     public Instrumentalist(){
    46         
    47     }
    48 
    49     public void perform() throws Exception {
    50         System.out.println("Playing "+song+": ");
    51     }
    52 
    53 }
    View Code
    1 //测试注解@Component
    2 @Test
    3 public void testComponent() throws Exception {
    4         
    5     Instrumentalist eddie = (Instrumentalist) ac.getBean("eddie");
    6     eddie.perform();
    7     eddie.getInstrument().play();
    8         
    9 }
    View Code

    测试结果:

    3.4 使用spring基于java的配置

    3.4.1 创建基于Java的配置

      即使Spring的java配置可以使用XML就可以编写大多数的Spring配置,但是我们仍然需要极少量的XML来启用Java配置:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans xmlns="http://www.springframework.org/schema/beans"
     3     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     4     xmlns:context="http://www.springframework.org/schema/context"
     5     xmlns:p="http://www.springframework.org/schema/p"
     6     xsi:schemaLocation="http://www.springframework.org/schema/beans 
     7         http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
     8         http://www.springframework.org/schema/context
     9         http://www.springframework.org/schema/context/spring-context-4.0.xsd">
    10     <context:component-scan 
    11         base-package="com.springinaction.springidol*"></context:component-scan>
    12 
    13 </beans>
    View Code

      首先创建一个类,用@Configuration标注该类,使用它标注后,这个类就相当于<beans>容器了:

     1 package com.springinaction.springidol;
     2 
     3 import org.springframework.context.annotation.Bean;
     4 import org.springframework.context.annotation.Configuration;
     5 
     6 //在基于java的配置里使用@Configuration注解java类,就等价于XML配置中的<beans>
     7 //@Configuration注解会作为一个标识告知Spring:这个类将包含一个多个SpringBean的定义。
     8 //这些Bean的定义是使用@Bean注解所标注的方法。
     9 @Configuration
    10 public class SpingIdolConfig {
    11     
    12     /*
    13      * 它等价于使用XML所配置的<bean>元素。@Bean告知Sping这个方法返回一个对象,
    14      * 该对象应该被注册为Spring应用上下文中的一个Bean。方法名将作为该Bean的ID
    15      */
    16     @Bean
    17     public Performer duke(){
    18         return new Juggler();
    19     }
    20     
    21     @Bean
    22     public Performer kenny(){
    23         
    24         Instrumentalist kenny = new Instrumentalist();
    25         kenny.setSong("认真的雪Bean----薛之谦");
    26         kenny.setInstrument(guitar());
    27         return kenny;
    28         
    29     }
    30     
    31     @Bean
    32     public Instrument guitar(){
    33         return new Guitar();
    34     }
    35     
    36 }
    View Code

      此时Instrumentalist类是这样的,两个属性的注解被注释了:

     1 package com.springinaction.springidol;
     2 
     3 import javax.inject.Inject;
     4 import javax.inject.Named;
     5 
     6 import org.springframework.beans.factory.annotation.Autowired;
     7 import org.springframework.beans.factory.annotation.Qualifier;
     8 import org.springframework.beans.factory.annotation.Value;
     9 import org.springframework.stereotype.Component;
    10 
    11 
    12 
    13 /**
    14  * 
    15 * @ClassName: Instrumentalist 
    16 * @Description: 一个有天赋的音乐家
    17 * @author mao
    18 * @date 2017年3月19日 下午7:15:17 
    19 *
    20  */
    21 //将该类注册到Spring容器中,显示的为其命名为eddie。即ID为eddie
    22 
    23 public class Instrumentalist implements Performer {
    24     
    25 //    @Value("演员---薛之谦")
    26     private String song;
    27     
    28 //    @Autowired
    29 //    @Qualifier("saxophone")
    30     private Instrument instrument;
    31     
    32     //注入乐器
    33     public void setInstrument(Instrument instrument) {
    34         this.instrument = instrument;
    35     }
    36     public Instrument getInstrument() {
    37         return instrument;
    38     }
    39     //注入歌曲
    40     public void setSong(String song) {
    41         this.song = song;
    42     }
    43     public String getSong() {
    44         return song;
    45     }
    46     
    47     public Instrumentalist(){
    48         
    49     }
    50 
    51     public void perform() throws Exception {
    52         System.out.println("Playing "+song+": ");
    53     }
    54 
    55 }
    View Code

      测试代码:

    1 //测试基于java的配置
    2     @Test
    3     public void testJava() throws Exception {
    4         
    5         Instrumentalist eddie = (Instrumentalist) ac.getBean("kenny");
    6         eddie.perform();
    7         eddie.getInstrument().play();
    8         
    9     }
    View Code

      结果:

      这个基于java的注解,若是将上述的两个注释的注解解封:

     1 package com.springinaction.springidol;
     2 
     3 import javax.inject.Inject;
     4 import javax.inject.Named;
     5 
     6 import org.springframework.beans.factory.annotation.Autowired;
     7 import org.springframework.beans.factory.annotation.Qualifier;
     8 import org.springframework.beans.factory.annotation.Value;
     9 import org.springframework.stereotype.Component;
    10 
    11 
    12 
    13 /**
    14  * 
    15 * @ClassName: Instrumentalist 
    16 * @Description: 一个有天赋的音乐家
    17 * @author mao
    18 * @date 2017年3月19日 下午7:15:17 
    19 *
    20  */
    21 //将该类注册到Spring容器中,显示的为其命名为eddie。即ID为eddie
    22 
    23 public class Instrumentalist implements Performer {
    24     
    25     @Value("演员---薛之谦")
    26     private String song;
    27     
    28     @Autowired
    29     @Qualifier("saxophone")
    30     private Instrument instrument;
    31     
    32     //注入乐器
    33     public void setInstrument(Instrument instrument) {
    34         this.instrument = instrument;
    35     }
    36     public Instrument getInstrument() {
    37         return instrument;
    38     }
    39     //注入歌曲
    40     public void setSong(String song) {
    41         this.song = song;
    42     }
    43     public String getSong() {
    44         return song;
    45     }
    46     
    47     public Instrumentalist(){
    48         
    49     }
    50 
    51     public void perform() throws Exception {
    52         System.out.println("Playing "+song+": ");
    53     }
    54 
    55 }
    View Code

      结果就是:

      这个感觉像是就近原则啊(xml的注解在属性上方),要么是先是执行了基于java的配置,后又执行了基于xml的配置,xml的配置覆盖了前者的结果。

     源码下载地址

  • 相关阅读:
    彻底卸载网易UU网游加速器的方法
    OO之策略模式
    android锁屏和finish()后activity生命周期的变化
    ARTS打卡计划第6周-TIPS-多台服务器免密码登录
    ARTS打卡计划第6周-REVIEW-超越编码的避免项目失败的软技能
    ARTS打卡计划第6周-ALGORITHM
    ARTS打卡计划第5周-SHARE-java构建树形结构
    ARTS打卡计划第5周-TIPS
    ARTS打卡计划第5周-REVIEW-SpringBoot的api版本化实践
    ARTS打卡计划第5周-ALGORITHM
  • 原文地址:https://www.cnblogs.com/huaxueyihao/p/6594501.html
Copyright © 2011-2022 走看看