4. Spring配置文件 - Bean的依赖注入方式

我们之前写的代码 只有实现业务逻辑层,实际MVC模式开发中 业务逻辑层 和 Dao【数据库】层是一起用的,那么在MVC开发模式中:

业务逻辑层里面要调用Dao层,怎么来获取Bean呢?  我们写个简单的示范:【先来个MVC Service调用Dao 层的模拟:】

 

 

 图片可以看出 其中 接口都是 void show() 方法,

实现都是实现 show方法:

DAOIMPL: 

   public void show() {
        System.out.println("HelloDaoImpl 的 show 方法.");
    }

SERVICEIMPL:

  public void show() {
        System.out.println("HelloServiceImpl 的 show 方法.");
    }

 

Spring配置文件:

<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="Dao" class="com.bihu.helloDaoImpl.HelloDaoImpl"></bean>
    <bean id="Service" class="com.bihu.helloServiceImpl.HelloServiceImpl"></bean>
</beans>

 

 

然后我们改写一下 SERVICEIMPL,我们来调用DAO的实现:

 

package com.bihu.helloServiceImpl;

import com.bihu.helloDaoImpl.HelloDaoImpl;
import com.bihu.helloService.HelloService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class HelloServiceImpl implements HelloService {

    /**
     * 相当于通过 Service的show方法调用Dao的show方法。
     */
    public void show() {
        ApplicationContext app =  new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取到Dao的Bean
        HelloDaoImpl dao = (HelloDaoImpl) app.getBean("Dao");
        //调用HelloDaoImpl 中的 show
        dao.show();
    }
}

 

 

最后我们测试类,获取到HelloServiceImpl的Bean,调用HelloServiceImol 的show 方法 方可 在内部又调用到了 HelloDaoImpl 的show方法:

import com.bihu.helloServiceImpl.HelloServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {

    @Test //要导入 Junit 的 GAV
    public void springTest(){
        ApplicationContext app =  new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloServiceImpl service = (HelloServiceImpl)app.getBean("Service");
        service.show();
    }
}

 

运行结果:

信息: Loading XML bean definitions from class path resource [applicationContext.xml]
HelloDaoImpl 的 show 方法.

所以说 这就是一个很简单很简单的一个传递调用而已。

 

 所以下面我们分析一下:

因为UserService和UserDao都在Spring容器中,而最终程序直接使用的是UserService,所以可以在 Spring容器中,将UserDao设置到UserService内部。

获得UserService实例,内部已经存在UserDao 实例了,直接调用UserDao的save()方法即可。

 


 

 

Bean的依赖注入概念

依赖注入(Dependency Injection):它是 Spring 框架核心 IOC 的具体实现

在编写程序时,通过控制反转把对象的创建交给了 Spring,但是代码中不可能出现没有依赖的情况。 IOC 解耦只是降低他们的依赖关系,但不会消除。

例如:业务层仍会调用持久层的方法。 那这种业务层和持久层的依赖关系,在使用 Spring 之后,就让 Spring 来维护了。 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

------

依赖注入的涵义:通过Spring容器帮我们new指定实例并且将实例注入到需要该对象的类中。依赖注入的另一种说法是“控制反转”,通俗的理解是:平常我们new一个实例,这个实例的控制权是我们程序员,而控制反转是指new实例工作不由我们程序员来做而是交给spring容器来做。

------

怎么将HelloDao怎样注入到HelloService内部呢?

1 构造方法

2 set方法

 

Set方法注入:

在HelloServiceImpl 中添加 方法 SethelloDao,

package com.bihu.helloServiceImpl;

import com.bihu.helloDao.HelloDao;
import com.bihu.helloDaoImpl.HelloDaoImpl;
import com.bihu.helloService.HelloService;


    public class HelloServiceImpl implements HelloService {

       private HelloDao helloDao;
        /*创建SetHelloDao方法*/
    //注意这里名字一定要是 setXXXXX!前面的set是写死了的 我就吃亏了..
    public void setHelloDao(HelloDao helloDao){
        this.helloDao = helloDao;
    }


    @Override
    public void show() {
        this.helloDao.show();   //调用HelloDap的show方法
    }
}

 

⭐然后去Spring配置文件 配置一下:

<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!-- Dao的Bean   -->
    <bean id="Dao" class="com.bihu.helloDaoImpl.HelloDaoImpl"></bean>

    <!-- Service的Bean   -->
    <bean id="Service" class="com.bihu.helloServiceImpl.HelloServiceImpl">
<!--  property 标签,其中name是setHelloDao方法名,规则就是要把set去掉然后首字母变小写 【注意!!!  只有Set方法注入参数name才是这个按照这个规则写! 如果是有参构造的话 不是这样的啊 name指的是参数名】
      ref指的是Spring配置文件中HelloDaoImpl的Bean的(ID)
-->
        <property name="helloDao" ref="Dao"></property>
    </bean>
</beans>

 

其中的 property 标签 就是将HelloDao怎样注入到HelloService内部的实现。

然后Spring会自动执行setHelloDao这个方法,这样 HelloServiceImpl 的this.helloDao 就会有值了.

然后你就可以调用 HelloDao中的方法了。

总结就是说,学会这种调用方法.

 

import com.bihu.helloServiceImpl.HelloServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {

    @Test //要导入 Junit 的 GAV
    public void springTest(){
        ApplicationContext app =  new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloServiceImpl service = (HelloServiceImpl)app.getBean("Service");
        service.show();
    }
}

允许结果:

信息: Loading XML bean definitions from class path resource [applicationContext.xml]
HelloDaoImpl 的 show 方法.

所以证明Set依赖注入成功~

你也可以试着 直接new一个HelloServiceImpl对象 会发现空指针异常 因为Spring没有给他里面的this.HelloDao赋值。

Get依赖注入还有一个更方便的方式:

P命名空间注入 他本质也是Set方式注入的一种,入,但比起上述的set方法注入更加方便,主要体现在配置文件中,如下: 首先,需要引入P命名空间:

首先,需要在Spring配置文件中引入P命名空间

然后我们需要在Sping中: 因为是P命名空间模式 所以下面用把property删除 然后在bean中加上p标签    p:helloDao-ref=xxx 值是helloDaoBean的ID 很方便啊 但是一般用的多还是普通的Set方式:

<?xml version="1.0" encoding="UTF-8" ?>
<!--首先,需要引入P命名空间: xmlns:p="http://www.springframework.org/schema/p" 【在下面】-->
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="Dao" class="com.bihu.helloDaoImpl.HelloDaoImpl"></bean>

<!-- 因为是P命名空间模式 所以下面用把property删除 然后在bean中加上p标签
    p:helloDao-ref=xxx  值是helloDaoBean的ID 很方便啊 但是一般用的多还是普通的Set方式
 -->
    <bean id="Service" class="com.bihu.helloServiceImpl.HelloServiceImpl" p:helloDao-ref="Dao"></bean>
</beans>

 

所以P标签命名注入 对比 传统的Set依赖注入:

要改的地方只有一处 那就是Spring的配置文件。

 

Set方法讲完了 讲第二种方法: 通过构造方法(有参)注入:

创建有参构造:

package com.bihu.helloServiceImpl;

import com.bihu.helloDao.HelloDao;
import com.bihu.helloDaoImpl.HelloDaoImpl;
import com.bihu.helloService.HelloService;


    public class HelloServiceImpl implements HelloService {

        private HelloDao helloDao;
        /*创建有参构造*/
        public HelloServiceImpl(HelloDao helloDao) {
            this.helloDao = helloDao;
        }

        @Override
    public void show() {
        this.helloDao.show();   //调用HelloDao的show方法
    }
}

 

配置文件:

<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">


    <bean id="Dao" class="com.bihu.helloDaoImpl.HelloDaoImpl"></bean>

<!--
       构造方法注入:
       其中 constructor-arg 标签 代表 构造的意思
       name值得是构造参数的名字
       ref值得是 Dao的Id
除了name 还可以用index来定位参数,例如:
<constructor-arg index="0" ref="dao"/>
-->
    <bean id="Service" class="com.bihu.helloServiceImpl.HelloServiceImpl">
        <constructor-arg name="helloDao" ref="Dao"></constructor-arg>
    </bean>

</beans>

 不仅可以用name进行参数固定 还可以用 index 来 index从0开始  0代表第一个参数!

所以你看 有新学了个constructor-arg  标签 可以看出 constructor是构造的意思 ,会用即可,注释写的明明白白的呢...

 

Bean的依赖注入的数据类型

上面的操作,都是注入的引用Bean,除了对象的引用可以注入,普通数据类型,集合等都可以在容器中进行注入。

注入数据的三种数据类型:

1、普通数据类型

2、引用数据类型

3、集合数据类型

 

其中引用数据类型,这里不说了,之前的操作都是对UserDao对象的引用进行注入的,下面将以set方法注入为 例,演示普通数据类型和集合数据类型的注入。

下面示例一下 普通数据依赖注入,注入方法用set:

package com.bihu.helloDaoImpl;

import com.bihu.helloDao.HelloDao;

public class HelloDaoImpl implements HelloDao {

    private String name;
    private String age;

    /*⭐⭐ 这里注入 普通参数 创建有参构造: */

    public void setName(String name) {
        this.name = name;
    }

    public void setAge(String age) {
        this.age = age;
    }

    public void show() {
        System.out.println("HelloDaoImpl 的 show 方法.");
        System.out.println("我叫" + name + "我今年" + age +  "岁");
    }

}

配置文件:

<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--  通过set方式去注入普通参数  -->
    <bean id="Dao" class="com.bihu.helloDaoImpl.HelloDaoImpl">
        <property name="name" value="小红"></property>
        <property name="age" value="18" ></property>
    </bean>


    <bean id="Service" class="com.bihu.helloServiceImpl.HelloServiceImpl">
        <constructor-arg name="helloDao" ref="Dao"></constructor-arg>
    </bean>

</beans>

 

所以 当你调用Dao的show方法时 就会打印出来,这就是很简单的一种普通变量依赖注入。【带脑子看代码】

 

记住一个点 :  带ref 的一般都是 引用的数据类型

 

集合注入:

这次直接Service 写接口  ServiceImpl写实现:

也是用set方式进行注入吧:

三种集合:

List

Map

Properties:

代码入下:

ServiceImpl:

package com.bihu.helloServiceImpl;

import com.bihu.Mondle.User;
import com.bihu.helloService.HelloService;

import java.util.List;
import java.util.Map;
import java.util.Properties;


public class HelloServiceImpl implements HelloService {
    private List list;
    private Map<String , User> map;
    private Properties properties;

    //Set 方法:
    public void setList(List list) {
        this.list = list;
    }

    public void setMap(Map<String, User> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    @Override
    public void show() {
        //此方法显示全部集合数据
        System.out.println(list);
        System.out.println(map);
        System.out.println(properties);
    }
}

 

User:

package com.bihu.Mondle;

public class User {
    private String name;
    private int age;

    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;
    }


@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}

 

那么配置文件:

  

 

<?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/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="Service" class="com.bihu.helloServiceImpl.HelloServiceImpl">
<!--    list 直接用一个list表标签 下面直接value 注入值    -->
      <property name="list">
          <list>
              <value>值1</value>
              <value>值2</value>
              <value>值3</value>
          </list>
      </property>

        <!--    Map集合注入 那么你肯定先要有个user 那就先创建个userBean    -->
        <!--    然后来个map标签  map标签中放entry标签  -->
        <!--    entry标签 有个属性 key 就是map对应的key value 就是key对应的值 -->
        <!--    主义 key属性和value属性 如果带 ref 那么就是引用类型的!!
                好比如 这里的User就是专门示范这个特性的 User是引用类型
         -->
        <property name="map">
           <map>
               <entry key="key1" value-ref="user1"></entry>
               <entry key="key2" value-ref="user2"></entry>
           </map>
        </property>

        <!--  properties 集合类型:
  没听过这个类型 但是听说key和value都是字符串....
   JDK:本Properties类代表一个持久的属性集。所述Properties可以被保存到一个流或从流加载。属性列表中的每个键及其对应的值都是一个字符串。
   此类是线程安全的:多个线程可以共享单个 Properties对象,而无需外部同步。
   -->
<!--   其中 property集合注入用 props标签 props标签下分prop标签
     prop标签属性key代表key 值直接在外面写
     key和value都是整型
     -->
        <property name="properties">
            <props>
                <prop key="v1">值1</prop>
                <prop key="v2">值2</prop>
                <prop key="v3">值3</prop>
                <prop key="v4">值4</prop>
                <prop key="v5">值5</prop>
            </props>
        </property>



    </bean>









<!--  UserBean:  -->
    <bean id="user1" class="com.bihu.Mondle.User">
<!--   user 也是一个类 也有值 所以。。。。    -->
        <property name="name" value="小w"></property>
        <property name="age" value="18"></property>
    </bean>

    <bean id="user2" class="com.bihu.Mondle.User">
        <!--   user 也是一个类 也有值 所以。。。。    -->
        <property name="name" value="小黄"></property>
        <property name="age" value="3"></property>
    </bean>


</beans>

 

注意看注释即可,这玩意有点套娃 但是只要逻辑清晰步入 没啥问题哈

 然后我们打印一下测试类:

import com.bihu.helloServiceImpl.HelloServiceImpl;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class test {

    @Test //要导入 Junit 的 GAV
    public void springTest(){
        ApplicationContext app =  new ClassPathXmlApplicationContext("applicationContext.xml");
        HelloServiceImpl service = (HelloServiceImpl)app.getBean("Service");
        service.show();
    }
}

输出值: 

 

信息: Loading XML bean definitions from class path resource [applicationContext.xml]
[值1, 值2, 值3]
{key1=com.bihu.Mondle.User@612679d6, key2=com.bihu.Mondle.User@11758f2a}
{v5=值5, v4=值4, v3=值3, v2=值2, v1=值1}

Process finished with exit code 0

都是有值的啊  所以呢 这代表注入成功了,目前介绍那么多。

 

 

所以 我小总结了一下,

 

注入参数,三种方式:

1. 普通数据类型

2.引用数据类型

3.集合数据类型

 

注入模式有两种:

1.有参构造方式

2.set方法 方式【p标签模式】

 

PS: 这里是 Bean的依赖注入参数 请别和 Bean的实例化 搞混!

Bean的实例化有Bean实例化的方法:无参构造、静态工厂、工厂模式,而
Bean的参数注入有Bean参数注入方法:有参构造、Set方式. 

这里重点说下 Bean的参数注入:千万别弄混了!
【问过老师 的:】

有参构造: 是用 constructor-arg 标签配,字段name是参数名,字段index是参数的下标,参数名和下标二选一,然后注入的值 如果你要注入的值是 普通类型 那么就写 value 如果是引用数据类型的话 那么用ref即可


Set方式:是用 
property 标签配,字段name是去掉set[记住是小写的set,推荐的一键生成];然后首字母小写【严格要求标准】,如果你要注入的值是 普通类型 那么就写 value 如果是引用数据类型的话 那么用ref即可,但 Set方式只能注入一个参数!!!! 不可能存在很多个的!我尝试注入很多个失败才去找的老师总的结。


个人很喜欢学原始【手动配置】的在学方便【注解】的!emm因为那样看你会懂得多 也学得好一点吧!

 

 

 

还有一些要注意的是 习惯性导入GAV 习惯性Test 和 源码分文件夹,习惯性xml配置文件放resource 代码放java中 习惯性用MVC...

 

posted @ 2021-06-18 22:34  咸瑜  阅读(556)  评论(0编辑  收藏  举报