• 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')");
      }
    }
  • 相关阅读:
    《大道至简》读后感
    PowerBuilder学习笔记之1开发环境
    PowerBuilder学习笔记之14用户自定义对象
    查询数据库大小的代码
    JAVA基础_修饰符
    SQLSERVER查询存储过程内容
    Asp.Net WebAPI中Filter过滤器的使用以及执行顺序
    运算符
    判断(if)语句
    变量的命名
  • 原文地址:https://www.cnblogs.com/L-Test/p/11593641.html
Copyright © 2020-2023  润新知