前言:去年到现在一直没有很好的时间完成这个spring基础+源码的博客目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事情,总之不对的地方,多多指出,我们一起徜徉代码的海洋!

我这里做每个章节去说的前提,不是一定很标准的套用一些官方名词,目的是为了让大家可以理解更加清楚,如果形容的不恰当,可以留言出来,万分感激

1、什么是注入

定义:通过Spring工厂以及配置文件,为所创建的对象的成员变量赋值

1.1、为什么需要注入

通过编码的方式,为成员变量赋值,存在耦合,举个例子:

public void Test1(){ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");Person person = (Person) ctx.getBean("person");//代码中为成员变量赋值,存在耦合person.setId(1);person.setName("doctchen");System.out.println("person = " + person);}

1.2、如何进行注入

  • 类的成员变量提供get set方法
  • 配置Spring的配置文件
<bean id="person" class="com.doctchen.spring5.entity.Person"><property name="id"><value>2</value></property><property name="name"><value>zhangsan</value></property>
</bean>

注入的好处:解耦合

2、Spring注入原理分析(简易版)

Spring通过底层调⽤对象属性对应的set⽅法,完成成员变量的赋值,这种⽅式我们也称之为set注⼊

2.1、Set注入详解

针对于不同类型的成员变量,在<property>标签,需要嵌套其他标签,这句话留个影响,后面我们做案例!

<property>xxxxxxxx
</property>

看个大概的图文表示:

图中很清楚的表示Set注入可以有两种类型的注入:1、基本的JDK内置类型       2、用户可以自定义类型

所以我们一定要注意,Set注入前提,你这些属性一定是要有Set方法,不然配置文件applicationContext.xml会报错property标签的name属性找不到

我们来看第一种

1、基本的JDK内置类型

  • private String name;
<bean id="customer" class="com.doctchen.spring5.entity.Customer"><!--    对应private String name    --><property name="name"><value>doctchen</value></property>
</bean>
  • private int age
<bean id="customer" class="com.doctchen.spring5.entity.Customer"><!--    对应private int age    --><property name="age"><value>111</value></property>
</bean>
  • private String[] emails
 <bean id="customer" class="com.doctchen.spring5.entity.Customer"><!--    对应private String[] emails    --><property name="emails"><list><value>zhangsan@qq.com</value><value>lisi@qq.com</value><value>wangwu@qq.com</value></list></property>
</bean>
  • private Set<String> tels

注意这里强调下,因为Set集合是无序的,所以后期你打印结果的值,有可能是无序的,你发现你打印的结果是按照顺序打印的,说明巧了,不一定是按照配置文件写入value的顺序打印

还有就是我在Set中定义的泛型是String,所以你的值可以写value,但是如果是private Set tels,泛型就是Object类型了;这时候就可以不止写value了,还可以加其他的类型(JDK基本+自定义),这里先提出一个类型:

<ref bean=""/> ------>自定义类型

<bean id="customer" class="com.doctchen.spring5.entity.Customer"><!--    对应private Set<String> tels    --><property name="tels"><set><value>set1@qq.com</value><value>set2@qq.com</value><value>set3@qq.com</value><value>set3@qq.com</value><value>set3@qq.com</value></set></property>
</bean>
  • private List<String> address
 <bean id="customer" class="com.doctchen.spring5.entity.Customer"><!--    对应private List<String> address    --><property name="address"><list><value>list1@qq.com</value><value>list2@qq.com</value><value>list3@qq.com</value></list></property>
</bean>
  • private Map<String,String> qqs

Map我这里要提两句,因为Map对象我们知道是由一个一个Entry对象组成,所以要有个Entry标签,其次在key中,因为我们设定泛型是两个String,key这个标签里不知道你key值的类型,所以要有个<value>标签,当然如果你key泛型为Object,自然value标签就换成<ref bean=""/>;而value值做为key的对应关系,spring为了简化代码,就直接给了<value>标签,不会是

<value><value>sss</value></value>这种类型的,同理Map的value中,也是泛型Object,你也可以套用其他的如自定义<ref bean=""/>,都可以动手试试!!

<bean id="customer" class="com.doctchen.spring5.entity.Customer"><property name="qqs"><map><entry><key><value>doctchen</value></key><value>zhangsan@qq.com</value></entry></map></property>
</bean>
  • private Properties properties;

Properties类型 特殊的Map key=String,value=String

<bean id="customer" class="com.doctchen.spring5.entity.Customer"><property name="p"><props><prop key="zhangsan">doctchen</prop><prop key="lisi">doctchen</prop><prop key="wangwu">doctchen</prop></props></property>
</bean>
  • 对于复杂的JDK类型,比如Date

需要程序员自定义类型转换器,处理,这个后面章节体现

上面的代码:

public class Customer implements Serializable {private String name;private int age;private String[] emails;private Set<String> tels;private List<String> address;private Map<String, String> qqs;private Properties p;@Overridepublic String toString() {return "Customer{" +"name='" + name + '\'' +", age=" + age +", emails=" + Arrays.toString(emails) +", tels=" + tels +", address=" + address +", qqs=" + qqs +", p=" + p +'}';}public String getName() {return name;}public void setName(String name) {this.name = name;}public int getAge() {return age;}public void setAge(int age) {this.age = age;}public Map<String, String> getQqs() {return qqs;}public void setQqs(Map<String, String> qqs) {this.qqs = qqs;}public String[] getEmails() {return emails;}public void setEmails(String[] emails) {this.emails = emails;}public Set<String> getTels() {return tels;}public void setTels(Set<String> tels) {this.tels = tels;}public List<String> getAddress() {return address;}public void setAddress(List<String> address) {this.address = address;}public Properties getP() {return p;}public void setP(Properties p) {this.p = p;}
}
    @Testpublic void Test2(){ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");Customer customer = (Customer) ctx.getBean("customer");System.out.println("customer = " + customer);}

打印结果:

customer = Customer{name='doctchen', age=111, emails=[zhangsan@qq.com, lisi@qq.com, wangwu@qq.com], tels=[set1@qq.com, set2@qq.com, set3@qq.com], address=[list1@qq.com, list2@qq.com, list3@qq.com], qqs={doctchen=zhangsan@qq.com}, p={zhangsan=doctchen, lisi=doctchen, wangwu=doctchen}}

2、用户可以自定义类型

需求,我们要往UserServiceImpl类中注入UserDao

/*** 就把UserServiceImpl作为一个普通的类* 这个类里就一个UserDao这个属性* 然后剩下get,set方法*/
public class UserServiceImpl implements UserService {private UserDao userDao;public UserDao getUserDao() {return userDao;}public void setUserDao(UserDao userDao) {this.userDao = userDao;}
}

既然要Set注入属性userDao,就一定要先有这个UserServiceImpl对象,也就是先有这个bean

<bean id="userServiceImpl" class="com.doctchen.spring5.service.UserServiceImpl"><property name="userDao"></property>
</bean>

而需要注入userDao的话,又得需要一个userDao对象,所以我们之间在属性property里面之间做一个bean对象

<bean id="userServiceImpl" class="com.doctchen.spring5.service.UserServiceImpl"><property name="userDao"><bean id="userDao" class="com.doctchen.spring5.dao.UserDaoImpl"/></property>
</bean>

但是假如我又来了一个OrderServiceImpl,里面也需要一个userDao对象,是不是又这样写?

<bean id="userServiceImpl" class="com.doctchen.spring5.service.UserServiceImpl"><property name="userDao"><bean id="userDao" class="com.doctchen.spring5.dao.UserDaoImpl"/></property>
</bean><bean id="orderServiceImpl" class="com.doctchen.spring5.service.OrderServiceImpl"><property name="userDao"><bean id="userDao" class="com.doctchen.spring5.dao.UserDaoImpl"/></property>
</bean>

如果userDao在很多业务中都要用到,这个重复代码是不是很多,于是Spring有一种简化方式,引出我们的<ref bean=""/>

<bean id="userServiceImpl" class="com.doctchen.spring5.service.UserServiceImpl"><!-- name代表private UserDao userDao 这属性值--><property name="userDao"><!-- bean的名字代表下面的<bean id="userDao"> 这个对象的id--><ref bean="userDao"></ref></property></bean><bean id="userDao" class="com.doctchen.spring5.dao.UserDaoImpl">
</bean>

测试代码:

    //打印 com.doctchen.spring5.dao.UserDaoImpl@63440df3@Testpublic void Test3(){ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");UserServiceImpl userServiceImpl = (UserServiceImpl) ctx.getBean("userServiceImpl");System.out.println(userServiceImpl.getUserDao());}

ok读者可以下去测试下!!

2.2、Set注入简化写法

  • 基于属性的简化

我们用value属性的方式,替换了原有的value标签,举个例子

<bean id="customer" class="com.doctchen.spring5.entity.Customer"><!--    对应private String name    -->
<!--        <property name="name">-->
<!--            <value>doctchen</value>-->
<!--        </property>--><!--  简化后 --><property name="name" value="doctchen"/>
</bean>

注意:value属性,只能简化,8种基本类型+String,也就是value属性只能替换value标签,这里不理解也没关系,只是简化写法的一种方式,但是像List,Set,Map里面的value标签,是简化不了的;

下面这种是用户自定义类型简化

<bean id="userServiceImpl" class="com.doctchen.spring5.service.UserServiceImpl">
<!--        <property name="userDao">-->
<!--            <ref bean="userDao"></ref>-->
<!--        </property>--><!--  用户自定义类型简化 --><property name="userDao" ref="userDao"/>
</bean>
  • 基于p空间命名简化

p的意思就是property标签的缩写,记得你用p标签的时候,xml文件中要有这句,idea会自动给你引入的不需要手敲

这就很明白意思了,上代码你们看看

这里强调下,p命名空间注入Map,我目前是没有做过,但是应该可以,后续我可以贴出来,这里了解下p命名空间就可以了,知道是可以简化代码的一种方式

那么对于p命名在自定义类型呢

<bean id="userService" class="xx.UserServiceImpl"><property name="userDAO"><ref bean="userDAO"/></property>
</bean><!-- 简化后 -->
<bean id="userService" class="xxx.UserServiceImpl" p:userDAO-ref="userDAO"/>

3、构造注入

我们总结下前面的

  • 注入:通过Spring的配置文件,为成员变量赋值
  • Set注入:Spring调用Set方法,通过配置文件,为成员变量赋值
  • 构造注入:Spring调用构造方法,通过配置文件,为成员变量赋值

3.1、提供有参构造

public class Customer implements Serializable {public Customer(String name, int age) {this.name = name;this.age = age;}
}

3.2、Spring的配置文件

当构造方法重载的时候,注意以下两个特点(重载定义:方法名字相同,而参数类型不同,参数个数,参数顺序不同)

1、参数个数不同时,很明显下面代码

public Customer(String name, int age) {this.name = name;this.age = age;}public Customer(String name, int age, String[] emails) {this.name = name;this.age = age;this.emails = emails;}

如果是前两个参数,Spring就调用第一个构造方法,如果三个参数都有,就调用第二个构造方法

<bean id="customer" class="com.doctchen.spring5.entity.Customer"><constructor-arg><value>doctchen</value></constructor-arg><constructor-arg><value>21</value></constructor-arg><constructor-arg><array><value>doc</value><value>sss</value><value>chenx</value></array></constructor-arg></bean>

这个时候通过控制<constructor-arg>标签的数量进⾏区分

2、构造参数个数相同时,顺序不同

public Customer(String name, int age) {this.name = name;this.age = age;}public Customer(int age, String name) {this.name = name;this.age = age;}

配置文件

<bean id="customer" class="com.doctchen.spring5.entity.Customer">
<!--  一个constructor-arg对应构造方法的一个参数 --><constructor-arg><value>doctchen</value></constructor-arg><constructor-arg><value>21</value></constructor-arg></bean>

这个是语法允许的,如果出现这样的情况,想靠两个<constructor-arg>标签来让Spring区别,就有点为难了,因为你的String,int都在8中基本数据+String类型,并且都是<value>标签(上面说过value标签适用8+String),所以Spring分不出来,默认就会按序只赋值给第一个构造方法。

如果想让Spring区分,系统通过在标签引⼊type属性 进⾏类型的区分 <constructor-arg type="">

 <bean id="customer" class="com.doctchen.spring5.entity.Customer"><constructor-arg type="int"><value>111</value></constructor-arg><constructor-arg type="java.lang.String"><value>doctchen</value></constructor-arg></bean>

这样就可以调用到第二个构造方法了!!!

4、注入总结

未来的实战中,应⽤set注⼊还是构造注⼊?
答案:set注⼊更多

  • 构造注⼊麻烦(重载)
  • Spring框架底层 ⼤量应⽤了set注⼊

画了一张图,可以看看理解下

好了,本章就先到这,下一章节我们说反转控制+依赖注入,敬请期待!!!

喜欢的点赞三连吧 哈哈哈

Spring基础专题——第二章(注入Injection)相关推荐

  1. Dubbo基础专题——第二章(Dubbo工程简单实践)

    前言:刚完成的Spring基础专题本想更新源码的,但是发现分布式非常火,而我喜欢玩这个,所以今年我希望把我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个 ...

  2. Spring基础专题——第一章(第一个Spring程序)

    前言:去年到现在一直没有很好的时间完成这个spring基础+源码的博客目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从 ...

  3. Spring基础专题——第九章(基础注解编程——上)

    目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个互联网职场小白到一个沪漂湿人,一路让我知道分享是一件多么重要的事情 ...

  4. Dubbo基础专题——第一章(带你认识Dubbo)

    前言:刚完成的Spring基础专题本想更新源码的,但是发现分布式非常火,而我喜欢玩这个,所以今年我希望把我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从一个 ...

  5. 《Spring Recipes》第二章笔记:Creating Beans by Invokin...

    2019独角兽企业重金招聘Python工程师标准>>> <Spring Recipes>第二章笔记:Creating Beans by Invoking an Insta ...

  6. 《Spring Recipes》第二章笔记:Customizing Bean Initiali...

    2019独角兽企业重金招聘Python工程师标准>>> <Spring Recipes>第二章笔记:Customizing Bean Initialization and ...

  7. JavaSE入门0基础笔记 第二章Java基础语法

    JavaSE入门0基础笔记 第二章Java基础语法 1.运算符 1.1算术运算符 1.1.1运算符和表达式 1.1.2 算术运算符 1.1.3字符的"+"操作 1.1.4 字符串中 ...

  8. Spring基础专题——第三章(反转控制与依赖注入)

    前言:去年到现在一直没有很好的时间完成这个spring基础+源码的博客目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从 ...

  9. Spring基础专题——第十一章(高级注解编程完结)

    前言:去年到现在一直没有很好的时间完成这个spring基础+源码的博客目标,去年一年比较懒吧,所以今年我希望我的知识可以分享给正在奋斗中的互联网开发人员,以及未来想往架构师上走的道友们我们一起进步,从 ...

最新文章

  1. 安卓通讯录管理软件_安卓又曝严重漏洞,或监控数亿用户,请尽快更新系统补丁!...
  2. 输变电设备物联网节点设备无线组网协议_SmartModule物联网智能测控组件
  3. 本博打开方式,请详读
  4. 无法将 lambda 表达式 转换为类型“System.Delegate”,因为它不是委托类型
  5. php curl发送delte,php Curl如何发起POST DELETE GET POST请求
  6. docker运行Linux性能怎么样,Docker容器OSX与Linux的性能差异
  7. linux man指令问题
  8. ios8来了,屏幕更大,准备好使用 iOS Auto Layout了吗?
  9. 华为odjava机试题_华为机试题及答案
  10. 2019华为机试题 消息扩散
  11. 数据库表关系详解(一对多、一对一、多对多)
  12. 互联网大佬扎堆出书 不为赚钱只为粉丝
  13. 谷粒商城P46 gulimall-gateway刷新验证码出现503错误
  14. 郁金香java_郁金香搜索引擎的方案
  15. 简单几行代码带你爬取王者荣耀皮肤
  16. 最近面试SAR ADC总是被问到DAC 单位电容的取法
  17. IOS中通过快捷捷径打开场所码或者健康码
  18. 如何干净地卸载VMware
  19. java.lang.ArithmeticException: Non-terminating decimal expansion; no exact representable decimal
  20. nginx的location,upstream,rewrite 和 proxy_pass使用,以及需要注意的地方

热门文章

  1. 亚洲最大的元宇宙平台,体验在豪宅里开party
  2. 基于PyTorch,如何构建一个简单的神经网络
  3. 解构 StyleCLIP:文本驱动、按需设计,媲美人类 P 图师
  4. ​AI 面试“泛滥”的时代,HR该如何甄别真假“AI”?
  5. 优酷智能档在大型直播场景下的技术实践
  6. 渗透各行各业,这家RPA外企宣布全面进军中国市场
  7. AI一分钟 | 北京开放自动驾驶车辆路测道路105公里;阿里将人工智能引入时尚界...
  8. 书单 | 计算机视觉的修炼秘笈
  9. AI一分钟 | 教育部公布高中新课标,编程、算法思维成必修内容;李彦宏否认百度会“All in AI”
  10. JetBrains 宣布:IntelliJ 平台彻底停用 Log4j 组件,建议切换至 java.util.logging