2、Spring配置文件-依赖注入

1.0、全局知识要点回顾

1.1、配置我们的业务层Service

  • 接口

  • public interface UserService {
        // save方法--调用dao层的save方法
        public void save();
    }
  • 实现类

  • //  表示我们的业务层
    public class UserServiceImpl implements UserService {
        // 调用到dao层的save方法
        public void save() {
            ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
            // 通过getBean方法获取到我们刚刚在容器中创建好的组件
            UserImpl userdao = (UserImpl) app.getBean("Userdao");
            userdao.save();
        }
    }
  • 将我们的UserService注入到配置文件当中

  • <!--Service层的对象-->
    <bean id="UserService" class="com.waves.service.impl.UserServiceImpl"></bean>

1.2、模拟Controller控制层,模拟web环境下的后端响应

// 模拟控制层--响应web开发(目前是没得的)
public class UserController {
    public static void main(String[] args) {
        // 加载配置文件
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取我们注入到配置文件的UserService对象
        UserService userService = (UserService) app.getBean("UserService");
        userService.save();
    }
}
  • 启动--响应结果

2、Spring配置--依赖注入(Bean依赖注入分析)

2.1、分析部分

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

  • 在我们编写程序的时候,通过控制反转,将对象的创建权交给了Spring来帮我们进行管理和创建,但是我们写代码的,不可能出现没有依赖的情况,例如我们的控制层,依赖于业务层,业务层,依赖于我们的持久层,持续套娃,IOC解耦只是为了降低他们的依赖关系,并不会消除,也就是说,业务层仍旧会调用持久层的方法,而控制层仍旧会调用业务层的方法

  • 那种业务层和持久层的依赖关系,使用Spring之后,就让Spring来维护了,简单的说就是坐等框架把持久层的对象传入业务层,而不用我们自己去调用

  • 刚刚那坨代码有大问题,就是没有依赖注入,我表达不出来,但是可以很明显的看到,这个程序运行完了以后,加载配置文件的方法被执行了两次,相当的麻烦,如果我能够在Service层,将Dao层的对象获取到,我是不是就可以不用在Service层就直接加载配置文件了?

2.2、对象依赖注入的两种方法(set方法or有参构造)

  • set方法

  • 有参构造

  • 我们主要将set方法,在UserService层创建一个私有的UserDao对象,通过Set的方法将对象直接注入到我们Service创建的那个Userdao对象中

  • // 创建一个UserDao的对象
    private UserDao userdao;
    ​
    // 将这个对象传递到我们本身创建的这个对象中
    public void setUserdao(UserDao userdao) {
        this.userdao = userdao;
    }
  • 实现UserService中的save方法

  • // 调用到dao层的save方法
    public void save() {
        // 直接用我们创建好的这个对象来调用Dao层的save()方法
        userdao.save();
    }
  • 问题--你知道,但是Spring不知道,那该怎么办?

  • 我们需要稍稍改造一下我们刚开始在容器中创建的Service对象

  • property属性设置,name,是set方法后面那坨东西的名字,并且,第一个字母必须是小写

  • 例如setUserdao,那么我写这个name的时候就必须要写成name="userdao"

  • ref:对象的引用,你要把哪个注入到我的Service对象中?---Userdao这个对象撒~

  • <!--Service层的对象-->
    <bean id="UserService" class="com.waves.service.impl.UserServiceImpl">
        <property name="userdao" ref="Userdao"></property>
    </bean>
  • 我现在通过Service实现类中的SetUserdao的方法,将我的Userdao对象注入到我的Service对象中

  • 现在我们来测试下

  • Very Deep!这就是依赖注入的一个体现

2.3、加深印象

  • 如果我通过new 对象的方式来创建这个业务层的对象,会发生什么?

  • public static void main(String[] args) {
        /*// 加载配置文件
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 获取我们注入到配置文件的UserService对象
        UserService userService = (UserService) app.getBean("UserService");
        userService.save();*/
        UserService userService = new UserServiceImpl();
        userService.save();
    }

  • 为什么会空指针异常,因为我new的这个对象并不是从容器当中获取的,而只有容器中的那个UserService的对象,他被UserDao对象注入进去了,我们new的这个对象是木得滴~

3、Spring配置--依赖注入(set方法注入简便的方式)

3.1、引入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"

3.2、修改注入的方式

  • 如果我们注入的是一个对象的话,那就直接p:userdao-ref="方法名"(userdao)

  • <bean id="UserService" class="com.waves.service.impl.UserServiceImpl" p:userdao-ref="Userdao"/>
  • 测试一下

  • Very Deep!但是我们用的多的还是property的属性注入,只有需要设置很多属性的时候,才会用这个,了解一下

4、Spring配置--依赖注入(有参构造)

4.1、创建有参构造

//依赖注入--有参构造
​
public UserServiceImpl(UserDao userdao) {
    this.userdao = userdao;
}

4.2、配置xml文件

  • 告诉Spring我是通过有参构造的方式进行依赖注入

  • 构造构造--Contructor-arg(参数)--这里放的是我们的构造方法中的参数名

  • ref-你要引用的对象是谁--要把哪个对象注入进来

  • <!-- 有参构造的依赖注入方式 -->
    <bean id="UserService" class="com.waves.service.impl.UserServiceImpl">
        <!-- 还是在内部写 -->
        <constructor-arg name="userdao" ref="Userdao"></constructor-arg>
    </bean>
  • 测试

5、Spring配置--依赖注入(Bean依赖注入的数据类型--普通数据)

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

5.1、在我们的UserDaoImpl实现类中,设置两个参数,并注入到我们的Bean中

  • 要设置好set方法哦,不然怎么注入呢?

  • public class UserImpl implements UserDao {
        private String name;
        private Integer age;
    ​
        public void setName(String name) {
            this.name = name;
        }
    ​
        public void setAge(Integer age) {
            this.age = age;
        }   
    }
  • 在Bean中注入我们设置的两个属性值

  • 设置姓名为张三,年龄为18

  • <bean id="Userdao" class="com.waves.dao.impl.UserImpl"
    init-method="init" destroy-method="destory">
        <property name="name" value="zhangsan"></property>
        <property name="age" value="18"></property>
    </bean>
  • 测试

6、Spring配置--依赖注入(复杂数据类型)

6.1、在UserDao中新增三个复杂类型的数据,设置好set方法

// Bean数据类型的注入--复杂数据
private List<String> strList;
private Map<String, User> userMap;
private Properties properties;
​
public void setStrList(List<String> strList) {
    this.strList = strList;
}
​
public void setUserMap(Map<String, User> userMap) {
    this.userMap = userMap;
}
​
public void setProperties(Properties properties) {
    this.properties = properties;
}

6.2、List的注入

<bean id="Userdao" class="com.waves.dao.impl.UserImpl">
    <!-- 属性的注入-开头肯定是property -->
    <property name="strList">
        <!-- 这个List是一个复杂的数据类型 -->
        <list>
            <!--且泛型为String- 使用value-->
            <value>aaa</value>
            <value>bbb</value>
            <value>ccc</value>
            <value>ddd</value>
        </list>
    </property>
</bean>

打印一下结果

6.3、Map的注入

  • 因为我这里的Map,内部含了一个User的对象,如果我要对这个user设置值,那么这个User的对象必须要在这个容器当中,这样才能被我引用

  • 将User注册到容器当中

  • <!-- 注册User到容器当中 -->
    <bean id="User1" class="com.waves.unity.User">
        <!-- 将User的值注入 -->
        <property name="name" value="律师罗翔"></property>
        <property name="hobby" value="审判张三"></property>
    </bean>
    <!-- 注册User到容器当中 -->
    <bean id="User2" class="com.waves.unity.User">
        <!-- 将User的值注入 -->
        <property name="name" value="法外狂徒罗翔"></property>
        <property name="hobby" value="狡辩"></property>
    </bean>
  • 开始将User注入到map的集合当中

<property name="userMap">
    <!-- 他是map类型,内部的注入方式肯定是map -->
    <map>
        <entry key="user1" value-ref="User1"></entry>
        <entry key="user2" value-ref="User2"></entry>
    </map>
</property>
  • 测试,User那个是因为我们重写了User类当中的toString方法

6.4、properties的注入

他是一个字符串类型的键值对,应该是

<property name="properties">
    <!-- 外部写参数类型 -->
    <props>
        <!-- 一堆一堆的写 -->
        <prop key="p1">ppp</prop>
        <prop key="p2">hhh</prop>
        <prop key="p3">ttt</prop>
        <prop key="p4">rrr</prop>
    </props>
</property>
  • 测试

7、Spring配置文件-import导入文件和知识要点--(分模块开发)

7.1、实际开发中,Spring的配置文件内容会非常多

  • 因为设计到控制层-业务层-持久层(dao层)这些模块的开发

  • 这些模块不可能放在一个配置文件当中

  • 需要相应的去设计每个模块的配置文件

  • 如果在项目启动的时候,我们去加载这些配置文件,是不是就会有许多这样的

  • 配置文件加载?

  • 例如我在这里添加了几个逻辑层的配置文件

  • 项目运行的时候,我是不是要先加载这些配置文件?

  • 因为我需要获取到容器当中,这些配置类所创建的对象

  • ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
    ​
    ClassPathXmlApplicationContext app1 = new ClassPathXmlApplicationContext("applicationContext-controller.xml");
    ​
    ClassPathXmlApplicationContext app2 = new ClassPathXmlApplicationContext("applicationContext-service.xml");
    ​
    ClassPathXmlApplicationContext app3 = new ClassPathXmlApplicationContext("applicationContext-user.xml");

7.2、如何解决?----import导入

  • 将其他配置文件导入到主配置文件当中,这个配置文件启动的时候带动其他的配置文件一起加载

  • 导入两个配置试试水

  • <import resource="applicationContext-controller.xml"></import>
    <import resource="applicationContext-user.xml"></import>

8、Spring配置文件--相关的API1

8.1、ApplicationContext的继承体系--图解

8.2、ApplicationContext的实现类(目前三个)

8.2.1、ClassPathXmlApplicationContext()--使用的最多

他是从类的更路径下加载配置文件--resources下配置文件

8.2.2、FileSystemXmlApplicationContext()--较少,不易改变

这个是从磁盘的路径上加载配置文件,配置文件可以再磁盘的任意位置上

举个例子--二者的对比

// 这个是我们Spring程序的入口函数,参数为我们创建好的那个配置文件xml
ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
// 磁盘加载--内容的跟原路径,死路径
ApplicationContext app1 = new FileSystemXmlApplicationContext("D:\\Spring\\Spring快速入门\\src\\main\\resources\\applicationContext.xml");

8.2.3、AnnotationConfigApplicationContext--注解开发

当使用注解配置容器对象的时候,需要使用这个类来创建Spring的容器,让他来读取注解

9、Spring配置文件--相关API2--getBean

9.1、两个不同类型的接收参数(id和class)

  • getBean接口有两个方式参数类型不相同

  • 之前案例中用的是普遍以id的方式进行调用

  • 第二个需要的参数是--对象的类型

9.2、二者的不同之处

  1. 第一个getBean,他可以根据id来获取自己想要获取的那个对象,但是获取的时候需要类型强转

  2. 而第二个getBean,他可以直接通过那个类的对象来获取

  3. 打印结果为

  4. public static void main(String[] args) {
        // 这个是我们Spring程序的入口函数,参数为我们创建好的那个配置文件xml
        ClassPathXmlApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 我要获取我那个user的对象,我已经注释掉了之前容器中创建的第二个user对象
        User user1 = app.getBean(User.class);
        System.out.println(user1);
    }
  • 如果容器中该对象的实例创建超过两次以上,那么getBean(class)会报错

9.3、总结

  • 在Spring的IOC容器当中,如果我们创建的Bean,他的类型在这个xml配置文件(IOC)容器当中的类型是唯一的
  • 那么我们通过上下文对象(ApplicationContext)的getBean去获取到这个组件
  • 获取这个组件的方式可以是传入类型来获取,否则就老老实实使用其他方式来获取
posted @ 2022-05-12 11:08  澜璨  阅读(193)  评论(0)    收藏  举报