• 三天掌握Spring系列第一讲 IOC 解决耦合就是这么简单


    1、Spring概述

    首先要了解到一个小知识,Spring和Spring Framework的关系。没错,他们是包含的关系。我们常说Spring框架就是Spring Framework框架。其实,Spring项目是一个集合,Spring Framework是其中的一个子项目,其他的还有Spring Boot、Spring Cloud等。只不过我们把Spring Framework简称为Spring了。自己理解即可!

    Spring 与其他方便集成,自身Spring 提供一些集成的组件,实际就是按照其他厂商提供的接口(使用文档)进行了封装,方便使用Spring框架的开发人员。给Spring点赞!

    1.1、Spring是什么

    Spring是一个轻量级控制反转(IoC)和面向切面(AOP)的容器框架。(详细请参考官方文档或者百度百科)

    1.2、Spring体系结构

    当你学习一个新东西的时候,首先要了解它里面包含什么,哪些是在项目中可能被使用到的。当然,很多公司选择它,它里面肯定会有一些被大家认可和常常使用的组件。

    image-20200810100338057

    1. 数据访问/集成

    2. Web(Spring MVC)

    3. 核心

    4. 测试

    5. 其他组件

    1.3、Spring的俩大核心

    • 依赖注入(DI):

    Spring 最认同的技术是控制反转的依赖注入(DI)模式。控制反转(IoC)是一个通用的概念,它可以用许多不同的方式去表达,依赖注入仅仅是控制反转的一个具体的例子。 当编写一个复杂的 Java 应用程序时,应用程序类应该尽可能的独立于其他的 Java 类来增加这些类可重用可能性,当进行单元测试时,可以使它们独立于其他类进行测试。依赖注入(或者有时被称为配线)有助于将这些类粘合在一起,并且在同一时间让它们保持独立。 到底什么是依赖注入?让我们将这两个词分开来看一看。这里将依赖关系部分转化为两个类之间的关联。例如,类 A 依赖于类 B。现在,让我们看一看第二部分,注入。所有这一切都意味着类 B 将通过 IoC 被注入到类 A 中。 依赖注入可以以向构造函数传递参数的方式发生,或者通过使用 setter 方法 post-construction。由于依赖注入是 Spring 框架的核心部分,所以我将在一个单独的章节中利用很好的例子去解释这一概念。

    • 面向切面的程序设计(AOP):

    Spring 框架的一个关键组件是面向方面的程序设计(AOP)框架。一个程序中跨越多个点的功能被称为横切关注点,这些横切关注点在概念上独立于应用程序的业务逻辑。有各种各样常见的很好的关于方面的例子,比如日志记录、声明性事务、安全性,和缓存等等。 在 OOP 中模块化的关键单元是类,而在 AOP 中模块化的关键单元是方面。AOP 帮助你将横切关注点从它们所影响的对象中分离出来,然而依赖注入帮助你将你的应用程序对象从彼此中分离出来。 Spring 框架的 AOP 模块提 供了面向方面的程序设计实现,可以定义诸如方法拦截器和切入点等,从而使实现功能的代码彻底的解耦出来。使用源码级的元数据,可以用类似于.Net属性的方式合并行为信息到代码中。我将在一个独立的章节中讨论更多关于 Spring AOP 的概念。

    1.4、Spring的好处

    • 方便解耦,简化开发

      通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。

    • AOP编程的支持

      通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以通过 AOP 轻松应付。

    • 声明式事务的支持

      可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。

    • 方便程序的测试

      可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。

    • 方便集成各种优秀框架

      Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。

    2、IOC容器

    2.1、为什么需要IOC

    我们项目中的数据基本都使用到数据库,那么连接数据库是非常重要的,例如:

     public class JdbcDemo {
      public static void main(String[] args) throws Exception{
      //1.注册驱动
      //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
      Class.forName("com.mysql.jdbc.Driver");
      //2.获取连接
      //3.获取预处理 sql 语句对象
      //4.获取结果集
      //5.遍历结果集
      }
     }

    我们的类依赖了数据库的具体驱动类(MySQL),如果这时候更换了数据库品牌(比如 Oracle),需要修改源码来重新数据库驱动。这显然不是我们想要的。


    当然你可以说,我写个工具类不就好了!卧槽??

    同时,也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。

    解决这个问题也很简单,使用配置文件配置。


    这个例子是用来介绍耦合的,IOC在一定程度上解决了,代码中极大多数出现的异常为空指针,类型异常等。

    2.2、基于 XML 的配置(入门案例)[掌握]

    项目准备

    image-20200810175619270

     public interface UserDao {
         User saveUser(User user);
     }
     public class UserDaoImpl implements UserDao {
         @Override
         public User saveUser(User user) {
             return user;
        }
     }
     public interface UserService {
         User saveUser(User user);
     }
     public class UserServiceImpl implements UserService {
         private UserDao userDao=new UserDaoImpl();
     
         @Override
         public User saveUser(User user) {
             return userDao.saveUser(user);
        }
     }
     @Data
     public class User {
         private Integer id;
         private String name;
         private int age;
     }
     

    1、在Maven工程的resource下创建bean.xml

    给配置文件导入约束:

     <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">
      <!-- more bean definitions go here -->
     </beans>

    image-20200810111429581

    2、让spring管理资源,在配置文件中配置 service 和 dao

     <!-- bean 标签:用于配置让 spring 创建对象,并且存入 ioc 容器之中 
      id 属性:对象的唯一标识。
      class 属性:指定要创建对象的全限定类名
     -->
     <!-- 配置 service -->    
     <bean id="userService" class="cn.fage.spring.impl.UserServiceImpl"></bean>
     <!-- 配置 dao -->
     <bean id="userDao" class="cn.fage.spring.impl.UserDaoImpl"></bean>

    内部执行了一个new 对象(); 的一个操作

    3、单元测试

     public class TestSpring {
         ApplicationContext context;
     
         @Before
         public void before() {
             context = new ClassPathXmlApplicationContext("spring_xml/bean.xml");
        }
     
         @Test
         public void test01() {
             System.out.println(context);
             System.out.println(context.getBean("userDao"));
             System.out.println(context.getBean("userService"));
        }
     }
     结果:
     org.springframework.context.support.ClassPathXmlApplicationContext@351d0846, started on Mon Aug 10 14:56:06 CST 2020
     cn.fage.spring.impl.UserDaoImpl@4b8ee4de
     cn.fage.spring.impl.UserServiceImpl@27f981c6    

    2.3、ApplicationContext继承关系

    1、 Spring 中 工厂的类结构图

    image-20200810152018447

     

    image-202008101520364152、 BeanFactory和 ApplicationContext 的区别

    BeanFactory 才是 Spring 容器中的顶层接口。

    ApplicationContext 是它的子接口。

    BeanFactory 和 ApplicationContext 的区别:

    • 创建对象的时间点不一样。

      • ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。

      • BeanFactory:什么使用什么时候创建对象。

    3、 ApplicationContext 接口的实现类

    • ClassPathXmlApplicationContext: 它是从类的根路径下加载配置文件 推荐使用这种

    • FileSystemXmlApplicationContext: 它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。

    • AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

    2.4、Bean

    1、bean标签

    作用:

    • 用于配置对象让 spring 来创建的。

    • 默认情况下它调用的是类中的无参构造函数。如果没有无参构造函数则不能创建成功。

    属性:

    • id: 给对象在容器中提供一个唯一标识。用于获取对象。

    • class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。

    • scope:指定对象的作用范围。

      • singleton :默认值,单例的。

      • prototype :多例的。

      • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中。

      • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中。

      • global session :WEB 项目中,应用在 Portlet 环境。如果没有 Portlet 环境那么 globalSession 相当于 session。

    2、bean 的作用范围和生命周期

    单例对象:scope="singleton"

    一个应用只有一个对象的实例。它的作用范围就是整个引用。

    生命周期:

    • 对象出生:当应用加载,创建容器时,对象就被创建了。

    • 对象活着:只要容器在,对象一直活着。

    • 对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

    多例对象:scope="prototype"

    每次访问对象时,都会重新创建对象实例。

    生命周期:

    • 对象出生:当使用对象时,创建新的对象实例。

    • 对象活着:只要对象在使用中,就一直活着。

    • 对象死亡:当对象长时间不用时,被 Java 的垃圾回收器回收了。

    3、实例化 Bean 的三种方式

    1、使用默认无参构造函数

      <!--在默认情况下: 
       它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。  
      -->
     <bean id="userService" class="cn.fage.spring.impl.UserServiceImpl"/>

    2、Spring管理静态工厂-使用静态工厂的方法创建对象

     /**
      * @author lin
      * @version 1.0
      * @date 2020-08-10 15:30
      * @Description //模拟一个静态工厂,创建业务层实现类
      */
     public class StaticFactory {
         public static UserService createUserService() {
             return new UserServiceImpl();
        }
     }
     
     static-factory.xml:
     
         <!-- 此种方式是:
           使用 StaticFactory 类中的静态方法 createUserService 创建对象,并存入 spring 容器
           id 属性:指定 bean id,用于从容器中获取
           class 属性:指定静态工厂的全限定类名
           factory-method 属性:指定生产对象的 静态方法
         -->
         <!-- 配置 service -->
         <bean id="userService" class="cn.fage.factory.StaticFactory" factory-       method="createUserService"/>

    3、Spring管理实例工厂-使用实例工厂的方法创建对象

     public class InstanceFactory {
         /**
          * 模拟一个实例工厂,创建业务层实现类
          * 此工厂创建对象,必须现有工厂实例对象,再调用方法
          */
         public UserService createUserService() {
             return new UserServiceImpl();
        }
     }
     
         <!-- 此种方式是:
              先把工厂的创建交给 spring 来管理。
              然后在使用工厂的 bean 来调用里面的方法
              factory-bean 属性:用于指定实例工厂 bean id。
              factory-method 属性:用于指定实例工厂中创建对象的方法。
         -->
         <bean id="instanceFactory" class="cn.fage.factory.InstanceFactory"/>
         <bean id="userService2" factory-bean="instanceFactory"  factory-method="createUserService"/>

    2.5、Spring的依赖注入

    1、依赖注入的概念

    依赖注入:Dependency Injection。它是 spring 框架核心 ioc 的具体实现。

    我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。 ioc 解耦只是降低他们的依赖关系,但不会消除。例如:我们的业务层仍会调用持久层的方法。

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

    2、构造函数注入

    顾名思义,就是使用类中的构造函数,给成员变量赋值。注意,赋值的操作不是我们自己做的,而是通过配置的方式,让 spring 框架来为我们注入。具体代码如下:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Integer id;
        private String name;
        private int age;
        private Date birthday;
    }
    
        <!-- 使用构造函数的方式,给 service 中的属性传值
            要求:
                类中需要提供一个对应参数列表的构造函数。
            涉及的标签:
                constructor-arg
                    属性:
                        index:指定参数在构造函数参数列表的索引位置
                        type:指定参数在构造函数中的数据类型
                        name:指定参数在构造函数中的名称     用这个找给谁赋值
                        =======上面三个都是找给谁赋值,下面两个指的是赋什么值的==============
                        value:它能赋的值是基本数据类型和 String 类型
                        ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
        -->
        <bean id="user" class="cn.fage.pojo.User">
            <constructor-arg name="id" value="1"/>
            <constructor-arg name="name" value="张三"/>
            <constructor-arg name="age" value="18"/>
            <constructor-arg name="birthday" ref="now"/>
        </bean>
        <bean id="now" class="java.util.Date"/>

    3、Set方法注入

    顾名思义,就是在类中提供需要注入成员的 set 方法。具体代码如下:

        <!-- 通过配置文件给 bean 中的属性传值:使用 set 方法的方式
    	涉及的标签:
    		property
    			属性:
    				name:找的是类中 set 方法后面的部分
    				ref:给属性赋值是其他 bean 类型的
    				value:给属性赋值是基本数据类型和 string 类型的
    		实际开发中,此种方式用的较多。
    -->
        <bean id="user2" class="cn.fage.pojo.User">
            <property name="age" value="21"/>
            <property name="name" value="test"/>
            <property name="birthday" ref="now"/>
        </bean>
    

    4、使用 p 名称空间注入数据(本质还是调用 set 方法)

    此种方式是通过在 xml中导入 p名称空间,使用 p:propertyName 来注入数据,它的本质仍然是调用类中的 set 方法实现注入功能。

    --引入命名空间描述
    xmlns:p="http://www.springframework.org/schema/p" 
    
    	<!--  P 标签 注入  -->
        <bean id="user3"
              class="cn.fage.pojo.User"
              p:name="user3" p:age="22" p:birthday-ref="now"/>

    5 、注入集合属性

    顾名思义,就是给类中的集合成员传值,它用的也是set方法注入的方式,只不过变量的数据类型都是集合。 我们这里介绍注入数组,List,Set,Map,Properties。具体代码如下:

    @Data
    @AllArgsConstructor
    @NoArgsConstructor
    public class User {
        private Integer id;
        private String name;
        private int age;
        private Date birthday;
        private String[] phones;
        private Set<String> tels;
        private List<User> friends;
        private Map<String, Object> family;
        private Properties pros;
    
        public User(Integer id, String name, int age, Date birthday) {
            this.id = id;
            this.name = name;
            this.age = age;
            this.birthday = birthday;
        }
    }
    
      <!-- 注入集合数据
            List 结构的:
                array,list,set
            Map 结构的
                map,entry,props,prop
        -->
        <!--    集合注入    -->
        <bean class="cn.fage.pojo.User" id="user4">
            <!--    基础属性    -->
            <property name="id" value="1"/>
            <property name="age" value="18"/>
            <property name="name" value="user4"/>
            <property name="birthday" ref="now"/>
            <!-- 在注入集合数据时,只要结构相同,标签可以互换 -->
            <!-- 给数组注入数据 -->
            <property name="phones">
                <array>
                    <value>华为手机</value>
                    <value>苹果手机</value>
                </array>
            </property>
            <!-- 注入Set集合数据 -->
            <property name="tels">
                <set>
                    <value>134xxx</value>
                    <value>135xxx</value>
                    <value>136xxx</value>
                    <!--  测试重复数据 -->
                    <value>134xxx</value>
                </set>
            </property>
            <!-- 注入List集合数据 -->
            <property name="friends">
                <list value-type="cn.fage.pojo.User">
                    <ref bean="user"/>
                    <ref bean="user2"/>
                    <ref bean="user3"/>
                </list>
            </property>
            <!-- 注入Map数据 -->
            <property name="family">
                <map>
                    <entry key="老婆" value="陈乔恩"/>
                    <entry key="朋友" value="易烊千玺"/>
                </map>
            </property>
            <!-- 注入properties数据 -->
            <property name="pros">
                <props>
                    <prop key="testA">aaa</prop>
                    <prop key="testB">bbb</prop>
                </props>
            </property>
        </bean>

    结果:

    User(id=1, name=user4, age=18, birthday=Mon Aug 10 16:17:35 CST 2020, phones=[华为手机, 苹果手机], tels=[134xxx, 135xxx, 136xxx], friends=[User(id=1, name=user, age=18, birthday=Mon Aug 10 16:17:35 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null), User(id=null, name=user2, age=21, birthday=Mon Aug 10 16:17:35 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null), User(id=null, name=user3, age=22, birthday=Mon Aug 10 16:17:35 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null)], family={老婆=陈乔恩, 朋友=易烊千玺}, pros={testB=bbb, testA=aaa})

    3、给予注解的IOC配置

    1、明确:写在最前

    企业中开发常常使用的一种方式

    注解配置和 xml 配置要实现的功能都是一样的,都是要降低程序间的耦合。只是配置的形式不一样。

    关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。

    在学习注解配置时,采用上一章节的案例,把 spring 的 xml 配置内容改为使用注解逐步实现。

    2 、使用注解配置管理的资源

    @Repository(value = "userDao")
    public class UserDaoImpl implements UserDao {
        @Override
        public User saveUser(User user) {
            System.out.println("UserDaoImpl 执行 saveUser() 方法 ");
            return user;
        }
    }
    
    @Service(value="userService")
    public class UserServiceImpl implements UserService {
        @Autowired
        @Qualifier("userDao")
        private UserDao userDao;
    
        @Override
        public User saveUser(User user) {
            return userDao.saveUser(user);
        }
    }
    
    @Configuration
    public class SpringConfiguration {
    
        @Bean(value = "user")
        public User createUser1() {
            return new User(1, "user1", 18, new Date());
        }
    
    }

    3、修改bean.xml开启对注解的支持

    注意: 基于注解整合时,导入约束时需要多导入一个 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
            http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 告知spring在创建容器时要扫描注解的包 -->
        <context:component-scan base-package="cn.fage"/>
    
    </beans>

    4、常用注解

    4.1 、用于创建对象的

    相当于:<bean id="" class="">

    1、 @Component

    作用: 把资源让 spring 来管理。相当于在 xml 中配置一个 bean。

    属性: value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。

    2、 @Controller @Service @Repository

    他们三个注解都是针对@Component的衍生注解,他们的作用及属性都是一模一样的。

    他们只不过是提供了更加明确的语义化。

    • @Controller:一般用于表现层的注解。

    • @Service:一般用于业务层的注解。

    • @Repository:一般用于持久层的注解。

    细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value在赋值是可以不写。

    4.2 用于注入数据的

    相当于:<property name="" ref=""> <property name="" value="">

    1、 @Autowired

    作用:

    自动按照类型注入。当使用注解注入属性时,set方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器查找,找到了也可以注入成功。找不到就报错。

    2、 @Qualifier

    作用:

    在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。

    属性:

    value:指定 bean 的 id。

    3、 @Resource

    作用:

    直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。

    属性:

    name:指定 bean 的 id。

    4、 @Value

    作用:

    注入基本数据类型和 String 类型数据的

    属性:

    value:用于指定值

    4.3、用于改变作用范围的

    相当于:<bean id="" class=""scope=""/>

    1、 @Scope

    作用:

    指定 bean 的作用范围。

    属性:

    value: 指定范围的值

    取值:singleton prototype request session globalsession

    4.4、和生命周期相关的(了解)

    相当于:<bean id="" class=""init-method="" destroy-method=""/>

    1、 @PostConstruct

    作用: 用于指定初始化方法。

    2、 @PreDestroy

    作用: 用于指定销毁方法。

    4.5 、关于 Spring 注解 和 XML 的选择问题

    • 注解的优势

      • 配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。

    • XML的优势

      • 修改时,不用改源码。不涉及重新编译和部署。

    • Spring管理Bean方式的比较:

    image-20200810165843390

    4.5 、spring管理对象细节

    基于注解的 spring IoC 配置中,bean 对象的特点和基于 XML 配置是一模一样的。

    4.6、 spring的纯注解配置

    写到此处,基于注解的 IoC 配置已经完成,但是我们依然离不开 spring 的 xml 配置文件,那么能不能不写这个 bean.xml,所有配置都用注解来实现呢?

    4.7、配置类注解

    1、 @Configuration

    作用:

    用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用 AnnotationApplicationContext(有@Configuration 注解的类.class)。

    属性:

    value:用于指定配置类的字节码

    //spring的配置类,相当于bean.xml文件
    @Configuration
    public class SpringConfiguration {
    
    }

    2 、@ComponentScan

    如何配置创建容器时要扫描的包呢?

    作用:

    用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的:<context:component-scan base-package="cn.fage"/> 是一样的。

    属性:

    basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。

    //spring的配置类,相当于bean.xml文件
    @Configuration
    @ComponentScan("cn.fage")
    public class SpringConfiguration {
    
    }

    3、 @Bean

    作用:

    该注解只能写在方法上,用于把当前方法的返回值作为bean对象存入spring的ioc容器中。

    属性:

    name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。当不写时,默认值是当前方法的名称。

    细节:

    当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。 查找的方式和Autowired注解的作用是一样的。

    示例参考第三章第三节

    4、 @PropertySource

    读取配置文件

    作用:

    用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到 properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。

    属性:

    value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:

    示例代码:

    @Configuration
    @PropertySource(value = {"classpath:user/user.properties"})
    public class UserConfigs {
        @Value("${id}")
        int id;
        @Value("${name}")
        String name;
        @Value(("${age}"))
        int age;
    
        @Bean(value = "user2")
        public User createUser1() {
            return new User(id, name, age);
        }
    
    }
    
    id=2
    name=user2
    age=18
    

    5、 @Import

    作用:

    用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问题。如果ComponentScan扫描包里面包括其,那么则不需要引入。

    属性:

    value[]:用于指定其他配置类的字节码。

    示例代码:

    复制代码

    @Configuration
    @Import(value = {UserConfigs.class})
    @ComponentScan(value = "cn.fage")
    public class SpringConfiguration {}

    6 、通过注解获取容器

    /**
     * @author lin
     * @version 1.0
     * @date 2020-08-10 11:36
     * @Description TODO
     */
    public class TestSpringAnnotation {
        ApplicationContext context;
        @Before
        public void before() {
            // 使用 xml 里面的 <context:component-scan base-package="cn.fage"/>
            // context = new ClassPathXmlApplicationContext("spring-xml/annotation-bean.xml");
            context = new AnnotationConfigApplicationContext(SpringConfiguration.class);
        }
        @Test
        public void test01() {
            System.out.println(context);
            for (String name : context.getBeanDefinitionNames()) {
                System.out.println(name);
            }
            UserService userService = (UserService) context.getBean("userService");
            userService.saveUser(context.getBean("user", User.class));
        }
    }
    

    运行结果如下:

    org.springframework.context.annotation.AnnotationConfigApplicationContext@4d49af10, started on Mon Aug 10 17:28:07 CST 2020
    org.springframework.context.annotation.internalConfigurationAnnotationProcessor
    org.springframework.context.annotation.internalAutowiredAnnotationProcessor
    org.springframework.context.event.internalEventListenerProcessor
    org.springframework.context.event.internalEventListenerFactory
    springConfiguration
    userConfigs
    userDao
    userService
    user2
    user
    UserDaoImpl 执行 saveUser() 方法 
    
    进程已结束,退出代码0
    

    5、使用spring-test的启动器

    在代码中我们使用before方法

        ApplicationContext context;
    
        @Before
        public void before() {
            context = new ClassPathXmlApplicationContext("spring-xml/bean.xml");
        }
    

    这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

    1、解决思路分析

    针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就无须手动创建了,问题也就解决了。

    我们都知道,junit 单元测试的原理,但显然,junit 是无法实现的,因为它自己都无法知晓我们是否使用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们暴露了一个注解,可以让我们替换掉它的运行器。

    这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。

    当然你可以想,你如果启动的是tomcat后台服务器,再把接口暴露出来,那么是不是就想到Spring Boot了!!

    2、配置步骤

    第一步:使用@RunWith注解替换原有运行器

    第二步:使用@ContextConfiguration指定spring配置文件的位置

    第三步:使用@Autowired给测试类中的变量注入数据(就是使用了!!)

    /**
     * @author lin
     * @version 1.0
     * @date 2020-08-10 17:31
     * @Description TODO
     */
    // SpringJUnit4ClassRunner 和 SpringRunner 都可以
    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfiguration.class)
    public class TestRunner {
        @Autowired
        UserService userService;
    
        @Autowired
        User user;
    
        @Test
        public void test01() {
            User user = userService.saveUser(this.user);
            System.out.println(user);
        }
    
    }
    

    运行结果如下:

    UserDaoImpl 执行 saveUser() 方法 
    User(id=1, name=user1, age=18, birthday=Mon Aug 10 18:00:09 CST 2020, phones=null, tels=null, friends=null, family=null, pros=null)

    image-20200810180023927

    @ContextConfiguration 注解:

    • locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明 例如 locations=("classpath:bean.xml")

    • classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。

    3、为什么到 不把测试类配到 xml 中

    配到xml中肯定是可以使用的。

    那么为什么不采用配置到 xml 中的方式呢?

    这个原因是这样的:

    第一:当我们在 xml 中配置了一个 bean,spring 加载配置文件创建容器时,就会创建对象。

    第二:测试类只是我们在测试功能时使用,而在项目中它并不参与程序逻辑,也不会解决需求上的问题,所以创建完了,并没有使用。那么存在容器中就会造成资源的浪费。

    所以,基于以上两点,我们不应该把测试配置到 xml 文件中。

    QQ

    公众号发哥讲

    这是一个稍偏基础和偏技术的公众号,甚至其中包括一些可能阅读量很低的包含代码的技术文,不知道你是不是喜欢,期待你的关注。

    img

    如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

    ● 扫码关注我们

    据说看到好文章不推荐的人,服务器容易宕机!

    本文版权归 发哥讲博客园 共有,原创文章,未经允许不得转载,否则保留追究法律责任的权利。

  • 相关阅读:
    10分钟入门spark
    10分钟入门kubernetes(上)
    深入浅出Hadoop之mapreduce
    深入浅出Hadoop之HDFS
    闲聊cassandra
    深入浅出zookeeper
    Asp.net日期字符串格式化显示
    C#里面比较时间大小三种方法
    (ASP.net)利用Application对象制作简单聊天室
    Response.ContentType 详细列表
  • 原文地址:https://www.cnblogs.com/naimao/p/13471464.html
Copyright © 2020-2023  润新知