一、Spring 框架概述
1. Spring 框架的作用
Spring 框架的作用就是为了代码的“解耦”,为了降低代码之间的耦合度。根据功能不同,可以将系统中的代码分为主业务逻辑代码和交叉业务逻辑代码:
主业务逻辑代码:有具体的专业业务应用场景,复用性比较低。
交叉业务逻辑代码:没有专业业务应用场景,主要是为主业务逻辑提供系统级的服务,例如日志、安全、事务等等,复用性比较强。
2. Spring 框架降低耦合度的方式
Spring 框架降低耦合度的方式分为两种:控制反转 (IoC)和面向切面编程 (AOP)。
IoC:主业务逻辑在相互调用的过程中,不再自己去创建要使用的对象,而是由 Spring 容器统一管理,在主业务逻辑需要的时候,自动“注入”给主业务逻辑。
AOP:使得交叉业务逻辑得到最大的复用,且交叉业务逻辑不会混杂在主业务逻辑中,而是由 Spring 容器统一完成”织入“。
3. Spring 的体系结构
Spring 由 20 多个模块组成,它们可以分为:数据访问/集成 (Data Access/Integration)、Web、面向切面编程 (AOP、Aspect)、用于服务器设备管理 (Instrumentation)、消息发送 (Messaging)、核心容器 (Core Container) 和 测试 (Test)。
4. Spring 的下载
Spring 官网:http://spring.io
5. Spring 的特点
(1) 非侵入式:Spring 框架的 API 不会出现在主业务逻辑代码中,即主业务逻辑代码是一个普通的 Java 类 (pojo),可以快速移植到其他框架。
(2) 容器:Spring 作为一个容器,可以管理对象的生命周期、对象与对象之间的依赖关系,可以通过配置文件来定义对象以及设置对象与对象之间的关系。
(3) IoC (控制反转):创建被调用的实例不是由调用者完成,而是由 Spring 容器完成,然后注入给调用者。这样就降低了主调方和被调方的耦合度。
举个例子:
// 传统的做法
main(){
// 主调方法 main() 和被调方 Student 对象耦合在一起了
Student student = new Student();
student.show();
}// Spring 的 IoC:将 student 对象配置到 Spring 容器中
main() {
// 从 Spring 容器中获取 student 对象,降低了代码之间耦合度
student.show();
}
(4) AOP (面向切面编程):是一个编程思想,是对面向对象编程的补充。通过分离主业务逻辑和交叉业务逻辑,将复用性比较高的日志、安全、事务等单独写在一个称为“切面”的类中。在需要时将”切面“动态的“织入”到主业务逻辑中,降低代码的耦合性。
二、Spring 与 IoC
1. IoC 实现方式
IoC 是一个概念,也是一种思想,其实现方式有多个,目前比较流行的:
(1) 依赖查找 (DL, Dependency Lookup):容器提供回调接口和上下文环境给组件,程序代码则需要提供具体的查找方式。典型的应用就是 JNDI 系统查找(应用不多)。
(2) 依赖注入 (DI, Dependency Injection):程序代码不做定位查询,由外部容器自己完成。依赖注入是目前最优秀的解耦方式。
2. 开发第一个 Spring 应用 (DI 的体现)
(1) 导入 Spring 的基础 jar 包
包含 beans、core、context、expression 以及依赖库 commons-logging,还可以引入 log4j 日志包和 junit 测试包。
(2) 定义主业务逻辑接口和实现类
接口:
package com.edu.service; public interface IStudentService { void show(); }实现类:
package com.edu.service.impl; import com.edu.service.IStudentService; public class StudentServiceImpl implements IStudentService { @Override public void show() { System.out.println("我是一个学生"); } }(3) 创建 Spring 的主配置文件
在src或resources目录下创建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" 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"> <bean id="studentService" class="com.edu.service.impl.StudentServiceImpl"/> </beans>(4) 测试类
package com.edu.test; import com.edu.service.IStudentService; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class MySpringTest { @Test public void testFirstSpringApp(){ // 1. 读取 Spring 的主配置文件,获取 Spring 的容器:ApplicationContext ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); // 2. 从容器中获取 bean 对象 IStudentService studentService = context.getBean("studentService", IStudentService.class); studentService.show(); } }3. ApplicationContext 容器
ApplicationContext用于加载核心配置文件,充当“容器”的角色,其常用实现类有:
ClassPathXmlApplicationContext:配置文件在类路径下 (src或resources目录)。FileSystemXmlApplicationContext:配置文件在本地磁盘目录中或项目的根路径下。
4. BeanFactory 接口容器
BeanFactory是ApplicationContext的父接口,也可以作为 Spring 容器,其实现类有XmlBeanFactory。需要将配置文件以Resource形式传递:
@Test public void testBeanFactory(){ BeanFactory factory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml")); IStudentService studentService = factory.getBean("studentService", IStudentService.class); studentService.show(); }ApplicationContext 和 BeanFactory 的区别:
ApplicationContext:容器创建时就已经将对象装配好。执行效率高,比较占内存(空间换时间)。
BeanFactory:采用延迟装配策略,第一次调用
getBean()时才去装配对象。节约内存,响应速度慢(时间换空间)。注意:Spring 容器在装配 bean 对象时默认调用无参构造方法。
5. Bean 的装配
(1) 默认装配方式
调用 bean 的无参构造器创建空值对象。
(2) 动态工厂 Bean
利用 Spring 提供的factory-bean和factory-method属性:
<bean id="factory" class="com.edu.utils.ServiceFactory"/> <bean id="studentService" factory-bean="factory" factory-method="getStudentService"/>(3) 静态工厂 bean
无需工厂实例,直接通过工厂类的静态方法创建:
<bean id="studentService" class="com.edu.utils.ServiceFactory" factory-method="getStudentService"/>(4) 容器中 bean 的作用域 (scope)
| 作用域 | 说明 |
| singleton | 默认值。单例模式,整个容器中只有一个实例。(非线程安全) |
| prototype | 原型模式。每次getBean()都会创建一个新的实例。(延迟加载,线程安全) |
| request | Web 应用中,每次 HTTP 请求产生一个不同的 bean 对象。 |
| session | Web 应用中,每次 HTTP 会话产生一个不同的 bean 对象。 |
| global session | Web 集群应用中,全局 HTTP 会话对应一个 bean 对象。 |
(5) Bean 后处理器 (BeanPostProcessor)
是一个特殊的 bean,容器初始化 bean 时自动执行该类的前置和后置方法,常用于结合 JDK 动态代理对目标 bean 进行增强。
(6) 定制 bean 的生命周期
通过init-method和destroy-method属性指定初始化和销毁方法:
<bean id="someService" class="com.edu.service.impl.SomeServiceImpl" init-method="setUp" destroy-method="tearDown"/>三、基于 XML 配置的 DI(依赖注入)
1. 设值注入 (setter 注入)
要求 bean 必须有默认构造方法和对应属性的 setter 方法。
基本类型注入:使用
<property name="..." value="..."/>域属性注入 (对象引用):使用
<property name="..." ref="..."/>
<bean id="school" class="com.edu.beans.School"> <property name="id" value="10011"/> <property name="name" value="清华大学"/> </bean> <bean id="student" class="com.edu.beans.Student"> <property name="id" value="11011"/> <property name="name" value="小明"/> <property name="school" ref="school"/> </bean>2. 构造注入
要求 bean 必须有全参构造函数。
<bean id="student" class="com.edu.beans.Student"> <constructor-arg name="id" value="100011"/> <constructor-arg name="name" value="娜娜"/> <constructor-arg name="school" ref="school"/> </bean>3. 集合属性的注入
支持数组<array>、List<list>、Set<set>、Map<map>和 Properties<props>的注入。
<property name="students"> <list> <ref bean="student1"/> <ref bean="student2"/> </list> </property>4. 对域属性的自动注入 (autowire)
byName:根据域属性的名称和 Spring 容器中 bean 的 id 匹配注入。byType:根据类型同源(相同类型或其子类/实现类)自动匹配注入。注意容器中同源的 bean 只能有一个。
5. 使用 SPEL 注入
Spring EL 表达式,语法#{...}:
<property name="personAge" value="#{T(java.lang.Math).random()*30}"/> <property name="empName" value="#{person.personName.toString()}"/>6. 使用抽象 bean 注入
使用abstract="true"标记父 bean,子 bean 使用parent="父bean的id"继承属性,减少冗余配置。
7. 多配置文件加载
可以使用<import resource="spring-*.xml"/>在总配置文件中引入其他子配置文件。
四、基于注解的 DI (依赖注入)
开启注解扫描 (需引入 AOP 包):
<context:component-scan base-package="com.edu.beans"></context:component-scan>1. 常用创建对象的注解
@Component:通用组件注解。@Repository:用于 Dao 层。@Service:用于 Service 层。@Controller:用于 Web 控制层。
2. 属性注入注解
@Value:注入基本数据类型。@Autowired:按类型自动注入域属性。@Qualifier("beanId"):配合@Autowired实现按名称注入。@Resource:默认按类型注入,@Resource(name="beanId")按名称注入。
3. 其他相关注解
@Scope("prototype"):设置作用域。@PostConstruct:初始化方法。@PreDestroy:销毁方法。
4. JavaConfig 配置类
使用@Configuration标注配置类,@Bean标注向容器注册对象的方法。
@Configuration public class DataSourceConfig { @Bean(name = "dataSource") public DataSource dataSource(){ return new DataSource("com.edu.jdbc.Driver","jdbc:mysql://localhost:3306/test","root","root"); } }5. Spring Test 整合 JUnit
@RunWith(SpringRunner.class) @ContextConfiguration(locations = "classpath:applicationContext.xml") public class SpringTest { @Autowired private IUserService userService; // ... @Test methods }