文章目录
【Spring】 1. IOC
学习框架前的引言
框架要学什么?
- 框架的使用
- 框架的底层原理
框架和类库的区别
- 类库:就对某个功能的实现,基本上都是一些功能开发的工具。类库相当于挖掘机、推土机、扳手
- 框架:是对项目整体的架构和设计。框架就相当于建筑物的结构,比如电梯井预留好,只好放入电梯就可以工作了。
框架的难度
- 完成项目,必须依照框架定义好的规范。如果不符合规范,出现错误很难调试和定位。
- 新增术语
框架的实现方式
- 基于xml配置文件的
- 基于注解的
1. Spring 简介
1.1 Spring 概述
Spring是一个开源的设计层面框架,于2003 年兴起的由Rod Johnson创建。Spring是一个分层的JavaSE/EE的一站式、轻量级、开源框架。
- 一站式:在JavaEE的三层结构中,每一层都提供了不同的解决方案。
- 轻量级:Spring 是非侵入性的 ,在 IOC 和 AOP 的基础上可以整合各种企业应用的的第三方类库。
1.2 Spring 核心
-
IOC(Inversion of Control):控制反转。降低了业务对象替换的复杂性,提高了组件之间的解耦。
-
AOP(Aspect Oriented Programming):面向切面编程。允许将一些通用任务(如安全、事务、日志等)进行集中式管理,从而实现更好的复用。
1.3 Spring 下载
官网地址:https://spring.io/
类库下载:http://repo.spring.io/release/org/springframework/spring/
1.4 sts
sts(spring tool suite):Spring 工具套件。它是一个基于Eclipse的开发环境, 用于开发Spring应用程序。它提供了一个现成的使用环境来实现调试、运行和部署你的Spring应用程序。包括为关键的的服务器和云计算,Git, Maven, AspectJ和最新的Eclipse版本提供整合支持。
各Eclipse版本的sts下载地址 : https://spring.io/tools/sts/legacy。
sts 在线安装:使用Eclipse Marketplace ,可直接搜索 sts 安装。
2. IOC
IOC(Inversion of Control)中文含义是控制反转。
当需要某个对象实例时,在传统的程序设计过程中,通常由调用者来创建该实例。但在Spring里,创建对象的工作由Spring来完成,因此称为控制反转。IOC 降低了业务对象替换的复杂性,提高了组件之间的解耦。
2.1 原始方式
// Dao层接口
public interface UserDao {
void insert();
}
// Dao层实现类
public class UserDaoImpl implements UserDao{
@Override
public void insert() {
System.out.println("插入一条数据...");
}
}
//原始的方式
UserDao userDao = new UserDaoImpl();
userDao.insert();
原始方式就是手动实例化对象。如果项目中有10处使用 UserDao实例,则需要实例化10次对象。问题在于,当 UserDao的实现方式发生变化时(比如实现类是UserDaoImpl2),这10处位置都需要修改。
换句话说,组件之间的耦合性较高,业务对象替换的比较复杂。
2.2 IOC 底层原理
- Bean工厂
把组件实例化过程交给工厂,需要实例化对象的时候,调用工厂类的方法统一处理。
public interface BeanFactory {
//根据id获得bean实例
Object getBean(String id);
}
-
根据全路径类名获得类的实例
通过反射机制,根据全路径类名,获得类的实例
/**
* 根据全路径类名获得类的实例
* 技术点:反射
* @param className 全路径类名
* @return 类的实例
* @throws Exception
*/
private Object getInstanceByClassName(String className) throws Exception{
//获得类的加载器
ClassLoader classLoader = getClass().getClassLoader();
//加载类
Class<?> cls = classLoader.loadClass(className);
//创建实例
Object obj = cls.newInstance();
return obj;
};
-
配置文件
在class 类路径下新建配置文件“application-config.xml”,统一对需要实例化的对象进行配置。
<?xml version="1.0" encoding="UTF-8"?>
<!-- beans:根节点,一个xml中只能有一个根节点 -->
<beans>
<!--
bean:子节点,根节点下可以有多个子节点
id: 对象实例的别名,后期通过id获得对象实例
class: 对象类的全路径
-->
<bean id="UserDao" class="com.bodhixu.ssm.UserDaoImpl"/>
</beans>
-
读取并解析配置文件
读取配置文件,并通过dom4j进行解析。并通过反射生成实例,放入map集合
/**
* 读取配置文件,获得bean实例
* 技术点:dom4j
* @param xmlPath 配置文件路径
* @return bean的map集合
* @throws DocumentException
*/
private Map<String, Object> initConfig(String xmlPath) throws Exception {
Map<String, Object> beanMap = new HashMap<>();
//读取配置文件,获得输入流
InputStream is = getClass().getResourceAsStream(xmlPath);
//创建解析器
SAXReader reader = new SAXReader();
//加载数据,获得文档模型
Document doc = reader.read(is);
//获得文档根节点
Element rootElement = doc.getRootElement();
//获得所有bean的子节点
List<Element> beanElements = rootElement.elements("bean");
//遍历bean子节点
for (Element element : beanElements) {
//获得id属性
String id = element.attributeValue("id");
//获得class属性
String cls = element.attributeValue("class");
//根据类名获得类的实例
Object obj = getInstanceByClassName(cls);
//放入map集合
beanMap.put(id, obj);
}
return beanMap;
}
-
根据id获得bean实例
提供 get 方法,根据 id 返回 bean 对象
public Object getBean(String id) {
return beanMap.get(id);
}
2.3 BeanFactory 工厂类
-
BeanFactory:接口
-
ApplicationContext:接口
-
ClassPathXmlApplicationContext:实现类,加载类路径下的配置文件
-
FileSystemXmlApplicationContext:实现类,加载文件系统下的配置文件
2.4 入门程序
- 导入Spring IOC 所依赖的 jar 包
- 在类路径下,创建配置文件“application-config.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="userDao" class="com.bodhixu.ssm.UserDaoImpl"/>
</beans>
- 创建 ApplicationContext 对象,通过其获得bean实例。
ApplicationContext context =
new ClassPathXmlApplicationContext("/application-config.xml");
UserDao userDao = (UserDao) context.getBean("UserDao");
2.5 bean 的常用属性
- id:bean对象的名字,不能包含特殊字符
- name:和id功能一致,可以包含特殊字符
- class:bean对象对应类的全路径
- scope:设置bean的作用域
- singleton:单例模式,默认的
- prototype:多例模式
- request:将实例存入到request域中
- session:将实例存入到session域中
3. DI
DI(Dependency Injection)依赖注入,用来实现对bean对象属性赋值。
3.1 注入方式
DI 的实现方式主要有构造器注入和 setter注入,另外其他一些方式如工厂方法注入(很少使用,不推荐)。
- 构造器注入
必须提供有参构造方法
<bean id="Book" class="com.bodhixu.ssm.demo01.Book">
<constructor-arg name="name" value="Java"/>
<constructor-arg name="price" value="200"/>
</bean>
-
setter 注入
必须提供 setter 方法
<bean id="Book" class="com.bodhixu.ssm.demo02.Book">
<property name="name" value="Oracle"/>
<property name="price" value="200"/>
</bean>
3.2 注入复杂属性
- 注入 bean 对象
<bean id="book" class="com.bodhixu.ssm.demo03.Book">
<property name="name" value="Oracle"/>
<property name="price" value="200"/>
</bean>
<!-- 注入bean -->
<bean id="student" class="com.bodhixu.ssm.demo03.Student">
<property name="name" value="小张"/>
<property name="book" ref="book" />
</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 id="book1" class="com.bodhixu.ssm.demo04.Book">
<property name="name" value="java"/>
<property name="price" value="100"/>
</bean>
<bean id="book2" class="com.bodhixu.ssm.demo04.Book">
<property name="name" value="orcle"/>
<property name="price" value="200"/>
</bean>
<!-- 注入数组、集合 -->
<bean id="Student" class="com.bodhixu.ssm.demo04.Student">
<!-- 注入数组 -->
<property name="arr">
<array>
<value>arrItem1</value>
<value>arrItem2</value>
</array>
</property>
<!-- 注入List -->
<property name="list">
<list>
<value>listItem1</value>
<value>listItem2</value>
</list>
</property>
<!-- 注入Set -->
<property name="set">
<set>
<value>listItem1</value>
<value>listItem2</value>
</set>
</property>
<!-- 注入Map -->
<property name="map">
<map>
<entry key="mapkey1" value="mapValue1" />
<entry key="mapkey2" value="mapValue2" />
</map>
</property>
<!-- 注入Bean的List -->
<property name="books">
<list>
<ref bean="book1"/>
<ref bean="book2"/>
</list>
</property>
</bean>
</beans>
3.3 命名空间注入
为了简化XML文件的属性配置,Spring从2.5后,引入了新的p命 名空间。
- 声明p命名空间,可以通过 sts 实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0AmksVvj-1587633631341)(img/img7.png)]
xmlns:p="http://www.springframework.org/schema/p"
- 采用 p 命名空间注入属性值
<bean id="Book" class="com.bodhixu.ssm.demo05.Book"
p:name="HTML" p:price="60"/>
- Spring从3.0后,引入了新的c命 名空间。
3.4 SpEL 注入
SpEL(Spring Expression Language)Spring的表达式语言。
语法:#{SpEL}
<bean id="book1" class="com.bodhixu.ssm.demo06.Book">
<property name="name" value="Oracle"/>
<property name="price" value="200"/>
</bean>
<!-- spEL注入 -->
<bean id="book2" class="com.bodhixu.ssm.demo06.Book">
<property name="name" value="#{'new' + book1.getName()}"/>
<property name="price" value="#{200+20}"/>
</bean>
4. 基于注解的Bean管理
Spring 中对 Bean 的管理有两种方式,一种是通过 xml 配置来管理,一种是通过注解来管理。
4.1 配置文件
- 新建配置并引入context约束,可以通过 sts 实现·
<context:component-scan base-package="com.bodhixu.ssm" />
- 开启注解扫描
<?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-4.3.xsd">
<!-- 开启注解扫描,扫描指定包 -->
<context:component-scan base-package="com.bodhixu.web.bean"/>
<!-- 开启注解扫描,扫描多个包(用逗号分隔)-->
<!-- <context:component-scan base-package="com.bodhixu.web.bean, com.bodhixu.web.dao"/> -->
<!-- 只扫描属性上的注解 -->
<!-- <context:annotation-config></context:annotation-config> -->
</beans>
4.2 组件注解
组件注解的类型
组件注解注释在类上,共有4个,他们作用一样,只是语义化了。暂时我们使用 @Component 注解,在后面的SrpingMVC 中会使用其他注解。
- @Component: 组件
- @Controller : 控制组件(WEB层)
- @Service :业务组件(业务层)
- @Repository:仓库组件(持久层)
组件名称的设置
给组件注入名称相当于 xml 配置中的 id 属性,设置名称有3种方式。
- 通过value属性指定组件名称
@Component(value="book")
public class Book {}
- 只有一个属性,可以省略value
@Component("book")
public class Book {}
- 未指定属性名称,默认使用类名小写作为组件名
@Component //名称默认是book
public class Book {}
4.3 作用域注解
使用 @Scope 可以设置Bean的作用域,作用同 xml 中配置 bean 的作用域一样。常用的有 singleton 单例模式和 prototype 多例模式。默认是单例的。
@Scope(value="prototype") //单一属性可以省略value=
4.4 属性注解
使用注解注入属性,对象不需要setter方法。如果有setter方法,注解需要写在setter方法上。
- @Value:普通属性注入
@Component("book3")
public class Book {
@Value(value="java编程") //单一属性可以省略value=
private String name;
@Value("100")
private int price;
}
- @Resource:使用bean的名字注入
@Component("book4")
public class Book {}
@Component("stu1")
public class Student {
@Resource(name="book4")
private Book book;
}
- @Autowired:自动注入bean
@Repository
public class UserDao {
public void insert() {
System.out.println("UserDao insert...");
}
}
@Service
public class UserService {
@Autowired
private UserDao userDao;
public void regist() {
userDao.insert();
}
}
class Test {
@org.junit.jupiter.api.Test
void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("/application-config.xml");
UserService service = (UserService) context.getBean("userService");
service.regist();
}
}
4.5 基于xml和注解管理Bean的区别
- 注解方式管理,简单快捷,对于自己实现的类,建议使用
- xml方式管理,相对较繁琐,但对于第三方的类,比如jar包引入的类,需要使用xml方式配置