一、今日内容
二、spring前言
1. 为什么要学习spring框架
传统web程序的问题:程序间耦合度高。
其实,使用传统的servlet和jsp已经可以写出web程序了。但是,这样写出来的程序有一个问题,就是程序的耦合度太高,换句话说就是程序的各个模块不够独立,使得修改一个模块时,往往要修改与其相关的所有模块,这样导致程序在后期维护的时候成本特别高。
而spring主要就是为了解决这个问题的:降低程序的耦合度。
相关概念:
1. 程序的耦合:指程序间的依赖关系。
* 包括:类之间的依赖、方法之间的依赖。
2. 解耦:降低程序间的依赖关系。
* 特点:实际开发过程中应做到:编译器不依赖,运行时依赖
3. 解耦的思路:
* 步骤:
1. 使用反射技术来创建对象,而避免使用new关键字。
2. 通过读取配置文件来获取要创建的对象的全限定类名。
记住,解耦是一种思想,而不是一种技术。上面说的解耦的思路只是解耦的一种具体方式。下面我简单说一下,为什么避免new关键字就能减低程序的耦合度:
如果某个方法或者类中new了一个对象,那么这个方法或者类就与new产生了因果,因为这个new出来的对象是这段程序主动建立的。就好比我现在做了一道菜,因为菜是我做的,所以我与菜有关系,存在耦合。而现在,我请一个保姆来做菜,且这个保姆做很多菜,那么这些菜之间是没有关系,菜现在只与保姆有耦合。所以,在程序中,我们利用第三方来建立对象,不能直接在程序中new出对象,如何做到?用一个工厂,工厂负责利用反射技术创建对象,工厂可以理解为一个专门生产并存放对象的容器。而spring框架内部就有这个工厂。
上述过程可以用下面一张图描述:
注意,此时,创建的对象与工厂有耦合,是工厂创建的,以前是类或方法中的代码块主动建立的,创建对象的权力由程序主动建立交给了工厂建立,控制权发生了反转,这就是spring的核心,IOC控制反转,后面会讲到,现在有个了解就行。
2. 手动创建工厂之感受工厂
接下来,我们手动的建立一个简单的工厂,来体验一下解耦的感觉。
步骤:
1. 需要一个配置文件来配置我们的service和dao的唯一标识:全限定类名
2. 建立一个Bean工厂
解释:
1. Bean:在计算机英语中,是可重用组件的意思。
2. javaBean:用java语言编写的可重用组件。 JavaBean包含实体类,JavaBean的范围更大。
3. 在工厂中读取配置文件内容,反射创建对象。
- 建立工厂的配置文件factoryConfig.properties和其他的类文件:
配置文件不一定是propertis的,框架使用更多的是xml格式的,这里用propertis做配置文件只是为了方便。
2. 建立一个工厂:BeanFactory.java类
package cn.wanghao.factory.myFactory;
import cn.wanghao.factory.service.impl.UserServiceImpl;
import java.io.IOException;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
public class BeanFactory {
/* 存放单例对象的容器 */
private static Map<String,Object> beans;
//初始化
static {
//1. 创建properties对象
Properties properties = new Properties();
//2. 读取配置文件
InputStream is = BeanFactory.class.getClassLoader().getResourceAsStream("factoryConfig.properties");
try {
properties.load(is);
//3. 创建实例对象们
Enumeration keys = properties.keys();
beans = new HashMap<String,Object>();
while (keys.hasMoreElements()) {
String key = keys.nextElement().toString();
//获取value
String value = properties.getProperty(key);
//反射创建对象
Object instance = Class.forName(value).newInstance();
//把key和value放入容器中
beans.put(key, instance);
}
} catch (IOException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
public static Object getBeans(String beanName) {
return beans.get(beanName);
}
}
- 使用工厂创建对象,现在在servlet里面建立一个Demo类,来模拟servlet处理请求:
工厂利用反射创建对象,我使用的是单例模式,主要是因为单例模式节省资源。
三、spring学习
1. spring概述
1. spring:Spring是分层的 Java SE/EE应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC
和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的Java EE 企业应用开源框架。
2. 优点:
1. 方便解耦,简化开发
* 解释:通过 Spring提供的 IoC容器,可以将对象间的依赖关系交由 Spring进行控制,避免硬编码所造 成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
2. AOP编程的支持
* 解释:通过 Spring的 AOP 功能,方便进行面向切面的编程,许多不容易用传统OOP 实现的功能可以通过 AOP 轻松应付。
3. 声明式事务的支持
* 解释:可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理, 提高开发效率和质量。
4. 方便程序的测试
* 解释:可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可 做的事情。
5. 方便集成各种优秀框架
* 解释:Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。
- spring 的体系结构如下图所示
2. spring的IOC解决程序耦合
1. 耦合:在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。
软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个 准则就是高内聚低耦合。
2. 内聚:内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。通俗的讲,就是一个方法尽量只做一件事。
3. 程序设计原则:高内聚低耦合。如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。
4. IOC(控制反转): 把创建对象的权力交给框架,是框架的重要特征。他包括依赖注入(DI)和依赖查找(DL)。至于为什么叫控制反转,在前面自己创建工厂,降低耦合的过程中我已经讲了。
5. 明确IOC作用:削减计算机程序的耦合(解除我们代码中的依赖关系)。
6. IOC容器是一个Map结构。
3. Spring基于 XML 的 IOC
spring 中工厂的类结构图
要使用XML配置,首先需要在Resources
目录下建立一个xml
文件,假设我建立的是bean.xml
。然后将下面的代码粘贴到文件中就启动了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
https://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="..." class="...">
<!-- collaborators and configuration for this bean go here -->
</bean>
</beans>
下面是关于如何创建对象的知识点:
1. BeanFactory和 ApplicationContext(了解)
* 关系:BeanFactory是spring的顶层接口,ApplicationContext是他的一个子接口
* 区别: 创建对象的时间点不一样
1. ApplicationContext(常用): 采用立即加载的方式。只要一读取配置文件,默认情况下就会创建对象。
2. BeanFactory:采用延迟加载的方式。什么使用什么时候创建对象。
* 注意:从上面可以看出BeanFactory适用于多例模式,ApplicationContext适用于单例模式。但是,在实际的开发中,我们都会用ApplicationContext,
通过设置bean标签的属性设置是创建对象是单例还是多例,从而单例/多例的情况都可以照顾到。
2. ApplicationContext 接口的三个实现类:
1. ClassPathXmlApplicationContext(常用):它是从类的根路径下加载配置文件(从Resources目录下加载)
2. FileSystemXmlApplicationContext:它是从磁盘路径上加载配置文件,配置文件可以在磁盘的任意位置。
3. AnnotationConfigApplicationContext: 当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器。它用来读取注解。
3. IOC 中(即xml配置文件中) bean 标签和管理对象
1. bean 标签 (用来创建对象)
* 作用:用于配置对象让 spring 来创建的。
* 属性:
1. id:给对象在容器中提供一个唯一标识。用于获取全限定类名。
2. class:指定类的全限定类名。用于反射创建对象。默认情况下调用无参构造函数。
3. scope:指定对象的作用范围。
* scope属性值:
1. singleton(常用) :默认值,单例的.
2. prototype(常用) :多例的.
3. request :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
4. session :WEB 项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
5. global session :WEB 项目中,应用在 Portlet 环境.如果没有 Portlet 环境那么 globalSession 相当于 session.
4. factory-bean 属性:用于指定实例工厂 bean 的 id。
5. factory-method 属性:用于指定实例工厂中创建对象的方法。
6. init-method:指定类中的初始化方法名称。
7. destroy-method:指定类中销毁方法名称。
* 注意:1、2、4、5结合起来设置实例化Bean的方式,3用来设置实例化对象的范围, 6、7与生命周期相关。
2. 单例对象和多例的对象的生命周期
1. 单例对象:scope="singleton"
* 特点:一个应用只有一个对象的实例。它的作用范围就是整个应用。
* 生命周期:
对象出生:当应用加载,创建容器时,对象就被创建了。
对象活着:只要容器在,对象一直活着。
对象死亡:当应用卸载,销毁容器时,对象就被销毁了。
2. 多例对象:scope="prototype"
* 特点:每次访问对象时,都会重新创建对象实例。
* 生命周期:
对象出生:当使用对象时,创建新的对象实例。
对象活着:只要对象在使用中,就一直活着。
对象死亡:当对象长时间不用时,被 java 的垃圾回收器回收了。
3. 实例化 Bean 的三种方式
1. 使用默认无参构造函数
<!--在默认情况下: 它会根据默认无参构造函数来创建类对象。如果 bean 中没有默认无参构造函数,将会创建失败。 -->
<bean id="id" class="要创建的对象的全限定类名"/>
2. 第二种方式:静态工厂的静态方法创建对象 (调用方法,返回对象)
<bean id="id" class="静态工厂类的全限定类名" factory-method="静态工厂类的静态方法名"></bean>
3. 使用实例工厂的方法创建对象(调用方法,返回对象):
<!-- 此种方式是: 先把工厂的创建交给 spring 来管理。然后在使用工厂的 bean 来调用里面的方法
factory-bean 属性:用于指定实例工厂 bean 的 id。factory-method 属性:用于指定实例工厂中创建对象的方法。 -->
<bean id="uid" class="工厂的全限定类名"></bean>
<bean id="id" factory-bean="uid(上面工厂的id)" factory-method="工厂类的方法名"></bean>
4. spring基于XML的依赖注入
1. 依赖注入:Dependency Injection。
说明:我们的程序在编写时,通过控制反转,把对象的创建交给了 spring,但是代码中不可能出现没有依赖的情况。 ioc 解耦只是降低他们的依赖关系,但不会消除。
比如,实例化对象时,如何给其类成员变量赋值?这些需要依赖的地方也交给spring来做。
2. 作用:设置一些必须的依赖关系。简单点说就是在spring创建对象时,给其传递创建对象的参数。
3. 能注入的内容:
1. 基本类型和String
2. 其他bean类型(在配置文件中或者注解配置过的bean)
3. 复杂类型(集合类型)
4. 注入的方式:
1. 使用构造函数
* 使用标签:constructor-arg标签
* 标签出现的位置:bean标签的内部
* 标签中的属性:
* type:指定参数在构造函数中的数据类型
* index:指定参数在构造函数参数列表的索引位置 ,从0开始
* name(常用):指定参数在构造函数中的名称
=======上面三个都是找给谁赋值,下面两个指的是赋什么值的==============
* value:它能赋的值是基本数据类型和 String 类型
* ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
* 优点:确保构造函数中设置的类成员变量一定有值,如果少参数,会无法创建对象。
* 缺点:不够灵活,如果我们构造函数中设置的类成员变量,也必须提供所有的参数值。
2. 使用set方法提供(要创建的对象的类中一定要有相关的setter方法)
* 使用标签:property
* 出现的位置:bean标签的内部
* 标签的属性:
* name:用于指定注入时所调用的set方法名词(即填类的属性,不是类成员变量)
* value:它能赋的值是基本数据类型和 String 类型
* ref:它能赋的值是其他 bean 类型,也就是说,必须得是在配置文件中配置过的 bean
* 优点:创建对象时不需要指定所有的参数,可以设置想设置的,灵活。
* 缺点:如果成员必须有值,可能出现没有设置值的情况。
3. 使用注解提供
5. 注入依赖时,值为集合/复杂类型:
* 说明:基本类型和String用构造函数或者set注入的value属性,bean类型用构造函数或者set注入的ref属性设置,而集合/复杂类型需要用新的标签
* 标签位置:依赖注入的 constructor-arg标签/property标签 的内部。
* 标签:
1. array
* 例如:
<property name="myList">
<array>
<value>AAA</value>
<value>BBB</value>
</array>
</property>
2. list
* 例如:
<property name="mySet">
<list>
<value>AAA</value>
<value>BBB</value>
</list>
</property>
3. set
* 例如:
<property name="myStrs">
<set>
<value>AAA</value>
<value>BBB</value>
</set>
</property>
4. map
* 例如:
<property name="myProps">
<map>
<entry key="testA" value="aaa"></entry>
<entry key="testB">
<value>bbb</value>
</entry>
</map>
</property>
5. property
* 例如:
<property name="myMap">
<props>
<prop key="testA">aaa</prop>
<prop key="testB">bbb</prop>
</props>
</property>
比如:
5. Spring基于注解的 IOC 和其依赖注入
首先,要明确,基于注解配置的IOC与基于XML配置的IOC实现的功能都是一模一样的,都是IOC思想的具体实现。只是实现的方式不一样。关于实际的开发中到底使用xml还是注解,每家公司有着不同的使用习惯。所以这两种配置方式我们都需要掌握。
要使用注解配置有两种方法:
- 方法一:首先需要在
Resources
目录下建立一个xml
文件,假设我建立的是bean.xml
。然后将下面的代码粘贴到文件中就启动了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">
<!--告知spring在创建容器时要扫描的包,配置所需要的标签不是在beans的约束中,而是一个名称为context名称空间和约束中-->
<context:component-scan base-package="填项目的全限定类名,比如cn.wanghao.spring"></context:component-scan>
</beans>
- 方法二:创建一个类,在上面@Configuration和@ComponentScan(“填项目的全限定类名,比如cn.wanghao.spring”) 注释
下面介绍注解配置的相关知识点:
* IOC 中注解方式管理对象
1. 用于创建对象的:相当于:<bean id="" class="">
1. @Component
* 作用:把资源让 spring 来管理。相当于在 xml 中配置一个 bean。
* 属性:value:指定 bean 的 id。如果不指定 value 属性,默认 bean 的 id 是当前类的类名。首字母小写。
* 比如:@Component("user")
2. @Controller:一般用于表现层
3. @Service:一般用于业务层的注解
4. @Repository:一般用于持久层的注解
* 注意:@Component是@Controller、@Service、@Repository的父类,后三者只是为了规范化注解而产生的,所以用法和@Component一样
2. 用于注入数据的 :相当于:<property name="" ref=""> 或<property name="" value="">
1. @Autowired
* 作用:在spring的IOC容器中找到数据类型与被@Autowired注释的属性类型相同的数据,如果该数据唯一则就是该数据。如果不唯一,则会根据属性名再在里面匹配,如果没有则报错。
2. @Qualifier
* 作用: 在自动按照类型注入的基础之上,再按照 Bean 的 id 注入。它在给字段注入时不能独立使用,必须和 @Autowire 一起使用;但是给方法参数注入时,可以独立使用。
* 属性: value:指定容器中bean 的 id。
* 举例:
@Autowired
@Qualifier("user1")
3. @Resource
* 作用:直接按照 Bean 的 id 注入。它也只能注入其他 bean 类型。
* 属性:name:指定 bean 的 id。
4. @Value
* 作用:注入基本数据类型和 String 类型数据的
* 属性:value:用于指定值
3. 用于改变作用范围的: 相当于:<bean id="" class="" scope="">
1. @Scope
* 作用:指定 bean 的作用范围。
* 属性:value取值:singleton prototype request session globalsession
4. 和生命周期相关的: 相当于:<bean id="" class="" init-method="" destroy-method="" />
1. @PostConstruct
* 作用:指定初始化方法
2. @PreDestroy
* 作用:指定销毁方法
将上面的xml配置的例子改为注解配置:
6. 关于 Spring 注解和 XML的选择问题
1. 注解优势:配置简单,维护方便(我们找到类,就相当于找到了对应的配置)。
2. XML 的优势: 修改时,不用改源码。不涉及重新编译和部署
转载:https://blog.csdn.net/qq_43546676/article/details/104712850