上一节,我们完成了基于xml的IOC数据库实际操作案例,本节,我们将上一节中的案例改造成基于注解的IOC,首先我们实现半注解,半xml的案例,然后我们完全脱离xml,进行纯注解的案例。

1.半注解半xml

对于我们自己写的类,比如dao、service。我们可以使用component、autowired等注解创建bean对象以及进行依赖注入。

1.1 持久层实现类

在Dao层的实现类上,我们使用@Repository注解将持久层实现类添加到容器中,并使用@Autowired注解对runner对象进行依赖注入,该对象是在xml配置文件中创建的。

package com.eastnotes.dao.impl;

import com.eastnotes.dao.AccountDao;
import com.eastnotes.domain.Account;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;

import java.sql.SQLException;
import java.util.List;

/**
 * 账户的持久层实现类
 */
@Repository("accountDao")
public class AccountDaoImpl implements AccountDao {

    @Autowired
    private QueryRunner runner;

    // 查找数据库全部数据
    public List<Account> findAllAccount() {
        try {
            return runner.query("select * from account", new BeanListHandler<Account>(Account.class));
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //根据id查找某条数据
    public Account findAccountById(Integer id) {
        try {
            return runner.query("select * from account where id = ?", new BeanHandler<Account>(Account.class), id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //添加新数据
    public void saveAccount(Account account) {
        try {
            runner.update("insert into account(name,money)values(?,?) ", account.getName(), account.getMoney());
        } catch (SQLException 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 (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    //删除某条数据
    public void deleteAccount(Integer id) {
        try {
            runner.update("delete from account where id = ?", id);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}

1.2 业务层实现类

在该类中,我们使用@Service注解将对象存入Spring容器中,并使用@Autowired注解,将accountDao对象注入进来,该对象是我们刚刚使用注解存入容器中的:

package com.eastnotes.service.impl;

import com.eastnotes.dao.AccountDao;
import com.eastnotes.domain.Account;
import com.eastnotes.service.AccountService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * 账户的业务层实现类
 */
@Service("accountService")
public class AccountServiceImpl implements AccountService {

    @Autowired
    private AccountDao accountDao;

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

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

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

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

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

1.3 xml配置文件

xml配置文件中配置了QueryRunner对象、DataSource数据源对象,因为这两个对象是在第三方包中,我们无法在编译好的jar包中给他们添加注解,因此现在还需要依赖xml配置文件,具体配置如下,其实就是删除了对accountDaoaccountService的配置:

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

    <!--告知Spring在创建容器时要扫描的包-->
    <context:component-scan base-package="com.eastnotes"></context:component-scan>

    <!--配置QueryRunner对象-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner">
        <!--使用构造函数注入数据源datasource-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>

    <!--配置数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--使用set注入连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="user" value="root"></property>
        <property name="password" value="asd100"></property>
    </bean>

</beans>

2.新注解

在讲纯注解的案例前,我们会介绍几个新的注解:

放在配置类前的注解

  • @Configuration:指定当前类为一个配置类,相当于使用了xml配置文件。当配置类作为AnnotationConfigurationApplicationContext创建的参数时,该注解可以不写。
  • @ComponentScan:用于指定spring在创建容器时要扫描的包,相当于在xml中配置了<context:component-scan base-package="com.eastnotes"></context:component-scan>
  • @Import:用于导入其他的配置类,它的属性是value:用于指定其他配置类字节码。
  • @PropertySource:用于指定Properties文件的位置,它的属性是value:指定文件的名称和文件的路径,属性里的关键字是classpath:表示类路径下

放在配置类里的方法前的注解

  • @Bean:用于将当前方法的返回值,作为bean对象存入Spring ioc的容器中,它的属性是name:用于指定bean的id,当不写时,默认值是当前方法的名称。

    当我们使用注解配置时,如果方法有参数,那么Spring框架会去容器中查找有没有可以用的bean对象,查找的方法和@autowired注解的方法是一样的,自动按照类型注入,有唯一的一个类型就注入,如果没有就报错。相当于省去了set方法注入。

3.纯注解

纯注解所要解决的就是如何完全抛弃对xml文件的依赖,并且将第三方jar包中的对象存入Spring容器中。在这里,就是QueryRunner对象、DataSource数据源对象。

3.1 创建配置类

既然不用xml配置文件了,那么就需要找到一个替代品,这个替代品就是配置类,新建一个包,并在该包下创建一个名为SpringConfiguration的类,该类就是我们要的替代xml文件的配置类。然后使用上面提供的新注解完成配置:

@Configuration
@ComponentScan(basePackages = "com.eastnotes")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {

}

Configuration注解指明了SpringConfiguration这个类为spring容器的配置类。ComponentScan注解指定了该配置类会去扫描com.eastnotes这个包下面的注解,并对其进行解析。Import注解表明该配置类为父类,导入了一个子类配置类JdbcConfig,这是因为jdbc这个配置类配置的都是与数据连接的信息,所以,单独放在一个配置里面,其他类型的配置也可以这样做,PropertySource指明了properties配置文件的位置。

下面是子配置类的代码:

package config;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import org.apache.commons.dbutils.QueryRunner;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Scope;

import javax.sql.DataSource;
import java.beans.PropertyVetoException;


public class JdbcConfig {

    @Value("${jdbc.driver}")
    private String driver;

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

    /**
     * 创建QueryRunner对象
     * @param dataSource
     * @return
     */
    @Bean(name="runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }


    /**
     * 创建数据源对象
     * @return
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (PropertyVetoException e) {
            throw new RuntimeException();
        }
    }
}

3.2 测试

在这里,我只测试一个方法:

package com.eastnotes.test;

import com.eastnotes.domain.Account;
import com.eastnotes.service.AccountService;
import config.SpringConfiguration;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

import java.util.List;

public class AccountServiceTestDemo {
    @Test
    public void testFindAll() {

        //1.创建容器对象
        ApplicationContext ac = new AnnotationConfigApplicationContext(SpringConfiguration.class);

        //2.获取bean对象
        AccountService as = ac.getBean("accountService",AccountService.class);

        List<Account> accounts = as.findAllAccount();

        for(Account account : accounts){
            System.out.println(account);
        }
    }
}

可以看到,这里我们使用的是ApplicationContext的另外一个实现类:AnnotationConfigApplicationContext,它是专门用于创建使用了纯注解配置的容器。