浅谈Spring IoC依赖注入(DI)——从配置文件到纯注解(二)

4、功能完善

添加更新、删除、查询byID、查询所有的功能

至此,spring的配置文件已经全部写好,但是功能还较为单一,我们来补充一下其他的CURD功能,这部分工作不算难。只需要把studentDao和studentService的相关类完善后进行测试即可:

完善studentDao.class

public interface StudentDao {
    //保存
    void saveStudent(Student student);
    //更新
    void updateStudent(Student student);
    //删除
    void deleteStudent(int id);
    //根据id查询
    Student selectStudentById(int id);
    //查询全部
    List<Student> selectAll();
}

完善studentDaoImpl.class

/**
     * 更新信息,根据id更新内容
     * @param student 学生对象
     */
    @Override
    public void updateStudent(Student student) {
        String sql = "update student set name = ?,age = ? where id = ?";
        //三个占位分别表示姓名、年龄、id
        try {
            queryRunner.update(sql,student.getName(),student.getAge(),student.getId());
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据id删除
     * @param id
     */
    @Override
    public void deleteStudent(int id) {
        String sql = "delete from student where id = ?";
        try {
            queryRunner.update(sql,id);
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

    /**
     * 根据id查询
     * @param id
     * @return
     */
    @Override
    public Student selectStudentById(int id) {
        String sql = "select * from student where id = ?";
        try {
            Student student = queryRunner.query(sql, new BeanHandler<Student>(Student.class), id);
            return student;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 查询所有学生
     * @return
     */
    @Override
    public List<Student> selectAll() {
        String sql = "select * from student";
        try {
            List<Student> studentList = queryRunner.query(sql, new BeanListHandler<Student>(Student.class));
            return studentList;
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

完善studentService.class

public interface StudentDao {
    //保存
    void saveStudent(Student student);
    //更新
    void updateStudent(Student student);
    //删除
    void deleteStudent(int id);
    //根据id查询
    Student selectStudentById(int id);
    //查询全部
    List<Student> selectAll();
}

完善studentServiceImpl.class

@Override
    public void updateStudent(Student student) {
        studentDao.updateStudent(student);
    }

    @Override
    public void deleteStudent(int id) {
        studentDao.deleteStudent(id);
    }

    @Override
    public Student selectStudentById(int id) {
        return studentDao.selectStudentById(id);
    }

    @Override
    public List<Student> selectAll() {
        return studentDao.selectAll();
    }

测试功能

为了方便测试,我们在测试类中提取studentServiceImpl对象为成员变量,把容器的获取和studentServiceImpl对象的获取放到@before里:
注意:这里我修改了增加方法的代码,因为修改了代码要再次测试一下原有功能。

public class TestIoCandDI {

    private StudentService studentService;

    @Before
    public void setUp(){
        //拿到容器
        ApplicationContext ac =
                new ClassPathXmlApplicationContext("application-context.xml");
        //从容器中拿到service,第一个参数是容器中bean的id,第二个参数指定bean的类型.
        //如果不指定也可以,拿到的是Object类,需要通过强转.
        //StudentService studentService = (StudentService) ac.getBean("studentService");
        studentService = ac.getBean("studentService", StudentService.class);
    }
    @Test
    public void testSaveStudent(){
        Student student = new Student(null,"不与三的毛竹",18);
        studentService.saveStudent(student);
    }
}
再次测试增加功能

在这里插入图片描述

测试删除功能

删除id为5的学生:

    @Test
    public void testDeleteStudent(){
        studentService.deleteStudent(5);
    }

结果:
在这里插入图片描述

测试更新功能

把id为4的学生姓名改为不与三的毛竹,年龄改为18

    @Test
    public void testUpdateStudent(){
        Student student = new Student(4, "不与三的毛竹", 18);
        studentService.updateStudent(student);
    }

结果:
在这里插入图片描述

测试根据id查找功能

查找id为4的学生,并在控制台打印

	@Test
    public void testSelectStudentById(){
        Student student = studentService.selectStudentById(4);
        System.out.println(student);
    }

结果:
在这里插入图片描述

测试查询所有功能

查找所有的学生,并在控制台打印

	@Test
    public void testSelectAll(){
        List<Student> students = studentService.selectAll();
        for (Student student : students) {
            System.out.println(student);
        }
    }

结果:
在这里插入图片描述

5、分步改为纯注解开发

0、增加注解扫描

我们需要让spring去扫描所有的类,需要在配置文件增加一行:
<context:component-scan base-package="psers.buyusan"/>
根据提示补全约束:
在这里插入图片描述
这样程序在加载到这里的时候,spring就会扫描所有的类,去读取注解。

1、修改service

先来看目前的application-context.xml有这样几行:

<!--管理studentService,并注入studentDao-->
<bean id="studentService" class="pers.buyusan.service.StudentServiceImpl">
    <property name="studentDao" ref="studentDao"/>
</bean>

它的作用是给studentServiceImpl类取名id为studentService放入Spring容器,然后把容器中id为studenDao的类注入到studentServiceImpl中的studentDao属性。
接下来我们在studentServiceImpl类中添加如下注解:
在这里插入图片描述
@Service的作用就是把该类的对象放入Spring,除了@Service,还可以用@Repository、@Controller、@Component,他们的功能一样,为了方便区别类所属的层,@Service一般用于业务层,@Controller一般用于表现层/控制层,@Repository一般用于持久层,而@Component用于注解其他的类型的类。

然后我们要给studentServiceImpl对象注入id为studentDao的对象,我们发现,studentDao属性和id完全一样,所以我们只需在属性上增加注解@Autowired即可。

如果bean中的id和属性名不一致,可以在@Autowired的基础上增加@Qualifier,并在@Qualifier中指定bean的id即可。

另外也可以通过@Resource来注入属性,但这个注解是jdk提供的,功能不如Spring强大,一般不做使用。

增加了@Autowired后,可以去掉set方法了。并去掉配置文件中的对应的bean标签。

改完之后我们随便测试一个方法,发现仍能运行,说明程序OK,继续下一步。

2、修改DAO

同样的把studentDaoImpl.class修改如下:
在这里插入图片描述
这里我们用了@Repository,因为dao属于持久层。
再次测试。通过。

如果注入的是基本类型数据,则用@Value。后面会体现。

3、把第三方类改为用注解引入

个人觉得开发中,只需要将自己编写的类用注解注入即可,第三类注入比较复杂,相比之下用配置文件的方式更容易接受,但我们的目标是全注解,所以我们还要继续。
现在配置文件里有两个bean的注入,一个是queryRunner,一个dataSource,我们知道,容器中添加bean的方式有三种,我们无法修改第三方的类,不能在上边增加@Component,所以我们要采取另一种工厂的方式,把这两个实例交给Spring。
这里我们需要创建一个额外的类JdbcConfig,通过注解把创建的对象交给Spring。
首先是queryRunner:
类:JdbcConfig,增加方法:

@Bean(name = "queryRunner")
    public QueryRunner createQueryRunner(DataSource dataSource){
        QueryRunner queryRunner = new QueryRunner(dataSource);
        return queryRunner;
    }

这里用了一个方法返回一个queryRunner,同时给这个方法加入注解@Bean,@Bean的name是该对象在容器中的id,方法的参数名是bean里的其他对象的id。
这里dataSource不能随便修改,否则会Spring会找不到容器中的dataSource。

增加方法:

@Bean(name = "dataSource")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/spring");
            dataSource.setUser("root");
            dataSource.setPassword("root");
            return dataSource;
        } catch (PropertyVetoException e) {
            e.printStackTrace();
        }
        return null;
    }

这里的@Bean中的name就是对应进入容器后的id,这里要和刚才createQueryRunner的方法的参数一致。最后,别忘了给JdbcConfig加入@Component,否则这个类不会被Spring识别到。
写到这里,配置文件的<bean>就全部消失了。
再次测试,依然通过。

4、纯注解的最后一步

现在的配置文件变成什么样了呢?

<?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">

    <context:component-scan base-package="pers.buyusan"/>

</beans>

如果能把扫描注解的标签去掉,整个xml文件就没必要存在了。
现在分析一下:
首先,我们通过ApplicationContext ac = new ClassPathXmlApplicationContext("application-context.xml");获取到了bean容器,这里是通过读取xml配置文件的方式,当Spring读到xml,发现xml又让Spring去扫描包,读取注解,那能不能直接告诉Spring去读注解,不用再读取配置文件了呢?当然可以。
ApplicationContext ac = new AnnotationConfigApplicationContext(XXX.class);
这样就告诉Spring去读取一个类的注解,而不是读取配置文件了。
接下来的问题是,被读取的这个类,要有什么样的特征,让Spring知道去扫描其他的注解呢?
这个类可以是任意的类,为了方便功能划分。我们把JdbcConfig改名为Config,意为此类为加载配置的类。在这个类中的@Component下增加@ComponentScan,设置basePackages为"pers.buyusan",这样,Spring就会读取其他的注解并加载和注入bean了。
在这里插入图片描述
这是我们把application-context.xml删除就可以了,测试类也进行修改一下:
在这里插入图片描述
最后进行测试,各个功能仍能正常使用。
到这里,就完成了由配置文件转型为纯注解开发。

6、进一步优化

@Qualifier的使用

在queryRunner中注入dataSource的时候我们需要参数名和bean的id一致,那么如果不一致的情况下我们可以用@Qualifier进行id指定。
在@Autowired的时候如果属性名和bean的id无法对应自动注入,也可以通过@Qualifier注解来进行手动指定。
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

配置信息的提取

我们发现,在装配dataSource的时候,把数据库的连接信息以硬编码的方式写死到了代码里,这违背了Spring的解耦的思想,所以Spring提供了读取配置文件的功能,同样可以用注解表示,这个注解就是@PropertySource,他的value是一个数组,指定读取哪个配置文件。
我们现在resources目录下创建一个jdbc.properties
在这里插入图片描述
填入配置信息:
在这里插入图片描述
在Config中加入@PropertySource注解
在这里插入图片描述
在Config类中,把数据库信息提取成成员变量,并用@value方式注入。
在这里插入图片描述

单元测试的整合

在maven的pom.xml文件中添加依赖,在开头环境准备的时候我们已经添加。
在这里插入图片描述
注意junit版本必须是4.12以上,另外spring-test的版本要与spring-context版本一致。
我们在web包下创建SpringTest类,用作新的测试类。
在这里插入图片描述
在类中我们写入注解@Runwith和@ContextConfiguration

package pers.buyusan.web;

import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import pers.buyusan.config.Config;

/**
 * @author dongqg
 * @Description
 * @date 2019-07-13 19:49
 **/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Config.class})
public class SpringTest {
}

说明:
@Runwith:测试运行器,JUnit所有的测试方法都是由测试运行器负责执行,JUnit为单元测试提供了一个默认的测试运行器BlockJUnit4ClassRunner。
SpringJUnit4ClassRunner:运行于Spring测试环境(spring集成了Junit)。
@ContextConfiguration
locations属性:用于指定配置文件的位置。如果是类路径下,需要用classpath:表明
classes属性:用于指定注解的类。当不使用xml配置时,需要用此属性指定注解类的位置。我们用classes属性。
这时,我们的类SpringTest就变成了测试类,我们可以直接为此类注入studentService。并加入测试方法:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = {Config.class})
public class SpringTest {
    @Autowired
    @Qualifier("studentService")
    private StudentService studentService;

    @Test
    public void testSelectAll(){
        List<Student> students = studentService.selectAll();
        for (Student student : students) {
            System.out.println(student);
        }
    }
}

测试结果为:
在这里插入图片描述

总结

至此,所有工作已全部完成。这个demo我写了两次,有个坑两次都踩中了,就是Config类忘记添加@Component就直接去测试,结果报错找不到queryRunner对象。这个很容易忘记。
最后来一份终极的目录结构,与开始的有些出入,删除了一些类并加入了一些类。可能开始有些类创建了没有用到,写到后边懒得修改了,我贴一份新的。
在这里插入图片描述
最后,这个小案例不算难,主要用于Spring容器中bean的装配和注入的练习,并分别用了配置文件和注解的方式进行比较,正常一个小时内应该可以练习完毕整个过程。由于我一边写博客一边编码,这个案例差不多用了五六个小时的样子。经过这样的练习,我对Spring的依赖注入和注解的功能有了更深的印象。
谢谢大家。

上一篇:浅谈Spring IoC依赖注入(DI)——从配置文件到纯注解(一)

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值