文章目录

  • 1.概述
  • 2.依赖回顾
  • 2.依赖对象的初始化方式
    • 2.1 通过构造器设置依赖对象
    • 2.2 通过set方法设置依赖对象
  • 3.spring依赖注入
    • 3.1 通过构造器注入
      • 3.1.1 根据构造器参数索引注入
        • 3.1.1.1 案例
        • 3.1.1.2 优缺点
      • 3.1.2根据构造器参数类型注入
        • 3.1.2.1 优缺点
      • 3.1.3 根据构造器参数名称注入
    • 3.2 setter注入
      • 3.2.1 优缺点
    • 3.3 注入容器中的bean
      • 3.3.1 ref属性方式
      • 3.3.2 内置bean的方式
    • 3.4 其他类型注入
  • 4 总结

1.概述

转载:添加链接描述

2.依赖回顾

通常情况下,系统中类和类之间是有依赖关系的,如果一个类对外提供的功能需要通过调用其他类的方法来实现的时候,说明这两个类之间存在依赖关系,如:

public class UserService{public void insert(UserModel model){//插入用户信息}
}public class UserController{private UserService userService;public void insert(UserModel model){this.userService.insert(model);}}

UserController中的insert方法中需要调用userServiceinsert方法,说明UserController依赖于UserService,如果userService不存在,此时UserControler无法对外提供insert操作。

那么我们创建UserController对象的时候如何将给userService设置值呢?通常有2种方法。

2.依赖对象的初始化方式

2.1 通过构造器设置依赖对象

UserController中添加一个有参构造方法,如下:

public class UserController{private UserService userService;public UserController(UserService userService){this.userService = userService;}public void insert(UserModel model){this.userService.insert(model);}}//UserController使用
UserSerivce userService = new UserService();
UserController userController = new UserController(userService);
//然后就可以使用userController对象了

2.2 通过set方法设置依赖对象

可以在UserController中给userService添加一个set方法,如:

public class UserController{private UserService userService;public setUserService(UserService userService){this.userService = userService;}public void insert(UserModel model){this.userService.insert(model);}}//UserController使用
UserSerivce userService = new UserService();
UserController userController = new UserController();
userController.setService(userService);
//然后就可以使用userController对象了

上面这些操作,将被依赖的对象设置到依赖的对象中,spring容器内部都提供了支持,这个在spirng中叫做依赖注入。

3.spring依赖注入

spring中依赖注入主要分为手动注入和自动注入,本文我们主要说一下手动注入,手动注入需要我们明确配置需要注入的对象。

刚才上面我们回顾了,将被依赖方注入到依赖方,通常有2种方式:构造函数的方式和set属性的方式,spring中也是通过这两种方式实现注入的,下面详解2种方式。

3.1 通过构造器注入

构造器的参数就是被依赖的对象,构造器注入又分为3种注入方式:

  1. 根据构造器参数索引注入

  2. 根据构造器参数类型注入

  3. 根据构造器参数名称注入

3.1.1 根据构造器参数索引注入

用法

<bean id="diByConstructorParamIndex" class="com.javacode2018.lesson001.demo5.UserModel"><constructor-arg index="0" value="路人甲Java"/><constructor-arg index="1" value="上海市"/>
</bean>

constructor-arg用户指定构造器的参数

index:构造器参数的位置,从0开始value:构造器参数的值,value只能用来给简单的类型设置值,value对应的属性类型只能为byte,int,long,float,double,boolean,Byte,Long,Float,Double,枚举,spring容器内部注入的时候会将value的值转换为对应的类型。

3.1.1.1 案例

UserModel.java

package com.javacode2018.lesson001.demo5;public class UserModel {private String name;private int age;//描述信息private String desc;public UserModel() {}public UserModel(String name, String desc) {this.name = name;this.desc = desc;}public UserModel(String name, int age, String desc) {this.name = name;this.age = age;this.desc = desc;}@Overridepublic String toString() {return "UserModel{" +"name='" + name + '\'' +", age=" + age +", desc='" + desc + '\'' +'}';}
}

注意上面的3个构造函数。

diByConstructorParamIndex.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><!-- 通过构造器参数的索引注入 --><bean id="diByConstructorParamIndex" class="com.javacode2018.lesson001.demo5.UserModel"><constructor-arg index="0" value="路人甲Java"/><constructor-arg index="1" value="我是通过构造器参数位置注入的"/></bean></beans>

上面创建UserModel实例代码相当于下面代码:

UserModel userModel = new UserModel("路人甲Java","我是通过构造器参数类型注入的");

工具类IoUtils.java

package com.javacode2018.lesson001.demo5;import org.springframework.context.support.ClassPathXmlApplicationContext;/*** 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!*/
public class IocUtil {public static ClassPathXmlApplicationContext context(String beanXml) {return new ClassPathXmlApplicationContext(beanXml);}
}

测试用例DiTest.java

package com.javacode2018.lesson001.demo5;import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;/*** 公众号:路人甲Java,工作10年的前阿里P7分享Java、算法、数据库方面的技术干货!坚信用技术改变命运,让家人过上更体面的生活!*/
public class DiTest {/*** 通过构造器的参数位置注入*/@Testpublic void diByConstructorParamIndex() {String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diByConstructorParamIndex.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diByConstructorParamIndex"));}}

效果
运行diByConstructorParamIndex输出

UserModel{name='路人甲Java', age=0, desc='我是通过构造器参数位置注入的'}

3.1.1.2 优缺点

  1. 参数位置的注入对参数顺序有很强的依赖性,若构造函数参数位置被人调整过,会导致注入出错。

  2. 不过通常情况下,不建议去在代码中修改构造函数,如果需要新增参数的,可以新增一个构造函数来实现,这算是一种扩展,不会影响目前已有的功能。

3.1.2根据构造器参数类型注入

用法

<bean id="diByConstructorParamType" class="com.javacode2018.lesson001.demo5.UserModel"><constructor-arg type="参数类型" value="参数值"/><constructor-arg type="参数类型" value="参数值"/>
</bean>

constructor-arg用户指定构造器的参数

type:构造函数参数的完整类型,如:java.lang.String,int,doublevalue:构造器参数的值,value只能用来给简单的类型设置值

案例
diByConstructorParamType.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><!-- 通过构造器参数的类型注入 --><bean id="diByConstructorParamType" class="com.javacode2018.lesson001.demo5.UserModel"><constructor-arg type="int" value="30"/><constructor-arg type="java.lang.String" value="路人甲Java"/><constructor-arg type="java.lang.String" value="我是通过构造器参数类型注入的"/></bean></beans>

上面创建UserModel实例代码相当于下面代码:

UserModel userModel = new UserModel("路人甲Java",30,"我是通过构造器参数类型注入的");

新增测试用例
DiTest类中新增一个测试用例

/*** 通过构造器的参数类型注入*/
@Test
public void diByConstructorParamType() {String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diByConstructorParamType.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diByConstructorParamType"));
}

效果
运行diByConstructorParamType输出

UserModel{name='路人甲Java', age=30, desc='我是通过构造器参数类型注入的'}

3.1.2.1 优缺点

实际上按照参数位置或者按照参数的类型注入,都有一个问题,很难通过bean的配置文件,知道这个参数是对应UserModel中的那个属性的,代码的可读性不好,比如我想知道这每个参数对应UserModel中的那个属性,必须要去看UserModel的源码,下面要介绍按照参数名称注入的方式比上面这2种更优秀一些。

3.1.3 根据构造器参数名称注入

用法

<bean id="diByConstructorParamName" class="com.javacode2018.lesson001.demo5.UserModel"><constructor-arg name="参数类型" value="参数值"/><constructor-arg name="参数类型" value="参数值"/>
</bean>

constructor-arg用户指定构造器的参数

name:构造参数名称value:构造器参数的值,value只能用来给简单的类型设置值

关于方法参数名称的问题

java通过反射的方式可以获取到方法的参数名称,不过源码中的参数通过编译之后会变成class对象,通常情况下源码变成class文件之后,参数的真实名称会丢失,参数的名称会变成arg0,arg1,arg2这样的,和实际参数名称不一样了,如果需要将源码中的参数名称保留在编译之后的class文件中,编译的时候需要用下面的命令:

javac -parameters java源码

但是我们难以保证编译代码的时候,操作人员一定会带上-parameters参数,所以方法的参数可能在class文件中会丢失,导致反射获取到的参数名称和实际参数名称不符,这个我们需要先了解一下。

参数名称可能不稳定的问题,spring提供了解决方案,通过ConstructorProperties注解来定义参数的名称,将这个注解加在构造方法上面,如下:

@ConstructorProperties({"第一个参数名称", "第二个参数的名称",..."第n个参数的名称"})
public 类名(String p1, String p2...,参数n) {}

案例

CarModel.java

package com.javacode2018.lesson001.demo5;import java.beans.ConstructorProperties;public class CarModel {private String name;//描述信息private String desc;public CarModel() {}@ConstructorProperties({"name", "desc"})public CarModel(String p1, String p2) {this.name = p1;this.desc = p2;}@Overridepublic String toString() {return "CarModel{" +"name='" + name + '\'' +", desc='" + desc + '\'' +'}';}
}

diByConstructorParamName.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><!-- 通过构造器参数的名称注入 --><bean id="diByConstructorParamName" class="com.javacode2018.lesson001.demo5.CarModel"><constructor-arg name="desc" value="我是通过构造器参数类型注入的"/><constructor-arg name="name" value="保时捷Macans"/></bean></beans>

上面创建CarModel实例代码相当于下面代码:

CarModel carModel = new CarModel("保时捷Macans","我是通过构造器参数类型注入的");

新增测试用例

DiTest类中新增一个测试用例

/*** 通过构造器的参数名称注入*/
@Test
public void diByConstructorParamName() {String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diByConstructorParamName.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diByConstructorParamName"));
}

效果
运行diByConstructorParamName输出

CarModel{name='保时捷Macans', desc='我是通过构造器参数类型注入的'}

3.2 setter注入

通常情况下,我们的类都是标准的javabean,javabean类的特点:

  1. 属性都是private访问级别的
  2. 属性通常情况下通过一组setter(修改器)和getter(访问器)方法来访问
  3. setter方法,以set开头,后跟首字母大写的属性名,如:setUserName,简单属性一般只有一个方法参数,方法返回值通常为void;
  4. getter方法,一般属性以get开头,对于boolean类型一般以is开头,后跟首字母大写的属性名,如:getUserName,isOk;

spring对符合javabean特点类,提供了setter方式的注入,会调用对应属性的setter方法将被依赖的对象注入进去。

用法

<bean id="" class=""><property name="属性名称" value="属性值" />...<property name="属性名称" value="属性值" />
</bean>

property用于对属性的值进行配置,可以有多个

name:属性的名称value:属性的值

案例
MenuModel.java

package com.javacode2018.lesson001.demo5;/*** 菜单类*/
public class MenuModel {//菜单名称private String label;//同级别排序private Integer theSort;public String getLabel() {return label;}public void setLabel(String label) {this.label = label;}public Integer getTheSort() {return theSort;}public void setTheSort(Integer theSort) {this.theSort = theSort;}@Overridepublic String toString() {return "MenuModel{" +"label='" + label + '\'' +", theSort=" + theSort +'}';}
}

diBySetter.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><bean id="diBySetter" class="com.javacode2018.lesson001.demo5.MenuModel"><property name="label" value="spring系列"/></bean></beans>

新增测试用例
DiTest类中添加一个测试方法

/*** 通过setter方法注入*/
@Test
public void diBySetter() {String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diBySetter.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diBySetter"));
}

效果
运行diBySetter输出

MenuModel{label='spring系列', theSort=null}

3.2.1 优缺点

setter注入相对于构造函数注入要灵活一些,构造函数需要指定对应构造函数中所有参数的值,而setter注入的方式没有这种限制,不需要对所有属性都进行注入,可以按需进行注入。

上面介绍的都是注入普通类型的对象,都是通过value属性来设置需要注入的对象的值的,value属性的值是String类型的,spring容器内部自动会将value的值转换为对象的实际类型。

若我们依赖的对象是容器中的其他bean对象的时候,需要用下面的方式进行注入。

3.3 注入容器中的bean

注入容器中的bean有两种写法:

ref属性方式内置bean的方式

3.3.1 ref属性方式

将上面介绍的constructor-arg或者property元素的value属性名称替换为ref,ref属性的值为容器中其他bean的名称,如:

构造器方式,将value替换为ref:

<constructor-arg ref="需要注入的bean的名称"/>

setter方式,将value替换为ref:

<property name="属性名称" ref="需要注入的bean的名称" />

3.3.2 内置bean的方式

构造器的方式:

<constructor-arg><bean class=""/>
</constructor-arg>

setter方式:

<property name="属性名称"><bean class=""/>
</property>

案例

PersonModel.java

package com.javacode2018.lesson001.demo5;public class PersonModel {private UserModel userModel;private CarModel carModel;public PersonModel() {}public PersonModel(UserModel userModel, CarModel carModel) {this.userModel = userModel;this.carModel = carModel;}public UserModel getUserModel() {return userModel;}public void setUserModel(UserModel userModel) {this.userModel = userModel;}public CarModel getCarModel() {return carModel;}public void setCarModel(CarModel carModel) {this.carModel = carModel;}@Overridepublic String toString() {return "PersonModel{" +"userModel=" + userModel +", carModel=" + carModel +'}';}
}

PersonModel中有依赖于2个对象UserModel、CarModel,下面我们通过spring将UserModel和CarModel创建好,然后注入到PersonModel中,下面创建bean配置文件

diBean.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><bean id="user" class="com.javacode2018.lesson001.demo5.UserModel"></bean><!-- 通过构造器方式注入容器中的bean --><bean id="diBeanByConstructor" class="com.javacode2018.lesson001.demo5.PersonModel"><!-- 通过ref引用容器中定义的其他bean,user对应上面定义的id="user"的bean --><constructor-arg index="0" ref="user"/><constructor-arg index="1"><bean class="com.javacode2018.lesson001.demo5.CarModel"><constructor-arg index="0" value="宾利"/><constructor-arg index="1" value=""/></bean></constructor-arg></bean><!-- 通过setter方式注入容器中的bean --><bean id="diBeanBySetter" class="com.javacode2018.lesson001.demo5.PersonModel"><!-- 通过ref引用容器中定义的其他bean,user对应上面定义的id="user"的bean --><property name="userModel" ref="user"/><property name="carModel"><bean class="com.javacode2018.lesson001.demo5.CarModel"><constructor-arg index="0" value="保时捷"/><constructor-arg index="1" value=""/></bean></property></bean></beans>

新增测试用例
DiTest中新增一个测试方法

@Test
public void diBean(){String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diBean.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diBeanByConstructor"));System.out.println(context.getBean("diBeanBySetter"));
}

效果
运行diBean用例,输出:

PersonModel{userModel=UserModel{name='null', age=0, desc='null'}, carModel=CarModel{name='宾利', desc=''}}
PersonModel{userModel=UserModel{name='null', age=0, desc='null'}, carModel=CarModel{name='保时捷', desc=''}}

3.4 其他类型注入

注入java.util.List(list元素)

<list><value>Spring</value>或<ref bean="bean名称"/>或<list></list>或<bean></bean>或<array></array>或<map></map>
</list>

注入java.util.Set(set元素)

<set><value>Spring</value>或<ref bean="bean名称"/>或<list></list>或<bean></bean>或<array></array>或<map></map>
</set>

注入java.util.Map(map元素)

<map><entry key="路人甲Java" value="30" key-ref="key引用的bean名称" value-ref="value引用的bean名称"/>或<entry><key>value对应的值,可以为任意类型</key><value>value对应的值,可以为任意类型</value></entry>
</map>

注入数组(array元素)

<array>数组中的元素
</array>

注入java.util.Properties(props元素)
Properties类相当于键值都是String类型的Map对象,使用props进行注入,如下:

<props><prop key="key1">java高并发系列</prop><prop key="key2">mybatis系列</prop><prop key="key3">mysql系列</prop>
</props>

案例

对于上面这些类型来个综合案例。

DiOtherTypeModel.java

package com.javacode2018.lesson001.demo5;import java.util.*;public class DiOtherTypeModel {private List<String> list1;private Set<UserModel> set1;private Map<String, Integer> map1;private int[] array1;private Properties properties1;public List<String> getList1() {return list1;}public void setList1(List<String> list1) {this.list1 = list1;}public Set<UserModel> getSet1() {return set1;}public void setSet1(Set<UserModel> set1) {this.set1 = set1;}public Map<String, Integer> getMap1() {return map1;}public void setMap1(Map<String, Integer> map1) {this.map1 = map1;}public int[] getArray1() {return array1;}public void setArray1(int[] array1) {this.array1 = array1;}public Properties getProperties1() {return properties1;}public void setProperties1(Properties properties1) {this.properties1 = properties1;}@Overridepublic String toString() {return "DiOtherTypeModel{" +"list1=" + list1 +", set1=" + set1 +", map1=" + map1 +", array1=" + Arrays.toString(array1) +", properties1=" + properties1 +'}';}
}

上面这个类中包含了刚才我们介绍的各种类型,下面来进行注入。

diOtherType.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans-4.3.xsd"><bean id="user1" class="com.javacode2018.lesson001.demo5.UserModel"/><bean id="user2" class="com.javacode2018.lesson001.demo5.UserModel"/><bean id="diOtherType" class="com.javacode2018.lesson001.demo5.DiOtherTypeModel"><!-- 注入java.util.List对象 --><property name="list1"><list><value>Spring</value><value>SpringBoot</value></list></property><!-- 注入java.util.Set对象 --><property name="set1"><set><ref bean="user1"/><ref bean="user2"/><ref bean="user1"/></set></property><!-- 注入java.util.Map对象 --><property name="map1"><map><entry key="路人甲Java" value="30"/><entry key="路人" value="28"/></map></property><!-- 注入数组对象 --><property name="array1"><array><value>10</value><value>9</value><value>8</value></array></property><!-- 注入java.util.Properties对象 --><property name="properties1"><props><prop key="key1">java高并发系列</prop><prop key="key2">mybatis系列</prop><prop key="key3">mysql系列</prop></props></property></bean>
</beans>

新增测试用例
DiTest类中新增一个方法

/*** 其他各种类型注入*/
@Test
public void diOtherType() {String beanXml = "classpath:/com/javacode2018/lesson001/demo5/diOtherType.xml";ClassPathXmlApplicationContext context = IocUtils.context(beanXml);System.out.println(context.getBean("diOtherType"));
}

效果
运行测试用例输出:

DiOtherTypeModel{list1=[Spring, SpringBoot], set1=[UserModel{name='null', age=0, desc='null'}, UserModel{name='null', age=0, desc='null'}], map1={路人甲Java=30, 路人=28}, array1=[10, 9, 8], properties1={key3=mysql系列, key2=mybatis系列, key1=java高并发系列}}

4 总结

本文主要讲解了xml中bean的依赖注入,都是采用硬编码的方式进行注入的,这种算是手动的方式

注入普通类型通过value属性或者value元素设置注入的值;注入对象如果是容器的其他bean的时候,需要使用ref属性或者ref元素或者内置bean元素的方式

还介绍了其他几种类型List、Set、Map、数组、Properties类型的注入,多看几遍加深理解

后面我们将介绍spring为我们提供的更牛逼的自动注入

【Spring】Spring 依赖注入之手动注入相关推荐

  1. spring自定义作用域 依赖注入之手动注入

    自定义作用域 spring容器自带的有2种作用域,分别是singleton和prototype:还有3种分别是spring web容器 环境中才支持的request.session.applicati ...

  2. 注入双括号报错注入_SQL手动注入:sqlilabs(less110)

    种一棵树最好的时间是10年前,其次是现在. 忘了是谁 前言 说实话,由于前段时间学 python ,对于 OWASP TOP10 漏洞的学习都有所落下,虽然现在也在慢慢复习当中,但是今晚谈及的 SQL ...

  3. sql注入新手入门 从实战讲解SQL注入(手动注入+MySQL+靶场源码)

    资源放在下面,自取 https://blog.csdn.net/qq_45514735?spm=1011.2266.3001.5343 1.我们首先用小皮面板和sqli-labs靶场源码搭建一个环境 ...

  4. Spring学习第4篇:Spring 的依赖注入

    大家家好,我是一名网络怪咖,北漂五年.相信大家和我一样,都有一个大厂梦,作为一名资深Java选手,深知Spring重要性,现在普遍都使用SpringBoot来开发,面试的时候SpringBoot原理也 ...

  5. Spring之依赖注入源码解析

    依赖注入底层原理流程图:Spring中Bean的依赖注入原理 | ProcessOn免费在线作图,在线流程图,在线思维导图 | 1.Spring中到底有几种依赖注入方式 手动注入和自动注入 1.手动注 ...

  6. Spring bean依赖注入、bean的装配及相关注解

    依赖注入 Spring主要提供以下两种方法用于依赖注入 基于属性Setter方法注入 基于构造方法注入 Setter方法注入 例子: public class Communication {priva ...

  7. 在ABAP里模拟实现Java Spring的依赖注入

    Dependency Injection- 依赖注入,在Java Spring框架中有着广泛地应用.通过依赖注入,我们不必在应用代码里繁琐地初始化依赖的资源,非常方便. 那么ABAP能否从语言层面上也 ...

  8. 据说,80%的人没有真正理解了Spring的依赖注入

    前言 提起Spring,大家肯定不陌生,它是每一个Java开发者绕不过去的坎.Spring 框架为基于 java 的企业应用程序提供了一整套解决方案,方便开发人员在框架基础快速进行业务开发. 在官网中 ...

  9. Spring Bean 依赖注入

    动注入 手动注入就是在XML中定义Bean时,给Bean的某个属性指定了值. set方法注入 <bean name="orderService" class="co ...

最新文章

  1. 在Ubuntu 14.04 64bit上安装redis 3.0.3
  2. SpringBoot第十五篇:Springboot整合RabbitMQ
  3. 基于Lucene/XML的站内全文检索解决方案:WebLucene 【转】
  4. swagger python自动化用例_如何让Swaggergenerated Python客户机正常工作?
  5. 深度ip转换器手机版app_房串串经纪人版app下载-房串串经纪人版app手机版 v1.0.0...
  6. 分享录制的几个 Adobe Illustrator 操作的短视频,有声、1-2 分钟一个
  7. HTML/CSS开发规范指南
  8. PHP树形结构数据处理成面包屑结构
  9. Android自定义输入车牌号键盘、车牌简称 | 数字 | 字母键盘
  10. 画图工具轻松打印长图
  11. JPush极光推送详解
  12. mac tortoisesvn客户端_TortoiseSVN Mac版
  13. C++——打印乘法口诀表
  14. 微信H5音乐自动播放
  15. 成年人社交中的10个潜规则
  16. 新型循环神经网络IndRNN:可构建更长更深的RNN
  17. mysql中in条件使用字符串
  18. 超详细MapReduce程序实现WordCount案例
  19. javascript简介,什么是 JavaScript?,JS的特征,开发环境
  20. GNSS原理与应用(四)——卫星运动基本知识

热门文章

  1. 马斯克回应“逃税”:没有从特斯拉领取高薪 一直在按规定缴税
  2. 手机变身车机导航遥控器 高德地图上线手车互联新功能
  3. 沃尔沃汽车与高德合作:首款纯电动汽车XC40 RECHARGE采用高德方案
  4. 母亲确诊的员工发烧 当当网通知全员在家办公
  5. 让携号转网不再难!但你得了解这几大限制
  6. 那些慢慢消失的手机功能,最怀念第一个!
  7. 苹果为何没赶上5G手机的“首班车”?
  8. 迅游科技上演“宫斗” 董事长总裁互相罢免
  9. 张家辉申请“渣渣辉”商标,真的很有知识产权意识了
  10. 比新iPhone好看多了!华为Mate 30系列机型外观曝光