Spring系列第6篇:Spring的依赖注入之自动注入

一、Spring中IOC常用注解

1.用于创建对象

他们的作用和在xml中编写一个bean标签实现的功能是一样的
@Component作用:用于把当前类对象存入spring容器中(beans.xml配置约束context)

<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在创建容器的时候要扫描的包,配置conext名称空间和约束,在croe中搜索xmlns:context-->
<context:component-scan base-package="com.ming"></context:component-scan>

</beans>

value属性:用于指定bean的id,不写时默认是当前类名首字母小写
@Component衍生的注解:
@controller:一般用在表现层
@service:一般用在业务层
@Repository:一般用在持久层
这三个注解作用和属性与component一样,spring框架提供的明确的三层使用注解,使我们的三层对象更加清晰

2.用于注入数据的

他们的作用和在xml中的bean标签内写一个property标签作用是一样的
@Autowired作用:自动按照类型注入。先按类型找,然后按名称找。只要容器中有唯一的一个bean对象类型和注入的变量类型匹配,就可以注入成功
出现位置:变量和方法上
@Qualifier作用:按照类型注入的基础之上再按照名称注入。它在给类成员注入的时候不能单独使用必须和Autowired组合一起使用,在给方法参数注入的时候可以单独使用。
@Resource作用:按照名称注入,可以单独使用。先按名称找,然后按类型找
属性name:用于指定bean的id
总结:以上三个注解注入数据的时候只能注入bean类型的数据,而基本类型和String类型无法使用上述注解实现,另外集合类型的注入只能通过xml来实现
@value:注入基本数据类型和String类型的数据
属性:value用于指定数据的值,可以使用SpEL(EL表达式),spel写法:${}

3.用于改变作用范围的

他们的作用和在bean标签上使用scpoe属性是一样的
@scope作用:用于指定bean的作用范围
value:指定范围的取值 singleton prototype

4.生命周期相关的

他们的作用就和在bean标签中使用init-method和destroy-method是一样的
@PreDestroy作用用于销毁方法 @PostConstruct用于初始化方法

二、基于xml的IOC案例

1.引入依赖

持久层用的是dbutils

     <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.3.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.6.11</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.2.3.RELEASE</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>
    </dependencies>

mysql:创建表

CREATE TABLE account (
  id int PRIMARY KEY AUTO_INCREMENT,
  name varchar(40),
  money float
)CHARACTER SET utf8 COLLATE utf8_general_ci;

2.案例代码

创建业务层接口:

package com.ming.service;

import com.ming.model.Account;

import java.util.List;

/**
 * 账户业务层接口
 */
public interface IAccountService {
    /**
     * 查找所有的账户
     *
     * @return
     */
    List<Account> findAll();

    /**
     * 按照id查找账户
     *
     * @param id
     * @return
     */
    Account findById(Integer id);

    /**
     * 保存
     *
     * @param account
     */
    void saveAccount(Account account);

    /**
     * 更新
     *
     * @param account
     */
    void updateAccount(Account account);

    /**
     * 删除
     *
     * @param id
     */
    void deleteAccount(Integer id);

    /**
     * @param sourceName 转出账户
     * @param targetName 转入账户
     * @param money      金额
     */
    public void transfer(String sourceName, String targetName, Float money);

}

业务层实现类:

package com.ming.service.impl;

import com.ming.dao.IAccountDao;
import com.ming.model.Account;
import com.ming.service.IAccountService;
import com.ming.util.TransacationManger;

import java.util.List;

/**
 * 账户的业务层实现类
 */
public class AccountServiceImpl implements IAccountService {
    //最早的写法
    //private IAccountDao accountDao = new AccountDaoImpl();
    //BeanFactory读取配置的bean.properties的dao
    //private IAccountDao accountDao= (IAccountDao) BeanFactory.getBean("accountDao");
    private IAccountDao accountDao;
    private TransacationManger tx;

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

    public void setTx(TransacationManger tx) {
        this.tx = tx;
    }

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

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }

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

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

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

    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        Account sourceAccount = accountDao.findAccountByName(sourceName);
        sourceAccount.setMoney(sourceAccount.getMoney() - money);
        accountDao.updateAccount(sourceAccount);
        Account targetAccount = accountDao.findAccountByName(targetName);
        int a = 4 / 0;
        targetAccount.setMoney(targetAccount.getMoney() + money);
        accountDao.updateAccount(targetAccount);
    }


}

账户持久层接口:

package com.ming.dao;

import com.ming.model.Account;

import java.util.List;

/**
 * 账户的持久层接口
 */
public interface IAccountDao {

    List<Account> findAll();
    
    Account  findById(Integer id);

    void saveAccount(Account account);

    void updateAccount(Account account);

    void deleteAccount(Integer id);
	//根据账户名称查询账户,为了演示事物准备
    Account findAccountByName(String accountName);
}

账户持久层实现类:

package com.ming.dao.impl;
import com.ming.dao.IAccountDao;
import com.ming.model.Account;
import com.ming.util.ConnectionThredUtil;
import org.apache.commons.dbutils.QueryRunner;
import org.apache.commons.dbutils.handlers.BeanHandler;
import org.apache.commons.dbutils.handlers.BeanListHandler;
import java.util.List;
public class AccountDaoImpl implements IAccountDao {

    private QueryRunner queryRunner;
    private ConnectionThredUtil conn;

    public void setQueryRunner(QueryRunner queryRunner) {
        this.queryRunner = queryRunner;
    }

    public void setConn(ConnectionThredUtil conn) {
        this.conn = conn;
    }

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

    public Account findById(Integer id) {
        try {
            return queryRunner.query(conn.getThredConnection(), "select * from account where id = ? ", new BeanHandler<Account>(Account.class), id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void saveAccount(Account account) {
        try {
            queryRunner.update(conn.getThredConnection(), "insert into account(name,money) values(?,?)", account.getName(), account.getMoney());
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    public void updateAccount(Account account) {
        try {
            queryRunner.update(conn.getThredConnection(), "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 {
            queryRunner.update(conn.getThredConnection(), "delete account where id=?", id);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    @Override
    public Account findAccountByName(String accountName) {
        try {
            return queryRunner.query(conn.getThredConnection(), "select * from account where name = ? ", new BeanHandler<Account>(Account.class), accountName);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

bean.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">
    <!--  =============基于xml的IOC配置===============-->
    <!-- 配置service对象-->
    <bean id="accountService" class="com.ming.service.impl.AccountServiceImpl">
        <!-- 注入dao对象-->
        <property name="accountDao" ref="accountDao"></property>
    </bean>
    <!-- 配置dao对象-->
    <bean id="accountDao" class="com.ming.dao.impl.AccountDaoImpl">
        <!-- 注入queryRunner对象-->
        <property name="queryRunner" ref="runner"></property>
    </bean>
    <!-- 配置QueryRunner,它是单例对象所以配置作用域scope-->
    <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!--注入数据源没有提供set方法,只能用构造函数注入-->
        <constructor-arg name="ds" ref="dataSource"></constructor-arg>
    </bean>
    <!--注入数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!--注入连接数据库的必备信息-->
        <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test"></property>
        <property name="user" value="root"></property>
        <property name="password" value="root"></property>
    </bean>
</beans>

基于XML的IOC配置用Junit测试

package com.ming.test;

import com.ming.model.Account;
import com.ming.service.IAccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 * 基于xml的IOC配置使用Junit单元测试
 */
public class AccountServiceTest {
    @Test
    public void testFindAll() {
        //1.获取容器
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        //2.得到业务层对象
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        List<Account> accountList = accountService.findAll();
        System.out.println(accountList);
    }

    @Test
    public void testFindById() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        Account account = accountService.findById(1);
        System.out.println(account);
    }


    @Test
    public void testSaveAccount() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        Account account = new Account();
        account.setName("aaa");
        account.setMoney(1000f);
        accountService.saveAccount(account);
    }

    @Test
    public void testUpdateAccount() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        Account account = new Account();
        account.setName("aaa");
        account.setMoney(1000f);
        accountService.updateAccount(account);
    }

    @Test
    public void testDeleteAccount() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService = (IAccountService) context.getBean("accountService");
        accountService.deleteAccount(1);
    }

}

三、Spring的新注解

@configuration
作用:指定当前类是一个配置类 等同于bean.xml
细节:当配置作为AnnotationConfigApplictionContext创建对象的参数时,可以不写。
扫描包的时候首先会扫描这个包下的类,首先这个类要是一个配置类,才会去扫描这个类中的注解
@ComponentScan
作用:用于通过注解告知spring在创建容器时要扫描的包
属性value和basePackages可以互换:它和beans.xml中base-package的作用是一样的,都是用于指定创建容器时要扫描的包。
等同于: <context:component-scan base-package=“com.ming”></context:component-scan>
@Bean
作用在方法上,表示通过方法来定义一个bean,默认方法的名称作为bean的名称,将方法的返回值作为bean对象,注册到spring容器中
@import
用于导入其他配置类
@PropertySource
用于指定properties文件的位置
纯注解的方式和xml的方式比起来更费劲,尽量选择xml和注解结合使用。
属性value:用于指定文件名称和路径 关键字classpath:类路径下的文件
总结:如果是第三方jar用xml,本地代码用注解方便

1.基于注解的IOC

1.bean.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">
        <!--  =============基于注解的IOC配置===============-->
        <!--告知spring创建容器时要扫描的包-->
        <context:component-scan base-package="com.ming"></context:component-scan>
        <!-- 配置QueryRunner,它是单例对象所以配置作用域scope-->
        <bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
            <!--注入数据源没有提供set方法,只能用构造函数注入-->
            <constructor-arg name="ds" ref="dataSource"></constructor-arg>
        </bean>
        <!--注入数据源-->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <!--注入连接数据库的必备信息-->
            <property name="driverClass" value="com.mysql.jdbc.Driver"></property>
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3307/test"></property>
            <property name="user" value="liming"></property>
            <property name="password" value="liming"></property>
        </bean>
</beans>

总结:bean.xml中有两个配置没有用注解(第三方jar和component-scan),用@Bean注解解决。
Spring配置类:

package com.ming.config;

import com.ming.util.JdbcProConfig;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.PropertySource;

/**
 * 该类是一个配置类,它的作用和bean.xml是一样的
 */
@Configuration
@ComponentScan(basePackages = {"com.ming"})
@PropertySource("classpath:jdbc.properties")
@Import(value = JdbcProConfig.class)
public class SpringConfiguration {
    
}


当 @ComponentScan(basePackages = {"com.ming"})注解出现时,bean.xml中的 <context:component-scan base-package="com.ming"></context:component-scan>配置可以注释掉,因为作用是相同的。
jdbc.properties文件

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3307/test
user=liming
pwd=liming

@Import导入类:

package com.ming.util;

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;

/**
 * jdbc配置类,用于@import注解导入
 */
public class JdbcProConfig {
    @Value("${driver}")
    private String driver;

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

    @Value("${user}")
    private String user;

    @Value("${pwd}")
    private String pwd;

    /**
     * 该注解可以将一个方法的返回值注册到spring容器中
     * @param dataSource
     * @return
     */
    @Bean(name="queryRunner")
    @Scope("prototype")
    public QueryRunner getQueryRunner(DataSource dataSource){
        return  new QueryRunner(dataSource);
    }

    @Bean(name="dataSource")
    public  DataSource getDataSource() {
        try {
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass(driver);
            dataSource.setJdbcUrl(url);
            dataSource.setUser(user);
            dataSource.setPassword(pwd);
            return  dataSource;
        } catch (Exception e) {
            throw  new RuntimeException(e);
        }
    }
}


2.spring整合Junit

分析:
1.应用程序的入口 main方法
2.junit单元测试中,没有main方法也能执行,junit集成了main方法,该方法会判断当前类中哪些方法有@test注解,Junit就会让@test注解的方法执行
3.junit不会管我们是否采用spring框架,在执行测试方法的时候Junit根本不会知道我们使用了spring框架,所以也就不会为我们读取配置文件/配置类,也就不会为我们创建spring核心容器。
4。总结以上三点可知,当我们执行测试方法时,没有IOC容器,就算写了@AutoWired注解,也无法实现注入。
所以在执行test方法时,要创建spring核心容器。
spring整合Junit的配置:
1.导入spring整合Junit的jar(spring-test)
2.使用spring提供的一个注解把原有的main方法替换了,替换成spring提供的@RunWith(SpringJUnit4ClassRunner.class)
3.告知spring的运行器,spring的IOC创建时基于xml还是注解的,并说明位置 locations指定xml的文件位置 classes指定注解类所在的位置@ContextConfiguration(classes = SpringConfiguration.class)

package com.ming.test;

import com.ming.config.SpringConfiguration;
import com.ming.model.Account;
import com.ming.service.IAccountService;
import com.ming.util.AccountProxy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfiguration.class)
public class AccountTest {
    @Autowired
    private IAccountService accountService;

    @Test
    public void addTest() {
        Account account = new Account();
        account.setName("ddd");
        account.setMoney(1000f);
        accountService.saveAccount(account);
    }

    @Test
    public void findAllTest() {
        List<Account> li = accountService.findAll();
        System.out.println(li);
    }

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值