简单易懂的Spring基础入门学习总结笔记(上)

连着10次发帖失败。。
再发不出去不发了


计算机网络:https://www.nowcoder.com/discuss/342320
MySQL:https://www.nowcoder.com/discuss/353707
Java并发上:https://www.nowcoder.com/discuss/355081
Java并发下:https://www.nowcoder.com/discuss/355876
JDBC:https://www.nowcoder.com/discuss/356804
Linux:https://www.nowcoder.com/discuss/357410
JavaWeb上:https://www.nowcoder.com/discuss/358423
JavaWeb中:https://www.nowcoder.com/discuss/358741
JavaWeb下:https://www.nowcoder.com/discuss/359327
Redis:https://www.nowcoder.com/discuss/359700
Mybatis上:https://www.nowcoder.com/discuss/360108
Mybatis下:https://www.nowcoder.com/discuss/360108
本篇博客源址:https://blog.csdn.net/qq_41112238/article/details/103859726


Spring概述

Spring是什么

Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:反转控制)AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层
Spring MVC 和持久层 Spring JDBC
以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。


优势

  • 方便解耦,简化开发
    通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
  • AOP 编程的支持
    通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
  • 声明式事务的支持
    可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
  • 方便程序的测试
    可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
  • 方便集成各种优秀框架
    Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
  • 降低 JavaEE API 的使用难度
    Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。

体系结构

在这里插入图片描述


程序间耦合

编译期依赖

创建一个maven工程,导入数据库的依赖,然后在数据库中新建一个account表:

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);

创建一个Jdbc测试类

public class JdbcDemo1 {

    public static void main(String[] args) throws Exception{
        //注册驱动
        DriverManager.registerDriver(new Driver());
        //获取连接
        Connection connection = DriverManager.getConnection("jdbc:mysql:///test?serverTimezone=UTC", "root", "sjh2019");
        //获取预处理对象
        PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
        //得到结果集
        ResultSet resultSet = preparedStatement.executeQuery();
        //遍历结果集
        while(resultSet.next()){
            System.out.println(resultSet.getString("name"));
        }
        //释放资源
        resultSet.close();
        preparedStatement.close();
        connection.close();
    }
}

当把pom.xml的mysql依赖注释掉时,程序出现编译错误
在这里插入图片描述
在这里插入图片描述


  • 程序的耦合:
    程序间的依赖关系 包括类之间的依赖和方法间的依赖
  • 解耦:
    降低程序间的依赖关系
  • 实际开发中应该做到:
    编译期不依赖,运行时才依赖
  • 解耦的思路:
    1 创建对象时使用反射,避免使用new关键字。 2 通过读取配置文件来获取要创建的对象全限定类名。

代码中存在的问题

创建maven工程,分别创建业务层和持久层的接口和实现类,以及模拟表现层的测试类。

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

    void save();
}

//账户持久层实现类
public class AccountDaoImpl implements IAccountDao {

    public void save() {
        System.out.println("save account");
    }
}

//业务层接口
public interface IAccountService {

    //模拟保持账户
    void save();
}

//账户的业务层实现类
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao=new AccountDaoImpl();

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

//模拟表现层
public class Client {
    public static void main(String[] args) {
        IAccountService accountService=new AccountServiceImpl();
        accountService.save();
    }
}

工程结构
在这里插入图片描述

可以看到,这样的代码具有很高的耦合性(业务层实现类new了持久层实现类,测试类new了业务层实现类,依赖关系很强,代码不灵活)。


利用工厂模式解耦

创建一个工厂类

public class BeanFactory {

    private static Properties properties;
    //使用静态代码块赋值
    static {
        try {
            properties=new Properties();
            properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"));
        } catch (IOException e) {
            throw new ExceptionInInitializerError("初始化失败");
        }
    }
    //根据bean名称获取bean对象
    public static Object getBean(String beanName){
        Object bean = null;
        try {
            String beanPath=properties.getProperty(beanName);
            bean=Class.forName(beanPath).newInstance();
        }catch (Exception e){
            e.printStackTrace();
        }
        return bean;
    }
}

创建配置文件

accountService=service.impl.AccountServiceImpl
accountDao=dao.impl.AccountDaoImpl

利用工厂创建对象取代new

//测试类中:
IAccountService accountService= (IAccountService) BeanFactory.getBean("accountService");
//业务层:
private IAccountDao accountDao= (IAccountDao) BeanFactory.getBean("accountDao");

优化工厂为单例模式

/**
    一个创建Bean对象的工厂
    bean在计算机英语中,有可重用组件的含义
    JavaBean(用Java语言编写的可重用组件),范围大于实体类
    创建service和dao对象:
    1. 需要一个配置文件配置service和dao 配置的内容:全限定类名 唯一标识(key value形式)
    2. 通过读取配置文件,反射创建对象
    配置文件可以是xml或properties
*/
public class BeanFactory {

    private static Properties properties;
    //创建一个容器存放要创建的对象
    private static Map<String,Object> beans;

    //使用静态代码块赋值
    static {
        try {
            properties=new Properties();
            properties.load(BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties"));
            //实例化容器
            beans=new HashMap<>();
            //取出配置文件所有的key
            Enumeration keys = properties.keys();
            while (keys.hasMoreElements()){
                String key = keys.nextElement().toString();
                String beanPath = properties.getProperty(key);
                Object value = Class.forName(beanPath).newInstance();
                beans.put(key,value);
            }
        } catch (Exception e) {
            throw new ExceptionInInitializerError("初始化失败");
        }
    }
    //根据bean名称获取bean对象
    public static Object getBean(String beanName){
        return beans.get(beanName);
    }
}

Spring中的IOC

IOC的概念和作用

  • 概念
    控制反转把创建对象的权利交给框架,是框架的重要特征,并非面向对象编程的专用术语。它包括依赖注入DI和依赖查找。
  • 作用
    削减计算机程序的耦合。

使用IOC解决程序耦合(XML方式)

在pom.xml中添加spring依赖

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.RELEASE</version>
        </dependency>

在resources目录下创建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
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--把对象的创建交给spring -->
    <bean id="accountService" class="service.impl.AccountServiceImpl"/>

    <bean id="accountDao" class="dao.impl.AccountDaoImpl"/>
</beans>

测试类

public class Client {

    //获取spring的IOC核心容器,并根据id获取对象
    public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService= applicationContext.getBean("accountService",IAccountService.class);
        IAccountDao accountDao=applicationContext.getBean("accountDao",IAccountDao.class);
        System.out.println(accountService);
        System.out.println(accountDao);
    }
}

结果:
在这里插入图片描述


ApplicationContext 接口的实现类

  • ClassPathXmlApplicationContext:
    它是从类的根路径下加载配置文件 推荐使用这种
  • FileSystemXmlApplicationContext:
    它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
  • AnnotationConfigApplicationContext:
    当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。

BeanFactory 和 ApplicationContext 的区别

  • BeanFactory 才是 Spring 容器中的顶层接口。
    ApplicationContext 是它的子接口。
  • 创建对象的时间点不一样。
    ApplicationContext:只要一读取配置文件,默认情况下就会创建对象。(适用单例)
    BeanFactory:什么使用什么时候创建对象。(适用多例)

三种创建bean对象的方式

环境准备,去掉dao包,修改service实现类

public class AccountServiceImpl implements IAccountService {

    //private IAccountDao accountDao= new AccountDaoImpl();

    public void save() {
        //accountDao.save();
        System.out.println("service中的save方法执行了");
    }

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

    public void destroy(){
        System.out.println("销毁");
    }
}

  • 第一种方式:使用默认无参构造函数
   <!-- 使用默认构造函数创建 使用bean标签 配以id和class属性 且没有其他属性 如果类中没有默认构造函数会失败-->
    <bean id="accountService" class="service.impl.AccountServiceImpl"/>
  • 第二种方式:spring 管理静态工厂-使用静态工厂的方法创建对象
public class Factory {

    public static IAccountService getAccountService(){
        return new AccountServiceImpl();
    }
}
    <!-- 使用静态工厂中的方法创建对象-->
    <bean id="accountService" class="factory.Factory" factory-method="getAccountService"/>
  • 第三种方式:spring 管理实例工厂-使用实例工厂的方法创建对象
public class Factory {

    public IAccountService getAccountService(){
        return new AccountServiceImpl();
    }
}
    <!-- 使用普通工厂中的方法创建对象-->
    <bean id="factory" class="factory.Factory"/>
    <bean id="accountService" factory-bean="factory" factory-method="getAccountService"/>

bean的作用范围和生命周期

  • scope:指定对象的作用范围。
     - singleton :默认值,单例的.     
    • prototype :多例的.
      • request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
      • session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
      • global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么 globalSession 相当于 session.

测试类

public static void main(String[] args) {
        ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService= applicationContext.getBean("accountService",IAccountService.class);
        accountService.save();
        applicationContext.close();
    }

  • 单例对象:scope="singleton"

一个应用只有一个对象的实例。它的作用范围就是整个引用。

  • 生命周期:
    对象出生:当应用加载,创建容器时,对象就被创建了。
    对象活着:只要容器在,对象一直活着。
    对象死亡:当应用卸载,销毁容器时,对象就被销毁了。

配置
init-method:指定类中的初始化方法名称。
destroy-method:指定类中销毁方法名称。

<bean id="accountService" class="service.impl.AccountServiceImpl"
     scope="singleton" init-method="init" destroy-method="destroy"/>

结果
在这里插入图片描述


  • 多例对象:scope="prototype"

每次访问对象时,都会重新创建对象实例。

  • 生命周期:
    对象出生:当使用对象时,创建新的对象实例。
    对象活着:只要对象在使用中,就一直活着。
    对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。

配置

<bean id="accountService" class="service.impl.AccountServiceImpl"
     scope="prototype" init-method="init" destroy-method="destroy"/>

结果
在这里插入图片描述


Spring的依赖注入

概念

  • Dependency Injection,它是 spring 框架核心 ioc 的具体实现。
  • 我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,由spring来维护依赖关系,这种依赖关系的维护就叫做依赖注入。
  • 简单的说,就是坐等框架把持久层对象传入业务层,而不用我们自己去获取。

能注入的数据

  • 基本类型和String
  • 其他bean类型(在配置文件或注解配置的bean)
  • 复杂类型/集合类型

注入集合数据

  • List 结构的:
    array,list,set
  • Map 结构的:
    map,entry,props,prop

注入的方式

  • 使用构造函数
  • 使用set方法
  • 使用注解

使用构造函数注入

constructor-arg
属性:

  • index:指定参数在构造函数参数列表的索引位置
  • type:指定参数在构造函数中的数据类型
  • name:指定参数在构造函数中的名称 用这个找给谁赋值
    ======= 上面三个都是找给谁赋值,下面两个指的是赋什么值的==============
  • value:它能赋的值是基本数据类型和 String 类型
  • ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean

优点:
在获取bean对象时,注入数据是必需的,否则无法创建成功
缺点:
改变了bean对象的实例化方式,即使不使用某些对象也需要提供


业务层实现类

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

    public AccountServiceImpl(IAccountDao accountDao){
        this.accountDao=accountDao;
    }

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

}

配置

<bean id="accountService" class="service.impl.AccountServiceImpl">
       <constructor-arg name="accountDao" ref="accountDao"/>
    </bean>


    <bean id="accountDao" class="dao.impl.AccountDaoImpl"/>

测试类

public static void main(String[] args) {
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("bean.xml");
        IAccountService accountService= applicationContext.getBean("accountService",IAccountService.class);
        accountService.save();
    }

结果
在这里插入图片描述


使用set方法注入(常用)

涉及的标签:
property
属性:

  • name:找的是类中 set 方法后面的部分
  • ref:给属性赋值是其他 bean 类型的
  • value:给属性赋值是基本数据类型和 string 类型的

优点
创建对象时没有明确限制,可以直接使用默认构造函数
缺点
如果某个成员必须有值,则获取对象时可能set方法没有执行

业务层实现类

public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

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

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

}

配置

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

测试类和结果不变


使用IOC解决程序耦合(注解方式)

常用注解和分类

  • 用于创建对象的
    相当于:<bean id="" class="" >
  • @Component
    作用:
    把当前类对象存入spring容器中,相当于在 xml 中配置一个 bean。
    属性:
    value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名,首字母小写。
  • @Controller @Service @Repository
    他们三个注解都是针对一个的衍生注解,他们的作用及属性都是一模一样的。
    他们只不过是提供了更加明确的语义化。
    @Controller:一般用于表现层的注解。
    @Service:一般用于业务层的注解。
    @Repository:一般用于持久层的注解。
    细节:如果注解中有且只有一个属性要赋值时,且名称是 value,value 在赋值是可以不写。

  • 用于注入数据的
    相当于:<property name="" ref=""> <property name="" value="">
  • @Autowired
    作用:
    自动按照类型注入。当使用注解注入属性时,set 方法可以省略。它只能注入其他 bean 类型。当有多个类型匹配时,使用要注入的对象变量名称作为 bean 的 id,在 spring 容器继续查找,找到了也可以注入成功。找不到就报错。
  • @Qualifier
    作用:
    在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和@Autowire 一起使用;但是给方法参数注入时,可以独立使用。
    属性:
    value:指定 bean 的 id。
  • @Resource
    作用:
    直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
    属性:
    name:指定 bean 的 id。
  • @Value
    作用:
    注入基本数据类型和 String 类型数据,可以使用spEL,写法:${表达式}
    属性:
    value:用于指定值

  • 用于改变作用范围的:
    相当于:<bean id="" class="" scope="">
  • @Scope
    作用:
    指定 bean 的作用范围。
    属性:
    value:指定范围的值。

    取值:singleton prototype request session globalsession

  • 和生命周期相关的:(了解)
    相当于:<bean id="" class="" init-method="" destroy-method="" />
  • @PostConstruct
    作用:
    用于指定初始化方法。
  • @PreDestroy
    作用:
    用于指定销毁方法。

beanAnno.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
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 告之spring创建容器时要扫描的包,配置所需要的标签不在beans约束中,在context名称空间和约束中 -->
    <context:component-scan base-package="service"/>
    <context:component-scan base-package="dao"/>
</beans>

dao接口实现类添加持久层注解

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

    public void save() {
        System.out.println("save account");
    }
}

service接口实现类添加业务层注解

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

    @Autowired
    private IAccountDao accountDao;

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

测试方法

public class Client {
    ApplicationContext applicationContext=new ClassPathXmlApplicationContext("beanAnno.xml");


    IAccountService accountService= (IAccountService) applicationContext.getBean("accountService");

    @Test
    public void test() {
        accountService.save();
    }
}

结果

在这里插入图片描述


基于XML的 IOC案例

pom.xml配置的依赖

 <dependencies>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.18</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.1.5.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>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>

业务层的接口和实现类

//业务层接口
public interface IAccountService {

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

    //查询一个
    Account findAccountById(Integer id);

    //保存
    void saveAccount(Account account);

    //更新
    void updateAccount(Account account);

    //删除
    void deleteAccount(Integer id);
}

//账户的业务层实现类
public class AccountServiceImpl implements IAccountService {

    private IAccountDao accountDao;

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

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

    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);
    }
}

持久层的接口和实现类

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

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

    //查询一个
    Account findAccountById(Integer id);

    //保存
    void saveAccount(Account account);

    //更新
    void updateAccount(Account account);

    //删除
    void deleteAccount(Integer id);
}

//账户持久层实现类
public class AccountDaoImpl implements IAccountDao {

    private QueryRunner queryRunner;

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

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

    public Account findAccountById(Integer id) {
        try {
            return queryRunner.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 {
            queryRunner.update("insert into account(name,money) values (?,?)",account.getName(),account.getMoney());
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

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

在resources目录下创建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
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!--把对象的创建交给spring -->
    <bean id="accountService" class="service.impl.AccountServiceImpl">
        <!-- 注入dao对象 -->
       <property name="accountDao" ref="accountDao"/>
    </bean>

    <!-- 配置dao -->
    <bean id="accountDao" class="dao.impl.AccountDaoImpl">
        <!-- 注入queryrunner -->
        <property name="queryRunner" ref="query"/>
    </bean>

    <!-- 配置query runner 使用多例-->
    <bean id="query" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!-- 注入数据源 -->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 连接数据库的信息 -->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///test?serverTimezone=UTC"/>
        <property name="user" value="root"/>
        <property name="password" value="sjh2019"/>
    </bean>

</beans>

测试类

public class AccountTest {
    private ApplicationContext ac=new ClassPathXmlApplicationContext("bean.xml");
    IAccountService accountService=ac.getBean("accountService",IAccountService.class);

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

    @Test
    public void findOne(){
        Account account = accountService.findAccountById(1);
        System.out.println(account);
    }

    @Test
    public void save(){
        Account account = new Account();
        account.setName("范闲");
        account.setMoney(5000f);
        accountService.saveAccount(account);
    }

    @Test
    public void update(){
        Account account = accountService.findAccountById(4);
        account.setMoney(2000f);
        accountService.updateAccount(account);
    }

    @Test
    public void delete(){
        accountService.deleteAccount(4);
    }
}

对自己写的类添加注解配置

给业务层实现类添加注解,并给dao对象添加注解,可去掉dao的set方法

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

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

给持久层实现类添加注解,并给query runner添加注解,并去掉其set方法

@Repository("accountDao")
public class AccountDaoImpl implements IAccountDao {

    @Resource(name = "query")
    private QueryRunner queryRunner;

beanAnno.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
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 告之spring创建容器时要扫描的包,配置所需要的标签不在beans约束中,在context名称空间和约束中 -->
    <context:component-scan base-package="service"/>
    <context:component-scan base-package="dao"/>

    <!-- 配置query runner 使用多例-->
    <bean id="query" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
        <!-- 注入数据源 -->
        <constructor-arg name="ds" ref="dataSource"/>
    </bean>

    <!-- 配置数据源 -->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <!-- 连接数据库的信息 -->
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql:///test?serverTimezone=UTC"/>
        <property name="user" value="root"/>
        <property name="password" value="sjh2019"/>
    </bean>
</beans>

纯注解配置

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

<!-- 告知spring框架在,读取配置文件,创建容器时,扫描注解,依据注解创建对象,并存入容器中 -->
 <context:component-scan base-package="com.itheima"></context:component-scan>

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

  • @Configuration
    作用:
    用于指定当前类是一个 spring 配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration 注解的类.class),类作为参数传递时可不加。
    属性:
    value:用于指定配置类的字节码
  • @ComponentScan
    作用:
    用于指定 spring 在初始化容器时要扫描的包。作用和在 spring 的 xml 配置文件中的:
    <context:component-scan base-package="com.itheima"/>是一样的。
    属性:
    basePackages:用于指定要扫描的包。和该注解中的 value 属性作用一样。
  • @Bean
    作用:
    该注解只能写在方法上,表明使用此方法创建一个对象,并且放入 spring 容器。
    属性:
    name:给当前@Bean 注解方法创建的对象指定一个名称(即 bean 的 id)。
    细节:
    当我们使用注解配置方法时,如果方法有参数,spring会去容器查找是否有可用bean对象,查找方式和auto wired一样

创建一个SpringConfig类,并添加相应注解

@Configuration
@ComponentScan(basePackages = {"dao","service"})
public class SpringConfig {

    //创建一个queryRunner
    @Bean("query")@Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

    //创建数据源对象
    @Bean("dataSource")
    public DataSource createDataSource(){
        try{
            ComboPooledDataSource dataSource = new ComboPooledDataSource();
            dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
            dataSource.setJdbcUrl("jdbc:mysql:///test?serverTimezone=UTC");
            dataSource.setUser("root");
            dataSource.setPassword("sjh2019");
            return dataSource;
        }catch (Exception e){
            throw new RuntimeException();
        }
    }
}

测试类

public class AccountTest {
    private ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);
    IAccountService accountService=ac.getBean("accountService",IAccountService.class);

//...其余测试方法代码不变

  • @PropertySource
  • 用于加载.properties 文件中的配置。例如我们配置数据源时,可以把连接数据库的信息写到properties 配置文件中,就可以使用此注解指定 properties 配置文件的位置。
    属性:
    value[]:用于指定 properties 文件位置。如果是在类路径下,需要写上classpath:

  • @Import
    作用:
    用于导入其他配置类,在引入其他配置类时,可以不用再写@Configuration 注解。当然写上也没问题。有import的是父配置类,引入的是子配置类。
    属性:
    value[]:用于指定其他配置类的字节码。

创建一个JdbcConfig类

@Configuration
public class JdbcConfig {
    @Value("${driver}")
    private String driver;
    @Value("${url}")
    private String url;
    @Value("${user}")
    private String user;
    @Value("${password}")
    private String password;

    //创建一个queryRunner
    @Bean("query")@Scope("prototype")
    public QueryRunner createQueryRunner(DataSource dataSource){
        return new QueryRunner(dataSource);
    }

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

创建一个jdbc.propertis配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql:///test?serverTimezone=UTC
user=root
password=sjh2019

优化SpringConfig主配置

@ComponentScan(basePackages = {"dao","service"})
@Import(JdbcConfig.class)
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {

}

测试类

public class AccountTest {
    private ApplicationContext ac=new AnnotationConfigApplicationContext(SpringConfig.class);
    IAccountService accountService=ac.getBean("accountService",IAccountService.class);

    @Test
    public void findAll(){
        List<Account> accounts = accountService.findAll();
        accounts.forEach(System.out::println);
    }
    //。。。。跟前面相同 省略

Spring整合junit

问题

在测试类中,每个测试方法都有以下两行代码:

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

这两行代码的作用是获取容器,如果不写的话,直接会提示空指针异常。所以又不能轻易删掉。


步骤

  • 在pom.xml中导入依赖
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>

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

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

  • 使用@Autowired 给测试类中的变量注入数据

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {SpringConfig.class})
    public class AccountTest {
    
      @Autowired
      IAccountService accountService;
      //。。。省略
    
#笔记##Spring#
全部评论
加油、虽然我快放弃了
点赞 回复
分享
发布于 2020-01-06 22:00
大佬您好,请问你是怎么学习这些知识点的?是看书还是看视频啊?
点赞 回复
分享
发布于 2020-01-08 18:21
阿里巴巴
校招火热招聘中
官网直投

相关推荐

头像 头像
03-05 22:00
C++
点赞 评论 收藏
转发
6 25 评论
分享
牛客网
牛客企业服务