Spring初体验

1.Spring简介

1.1 Spring历史

  • Spring作为当今java开发的框架,与java语言已经密不可分,没有Spring,无法想象开发java程序是多么的工程巨大,Spring将程序开发简化,使得程序开发变得越来越简单。

  • Spring历史:

    1. Spring寓意“春天”,就如其名一样,Spring给Java开发乃至整个软件开发行业带来的春天。在Spring框架出来之前,JavaEE 是Sun公司的所制订的EJB框架为标准的。在“遥远”的EJB年代,开发一个EJB需要大量的接口和配置文件,直至EJB 2.0的年代,开发一个EJB还需要配置两个文件,其结果就是配置的工作量比开发的工作量还要大。EJB框架的庞大性与学习难度也比较高,对于软件开发是不利的。

    2. Spring框架的前身是interface21(interface21官网),interface21作为Spring的前身,在软件行业也有着起作用。2002年,Rod Johnson(牛人),首次推出了Spring框架的雏形interface21框架。

    3. 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,推出了1.0正式版。说到Rod Johnson这个人,也不得不说一下,Rod Johnson是一名悉尼大学的博士。

    4. Spring框架有着以下的优点:(以下是官网描述)

      (1)Spring 使创建 Java 企业应用程序变得容易。它提供了在企业环境中使用 Java 语言所需的一切,并支持 Groovy 和 Kotlin 作为 JVM 上的替代语言,并且可以根据应用程序的需求灵活地创建多种体系结构。从 Spring Framework 5.0 开始,Spring 需要 JDK 8(Java SE 8)(使用Spring推荐版本),并且已经为 JDK 9 提供了现成的支持。

      (2)Spring 支持广泛的应用场景。在大型企业中,应用程序通常存在很长时间,并且必须在升级周期不受开发人员控制的 JDK 和应用程序服务器上运行。其他服务器则可以作为单个 jar 运行,并且服务器可以嵌入云环境中。还有一些可能是不需要服务器的独立应用程序(例如批处理或集成工作负载)。

      (3)Spring 是开源的。它拥有一个庞大而活跃的社区,可以根据各种实际用例提供持续的反馈。这帮助 Spring 在很长一段时间内成功地 Developing 了。

    5. 参考网页:

      Spring官网:https://spring.io

      Spring中文文档网站:https://www.docs4dev.com/docs/zh/spring-framework/5.1.3.RELEASE/reference/overview.html

      github网站:https://github.com/spring-projects/spring-framework

1.2 优点:

  • Spring是一个开源的免费的框架,容器
  • Spring是一个轻量级的框架,是非侵入式的的框架
  • Spring的核心是控制反转(IOC),面向切面编程(AOP)
  • Spring框架支持事务处理,对其他框架着良好的支持

总结:Spring框架是一个轻量级的,开源的的支持事务处理与有着IOC与AOP的框架(容器)

1.3 Spring框架组成

image-20210415191446160

Spring框架是一个分层的架构,由7个定义良好的模块组成。Spring模块构建在核心容器上,核心容器定义了创建,配置管理和管理bean的方式。

image-20210415192029957

组成Spring框架的每个模块(或组件)都可以单独存在,或者与其他一个或者多个模块联合使用。每个模块的功能如下:

  • Spring Core核心容器:核心容器提供了Spring框架的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分离。

  • Spring Context:Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,例如:JNDI(ava 命名与目录接口(Java Naming and Directory Interface)),EJB,电子邮件,国际化,校验和调度功能。

  • Spring AOP:通过配置管理特征,Spring AOP模块直接将面向切面(底层实现:动态代理)的编程功能,集成到了Spring框架中。所以,可以很容易的事Spring框架管理支持AOP的对象。Spring AOP模块为基于Spring的应用程序中的对象提供了事务管理服务。通过Spring AOP,不用依赖组件,就可以将声明性事务管理集成到应用程序中。

  • Spring DAO:JDBC DAO抽象层提供了有意义的异常层次结构,可用该结构来管理异常处理和不同数据库供应商抛出的错误信息。异常层次结构简化了错误处理,并且极大的降低了需要编写的异常代码数量(例如打开和关闭连接)。Sprin DAO的面向JDBC的异常遵从通用的DAO异常层次结构。

  • Spring ORM:Sping框架插入了若干个ORM框架,从而提供了ORM到对象关系工具,其中包括JDO(Java数据对象Java Data Objects),Hibernate和iBatis SQL Map。所有这些都遵从Spring的通用事务和DAO异常层次结构。

  • Spring Web模块:Web上下文模块建立在应用程序上下文模块之上,为基于Web的应用程序提供了上下文。所以,Spring框架支持与Jakarta Structs的集成。Web模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。

  • Spring Web MVC框架MVC框架是一个全功能的构建Web应用程序的MVC实现。通过策略接口,MVC框架变为高度可配置化,MVC容纳了大量的视图技术,其中包括JSP,Velocity(Velocity是基于Java开发的模板引擎),Tiles,iText和POI。

1.4 扩展

Spring Boot与Spring Cloud

  • Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务。
  • Spring Cloud是基于Spring Boot实现的。
  • Spring Boot专注于快速,方便集成的单个微服务个体,Spring Cloud关注全局的的服务整治框架。
  • Spring Boot使用了约束大于配置的理念。Spring Booy可以离开Spring Cloud独立开发项目,但是Spring Cloud离不开Spring Boot的支持,属于依赖关系。
  • Spring Boot在啊Spring Cloud中起到了承上启下的作用,如果需要学习Spring Cloud,必须需要学习Spring Boot。

2.Spring IOC

我们上面已经简要的介绍了IOC(控制反转),接下来将使用代码与举例方式来更好的了解什么是IOC。

2.1 提要

在介绍Spring IOC之前,我们先来看一个案例:

  1. 新建一个Maven项目,在使用Spring之前,我们需要导入Spring需要的依赖。依赖我们进入Maven仓库中寻找。

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
    
        <groupId>com.xiaoli</groupId>
        <artifactId>Spring</artifactId>
        <packaging>pom</packaging>
        <version>1.0-SNAPSHOT</version>
        <modules>
            <module>spring-01-ioc</module>
        </modules>
    
        <dependencies>
            <!--导入Spring的依赖,我们导入Spring-webmvc依赖是因为webmvc会为我们导入Spring其他依赖的jar包-->
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-webmvc</artifactId>
                <version>5.2.9.RELEASE</version>
            </dependency>
    
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.11</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
        <properties>
            <maven.compiler.source>8</maven.compiler.source>
            <maven.compiler.target>8</maven.compiler.target>
        </properties>
    
    </project>
    
  2. 以我们之前的dao接口为例,我们在接口中编写以下代码:

    • 项目结构图

      image-20210415201022087

    • UserDao接口:

      package com.xiaoli.dao;
      
      //dao接口
      public interface UserDao {
          void getUser();
      }
      
      

      UserMysqlDaoImple实现类:

      package com.xiaoli.dao;
      
      public class UserMysqlDaoImpl implements UserDao{
          @Override
          public void getUser() {
              System.out.println("UserMysqlDao接口的实现");
          }
      }
      
      

      UserOracleDaoImpl实现类:

      package com.xiaoli.dao;
      
      public class UserOracleDaoImpl implements UserDao{
          @Override
          public void getUser() {
              System.out.println("UserOracleDao接口的实现");
          }
      }
      
      

      UserService接口:

      package com.xiaoli.Service;
      
      
      //Service接口
      public interface UserService {
      
          void getUserList();
      }
      
      

      UserServiceImpl实现类:

      package com.xiaoli.Service;
      
      import com.xiaoli.dao.UserDao;
      import com.xiaoli.dao.UserOracleDaoImpl;
      
      public class UserServiceImpl implements UserService{
          //Service需要组合Dao
          private UserDao userDao = new UserOracleDaoImpl();
      
      
          @Override
          public void getUserList() {
              userDao.getUser();
          }
      }
      
      

      测试:

      import com.xiaoli.Service.UserService;
      import com.xiaoli.Service.UserServiceImpl;
      import org.junit.Test;
      
      //传统的方式实现
      public class TestTradition {
      
          @Test
          public void test1(){
      
              UserService userService = new UserServiceImpl();
      
              //service层调用dao层
              userService.getUserList();
              
          }
      }
      
      
    • 需求,我们现在需要调用dao到不同实现类:myql与oracle的实现类。

      1. 第一种解决方法:
        我们需要到service层修改我们的源代码,代码的修改权在开发者的手中,主动的修改代码,不符合设计模式中的“开闭原则”,对于需求的变动每一次都要去修改源代码,这是一种”愚蠢“的编码方式。

        package com.xiaoli.Service;
        
        import com.xiaoli.dao.UserDao;
        import com.xiaoli.dao.UserMysqlDaoImpl;
        import com.xiaoli.dao.UserOracleDaoImpl;
        
        import java.util.Date;
        
        public class UserServiceImpl implements UserService{
            //Service需要组合Dao
        //    private UserDao userDao = new UserOracleDaoImpl();
            private UserDao userDao1 = new UserMysqlDaoImpl();
        
        
            @Override
            public void getUserList() {
        //        userDao.getUser();
                userDao1.getUser();
            }
        }
        
        
      2. 第二种解决方式:我们修改Service层的dao方法,使其可以动态的修改,这个时候,将控制权交予用户之手,用户决定什么时候修改。

        UserServiceImple实现类:

        package com.xiaoli.Service;
        
        import com.xiaoli.dao.UserDao;
        import com.xiaoli.dao.UserMysqlDaoImpl;
        import com.xiaoli.dao.UserOracleDaoImpl;
        
        import java.util.Date;
        
        public class UserServiceImpl implements UserService{
            //Service需要组合Dao
            private UserDao userDao;
            
            //使用set方法,动态的修改dao
            public void setUserDao(UserDao userDao){
                this.userDao = userDao;
            }
        
        
            @Override
            public void getUserList() {
                userDao.getUser();
            }
        }
        
        

        测试(用户端)

        import com.xiaoli.Service.UserService;
        import com.xiaoli.Service.UserServiceImpl;
        import com.xiaoli.dao.UserMysqlDaoImpl;
        import com.xiaoli.dao.UserOracleDaoImpl;
        import org.junit.Test;
        
        //传统的方式实现
        public class TestTradition {
        
            @Test
            public void test1(){
        
                UserService userService = new UserServiceImpl();
        
                //动态的修改需要的dao方法
                ((UserServiceImpl)userService).setUserDao(new UserMysqlDaoImpl());//动态变化
                //service层调用dao层
                userService.getUserList();
        
                //动态的修改需要的dao方法
                ((UserServiceImpl)userService).setUserDao(new UserOracleDaoImpl());//动态变化
                userService.getUserList();
        
            }
        }
        
        

        测试结果:

        image-20210415202557676

        我们是否已经发现了程序出现了什么变化?对,我们将程序修改的主动权交予了用户,使得程序被动的接受用户的改变,程序也会发生相应的改变。

        这种实现,从本质上解决了问题,我们程序员不在去管理对象的创建,更多的关注业务代码的实现,使得程序的耦合性更低,大大提高了程序的灵活性,这就是IOC的原型!!!

2.2 IOC的本质

控制反转IOC(Inversion of Control)是一种设计思想,DI(依赖注入)是实现IOC的一种方法。也有人认为DI只是IOC的另一种说法。在没有IOC的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系完全编码在程序中,对象的创建由程序自己控制,控制反转后将对象创建的主动权转移到了第三方。

所以,我们可以暂且的认为,IOC就是:获得依赖对象的方式反转了!!!

c

  • IOC是Spring框架的核心内容,使用多种方式完美的实现了IOC,可以使用XML配置文件,也可以使用注解的方式实现。在Spring最新的版中可以使用零配置实现IOC。
  • Spring容器在初始化时先读取配置文件,根据配置文件或者元数据创建于组织对象存入容器中,程序使用时再从容器中取出需要的对象。

image-20210415204721150

在采用XML文件方式配置Bean时,Bean的定义信息和实现是分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实现类中,从而达到了零配置的目的。

2.3 第一个IOC程序

我们为IOC做了这么多的铺垫,接下来我们就要实现我们的第一个IOC的程序,在这之前,我们采用XML配置文件的方式进行创建。

  1. 创建一个普通的Maven项目,编写一个实体类为Hello.java

    image-20210415211220496

    Hello实体类

    package com.xiaoli.pojo;
    
    //实体类(演示IOC)
    public class Hello {
    
        private String desc;
    
        public Hello() {
        }
    
        public Hello(String desc) {
            this.desc = desc;
        }
    
        public String getDesc() {
            return desc;
        }
    
        public void setDesc(String desc) {
            this.desc = desc;
        }
    
        @Override
        public String toString() {
            return "Hello{" +
                    "desc='" + desc + '\'' +
                    '}';
        }
    }
    
    
  2. 进入官网,拷贝下我们的配置文件,我们为其命名为applicationContext.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/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    </beans>
    
  3. 我们在配置文件中将我们的实体类Hello放入到Spring Container(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
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--
            传统创建对象的方式:
                1.类型 类型名 = new 类型()         Hello hello = new Hello()
                2.为对象设置属性: hello.setDesc("Hellow Spring")
            
            IOC创建对象的方式:目前我们介绍第一种
                1.bean 代表java对象,由spring进行管理,在这里代表Hello类对象
                2.id:相当于类型名,可以自定义
                3.class:需要创建的类的全限定名(全路径名)
            IOC设置属性的方法:目前只讲解第一种,后面的几种方法后面介绍
                1.property:表示给属性设置值的元素(标签)
                2.name:给属性设置值的方法,对于的是setName方法,如果类中没有该方法,就会报错
                3.value:设置属性
        -->
        <bean id="hello" class="com.xiaoli.pojo.Hello">
            <property name="desc" value="Hello Spring"/>
        </bean> 
    </beans>
    
  4. 测试:

    import com.xiaoli.pojo.Hello;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    //测试
    public class test {
    
        @Test
        public void testIOC(){
    
            //通过配置文件获取应用上下文
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            //从上下文中获取对象(通过bean的id)
            Hello hello = (Hello) context.getBean("hello");
    
            System.out.println(hello);
        }
    
    }
    
    
    1. 测试结果:

      image-20210415211343180

3.DI(依赖注入)

3.1 Spring创建对象方式

3.1.1 通过无参构造方法来创建

  • User实体类

    public class User {
    
        private int id;
        private String name;
    
        //无参构造函数
        public User() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • applicationContext.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/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
    <!--无参数构造方法注入-->
        <bean id="user" class="User">
            <property name="id" value="10"/>
            <property name="name" value="xiaoli"/>
        </bean>
    
    </beans>
    
  • 测试类

    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestDI {
        
        @Test
        public void testDD(){
            //无参构造函数注入
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            User user = context.getBean("user", User.class);
    
            System.out.println(user);
        }
    }
    

3.1.2 通过有参构造方法进行注入

  • User实体类

    public class User {
    
        private int id;
        private String name;
    
        public User() {
        }
    
        //有参构造函数
        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
    
  • applicationContext.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/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--
            有参构造函数注入
            1.通过参数下标进行注入
            2.通过参数类型进行注入(有局限性性,当多个参数类型相同时)
            3.通过参数名进行注入(推荐)
        -->
        <bean id="user1" class="User">
            <constructor-arg index="0" value="100"/>
            <constructor-arg index="1" value="xiaowang"/>
        </bean>
        <bean id="user2" class="User">
            <constructor-arg type="int" value="1000"/>
            <constructor-arg type="java.lang.String" value="xiaohuai"/>
        </bean>
        <bean id="user3" class="User">
            <constructor-arg name="id" value="10000"/>
            <constructor-arg name="name" value="xiaozhang"/>
        </bean>
    
    </beans>
    
  • 测试类

    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestDI {
    
        @Test
        public void testDI02(){
            //有参构造函数注入
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            User user1 = context.getBean("user1", User.class);
            User user2 = context.getBean("user2", User.class);
            User user3 = context.getBean("user3", User.class);
    
            System.out.println(user1);
            System.out.println(user2);
            System.out.println(user3);
        }
    }
    
    

3.2 set方法注入

  1. set方法进行注入时,必须要有set方法,set方法的方法名由 set + 属性名首字母大写组成;如果属性时boolean类型,没有set方法,是is。
  2. 普通的set注入方法(注入的值是普通的数据类型)
  • User实体类

    public class User {
    
        private int id;
        private String name;
    
        public User() {
        }
    
        //有参构造函数
        public User(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    
  • applicationContext.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/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--set方法注入-->
        <bean id="user4" class="User">
            <property name="id" value="50"/>
            <property name="name" value="xiaoyu"/>
        </bean>
    
    </beans>
    
  • 测试

    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestDI {
        @Test
        public void testDI03(){
            //普通的set方法注入
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    
            User user4 = context.getBean("user4", User.class);
    
            System.out.println(user4);
        }
    }
    

3.复杂的数据类型的set注入

我们接下来会演示注入list集合,set于map容器一样的数据类型

  • 实体类

    Teacher类

    //实体类teacher
    public class Teacher {
        private int id;
        private String name;
    
        public Teacher() {
        }
    
        public Teacher(int id, String name) {
            this.id = id;
            this.name = name;
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        @Override
        public String toString() {
            return "Teacher{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    '}';
        }
    }
    

    Student类

    import java.util.*;
    
    //实体类Student
    public class Student {
    
        private int id;//Id
        private String name;//姓名
        private Teacher teacher;//老师
        private String[] classes;//课程
        private Set<String> favor;//爱好
        private Map<String,Object> card;//证件
        private List<String> friends;//朋友
        private String work;//工作
        private Properties properties;//文件
    
        public Student() {
        }
    
        public int getId() {
            return id;
        }
    
        public void setId(int id) {
            this.id = id;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public Teacher getTeacher() {
            return teacher;
        }
    
        public void setTeacher(Teacher teacher) {
            this.teacher = teacher;
        }
    
        public String[] getClasses() {
            return classes;
        }
    
        public void setClasses(String[] classes) {
            this.classes = classes;
        }
    
        public Set<String> getFavor() {
            return favor;
        }
    
        public void setFavor(Set<String> favor) {
            this.favor = favor;
        }
    
        public Map<String, Object> getCard() {
            return card;
        }
    
        public void setCard(Map<String, Object> card) {
            this.card = card;
        }
    
        public List<String> getFriends() {
            return friends;
        }
    
        public void setFriends(List<String> friends) {
            this.friends = friends;
        }
    
        public String getWork() {
            return work;
        }
    
        public void setWork(String work) {
            this.work = work;
        }
    
        public Properties getProperties() {
            return properties;
        }
    
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "id=" + id +
                    ", name='" + name + '\'' +
                    ", teacher=" + teacher +
                    ", classes=" + Arrays.toString(classes) +
                    ", favor=" + favor +
                    ", card=" + card +
                    ", friends=" + friends +
                    ", work='" + work + '\'' +
                    ", properties=" + properties +
                    '}';
        }
    }
    
  • applicationContext.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/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
      
        <!--
            复杂set方法注入
         -->
        <bean id="teacher" class="Teacher">
            <property name="id" value="10"/>
            <property name="name" value="王老师"/>
        </bean>
        <bean id="student" class="Student">
            <!--常量注入-->
            <property name="id" value="1"/>
            <property name="name" value="小李"/>
            <!--ref注入-->
            <property name="teacher" ref="teacher"/>
            <!--数组注入-->
            <property name="classes">
                <array>
                    <value>英语</value>
                    <value>数学</value>
                    <value>语文</value>
                </array>
            </property>
            <!--set注入-->
            <property name="favor">
                <set>
                    <value>羽毛球</value>
                    <value>游泳</value>
                    <value>滑雪</value>
                </set>
            </property>
            <!--Map注入-->
            <property name="card">
                <map>
                    <entry key="手机号" value="17828732"/>
                    <entry key="学生号" value="2018232483237"/>
                </map>
            </property>
            <!--list注入-->
            <property name="friends">
                <list>
                    <value>小王</value>
                    <value>小花</value>
                    <value>小刘</value>
                </list>
            </property>
            <!--null值注入-->
            <property name="work">
                <null></null>
            </property>
            <!--Properties注入-->
            <property name="properties">
                <props>
                    <prop key="username">root</prop>
                    <prop key="password">12345</prop>
                </props>
            </property>
        </bean>
    </beans>
    
  • 测试

    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class TestDI {
    
        @Test
        public void testDI04(){
            //复杂set方法注入
            ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    
            Student student = context.getBean("student", Student.class);
    
            System.out.println(student);
    
            /*
            Student{
                id=1, name='小李',
                teacher=Teacher{id=10, name='王老师'},
                classes=[英语, 数学, 语文],
                favor=[羽毛球, 游泳, 滑雪],
                card={手机号=17828732, 学生号=2018232483237},
                friends=[小王, 小花, 小刘],
                work='null',
                properties={password=12345, username=root}
            }
    
             */
        }
    }
    
    

3.3 拓展方法注入

3.3.1 c命名空间注入

在使用c命名空间注入时,需要在xml文件的头文件中导入约束

xmlns:c="http://www.springframework.org/schema/c"

使用c命名空间注入属性:

<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--c命名空间注入-->
    <bean id="userNew" class="User" c:id="10" c:name="小吴"/>

</beans>

3.3.2 p命名空间注入

在使用p命名空间注入时,需要在xml文件的头文件中导入约束

xmlns:p="http://www.springframework.org/schema/p"

使用c命名空间注入属性:

<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--p命名空间注入-->
    <bean id="userNew1" class="User" p:id="20" name="小武"/>

</beans>

4.Bean对象的自动装配

  • 自动装配是使用Spring满足bean依赖的一种方式
  • Spring会自动的在上下文中对某一个bean进行寻找其依赖的bean对象

4.1 Spring的bean的装配机制

  1. 在xml中显示的进行配置
  2. 在java中显示的配置
  3. 隐式的bean发现机制和自动装配

4.2 在XML中进行显示装配

  • Byname与ByType

Teacher类

package com.xiaoli.dao;

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

    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = 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 "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

Student类

package com.xiaoli.dao;

public class Student {
    private String name;
    private int age;
    private Teacher teacher;

    public Student() {
    }

    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }

    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 Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

beans.xml配置文件

  • ByName
<?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:c="http://www.springframework.org/schema/c"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--xml进行显示的装配-->
    <bean id="teacher" class="com.xiaoli.dao.Teacher">
        <property name="name" value="王老师"/>
        <property name="age" value="20"/>
    </bean>

    <!--自动装配:byName-->
    <bean id="student" class="com.xiaoli.dao.Student" autowire="byName">
        <property name="age" value="10"/>
        <property name="name" value="小张"/>
    </bean>
</beans>
  • ByType

    <?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:c="http://www.springframework.org/schema/c"
           xmlns:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd">
    
        <!--xml进行显示的装配-->
        <bean id="teacher" class="com.xiaoli.dao.Teacher">
            <property name="name" value="王老师"/>
            <property name="age" value="20"/>
        </bean>
    
        <!--自动装配:byType-->
        <bean id="student" class="com.xiaoli.dao.Student" autowire="byType">
            <property name="age" value="10"/>
            <property name="name" value="小张"/>
        </bean>
    </beans>
    

测试:

import com.xiaoli.dao.Student;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class testAutowire {

    @Test
    public void testAutowire(){

        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        Student student = context.getBean("student", Student.class);

        System.out.println(student);
    }
}

测试结果

image-20210417175706029

使用xml进行自动装配时,使用byName或者byType,可能会有多个类型或者名字一样的bean,就会出现装配失败的情况!!!

4.3 使用注解进行自动装配

Jdk1.5开始支持注解,Spring2.5开始全面支持注解。

备注:

@Autowired(required = false):表示对象可以为null;

@Autowired(required = true):表示对象可以不能为null;

  1. 准备工作,在Spring配置文件中引入context文件头

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd"> 
    </beans>
    
  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"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--开启注解支持-->
        <context:annotation-config/>
        <!--开启组件扫描机制-->
        <context:component-scan base-package="com.xiaoli.dao"/>
    
    </beans>
    
  3. 使用@Autowired注解,需要导入spring-aop依赖

    <dependency>
      <groupId>spring</groupId>
      <artifactId>spring-aop</artifactId>
      <version>1.0.2</version>
    </dependency>
    
  4. @Autowired注解时按类型自动转型的,不支持id匹配

    • 将bean注册到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"
             xmlns:context="http://www.springframework.org/schema/context"
             xsi:schemaLocation="http://www.springframework.org/schema/beans
              https://www.springframework.org/schema/beans/spring-beans.xsd
              http://www.springframework.org/schema/context
              https://www.springframework.org/schema/context/spring-context.xsd">
      
          <!--开启注解支持-->
          <context:annotation-config/>
          <!--开启组件扫描机制-->
          <context:component-scan base-package="com.xiaoli.dao"/>
      
          <!--注册bean-->
          <bean id="teacher" class="com.xiaoli.dao.Teacher">
              <property name="name" value="万老师"/>
              <property name="age" value="30"/>
          </bean>
          <bean id="student" class="com.xiaoli.dao.Student"/>
      </beans>
      
    • Student类

      package com.xiaoli.dao;
      
      
      import org.springframework.beans.factory.annotation.Autowired;
      
      public class Student {
          
          private String name;
          private int age;
          //在外部类的属性或者set方法上加上自动装配注解
          @Autowired
          private Teacher teacher;
      
          public Student() {
          }
      
          public Student(String name, int age, Teacher teacher) {
              this.name = name;
              this.age = age;
              this.teacher = teacher;
          }
      
          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 Teacher getTeacher() {
              return teacher;
          }
      
          public void setTeacher(Teacher teacher) {
              this.teacher = teacher;
          }
      
          @Override
          public String toString() {
              return "Student{" +
                      "name='" + name + '\'' +
                      ", age=" + age +
                      ", teacher=" + teacher +
                      '}';
          }
      }
      
    • 测试结果

      image-20210417181339785

  5. @Autowired

    在之前我们使用@Autowired自动装配注解,接下来我们使用与@Autowired搭配是的注解@Autowired,可以指定需要注入的值。

    <?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
            https://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            https://www.springframework.org/schema/context/spring-context.xsd">
    
        <!--开启注解支持-->
        <context:annotation-config/>
        <!--开启组件扫描机制-->
        <context:component-scan base-package="com.xiaoli.dao"/>
    
        <!--注册bean-->
        <bean id="teacher1" class="com.xiaoli.dao.Teacher">
            <property name="age" value="40"/>
            <property name="name" value="吴老师"/>
        </bean>
        <bean id="teacher2" class="com.xiaoli.dao.Teacher">
            <property name="age" value="50"/>
            <property name="name" value="张老师"/>
        </bean>
        <bean id="student" class="com.xiaoli.dao.Student"/>
    </beans>
    

    Student类

    package com.xiaoli.dao;
    
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    
    public class Student {
    
        private String name;
        private int age;
        //在外部类的属性或者set方法上加上自动装配注解
        @Autowired
        @Qualifier(value = "teacher2")
        private Teacher teacher;
    
        public Student() {
        }
    
        public Student(String name, int age, Teacher teacher) {
            this.name = name;
            this.age = age;
            this.teacher = teacher;
        }
    
        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 Teacher getTeacher() {
            return teacher;
        }
    
        public void setTeacher(Teacher teacher) {
            this.teacher = teacher;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "name='" + name + '\'' +
                    ", age=" + age +
                    ", teacher=" + teacher +
                    '}';
        }
    }
    

    测试结果

    image-20210417182454370

5.使用注解进行开发

5.1 准备工作

在Spring4之后,如果想要使用注解进行开发,需要导入aop的依赖包

 <dependency>
            <groupId>spring</groupId>
            <artifactId>spring-aop</artifactId>
            <version>1.0.2</version>
        </dependency>

在导入包之后,还需要在spring配置文件中引入一个context约束条件

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

    <!--开启注解支持-->
    <context:annotation-config/>
</beans>

5.2 Bean的实现

在之前,我们都是通过xml文件进行bean的注入,但是在实际的开发中,我们都是会选择注解进行开发。

使用注解进行开发的步骤:

  1. 配置需要扫描的包下的注解
<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启注解支持-->
    <context:annotation-config/>
    <!--开启组件扫描机制,扫描需要配置注解的包-->
    <context:component-scan base-package="com.xiaoli.dao"/>

</beans>

2.在指定扫描包下进行配置注解

Student类

package com.xiaoli.dao;


import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/*
    Component组件在Spring容器中注册Bean
    相当于:<bean id="stduent" class="com.xiaoli.dao.Student"/>
    在不写value值时,默认为该类的全小写
 */

@Component("student")
public class Student {

    /*
        Value注解可以添加到属性上,也可以添加到set方法上
        等价于:<property name="name" value="小李"/>
        对属性进行注入
     */

    @Value("小李")
    private String name;
    @Value("10")
    private int age;

    //这里对注解使用@Value会报错,使用自动装配,填入teacher类对bean的id
    @Autowired
    @Qualifier(value = "teacher")
    private Teacher teacher;

    public Student() {
    }

    public Student(String name, int age, Teacher teacher) {
        this.name = name;
        this.age = age;
        this.teacher = teacher;
    }

    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 Teacher getTeacher() {
        return teacher;
    }

    public void setTeacher(Teacher teacher) {
        this.teacher = teacher;
    }

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

Teacher类

package com.xiaoli.dao;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component(value = "teacher")
public class Teacher {

    @Value("王老师")
    private String name;
    @Value("39")
    private int age;

    public Teacher() {
    }

    public Teacher(String name, int age) {
        this.name = name;
        this.age = 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 "Teacher{" +
                "name='" + name + '\'' +
                ", age=" + age +
                '}';
    }
}

测试:

image-20210417195001587

5.3 衍生注解

我们是使用注解,就是替代了我们在Spring容器(xml配置文件)中的配置步骤而已!只是使用注解方式进行开发会使得开发更加的快捷键与方便!!!我们接下来会介绍几个在开发中常用的注解:

@Component注解的三个衍生注解(@Component注解用于pojo层

为了更好的进行开发分层,Spring可以使用其他三个注解,这三个注解与@Component注解是一样的功能,只是为了更好的进行分层,我们引入其他三个注解!!!

  • @Controller注解:用于Web层
  • @Service注解:用于Service层
  • Repository注解:用于dao层

在开发中使用以上注解,相当于将这些类交予Spring容器进行统一管理!!!

5.4 作用域@Scope

在外面将Bean对象托管到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
        https://www.springframework.org/schema/context/spring-context.xsd">

    <!--
        scope:默认是singleton(单例,相当于在内存中只有一份这个对象)
        scope作用域的其他属性:
            prototype:当一个bean当作用域被声明为prototype(原型模式)时候,表示一个bean对应多个对象实例。
            request:当一个bean被声明为request时,表示在一次HTTP请求中,一个ben定义对应一个实例;即每个HTTP
                    请求都会有各自都bean实例。该作用域仅在基于web都Spring ApplicationContext情形下有效。
            session:当一个作用域为Session,表示在一个HTTP Session中,一个bean定义一个实例。该作用域仅在基于Web
            当Spring ApplicationContext下有效。
            
    -->
    <bean id="user" class="com.xiaoli.dao.User" scope="singleton">

    </bean>
</beans>

5.5 注解开发与基于xml开发小结

  1. XML与注解比较

    • xml可以适用于任何场景,结构清晰,维护方便

    • 注解不是自己提供的类的情况下会出现无法使用的情况,但是开发简单,方便

  2. XML与注解整合开发:推荐最佳实战

    • xml管理bean

    • 注解完成属性注入

    • 使用过程中,可以不使用扫描,扫描是为了类上的注解

      <!--开启注解支持-->
          <context:annotation-config/>
      
  3. 作用:

    • 使用注解驱动注册,从而使得注解生效
    • 用于激活那些已经在spring容器中注册过的bean上面的注解,也就是显示的向Spring进行注册
    • 如果不扫描包,则需要手动配置bean(将bean对象托管到Spring容器中)
    • 如果不加注解驱动,则注入到值为null

6.基于Java类进行配置

在完全摒弃Spring xml配置文件下,如何进行开发呢?

我们使用JavaConfig,JavaConfig原来是Spring到一个子项目,他通过Java类的方式提供bean的定义信息,在Spring4的版本中,JavaConfig已经正式成为Spring4的核心功能!!!

6.1 存粹基于Java的代码

  1. 编写一个实体类(如Book)

    Book实体类

    package com.xiaoli.dao;
    
    import org.springframework.stereotype.Component;
    
    @Component(value = "book")
    public class Book {
        private String name;
        private String author;
        private float price;
    
        public Book() {
        }
    
        public Book(String name, String author, float price) {
            this.name = name;
            this.author = author;
            this.price = price;
        }
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getAuthor() {
            return author;
        }
    
        public void setAuthor(String author) {
            this.author = author;
        }
    
        public float getPrice() {
            return price;
        }
    
        public void setPrice(float price) {
            this.price = price;
        }
    
        @Override
        public String toString() {
            return "Book{" +
                    "name='" + name + '\'' +
                    ", author='" + author + '\'' +
                    ", price=" + price +
                    '}';
        }
    }
    
  2. 新建一个config类,编写我们需要配置bean的配置类

    BookConfig配置类

    package com.xiaoli.dao;
    
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    
    //@Configuration注解代表这是一个配置类
    @Configuration
    public class BookConfig {
    
        //通过方法注册一个bean,这里的返回值就是bean的类型,方法名就是bean的id,方法名必须与id一致,否则会报错
        @Bean
        public Book book(){
            return new Book("西游记","吴承恩", (float) 80.8);
        }
    
    }
    
  3. 测试代码

    import com.xiaoli.dao.Book;
    import com.xiaoli.dao.BookConfig;
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    public class TestAnnotationConfig {
    
        //测试存粹的注解开发,不使用xml配置文件
        @Test
        public void test(){
            //我们这里需要的Spring上下文是来自与注解配置类的实例
            ApplicationContext context = new AnnotationConfigApplicationContext(BookConfig.class);
    
            Book book = context.getBean("book", Book.class);
    
            System.out.println(book);
    
        }
    }
    

6.2 使用场合

我们通过上面的代码,已经发现了基于存粹的Java代码编写Bean和注入bean成为可能,我们在之后学习Spring Boot与Spring Cloud框架的学习中,将会看到大量的这样的基于存粹的注解开发的代码!!!

posted on 2021-04-17 21:14  Code_xiaoli  阅读(64)  评论(0)    收藏  举报



Live2D