Spring学习之==>IoC

一、概述

Spring的三大核心思想:IoC(控制反转),DI(依赖注入),AOP(面向切面编程)。本问讲着重介绍一下控制反转。

何谓控制反转:Spring 通过一种称作控制反转(IoC)的技术促进了松耦合。当应用了IoC,一个对象依赖的其它对象会通过被动的方式传递进来,而不是这个对象自己创建或者查找依赖对象。你可以认为IoC与JNDI相反——不是对象从容器中查找依赖,而是容器在对象初始化时不等对象请求就主动将依赖传递给它。

通俗的讲,控制权由代码中转到了外部容器,控制权的转移,就是所谓反转。也就是说,正常我们都是 new 一个新对象,才可以调用对象。有了控制反转就不需要了,交给容器来管理,我们只需要通过一些配置来完成把实体类交给容器这么个过程。这样可以减少代码量,简化开发的复杂度和耦合度。

二、面向接口编程

在一个面向对象的系统中,系统的各种功能是由许许多多的不同对象协作完成的。在这种情况下,各个对象内部是如何实现自己的,对系统设计人员来讲就不那么重要了;而各个对象之间的协作关系则成为系统设计的关键。小到不同类之间的通信,大到各模块之间的交互,在系统设计之初都是要着重考虑的,这也是系统设计的主要工作内容。面向接口编程就是指按照这种思想来编程。

面向接口编程,具体到某一个接口,对于调用者来说,我们不关心该接口是由哪个实现类来实现的,也不关心具体是怎么实现的,你只需把该接口提供给调用者就可以了。

三、编码实现IoC

下面,我们使用IoC思想来实现一个简单的面向接口编程。

首先定义两个接口 IAccountService 和 IOrderService,如下:

public interface IAccountService {

  int insertAccount(String msg);

}
IAccountService 接口
public interface IOrderService {

  void insert(String msg);

}
IOrderService 接口

然后,定义这两个接口的实现类

public class AccountBigServiceImpl implements IAccountService {

  @Override
  public int insertAccount(String msg) {

    System.out.println("===== AccountBigServiceImpl ====" + msg);

    return 0;
  }
}
AccountBigServiceImpl
public class AccountSamllServiceImpl implements IAccountService {

  @Override
  public int insertAccount(String msg) {

    System.out.println("smallService");

    return 0;
  }
}
AccountSamllServiceImpl
public class OrderServiceImpl implements IOrderService {

  @Override
  public void insert(String msg) {

    System.out.println("===== OrderServiceImpl ====" + msg);
  }
}
OrderServiceImpl

正常我们使用下面这段代码来调用这两个接口

public class Controller {
  public static void main(String[] args) {
    IAccountService accountService = new AccountBigServiceImpl();
    accountService.insertAccount("Hello!!");

    IOrderService orderService = new OrderServiceImpl();
    orderService.insert("World!!");
  }
}

我们在 Controller 这个类中直接 new 了 IAccountService 和 IOrderService 这两个接口的子类:AccountBigServiceImpl 和 OrderServiceImpl。这样就违背上面提到的面向接口编程的思想,我既要处理 Controller 这个类中的业务逻辑,还要关心 IAccountService 和 IOrderService 这两个接口具体的实现类是谁,上面是使用 AccountBigServiceImpl 这个类来实现接口 IAccountService,如果我要使用 AccountSamllServiceImpl这个实现类来实现这个接口,那我还得修改 Controller 中的代码,显然这样做的维护成本就太高了,而且也很容易出错,那么,我们可以想其他的办法。

首先,我们使用一个配置文件来定义这两个接口指向的实现类

IAccountService=com.jack.course.spring.factory.impl.AccountBigServiceImpl
IOrderService=com.jack.course.spring.factory.impl.OrderServiceImpl
conf.properties

然后,我们再定义一个工厂类

public class ServiceFactory {

  private static final String CONF_FILE_NAME = "factory/conf.properties";

  private static Properties prop;

  private static Map<String, Object> beanContainer;

  /**
   * 静态代码块,程序执行时只会被加载一次
   */
  static {

    try {
      beanContainer = Maps.newHashMap();
      //1.从配置文件中找出关系
      prop = new Properties();
      prop.load(ServiceFactory.class.getClassLoader().getResourceAsStream(CONF_FILE_NAME));
    } catch (IOException e) {
      throw new IllegalArgumentException(e);
    }
  }

  public static <T> T getService(Class<T> clazz){

    if (beanContainer.containsKey(clazz.getSimpleName())){
      return (T) beanContainer.get(clazz.getSimpleName());
    }

    try {
      //2.根据关系,将具体的实现类返回
      Object obj = instanceObj(clazz);
      beanContainer.put(clazz.getSimpleName(),obj);
      return (T) obj;
    } catch (Exception e) {
      throw new IllegalArgumentException(e);
    }
  }

  private static <T> Object instanceObj(Class<T> clazz) throws Exception {
    String className = prop.getProperty(clazz.getSimpleName());
    Class<?> instance = Class.forName(className);
    return instance.newInstance();
  }
}

注意:以上代码做了两处优化。第一,把读取配置文件的操作单独抽出来放入静态代码块,只会执行一次不会重复读取。第二,定义了一个 Map 作为 bean容器存放实例化后的对象,如果容器中已经存在实例化后的对象就可以直接返回,不用在重现通过 instanceObj() 方法来实例化对象了,通过这两处优化,能够大大提高性能。

这个工厂类实现的功能是:通过读取配置文件 conf.properties 得到接口的具体实现类,然后通过反射来生成一个对象传给 Controller,这样 Controller 类当中就可以不关心接口的实现类是谁,怎么实现也不用关心。

那我们 Controller 中就可以这么实现

public class Controller {
  public static void main(String[] args) {

    IAccountService accountService = ServiceFactory.getService(IAccountService.class);
    accountService.insertAccount("Hello!");

    IOrderService orderService1 = ServiceFactory.getService(IOrderService.class);
    IOrderService orderService2 = ServiceFactory.getService(IOrderService.class);
    orderService1.insert("One");
    orderService2.insert("Two");
  }
}

这样,我们如果想修改实现类的话,只需要修改配置文件就行了,不用修改代码。

运行结果如下:

以上,我们就通过 IoC 的思想实现了面向接口编程。但是,我们这样的实现就完全没有问题吗?

我们稍微修改一下 AccountBigServiceImpl 实现类,增加一个有参数的构造方法来覆盖默认的构造方法

public class AccountBigServiceImpl implements IAccountService {

  private AccountBigServiceImpl(String msg) {
    System.out.println(msg);
  }

  @Override
  public int insertAccount(String msg) {

    System.out.println("===== AccountBigServiceImpl ====" + msg);

    return 0;
  }
}

然后再执行 Controller,就会报错了。原因是因为我们使用 newInstance 实例化对象时使用的是默认的无参构造方法。

那么,我们通过修改工厂类,也可以实现该功能。但是,可能慢慢还会发现有其他问题,那么我们是不是要全部自己来实现呢,我们可以直接来使用 Spring 框架即可。Spring 框架把我们能想到的和想不到的功能都给实现了,我们只需直接使用就可以了。

四、使用XML文件配置IoC

我们基于 MVC 三层架构来做一个简单的实现,分为:表现层(Controller)、业务逻辑层(Service)和持久层(Dao)。

定义两个接口

public interface IocxService {

  String foo(String msg);

}
IocxService 接口
public interface IocxDao {

  String iocxFoo(String msg);

}
IocxDao 接口

再定义三个类,IocxController、IocxServiceImpl 和 IocxDaoImpl

public class IocxController {

  private IocxService iocxService;

  public IocxController() {
    System.out.println("***** IocxController init ******");
  }

  public String iocxFoo(String msg){

    return iocxService.foo(msg);
  }

  public void setIocxService(IocxService iocxService){

    this.iocxService = iocxService;

  }
}
IocxController
public class IocxServiceImpl implements IocxService {

  private IocxDao iocxDao;

  private Set<String> infos;

  private List<IocxAccount> accounts;

  private Map<String, String> mapData;

  @Override
  public String foo(String msg) {

    System.out.println("IocxServiceImpl = " + msg);

    System.out.println("infos = " + infos);

    return iocxDao.iocxFoo("account");
  }

  public void setIocxDao(IocxDao iocxDao) {
    this.iocxDao = iocxDao;
  }

  public void setInfos(Set<String> infos) {
    this.infos = infos;
  }

  public void setAccounts(List<IocxAccount> accounts) {
    this.accounts = accounts;
  }

  public void setMapData(Map<String, String> mapData) {
    this.mapData = mapData;
  }
}
IocxServiceImpl
public class IocxDaoImpl implements IocxDao {

  private String info;
  private Integer rows;

  public IocxDaoImpl(String info) {
    this.info = info;
    System.out.println("单参数构造方法");
  }

  public IocxDaoImpl(String info, Integer rows) {
    this.info = info;
    this.rows = rows;
    System.out.println("俩参数构造方法");
  }

  @Override
  public String iocxFoo(String msg) {
    return "IocxDaoImpl response";
  }
}
IocxDaoImpl

IocxController 为 Controller 层,定义一个类型为 IocxService(Service层) 的依赖属性,同时也定义了这个依赖属性的 set 方法。

IocxServiceImpl 为 Service 层,定义了一个类型为 IocxDao(Dao层) 的依赖属性,同时定义了另外三个 Set、List 和 Map 属性,并为这四个属性定义 set 方法

IocxDaoImpl 为 Dao 层,定义了两个不同参数的构造方法以及实现了 IocxDao 接口的 iocxFoo 方法。

就这样,Controller 层依赖于 Service 层,Service 层依赖于 Dao 层。

再来看一下 XML 配置文件 beans.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">

    <!-- 默认实现单例: scope="singleton" -->
    <!-- 要实现多例: scope="prototype" -->
    <bean id="controller" class="com.jack.course.spring.iocx.controller.IocxController"
          scope="prototype">
    <!-- DI,依赖注入,基于属性往对应的对象中注入数据 -->
        <property name="iocxService" ref="service"/>
    </bean>

    <bean id="service" class="com.jack.course.spring.iocx.service.impl.IocxServiceImpl">
        <property name="iocxDao" ref="dao"/>
        <!-- Set注入 -->
        <property name="infos">
            <set>
                <value>aaa</value>
                <value>bbb</value>
                <value>ccc</value>
            </set>
        </property>
        <!--  List注入 -->
        <property name="accounts">
            <list>
                <ref bean="abc1"/>
                <ref bean="abc2"/>
                <ref bean="abc3"/>
            </list>
        </property>
        <!-- Map注入 -->
        <property name="mapData">
            <map>
                <entry key="k1" value="v1"/>
                <entry key="k1" value="v1"/>
            </map>
        </property>
    </bean>

    <bean id="abc1" class="com.jack.course.spring.iocx.pojo.IocxAccount">
        <property name="name" value="hahah-haha"/>
    </bean>

    <bean id="abc2" class="com.jack.course.spring.iocx.pojo.IocxAccount">
        <property name="name" value="hahah-haha"/>
    </bean>

    <bean id="abc3" class="com.jack.course.spring.iocx.pojo.IocxAccount">
        <property name="name" value="hahah-haha"/>
    </bean>

    <bean id="dao" class="com.jack.course.spring.iocx.dao.impl.IocxDaoImpl">
        <constructor-arg value="test" index="0"/>
        <constructor-arg value="1024" index="1"/>
    </bean>

    <!-- 懒加载:用到时才加载,不用时不加载 -->
    <bean id="orderTestLazy" class="com.jack.course.spring.iocx.pojo.OrderTestLazy"
          lazy-init="true"/>

</beans>
beans.xml

从 XML 配置文件中可以看到,我们将每一个类都通过 id 和 class 对应的关系放入到了每个 bean 标签当中,id需要是唯一的,这就有点像上面我们自己实现 IoC 的时候定义的properties配置文件,Spring 一样是通过反射机制去加载类和生成对象。同时,我们看到配置文件中是通过 property 标签,基于属性的名字往对应的对象中去注入数据,这就是所谓的 DI(依赖注入),不过这里有个前提,这个属性必须在对应的类中定义且实现了它的 set 方法。另外,像非自定义的属性类型 Set、List、Map 的属性,XML文件中同样描述了如何注入。下面是测试程序:

public class App {

  @Test
  public void testNormal() {
    ApplicationContext context = new ClassPathXmlApplicationContext("iocx/beans.xml");
    IocxController controller = context.getBean("controller", IocxController.class);
    String response = controller.iocxFoo("hello");
    System.out.println("response:::" + response);
  }

  /**
   * 测试单例/多例模式
   * 默认: scope="singleton"
   * 要实现多例: scope="prototype"
   */
  @Test
  public void testScope() {
    ApplicationContext context = new ClassPathXmlApplicationContext("iocx/beans.xml");

    for (int i = 0; i < 5; i++) {
      IocxController controller = context.getBean("controller", IocxController.class);
      System.out.println("controller = " + controller);
    }
  }

  @Test
  public void testFactory() {
    ApplicationContext context = new ClassPathXmlApplicationContext("iocx/beans.xml");

    Object obj = context.getBean("beanFactory");

    // com.jack.course.spring.iocx.service.impl.IocxServiceImpl@6b53e23f
    System.out.println("obj = " + obj);
  }

  /**
   * 测试懒加载
   *
   * 默认是非懒加载
   *
   * 需要配置懒加载时,设置属性 lazy-init="true"
   */
  @Test
  public void testLazyInit() {
    ApplicationContext context = new ClassPathXmlApplicationContext("iocx/beans.xml");

    // Object controller = context.getBean("controller");

    Object orderTestLazy = context.getBean("orderTestLazy");

    // System.out.println("controller = " + controller);

    System.out.println("orderTestLazy = " + orderTestLazy);
  }
}
App 测试类

步骤是先读取配置文件,再生成实例化对象,最后再调用方法。使用配置文件的方法看上去比较麻烦,现在已经很少有人使用这种方式。

五、使用注解+XML文件配置IoC

同样,还说先定义两个接口

public interface IocaService {
  
  String foo(String msg);
  
}
IocaService 接口
public interface IocaDao {
  
  String foo(String msg);
  
}
IocaDao 接口

接着是三个类,IocaController、IocaServiceImpl 和 IocaDaoImpl

@Controller // 相当于是<bean id=xxx class=xxx>
public class IocaController {

  @Autowired // 相当于是 <propertity name=iocaService ref="">
  private IocaService iocaService;
  public String foo(String msg) {
    return iocaService.foo(msg);
  }

  @Autowired
  private Gson gson;
  public String format(Object object) {
    return gson.toJson(object);
  }
}
IocaController
@Service
public class IocaServiceImpl implements IocaService {

  @Autowired
  private IocaDao iocaDao;

  @Override
  public String foo(String msg) {
    return iocaDao.foo(msg);
  }
}
IocaServiceImpl
@Repository
public class IocaDaoImpl implements IocaDao {

  @Override
  public String foo(String msg) {

    System.out.println("msg = " + msg);

    return "dao response!!!";
  }
}
IocaDaoImpl

我们注意到:Controller、Service、Dao层分别使用 @Controller、@Service 和 @Repository 注解,关联属性使用 @Autowired 注解注入数据。

再来看一下 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"
       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:component-scan base-package="com.jack.course.spring.ioca"/>

    <!-- 因为不能给非自定义类加注解,所以需要在配置文件中注明 -->
    <bean id="gson" class="com.google.gson.Gson"/>
</beans>

配置文件中主要有两个内容,一个是指明注解扫描的包的路径,二是 IocaController 类中定义了 Gson 类型的属性,该类属于第三方 Jar包,没有办法添加注解,所以需要在配置文件中注明。下面是测试程序:

public class App {

  @Test
  public void testNormal() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ioca/beans.xml");

    IocaController controller = applicationContext.getBean(IocaController.class);

    String resp = controller.foo("hello");

    System.out.println("resp = " + resp);
  }

  @Test
  public void testFormat() {
    ApplicationContext applicationContext = new ClassPathXmlApplicationContext("ioca/beans.xml");

    IocaController controller = applicationContext.getBean(IocaController.class);

    Map<String, String> mapData = Maps.newHashMap();
    mapData.put("abc", "xyz");
    mapData.put("name", "jim");

    String resp = controller.format(mapData);
    System.out.println("resp = " + resp);
  }
}
App 测试类

步骤同样是先读取配置文件,再生成实例化对象,最后再调用方法。注解加配置文件的方式看上去比较简介,但是如果引入的第三方 Jar包比较多,在配置文件中就需要添加很多内容,所以这种方式也不建议使用。

六、使用全注解配置IoC

使用全注解的方式我们就不需要 XML 配置文件了,只需要新增一个配置类。同时,如果有一些数据库的配置文件需要加载的话,也可以在配置文件中进行配置

/**
 * 注解配置文件
 * @author Luolei
 */
// 相当于 beans.xml,标注这是一个配置类
@Configuration

// <context:component-scan base-package="com.jack.course.spring.ioca"/>
@ComponentScan(basePackages = "com.jack.course.spring.ioca")

// 相当于我们自己实例化了一个Properties对象, 并将相关数据读进来
@PropertySource("ioca/db.properties")
public class IocaConfiguration {

  // 相当于 <bean id="gson" class="com.google.gson.Gson"/>
  @Bean
  public Gson createGson(){
    return new Gson();
  }

  @Value("${driver}")
  private String driver;
  @Value("${url}")
  private String url;
  @Value("${usname}")
  private String username;
  @Value("${passwd}")
  private String passwd;
  @Bean
  public DataSource createDataSource(){
    DriverManagerDataSource dataSource = new DriverManagerDataSource();

    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(passwd);

    System.out.println("config datasources: " + driver + "*" + url
            + "*" + username + "*" + passwd);

    return dataSource;
  }
}

测试程序如下:

/**
 * 读取配置类IocaConfiguration
 */
public class App {

  @Test
  public void testFormat() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(IocaConfiguration.class);

    IocaController controller = applicationContext.getBean(IocaController.class);

    Map<String, String> mapData = Maps.newHashMap();
    mapData.put("abc", "123");
    mapData.put("name", "456");

    String resp = controller.format(mapData);
    System.out.println("resp = " + resp);
  }

  @Test
  public void testDataSource() {
    ApplicationContext applicationContext = new AnnotationConfigApplicationContext(IocaConfiguration.class);

    IocaController controller = applicationContext.getBean(IocaController.class);

    controller.callDataSource();
  }
}
App 测试类

这里读取的就不是 XML 配置文件,而是上面那个配置类。

常用注解说明:

  • @Component
    • 用于把当前类对象存入spring容器中;
    • 属性 value:用于指定bean的id。当我们不写时,它的默认值是当前类名首字母改小写;
  • @Controller
    • 一般用于表现层;
  • @Service
    • 一般用于业务层;
  • @Repository
    • 一般用于持久层;

以上三个注解他们的作用和Conmponant一样。

它们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰用于注入数据的。

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

  • @Autowired
    • 自动按照类型注入,只要容器中有唯一的一个 bean 对象类型和要注入的变量类型匹配,就可以注入成功;
    • 如果 IoC 容器中没有任何 bean 的类型和要注入的变量类型匹配,则报错;
    • 如果 IoC 容器中有多个类型匹配时,先按照数据类型在 IoC中 查找,再以变量名称为 IoC 中 id 查找 bean 对象注入;
    • 书写位置可以是变量上,也可以是方法上;
    • 和使用 XML 配置文件不一样的是:在使用注解注入时,set方法就不是必须的了;
  • @Qualifier
    • 在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以;
    • 属性value:用于指定注入bean的 id;
  • @Resource
    • 直接按照bean的id注入,可以独立使用;
    • 属性name:用于指定bean的 id;

以上三种注解只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现。另外,集合类型的注入只能用 XML 文件来实现。

  • @Value
    • 用于注入基本类型和String类型的数据;
    • 属性value:用于指定数据的值,它可以使用 spring 中 SpEL表达式;
    • SpEL 表达式的写法:${表达式};
  • @Scope
    • bean的作用范围;
    • 属性value:两个值 singleton 和 prototype;
      • singleton单例模式:全局有且仅有一个实例;
      • prototype原型模式:每次获取Bean的时候会有一个新的实例;

新增注解:

  • @Configuration
    • 指定当前类是一个配置类,它的作用和bean.xml一样;
    • 当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写;
    • 配置类中可以不写,因为获取容器时通过ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);扫描主配置类。其余配置类需要写。并在主配置类中@ComponentScan({"com.test"})添加扫描的类,建议最好还是写,免得麻烦;
  • @ComponentScan
    • 用于通过注解指定spring在创建容器时要扫描的包;
    • 属性value:它和basePackages的作用一样,都适用于指定创建容器时要扫描的包;
    • 我们使用此注解就等同于在xml中配置了:<context:component-scan base-package="com.jack.course.spring.ioca"/>;
    • 默认就会装配标识了@Controller,@Service,@Repository,@Component注解的类到spring容器中。当然,这个的前提就是你需要在所扫描包下的类上引入注解。
  • @Bean
    • 把当前方法的返回值作为bean对象存入spring的ioc容器中;
    • 属性name:用于指定bean的id。默认值是当前方法的名称;
    • 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象;
    • 查找的方式和Autowired注解的作用是一样的。先根据数据类型查找,再根据id查找,均在ioc容器中查找;
  • @PropertySource
    • 用于指定properties文件的位置;
    • 属性value:指定文件的名称和路径;
  • @Import
    • 导入其他的配置类.指定配置类的.class;
    • 属性value:用于指定配置类的字节码;
    • 当我们使用Import的注解之后,有Import注解的类就是父配置类即主配置类,而导入的都是子配置类;

七、Spring 整合 Junit

@Controller // 相当于是<bean id=xxx class=xxx>
public class IocaController {

  @Autowired // 相当于是 <propertity name=iocaService ref="">
  private IocaService iocaService;
  public String foo(String msg) {
    return iocaService.foo(msg);
  }

  @Autowired
  private Gson gson;
  public String format(Object object) {
    return gson.toJson(object);
  }

  @Autowired
  private DataSource dataSource;
  public void callDataSource(){
    System.out.println(dataSource);
  }
}
IocaController
// 相当于 beans.xml,标注这是一个配置类
@Configuration

// <context:component-scan base-package="com.jack.course.spring.ioca"/>
@ComponentScan(basePackages = "com.jack.course.spring.ioca")
// @ComponentScan(value = "com.jack.course.spring.ioca")

// 相当于我们自己实例化了一个Properties对象, 并将相关数据读进来
@PropertySource("ioca/db.properties")
public class IocaConfiguration {

  // 相当于 <bean id="gson" class="com.google.gson.Gson"/>
  @Bean(name = "gson")
  public Gson createGson(){
    return new Gson();
  }

  @Value("${driver}")
  private String driver;
  @Value("${url}")
  private String url;
  @Value("${usname}")
  private String username;
  @Value("${passwd}")
  private String passwd;

  /**
   * 创建数据源对象
   * @return DataSource
   */
  @Scope("prototype")
  @Bean(name = "dataSource")
  public DataSource createDataSource(){
    DriverManagerDataSource dataSource = new DriverManagerDataSource();

    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(username);
    dataSource.setPassword(passwd);

    System.out.println("config datasources: " + driver + "*" + url
            + "*" + username + "*" + passwd);

    return dataSource;
  }
}
IocaConfiguration

使用Spring 做单测

@RunWith(SpringJUnit4ClassRunner.class)
/**
 * 加载配置类IocaConfiguration
 * 相当于: ApplicationContext context = new AnnotationConfigApplicationContext(IocaConfiguration.class)
 *
 * 也可以使用xml配置文件的形式
 * 相当于:ApplicationContext applicationContext = new ClassPathXmlApplicationContext("iocx/beans.xml");
 */
// @ContextConfiguration(locations = {"classpath:iocx/beans.xml"})
@ContextConfiguration(classes = IocaConfiguration.class)
public class App2 {

  @Autowired
  private IocaController controller;

  @Test
  public void testNormal() {
    String resp = controller.foo("hello");
    System.out.println("resp = " + resp);
  }

  @Test
  public void testFormat() {
    Map<String, String> mapData = Maps.newHashMap();
    mapData.put("abc", "xyz");
    mapData.put("name", "jim");

    String resp = controller.format(mapData);
    System.out.println("resp = " + resp);
  }
}

@RunWith(SpringJUnit4ClassRunner.class)

  • 就是一个运行器,测试时使用,里面的参数需要传入 .class类对象;
  • @RunWith(SpringJUnit4ClassRunner.class) 表示让测试运行于 Spring 环境;

@ContextConfiguration(classes = IocaConfiguration.class)

  • 用于Spring整合JUnit4测试时,使用注解引入配置类或xml配置文件;
  • 属性 locations:引入xml 配置文件;
  • 属性classes:引入配置类;

八、Spring 整合 JDBC

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="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="datasource"/>
    </bean>

    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://192.168.182.128:3306/cakes"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

</beans>

首先将 jdbcTemplate 和 datasource 加入bean 容器

测试程序

/**
 * 读取jdbc/beans.xml文件中的配置
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:jdbc/beans.xml")
public class App {

  @Autowired
  private JdbcTemplate template;

  @Test
  public void testSpringJdbcTemplate(){
    template.execute("insert into user(name,passwd) values('jack','123455')");
  }
}

注解版

数据库连接配置

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://192.168.182.128:3306/cakes
usname=root
passwd=123456
db.properties

配置类

@Configuration
@ComponentScan(basePackages="com.jack.course.spring.jdbc")
@PropertySource("jdbc/db.properties")
public class JdbcConfiguration {

  @Value("${driver}")
  private String driver;
  @Value("${url}")
  private String url;
  @Value("${usname}")
  private String userName;
  @Value("${passwd}")
  private String passwd;

  /**
   * 用于创建一个JdbcTemplate对象
   */
  @Bean
  public JdbcTemplate createTemplate(DataSource dataSource) {
    return new JdbcTemplate(dataSource);
  }

  /**
   * 创建数据源对象
   */
  @Bean
  public DataSource createDataSource() {
    
    DriverManagerDataSource dataSource = new DriverManagerDataSource();
    dataSource.setDriverClassName(driver);
    dataSource.setUrl(url);
    dataSource.setUsername(userName);
    dataSource.setPassword(passwd);

    return dataSource;
  }
}
JdbcConfiguration

测试程序

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = JdbcConfiguration.class)
public class App2 {

  @Autowired
  private JdbcTemplate jdbcTemplate;

  @Test
  public void testSpringJdbcTemplate(){
    jdbcTemplate.execute("insert into user(name,passwd) values('木木','123459')");
  }
}

 

posted on 2019-09-27 18:39  破解孤独  阅读(374)  评论(0编辑  收藏  举报

导航