# Spring 快速入门

# 1. 介绍

# 1.1. 为什么要学

  • 简化开发
  • 框架整合

# 1.2. 初识 Spring

全家桶:

  • Spring Framework - 底层框架
  • Spring Boot
  • Spring Cloud
  • ...

Spring 5:

  • 全面支持 JDK8

# 1.3. 核心概念

概念:

  • IOC / DI
  • IoC 容器
  • Bean

IoC:

  • Inversion of Control, 控制反转,是思想
  • 不主动 new 对象,由外部创建对象

IoC 容器:

  • 充当 IoC 思想的 “外部”
  • 容器里面创建、管理的对象,叫做 bean

DI:

  • Dependency Injection, 依赖注入
  • 在容器中建立 bean 与 bean 之间的依赖关系的过程,称为依赖注入

目标:充分解耦

  • 使用 IoC 容器管理 bean (IoC)
  • 在 IoC 容器内将有依赖关系的 bean 进行关系绑定 (DI)

最终效果

  • 使用对象时不仅可以直接从 IoC 容器中夫取,并且获取到的 bean 已经绑定了所有的依赖关系

# 2. IoC

# 2.1. 入门案例

目录:

project/
  src/main/
    java/
      org.example/
        dao/
          BookDao.java
          impl/
            BookDaoImpl.java
        service/
          BookService.java
            impl/
              BookServiceImpl.java
        App.java
    resources/
      applicationContext.xml

文件:

  • App.java:

    package org.example;
    
    import org.example.service.BookService;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    public class App {
        public static void main( String[] args ) {
            ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
            BookService bookService = (BookService) ctx.getBean("bookService");
            bookService.save();
        }
    }
    
  • applicationContext.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">
    
        <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>
    
        <bean id="bookService" class="org.example.service.impl.BookServiceImpl">
            <property name="bookDao" ref="bookDao"/>
        </bean>
    
    </beans>
    
  • BookService.java

    package org.example.service;
    
    public interface BookService {
        void save();
    }
    
  • BookServiceImpl.java

    package org.example.service.impl;
    
    import org.example.dao.BookDao;
    import org.example.service.BookService;
    
    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
        
        public void save() {
            System.out.println("BookServiceImpl: save()!");
            bookDao.save();
        }
    
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    }
    
  • BookDao.java

    package org.example.dao;
    
    public interface BookDao {
        void save();
    }
    
  • BookDaoImpl.java

    package org.example.dao.impl;
    
    import org.example.dao.BookDao;
    
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("BookDaoImpl save!");
        }
    }
    

# 2.2. bean 配置

别名:

  • 属性: name
  • 说明: 可定义多个,用 逗号、分号、空格 分隔
  • 示例: <bean name="service, bookServiceImpl" />

作用范围:

  • 属性: scope
  • 说明: singleton (单例、默认), prototype (非单例)
  • 示例: <bean ... scope="prototype">

# 2.3. 实例化 bean 的四种方式

  1. 默认构造函数

    /*
    <bean 
        id="bookDao" 
        class="org.example.dao.impl.bookDaoImpl" 
    />
     */
    
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("BookDaoImpl save!");
        }
    }
    
  2. 静态工厂

    /*
    <bean 
        id="orderDaoFactory"
        class="org.example.factory.OrderDaoFactory" 
        factory-method="getOrderDao" 
    />
    */
    
    public class OrderDaoFactory {
        public static OrderDao getOrderDao() {
            return new OrderDaoImpl();
        }
    }
    
  3. 实例工厂

    /*  
    <bean 
        id="userDao"
        factory-method="getUserDao"
        factory-bean="org.example.factory.userDaoFactory"
    />
    */
    public class UserDaoFactory {
        public UserDao getUserDao() {
            return new UserDaoImpl();
        }
    }
    
  4. 实例工厂(FactoryBean)

    /*
    <bean 
        id="userDao"
        class="org.example.factory.UserDaoFactoryBean"
    />
    */
    public class UserDaoFactoryBean implements FactoryBean<UserDao> {
        public UserDao getObject() throws Exception {
            return new UserDaoImpl();
        }
    
        public Class<?> getObjectType() {
            return UserDao.class;
        }
    }
    

# 2.4. bean 生命周期

生命周期:

  • 初始化容器

    1. 创建对象(内存分配)
    2. 执行构造方法
    3. 执行属性注入(set 操作)
    4. 执行 bean 初始化方法
  • 使用 bean

    1. 执行业务操作
  • 关闭/销毁容器

    1. 执行 bean 销毁方法

生命周期方法:

  1. 方式一:

    /*  
        <bean 
            id="bookService" 
            class="org.example.service.impl.BookServiceImpl" 
            init-method="init" 
            destroy-method="destory">
        </bean>
    */
    public class BookServiceImpl implements BookService {
        public void init() {
            System.out.println("init");
        }
        public void destory() {
            System.out.println("destory");
        }
    }
    
  2. 方式二:

    /*  
        <bean 
            id="bookService" 
            class="org.example.service.impl.BookServiceImpl">
        </bean>
    */
    public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {
        public void afterPropertiesSet() throws Exception {
            System.out.println("init2");
        }
    
        public void destroy() throws Exception {
            System.out.println("destory2");
        }
    }
    

关闭容器的方法:

  1. 手动关闭

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    ctx.close();
    
  2. 注册关闭钩子

    ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
    ctx.registerShutdownHook();
    

# 3. DI

# 3.1. 依赖注入方式

  1. setter 注入

    • 简单类型
    • 引用类型
  2. 构造器注入

    • 简单类型
    • 引用类型

# 3.1.1. setter 注入

注入简单类型:

/*
    <bean 
        id="bookService" 
        class="org.example.service.impl.BookServiceImpl"
    >
        <property name="id" value="abc" />
        <property name="total" value="100" />
    </bean>
 */
public class BookServiceImpl {
    private int total;

    private String id;


    public void setTotal(int total) {
        this.total = total;
    }

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

注入引用类型:

/*  
    <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>
    
    <bean 
        id="bookService" 
        class="org.example.service.impl.BookServiceImpl"
    >
        <property name="bookDao" ref="bookDao"/>
    </bean>
*/
public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

# 3.1.2. 构造器注入

示例:

/* 
    <bean id="bookDao" class="org.example.dao.impl.BookDaoImpl"/>

    <bean 
        id="bookService" 
        class="org.example.service.impl.BookServiceImpl"
    >
        <constructor-arg name="bookDao" ref="bookDao"/>

        <constructor-arg name="id" value="xyz" />
        <constructor-arg name="total" value="123" />
    </bean>
 */
public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    private int total;

    private String id;

    public BookServiceImpl(BookDao bookDao, int total, String id) {
        this.bookDao = bookDao;
        this.total = total;
        this.id = id;
    }
}

# 3.1.3. 总结

依赖注入方式选择:

  1. 强制依赖 使用构造器注入
  2. setter 注入有概率不进行注入 导致 nu11 对象出现;
  3. 可选依赖使用 setter 注入进行,灵活性强;
  4. Spring 框架推荐使用构造器注入,第三方框架背部大多数采用构造器注入的形式进行数据初始化,相对严谨;
  5. 如果有必要可以两者同时使用,使用构造器入完成强制依赖的注入,使用 setter 注入完成可选依赖的注入;
  6. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供 setter 方法就必须使用构造器注入;
  7. 自己开发的模块推荐使用 setter 注入

# 3.2. 依赖自动装配

# 3.2.1. 说明

自动装配:

  • IoC 容器根据 bean 所依赖的资源 在容器中自动查找 并注入到 bean 中的过程称为自动装配

自动装配方式:

  • 按类型(常用)
  • 按名称
  • 按构造方法
  • 不启用自动装配

# 3.2.2. 按类型

示例:

/* 
    <bean class="org.example.dao.impl.BookDaoImpl" />

    <bean 
        id="bookService" 
        class="org.example.service.impl.BookServiceImpl" 
        autowire="byType"
    />
 */
public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

# 3.2.3. 按名称

示例:

/* 
    <bean 
        id="bookDao"
        class="org.example.dao.impl.BookDaoImpl" 
    />

    <bean 
        id="bookService" 
        class="org.example.service.impl.BookServiceImpl" 
        autowire="byName"
    />
 */
public class BookServiceImpl implements BookService {
    private BookDao bookDao;

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}

# 3.2.4. 注意

  1. 自动装配用于引用类型依注入,不能对简单类型进行操作
  2. 使用按类型装配时(byType) 必须保障容器中相同类型的 bean 唯一,推荐使用
  3. 使用按名称装配时(byName) 必须保障容器中具有指定名称的 bean,因变量名与配置耦合,不推荐使用
  4. 自动装配优先级低于 setter注入 与 构造器注入,同时出现时自动装配配置失效

# 3.3. 集合注入

集合:

  • Array
  • List
  • Set
  • Map
  • Properties

示例:

/* 
<bean id="bookService" class="org.example.service.impl.BookServiceImpl">
    <property name="myArray">
        <array>
            <value>1</value>
            <value>2</value>
            <value>3</value>
        </array>
    </property>

    <property name="myList">
        <list>
            <value>list1</value>
            <value>list2</value>
            <value>list3</value>
        </list>
    </property>

    <property name="mySet">
        <set>
            <value>set1</value>
            <value>set2</value>
            <value>set3</value>
        </set>
    </property>

    <property name="myMap">
        <map>
            <entry key="key1" value="val1" />
            <entry key="key2" value="val2" />
            <entry key="key3" value="val3" />
        </map>
    </property>

    <property name="myProperties">
        <props>
            <prop key="prop1">value1</prop>
            <prop key="prop2">value2</prop>
            <prop key="prop3">value3</prop>
        </props>
    </property>
</bean>
 */
public class BookServiceImpl implements BookService {
    private int[] myArray;

    private List<String> myList;

    private Set<String> mySet;

    private Map<String, String> myMap;

    private Properties myProperties;

    public void setMyArray(int[] myArray) {
        this.myArray = myArray;
    }

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

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

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

    public void setMyProperties(Properties myProperties) {
        this.myProperties = myProperties;
    }

    public void save() {
        System.out.println("myArray: " + Arrays.toString(myArray));
        System.out.println("myList: " + myList);
        System.out.println("mySet: " + mySet);
        System.out.println("myMap: " + myMap);
        System.out.println("myProperties: " + myProperties);
    }
}

# 3.4. 读取 properties 文件

步骤:

  1. 开启 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
            http://www.springframework.org/schema/beans/spring-beans.xsd
    
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context.xsd
    
            "
    >
    
  2. 加载配置文件

    <!-- 
        jdbc.username=root
        jdbc.password=123456
    -->
    <context:property-placeholder location="jdbc.properties" />
    
    <bean id="bookService" class="org.example.service.impl.BookServiceImpl">
        <property name="username" value="${jdbc.username}"/>
    </bean>
    

注意:

<!--  
    不加载系统属性,当 properties 文件中的 key 与环境变量冲突的情况
-->
<context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER" />


<!--  
    加载多个 properties 文件, 逗号分隔
-->
<context:property-placeholder location="jdbc.properties,xyz.properties" />


<!--  
    加载当前项目下所有 properties 文件
-->
<context:property-placeholder location="*.properties" />


<!--  
    加载当前项目下所有 properties 文件, 标准格式
-->
<context:property-placeholder location="classpath:*.properties" />


<!--  
    加载当前项目及依赖的 jar 包中的所有 properties 文件
-->
<context:property-placeholder location="classpath*:*.properties" />

# 3.5. 容器

创建容器的方式:

  1. ClassPathXmlApplicationContext

    ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
  2. FileSystemXmlApplicationContext

    ApplicationContext ctx = new FileSystemXmlApplicationContext("W:\\spring_07_container\\src\\main\\resources\\applicationContext.xml");
    

获取 bean 的方式:

  1. 名称 + 强转

    BookService bookService = (BookService) ctx.getBean("bookService");
    
  2. 名称 + 类型

    BookService bookService = ctx.getBean("bookService", BookService.class);
    
  3. 类型

    BookService bookService = ctx.getBean(BookService.class);
    

BeanFactory:

  • IoC 容器的顶层接口
  • 加载的 bean 默认延迟加载

# 4. 注解开发

# 4.1. 注解开发定义 bean

说明:

  • Spring 2.0 开启注解功能,通过注解配置 bean

步骤:

  1. 配置文件(applicationContext.xml)中设置组件扫描加载 bean

    <beans ...>
        <!-- base-package: 扫描当前包及其所有后代包 -->
        <context:component-scan base-package="org.example" />
    </beans>
    
  2. 使用 @Component 定义 bean

    @Component("bookService")
    public class BookServiceImpl implements BookService {
        public void save() {
            System.out.println("BookServiceImpl.save()");
        }
    }
    
    @Component
    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("BookDaoImpl.save()");
        }
    }
    
    public class App {
        public static void main( String[] args ) {
            ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
    
            BookService bookService = (BookService) ctx.getBean("bookService");
            bookService.save();
    
            BookDao bookDao = ctx.getBean(BookDao.class);
            bookDao.save();
        }
    }
    

Spring 提供 @Component 注解的三个衍生注解(功能完全一致):

  • @Controller: 表现层 bean
  • @Service: 业务层 bean
  • @Repository: 数据层 bean

# 4.2. 纯注解开发

说明:

  • Spring 3.0 升级了纯注解开发模式,使用 Java 类替代配置文件

步骤:

  1. 配置类:

    // 替代 <beans></beans>
    @Configuration 
    // 替代 <context:component-scan base-package="org.example" />
    @ComponentScan({"org.example.dao", "org.example.service"}) 
    public class SpringConfig {
    }
    
  2. 容器类

    public class App {
        public static void main( String[] args ) {
            ApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);
    
            BookService bookService = (BookService) ctx.getBean("bookService");
            bookService.save();
    
            BookDao bookDao = ctx.getBean(BookDao.class);
            bookDao.save();
        }
    }
    

# 4.3. bean 管理

bean 作用范围:

  • @Scope

bean 生命周期:

  • @PostConstruct
  • @PreDestroy

示例:

@Component
// @Scope("prototype") // 非单例
@Scope("singleton") // 单例,默认
public class BookDaoImpl implements BookDao {

    // 构造之后
    @PostConstruct
    public void init() {
        System.out.println("init()");
    }

    // 销毁之前
    @PreDestroy
    public void destroy() {
        System.out.println("destroy()");
    }

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


public class App {
    public static void main( String[] args ) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(SpringConfig.class);

        System.out.println(ctx.getBean(BookDao.class));
        System.out.println(ctx.getBean(BookDao.class));

        ctx.close();
    }
}

# 4.4. 依赖注入

自动装配:

  • @Autowired: 按类型自动装配
  • @Qualifier("bookDaoImpl2"): 按名称装配,需配合 @Autowired 使用
  • @Value: 注入简单类型

读取 properties 文件:

  • @PropertySource("jdbc1.properties", ...):

示例:

@Configuration
@ComponentScan({"org.example.dao", "org.example.service"})
@PropertySource({"user.properties"})
    public class SpringConfig {
}

/* 
# src/main/resources/user.properties
age=18
*/

@Service
public class BookServiceImpl implements BookService {
    @Autowired
    @Qualifier("bookDaoImpl2")
    private BookDao bookDao;

    @Value("张三")
    private String name;

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

    public void save() {
        System.out.println("BookServiceImpl.save()");
        bookDao.save();

        System.out.println("name=" + name);
        System.out.println("age=" + age);
    }
}

# 4.5. 第三方 bean 管理

第三方 bean 管理:

  1. 定义 bean

    /*
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.16</version>
    </dependency>
    */
    public class JdbcConfig {
        // 1. 定义一个方法,返回要管理的对象
        // 2. 添加 @Bean,标记返回的对象是一个 bean
        @Bean
        public DataSource dataSource() {
            DruidDataSource ds = new DruidDataSource();
            ds.setDriverClassName("com.mysql.jdbc.Driver");
            ds.setUrl("jdbc:mysql://localhost:3306/spring_db");
            ds.setUsername("root");
            ds.setPassword("root");
            return ds;
        }
    }
    
  2. 导入 bean

    @Configuration
    @Import({JdbcConfig.class})
    public class SpringConfig {
    }
    

第三方 bean 依赖注入:

public class JdbcConfig {
    // 通过成员变量,注入简单类型
    @Value("com.mysql.jdbc.Driver")
    private String driverClass;

    // 通过方法参数,注入引用类型 (按类型自动装配)
    @Bean
    public DataSource dataSource(BookDao bookDao) {
        System.out.println(bookDao);
        
        DruidDataSource ds = new DruidDataSource();
        
        ds.setDriverClassName(driverClass);

        return ds;
    }
}

# 4.6. 注解开发总结

XML 配置 VS 注解配置

定义 bean:

  • XML 配置:

    • <bean id="xx" class="xx">
  • 注解配置:

    • @Component(@Controller / @Service / @Repository)
    • @ComponentScan

依赖注入:

  • XML 配置:

    • setter 注入
    • 构造器注入
    • 自动装配
  • 注解配置:

    • @Autowired (@Qualifier)
    • @Value

第三方 bean:

  • XML 配置:

    • 静态工厂
    • 实例工厂
  • 注解配置:

    • @Bean

作用范围:

  • XML 配置:

    • scope 属性
  • 注解配置:

    • @Scope

生命周期:

  • XML 配置:

    • 标准接口
    • init-method / destroy-method
  • 注解配置:

    • @PostConstructor
    • @PreDestroy

# 5. Spring 整合 MyBatis

本章目录