spring(一)

120 阅读6分钟

Spring系统架构

image.png (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来完成单元测试和集成测试

核心概念

解决问题:业务层需要调用数据层的方法,就需要在业务层new数据层的对象,耦合度偏高 解决方案:spring在使用对象时,在程序中不要主动使用new产生对象,转换为由外部提供对象

IoC控制反转

IoC:使用对象时,由主动new产生对象转换为由外部提供对象,此过程中对象创建控制权由程序转移到外部,此思想称为控制反转

Spring提供了一个容器,称为IOC容器,用来充当IOC思想中的"外部"

IOC容器负责对象的创建、初始化等一系列工作,其中包含了数据层和业务层的类对象,被创建或被管理的对象在IOC容器中统称为Bean,IOC容器中放的就是一个个的Bean对象

DI依赖注入

image.png 在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入

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

IoC相关配置

Bean基础配置

image.png

scope思考

  • 为什么bean默认为单例?

    • bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
    • bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
  • bean在容器中是单例的,会不会产生线程安全问题?

    • 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
    • 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
  • 哪些bean对象适合交给容器进行管理?

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

    • 封装实例的域对象,因为会引发线程安全问题,所以不适合。

Bean实例化

实例化bean的三种方式,构造方法,静态工厂实例工厂 Spring底层使用的是类的无参构造方法。

静态工厂实例化

image.png

FactoryBean的使用

简化实例化工厂

1、实现FactoryBean接口

public class UserDaoFactoryBean implements FactoryBean<UserDao> {
    //代替原始实例工厂中创建对象的方法
    public UserDao getObject() throws Exception {
        return new UserDaoImpl();
    }
    //返回所创建类的Class对象
    public Class<?> getObjectType() {
        return UserDao.class;
    }
}

2、在Spring中配置

<bean id="userDao" class="com.itheima.factory.UserDaoFactoryBean"/>

获取Bean对象

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

DI相关

Setter注入

1、在bean中定义引用类型属性,并提供可访问的set方法

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

2、配置中使用==property==标签==ref==属性注入引用类型对象

<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
	<property name="bookDao" ref="bookDao"/> 引用数据类型
        <property name="databaseName" value="mysql"/>  简单数据类型
     	<property name="connectionNum" value="10"/>
</bean>

<bean id="bookDao" class="com.itheima.dao.imipl.BookDaoImpl"/>

构造器注入

BookServiceImpl类中添加带有bookDao参数的构造方法

public class BookServiceImpl implements BookService{
    private BookDao bookDao;

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

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

在applicationContext.xml中配置

<bean id="bookDao" class="com.itheima.dao.impl.BookDaoImpl"/>
    <bean id="bookService" class="com.itheima.service.impl.BookServiceImpl">
        <constructor-arg name="bookDao" ref="bookDao"/>
        <constructor-arg name="userDao" ref="userDao"/>  引用数据类型
         //如果构造方法参数中有类型相同的参数,这种方式就不太好实现了
        <constructor-arg type="int" value="10"/>
        <constructor-arg type="java.lang.String" value="mysql"/>  按照类型注入
        
        <constructor-arg index="1" value="100"/>
        <constructor-arg index="0" value="mysql"/>     按照索引下标注入
        //但是如果构造方法参数顺序发生变化后,这种方式又带来了耦合问题
    </bean>

标签中

  • name属性对应的值为构造函数中方法形参的参数名,必须要保持一致。
  • ref属性指向的是spring的IOC容器中其他bean对象。

介绍完两种参数的注入方式,具体我们该如何选择呢?

  1. 强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现

    • 强制依赖指对象在创建的过程中必须要注入指定的参数
  2. 可选依赖使用setter注入进行,灵活性强

    • 可选依赖指对象在创建过程中注入的参数可有可无
  3. Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨

  4. 如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入

  5. 实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入

  6. 自己开发的模块推荐使用setter注入

自动装配

  • 按类型(常用)
  • 按名称
  • 按构造方法
  • 不启用自动装配
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byType"/>    

==注意事项:==

  • 需要注入属性的类中对应属性的setter方法不能省略
  • 被注入的对象必须要被Spring的IOC容器管理
  • 按照类型在Spring的IOC容器中如果找到多个对象,会报NoUniqueBeanDefinitionException 一个类型在IOC中有多个对象,还想要注入成功,这个时候就需要按照名称注入,配置方式为:
<!--autowire属性:开启自动装配,通常使用按类型装配-->
<bean id="bookService" class="com.itheima.service.impl.BookServiceImpl" autowire="byName"/>

集合注入

  • property标签表示setter方式注入,构造方式注入constructor-arg标签内部也可以写<array><list><set><map><props>标签
  • List的底层也是通过数组实现的,所以<list><array>标签是可以混用
  • 集合中要添加引用类型,只需要把<value>标签改成<ref>标签,这种方式用的比较少