前言

前两天和一个同学吃饭的时候同学跟我说了一件事,说他公司有个做了两年的人向他提出要涨薪资,他就顺口问了一个问题关于spring依赖注入的,那个要求涨薪的同学居然被问懵了。。。事后回家想了想这一块确实有点难度的就写篇文章把我自己知道的和网上整理的分享给大家,至少大家在被问到这一块的时候能答上来,不会因为这个被卡涨薪。话不多说,满满的干货都在下面了!

1.什么是Spring的依赖注入?

依赖注入,是IOC的一个方面,是个通常的概念,它有多种解释。这概念是说你不用创建对象,而只需要描述它如何被创建。你不在代码里直接组装你的组件和服务,但是要在配置文件里描述哪些组件需要哪些服务,之后一个容器(IOC容器)负责把他们组装起来。

2. IOC的作用

降低程序间的耦合(依赖关系)
依赖关系的管理:
以后都交给spring来维护
在当前类需要用到其他类的对象,由spring为我们提供,我们只需要在配置文件中说明依赖关系的维护,就称之为依赖注入。

3.Spring依赖注入的几种方式

能注入的数据:有三类

基本类型和String。
其他bean类型(在配置文件中或者注解配置过的bean)。
复杂类型/集合类型。
注入的方式:有三种

使用构造函数提供。
使用set方法提供。
使用注解提供。

构造函数注入

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

<!--构造函数注入:
    使用的标签:constructor-arg
    标签出现的位置:bean标签的内部
    标签中的属性
        type:用于指定要注入的数据的数据类型,该数据类型也是构造函数中某个或某些参数的类型
        index:用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始
        name:用于指定给构造函数中指定名称的参数赋值
        ================以上三个用于指定给构造函数中哪个参数赋值===================
        value:用于提供基本类型和String类型的数据
        ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象

    优势:
        在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
    弊端:
        改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
    <constructor-arg name="name" value="泰斯特"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
    <constructor-arg name="birthday" ref="now"></constructor-arg>
</bean>

<!-- 配置一个日期对象 -->
<bean id="now" class="java.util.Date"></bean>

Set方式注入

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

<!-- set方法注入 --->   更常用的方式
    涉及的标签:property
    出现的位置:bean标签的内部
    标签的属性
        name:用于指定注入时所调用的set方法名称
        value:用于提供基本类型和String类型的数据
        ref:用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象
    优势:
        创建对象时没有明确的限制,可以直接使用默认构造函数
    弊端:
        如果有某个成员必须有值,则获取对象是有可能set方法没有执行。
-->
<bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
    <property name="name" value="tom" ></property>
    <property name="age" value="23"></property>
    <property name="birthday" ref="now"></property>
</bean>

集合方式注入

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

复杂类型的注入/集合类型的注入

用于给List结构集合注入的标签:
list,array,set
用于个Map结构集合注入的标签:
map,props

代码如下:

User类

public class User {
    private String name;
    private Integer age;
    private Date birth;

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

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

    public void setBirth(Date birth) {
        this.birth = birth;
    }

    public User(){
        System.out.println("我被创建了...");
    }
    public void show(){
        System.out.println("user中的show方法调用了。。。");
    }

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

Person类

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

    public Person() {
    }

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

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

CollectionDemo类

public class CollectionDemo {
    private String[] arr;
    private List<String> myList;
    private Set<String> mySet;
    private Map<String,String> myMap;
    private Properties myProp;

    public void setArr(String[] arr) {
        this.arr = arr;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void setMyProp(Properties myProp) {
        this.myProp = myProp;
    }

    public String[] getArr() {
        return arr;
    }

    public List<String> getMyList() {
        return myList;
    }

    public Set<String> getMySet() {
        return mySet;
    }

    public Map<String, String> getMyMap() {
        return myMap;
    }

    public Properties getMyProp() {
        return myProp;
    }
}

配置文件:

<!-- 基于xml形式装配bean -->
<bean id="user" class="com.atguigu.java1.User"></bean>

<!--使用get方法创建bean-->
<bean id="user2" class="com.atguigu.java1.User">
    <property name="name" value="张"></property>
    <property name="age">
        <value>20</value>
    </property>
    <property name="birth" ref="now"></property>
</bean>
<bean id="now" class="java.util.Date"></bean>

<!--集合和数组类型的依赖注入-->
<bean id="demo" class="com.atguigu.java1.CollectionDemo">
    <property name="arr">
        <array>
            <value>111</value>
            <value>222</value>
            <value>333</value>
        </array>
    </property>
    <property name="myList">
        <list>
            <value>111</value>
            <value>222</value>
            <value>333</value>
        </list>
    </property>
    <property name="mySet">
        <set>
            <value>111</value>
            <value>222</value>
            <value>333</value>
        </set>
    </property>
    <property name="myMap">
        <map>
            <entry key="aaa" value="aaa"></entry>
            <entry key="bbb" value="bbb"></entry>
            <entry key="ccc" value="ccc"></entry>
        </map>
    </property>
    <property name="myProp">
        <props>
            <prop key="aaa">aaa</prop>
            <prop key="bbb">bbb</prop>
            <prop key="ccc">ccc</prop>
        </props>
    </property>
</bean>

<!--使用默认构造器创建bean-->
<bean id="person" class="com.atguigu.java1.Person">
    <constructor-arg name="name" value="张三丰"></constructor-arg>
    <constructor-arg name="age" value="18"></constructor-arg>
</bean>

测试类:

/**
 * 测试基于xml形式的spring ioc获取对象
 */
@Test
public void test3(){
    ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
    User user= (User) ioc.getBean("user");//在此处打断点验证对象是什么时候被创建的。
    user.show();
}

/**
 * 采用默认构造器的形式创建bean对象
 */
@Test
public void test(){
    ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
    Person p= (Person) ioc.getBean("person");
    Person p2= (Person) ioc.getBean("person");
    System.out.println(p.toString());
}
/**
 * 使用get方法进行依赖注入
 */
@Test
public void test4(){
    ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
    User user= (User) ioc.getBean("user2");//在此处打断点验证对象是什么时候被创建的。
    System.out.println(user.toString());
}

/**
 * 集合和数组的依赖注入
 */
@Test
public void test5(){
    ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
    CollectionDemo demo= (CollectionDemo) ioc.getBean("demo");
    System.out.println(Arrays.toString(demo.getArr()));
    System.out.println(demo.getMyList());
    System.out.println(demo.getMySet());
    System.out.println(demo.getMyMap());
    System.out.println(demo.getMyProp());
}

4.使用spring的ioc实现账户的CRUD

4.1 基于xml形式

1.引用外部属性文件

2.SPEL表达式

Spring Expression Language,Spring表达式语言,简称SpEL。支持运行时查询并可以操作对象图。
和JSP页面上的EL表达式、Struts2中用到的OGNL表达式一样,SpEL根据JavaBean风格的getXxx()、setXxx()方法定义的属性访问对象图,完全符合我们熟悉的操作习惯。

2.基本语法
SpEL使用#{…}作为定界符,所有在大框号中的字符都将被认为是SpEL表达式。

3.使用字面量
    ●整数:<property name="count" value="#{5}"/>
    ●小数:<property name="frequency" value="#{89.7}"/>
    ●科学计数法:<property name="capacity" value="#{1e4}"/>
    ●String类型的字面量可以使用单引号或者双引号作为字符串的定界符号
        <property name=”name” value="#{'Chuck'}"/>
        <property name='name' value='#{"Chuck"}'/>
    ●Boolean:<property name="enabled" value="#{false}"/>

4.引用其他bean
    <bean id="emp04" class="com.atguigu.parent.bean.Employee">
        <property name="empId" value="1003"/>
        <property name="empName" value="jerry"/>
        <property name="age" value="21"/>
        <property name="detp" value="#{dept}"/>
    </bean>

5.引用其他bean的属性值作为自己某个属性的值
    <bean id="emp05" class="com.atguigu.parent.bean.Employee">
        <property name="empId" value="1003"/>
        <property name="empName" value="jerry"/>
        <property name="age" value="21"/>
        <property name="deptName" value="#{dept.deptName}"/>
    </bean>

6.调用非静态方法
    <!-- 创建一个对象,在SpEL表达式中调用这个对象的方法 -->
    <bean id="salaryGenerator" class="com.atguigu.spel.bean.SalaryGenerator"/>

    <bean id="employee" class="com.atguigu.spel.bean.Employee">
        <!-- 通过对象方法的返回值为属性赋值 -->
        <property name="salayOfYear" value="#{salaryGenerator.getSalaryOfYear(5000)}"/>
    </bean>

7.调用静态方法
    <bean id="employee" class="com.atguigu.spel.bean.Employee">
        <!-- 在SpEL表达式中调用类的静态方法 -->
        <property name="circle" value="#{T(java.lang.Math).PI*20}"/>
    </bean>

8.运算符
    ①算术运算符:+、-、*、/、%、^
    ②字符串连接:+
    ③比较运算符:<、>、==、<=、>=、lt、gt、eq、le、ge
    ④逻辑运算符:and, or, not, |
    ⑤三目运算符:判断条件?判断结果为true时的取值:判断结果为false时的取值
    ⑥正则表达式:matches

代码如下:

配置文件

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.2.xsd
        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-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
  
    <bean id="accountDao" class="com.atguigu.dao.impl.AccountDaoImpl">
        <property name="runner" ref="runner"></property>
    </bean>
    <bean id="accountService" class="com.atguigu.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <bean id="account" class="com.atguigu.domain.Account"></bean>
  
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
  
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    </bean>
</beans>

持久层

/*
  账户的持久层实现类
 */
public class AccountDaoImpl implements IAccountDao {
    
    private QueryRunner runner;

    public void setRunner(QueryRunner runner) {
        this.runner = runner;
    }

    public List<Account> findAllAccount() {
        try{
            return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer accountId) {
        try{
            return runner.query("select * from account where id = ? ",new BeanHandler<Account>(Account.class),accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try{
            runner.update("insert into account(name,money)values(?,?)",account.getName(),account.getMoney());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try{
            runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void deleteAccount(Integer accountId) {
        try{
            runner.update("delete from account where id=?",accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

业务层

/*
  账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService{
    
    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    public void deleteAccount(Integer acccountId) {
        accountDao.deleteAccount(acccountId);
    }
}

测试类

public class Test1 {
    ApplicationContext ioc=new ClassPathXmlApplicationContext("applicationContext.xml");
    @Test
    public void test1(){
        IAccountService service= (IAccountService) ioc.getBean("accountService");
        service.deleteAccount(2);
    }
}

4.2 xml和注解的混搭

XML配置形式:

<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="" 
        init-method="" destroy-method="">
    <property name=""  value="" | ref=""></property>
</bean>

1.用于创建对象的

他们的作用就和在XML配置文件中编写一个标签实现的功能是一样的。

Component:
作用:用于把当前类对象存入spring容器中
属性:
value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。

Controller:一般用在表现层

Service:一般用在业务层

Repository:一般用在持久层

以上个注解他们的作用和属性与Component是一模一样。
他们是spring框架为我们提供明确的层使用的注解,使我们的层对象更加清晰。

2.用于注入数据的

他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的。

Autowired:
作用:自动照类型注入。只要容器中唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功。
如果ioc容器中没任何bean的类型和要注入的变量类型匹配,则报错。
如果Ioc容器中多个类型匹配时:

出现位置:
可以是变量上,也可以是方法上。

细节:
在使用注解注入时,set方法就不是必须的了。

Qualifier:
作用:在照类中注入的基础之上再照名称注入。在给类成员注入时不能单独使用。但是在给方法参数注入时可以。

属性:
value:用于指定注入bean的id。

Resource:
作用:直接照bean的id注入。它可以独立使用。

属性:
name:用于指定bean的id。
以上注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。
另外,集合类型的注入只能通过XML来实现。

Value:
作用:用于注入基本类型和String类型的数据。

属性:
value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式
SpEL的写法:${表达式}

3.用于改变作用范围的

他们的作用就和在bean标签中使用scope属性实现的功能是一样的。

Scope:
作用:用于指定bean的作用范围。

属性:
value:指定范围的取值。常用取值:singleton prototype

4.和生命周期相关(了解)

他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的。

PreDestroy
作用:用于指定销毁方法。
PostConstruct
作用:用于指定初始化方法。

代码如下:

配置文件

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:jdbc="http://www.springframework.org/schema/jdbc"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.2.xsd
        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-4.2.xsd
        http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.2.xsd">
  
    <!--设置自动扫描的包-->
    <context:component-scan base-package="com.atguigu"></context:component-scan>
  
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
  
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/eesy"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    </bean>
</beans>

持久层

/**
 * 账户的持久层实现类
 */
@Repository(value = "accountDao")
public class AccountDaoImpl implements IAccountDao {
    @Autowired
    private QueryRunner runner;

    public List<Account> findAllAccount() {
        try{
            return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public Account findAccountById(Integer accountId) {
        try{
            return runner.query("select * from account where id = ? ",new BeanHandler<Account>(Account.class),accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try{
            runner.update("insert into account(name,money)values(?,?)",account.getName(),account.getMoney());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try{
            runner.update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void deleteAccount(Integer accountId) {
        try{
            runner.update("delete from account where id=?",accountId);
        }catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

业务层

/**
 * 账户的业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements IAccountService{
    @Autowired
    private IAccountDao accountDao;

    public List<Account> findAllAccount() {
        return accountDao.findAllAccount();
    }

    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);
    }

    public void saveAccount(Account account) {
        accountDao.saveAccount(account);
    }

    public void updateAccount(Account account) {
        accountDao.updateAccount(account);
    }

    public void deleteAccount(Integer acccountId) {
        accountDao.deleteAccount(acccountId);
    }
}

测试类

public class AccountServiceTest {
    @Test
    public void testFindAll() {
        //1.获取容易
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        //1.获取容易
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test");
        account.setMoney(12345f);
        //1.获取容易
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        as.saveAccount(account);
    }

    @Test
    public void testUpdate() {
        //1.获取容易
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        //1.获取容易
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        //3.执行方法
        as.deleteAccount(4);
    }
}

4.3 纯注解配置

1.注解

该类是一个配置类,它的作用和bean.xml是一样的。

spring中的新注解:

Configuration:

作用:指定当前类是一个配置类。
细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。

ComponentScan:

作用:用于通过注解指定spring在创建容器时要扫描的包。

属性:
value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。
我们使用此注解就等同于在xml中配置了:

<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,
    而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="com.itheima"></context:component-scan>

Bean:

作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中。

属性:
name:用于指定bean的id。当不写时,默认值是当前方法的名称。

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

Import:
作用:用于导入其他的配置类。

属性:
value:用于指定其他配置类的字节码。
当我们使用Import的注解之后,Import注解的类就父配置类,而导入的都是子配置类

PropertySource:

作用:用于指定properties文件的位置。

属性:
value:指定文件的名称和路径。
关键字:classpath,表示类路径下。

2.spring整合junit4

说明:
1、应用程序的入口
main方法

2、junit单元测试中,没有main方法也能执行
junit集成了一个main方法
该方法就会判断当前测试类中哪些方法有 @Test注解
junit就让有Test注解的方法执行

3、junit不会管我们是否采用spring框架
在执行测试方法时,junit根本不知道我们是不是使用了spring框架,
所以也就不会为我们读取配置文件/配置类创建spring核心容器。

4、由以上三点可知
当测试方法执行时,没有Ioc容器,就算写了Autowired注解,也无法实现注入。

使用Junit单元测试:
Spring整合junit的配置:测试我们的配置
1、导入spring整合junit的jar(坐标)

2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
@Runwith(SpringJUnit4ClassRunner.class)

3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
@ContextConfiguration

参数说明:

locations:指定xml文件的位置,加上classpath关键字,表示在类路径下。
classes:指定注解类所在地位置。
注意:当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上。

代码如下:

配置类

/**
 * @author Guohai
 * @createTime 2020-07-13 17:14
 */
@Configuration
@ComponentScan("com.atguigu")
@Import(JdbcConfig.class)
@PropertySource("classpath:c3p0.properties")
public class SpringConfig {

}

配置子类

/**
 * @author Guohai
 * @createTime 2020-07-13 17:16
 */
public class JdbcConfig {
    @Bean(name="runner")
    @Scope(value = "prototype")
    public QueryRunner getRunner(@Qualifier("ds1") DataSource dataSource) {
        QueryRunner runner = new QueryRunner(dataSource);
        return runner;
    }

    private static DataSource dataSource = null;

    @Bean(name="ds1")
    public DataSource getDataSource() {
        try {
            Properties prop = new Properties();
            InputStream is = JdbcConfig.class.getClassLoader().getResourceAsStream("jdbc.properties");
            prop.load(is);
            dataSource = DruidDataSourceFactory.createDataSource(prop);
            return dataSource;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;
    @Bean(name="ds2")
    public DataSource getDataSource2(){
        try {
            ComboPooledDataSource dataSource=new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(username);
            dataSource.setPassword(password);
            return dataSource;
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return null;
    }
}

测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountServiceTest {

    @Autowired
    private IAccountService as = null;

    @Test
    public void testFindAll() {
        //3.执行方法
        List<Account> accounts = as.findAllAccount();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        //3.执行方法
        Account account = as.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test anno");
        account.setMoney(12345f);
        //3.执行方法
        as.saveAccount(account);
    }

    @Test
    public void testUpdate() {
        //3.执行方法
        Account account = as.findAccountById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        //3.执行方法
        as.deleteAccount(4);
    }
}

5.Spring的自动装配

在spring中,对象无需自己查找或创建与其关联的其他对象,由容器负责把需要相互协作的对象引用赋予各个对象,使用autowire来配置自动装载模式。

在Spring框架xml配置中共有5种自动装配:
(1)no:默认的方式是不进行自动装配的,通过手工设置ref属性来进行装配bean。
(2)byName:通过bean的名称进行自动装配,如果一个bean的 property 与另一bean 的name 相同,就进行自动装配。
(3)byType:通过参数的数据类型进行自动装配。
(4)constructor:利用构造函数进行装配,并且构造函数的参数通过byType进行装配。
(5)autodetect:自动探测,如果有构造方法,通过 construct的方式自动装配,否则使用 byType的方式自动装配。

最后

大家看完有什么不懂的可以在下方留言讨论,也可以关注我私信问我,我看到后都会回答的。谢谢你的观看,觉得文章对你有帮助的话记得关注我点个赞支持一下!


前程有光
936 声望618 粉丝