Spring6 第3章——容器:IoC

一、IoC容器

定义与本质:IoC 是 Inversion of Control 的缩写,即 “控制反转”,它并非具体技术,而是一种设计思想

(1)控制反转

  1. 思想本质:控制反转旨在降低程序模块间的耦合度,提升程序的可扩展性和可维护性
  2. “反转”的内容
  3. 实现方式

(2)依赖注入

  1. 在配置文件中配置好,针对于某个类我们想要什么样的对象,把这个信息交给IoC。然后由IoC创建、返回对象
  2. 总结

(3)IOC容器在Spring的实现

  1. Spring的 IoC容器 与 IoC 思想的关系
  2. Spring 提供了实现 IoC 容器的两种方式
  3. ApplicationContext 的主要实现类:
  4. ApplicationContext 的主要实现类介绍
  5. ApplicationContext 就是IoC容器

二、基于XML管理Bean

(1)搭建子模块spring-ioc-xml

  1. 我们把 spring-first 的依赖都让父工程管理(即将 spring-first 的 pom.xml 文件中的 dependencies 标签和该标签里的所有内容放到 spring6 的 pom.xml 文件中,然后刷新):
  2. 搭建好子模块 spring-ioc-xml 后发现:因为此时它继承了父模块的所有依赖项
  3. 要在模块中使用 spring ,就必须有 spring 的配置文件,所以我们在 spring-ioc-xml 的 src/main/resources中 创建 bean.xml(名字可以随便起)
  4. 还要引入log4j2的配置文件:
  5. 在 spring-ioc-xml 的 src/main/java 下新建一个类

(2)实验一:获取bean

准备操作:

  1. 方式一根据id获取
    1. id属性指定了bean的唯一标识
    2. 演示:
      public class TestUser {
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
              //获取bean
              User user = (User)context.getBean("user");
          }
      }

    3. Loaded 1 bean definitions from class path resource [bean.xml] 意思是 加载 spring 配置文件

    4. Creating shared instance of singleton bean 'user' 意思是 创建了一个单例对象

  2. 方式二根据类型获取
    1. 此时根据id得到对象和根据类型得到对象,二者返回的是同一个对象因为单例
    2. 演示:
      public class TestUser {
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
              //1.根据id获取bean
              User user1 = (User)context.getBean("user");
              System.out.println("根据id获取bean:" + user1);
              //2.根据类型获取bean
              User user2 = context.getBean(User.class);
              System.out.println("根据类型获取bean:" + user2);
          }
      }

  3. 方式三根据id和类型
    1. 演示:
      public class TestUser {
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
              //1.根据id获取bean
              User user1 = (User)context.getBean("user");
              System.out.println("根据id获取bean:" + user1);
              //2.根据类型获取bean
              User user2 = context.getBean(User.class);
              System.out.println("根据类型获取bean:" + user2);
              //3.根据id和类型获取bean
              User user3 = context.getBean("user", User.class);
              System.out.println("根据id和类型获取bean:" + user3);
          }
      }

    2. user1,user2 和 user3 的地址值都是一样
  4. 用后两种方式时,因为已经指定了类型,所以不需要我们强转
  5. 注意:当根据类型获取bean时,要求IOC容器中指定类型的bean有且只能有一个
    1. 假如,我们给同一个类配置不同的id值,那么在用类型获取bean对象时就会报错
    2. 但是我们发现,如果我们id获取bean对象,或者id类型获取bean对象,就不会报错。因为根据id就可以精准定位
    3. expected single matching bean but found 2: user,user1 该错误信息表示:期望获得一个单实例的bean,但是获取了两个
    4. 也就是说,一个bean标签对应着一个单实例的bean。此时我们对同一个类设置了两个bean标签,我们分别用它们的id来获取bean,运行后可以发现,这确实是两个不同的bean
      public class TestUser {
          public static void main(String[] args) {
              ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
              //1.根据id获取bean
              User user = (User)context.getBean("user");
              System.out.println("根据id获取bean:" + user);
              User user1 = (User)context.getBean("user1");
              System.out.println("根据id获取bean:" + user1);
          }
      }

  6. 组件类通常是指那些会被 IoC 容器创建和管理对象的类
  7. 扩展知识
  8. 结论

(3)实验二:依赖注入之setter注入

  1. 类有属性,创建对象的过程中,给属性设置值
    1. 第一种方式:基于set方法完成
    2. 第二种方式:基于构造器完成
  2. 创建一个Book类:
    package com.atguigu.spring6.iocxml.di;
    
    public class Book {
        private String bname;
        private String author;
    
        //生成set方法
        public void setBname(String bname) {
            this.bname = bname;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        //生成有参数的构造器
        public Book(String bname, String author) {
            this.bname = bname;
            this.author = author;
        }
    
        //生成无参数的构造器
        public Book() {
        }
        
        //toString方法
        @Override
        public String toString() {
            return "Book{" +
                    "bname='" + bname + '\'' +
                    ", author='" + author + '\'' +
                    '}';
        }
    
        public static void main(String[] args) {
            //set方法注入
            Book book = new Book();
            book.setBname("java");
            book.setAuthor("尚硅谷");
    
            //构造器注入
            Book book1 = new Book("c++","尚硅谷");
        }
    }
  3. 配置 bean 标签时为属性赋值(此时我们重新建了一个配置文件,名字可以随便起。Spring 相关的配置文件都要放在 src/main/resources 下面):name 对应类的属性名value 对应类的属性值
  4. 测试代码如下,因为我们此时使用的是bean-di.xml文件,所以文件名别填错
    public class TestBook {
        @Test
        public void testSetter(){
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
            Book book = context.getBean(Book.class);
            System.out.println(book);
        }
    }
  5. 可以看到,通过setter注入成功(所谓注入,其实就是给属性赋值
  6. 基于 Setter 注入需类有 无参构造器和对应 Setter 方法,其本质就是 Spring 容器调用类的 Setter 方法完成依赖注入

(4)实验三:依赖注入之构造器注入

  1. 基于构造器注入前提是类有有参构造器,其本质就是利用类的有参构造器进行依赖注入
  2. 构造器注入,要这么写配置文件中的bean标签(可以用name也可以用index,index=0表示第一个属性,index=1表示第二个属性......)
  3. 为了测试效果更明显,我们在Book类的setter方法中加一条语句:
  4. 测试代码:
    public class TestBook {
        @Test
        public void testConstructor(){
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-di.xml");
            //因为现在Book类有两个id,所以我们不能根据类型获取bean了
            Book book = (Book)context.getBean("bookCon");
            System.out.println(book);
        }
    }

  5. 可以看到,在bean标签里使用 constructor-arg 标签,等价于调用有参构造器来初始化对象
  6. 总结:用 <bean> 标签会让 Spring 创建类的实例,id 类似实例名;用 <property> 标签是通过类的 setter 方法给对象属性赋值,先以无参构造器创建对象再赋值;用 <constructor-arg> 标签则是调用类的有参构造器

(5)实验四:特殊值处理

  1. 字面量赋值:字面量就是数据本身。例如,int a = 10;10就是字面量
  2. 使用value属性给bean的属性赋值时,Spring会把给value属性的值看作字面量。比如,这个“前端开发”就是字面量
  3. 如果我们此时,给Book类再添加一个属性,并为这个属性设置setter方法
  4. 如果我们在注入时,给该属性赋null,该怎么做呢?
  5. xml实体小于号在XML文档中是用来定义标签的开始的,不能随便使用。假如我们想给属性赋的值中出现了 < 该怎么办呢?例如
  6. 解决xml实体的方式一:转义,小于号(<)用 &lt; 表示,大于号(>)用 &gt; 表示。这样就不会报错了
  7. 解决xml实体的方式二:CDATA区。XML解析器看到 CDATA区 就知道这里是纯文本就不会当作XML标签或属性来解析,所以在CDATA区中写什么符号都可以
  8. <![CDATA[在这里面写什么符号都可以]]>
  9. 测试一下,发现成功

(6)实验五:为对象类型属性赋值

方式一:引用外部bean

  1. 准备两个类,员工类和部门类
  2. 代码:
    package com.atguigu.spring6.iocxml.ditest;
    
    //员工类
    public class Emp {
        //对象类型的属性 员工属于某个部门
        private Dept dept;
        //员工名称
        private String ename;
        //员工年龄
        private Integer age;
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public void work(){
            System.out.println(ename+"emp work..."+age);
            dept.info();
        }
    }
    package com.atguigu.spring6.iocxml.ditest;
    
    //部门类
    public class Dept {
        private String dname;
    
        public void setDname(String dname) {
            this.dname = dname;
        }
    
        public void info(){
            System.out.println("部门名称:" + dname);
        }
    }
  3. 我们新建一个Spring配置文件(文件名称可以随意,但是 Spring 配置文件必须放在src/main/resources 内)
    <?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">
    
        <!--
            1.创建两个类对象emp和dept
            2.在emp的bean标签里,用property引入dept的bean
        -->
        <bean id="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
            <property name="dname" value="安保部"></property>
        </bean>
        <bean id="emp" class="com.atguigu.spring6.iocxml.ditest.Emp">
            <!--基本类型属性注入-->
            <property name="ename" value="lucy"></property>
            <property name="age" value="50"></property>
            <!--对象类型属性注入-->
            <property name="dept" ref="dept"></property>
        </bean>
    
    </beans>

  4. 为对象类型的属性注入,不要用value,而是用ref。ref后面跟的是,要注入的对象的id

方式二:内部bean

  1. 内部bean方式代码演示:
    <?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注入-->
        <bean id="emp2" class="com.atguigu.spring6.iocxml.ditest.Emp">
            <property name="ename" value="mary"></property>
            <property name="age" value="20"></property>
            <property name="dept">
                <bean id="dept2" class="com.atguigu.spring6.iocxml.ditest.Dept">
                    <property name="dname" value="财务部"></property>
                </bean>
            </property>
        </bean>
    
    </beans>

  2. 总结

方式三:级联属性赋值

  1. 级联赋值代码演示:
    package com.atguigu.spring6.iocxml.ditest;
    
    //员工类
    public class Emp {
        //对象类型的属性 员工属于某个部门
        private Dept dept;
        //员工名称
        private String ename;
        //员工年龄
        private Integer age;
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public Dept getDept() {
            return dept;
        }
    
        public String getEname() {
            return ename;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void work(){
            System.out.println(ename+"emp work..."+age);
            dept.info();
        }
    }
    <?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="dept3" class="com.atguigu.spring6.iocxml.ditest.Dept">
            <property name="dname" value="技术研发部"></property>
        </bean>
        <bean id="emp3" class="com.atguigu.spring6.iocxml.ditest.Emp">
            <property name="ename" value="tom"></property>
            <property name="age" value="30"></property>
            <property name="dept" ref="dept3"></property>
            <property name="dept.dname" value="测试部"></property>
        </bean>
    
    </beans>
  2. 在进行级联赋值时,必须为对象类型的属性提供 getter 方法。具体过程为:(1)先通过 getter 方法获取该对象,(2)再利用该对象所属类中提供的 setter 方法为其属性赋值

(7)实验六:为数组类型属性赋值

  1. 修改Emp类的代码,为员工类添加一个新属性,该属性为数组类型
    package com.atguigu.spring6.iocxml.ditest;
    
    import java.util.Arrays;
    
    //员工类
    public class Emp {
        //对象类型的属性 员工属于某个部门
        private Dept dept;
        //员工名称
        private String ename;
        //员工年龄
        private Integer age;
        //爱好
        private String[] loves;
    
        public void setDept(Dept dept) {
            this.dept = dept;
        }
    
        public void setEname(String ename) {
            this.ename = ename;
        }
    
        public void setAge(Integer age) {
            this.age = age;
        }
    
        public void setLoves(String[] loves) {
            this.loves = loves;
        }
    
        public Dept getDept() {
            return dept;
        }
    
        public String getEname() {
            return ename;
        }
    
        public Integer getAge() {
            return age;
        }
    
        public void work(){
            System.out.println(ename+"emp work..."+age);
            dept.info();
            System.out.println(Arrays.toString(loves));
        }
    }
  2. 为了方便,我们再创建一个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="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
            <property name="dname" value="技术部"></property>
        </bean>
        <bean id="emp" class="com.atguigu.spring6.iocxml.ditest.Emp">
            <!--普通类型的属性-->
            <property name="ename" value="lucy"></property>
            <property name="age" value="20"></property>
            <!--对象类型的属性-->
            <property name="dept" ref="dept"></property>
            <!--数组类型的属性-->
            <property name="loves">
                <array>
                    <value>吃饭</value>
                    <value>睡觉</value>
                    <value>敲代码</value>
                </array>
            </property>
        </bean>
    </beans>

(8)实验七:为集合类型属性赋值

  1. List 集合类型属性赋值:首先我们修改部门类,为它添加一个List集合类型的属性
    package com.atguigu.spring6.iocxml.ditest;
    
    import java.util.List;
    
    //部门类
    public class Dept {
    
        //一个部门有很多员工
        private List<Emp> empList;
    
        private String dname;
    
        public String getDname() {
            return dname;
        }
        public void setDname(String dname) {
            this.dname = dname;
        }
    
        public List<Emp> getEmpList() {
            return empList;
        }
    
        public void setEmpList(List<Emp> empList) {
            this.empList = empList;
        }
    
        public void info() {
            System.out.println("部门名称:"+dname);
            for (Emp emp:empList) {
                System.out.println(emp.getEname());
            }
        }
    }

    然后我们新建一个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="empone" class="com.atguigu.spring6.iocxml.ditest.Emp">
            <property name="ename" value="lucy"></property>
            <property name="age" value="20"></property>
        </bean>
        <bean id="emptwo" class="com.atguigu.spring6.iocxml.ditest.Emp">
            <property name="ename" value="mary"></property>
            <property name="age" value="30"></property>
        </bean>
        <bean id="dept" class="com.atguigu.spring6.iocxml.ditest.Dept">
            <property name="dname" value="技术部"></property>
            <property name="empList">
                <list>
                    <ref bean="empone"></ref>
                    <ref bean="emptwo"></ref>
                </list>
            </property>
        </bean>
    </beans>
  2. Map 集合类型属性赋值(先创建两个类,Student类和Teacher类,然后配置一个Spring的xml文件):
    package com.atguigu.spring6.iocxml.dimap;
    
    import java.util.Map;
    
    //学生类
    public class Student {
        private Map<String,Teacher> teacherMap;
        private String sid;
        private String sname;
    
        public String getSid() {
            return sid;
        }
    
        public void setSid(String sid) {
            this.sid = sid;
        }
    
        public String getSname() {
            return sname;
        }
    
        public void setSname(String sname) {
            this.sname = sname;
        }
    
        public Map<String, Teacher> getTeacherMap() {
            return teacherMap;
        }
    
        public void setTeacherMap(Map<String, Teacher> teacherMap) {
            this.teacherMap = teacherMap;
        }
    
        public void run(){
            System.out.println("学生编号:" + sid + "学生名称:" + sname);
            System.out.println(teacherMap);
        }
    }
    package com.atguigu.spring6.iocxml.dimap;
    
    //老师类
    public class Teacher {
        private String teacherId;
        private String teacherName;
    
        public String getTeacherId() {
            return teacherId;
        }
    
        public void setTeacherId(String teacherId) {
            this.teacherId = teacherId;
        }
    
        public String getTeacherName() {
            return teacherName;
        }
    
        public void setTeacherName(String teacherName) {
            this.teacherName = teacherName;
        }
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "teacherId='" + teacherId + '\'' +
                    ", teacherName='" + teacherName + '\'' +
                    '}';
        }
    }
    <?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">
    
        <!--
            1.创建两个对象
            2.注入普通类型属性
            3.在学生bean注入map集合类型属性
        -->
        <bean id="teacherone" class="com.atguigu.spring6.iocxml.dimap.Teacher">
            <!--注入普通类型属性-->
            <property name="teacherId" value="100"></property>
            <property name="teacherName" value="西门讲师"></property>
        </bean>
        <bean id="teachertwo" class="com.atguigu.spring6.iocxml.dimap.Teacher">
            <!--注入普通类型属性-->
            <property name="teacherId" value="200"></property>
            <property name="teacherName" value="上官讲师"></property>
        </bean>
        <bean id="student" class="com.atguigu.spring6.iocxml.dimap.Student">
            <!--注入普通类型属性-->
            <property name="sid" value="2000"></property>
            <property name="sname" value="张三"></property>
            <!--注入Map集合类型属性-->
            <property name="teacherMap">
                <map>
                    <entry>
                        <key>
                            <value>10010</value>
                        </key>
                        <ref bean="teacherone"></ref>
                    </entry>
                    <entry>
                        <key>
                            <value>
                                10086
                            </value>
                        </key>
                        <ref bean="teachertwo"></ref>
                    </entry>
                </map>
            </property>
        </bean>
    </beans>
  3. 这是xml文件的约束,写明了xml文件中能用哪些标签,标签里能用哪些属性
  4. 使用 util:listutil:map 前,必须引入相应的命名空间,可以通过IDEA的提示功能选择
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/beans/spring-util.xsd
                               http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                              ">
  5. 注入List集合和Map集合的第二种方式( util:list 和 util:map 的id可以随便写,只是ref的时候是根据id来的):
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:util="http://www.springframework.org/schema/util"
           xsi:schemaLocation="http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
                               http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                              ">
            <!--
                1.创建三个对象
                2.注入普通类型属性
                3.使用util:类型 定义
                4.在学生bean引入
            -->
        <bean id="lessonone" class="com.atguigu.spring6.iocxml.dimap.Lesson">
            <property name="lessonName" value="java开发"></property>
        </bean>
        <bean id="lessontwo" class="com.atguigu.spring6.iocxml.dimap.Lesson">
            <property name="lessonName" value="前端开发"></property>
        </bean>
        <bean id="teacherone" class="com.atguigu.spring6.iocxml.dimap.Teacher">
            <property name="teacherId" value="100"></property>
            <property name="teacherName" value="西门讲师"></property>
        </bean>
        <bean id="teachertwo" class="com.atguigu.spring6.iocxml.dimap.Teacher">
            <property name="teacherId" value="200"></property>
            <property name="teacherName" value="欧阳讲师"></property>
        </bean>
        <bean id="student" class="com.atguigu.spring6.iocxml.dimap.Student">
            <property name="sid" value="10000"></property>
            <property name="sname" value="lucy"></property>
            <!--注入list和map类型的属性-->
            <property name="lessonList" ref="lessonList"></property>
            <property name="teacherMap" ref="teacherMap"></property>
        </bean>
        <util:list id="lessonList">
            <ref bean="lessonone"></ref>
            <ref bean="lessontwo"></ref>
        </util:list>
        <util:map id="teacherMap">
            <entry>
                <key>
                    <value>10010</value>
                </key>
                <ref bean="teacherone"></ref>
            </entry>
            <entry>
                <key>
                    <value>10086</value>
                </key>
                <ref bean="teachertwo"></ref>
            </entry>
        </util:map>
    </beans>
  6. 注意:按照上面的图写的xml文件写错了,有个地方要注意一下。就是我们要把beans的地方全换成util,有个地方漏了
  7. 总结: 

(9)实验八:p命名空间

  1. p命名空间是指这种(灰色部分):
  2. 注意xsi:schemaLocation部分就不用像util一样修改了
  3. p命名空间注入
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:util="http://www.springframework.org/schema/util"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/util
           http://www.springframework.org/schema/util/spring-util.xsd
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

(10)实验九:引入外部属性文件

  1. 加入数据库相关依赖(在pom.xml文件中,加入后记得刷新):
     <!-- MySQL驱动 -->
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
        <version>8.0.30</version>
    </dependency>
    
    <!-- 数据源 -->
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>1.2.15</version>
    </dependency>
  2. 创建外部属性文件(在 src/main/resources 中创建),properties 格式
    jdbc.user=root
    jdbc.password=atguigu
    jdbc.url=jdbc:mysql://localhost:3306/ssm?serverTimezone=UTC
    jdbc.driver=com.mysql.cj.jdbc.Driver
  3. 在src/main/resources中创建一个Spring的配置文件,并引入 context 命名空间,注意,此时还要添加xsi:schemaLocation的部分
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                              ">
    
    </beans>

  4. 还要引入外部属性文件,并完成数据库信息的注入:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                               http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                              ">
    
        <!--引入外部属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
        <!--完成数据库信息注入-->
        <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
            <property name="url" value="${jdbc.url}"></property>
            <property name="username" value="${jdbc.user}"></property>
            <property name="password" value="${jdbc.password}"></property>
            <property name="driverClassName" value="${jdbc.driver}"></property>
        </bean>
    
    </beans>
  5. 测试代码:
    public class TestJdbc {
        @Test
        public void demo1(){
            DruidDataSource dataSource = new DruidDataSource();
            dataSource.setUrl("jdbc:mysql://localhost:3306/spring?serverTimezone=UTC");
            dataSource.setUsername("root");
            dataSource.setPassword("atguigu");
            dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        }
    
        @Test
        public void demo2(){
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-jdbc.xml");
            DruidDataSource druidDataSource = context.getBean(DruidDataSource.class);
            System.out.println(druidDataSource.getUrl());
        }
    }

  6. 总结
  7. druidDataSource:

(11)实验十:bean的作用域

  1. 在 Spring 中可以通过配置 bean 标签的 scope 属性来指定 bean 的作用域范围,scope的取值含义参见下表:
    取值含义创建对象的时机
    singleton(默认在IoC容器中,这个bean的对象始终为单实例IoC容器初始化时
    prototype这个bean在IoC容器中有多个实例获取bean时
  2. 如果是在WebApplicationContext环境下,还会有另外几个作用域(但不常用):
    取值含义
    request在一个请求范围内有效
    session在一个会话范围内有效
  3. scope 的取值为 singleton ,测试(三个文件的代码)
    package com.atguigu.spring6.iocxml.scope;
    
    public class Orders {
    }
    <?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">
    
        <!--通过scope属性能配置单实例还是多实例-->
        <bean id="orders" class="com.atguigu.spring6.iocxml.scope.Orders" scope="singleton"></bean>
    </beans>
    package com.atguigu.spring6.iocxml.scope;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestOrders {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-scope.xml");
            Orders orders = context.getBean("orders", Orders.class);
            System.out.println(orders);
        }
    }

    Creating shared instance of singleton bean 'orders'代表创建了一个单实例对象

  4. 我们再获取一个bean看运行结果可以发现,当 scope 是 singleton 时,bean 标签创建的就是一个单实例对象
  5. scope 的取值为 prototype ,测试(我们只修改了 Spring 的 xml 文件,将 scope 的取值改成了 prototype )可以看到,获取的两个 bean 其实是不同的对象
  6. 总结:
    1. scope 为 singleton 时,Spring IoC 容器初始化就创建 Bean 实例
    2. scope 为 prototype 时,在调用 getBean() 获取 Bean 时才创建

(12)实验十一:bean生命周期

  1. 具体的生命周期过程(单例bean就是指被Singleton修饰,原型bean就是指被prototype修饰)
  2. 创建User类:
    package com.atguigu.spring6.iocxml.life;
    
    public class User {
        private String name;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            System.out.println("2.给bean对象设置属性值");
            this.name = name;
        }
    
        //无参数构造
        public User() {
            System.out.println("1.bean对象创建,调用无参数构造");
        }
    
        //初始化方法
        public void initMethod(){
            System.out.println("4.bean对象的初始化,会调用指定的初始化的方法");
        }
        //销毁方法
        public void destroyMethod(){
            System.out.println("7.bean对象销毁,调用指定的销毁方法");
        }
    }
  3. 编写 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="user" class="com.atguigu.spring6.iocxml.life.User"
              scope="singleton" init-method="initMethod" destroy-method="destroyMethod">
            <property name="name" value="lucy"></property>
        </bean>
    </beans>
  4. 测试类:
    package com.atguigu.spring6.iocxml.life;
    
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestUser {
        public static void main(String[] args) {
            ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean-life.xml");
            User user = context.getBean("user", User.class);
            System.out.println("6.bean对象创建完成了,可以使用了");
            System.out.println(user);
            //该方法表示会进行销毁
            context.close();
        }
    }
  5. 进行测试(注意:要想使用close方法,就不能用ApplicationContext接口了):
  6. Bean 后置处理器在 Bean 生命周期的初始化前后添加额外操作,需实现 BeanPostProcessor 接口并配置到 IoC 容器中。它并非只对单个 Bean 生效,而是作用于 IoC 容器里的所有 Bean
  7. 创建 bean 的后置处理器:
    public class MyBeanPost implements BeanPostProcessor {
        @Override
        public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("3.后置处理器在初始化之前执行");
            System.out.println(beanName + "::" + bean);
            return bean;
        }
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
            System.out.println("5.后置处理器在初始化之后执行");
            System.out.println(beanName + "::" + bean);
            return bean;
        }
    }
  8. 在 IoC 容器中配置后置处理器:
    <?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="user" class="com.atguigu.spring6.iocxml.life.User"
              scope="singleton" init-method="initMethod" destroy-method="destroyMethod">
            <property name="name" value="lucy"></property>
        </bean>
        
        <bean id="myBeanPost" class="com.atguigu.spring6.iocxml.life.MyBeanPost"></bean>
    </beans>
  9. 加了后置处理器以后的测试效果:

(13)实验十二:FactoryBean

  1. FactoryBean:
  2. 创建一个User类:
    package com.atguigu.spring6.iocxml.factorybean;
    
    public class User {
    }
  3. 创建一个实现FactoryBean接口的类:
    package com.atguigu.spring6.iocxml.factorybean;
    
    import org.springframework.beans.factory.FactoryBean;
    
    public class MyFactoryBean implements FactoryBean<User> {
        @Override
        public User getObject() throws Exception{
            return new User();
        }
        @Override
        public Class<?> getObjectType(){
            return User.class;
        }
    }
  4. 编写一个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="user" class="com.atguigu.spring6.iocxml.factorybean.MyFactoryBean"></bean>
    </beans>
  5. 编写一个测试类:
    public class TestUser {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-factorybean.xml");
            User user = (User)context.getBean("user");
            System.out.println(user);
        }
    }
  6. 根据测试效果可以发现,虽然我们在配置bean时,对class的配置是myFactoryBean,但是得到的bean并不是这个类的对象,而是该类中的getObject方法返回的对象
  7. 总结

(14)实验十三:基于xml自动装配

  1. 基本环境准备:创建三个软件包(controller、service、dao),controller里面创建一个类UserController。service里面创建一个类UserServiceImpl、一个接口UserService,dao里面创建一个类UserDaoImpl、一个接口UserDao
    package com.atguigu.spring6.iocxml.auto.controller;
    
    import com.atguigu.spring6.iocxml.auto.service.UserService;
    import com.atguigu.spring6.iocxml.auto.service.UserServiceImpl;
    
    public class UserController {
        public void addUser(){
            System.out.println("controller方法执行了...");
            UserService userService = new UserServiceImpl();
            userService.addUserService();
        }
    }
    package com.atguigu.spring6.iocxml.auto.service;
    
    public interface UserService {
        public void addUserService();
    }
    package com.atguigu.spring6.iocxml.auto.service;
    
    import com.atguigu.spring6.iocxml.auto.dao.UserDao;
    import com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl;
    
    public class UserServiceImpl implements UserService{
        @Override
        public void addUserService() {
            System.out.println("userService方法执行了");
            UserDao userDao = new UserDaoImpl();
            userDao.addUserDao();
        }
    }
    package com.atguigu.spring6.iocxml.auto.dao;
    
    public interface UserDao {
        public void addUserDao();
    }
    package com.atguigu.spring6.iocxml.auto.dao;
    
    public class UserDaoImpl implements UserDao{
        @Override
        public void addUserDao() {
            System.out.println("userDao方法执行了");
        }
    }

    addUser方法里调用了addUserService方法、addUserService方法里调用了addUserDao方法

  2. 修改代码(UserController和UserServiceImpl):
    package com.atguigu.spring6.iocxml.auto.controller;
    
    import com.atguigu.spring6.iocxml.auto.service.UserService;
    import com.atguigu.spring6.iocxml.auto.service.UserServiceImpl;
    import org.junit.jupiter.api.Test;
    
    public class UserController {
        private UserService userService;
    
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        @Test
        public void addUser(){
            System.out.println("controller方法执行了...");
            userService.addUserService();
    //        UserService userService = new UserServiceImpl();
    //        userService.addUserService();
        }
    }
    package com.atguigu.spring6.iocxml.auto.service;
    
    import com.atguigu.spring6.iocxml.auto.dao.UserDao;
    import com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl;
    
    public class UserServiceImpl implements UserService{
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void addUserService() {
            System.out.println("userService方法执行了");
            userDao.addUserDao();
    //        UserDao userDao = new UserDaoImpl();
    //        userDao.addUserDao();
        }
    }
  3. 在resources中创建配置文件(这么写就是手动装配
  4. 自动装配怎么写呢?
    <?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="userController" class="com.atguigu.spring6.iocxml.auto.controller.UserController" 
              autowire="byType">
        </bean>
        <bean id="userService" class="com.atguigu.spring6.iocxml.auto.service.UserServiceImpl" 
              autowire="byType">
        </bean>
        <bean id="userDao" class="com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl">
        </bean>
    </beans>
  5. 测试代码:
    package com.atguigu.spring6.iocxml.auto;
    
    import com.atguigu.spring6.iocxml.auto.controller.UserController;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestUser {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean-auto.xml");
            UserController userController = context.getBean("userController", UserController.class);
            userController.addUser();
        }
    }

  6. 自动装配概念:原本通过外部 bean 方式注入对象类型属性,现在可利用 bean 标签的 autowire 属性实现自动装配
  7. byType装配方式:
  8. byName装配方式:
  9. 我们根据代码测试一下:
    <?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="userController" class="com.atguigu.spring6.iocxml.auto.controller.UserController"
              autowire="byName">
        </bean>
        <bean id="userService" class="com.atguigu.spring6.iocxml.auto.service.UserServiceImpl"
              autowire="byName">
        </bean>
        <bean id="userDao" class="com.atguigu.spring6.iocxml.auto.dao.UserDaoImpl">
        </bean>
    </beans>

  10. 当我们改变xml文件中对象的id,就可以发现不能自动装配成功了(因为UserController类中,UserService类型的属性名字叫userService,但是我们此时的对象id不是userService了,而是userServiceImpl。UserServiceImpl类中,UserDao类型的属性名字叫userDao,但是我们此时的对象id不是userDao了,而是userDaoImlp)可以看到爆红

三、基于注解管理Bean(⭐)

从 Java 5 起,Java 支持注解,它是代码特殊标记,能在编译、类加载和运行时被读取以执行对应处理,开发人员可借此在不改动原代码逻辑时嵌入补充信息

Spring 2.5 起全面支持注解技术,可用注解实现自动装配,简化 XML 配置。通过注解实现自动装配步骤为

  1. 引入依赖
  2. 开启组件扫描
  3. 使用注解定义bean
  4. 依赖注入

(1)搭建子模块spring6-ioc-annotation

  1. 引入日志模块log4j2.xml
  2. 创建一个Spring的配置文件
  3. 因为依赖已经在父模块中引入过,通过依赖的继承机制,子模块就不需要重复引入了

(2)开启组件扫描

  1. Spring 默认不采用注解装配 Bean,可在 Spring 的 XML 文件里,利用 <context:component-scan> 元素开启 Spring Beans 自动扫描功能。开启后,Spring 会自动扫描 base - package 属性指定的包及其子包下的所有类,若类使用了 @Component 注解,就会将该类装配到容器中
  2. 情况一:最基本的扫描方式
  3. 情况二:指定要排除的组件
    <context:component-scan base-package="com.atguigu.spring6">
        <!-- context:exclude-filter标签:指定排除规则 -->
        <!-- 
     		type:设置排除或包含的依据
    		type="annotation",根据注解排除,expression中设置要排除的注解的全类名
    		type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    	-->
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
            <!--<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
    </context:component-scan>

    (1)<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>表示Controller这个注解不进行扫描(2)<context:exclude-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>表示UserController这个不进行扫描

  4. 情况三:仅扫描指定组件
    <context:component-scan base-package="com.atguigu" use-default-filters="false">
        <!-- context:include-filter标签:指定在原有扫描规则的基础上追加的规则 -->
        <!-- use-default-filters属性:取值false表示关闭默认扫描规则 -->
        <!-- 此时必须设置use-default-filters="false",因为默认规则即扫描指定包下所有类 -->
        <!-- 
     		type:设置排除或包含的依据
    		type="annotation",根据注解排除,expression中设置要排除的注解的全类名
    		type="assignable",根据类型排除,expression中设置要排除的类型的全类名
    	-->
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    	<!--<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>-->
    </context:component-scan>

    (1)<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>表示只扫描Controller这个注解(2)<context:include-filter type="assignable" expression="com.atguigu.spring6.controller.UserController"/>表示只扫描UserController这个

(3)使用注解定义bean

  1. Spring提供了多个注解,这些注解可以直接标注在Java类上,将它们定义成Spring Bean
    注解说明
    @Component该注解用于描述Spring中的Bean,它是一个泛化的概念,仅仅表示容器中的一个组件(Bean),并且可以作用在应用的任何层次,例如Service层、Dao层等。使用时只需将该注解标注在相应的类上即可
    @Repository该注解用于将Dao层(数据访问层)的类标识为Spring中的Bean,其功能和@Component相同
    @Service该注解用于将Service层(业务层)的类标识为Spring中的Bean,其功能和@Component相同
    @Controller该注解用于将Controller层(控制层)的类标识为Spring中的Bean,其功能和@Component相同
  2. 使用举例:value可以不写,比如类名为User,如果不写value的话,value的值默认为user
  3. 建一个User类,建一个TestUser类
    package com.atguigu.spring6.bean;
    
    import org.springframework.stereotype.Component;
    
    @Component(value="user")//<bean id="user" class="...">
    public class User {
    }
    package com.atguigu.spring6.bean;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestUser {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            User user = context.getBean(User.class);
            System.out.println(user);
        }
    }
  4. 配置文件:
    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                              http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                              ">
            <!--开启组件扫描-->
            <context:component-scan base-package="com.atguigu.spring6"></context:component-scan>
    
            
    </beans>
  5. 测试效果:
  6. 说明在User类上加了@Component注解,效果和bean标签一样

(4)实验一:@Autowired注入

单独使用@Autowired注解,默认根据类型装配

@Autowired注解源码:

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}

源码中有两处需要注意:

  1. 第一处:该注解可以标注在哪里:
    1. 构造方法
    2. 方法
    3. 属性
    4. 形参
    5. 注解
  2. 第二处: 该注解有一个 required 属性,默认值是 true 。表示在注入的时候要求被注入的bean是必须存在的,如果不存在则报错。如果required属性设置为 false ,表示被注入的bean存在或不存在都没关系,存在的话就注入,不存在也不报错

3.4.1场景一:属性注入

  1. 属性注入就是根据类型找到对象,进行注入的。类似于,基于XML的自动装配
  2. 创建三个软件包(controller、service、dao)。分别在这几个包中创建实现类和接口
  3. 代码:
    package com.atguigu.spring6.autowired.controller;
    
    import com.atguigu.spring6.autowired.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
        //注入service
        //第一种方式:属性注入
        @Autowired //根据类型找到对应的对象,完成注入
        private UserService userService;
        public void add(){
            System.out.println("controller......");
            userService.add();
        }
    }
    package com.atguigu.spring6.autowired.service;
    
    public interface UserService {
        public void add();
    }
    package com.atguigu.spring6.autowired.service;
    
    import com.atguigu.spring6.autowired.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService{
        //注入dao
        @Autowired
        private UserDao userDao;
        @Override
        public void add(){
            System.out.println("service......");
            userDao.add();
        }
    }
    package com.atguigu.spring6.autowired.dao;
    
    public interface UserDao {
        public void add();
    }
    package com.atguigu.spring6.autowired.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository
    public class UserDaoImpl implements UserDao{
        @Override
        public void add(){
            System.out.println("dao执行...");
        }
    }
  4. 创建测试类:
    package com.atguigu.spring6.autowired;
    
    import com.atguigu.spring6.autowired.controller.UserController;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestUserController {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            UserController userController = context.getBean(UserController.class);
            userController.add();
        }
    }

  5. 解释:根据注解创建了三个bean(UserController、UserServiceImpl、UserDaoImpl),然后根据类型装配,UserController类中有一个Userservice类型的属性→给userService装配UserServiceImpl对象,UserServiceImpl中有一个UserDao类型的属性→给userDao装配UserDaoImlp对象

3.4.2场景二:set注入

  1. 修改代码(UserController和UserServiceImpl的):
    package com.atguigu.spring6.autowired.controller;
    
    import com.atguigu.spring6.autowired.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
        private UserService userService;
    
        @Autowired
        public void setUserService(UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller......");
            userService.add();
        }
    }
    package com.atguigu.spring6.autowired.service;
    
    import com.atguigu.spring6.autowired.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService{
        private UserDao userDao;
    
        @Autowired
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add(){
            System.out.println("service......");
            userDao.add();
        }
    }
  2. 测试效果:
  3. 理解

3.4.3场景三:构造方法注入

  1. 修改代码(UserController和UserServiceImpl的):
    package com.atguigu.spring6.autowired.controller;
    
    import com.atguigu.spring6.autowired.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
        private UserService userService;
    
        @Autowired
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller......");
            userService.add();
        }
    }
    package com.atguigu.spring6.autowired.service;
    
    import com.atguigu.spring6.autowired.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService{
        private UserDao userDao;
    
        @Autowired
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add(){
            System.out.println("service......");
            userDao.add();
        }
    }
  2. 测试效果:
  3. 理解

3.4.4场景四:形参上注入

  1. 就是不在构造器上加@Autowired,但是把 @Autowired 加在构造器形参上
  2. 修改代码(UserController和UserServiceImpl的):
    package com.atguigu.spring6.autowired.controller;
    
    import com.atguigu.spring6.autowired.service.UserService;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
        private UserService userService;
        
        public UserController(@Autowired UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller......");
            userService.add();
        }
    }
    package com.atguigu.spring6.autowired.service;
    
    import com.atguigu.spring6.autowired.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService{
        private UserDao userDao;
    
        public UserServiceImpl(@Autowired UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add(){
            System.out.println("service......");
            userDao.add();
        }
    }
  3. 测试效果:
  4. 理解

3.4.5场景五:只有一个构造函数,无注解

  1. 当有参数的构造方法只有一个时,@AutoWired注解可以省略
  2. 修改代码:
    package com.atguigu.spring6.autowired.controller;
    
    import com.atguigu.spring6.autowired.service.UserService;
    import org.springframework.stereotype.Controller;
    
    @Controller
    public class UserController {
        private UserService userService;
    
        public UserController(UserService userService) {
            this.userService = userService;
        }
    
        public void add(){
            System.out.println("controller......");
            userService.add();
        }
    }
    package com.atguigu.spring6.autowired.service;
    
    import com.atguigu.spring6.autowired.dao.UserDao;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService{
        private UserDao userDao;
    
        public UserServiceImpl(UserDao userDao) {
            this.userDao = userDao;
        }
    
        @Override
        public void add(){
            System.out.println("service......");
            userDao.add();
        }
    }
  3. 测试通过:

3.4.6场景六:@Autowired注解和@Qualifier注解联合

  1. 比如,当接口UserDao有两个实现类UserDaoImpl和UserRedisDaoImpl时,由于这两个类上都加了@Repository注解,因此会生成它们各自对应的bean。当只根据类型查找时,会发现满足UserDao的对象有两个,这时就会报错(expected single matching bean but found 2: userDaoImpl,userRedisDaoImpl)
  2. 针对该问题,解决方案是:使用两个注解,根据名称注入
    package com.atguigu.spring6.autowired.service;
    
    import com.atguigu.spring6.autowired.dao.UserDao;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    import org.springframework.stereotype.Service;
    
    @Service
    public class UserServiceImpl implements UserService{
        @Autowired
        @Qualifier(value = "userRedisDaoImpl")
        private UserDao userDao;
    
        @Override
        public void add(){
            System.out.println("service......");
            userDao.add();
        }
    }

  3. 个人理解

(5)实验二:@Resource注入

@Resource注解也可以完成属性注入。那么它和@Autowired注解有什么区别呢?

  1. 总结
  2. 如图所示:

@Resource注解不属于 JDK 核心,而是 Java EE(现 Jakarta EE)规范一部分。在 JDK 8 环境下使用它无需额外引入依赖;但在高于 JDK 11低于 JDK 8 的环境中使用,则需要引入相应依赖

<dependency>
    <groupId>jakarta.annotation</groupId>
    <artifactId>jakarta.annotation-api</artifactId>
    <version>2.1.1</version>
</dependency>

3.5.1根据name注入

  1. 总结
  2. 测试代码:
    package com.atguigu.spring6.resource.controller;
    
    import com.atguigu.spring6.resource.service.UserService;
    import jakarta.annotation.Resource;
    import org.springframework.stereotype.Controller;
    
    @Controller(value = "myUserController")
    public class UserController {
        @Resource(name="myUserService")
        private UserService userService;//此时,指定了名称,那么我们就要找到名为myUserService的对象
    
        public void add(){
            System.out.println("controller......");
            userService.add();
        }
    }
    package com.atguigu.spring6.resource.service;
    
    import com.atguigu.spring6.resource.dao.UserDao;
    import jakarta.annotation.Resource;
    import org.springframework.stereotype.Service;
    
    @Service(value = "myUserService")
    public class UserServiceImpl implements UserService {
        @Resource
        private UserDao myUserDao;
    
        @Override
        public void add(){
            System.out.println("service......");
            myUserDao.add();
        }
    }
    package com.atguigu.spring6.resource.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository(value = "myUserDao")
    public class UserDaoImpl implements UserDao {
        @Override
        public void add(){
            System.out.println("dao执行...");
        }
    }
    package com.atguigu.spring6.resource.dao;
    
    import org.springframework.stereotype.Repository;
    
    @Repository(value = "myUserRedisDao")
    public class UserRedisDaoImpl implements UserDao {
        @Override
        public void add() {
            System.out.println("dao redis......");
        }
    }
    package com.atguigu.spring6.resource;
    
    import com.atguigu.spring6.resource.controller.UserController;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestUserController {
        public static void main(String[] args) {
            ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
            UserController userController = context.getBean("myUserController",UserController.class);
            userController.add();
        }
    }
    package com.atguigu.spring6.resource.service;
    
    public interface UserService {
        public void add();
    }
    package com.atguigu.spring6.resource.dao;
    
    public interface UserDao {
        public void add();
    }
  3. 测试成功
  4. 如果根据name没有找到,就会根据类型找
  5. 我们修改一下UserController中的代码,把UserService类型的属性的属性名改成“userService”。由于此时@Resource也没有给name显式赋值,因此根据属性名“userService”来找对象,而此时没有叫userService的对象,所以它会根据类型来找

3.5.2name未知注入

  1. 当name没有显式指定时,name就是该属性的属性名
  2. 演示看3.5.1

3.5.3其他情况

  1. 当name没有显式指定时,name就是该属性的属性名。当根据属性名也找不到对应的对象时,我们就根据该类型的类型来找,找到可以兼容该类型的对象
  2. 演示看3.5.1

(6)Spring全注解开发

  1. 此前借助注解完成对象创建与属性注入时,需使用 Spring 配置文件开启组件扫描。而全注解开发则摒弃了配置文件,采用编写配置类的方式替代,以此实现组件扫描等配置功能
  2. 用配置类来代替配置文件,开启组件扫描。配置类写在 src/main/java 中
    package com.atguigu.spring6.config;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    @ComponentScan("com.atguigu.spring6")
    public class SpringConfig {
    
    }

    配置类上有两个注解,一个是@Configuration,一个是@ComponentScan。@Configuration说明这是一个配置类,@ComponentScan("包名")和配置文件中<context:component-scan base-package="com.atguigu.spring6"></context:component-scan>作用一样

  3. 在测试程序中进行修改,把加载配置文件改成加载配置类
    package com.atguigu.spring6.resource;
    
    import com.atguigu.spring6.config.SpringConfig;
    import com.atguigu.spring6.resource.controller.UserController;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class TestUserController {
        public static void main(String[] args) {
            //加载配置类
            ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
            UserController userController = context.getBean("myUserController",UserController.class);
            userController.add();
        }
    }

    把原来的 ClassPathXMLApplicationContext 改成 AnnotationConfigApplicationContext

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值