前言

上文说道Spring IOC思想:

Spring的IOC容器通过读取spring配置文件,可以帮助我们创建各种各样的对象,以实现调用方和被调用方在编译阶段的解耦 ,这些由Spring IOC创建出来的对象统称为Bean;

当我们把通过spring配置文件把需要的bean放到spring的IOC容器之后,如果这些容器中对象需要调用容器中其他对象,应该如何操作呢?  

这就需要spring的依赖注入技术,容器+依赖注入(Dependency injection)实现Spring IOC思想:

当1个bean对象的控制权由调用方转交给spring的IOC容器之后;

我们可以通过Spring的xml配置文件或者注解,完成当前bean对象的依赖注入,去调用其他bean对象;

 

一、Bean的作用域

在Spring中,bean支持多种作用域如下所示,我们可以通过配置文件和注解进行配置;

  • singleton(默认) 单例模式:即对象创建1次,创建之后一直存在,除非Spring IOC容器关闭;
  • prototype 多例模式:即每次获取bean的时候,IOC都给我们创建一个新对象
  • request域中生效:Spring创建一个Bean的对象,将对象存入到request域中
  • session域中生效:Spring创建一个Bean的对象,将对象存入到session域中

 1.Bean单例模式配置

<bean scope="singleton" id="UserDao" class="com.zhanggen.dao.impl.UserDaoImpl">

 2.Bean多例模式配置

<bean scope="prototype" id="UserDao" class="com.zhanggen.dao.impl.UserDaoImpl">

 

 

二、Bean的生命周期

生命周期指得是1个事物,从创建到销毁的过程; 

在Spring中,bean的作用范围会影响到其生命周期,所以我们要分单例和多例对象来研究bean的生命周期

init-method:指定的方法在当前对象(bean)创建之后执行

destroy-method指定的方法在当前对象(bean)销毁之前执行

 <bean init-method="UserDao" destroy-method="destory" id="UserDao" class="com.zhanggen.dao.impl.UserDaoImpl">

 

1.单例模式下bean的生命周期

创建:在容器创建的时候

销毁:在容器销毁的时候

2.多例模式下bean的生命周期

创建:每次调用getBean获取对象的时候

销毁:Spring不知道多例模式下,对象什么时候销毁;(bean创建之后Spring IOC容器就把对象交出去了,此时Spring IOC容器不再拥有bean的控制权);

 

三、Bean的依赖注入

依赖注入(Dependency Injection,DI) 其实就是给对象(bean)中的属性赋值

以下两种方式,都可以实现依赖注入,使用原则是能用set方法进行依赖注入尽量不使用构成函数

  • 使用构造函数:如果类是别人写的,没有set方法时使用;
  • set方法:          推荐使用

1.通过构造函数进行依赖注入

在bean标签中嵌套constructor-arg标签可以完成bean对象构造函数赋值; 

1.1.在类中添加有参构造

public class UserDaoImpl implements UserDao {
    private String name;
    private Integer age;
    private Date brithday; //对象属性

    //添加无参构造器
    public UserDaoImpl() {

    }

    //添加全参构造器
    public UserDaoImpl(String name, Integer age, Date brithday) {
        this.name = name;
        this.age = age;
        this.brithday = brithday;
    }

1.2.通过配置文件配置属性的值

 <!--相当于创建了1个对象Date brithday=new Date(); -->
    <bean id="date" class="java.util.Date"></bean>
    <!--
    constructor-arg:构造函数完成依赖注入
    name="name":指定构造器中形参的名称
    value="张根":指定给当前属性赋的值【简单类型:基本类型+基本类型包装类+String】
    vref="张根":指定给当前属性赋的值【引用类型】

    -->
    <bean id="UserDao" class="com.zhanggen.dao.impl.UserDaoImpl">
        <constructor-arg name="name" value="张根"></constructor-arg>
        <constructor-arg name="age" value="18"></constructor-arg>
        <constructor-arg name="brithday" ref="date"></constructor-arg>
    </bean>

1.3.测试

UserDaoImpl{name='张根', age=18, brithday=Wed May 18 21:08:06 CST 2022}

 

2.通过set方法进行依赖注入

在bean标签中嵌套property 标签可以完成bean对象构造函数赋值; 

2.1.在类中提供属性的set方法

//在类中提供属性的set()方法
    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBrithday() {
        return brithday;
    }

    public void setBrithday(Date brithday) {
        this.brithday = brithday;
    }

2.2.在配置文件中设置属性的值

  <!--相当于创建了1个对象Date brithday=new Date(); -->
    <bean id="date" class="java.util.Date"></bean>
    <!--
    property:set方法完成依赖注入
    name="name":指定构造器中形参的名称
    value="张根":指定给当前属性赋的值【简单类型:基本类型+基本类型包装类+String】
    vref="张根":指定给当前属性赋的值【引用类型】
    -->
    <bean id="UserDao" class="com.zhanggen.dao.impl.UserDaoImpl">
        <property name="name" value="张根"></property>
        <property name="age" value="18"></property>
        <property name="brithday" ref="date"></property>
    </bean>

2.3.测试

UserDaoImpl{name='张根', age=18, brithday=Wed May 18 21:27:22 CST 2022}

 

3.注入集合属性

在property 标签嵌套list、set、array、map标签,可以对bean对象的集合属性赋值;

3.1.增加集合复杂属性

package com.zhanggen.dao.impl;

import com.zhanggen.dao.UserDao;

import java.util.*;

public class UserDaoImpl implements UserDao {
    //简单类型属性
    private String name;
    private Integer age;
    //对象属性
    private Date brithday;
    //集合复杂属性
    private Set<String> mySet;
    private List<String> myList;
    private String[] myArr;
    private Map<String, String> myMap;

    //在类中提供属性的set()方法
    public String getName() {
        return name;
    }

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

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public Date getBrithday() {
        return brithday;
    }

    public void setBrithday(Date brithday) {
        this.brithday = brithday;
    }

    public Set<String> getMySet() {
        return mySet;
    }

    public void setMySet(Set<String> mySet) {
        this.mySet = mySet;
    }

    public List<String> getMyList() {
        return myList;
    }

    public void setMyList(List<String> myList) {
        this.myList = myList;
    }

    public String[] getMyArr() {
        return myArr;
    }

    public void setMyArr(String[] myArr) {
        this.myArr = myArr;
    }

    public Map<String, String> getMyMap() {
        return myMap;
    }

    public void setMyMap(Map<String, String> myMap) {
        this.myMap = myMap;
    }

    public void save() {
        System.out.println("用户保存成功");
    }

    @Override
    public String toString() {
        return "UserDaoImpl{" +
                "name='" + name + '\'' +
                ", age=" + age +
                ", brithday=" + brithday +
                ", mySet=" + mySet +
                ", myList=" + myList +
                ", myArr=" + Arrays.toString(myArr) +
                ", myMap=" + myMap +
                '}';
    }
}
UserDaoImpl.java

3.2.配置文件赋值

   <bean id="UserDao" class="com.zhanggen.dao.impl.UserDaoImpl">
        <property name="name" value="张根"></property>
        <property name="age" value="18"></property>
        <property name="brithday" ref="date"></property>
        <property name="myList">
            <list>
                <value>林心如</value>
                <value>范冰冰</value>
            </list>
        </property>
        <property name="mySet">
            <set>
                <value>CC</value>
                <value>DD</value>
            </set>
        </property>
        <property name="myArr">
            <array>
                <value>奔驰</value>
                <value>宝马</value>
            </array>
        </property>
        <property name="myMap">
            <map>
                <entry key="key1" value="18"></entry>
                <entry key="key2" value="19"></entry>
            </map>
        </property>
    </bean>

3.3.测试


UserDaoImpl{name='张根', age=18, brithday=Wed May 18 21:50:56 CST 2022, mySet=[CC, DD], myList=[林心如, 范冰冰], myArr=[奔驰, 宝马], myMap={key1=18, key2=19}}

 

 

四、多配置文件使用

我们现在的配置都集中配在了一个applicationContext.xml文件中,这样会使这个文件很难维护。

针对这个问题,Spring给我们提供了两种解决方案:

  • 在创建springIOC容器的时,同时引入多个配置文件
  • 在主配置文件中引入其他配置文件

 注意:

  • 同一个xml文件中不允许出现相同id的bean,如果出现会报错
  • 多个xml文件如果出现相同id的bean,不会报错,但是后加载的会覆盖前加载,所以尽量保证bean的名称是唯一的

1.同时引入多个配置文件

在创建spring IOC容器时一次读取多个配置文件

 //1.读取多个配置文件,初始化SpringIOC容器
 ClassPathXmlApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml","dateBean.xml");

 

2.引入1个主配置文件

spring IOC使用1个主配置文件,在主配置文件中引入其他配置文件

<!--引入其他配置文件-->
<import resource="dateBean.xml"></import>

 

 五、Spring IOC实现service和dao层(配置文件版本)

 通过配置文件配置spring的IOC容器,实现service层和dao层解耦;

 

1.添加依赖

<?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">
    <parent>
        <artifactId>spring</artifactId>
        <groupId>com.zhanggen</groupId>
        <version>1.0-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>day02-01-jt</artifactId>
    <dependencies>
        <!--spring核心-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
        <!--mysql-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>
        <!--druid-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.15</version>
        </dependency>

        <!--jdbcTemplate依赖所在-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>

        <!--lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.20</version>
        </dependency>

        <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
        </dependency>
    </dependencies>


</project>
pom.xml

2.定义dao层接口

package com.zhanggen.dao;

import com.zhanggen.domain.Account;

import java.util.List;

public interface AccountDao {
    //保存
    void save(Account account);

    //查询所有
    List<Account> queryAll();

    //查询1条记录(根据name)
    Account queryByName(String name);

    //修改(根据name修改余额balance)
    void update(Account account);

    //删除(根据主键删除)
    void delete(Integer aid);
}
AccountDao.interface

3.创建dao层实现类

package com.zhanggen.dao.impl;

import com.zhanggen.dao.AccountDao;
import com.zhanggen.domain.Account;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;

import java.util.List;

public class AccountDaoImpl implements AccountDao {
    //Spring的思想:需要对象了不能自己new了,而是向Spring 要1个bean
    private JdbcTemplate jdbcTemplate;//=new JdbcTemplate();

    //期望Spring依赖注入
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public void save(Account account) {
        jdbcTemplate.update("insert into account values (null,?,?)", account.getName(), account.getBalance());
    }

    @Override
    public List<Account> queryAll() {
        List<Account> accountList = jdbcTemplate.query("select * from account;", new BeanPropertyRowMapper<Account>(Account.class));
        return accountList;
    }

    @Override
    public Account queryByName(String name) {
        Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);
        return account;
    }

    @Override
    public void update(Account account) {
        jdbcTemplate.update("update account set balanc=? where name=?", account.getBalance(), account.getName());
    }

    @Override
    public void delete(Integer aid) {
        jdbcTemplate.update("delete from account where aid=?", aid);

    }
}
AccountDaoImpl.java

4.定义service层接口

service层不对dao层数据进行任何数据加工, 所以和dao层接口一致;

package com.zhanggen.serive;

import com.zhanggen.domain.Account;

import java.util.List;

public interface AccountService {
    //保存
    void save(Account account);

    //查询所有
    List<Account> queryAll();

    //查询1条记录(根据name)
    Account queryByName(String name);

    //修改(根据name修改余额balance)
    void update(Account account);

    //删除(根据主键删除)
    void delete(Integer aid);
}
AccountService.interface

5.创建service层实现类

package com.zhanggen.serive.impl;

import com.zhanggen.dao.AccountDao;
import com.zhanggen.domain.Account;
import com.zhanggen.serive.AccountService;

import java.util.List;

public class AccountServiceImpl implements AccountService {
    private AccountDao accountDao;

    //Spring注入
    public void setAccountDao(AccountDao accountDao) {
        this.accountDao = accountDao;
    }

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

    @Override
    public List<Account> queryAll() {
        List<Account> accountList = accountDao.queryAll();
        return accountList;
    }

    @Override
    public Account queryByName(String name) {
        Account account = accountDao.queryByName(name);
        return account;
    }

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

    @Override
    public void delete(Integer aid) {
        accountDao.delete(aid);
    }
}
AccountServiceImpl.java

6.加入spring的配置文件

就是到service层的实现类对象、dao层实现类对象,以及相关依赖都放到spring的IOC容器中,由spring的IOC容器进行管理;

<?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"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                         http://www.springframework.org/schema/context
                         http://www.springframework.org/schema/context/spring-context-3.0.xsd">


    <!--
    druidDataSource对象放到容器
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
    druidDataSource.setUrl("jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8");
    druidDataSource.setUsername("zhanggen");
    druidDataSource.setPassword("123.com");
    -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8"></property>
        <property name="username" value="zhanggen"></property>
        <property name="password" value="123.com"></property>
    </bean>
    <!--
    jdbcTemplate对象放到容器
      JdbcTemplate jdbcTemplate = new JdbcTemplate(druidDataSource);
    -->
    <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="druidDataSource"></constructor-arg>
        <!--<property name="dataSource" ref="druidDataSource"></property>-->
    </bean>
    <!--dao对象放入容器-->
    <bean id="accountDao" class="com.zhanggen.dao.impl.AccountDaoImpl">
        <property name="jdbcTemplate" ref="JdbcTemplate"></property>
    </bean>

    <!--
        servicl对象放到spring容器
        AccountService accountService=new AccountServiceImpl();
        accountService.setAccountDao(accountDao);
        -->
    <bean id="accountService" class="com.zhanggen.serive.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

</beans>
applicationContext.xml

7.测试

创建spring的IOC容器,从spring的IOC容器中获取service层对象;

package com.zhanggen.test;

import com.zhanggen.domain.Account;
import com.zhanggen.serive.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class AccountServiceTest {
    @Test
    public void testQueryAll() {
        //1.初始化Spring的IOC容器
        ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从Spring的IOC容器中获取AccountService的实现类对象
        AccountService accountService = act.getBean(AccountService.class);
        List<Account> accountList = accountService.queryAll();
        for (Account account : accountList) {
            System.out.println(account);
        }
    }
}
AccountServiceTest.java

 

 

 五、常用注解

首先明确注解就是对原来XML配置的简化,2者实现的功能是一样的。

1.对象(bean)创建注解

以下注解用于在spring的IOC容器中创建对象(bean);

1.1.@Component注解

用于实例化对象,相当于配置文件中的<bean id="" class=""/>
它支持一个属性value,相当于xml中bean的id,如果不写,默认值为类名的首字母小写

1.2.@Controller @Service @Repository

这3个注解的功能跟@Component类似,他们分别标注在不同的层上。

  • @Controller 标注在表示层的类上
  • @Service 标注在业务层的类上
  • @Repository 标注在持久层的类上

推荐使用这三个,当一个类实在不好归属在这三个层上时,再使用@Component

 

2.bean的作用域

@Scope用于指定bean的作用范围(单例和多例),相当于配置文件中的<bean scope="">

@Repository
@Scope("singleton")
public class AccountDaoImpl implements AccountDao {

 

3.bean的生命周期注解

@PostConstruct:此注解标注的方法会在当前对象创建之后自动执行

@PreDestroy:此注解标注的方法会在当前对象销毁之前自动执行

相当于<bean init-method="init" destroy-method="destory" />

//标注在类上表示使用AccountDaoImpl实现类创建1个对象放入spring的IOC容器里面
//@Component("accountDao") //定义当前对象在spring的IOC容器中表示,相当于xml的id,如果不声明id默认为当前类名首字母变小写
@Repository
@Scope("singleton")
public class AccountDaoImpl implements AccountDao {

    public void save() {
        System.out.println("保存成功了.....");
    }
    @PostConstruct//此注解标注的方法会在当前对象创建之后自动执行
    public void init() {
        System.out.println("对象创建完毕了.....");
    }
    @PreDestroy //此注解标注的方法会在当前对象销毁之前自动执行
    public void destory() {
        System.out.println("对象即将销毁.....");
    }

}

 

4.对象依赖的注入

其实大部分注入的依赖都是对象,如1个对象(bean)中的属性是另1个对象时,就需要进行对象依赖注入;

4.1. @Autowired

这个注解表示依赖注入,可以标注在属性上,也可以标注在方法上,当@Autowired标注在属性上的时候,属性对应的set方法可以省略不写

4.2. @Qualifier

跟@Autowired联合使用,代表在按照类型匹配的基础上,再按照名称匹配

4.3.自动注入依赖原理

@Autowired:Spring会在他的IOC容器中按照被标注属性的类型进行寻找,如果查找到了一个就进行注入,如果查找不到或者查找到多个就报错

@Qualifier:   跟@Autowired联合使用,代表在按照类型匹配的基础上,再按照名称匹配

 

5.注解总结

spring容器是IOC思想的载体和体现;

@Component、@Controller/@Service/@Repository是把当前对象放到spring容器中;

@Autowired、@Qualifier是指定当前bean对象,需要依赖哪些spring的容器中bean对象;

getBean():1个对象的所有属性赋值(注入)成功之后,再向spring的IOC容器要出这个对象,调用该对象的方法;

xml配置注解配置说明
< bean id="" class="" > @Component、@Controller/@Service/@Repository bean的实例化
< property name="" ref=""> @Autowired、@Qualifier bean的对象属性注入
< property name="" value=""> @Value bean的简单属性注入
< bean scope=""> @Scope 控制bean的作用范围
< bean init-method="init" destroy-method="destory" /> @PostConstruct、@PreDestroy bean创建之后和销毁之前调用

 

6.常用注解实现service和dao层

我们自己写的类(service层和dao层实现类)使用注解配置, 第三方的类(JdbcTemplate和Druid类对象)继续使用xml配置,实现web应用的service和dao层;

6.1.spring配置文件

<?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-3.0.xsd
                         http://www.springframework.org/schema/context
                         http://www.springframework.org/schema/context/spring-context-3.0.xsd">
    <!--注解扫描-->
    <context:component-scan base-package="com.zhanggen"></context:component-scan>

    <!--
    druidDataSource对象放到容器
    DruidDataSource druidDataSource = new DruidDataSource();
    druidDataSource.setDriverClassName("com.mysql.jdbc.Driver");
    druidDataSource.setUrl("jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8");
    druidDataSource.setUsername("zhanggen");
    druidDataSource.setPassword("123.com");
    -->
    <bean id="druidDataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8"></property>
        <property name="username" value="zhanggen"></property>
        <property name="password" value="123.com"></property>
    </bean>
    <!--
    jdbcTemplate对象放到容器
      JdbcTemplate jdbcTemplate = new JdbcTemplate(druidDataSource);
    -->
    <bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <constructor-arg name="dataSource" ref="druidDataSource"></constructor-arg>
        <!--<property name="dataSource" ref="druidDataSource"></property>-->
    </bean>

</beans>
applicationContext.xml

6.2.dao层

package com.zhanggen.dao.impl;

import com.zhanggen.dao.AccountDao;
import com.zhanggen.domain.Account;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.List;
@Repository
public class AccountDaoImpl implements AccountDao {
    //Spring的思想:需要对象了不能自己new了,而是向Spring 要1个bean
    @Autowired
    private JdbcTemplate jdbcTemplate;//=new JdbcTemplate();

    @Override
    public void save(Account account) {
        jdbcTemplate.update("insert into account values (null,?,?)", account.getName(), account.getBalance());
    }

    @Override
    public List<Account> queryAll() {
        List<Account> accountList = jdbcTemplate.query("select * from account;", new BeanPropertyRowMapper<Account>(Account.class));
        return accountList;
    }

    @Override
    public Account queryByName(String name) {
        Account account = jdbcTemplate.queryForObject("select * from account where name=?", new BeanPropertyRowMapper<Account>(Account.class), name);
        return account;
    }

    @Override
    public void update(Account account) {
        jdbcTemplate.update("update account set balanc=? where name=?", account.getBalance(), account.getName());
    }

    @Override
    public void delete(Integer aid) {
        jdbcTemplate.update("delete from account where aid=?", aid);

    }
}
AccountDaoImpl.java

6.3.service层

package com.zhanggen.serive.impl;

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

import java.util.List;

//把AccountServiceImpl对象放到spring的容器中
@Service
public class AccountServiceImpl implements AccountService {
    //去spring的容器中自动寻找AccountDao类型的bean进行依赖注入
    @Autowired
    private AccountDao accountDao;

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

    @Override
    public List<Account> queryAll() {
        List<Account> accountList = accountDao.queryAll();
        return accountList;
    }

    @Override
    public Account queryByName(String name) {
        Account account = accountDao.queryByName(name);
        return account;
    }

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

    @Override
    public void delete(Integer aid) {
        accountDao.delete(aid);
    }
}
AccountServiceImpl.java

6.4.测试

package com.zhanggen.test;

import com.zhanggen.domain.Account;
import com.zhanggen.serive.AccountService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

public class AccountServiceTest {
    @Test
    public void testQueryAll() {
        //1.初始化Spring的IOC容器
        ApplicationContext act = new ClassPathXmlApplicationContext("applicationContext.xml");
        //2.从Spring的IOC容器中获取AccountService的实现类对象
        AccountService accountService = act.getBean(AccountService.class);
        List<Account> accountList = accountService.queryAll();
        for (Account account : accountList) {
            System.out.println(account);
        }
    }
}
AccountServiceTest.java

 

  

六、纯注解

随着SpringBoot的兴起,纯注解方式也变得越来越重要;

纯注解就是将xml中的所有 配置项、自己写的类和第三方提供类,全部使用注解实现对象的实例化,并放到spring的容器中 。

@ComponentScan注解作用:扫描指定注解的类注册到IOC容器中;

@ComponentScan用于类或接口上主要是指定扫描路径,spring会把指定路径下带有指定注解的类注册到IOC容器中。

会被自动装配的注解包括@Controller、@Service、@Component、@Repository等等。

其作用等同于<context:component-scan base-package="com.maple.learn" />配置。

 

1.创建注解配置类

将原来spring配置文件中配置全部转换成1个Java类,这个Java类叫spring 配置类;

xml中的bean标签,转换成方法;

xml中非bean标签(如ComponentScan注解扫描),转换成方法的注解;

package com.zhanggen.config;

import com.alibaba.druid.pool.DruidAbstractDataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//把spring配置中的配置内容,全部转换到当前配置类
/*
bean标签---->方法
非bean标签(例如ComponentScan注解扫描)---->方法的注解
*/
@ComponentScan("com.zhanggen") //注解扫描
public class SpringConfig {
    /*
    编写1个方法,在方法中去创建对应对象,并且通过@bean注解来标识该方法
    @Bean注解仅仅可以够标注在方法上,作用是将标注方法的返回值放到spring的容器
    如果当前方法需要参数,@Bean可以自动从spring的容器中根据该参数类型查询(dl)到该参数,自动完成依赖注入
    @Bean("druidDataSource")此注解可以声明bean的id,默认为getDataSource
    */
    @Bean("druidDataSource")
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8");
        dataSource.setUsername("zhanggen");
        dataSource.setPassword("123.com");
        return dataSource;
    }


    //@Bean注解:自动从spring的容器中根据dataSource参数声明的DataSource类型,查询(dl)到dataSource参数,自动完成依赖注入
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

}
SpringConfig.java

2.提取外部properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.56.18:3306/dbForJava?characterEncoding=utf8
jdbc.username=zhanggen
jdbc.password=123.com
db.properties

--------------------------------------------------------------------------------------------------------------

@PropertySource("db.properties")//导入类路径下某一个配置文件

@Value():用于给对象中简单属性进行赋值

package com.zhanggen.config;

import com.alibaba.druid.pool.DruidAbstractDataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//把spring配置中的配置内容,全部转换到当前配置类
/*
bean标签---->方法
非bean标签(例如ComponentScan注解扫描)---->方法的注解
*/
@ComponentScan("com.zhanggen") //注解扫描
@PropertySource("db.properties")//导入类路径下某一个配置文件
public class SpringConfig {
    /*
    编写1个方法,在方法中去创建对应对象,并且通过@bean注解来标识该方法
    @Bean注解仅仅可以够标注在方法上,作用是将标注方法的返回值放到spring的容器
    如果当前方法需要参数,@Bean可以自动从spring的容器中根据该参数类型查询(dl)到该参数,自动完成依赖注入
    @Bean("druidDataSource")此注解可以声明bean的id,默认为getDataSource
    */

    // @Value():用于给对象中简单属性进行赋值
    @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("druidDataSource")
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }


    //@Bean注解:自动从spring的容器中根据dataSource参数声明的DataSource类型,查询(dl)到dataSource参数,自动完成依赖注入
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

}
SpringConfig.java

3.多配置类

spring的IOC容器创建时,可以1次加载多个配置文件(配置类),也可以先加载1个主配置文件(配置类),再由主配置文件加载其他配置文件(配置类);

 

3.1.方式1

Spring的容器实创建时,1次加载2个配置文件

package com.zhanggen.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
//导入类路径下某一个配置文件
@PropertySource("db.properties")
public class DbConfig {
    // @Value():用于给对象中简单属性进行赋值
    @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("druidDataSource")
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
DbConfig.java

------------------------------------------------------------------------------------------------------------

package com.zhanggen.config;

import com.alibaba.druid.pool.DruidAbstractDataSource;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//把spring配置中的配置内容,全部转换到当前配置类
/*
bean标签---->方法
非bean标签(例如ComponentScan注解扫描)---->方法的注解
*/
@ComponentScan("com.zhanggen") //注解扫描
public class SpringConfig {
    /*
    编写1个方法,在方法中去创建对应对象,并且通过@bean注解来标识该方法
    @Bean注解仅仅可以够标注在方法上,作用是将标注方法的返回值放到spring的容器
    如果当前方法需要参数,@Bean可以自动从spring的容器中根据该参数类型查询(dl)到该参数,自动完成依赖注入
    @Bean("druidDataSource")此注解可以声明bean的id,默认为getDataSource
    */
    //@Bean注解:自动从spring的容器中根据dataSource参数声明的DataSource类型,查询(dl)到dataSource参数,自动完成依赖注入
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

}
SpringConfig.java

---------------------------------------------------------------------------------------------------

  ApplicationContext act = new AnnotationConfigApplicationContext(SpringConfig.class, DbConfig.class);

 

3.2.方式2

Spring的容器实创建时,先加载1个主配置文件,在由主配置文件加载其他子配置文件;

@Import(DbConfig.class) //从1个配置类中导入另1个配置类;

package com.zhanggen.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
//导入类路径下某一个配置文件
@PropertySource("db.properties")
public class DbConfig {
    // @Value():用于给对象中简单属性进行赋值
    @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("druidDataSource")
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
DbConfig.java

------------------------------------------------------------------------------------------------------------------

package com.zhanggen.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//把spring配置中的配置内容,全部转换到当前配置类
/*
bean标签---->方法
非bean标签(例如ComponentScan注解扫描)---->方法的注解
*/
@ComponentScan("com.zhanggen") //注解扫描
@Import(DbConfig.class) //从1个配置类中导入另1个配置类
public class SpringConfig {
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

}
SpringConfig.java

 

4.配置类声明

使用@Configuration声明的类就是配置类,本质是1个@Component注解;

在spring容器初始化的时候, 该配置类会自动创建并进入spring容器,以供主配置类的对象进行调用;

package com.zhanggen.config;

import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;

import javax.sql.DataSource;
//导入类路径下某一个配置文件
@PropertySource("db.properties")
//@Configuration 声明的类就是配置类,在spring容器初始化的时候, 配置类会自动创建并进入容器
@Configuration
public class DbConfig {
    // @Value():用于给对象中简单属性进行赋值
    @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("druidDataSource")
    public DataSource getDataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}
DbConfig.java

------------------------------------------------------------------------------------------------------------------

package com.zhanggen.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Import;
import org.springframework.jdbc.core.JdbcTemplate;

import javax.sql.DataSource;

//把spring配置中的配置内容,全部转换到当前配置类
/*
bean标签---->方法
非bean标签(例如ComponentScan注解扫描)---->方法的注解
*/
@ComponentScan("com.zhanggen") //注解扫描
public class SpringConfig {
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource) {
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }

}
SpringConfig.java

 

5.纯注解总结

@ComponentScan
组件扫描注解。 相当于xml配置文件中的< context:component-scan base-package=""/>

@Bean
该注解只能写在方法上,表明使用此方法创建一个对象,并且放入spring容器。

@PropertySource
用于引入其它的properties配置文件

@Import
在一个配置类中导入其它配置类的内容

@Configuration
被此注解标注的类,会被Spring认为是配置类。Spring在启动的时候会自动扫描并加载所有配置类,然后将配置类中bean放入容器。

 

6.Spring整合到Junit测试

当我们吧bean对象放到spring的容器之后,如果相对这个bean对象进行功能测试;

无需每次都需创建1个ApplicationContext对象然后再去运行spring的容器;

Junit支持通过@runWith()注解的方式,快速运行spring容器对象,以供我们调用spring容器中对象的功能;

6.1.加入Spring单元测试的依赖

 <!--spring单元测试-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.1.6.RELEASE</version>
        </dependency>
pom.xml

6.2.修改测试类

package com.zhanggen.test;
import com.zhanggen.domain.Account;
import com.zhanggen.service.AccountService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import java.util.List;

//1.切换运行器:使用SpringJUnit4ClassRunner启动spring的容器
@RunWith(SpringJUnit4ClassRunner.class)
//2.启动spring的容器时加载配置类
//@ContextConfiguration(classes = SpringConfig.class)
////2.启动spring的容器时加载配置文件
//@ContextConfiguration("classpath:applicationContext.xml")
public class AccountServiceTest {
    @Autowired
    private AccountService accountService;

    @Test
    public void testQueryAll() {
        List<Account> accountList = accountService.queryAll();
        for (Account account : accountList) {
            System.out.println(account);
        }
    }
}
AccountServiceTest.java

 

7.@PostConstruc和@PreDestroy注解

@PostConstruc注释的方法,在当前对象初始化完成之后执行;

@PreDestory注释的方法,在对象销毁之前执行;

@Autowired不能注释static修饰的变量,静态成员属于类的,当类加载器加载静态变量时,Spring上下文尚未加载所以类加载器不会在bean中正确注入静态类,并且会失败

执行顺序如下

Constructor(对象的构造方法) -----> @Autowired(依赖注入) --------> @PostConstruct(注释的方法)

 

 

 

 

 

 

 

 

参考

posted on 2022-05-18 19:58  Martin8866  阅读(312)  评论(0编辑  收藏  举报