SSM框架之Spring(3)IOC及依赖注入(基于注解的实现)

Spring(3)IOC及依赖注入(基于注解的实现)

学习基于注解的 IoC 配置,大家脑海里首先得有一个认知,即注解配置和 xml 配置要实现的功能都是一样
的,都是要降低程序间的耦合。只是配置的形式不一样。

关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌
握。

环境配置

bean.xml的配置


<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="domain">context:component-scan>
beans>

对具体类的配置

package domain.service.impl;

import domain.service.IAccountService;
import domain.dao.IAccountDao;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.annotation.Resource;

@Service("accountService")
@Scope
public class AccountServiceImpl implements IAccountService {
//    @Autowired
//    @Qualifier("accountDao1")
    @Resource(name = "accountDao1")
    private IAccountDao accountDao = null;


    public AccountServiceImpl() {
        System.out.println("对象创建成功");
//        System.out.println("a = "+ a);
    }

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

    @PostConstruct
    public void init() {
        System.out.println("初始化。。");
    }

    @PreDestroy
    public void destory() {
        System.out.println("销毁方法。。");
    }
}

1、常用注解

<bean id="accountService" class="domain.service.impl.AccountServiceImpl" scope="singleton" init-method="" destroy-method="">
    <property name="" value="" ref="">property>
 bean>

1.1、用于创建对象的

作用等同于xml配置中的bean标签

<bean id="" class="">bean>

1.1.1、Component

作用:把被注解的类创建对象后放入spring容器中
属性:
value:指定当前的bean的id,如果不写那么他的默认值是被注解的类的类名,且首字母小写

@Service("accountDao1")
public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("保存账户成功!!!11111111111");
    }
}

1.1.2、Controller,Service,Repository

以上三个注解的作用和属性与Component完全相同,
它们是spring提供的三层架构的注解,使三层对象更加清晰

@Controller :一般用于表现层的注解。
@Service :一般用于业务层的注解。
@Repository :一般用于持久层的注解。

细节:如果注解中有且只有一个属性 要赋值时是 ,且名称是 value ,value 在赋值是可以不写。

1.2、用于注入数据

作用等同于xml中的bean标签下的properties标签

<bean id="" class="">
    property>
bean>

1.2.1、@Autowired

作用:自动按照类型注入,只要有唯一的一个bean对象与要注入的类型匹配,就可以注入成功
如果IOC容器中没有与要注入变量同类型的bean,则报错
如果IOC容器中有多个与之类型匹配,在不使用其他注解的情况下,依据要注入变量的名称和bean对象的id进行比较,如果变量名与id相同就可以注入,如果无匹配id则报错

出现位置:
变量和方法上都可以出现

细节:使用注解注入时,set方法非必须

@Autowired
private IAccountDao accountDao = null;

1.2.2、Qualifier:

作用:
在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和
@Autowire 一起使用;但是给方法参数注入时,可以独立使用。

属性:
value:指定 bean 的 id。

@Autowired
@Qualifier("accountDao1")
private IAccountDao accountDao = null;

1.2.3、@Resource

作用:
直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。

属性:
name:指定 bean 的 id。

@Resource(name = "accountDao1")
private IAccountDao accountDao = null;

1.2.4、@Value

作用:
注入基本数据类型和 String 类型数据的

属性:
value:用于指定数据的值。它可以使用spring中的spEl(spring中的EL表达式)

spEL写法:${表达式}

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

1.3、用于改变作用范围的

作用等同于xml配置中bean标签的scope属性

@Service("accountDao1")
@Scope("prototype")
public class AccountDaoImpl implements IAccountDao {
    public void saveAccount() {
        System.out.println("保存账户成功!!!11111111111");
    }
}

1.3.1、 @Scope

作用:
指定 bean 的作用范围。
属性:
value:指定范围的值。
取值:singleton prototype request session globalsession

1.4、改变作用范围

作用等同于xml配置中的init-method和destory-method属性

@PostConstruct
public void init() {
    System.out.println("初始化。。");
}

@PreDestroy
public void destory() {
    System.out.println("销毁方法。。");
}

1.4.1、@PostConstruct

作用:
用于指定初始化方法。== init-method

1.4.2、@PreDestroy

作用:
用于指定销毁方法。==destory-method

1.5、关于 Spring 注解和 XML

注解的优势:
配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。适用于自己定义的类
XML 的优势:
修改时,不用改源码。不涉及重新编译和部署。适用于通过jar导入的类

2、spring 的纯注解配置

写到此处,基于注解的 IoC 配置已经完成,但是大家都发现了一个问题:我们依然离不开 spring 的 xml 配
置文件,那么能不能不写这个 bean.xml,所有配置都用注解来实现呢?

2.1、问题

之所以我们现在离不开 xml 配置文件,是因为我们有一句很关键的配置:


<context:component-scan base-package="com.itheima">context:component-scan>

如果他要也能用注解配置,那么我们就离脱离 xml 文件又进了一步。
另外,数据源和 JdbcTemplate 的配置也需要靠注解来实现。


<bean id="dbAssit" class="com.itheima.dbassit.DBAssit">
<property name="dataSource" ref="dataSource">property>
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:///spring_day02">property>
<property name="user" value="root">property>
<property name="password" value="1234">property>
bean>

2.2、新注解说明

2.2.1、@Configuration

作用:
用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用
AnnotationApplicationContext(有@Configuration 注解的类.class)。

属性:
value:用于指定配置类的字节码

细节:当配置类作为AnnotationConfigApplicationContext对象创建容器的参数该注解可以省略

@Configuration
public class SpringCinfiguration {

}

注意:
我们已经把配置文件用类来代替了,但是如何配置创建容器时要扫描的包呢?
请看下一个注解。

2.2.2、@ComponentScan

作用:通过注解指定spring容器在创建时要扫描的包
属性:value:和basePackages作用相同,都是指定要创建容器要扫描的包
该注解作用相当于在xml中配置的:

     <context:component-scan base-package="wf">
@Configuration
@ComponentScan(basePackages = {"wf","config"})//wf,config是包名
public class SpringCinfiguration {

}

注意:
我们已经配置好了要扫描的包,但是数据源和 JdbcTemplate 对象如何从配置文件中移除呢?
请看下一个注解。

2.2.3、@Bean

作用:把当前方法的返回值作为bean对象存入spring的ioc容器中

​ 属性:
​ name:用于指定bean对象的id,当不写时默认是当前方法名

​ 细节:
​ 当我们使用注解配置方法时,如果方法有参数,sprig框架就会去容器中查找看是否由可用bean对象
查找方式和Autowired注解的作用一样

@Bean(name = "runner")
public QueryRunner creatQueryRunner(@Qualifier("ds1") DataSource dataSource){
    return new QueryRunner(dataSource);
}

注意:
我们已经把数据源和 DBAssit 从配置文件中移除了,此时可以删除 bean.xml 了。
但是由于没有了配置文件,创建数据源的配置又都写死在类中了。如何把它们配置出来呢?
请看下一个注解。

2.2.4、@PropertySource

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql:///spring
jdbc.username=root
jdbc.password=123456

作用:

用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到
properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。

属性:
value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上 classpath:

@Configuration
@PropertySource("classpath:jdbc.properties")
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;
}

注意:
此时我们已经有了两个配置类,但是他们还没有关系。如何建立他们的关系呢?
请看下一个注解。

2.2.5、@Import

作用:用于导入其他的配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然,写上也没问
题。

属性:
value[]:用于指定其他配置类的字节码。

@Configuration
@ComponentScan(basePackages = {"wf","config"})
@Import({JdbcConfig.class})
public class SpringCinfiguration {

}

注意:
我们已经把要配置的都配置好了,但是新的问题产生了,由于没有配置文件了,如何获取容器呢?
请看下一小节。

2.2.6、通过注解获取容器:

AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);

3、Spring 整合 Junit

3.1、问题

在测试类中,每个测试方法都有以下两行代码:
ApplicationContext ac = new ClassPathXmlApplicationContext(“bean.xml”);
IAccountService as = ac.getBean(“accountService”,IAccountService.class);
这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。

3.2、解决思路分析

针对上述问题,我们需要的是程序能自动帮我们创建容器。一旦程序能自动为我们创建 spring 容器,我们就无须手动创建了,问题也就解决了。

我们都知道,junit 单元测试的原理(在 web 阶段课程中讲过),但显然,junit 是无法实现的,因为它自己都无法知晓我们是否使用了 spring 框架,更不用说帮我们创建 spring 容器了。不过好在,junit 给我们暴露了一个注解,可以让我们替换掉它的运行器。

这时,我们需要依靠 spring 框架,因为它提供了一个运行器,可以读取配置文件(或注解)来创建容器。我们只需要告诉它配置文件在哪就行了。

3.3 、 配置步骤

3.3.1、导入相关坐标

<dependency>
    <groupId>org.springframeworkgroupId>
    <artifactId>spring-testartifactId>
    <version>5.1.6.RELEASEversion>
dependency>

3.3.2、使用@RunWith 注解替换原有运行器

@RunWith(SpringJUnit4ClassRunner.class)
public class AccountServiceTest {}

3.3.3、使用@ContextConfiguration 指定 spring 配置文件的位置

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringCinfiguration.class)
public class AccountServiceTest {}

@ContextConfiguration 注解:
locations 属性:用于指定配置文件的位置。如果是类路径下,需要用 classpath:表明
classes 属性:用于指定注解的类。当不使用 xml 配置时,需要用此属性指定注解类的位置。

3.3.4、使用@Autowired

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringCinfiguration.class)
public class AccountServiceTest {

    @Autowired
    private IAccountService as;
}

完整代码

package test;

import config.SpringCinfiguration;
import org.apache.commons.dbutils.QueryRunner;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import wf.domain.Account;
import org.junit.Test;
import wf.service.IAccountService;

import java.util.List;

/**
 * 使用junit进行单元测试
 * spring整合junit的配置
 *  1.导入spring整合junit的jar
 *  2.
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringCinfiguration.class)
public class AccountServiceTest {

    @Autowired
    private IAccountService as;
    @Test
    public void testQuery(){
        //1.加载配置文件
        AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
        //2.获取服务层对象
        QueryRunner as = ca.getBean("runner", QueryRunner.class);
        QueryRunner as1 = ca.getBean("runner", QueryRunner.class);
        System.out.println(as ==as1);

    }
    @Test
    public void testFindAll(){
       /* //1.加载配置文件
        //, JdbcConfig.class
        AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
        //2.获取服务层对象
        IAccountService as = ca.getBean("accountService", IAccountService.class);*/
        //3.根据对象执行方法
        List<Account> accounts = as.findAllAccount();
        for (Account account:accounts){
            System.out.println(account);
        }
    }

    @Test
    public void testFindById(){
       /* //1.加载配置文件
        AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
        //2.获取服务层对象
        IAccountService as = ca.getBean("accountService", IAccountService.class);*/
        //3.根据对象执行方法
        Account a = as.findOneById(3);
        System.out.println(a);
    }

    @Test
    public void testSave(){
        Account account = new Account();
        account.setName("gx");
        account.setMoney(1234.0f);
        /*//1.加载配置文件
        AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
        //2.获取服务层对象
        IAccountService as = ca.getBean("accountService", IAccountService.class);*/
        //3.根据对象执行方法
        as.saveAccount(account);
    }

    @Test
    public void testUpdate(){
        Account account = new Account();
        account.setId(4);
        account.setName("wf");
        account.setMoney(1234.0f);
       /* //1.加载配置文件
        AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
        //2.获取服务层对象
        IAccountService as = ca.getBean("accountService", IAccountService.class);*/
        //3.根据对象执行方法
        as.updateAccount(account);
    }
    @Test
    public void testDelete(){
       /* //1.加载配置文件
        AnnotationConfigApplicationContext ca = new AnnotationConfigApplicationContext(SpringCinfiguration.class);
        //2.获取服务层对象
        IAccountService as = ca.getBean("accountService", IAccountService.class);*/
        //3.根据对象执行方法
        as.deleteAccount(8);
    }
}

也可以在xml配置文件项目中使用此方法整合junit

你可能感兴趣的:(SSM框架之Spring(3)IOC及依赖注入(基于注解的实现))