Spring 基於註解的Ioc


1. 基於註解的Ioc配置

Spring基於XML文件配置的形式爲:

<bean id="accountService" class="dyliang.service.impl.AccountServiceImpl">
    <property name="accountDao" ref="accountDao"></property>
</bean>

<bean id="accountDao" class="dyliang.dao.impl.IAccountDaoImpl">
    <property name="runner" ref="runner"></property>
</bean>

accountService和accountDao相對應的類實現代碼爲:

public class IAccountDaoImpl implements IAccountDao {

    @Autowired
    private QueryRunner runner;

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

    public Account findById(Integer id) {
        try {
            return runner.query("select * from account where id=?", new BeanHandler<Account>(Account.class), id);
        }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 id) {
        try {
            runner.update("delete from account where id=?",id);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

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

    public Account findById(Integer id) {
        return accountDao.findById(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);
    }
}

spring中提供了註解@Component來配置資源的管理,對於

<bean id="accountService" class="dyliang.service.impl.AccountServiceImpl"></bean>

的功能相同實現只需要在對應的類上使用註解@Component,如下所示:

@Component
public class AccountServiceImpl implements IAccountService {
    private IAccountDao accountDao;

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

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

使用了註解@Component後,雖然在bean.xml文件中不需要配置<bean>,但需要配置<context:component-scan>來告訴spring在創建Ioc容器時應該掃描哪些包,即使用@Component註解的類所在的包

<context:component-scan base-package="dyliang"></context:component-scan>

同時導入約束時需要多導入一個context名稱空間下的約束,啓用註解

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

最後編寫測試類來執行單元測試

public class AccountTest {

    @Test
    public void testFindAll() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService", IAccountService.class);
        List<Account> accounts = as.findAll();
        for (Account account : accounts) {
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        Account account = as.findById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test");
        account.setMoney(12345f);

        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        as.saveAccount(account);
    }

    @Test
    public void testUpdate() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        Account account = as.findById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService as = ac.getBean("accountService",IAccountService.class);
        as.deleteAccount(4);
    }
}


2. 常用註解

2.1 創建對象

  • @Component:把資源讓spring來管理,相當於在xml中配置一個bean。
    • 屬性value:指定bean的id,如果不指定value屬性,默認bean的id是當前類首字母小寫的類名

@Controller @Service @Repository和@Component的作用相同,但是語義更加的明確,顯式的指明瞭註解適合使用的地方。同樣可使用value屬性來指定具體的名字,因爲只有value,所以也可以省略不寫。

  • @Controller:一般用於表現層

  • @Service:一般用於業務層

    @Service("accountService")
    public class AccountServiceImpl implements IAccountService {}
    
  • @Repository:一般用於持久層

    @Repository("accountDao")
    public class IAccountDaoImpl implements IAccountDao {}
    

2.2 注入數據

  • @Autowired:自動按照類型注入。當使用註解注入屬性時,set方法可以省略。它只能注入其他bean類型。當有多個類型匹配時,使用要注入的對象變量名稱作爲bean的id,在spring容器查找,找到了可以注入成功;找不到就報錯。使用方式如下:

    @Service("accountService")
    public class AccountServiceImpl implements IAccountService {
    
        @Autowired
        private IAccountDao accountDao;
    }
    

    當使用時,程序就會在Ioc容器中中找id爲accountDao,對應的class爲IAccountDao的對象。如果有,則自動注入,否則報錯。

  • @Qualifier:在自動按照類型注入的基礎之上,再按照Bean的id注入。它在給字段注入時不能獨立使用,必須和@Autowired一起使用;但是給方法參數注入時,可以獨立使用

    • 屬性value::指定bean的id
  • @Resource:直接按照Bean的id注入,它也能注入其他bean類型

    • 屬性name:指定bean的id
  • @Value:注入基本數據類型和String類型數據的值

    • 屬性value:用於指定值

2.3 改變作用範圍

  • @Scope:指定bean的作用範圍
    • 屬性value:指定範圍的值,取值:singleton、 prototype、 request 、session 、globalsession

2.4 生命週期

  • @PostConstruct : 用於指定初始化方法
  • @PreDestroy : 用於指定銷燬方法

2.5 配置

  • @Configuration:用於指定當前類是一個spring配置類,當創建容器時會從該類上加載註解。獲取容器時需要使用AnnotationApplicationContext(有@Configuration註解的類.class)

    • 屬性value:用於指定配置類的字節碼
  • @ComponentScan:用於指定spring在初始化容器時要掃描的包。作用和在spring的xml配置文件中的<context:component-scan base-package="dyliang"/>一樣

    • 屬性basePackages:用於指定要掃描的包
  • @Bean:該註解只能寫在方法上,表明使用此方法創建一個對象,並且放入spring容器

    • 屬性name:給當前@Bean註解方法創建的對象指定一個名稱(即bean的id)
  • @PropertySource:用於加載.properties文件中的配置。例如我們配置數據源時,可以把連接數據庫的信息寫到properties配置文件中,就可以使用此註解指定properties配置文件的位置

    • 屬性value[]:用於指定properties文件位置。如果是在類路徑下,需要寫上classpath:
  • @Import:用於導入其他配置類,在引入其他配置類時,可以不用再寫@Configuration註解

    • 屬性 value[]:用於指定其他配置類的字節碼
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;

    @Bean(name="runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier("ds") DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    @Bean(name="ds")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

2.6 總結

基於XML配置 基於註解配置
Bean定義 <bean id="..." class="..."/> @Component即衍生類@Repository、@Service、@Controller
Bean名稱 通過id或name指定 @Component("…")
Bean注入 <property>或者通過p命名空間 @Autowired按類型注入、@Qualifier按名稱注入
生命過程、Bean作用範圍 init-method、destroy-method;範圍scope屬性 @PostConstruct初始化、@PreDestroy銷燬、@Scope設置作用範圍
適合場景 Bean來自第三方,使用其他Bean Bean的實現類由用戶自己開發

3. Spring整合Junit

  • 導入座標

    <dependencies>
          <dependency>
              <groupId>org.springframework</groupId>
              <artifactId>spring-test</artifactId>
              <version>5.2.5.RELEASE</version>
          </dependency>
          <dependency>
              <groupId>junit</groupId>
              <artifactId>junit</artifactId>
              <version>4.12</version>
          </dependency>
      </dependencies>
    
  • 使用@RunWith註解替換原有運行器

    @RunWith(SpringJUnit4ClassRunner.class)
    public class AccountTest {}
    
  • 使用@ContextConfiguration指定spring配置文件的位置

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:bean.xml")
    public class AccountTest {}
    
  • 使用@Autowired給測試類中的變量注入數據

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(locations = "classpath:bean.xml")
    public class AccountTest {
    
        @Autowired
        private IAccountService as;
    }
    

完整的使用:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountTest {

    @Autowired
    private IAccountService as;

    @Test
    public void testFindAll(){
        List<Account> all = as.findAll();
        for (Account account : all) {
            System.out.println(account);
        }
    }
}

4. 基於完全註解的CURD

4.1 創建表

首先需要在數據庫中創建表,並插入數據:

create table account(
	id int primary key auto_increment,
	name varchar(40),
	money float
)character set utf8 collate utf8_general_ci;

insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);

使用select查詢:

mysql> select * from account;
+----+------+-------+
| id | name | money |
+----+------+-------+
|  1 | aaa  |  1000 |
|  2 | bbb  |  1000 |
|  3 | ccc  |  1000 |
+----+------+-------+
3 rows in set (0.00 sec)

4.2 創建工程

創建Maven工程,然後導入需要的pom.xml中導入需要的座標:

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>SpringAllAnnotationAccount</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>

    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.5.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.20</version>
        </dependency>
        <dependency>
            <groupId>commons-dbutils</groupId>
            <artifactId>commons-dbutils</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

</project>

4.3 創建實體類

創建對應於表的實體類,並實現序列化接口:

public class Account implements Serializable {

    private Integer id;
    private String name;
    private Float money;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public Float getMoney() {
        return money;
    }

    public void setMoney(Float money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}

4.4 持久層

public interface IAccountDao {

    List<Account> findAll();

    Account findById(Integer id);

    void saveAccount(Account account);

    void updateAccount(Account account);

    void deleteAccount(Integer id);
}
@Repository("accountDao")
public class IAccountDaoImpl implements IAccountDao {

    @Autowired
    private QueryRunner runner;

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

    public Account findById(Integer id) {
        try {
            return runner.query("select * from account where id=?", new BeanHandler<Account>(Account.class), id);
        }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 id) {
        try {
            runner.update("delete from account where id=?",id);
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

4.5 業務層

public interface IAccountService {
    List<Account> findAll();

    Account findById(Integer id);

    void saveAccount(Account account);

    void updateAccount(Account account);

    void deleteAccount(Integer id);
}

@Service("accountService")
public class AccountServiceImpl implements IAccountService {

    @Autowired
    private IAccountDao accountDao;

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

    public Account findById(Integer id) {
        return accountDao.findById(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);
    }
}

4.6 配置類

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;

    @Bean(name="runner")
    @Scope("prototype")
    public QueryRunner createQueryRunner(@Qualifier("ds") DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    @Bean(name="ds")
    public DataSource createDataSource(){
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        }catch (Exception e){
            throw new RuntimeException(e);
        }
    }
}

@ComponentScan("dyliang")
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}

其中配置文件jdbcConfig.properties內容爲:

jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/sql_store?serverTimezone=GMT
jdbc.username=root
jdbc.password=1234

4.7 單元測試

最後,編寫測試類並執行單元測試:

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

    @Autowired
    private IAccountService as = null;

    @Test
    public void testFindAll() {
        List<Account> accounts = as.findAll();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindOne() {
        Account account = as.findById(1);
        System.out.println(account);
    }

    @Test
    public void testSave() {
        Account account = new Account();
        account.setName("test anno");
        account.setMoney(12345f);
        as.saveAccount(account);

    }

    @Test
    public void testUpdate() {
        Account account = as.findById(4);
        account.setMoney(23456f);
        as.updateAccount(account);
    }

    @Test
    public void testDelete() {
        as.deleteAccount(4);
    }
}

t> accounts = as.findAll();
for(Account account : accounts){
System.out.println(account);
}
}

@Test
public void testFindOne() {
    Account account = as.findById(1);
    System.out.println(account);
}

@Test
public void testSave() {
    Account account = new Account();
    account.setName("test anno");
    account.setMoney(12345f);
    as.saveAccount(account);

}

@Test
public void testUpdate() {
    Account account = as.findById(4);
    account.setMoney(23456f);
    as.updateAccount(account);
}

@Test
public void testDelete() {
    as.deleteAccount(4);
}

}


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章