Spring系统架构
(1)核心层
Core Container:核心容器,这个模块是Spring最核心的模块,其他的都需要依赖该模块
(2)AOP层
AOP:面向切面编程,它依赖核心层容器,目的是在不改变原有代码的前提下对其进行功能增强Aspects:AOP是思想,Aspects是对AOP思想的具体实现
(3)数据层
Data Access:数据访问,Spring全家桶中有对数据访问的具体实现技术Data Integration:数据集成,Spring支持整合其他的数据层解决方案,比如MybatisTransactions:事务,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依赖注入
在容器中建立bean与bean之间的依赖关系的整个过程,称为依赖注入
使用对象时不仅可以直接从IOC容器中获取,并且获取到的bean已经绑定了所有的依赖关系.
IoC相关配置
Bean基础配置
scope思考
-
为什么bean默认为单例?
- bean为单例的意思是在Spring的IOC容器中只会有该类的一个对象
- bean对象只有一个就避免了对象的频繁创建与销毁,达到了bean对象的复用,性能高
-
bean在容器中是单例的,会不会产生线程安全问题?
- 如果对象是有状态对象,即该对象有成员变量可以用来存储数据的,因为所有请求线程共用一个bean对象,所以会存在线程安全问题。
- 如果对象是无状态对象,即该对象没有成员变量没有进行数据存储的,因方法中的局部变量在方法调用完成后会被销毁,所以不会存在线程安全问题。
-
哪些bean对象适合交给容器进行管理?
- 表现层对象
- 业务层对象
- 数据层对象
- 工具对象
-
哪些bean对象不适合交给容器进行管理?
- 封装实例的域对象,因为会引发线程安全问题,所以不适合。
Bean实例化
实例化bean的三种方式,构造方法,静态工厂和实例工厂
Spring底层使用的是类的无参构造方法。
静态工厂实例化
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对象。
介绍完两种参数的注入方式,具体我们该如何选择呢?
-
强制依赖使用构造器进行,使用setter注入有概率不进行注入导致null对象出现
- 强制依赖指对象在创建的过程中必须要注入指定的参数
-
可选依赖使用setter注入进行,灵活性强
- 可选依赖指对象在创建过程中注入的参数可有可无
-
Spring框架倡导使用构造器,第三方框架内部大多数采用构造器注入的形式进行数据初始化,相对严谨
-
如果有必要可以两者同时使用,使用构造器注入完成强制依赖的注入,使用setter注入完成可选依赖的注入
-
实际开发过程中还要根据实际情况分析,如果受控对象没有提供setter方法就必须使用构造器注入
-
自己开发的模块推荐使用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>标签,这种方式用的比较少