您现在的位置是:首页 >学无止境 >Spring网站首页学无止境

Spring

养一只摆烂猫. 2024-06-12 18:01:02
简介Spring

目录

Spring相关概念

Spring系统架构

系统架构图

Spring核心概念

 IOC、IOC容器、Bean、DI

入门案例 

IOC入门案例

DI入门案例

bean的配置

bean实例化

bean的生命周期

DI相关内容

setter注入

构造器注入

自动配置

集合注入

IOC/DI配置管理第三方bean

案例:数据源对象管理

容器

IOC/DI注解开发

纯注解开发模式 

注解开发bean作用范围与生命周期管理

注解开发依赖注入

IOC/DI注解开发管理第三方bean 

注解开发管理第三方bean

注解开发实现为第三方bean注入资源

注解开发总结

Spring整合

Spring整合Mybatis

Spring整合Junit


为什么要学?
Spring技术是JavaEE开发必备技能,企业开发技术选型命中率>90%
Spring可以简化开发,降低企业级开发的复杂性,使开发变得更简单快捷
Spring可以框架整合,高效整合其他技术,提高企业级应用开发与运行效率
学什么?
简化开发:  Spring框架中提供了两个大的核心技术,分别是:
    IOC
    AOP
        事务处理
1.Spring的简化操作都是基于这两块内容,所以这也是Spring学习中最为重要的两个知识点。
2.事务处理属于Spring中AOP的具体应用,可以简化项目中的事务管理,也是Spring技术中的一大亮点
框架整合:  Spring在框架整合这块已经做到了极致,它可以整合市面上几乎所有主流框架,比如:
MyBatis
MyBatis-plus 
Struts 
Struts2 
Hibernate...

综上所述,对于Spring的学习,主要学习四块内容:
(1)IOC,(2)整合Mybatis(IOC的具体应用),(3)AOP,(4)声明式事务(AOP的具体应用)

Spring相关概念

Spring家族
官网:https://spring.io
Spring  Framework:Spring框架,是Spring中最早最核心的技术,也是所有其他技术的基础。
SpringBoot:Spring是来简化开发,而SpringBoot是来帮助Spring在简化的基础上能更快速进行开发
SpringCloud:这个是用来做分布式之微服务架构的相关开发。

 Spring发展史

Spring  Framework这个技术是如何来的?

Spring1.0是纯配置文件开发
Spring2.0为了简化开发引入了注解开发,此时是配置文件加注解的开发方式
Spring3.0已经可以进行纯注解开发,使开发效率大幅提升
Spring4.0根据JDK的版本升级对个别API进行了调整
Spring5.0已经全面支持JDK8

Spring系统架构

系统架构图

Spring  Framework是Spring生态圈中最基础的项目,是其他项目的根基。
Spring  Framework的发展也经历了很多版本的变更,每个版本都有相应的调整

 

(1)核心层
Core  Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块
(2)AOP层
AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强
Aspects:AOP是思想,Aspects是对AOP思想的具体实现
(3)数据层
Data  Access:数据访问,Spring全家桶中有对数据访问的具体实现技术
Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如Mybatis 
Transactions:事务,Spring中事务管理是Spring  AOP的一个具体实现,也是后期学习的重点内容
(4)Web层
这一层的内容将在SpringMVC框架具体学习
(5)Test层
Spring主要整合了Junit来完成单元测试和集成测试

Spring学习路线

Spring的IOC/DI 
Spring的AOP
AOP的具体应用,事务管理
IOC/DI的具体应用,整合Mybatis

 

Spring核心概念

目前项目中的问题

(1)业务层需要调用数据层的方法,就需要在业务层new数据层的对象
(2)如果数据层的实现类发生变化,那么业务层的代码也需要跟着改变,
发生变更后,都需要进行编译打包和重部署
(3)所以,现在代码在编写的过程中存在的问题是:耦合度偏高
针对这个问题,该如何解决呢?

 

使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象
这种实现思就是Spring的一个核心概念

 IOCIOC容器、BeanDI

1.IOC(Inversion of Control)控制反转
(1)什么是控制反转呢?
使用对象时,由主动new产生对象转换为由外部提供对象,
此过程中对象创建控制权由程序转移到外部,此思想称为控制反转。
    业务层要用数据层的类对象,以前是自己 new 的
    现在自己不new了,交给别人[外部] 来创建对象
    别人[外部] 就反转控制了数据层对象的创建权这种思想就是控制反转
    别人[外部]指定是什么呢?继续往下学
(2)Spring和IOC之间的关系是什么呢?
Spring技术对IOC思想进行了实现
    Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部" 
    IOC思想中的别人[外部] 指的就是Spring的IOC容器
(3)IOC容器的作用以及内部存放的是什么?
IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象
    被创建或被管理的对象在IOC容器中统称为Bean
    IOC容器中放的就是一个个的Bean对象
(4)当IOC容器中创建好service和dao对象后,程序能正确执行么?
不行,因为service运行需要依赖dao对象
    IOC容器中虽然有service和dao对象
    但是service对象和dao对象没有任何关系
    需要把dao对象交给service,也就是说要绑定service和dao对象之间的关系
    像这种在容器中建立对象与对象之间的绑定关系就要用到DI:

2. DI(Dependency Injection)依赖注入
(1)什么是依赖注入呢?
    在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
    业务层要用数据层的类对象,以前是自己 new 的
    现在自己不new了,靠 别人[外部其实指的就是IOC容器] 来给注入进来
(2)IOC容器中哪些bean之间要建立依赖关系呢?
需要程序员根据业务需求提前建立好关系,如业务层需要依赖数据层,service就要和dao建立依赖关系
介绍完Spring的IOC和DI的概念后,我们会发现这两个概念的最终目标是:充分解耦
具体实现靠:
使用IOC容器管理bean(IOC)
在IOC容器内将有依赖关系的bean进行关系绑定(DI)
最终结果为:使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.

入门案例 

IOC入门案例

入门案例思路分析
(1)Spring是使用容器来管理bean对象的,那么管什么?
    主要管理项目中所使用到的类对象,比如(Service和Dao) 
(2)如何将被管理的对象告知IOC容器?
    使用配置文件
(3)被管理的对象交给IOC容器,要想从容器中获取对象,就先得思考如何获取到IOC容器? 
    Spring框架提供相应的接口
(4)IOC容器得到后,如何从容器中获取bean?
    调用Spring框架提供对应接口中的方法
(5)使用Spring导入哪些坐标?
    用别人的东西,就需要在pom.xml添加对应的依赖

入门案例代码实现

需求分析:将BookServiceImpl和BookDaoImpl交给Spring管理,
        并从容器中获取对应的bean对象进行方法调用。
1.创建Maven的java项目
2.pom.xml添加Spring的依赖jar包
3.创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
4.resources下添加spring配置文件,并完成bean的配置
5.使用Spring提供的接口完成IOC容器的创建
6.从容器中获取对象进行方法调用

步骤1:创建Maven项目

步骤2:添加Spring的依赖jar

 <dependencies>
        <dependency>
            <!--Spring的依赖-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

步骤3:添加案例中需要的类

//创建BookService,BookServiceImpl,BookDao和BookDaoImpl四个类
public interface BookDao { 
    void save();
}

public class BookDaoImpl implements BookDao { 
    public void save() {
        System.out.println("book dao save ..."); 
    }
}

public interface BookService { 
    void save();
}

public class BookServiceImpl implements BookService { 
    private BookDao bookDao = new BookDaoImpl(); 
    public void save() {
        System.out.println("book service save ..."); 
        bookDao.save();
    }
}

步骤4:添加spring配置文件

步骤5:在配置文件中完成bean的配置

<?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">

    <!--1.导入Spring的坐标Spring-context,对应版本是5.2.19.RELEASE-->

    <!--2.配置bean-->
   <!-- bean标签配置bean
    id属性表示给bean起名字
    class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.green.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.green.service.impl.BookServiceImpl"/>

</beans>

注意事项:bean定义时id属性在同一个上下文中(配置文件)不能重复

步骤6:获取IOC容器

//使用Spring提供的接口完成IOC容器的创建,创建类,编写main方法
public class App2 {
    public static void main(String[] args) {
        //3.获取IoC容器
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
    }
}

步骤7:从容器中获取对象进行方法调用

public class App2 {
    public static void main(String[] args) {
        //3.获取IoC容器
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        //4.获取bean
        /*BookDao bookDao = (BookDao) cxt.getBean("bookDao");
        bookDao.save();*/

        BookService bookService = (BookService) cxt.getBean("bookService");

        bookService.save();
    }
}

DI入门案例

入门案例思路分析

(1)要想实现依赖注入,必须要基于IOC管理Bean 
    DI的入门案例要依赖于前面IOC的入门案例
(2)Service中使用new形式创建的Dao对象是否保留?
    需要删除掉,最终要使用IOC容器中的bean对象
(3)Service中需要的Dao对象如何进入到Service中?
    在Service中提供方法,让Spring的IOC容器可以通过该方法传入bean对象
(4)Service与Dao间的关系如何描述?
    使用配置文件

入门案例代码实现

需求:基于IOC入门案例,在BookServiceImpl类中删除new对象的方式,
使用Spring的DI完成Dao层的注入
1.删除业务层中使用new的方式创建的dao对象
2.在业务层提供BookDao的setter方法
3.在配置文件中添加依赖注入的配置
4.运行程序调用方法

public class BookServiceImpl implements BookService {

    //5. 删除业务层中使用new的方式创建的dao对象
//    private BookDao bookDao = new BookDaoImpl();
    private BookDao bookDao;

    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }

    //6. 提供对应的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}
步骤3:修改配置完成注入
在配置文件中添加依赖注入的配置
<?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">

    <!--1.导入Spring的坐标Spring-context,对应版本是5.2.19.RELEASE-->

    <!--2.配置bean-->
   <!-- bean标签配置bean
    id属性表示给bean起名字
    class属性表示给bean定义类型-->
    <bean id="bookDao" class="com.green.dao.impl.BookDaoImpl"/>

<!--    <bean id="bookService" class="com.green.service.impl.BookServiceImpl"/>-->

<!--    7.配置service与dao的关系-->
    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <!--property标签表示配置当前bean的属性
        name属性表示配置哪一个具体属性
        ref属性表示参照哪一个bean
        -->
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>

注意:配置中的两个bookDao的含义是不一样的

bean的配置

bean基础配置

bean别名配置

beanname属性

步骤1:配置别名 

<?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">

    <!--1.导入Spring的坐标Spring-context,对应版本是5.2.19.RELEASE-->

    <!--2.配置bean-->
   <!-- bean标签配置bean
    id属性表示给bean起名字
    class属性表示给bean定义类型
    name属性定义bean的别名,可以定义多个,使用逗号、空格、分号分隔-->
    <bean id="bookDao" name="dao bookDao2" class="com.green.dao.impl.BookDaoImpl"/>

<!--    <bean id="bookService" class="com.com.green.service.impl.BookServiceImpl"/>-->

<!--    7.配置service与dao的关系-->
    <bean id="bookService" name="service" class="com.green.service.impl.BookServiceImpl">
        <!--property标签表示配置当前bean的属性
        name属性表示配置哪一个具体属性
        ref属性表示参照哪一个bean
        -->
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>

步骤2:根据名称容器中获取bean对象

public class AppForName {
    public static void main(String[] args) {
        //3.获取IoC容器
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        //4.获取bean
        //此处根据bean标签的id属性和name属性的任意一个值来获取bean对象
        BookService bookService = (BookService) cxt.getBean("bookService");
        //BookService bookService = (BookService) cxt.getBean("service");//别名
        bookService.save();
    }
}

bean作用范围scope配置

 

 验证IOC容器中对象是否为单例

验证思路
    同一个bean获取两次,将对象打印到控制台,看打印出的地址值是否一致。
具体实现
    创建一个AppForScope的类,在其main方法中来验证
public class AppForScope {
    public static void main(String[] args) {
        //3.获取IoC容器
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        //4.获取bean
        BookDao bookDao1 = (BookDao) cxt.getBean("bookDao");
        BookDao bookDao2 = (BookDao) cxt.getBean("bookDao");
        bookDao1.save();
        System.out.println(bookDao1);
        System.out.println(bookDao2);
    }
}

印,观察控制台的打印结果

结论:默认情况下,Spring创建的bean对象都是单例的

 配置bean为非单例

<?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的scope 属性可以控制bean的创建是否为单例:
    singleton 默认为单例
    prototype 为非单例-->
    <bean id="bookDao" name="dao bookDao2" class="com.green.dao.impl.BookDaoImpl" scope="prototype"/>

    <bean id="bookService" name="service" class="com.green.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>
为什么bean默认为单例?
bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,

性能高bean在容器中是单例的,会不会产生线程安全问题?
如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,
因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,
因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。

哪些bean对象适合交给容器进行管理?
表现层对象
业务层对象
数据层对象
工具对象

哪些bean对象不适合交给容器进行管理?
封装实例的域对象,因为会引发线程安全问题,所以不适合。

bean实例化

bean是如何创建的
实例化bean的三种方式, 构造方法 , 静态工厂 和 实例工厂

构造方法实例化

public class BookDaoImpl implements BookDao {

    public BookDaoImpl() {
        System.out.println("book dao constructor is running...");
    }
    public void save() {
        System.out.println("book dao save...");
    }
}
<?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="com.green.dao.impl.BookDaoImpl"/>

</beans>
public class AppForInstanceBook {
    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cxt.getBean("bookDao");
        bookDao.save();
    }
}

静态工厂实例化

工厂方式创建bean

//(1)准备一个OrderDao和OrderDaoImpl类

public interface OrderDao {
    void save();
}


public class OrderDaoImpl implements OrderDao {
    @Override
    public void save() {
        System.out.println("order dao save....");
    }
}
//(2)创建一个工厂类OrderDaoFactory并提供一个静态方法
public class OrderDaoFactory {
    public static OrderDao getOrderDao() {
        System.out.println("factory setup...");
        return new OrderDaoImpl();
    }
}

//(3)编写AppForInstanceOrder运行类,在类中通过工厂获取对象
public class AppForInstanceOrder {
    public static void main(String[] args) {

        //通过静态工厂创建对象
//        OrderDao orderDao = OrderDaoFactory.getOrderDao();
//        orderDao.save();

        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        OrderDao orderDao = (OrderDao) cxt.getBean("orderDao");
        orderDao.save();
    }
}
    <!--方式二:使用静态工厂实例化bean
    class:工厂类的类全名
    factory-mehod:具体工厂类中创建对象的方法名
    -->
    <bean id="orderDao" class="com.green.dao.factory.OrderDaoFactory" factory-method="getOrderDao"/>

实例工厂与FactoryBean

//(1)准备一个UserDao和UserDaoImpl类
public interface UserDao {
    void save();
}

public class UserDaoImpl implements UserDao {
    @Override
    public void save() {
        System.out.println("user dao save...");
    }
}

//(2)创建一个工厂类OrderDaoFactory
public class UserDaoFactory {
    public UserDao getUserDao() {
        return new UserDaoImpl();
    }
}
//(3)编写AppForInstanceUser运行类

public class AppForInstanceUser {
    public static void main(String[] args) {
//        //创建实例化工厂对象
//        UserDaoFactory userDaoFactory = new UserDaoFactory();
//        //通过实例化工厂对象创建对象
//        UserDao userDao = userDaoFactory.getUserDao();
//        userDao.save();

        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserDao userDao = (UserDao) cxt.getBean("userDao");
        userDao.save();
    }
}
<!--    方式三:使用实例化工厂实例化bean-->
    <bean id="userDaoFactory" class="com.green.dao.factory.UserDaoFactory"/>
    <bean id="userDao" factory-method="getUserDao" factory-bean="userDaoFactory"/>

 FactoryBean的使用

//(1)创建一个UserDaoFactoryBean的类,实现FactoryBean接口,重写接口的方法
public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例化工厂中创建对象的方法
    @Override
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }

    //创建对象的类型
    @Override
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}
<!--    方式四:使用FactoryBean实例化bean-->
    <bean id="userDao" class="com.green.dao.factory.UserDaoFactoryBean"/>

bean的生命周期

//项目中添加BookDao、BookDaoImpl、BookService和BookServiceImpl类
public interface BookDao {
    void save();
}

public class BookDaoImpl implements BookDao {

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

    //表示bean初始化对应的操作
    public void init(){
        System.out.println("init...");
    }
    //表示bean销毁前对应的操作
    public void destroy(){
        System.out.println("destroy...");
    }
}

public interface BookService {
    void save();
}

public class BookServiceImpl implements BookService, InitializingBean, DisposableBean {

    private BookDao bookDao;
    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
    @Override
    public void destroy() throws Exception {
        System.out.println("service destroy...");
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("service init...");
    }
}
//编写AppForLifeCycle运行类,加载Spring的IOC容器,并从中获取对应的bean对象
public class AppForLifeCycle {
    public static void main(String[] args) {
        //ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        //使用ClassPathXmlApplicationContext可以调用close方法关闭容器
        ClassPathXmlApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cxt.getBean("bookDao");
        bookDao.save();
        //注册关闭容器钩子
        cxt.registerShutdownHook();
        //cxt.close();//关闭方法相对暴力
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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="com.green.dao.impl.BookDaoImpl" init-method="init" destroy-method="destroy"/>

    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>

bean生命周期小结

(1)关于Spring中对bean生命周期控制提供了两种方式:
    在配置文件中的bean标签中添加init-method 和destroy-method 属性
    类实现InitializingBean 与DisposableBean 接口,这种方式了解下即可。
(2)对于bean的生命周期控制在bean的整个生命周期中所处的位置如下:
    初始化容器
        1.创建对象(内存分配) 
        2.执行构造方法
        3.执行属性注入(set操作) 
        4.执行bean初始化方法
    使用bean
        执行业务操作
        关闭/销毁容器
(3)关闭容器的两种方式:
    ConfigurableApplicationContext是ApplicationContext的子类
        close()方法
        registerShutdownHook()方法

DI相关内容

DI依赖注入

向一个类中传递数据的方式有几种?
    普通方法(set方法)
    构造方法
依赖注入描述了在容器中建立bean与bean之间的依赖关系的过程,
如果bean运行需要的是数字或字符串呢?
    引用类型
    简单类型(基本数据类型与String)
两种注入方式,分别是:
    setter注入
        简单类型
        引用类型
    构造器注入
        简单类型
        引用类型

setter注入

//在bean中定义引用类型属性,并提供可访问的set方法
    public class BookServiceImpl implements BookService {
        private BookDao bookDao;

        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }
    }
//配置中使用property标签ref属性注入引用类型对象
    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>
    <bean id="bookDao" class="com.green.dao.impl.BookDaoImpl"/>
(1)项目中添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl类

    public interface BookDao { 
        public void save();
    }

    public class BookDaoImpl implements BookDao {
        public void save() {
            System.out.println("book dao save ...");
        }
    }

    public interface UserDao {
        public void save();
    }

    public class UserDaoImpl implements UserDao {
        public void save() {
            System.out.println("user dao save ...");
        }
    }

    public interface BookService {
        public void save();
    }

    public class BookServiceImpl implements BookService {
        private BookDao bookDao;
        public void setBookDao(BookDao bookDao) {
            this.bookDao = bookDao;
        }

        public void save() {
            System.out.println("book service save ...");
            bookDao.save();
        }
    }
(3)编写AppForDISet运行类,加载Spring的IOC容器,并从中获取对应的bean对象
public class AppForDISet {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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="com.green.dao.impl.BookDaoImpl"/>

    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
    </bean>
</beans>

 注入引用数据类型

需求:在bookServiceImpl对象中注入userDao 
1.在BookServiceImpl中声明userDao属性
2.为userDao属性提供setter方法
3.在配置文件中使用property标签注入

步骤1:声明属性并提供setter方法

//在BookServiceImpl中声明userDao属性,并提供setter方法
public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    private UserDao userDao;

    //setter注入需要提供要注入对象的set方法
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    //setter注入需要提供要注入对象的set方法
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}

步骤2:置文件中进行注入配置

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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="com.green.dao.impl.BookDaoImpl"/>

    <bean id="userDao" class="com.green.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>

注入简单数据类型

需求:给BookDaoImpl注入一些简单数据类型的数据
参考引用数据类型的注入,可以推出具体的步骤为: 
1.在BookDaoImpl类中声明对应的简单数据类型的属性
2.为这些属性提供对应的setter方法
3.在applicationContext.xml中配置

步骤1:声明属性并提供setter方法

//在BookDaoImpl类中声明对应的简单数据类型的属性,并提供对应的setter方法
public class BookDaoImpl implements BookDao {
    //简单类型
    private String databaseName;
    private int connectionNum;
    //setter注入需要提供要注入对象的set方法
    public void setConnectionNum(int connectionNum) {
        this.connectionNum = connectionNum;
    }
    //setter注入需要提供要注入对象的set方法
    public void setDatabaseName(String databaseName) {
        this.databaseName = databaseName;
    }
    public void save() {
        // System.out.println("book dao save ...");
        System.out.println("book dao save ..." + databaseName + "," + connectionNum);
    }
}

步骤2:置文件中进行注入配置

<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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="com.green.dao.impl.BookDaoImpl"/>-->

    <!--配置简单类型的bean-->
    <bean id="bookDao" class="com.green.dao.impl.BookDaoImpl">
        <property name="databaseName" value="mysql"/>
        <property name="connectionNum" value="10"/>
    </bean>

    <bean id="userDao" class="com.green.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <property name="bookDao" ref="bookDao"/>
        <property name="userDao" ref="userDao"/>
    </bean>
</beans>
说明:
value:后面跟的是简单数据类型,对于参数类型,Spring在注入的时候会自动转换,但是不能写成
<property name="connectionNum" value="abc"/>

对于引用数据类型使用的是<property  name=""  ref=""/>
对于简单数据类型使用的是<property  name=""  value=""/>

构造器注入

//(1)项目中添加BookDao、BookDaoImpl、UserDao、UserDaoImpl、BookService和BookServiceImpl类
public interface BookDao {
     void save();
}

public interface UserDao {
     void save();
}

public interface BookService {
     void save();
}

public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    private UserDao userDao;

    //构造方法注入
//    public BookServiceImpl(BookDao bookDao) {
//        this.bookDao = bookDao;
//    }

    public BookServiceImpl(BookDao bookDao, UserDao userDao) {
        this.bookDao = bookDao;
        this.userDao = userDao;
    }

    public void save() {
        System.out.println("book service save ...");
        bookDao.save();
        userDao.save();
    }
}

public class BookDaoImpl implements BookDao {

    //简单类型
    private String databaseName;
    private int connectionNum;

    //构造方法
    public BookDaoImpl(String databaseName, int connectionNum) {
        this.databaseName = databaseName;
        this.connectionNum = connectionNum;
    }

    public void save() {
        System.out.println("book dao save ..."+databaseName+","+connectionNum);
    }
}

public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("user dao save ...");
    }
}
//编写AppForDIConstructor运行类,加载Spring的IOC容器,并从中获取对应的bean对象
public class AppForDIConstructor {
    public static void main( String[] args ) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) ctx.getBean("bookService");
        bookService.save();
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<!--suppress SpringFacetInspection -->
<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="com.green.dao.impl.BookDaoImpl">
        <constructor-arg name="connectionNum" value="666"/>
        <constructor-arg name="databaseName" value="mysql"/>
    </bean>
    <bean id="userDao" class="com.green.dao.impl.UserDaoImpl"/>

    <bean id="bookService" class="com.green.service.impl.BookServiceImpl">
        <!--此处name属性是构造函数的形参名-->
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>
    </bean>
</beans>
说明:标签中
name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
ref属性指向的是spring的IOC容器中其他bean对象。
当构造函数中方法的参数名发生变化后,配置文件中的name属性也需要跟着变
这两块存在紧耦合,具体该如何解决?
方式一:删除name属性,添加type属性,按照类型注入
<bean id="bookDao" class="com.green.dao.impl.BookDaoImpl">
    <constructor-arg type="int" value="10"/>
    <constructor-arg type="java.lang.String" value="mysql"/>
</bean>
这种方式可以解决构造函数形参名发生变化带来的耦合问题
但是如果构造方法参数中有类型相同的参数,这种方式就不太好实现了

方式二:删除type属性,添加index属性,按照索引下标注入,下标从0开始
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl">
    <constructor-arg index="1" value="100"/>
    <constructor-arg index="0" value="mysql"/>
</bean>
这种方式可以解决参数类型重复问题
但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
两种参数的注入方式,具体该如何选择呢?
1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
    强制依赖指对象在创建的过程中必须要注入指定的参数
2. 可选依赖使用setter注入进行,灵活性强
    可选依赖指对象在创建过程中注入的参数可有可无
3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
6. 自己开发的模块推荐使用setter注入
setter注入
    简单数据类型
    <bean ...>
        <property name="" value=""/>
    </bean>

    引用数据类型
    <bean ...>
        <property name="" ref=""/>
    </bean>

构造器注入
    简单数据类型
    <bean ...>
        <constructor-arg name="" index="" type="" value=""/>
    </bean>
    引用数据类型
    <bean ...>
        <constructor-arg name="" index="" type="" ref=""/>
    </bean>

自动配置

什么是依赖自动装配?
IoC容器根据bean所依赖的资源在容器中自动查找并注入到bean中的过程称为自动装配
自动装配方式有哪些?
按类型(常用)
按名称
按构造方法
不启用自动装配
//(1)项目中添加BookDao、BookDaoImpl、BookService和BookServiceImpl类
public interface BookDao {
    void save();
}

public class BookDaoImpl implements BookDao {

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

public interface BookService {
    void save();
}

public class BookServiceImpl implements BookService {

    private BookDao bookDao;

    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }

    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
}
//编写AppForAutoware运行类,加载Spring的IOC容器,并从中获取对应的bean对象
public class AppForDiAutoWare {
    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookService bookService = (BookService) cxt.getBean("bookService");
        bookService.save();
    }
}
<?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="com.green.dao.impl.BookDaoImpl"/>-->
    <bean class="com.green.dao.impl.BookDaoImpl"/>
    <!--按类型装配-->
    <bean id="bookService" class="com.green.service.impl.BookServiceImpl" autowire="byType"/>
</beans>
1.自动装配用于引用类型依赖注入,不能对简单类型进行操作
2.使用按类型装配时(byType)必须保障容器中相同类型的bean唯一,推荐使用
3.使用按名称装配时(byName)必须保障容器中具有指定名称的bean,因变量名与配置耦合,不推荐使用
4.自动装配优先级低于setter注入与构造器注入,同时出现时自动装配配置失效

集合注入

数组
List
Set
Map 
Properties
//1)项目中添加添加BookDao、BookDaoImpl类
public interface BookDao {
    void save();
}

package com.green.dao.impl;

import com.green.dao.BookDao;

import java.util.*;

public class BookDaoImpl implements BookDao {
    private int[] array;

    private List<String> list;

    private Set<String> set;

    private Map<String, String> map;

    private Properties properties;

    public void setArray(int[] array) {
        this.array = array;
    }

    public void setList(List<String> list) {
        this.list = list;
    }

    public void setSet(Set<String> set) {
        this.set = set;
    }

    public void setMap(Map<String, String> map) {
        this.map = map;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void save() {
        System.out.println("book dao save...");

        System.out.println("遍历数组:" + Arrays.toString(array));

        System.out.println("遍历List:" + list);

        System.out.println("遍历Set:" + set);

        System.out.println("遍历Map:" + map);

        System.out.println("遍历Properties:" + properties);
    }
}
//编写AppForDICollection运行类,加载Spring的IOC容器,并从中获取对应的bean对象
public class AppForDiCollection {
    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");

        BookDao bookDao = (BookDao) cxt.getBean("bookDao");

        bookDao.save();
    }
}
<?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="com.green.dao.impl.BookDaoImpl">
        <property name="array">
            <array>
                <value>100</value>
                <value>200</value>
                <value>300</value>
            </array>
        </property>
        <property name="list">
            <list>
                <value>java</value>
                <value>mysql</value>
                <value>spring</value>
            </list>
        </property>
        <property name="set">
            <set>
                <value>java</value>
                <value>mysql</value>
                <value>spring</value>
                <value>spring</value>
            </set>
        </property>
        <property name="map">
            <map>
                <entry key="county" value="china"/>
                <entry key="province" value="guangdong"/>
                <entry key="city" value="guangzhou"/>
            </map>
        </property>
        <property name="properties">
            <props>
                <prop key="county">china</prop>
                <prop key="province">guangdong</prop>
                <prop key="city">guangzhou</prop>
            </props>
        </property>
    </bean>
</beans>
说明:
property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写
<array> 、 <list> 、 <set> 、 <map> 、 <props> 标签
List的底层也是通过数组实现的,所以<list> 和<array> 标签是可以混用
集合中要添加引用类型,只需要把<value> 标签改成<ref> 标签,这种方式用的比较少

IOC/DI配置管理第三方bean

案例:数据源对象管理

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring_09_datasource</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>
    <dependencies>
        <dependency>
            <!--Spring的依赖-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.14</version>
        </dependency>
        <!--数据库连接池-->
        <dependency>
            <groupId>c3p0</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.1.2</version>
        </dependency>

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

思路分析 

需求:使用Spring的IOC容器来管理Druid连接池对象
1.使用第三方的技术,需要在pom.xml添加依赖
2.在配置文件中将【第三方的类】制作成一个bean,让IOC容器进行管理
3.数据库连接需要基础的四要素 驱动 、 连接 、 用户名 和 密码 ,【如何注入】到对应的bean中
4.从IOC容器中获取对应的bean对象,将其打印到控制台查看结果

 实现Druid和C3P0管理

<?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">

    <!--管理DruidDataSource对象-->
    <bean  class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db1"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
    </bean>
</beans>
//运行类
public class AppSystemProperties {
    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        DataSource dataSource = (DataSource) cxt.getBean("dataSource");
        System.out.println(dataSource);
    }
}

加载properties文件

实现思路

1.在resources下创建一个jdbc.properties(文件的名称可以任意)
2.将数据库连接四要素配置到配置文件中
3.在Spring的配置文件中加载properties文件
4.使用加载到的值实现属性注入

实现步骤

步骤1:准备properties配置文件

/resources下创建一个jdbc.properties文件,并添加对应的属性键值对
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://127.0.0.1:3306/spring_db 
jdbc.username=root
jdbc.password=123456

步骤2:开启context 命名空间

<?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
            ">

    <!--管理DruidDataSource对象-->
    <!--<bean  class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/db1"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>

    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.jdbc.Driver"/>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/db1"/>
        <property name="user" value="root"/>
        <property name="password" value="123456"/>
        <property name="maxPoolSize" value="1000"/>
    </bean>-->

    <!--加载properties文件-->

    <!--1.开启context命名空间-->
    <!--2.使用context空间加载properties文件-->
    <!--    <context:property-placeholder location="jdbc.properties"/>-->
    <!--system-properties-mode="NEVER不加载系统属性-->
    <!--    <context:property-placeholder location="jdbc.properties" system-properties-mode="NEVER"/>-->
    <!--    <context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties-mode="NEVER"/>-->
    <!--    <context:property-placeholder location="*.properties" system-properties-mode="NEVER"/>-->
    <!--    <context:property-placeholder location="classpath:*.properties" system-properties-mode="NEVER"/>-->
    <context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>

    <!--使用属性占位符${}读取properties文件中的属性-->
    <bean class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${jdbc.driver}"/>
        <property name="url" value="${jdbc.url}"/>
        <property name="username" value="${jdbc.username}"/>
        <property name="password" value="${jdbc.password}"/>
    </bean>

    <bean id="bookDao" class="com.green.dao.impl.BookDaoImpl">
        <property name="name" value="${jdbc.driver}"/>
    </bean>
</beans>

读取单个属性

需求:从properties配置文件中读取key为name的值,并将其注入到BookDao中并在save方法中进行打印。
1.在项目中添加BookDao和BookDaoImpl类
2.为BookDaoImpl添加一个name属性并提供setter方法
3.在jdbc.properties中添加数据注入到bookDao中打印方便查询结果
4.在applicationContext.xml添加配置完成配置文件加载、属性注入(${key})
public interface BookDao {
    void save();
}

public class BookDaoImpl implements BookDao {
    private String name;

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

    public void save() {
        System.out.println("book dao save..." + name);

    }
}

加载properties文件小结

如何开启context 命名空间

// 如何加载properties配置文件
<context:property-placeholder location="" system-properties-mode="NEVER"/>
//如何在applicationContext.xml引入properties配置文件中的值
${key}
<!--方式一 -->
<context:property-placeholder location="jdbc.properties,jdbc2.properties" system-properties- mode="NEVER"/>
<!--方式二-->
<context:property-placeholder location="*.properties" system-
properties-mode="NEVER"/>
<!--方式三 -->
<context:property-placeholder location="classpath:*.properties"
system-properties-mode="NEVER"/>
<!--方式四-->
<context:property-placeholder location="classpath*:*.properties" system-properties-mode="NEVER"/>
说明:
方式一:可以实现,如果配置文件多的话,每个都需要配置
方式二: *.properties 代表所有以properties结尾的文件都会被加载,可以解决方式一的问题,但是不标准
方式三:标准的写法,classpath: 代表的是从根路径下开始查找,但是只能查询当前项目的根路径
方式四:不仅可以加载当前项目还可以加载当前项目所依赖的所有项目的根路径下的properties配置文件

容器

容器的创建方式

public class AppContainer {
    public static void main(String[] args) {
        //1.加载类路径下的配置文件
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");

        //2.从文件系统下查找配置文件
        //ApplicationContext cxt = new FileSystemXmlApplicationContext("D:\code\spring_10_container\src\main\resources\applicationContext.xml");

//        BookDao bookDao = (BookDao) cxt.getBean("bookDao");
//        BookDao bookDao = cxt.getBean("bookDao",BookDao.class);
        BookDao bookDao = cxt.getBean(BookDao.class);//按类型加载bean
        bookDao.save();
    }
}
//容器的创建方式
方式一:类路径下的XML配置文件
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
方式二:文件系统下的XML配置文件    
ApplicationContext cxt = new FileSystemXmlApplicationContext
("D:\code\spring_10_container\src\main\resources\applicationContext.xml");

Bean的三种获取方式

方式一
  BookDao bookDao = (BookDao) ctx.getBean("bookDao");
方式二
  BookDao bookDao = ctx.getBean("bookDao",BookDao.class);
方式三
  BookDao bookDao = ctx.getBean(BookDao.class);
按类型注入,必须要确保IOC容器中该类型对应的bean对象只能有一个。

容器类层次结构

 BeanFactory的使用

public class AppForBeanFactory {
    public static void main(String[] args) {
        Resource resources = new ClassPathResource("applicationContext.xml"); 
        BeanFactory bf = new XmlBeanFactory(resources);
        BookDao bookDao = bf.getBean(BookDao.class); bookDao.save();
    }
}
//为了更好的看出BeanFactory 和ApplicationContext 之间的区别,
//在BookDaoImpl添加如下构造函数:
public class BookDaoImpl implements BookDao { 
    public BookDaoImpl() {
        System.out.println("constructor"); 
    }
    public void save() { 
        System.out.println("book dao save ..." );
    }
}
如果不去获取bean对象,打印会发现:
BeanFactory是延迟加载,只有在获取bean对象的时候才会去创建
ApplicationContext是立即加载,容器加载的时候就会创建bean对象
ApplicationContext要想成为延迟加载,只需要按照如下方式进行配置
<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl" lazy- init="true"/>
小结
容器创建的两种方式
    ClassPathXmlApplicationContext[掌握]
    FileSystemXmlApplicationContext[知道即可]
获取Bean的三种方式
    getBean("名称"):需要类型转换
    getBean("名称",类型.class):多了一个参数
    getBean(类型.class):容器中不能有多个该类的bean对象
上述三种方式,各有各的优缺点,用哪个都可以。

容器类层次结构
只需要知晓容器的最上级的父接口为 BeanFactory即可
BeanFactory
    使用BeanFactory创建的容器是延迟加载
    使用ApplicationContext创建的容器是立即加载
具体BeanFactory如何创建只需要了解即可。

bean相关

<bean 
	id = "bookDao"            							bean的id
	name = "dao bookDaoImpl daoImpl" 					bean的别名
	class = "com.green.dao.impl.bookDaoImpl"			bean类型,静态工厂,FactoryBean类
	scope = "singleton"									控制bean的实例化对象,默认为单例
	init-method = "init"								生命周期初始化方法
	destroy-method = "destroy"							生命周期销毁方法
	autowire = "bytype"									自动装配类型
	factory-bean = "com.green.factory.BookDaoFactory"   实例工厂bean
	lazy-init = "true"								    控制bean延迟加载
	/>

依赖注入相关

IOC/DI注解开发

public interface BookDao {
    void save();
}

//@Component("bookDao")
@Repository("bookDao")
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save...");
    }
}

public interface BookService {
    void save();
}

//@Component
@Service
public class BookServiceImpl implements BookService {

    private BookDao bookDao;

    public void save() {
        System.out.println("book service save...");
        bookDao.save();
    }
}
//运行类
public class AppAnnotation {
    public static void main(String[] args) {
        ApplicationContext cxt = new ClassPathXmlApplicationContext("applicationContext.xml");
        BookDao bookDao = (BookDao) cxt.getBean("bookDao");
        System.out.println(bookDao);

        BookService bookService = cxt.getBean(BookService.class);
        System.out.println(bookService);
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!--扫描-->
    <!--    <context:component-scan base-package="com.green.dao.impl"/>-->
    <context:component-scan base-package="com.green"/>
</beans>

注意:@Component注解不可以添加在接口上,因为接口是无法创建对象的。

说明:
component-scan
component:组件,Spring将管理的bean视作自己的一个组件scan:扫描
base-package指定Spring框架扫描的包路径,它会扫描指定包及其子包中的所有类上的注解。
包路径越多[如:com.itheima.dao.impl],扫描的范围越小速度越快
包路径越少[如:com.itheima],扫描的范围越大速度越慢
一般扫描到项目的组织名称即Maven的groupId下[如:com.green]即可

BookServiceImpl类没有起名称,所以在App中是按照类型来获取bean对象
@Component注解如果不起名称,会有一个默认值就是 当前类名首字母小写 

对于@Component注解,还衍生出了其他三个注解@Controller 、@Service 、@Repository
区分出这个类是属于 表现层 、 业务层 还是 数据层 的类

名称

@Component/@Controller/@Service/@Repository

类型

类注解

位置

类定义上方

作用

设置该类为spring管理的bean

属性

value(默认):定义beanid

纯注解开发模式 

Spring3.0开启了纯注解开发模式,使用Java类替代配置文件,开启了Spring快速开发赛道

思路分析

将配置文件applicationContext.xml删除掉,使用类来替换。

实现步骤

步骤1:创建配置类

创建一个配置类SpringConfig

 步骤2:识该类为配置类

在配置类上添加@Configuration 注解,将其标识为一个配置类,替换applicationContext.xml

 步骤3:用注解替换包扫描配置

在配置类上添加包扫描注解@ComponentScan替换<context:component-scan base-package=""/>

 步骤4:建运行类并执行

//创建一个新的运行类AppForAnnotation
public class AppForAnnotation {
    public static void main(String[] args) {
        //加载配置类初始化容器
        ApplicationContext cxt = new AnnotationConfigApplicationContext(SpringConfig.class);

        //配置bean
        BookDao bookDao = (BookDao) cxt.getBean("bookDao");
        System.out.println(bookDao);

        BookService bookService = cxt.getBean(BookService.class);
        System.out.println(bookService);
    }
}

Java类替换Spring心配置文件 

@Configuration注解用于设定当前类为配置类
@ComponentScan注解用于设定扫描路径,此注解只能添加一次,多个数据请用数组格式
  @ComponentScan({com.green.service","com.green.dao"})
读取Spring核心配置文件初始化容器对象切换为读取Java配置类初始化容器对象

 

 知识点1@Configuration

名称

@Configuration

类型

类注解

位置

类定义上方

作用

设置该类为spring配置类

属性

value(默认):定义beanid

知识点2@ComponentScan

名称

@ComponentScan

类型

类注解

位置

类定义上方

作用

设置spring配置类扫描路径,用于加载使用注解格式定义的bean

属性

value默认):扫描路径,此路径可以逐层向下扫描

小结
记住@Component、@Controller、@Service、@Repository这四个注解
applicationContext.xml中<context:component-san/> 的作用是指定扫描包路径,注解为
@ComponentScan
@Configuration标识该类为配置类,使用类替换applicationContext.xml文件ClassPathXmlApplicationContext是加载XML配置文件
AnnotationConfigApplicationContext是加载配置类

 注解开发bean作用范围与生命周期管理

pom.xml添加依赖

    <dependencies>
        <dependency>
            <!--Spring的依赖-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

        <!--注解-->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>
    </dependencies>
@Configuration
@ComponentScan("com.green")
public class SpringConfig {
}

public interface BookDao {
    void save();
}

@Repository
@Scope("singleton") //配置是否单例
public class BookDaoImpl implements BookDao {

    @PostConstruct
    public void init(){
        System.out.println("init...");
    }

    @PreDestroy
    public void destroy(){
        System.out.println("destroy...");
    }

    @Override
    public void save() {
        System.out.println("dao save ...");
    }
}
//运行类
public class App {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(SpringConfig.class);

        BookDao bookDao1 = cxt.getBean(BookDao.class);
        BookDao bookDao2 = cxt.getBean(BookDao.class);

        System.out.println(bookDao1);
        System.out.println(bookDao2);

    }
}

名称

@Scope

@PostConstruct

@PreDestroy

类型

类注解

方法注解方法注解

位置

类定义上方

方法上方法上

作用

设置该类创建对象的作用范围

可用于设置创建出的bean是否为单例对象

设置给方法为初始化方法设置该方法为销毁方法

属性

value(默认):定义bean作用范围,

默认值singleton(单例),可选值prototype非单例)

注解开发依赖注入

自动装配

添加一个配置类SpringConfig

//添加BookDao、BookDaoImpl、BookService、BookServiceImpl类
public interface BookDao {
    public void save(); 
}

@Repository
public class BookDaoImpl implements BookDao {
    public void save() {
        System.out.println("book dao save ..." );
    }
}

public interface BookService {
    public void save(); 
}

@Service
public class BookServiceImpl implements BookService {
    private BookDao bookDao;
    public void setBookDao(BookDao bookDao) {
        this.bookDao = bookDao;
    }
    public void save() {
        System.out.println("book service save ..."); 
        bookDao.save();
    }
}
//创建运行类
public class AppDi {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(SpringConfig.class);
        BookService bookService = cxt.getBean(BookService.class);
        bookService.save();
    }
}

 注解实现按照类型注入

(1)  在BookServiceImpl类的bookDao属性上添加@Autowired 注解

 

注意:
@Autowired可以写在属性上,也可也写在setter方法上,
最简单的处理方式是写在属性上并将setter方法删除掉

为什么setter方法可以删除呢?
自动装配基于反射设计创建对象并通过暴力反射为私有属性进行设值
普通反射只能获取public修饰的内容
暴力反射除了获取public修饰的内容还可以获取private修改的内容
所以此处无需提供setter方法
(2)@Autowired是按照类型注入,那么对应BookDao接口如果有多个实现类,比如添加
BookDaoImpl2

 

这个时候再次运行,就会报错
此时,按照类型注入就无法区分到底注入哪个对象,解决方案: 按照名称注入
先给两个Dao类分别起个名称

 

注解实现按照名称注入

使用到@Qualifier 来指定注入哪个名称的bean对象
@Qualifier注解后的值就是需要注入的bean的名称。

 

 注意:@Qualifier不能独立使用,必须和@Autowired一起使用

简单数据类型注入

数据类型换了,对应的注解也要跟着换,这次使用 @Value 注解,将值写入注解的参数中就行了
注意数据格式要匹配,如将"abc"注入给int值,这样程序就会报错。

 

 注解读取properties配置文件

步骤1resource下准备properties文件

jdbc.properties

name=book

步骤2:  使用注解加载properties配置文件

在配置类上添加@PropertySource 注解

 步骤3:使用@Value读取配置文件中的内容

注意:
如果读取的properties配置文件有多个,可以使用@PropertySource 的属性来指定多个
  @PropertySource({"jdbc.properties","xxx.properties"})

@PropertySource 注解属性中不支持使用通配符* ,运行会报错
  @PropertySource({"*.properties"})

@PropertySource 注解属性中可以把classpath: 加上,代表从当前项目的根路径找文件
  @PropertySource({"classpath:jdbc.properties"})

名称

@Autowired

类型

属性注解 方法注解(了解) 方法形参注解(了解)

位置

属性定义上方 标准set方法上方 set方法上方 方法形参前面

作用

为引用类型属性设置值

属性

requiredtrue/false,定义该属性是否允许为null

名称

@Qualifier

类型

属性注解 方法注解(了解)

位置

属性定义上方 标准set方法上方 set方法上方

作用

为引用类型属性指定注入的beanId

属性

value(默认):设置注入的beanId

名称

@Value

类型

属性注解 方法注解(了解)

位置

属性定义上方 标准set方法上方 set方法上方

作用

基本数据类型 字符串类型 属性设置值

属性

value(默认):要注入的属性值

名称

@PropertySource

类型

类注解

位置

类定义上方

作用

加载properties文件中的属性值

属性

value(默认):设置加载的properties件对应的文件名或文件名组成的数组

IOC/DI注解开发管理第三方bean 

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring_14_annotation_third_bean</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>

    <dependencies>
        <dependency>
            <!--Spring的依赖-->
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

        <!--注解-->
        <dependency>
            <groupId>javax.annotation</groupId>
            <artifactId>javax.annotation-api</artifactId>
            <version>1.3.2</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>
    </dependencies>

</project>

添加一个配置类SpringConfig

 添加BookDaoBookDaoImpl

//运行类
public class AppThird {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext cxt = new AnnotationConfigApplicationContext(SpringConfig.class);
        DataSource dataSource = cxt.getBean(DataSource.class);
        System.out.println(dataSource);
    }
}

注解开发管理第三方bean

在配置类中添加一个方法
@Bean注解的作用是将方法的返回值制作为Spring管理的一个bean对象
注意:不能使用DataSource ds = new DruidDataSource()
因为DataSource接口中没有对应的setter方法来设置属性。
如果有多个bean要被Spring管理,直接在配置类中多些几个方法,方法上添加@Bean注解即可

 引入外部配置类

//对于数据源的bean,新建一个JdbcConfig 配置类,并把数据源配置到该类下。

方式一:使用包扫描引入

Spring的配置类上添加包扫描

 步骤2:JdbcConfig上添加配置注解

JdbcConfig类要放入到com.green.config 包下,需要被Spring的配置类扫描到即可

 方式二:使用@Import 引入

步骤1:去除JdbcConfig类上的注解
步骤2:在Spring配置类中引入

 

注意:
扫描注解可以移除
@Import参数需要的是一个数组,可以引入多个配置类。
@Import注解在配置类中只能写一次,下面的方式是不允许的

名称

@Bean

@Import

类型

方法注解

类注解

位置

方法定义上方

类定义上方

作用

设置该方法的返回值作为spring管理的bean

导入配置类

属性

value(默认):定义beanid

value(默认):定义导入的配置类类名,

当配置类有多个时使用数组格式一次性导入多个配置类

注解开发实现为第三方bean注入资源

简单数据类型

 引用数据类型

步骤1:在SpringConfig中扫描BookDao
扫描的目的是让Spring能管理到BookDao,也就是说要让IOC容器中有一个bookDao对象
步骤2:在JdbcConfig类的方法上添加参数
引用类型注入只需要为bean定义方法设置形参即可,容器会根据类型自动装配对象。

注解开发总结

Spring整合

Spring整合Mybatis

整合思路分析

从图中可以获取到,真正需要交给Spring管理的是SqlSessionFactory

 

说明:
第一行读取外部properties配置文件,Spring有提供具体的解决方案@PropertySource ,需要交给Spring
第二行起别名包扫描,为SqlSessionFactory服务的,需要交给Spring
第三行主要用于做连接池,Spring之前我们已经整合了Druid连接池,这块也需要交给Spring
前面三行一起都是为了创建SqlSession对象用的,那么用Spring管理SqlSession对象吗?
回忆下SqlSession是由SqlSessionFactory创建出来的,
所以只需要将SqlSessionFactory交给Spring管理即可。
第四行是Mapper接口和映射文件[如果使用注解就没有该映射文件],这个是在获取到SqlSession以后执行具体操作的时候用,所以它和SqlSessionFactory创建的时机都不在同一个时间,可能需要单独管理。

 

步骤1:备数据库表

 步骤2:创建项目导入jar

<?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">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring_15_mybatis</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <maven.compiler.source>13</maven.compiler.source>
        <maven.compiler.target>13</maven.compiler.target>
    </properties>

    <dependencies>
        <!--spring依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>
        <!--数据库连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.16</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.6</version>
        </dependency>

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.47</version>
        </dependency>

        <!--spring-jdbc整合-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.10.RELEASE</version>
        </dependency>

        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>1.3.0</version>
        </dependency>
    </dependencies>
</project>

步骤3:据表创建模型类

 步骤4:创建Dao接口

public interface AccountDao {

    //添加数据
    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    void save(Account account);

    //删除数据
    @Delete("delete from tbl_account where id = #{id} ")
    void delete(Integer id);

    //根据id修改数据
    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    void update(Account account);

    //查询所有数据
    @Select("select * from tbl_account")
    List<Account> findAll();

    //根据id查询数据
    @Select("select * from tbl_account where id = #{id} ")
    Account findById(Integer id);
}

 步骤5:创建Service接口和实现类

public interface AccountService {

    void save(Account account);//添加

    void delete(Integer id);//删除

    void update(Account account);//修改

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

    Account findById(Integer id);//根据id查询数据
}


@Service
public class AccountServiceImpl implements AccountService {

    //自动装配
    @Autowired
    private AccountDao accountDao;

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

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

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

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

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

 步骤6:添加jdbc.properties文件

创建Spring的主配置类


@Configuration
@ComponentScan("com.green") //包扫描
//@PropertySource:加载类路径jdbc.properties文件
@PropertySource("classpath:jdbc.properties")//读properties并引入数据源配置类
@Import({JdbcConfig.class,MybatisConfig.class}) //导入配置类
public class SpringConfig {
}

创建数据源的配置类

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;

    @Bean
    public DataSource dataSource(){
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(driver);
        ds.setUrl(url);
        ds.setUsername(userName);
        ds.setPassword(password);
        return ds;
    }
}

创建Mybatis配置类并配置SqlSessionFactory

public class MybatisConfig {

    //定义bean,SqlSessionFactoryBean,用于产生SqlSessionFactory对象
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
        //设置模型类的别名扫描
        ssfb.setTypeAliasesPackage("com.green.domain");
        //设置数据源
        ssfb.setDataSource(dataSource);
        return ssfb;
    }

    //定义bean,返回MapperScannerConfigurer对象
    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer(){
        MapperScannerConfigurer msc = new MapperScannerConfigurer();
        msc.setBasePackage("com.green.dao");
        return msc;
    }
}
使用SqlSessionFactoryBean封装SqlSessionFactory需要的环境信息
使用MapperScannerConfigurer加载Dao接口,创建代理对象保存到IOC容器中

 步骤7:编写运行类

public class AppMybatisBean {
    public static void main(String[] args) {
        ApplicationContext cxt = new AnnotationConfigApplicationContext(SpringConfig.class);

        AccountService accountService = cxt.getBean(AccountService.class);

        Account ac = accountService.findById(1);

        System.out.println(accountService.findAll());

        System.out.println(ac);
    }
}

Spring整合Junit

直接使用SpringMybatis整合的环境

 步骤1:入依赖

 步骤2:编写测试类

//类运行器
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes =  SpringConfig.class) //上下文配置
public class AccountServiceTest {

    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById(){
        System.out.println(accountService.findById(1));
    }

    @Test
    public void testFindAll(){
        System.out.println(accountService.findAll());
    }
}
注意:
单元测试,如果测试的是注解配置类,则使用@ContextConfiguration(classes = 配置
类.class)
单元测试,如果测试的是配置文件,则使用@ContextConfiguration(locations={配置文件
名,...})
Junit运行后是基于Spring环境运行的,所以Spring提供了一个专用的类运行器,这个务必要设置,
这个类运行器就在Spring的测试专用包中提供的,导入的坐标就是这个东西
SpringJUnit4ClassRunner
上面两个配置都是固定格式,当需要测试哪个bean时,使用自动装配加载对应的对象

名称

@RunWith

类型

测试类注解

位置

测试类定义上方

作用

设置JUnit运行器

属性

value(默认):运行所使用的运行期

名称

@ContextConfiguration

类型

测试类注解

位置

测试类定义上方

作用

设置JUnit加载的Spring核心配置

属性

classes:核心配置类,可以使用数组的格式设定加载多个配置类
locations:配置文件,可以使用数组的格式设定加载多个配置文件名称

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。