一.什么是Spring框架
1.什么是Spring
Spring框架是个一个全栈的框架.意思就是使用Spring的框架可以开发web层,service层还有dao层.
本质:Spring就是一个对象的容器,因为在这个容器中有可以处理请求的对象,所以可以用来写web层,因为有service层对象,所以可以用来写service层,因为有dao层对象,所以可以用来操作数据库.
Spring的开放性和包容性非常强大,可以进行无缝整个第三方优秀的框架,不会重复造轮子.
Spring框架的结构:
Spring是一个模块化的框架,Spring功能很多很强大,但是并不是要求所有的功能我们都得用上,结构中的每一个圆角举行都对应一个jar,我们使用什么,就直接引入对应的jar包就可以.
2.spring的发展历程
- 1997年IBM提出了EJB的思想
- IBM 王国商业机器公司
- EJB:是sun公司的服务器端组件模型.设计目标和核心应用是部署分布式应用程序,简单说就是将编写好的代码打包放在服务器上执行.
- 1998年,SUN制定开发标准规范EJB1.0
- 1999年,EJB1.1发布
- 2001年,EJB2.0发布
- 2003年,EJB2.1发布
- 2006年,EJB3.0发布
- Rod Johnson(spring之父)
- Expert One-to-One J2EE Design and Development(2002)阐述了J2EE使用EJB开发设计的优点及解决方案Expert One-to-One J2EE Development without EJB(2004)阐述了J2EE开发不使用EJB的解决方式(Spring雏形)
- 2017年9月份发布了spring的最新版本spring 5.0通用版(GA)
3.spring的优势.
- 方便解耦,简化开发通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,避免硬编码所造成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用
- AOP编程的支持通过Spring的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻松应付。
- 声明式事务的支持可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
- 方便程序的测试可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
- 方便集成各种优秀框架Spring可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz等)的直接支持。
- 降低JavaEE API的使用难度Spring对JavaEE API(如JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些API的使用难度大为降低。
- Java源码是经典学习范例Spring的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对Java设计模式灵活运用以及对Java技术的高深造诣。它的源代码无意是Java技术的最佳实践的范例。
二.IOC的概念和作用
IOC和AOP不是从Spring开始有的,在Spring之前更趋于理论化,在Spring这才被发扬光大的.
IOC的概念:
new的方式去引入外部资源(其他类等),这叫做正向.现在Spring框架帮我们New对象,你什么时候需要直接Spring框架要,这就叫做反转(创建对象权利的一个反转).我们丧失了一个权利(主动创建对象的权利),但是我们拥有了一个福利(我们不用考虑对象的创建、销毁等问题)。
IOC的作用:IOC解决了程序耦合问题.
- 关于程序耦合(有一个认识即可)
- 耦合:耦合度的意思,程序当中:为了实现一个业务功能,不同的模块之间互相调用就会产生耦合
- 注意:耦合是不能避免的,耦合尽可能去降低
- “高内聚、低耦合”,内聚度过高不利于代码复用,内聚度往往通过增加方法来完成(麻烦自己、方便别人)。
三.程序的耦合的介绍
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。 在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。 它有如下
- 分类:
- (1) 内容耦合。当一个模块直接修改或操作另一个模块的数据时,或一个模块不通过正常入口而转入另一个模块时,这样的耦合被称为内容耦合。内容耦合是最高程度的耦合,应该避免使用之。
- (2) 公共耦合。两个或两个以上的模块共同引用一个全局数据项,这种耦合被称为公共耦合。在具有大量公共耦合的结构中,确定究竟是哪个模块给全局变量赋了一个特定的值是十分困难的。
- (3) 外部耦合 。一组模块都访问同一全局简单变量而不是同一全局数据结构,而且不是通过参数表传递该全局变量的信息,则称之为外部耦合。
- (4) 控制耦合 。一个模块通过接口向另一个模块传递一个控制信号,接受信号的模块根据信号值而进行适当的动作,这种耦合被称为控制耦合。
- (5) 标记耦合 。若一个模块A通过接口向两个模块B和C传递一个公共参数,那么称模块B和C之间存在一个标记耦合。
- (6) 数据耦合。模块之间通过参数来传递数据,那么被称为数据耦合。数据耦合是最低的一种耦合形式,系统中一般都存在这种类型的耦合,因为为了完成一些有意义的功能,往往需要将某些模块的输出数据作为另一些模块的输入数据。
- (7) 非直接耦合 。两个模块之间没有直接关系,它们之间的联系完全是通过主模块的控制和调用来实现的。z
- 总结: 耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。 内聚与耦合 内聚标志一个模块内各个元素彼此结合的紧密程度,它是信息隐蔽和局部化概念的自然扩展。内聚是从功能角度来度量模块内的联系,一个好的内聚模块应当恰好做一件事。它描述的是模块内的功能联系。耦合是软件结构中各模块之间相互连接的一种度量,耦合强弱取决于模块间接口的复杂程度、进入或访问一个模块的点以及通过接口的数据。 程序讲究的是低耦合,高内聚。就是同一个模块内的各个元素之间要高度紧密,但是各个模块之间的相互依存度却要不那么紧密。
四.自定义IOC实现程序解耦合
这个自定义的IOC中并没有连接数据库:
pojo类: 三个成员变量.
public class Account {
private Integer id;
private String name;
private Double money;
@Override
public String toString() {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}';
}
public Double getMoney() {
return money;
}
public void setMoney(Double money) {
this.money = money;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
BeanFactory工厂类:
1.使用dom4j 和xpath读取文件,
2.使用反射new出来这个类的对象
3.使用map集合将这个对象存储起来 xml中的id为键,
4.创建一个方法,传入的参数是xml中的id值,获取到xml文件中class类所对应的对象
xml文件的配置
<?xml version="1.0" encoding="UTF-8" ?>
<!--
存储需要实例化的对象
id 唯一标识
class 类全限定名
-->
<beans>
<bean id="accountDao" class="com.dao.AccountDaoImpl"></bean>
<bean id="accountService" class="com.service.AccountServiceImpl"></bean>
</beans>
BeanFactory
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* bean 工厂
* 读取xml文件
* 反射创建对象
* 存储到集合mao中.
* 提供一个方法,根据id获取对象
* @Auther:qingmu
* @Description:脚踏实地,只为出人头地
* @Date:Created in 20:32 2019/3/19
*/
public class BeanFactory {
private static Map<String,Object> map = new HashMap<String,Object>();
/**
* 使用静态,加载类的时候,
* 读取xml中的配置文件
* 创建对象,放在map集合中.
*/
static{
// 使用类加载器,获取一个流对象
InputStream inputStream = BeanFactory.class.getClassLoader().getResourceAsStream("beans.xml");
// 使用dom4j进行读取xml文件
SAXReader saxReader = new SAXReader();
try {
Document read = saxReader.read(inputStream);
// 使用xpath获取xml文件的标签
Element rootElement = read.getRootElement();
List<Element> selectNodes = rootElement.selectNodes("//bean");
if(selectNodes!=null&&selectNodes.size()>0){
for (Element element : selectNodes) {
//获取到bean标签中的id和class
String id = element.attributeValue("id");
String className = element.attributeValue("class");
//使用反射,获取出来对象
Class<?> clazz = Class.forName(className);
Object o = clazz.newInstance();
map.put(id,o);
}
}
}catch(Exception ex){
ex.printStackTrace();
}finally {
try {
inputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 定义一个方法,通过这个方法,获取实现类对象
*/
public static Object getInstance(String id){
return map.get(id);
}
}
dao层
/**
* @Auther:qingmu
* @Description:脚踏实地,只为出人头地
* @Date:Created in 20:38 2019/3/19
*/
public interface AccountDao {
void saveAccount(Account account);
}
dao实现类
/**
* @Auther:qingmu
* @Description:脚踏实地,只为出人头地
* @Date:Created in 20:38 2019/3/19
*/
public class AccountDaoImpl implements AccountDao {
@Override
public void saveAccount(Account account) {
System.out.println("模拟保存账户");
}
}
service层
public interface AccountService {
void transfer(Account account);
}
service层实现
public class AccountServiceImpl implements AccountService{
/**
* 这是一个进行转账的操作.
* 先进行获取
* 进行赋值
* 进行放入.
*/
@Override
public void transfer(Account account) {
AccountDao accountDao = (AccountDao) BeanFactory.getInstance("accountDao");
accountDao.saveAccount(account);
}
}
使用的maven导入的依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.itheima</groupId>
<artifactId>spring_05_qm01</artifactId>
<version>1.0-SNAPSHOT</version>
<name>spring_05_qm01</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>jaxen</groupId>
<artifactId>jaxen</artifactId>
<version>1.1.6</version>
</dependency>
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-jar-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
<!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
<plugin>
<artifactId>maven-site-plugin</artifactId>
<version>3.7.1</version>
</plugin>
<plugin>
<artifactId>maven-project-info-reports-plugin</artifactId>
<version>3.0.0</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
test类
import org.junit.Test;
/**
* @Auther:qingmu
* @Description:脚踏实地,只为出人头地
* @Date:Created in 20:52 2019/3/19
*/
public class MainTest {
@Test
public void testAccount(){
Account account = new Account();
AccountService accountService = (AccountService)BeanFactory.getInstance("accountService");
accountService.transfer(account);
}
}
运行结果:
这种方法,比较麻烦,需要进行配置文件,还需要使用BeanFactory工厂进行创建对象.
五.SpringIOC的使用
在maven中使用Spring时需要导入一个依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
配置ApplicationConfig.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="accountDao" class="com.dao.AccountDaoImpl"></bean>
<bean id="accountService" class="com.service.AccountServiceImpl"></bean>
</beans>
进行测试的测试类
@Test
public void testAccount2(){
Account account = new Account();
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("ApplicationConfig.xml");
AccountService accountService = (AccountService)context.getBean("accountService");
AccountDao accountDao = (AccountDao)context.getBean("accountDao");
accountService.transfer(account);
}
六.ApplicationContext和BeanFactory的区别?
ApplicationContext:立即加载模式,在Spring启动的时候会立即实例化对象,等待使用,推荐使用
BeanFactory:使用到对象的时候(getBean等)才去实例化对象(延迟加载模式),不推荐的
测试延迟加载:
/**
* 测试BeanFactory延迟加载
*/
@Test
public void testBeanFactory(){
// 加载跟目录下的配置文件
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
Resource res = resolver.getResource("applicationContext.xml");
// 创建Bean工厂类 注意 XmlBeanFactory已经过期!
BeanFactory factory = new XmlBeanFactory(res);
AccountDao accountDAO = factory.getBean("accountDao", AccountDao.class);
AccountService accountService = factory.getBean("accountService", AccountService.class);
System.out.println(accountDAO);
System.out.println(accountService);
}
七Bean标签
id:标识符
class:实例化类的全限定类名,供反射实例化使用
scope:对象的使用范围
- singleton:默认值,单例,项目全局内存中只有一个对象,一般在使用Spring的时候保持默认值即可
- prototype:原型的意思,在这里是多例,每引用或者getBean一次,Spring框架就返回一个新的对象,比如SqlSession这个特殊的对象
- request:这种配置针对web应用,每一个request范围内只对应一个对象
- session:这种配置针对web应用,每一次会话该bean只有一个对象
- global session:针对的是portlet环境,等同于servlet下的session
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="prototype">
</bean>
/**
* 测试scope属性
* 对象使用范围
*/
@Test
public void testBeanScope(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean方法获取对象
AccountService service = (AccountService) context.getBean("accountService");
AccountService service1 = (AccountService) context.getBean("accountService");
System.out.println(service);
System.out.println(service1);
}
1.Bean标签的生命周期
- init-method指定初始化时执行的方法
- destory-method指定销毁时执行的方法
- 销毁执行方法,scope配置为单例子
- 关闭方法在ApplicationContext接口实现类ClassPathXmlApplicationContext中定义
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl" scope="singleton" init-method="init" destroy-method="destroy">
</bean>
public AccountServiceImpl(){
System.out.println("我是构造方法");
}
public void init(){
System.out.println("我是初始化方法");
}
public void destroy(){
System.out.println("我是销毁方法");
}
/**
* 测试对象生命周期
*/
@Test
public void testBeanLife(){
ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
// getBean方法获取对象
AccountService service = (AccountService) context.getBean("accountService");
System.out.println(service);
context.close();
}
2.Bean标签的创建方式.
- 创建对象:通过class配置的全限定类名创建对象,框架使用反射技术.
- 自己创建对象:加入到IOC容器中
- 静态创建
/**
* 静态工厂
* 获取接口实现类对象
*/
public class StaticFactory {
public static AccountService createAccountService(){
return new AccountServiceImpl();
}
}
<!--对象产生方式:静态工厂 -->
<bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="createAccountService"></bean>
实例化创建
public class InstanceFactory {
public AccountService createAccountService(){
return new AccountServiceImpl();
}
}
<!-- 配置实例化的工厂对象-->
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory">
</bean>
<!-- 配置工厂创建的对象 factory-bean和id相同-->
<bean id="accountService" factory-bean="instanceFactory" factory-method="createAccountService">
</bean>
八.依赖注入(DI)
Dependance Inject(依赖注入的意思)
IOC和DI的区别?
简单的说:
IOC:就是将类的对象的创建交给Spring类管理创建.
DI:依赖注入,将类里面的属性在创建类的过程中给属性赋值.
IOC相当于是一个容器,用来存放对象的,但是DI是依靠IOC容器创建出来的对象,为他们进行"赋值"通过set方法和构造器方法进行注入.
从另一个角度考虑,也可以理解为ioc容器,将dao层的对象注入到service层中.
1.set注入
bean子标签property配置
name:set方法名,去掉set
ref:创建javaBean对象的id属性值.
<!--
向service层中的成员AccountDao注入对象
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!--
name:属性配置方法名
ref:配置创建JavaBean标签的id属性值
-->
<property name="accountDao" ref="accountDao"></property>
</bean>
service层
private AccountDao accountDao;
public void setAccountDao(AccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
public void saveAccount(Account account) {
accountDao.saveAccount(account);
}
测试set方法依赖注入
/**
* 测试set方法依赖注入
*/
@Test
public void testDISet(){
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
AccountService service = (AccountService) context.getBean("accountService");
service.saveAccount(new Account());
}
简单数据类型注入
javaBean添加成员
private Integer id;
private Double money;
private String name;
public void setId(Integer id) {
this.id = id;
}
public void setMoney(Double money) {
this.money = money;
}
public void setName(String name) {
this.name = name;
}
property配置
<property name="id" value="123"></property> <property name="money" value="10.99"></property> <property name="name" value="张三"></property>
2.构造方法的注入
AccountServiceImpl添加构造方法
private AccountDao accountDao;
private Integer id;
private Double money;
private String name;
public AccountServiceImpl(AccountDao accountDao, Integer id, Double money, String name){
this.accountDao = accountDao;
this.id = id;
this.money = money;
this.name = name;
}
property配置
<constructor-arg name="accountDao" ref="accountDao"></constructor-arg> <constructor-arg name="id" value="99"></constructor-arg> <constructor-arg name="money" value="8.55"></constructor-arg> <constructor-arg name="name" value="李四"></constructor-arg>
复杂对象注入
AccountServiceImpl添加复杂数据类型
private String[] myStrs;
private List<String> myList;
private Set<String> mySet;
private Map<String,String> myMap;
private Properties myPro;
public void setMyStrs(String[] myStrs) {
this.myStrs = myStrs;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMyPro(Properties myPro) {
this.myPro = myPro;
}
property标签配
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl">
<!--
name:属性配置方法名
ref:配置创建JavaBean标签的id属性值
-->
<property name="accountDao" ref="accountDao"></property>
<property name="id" value="123"></property>
<property name="money" value="10.99"></property>
<property name="name" value="张三"></property>
<!-- 注入数组 -->
<property name="myStrs">
<array>
<value>strs1</value>
<value>strs2</value>
<value>strs3</value>
</array>
</property>
<!-- 注入List集合 -->
<property name="myList">
<list>
<value>list1</value>
<value>list2</value>
<value>list3</value>
</list>
</property>
<!-- 注入Set集合 -->
<property name="mySet">
<set>
<value>set1</value>
<value>set2</value>
<value>set3</value>
</set>
</property>
<!-- 注入Map集合 -->
<property name="myMap">
<map>
<entry key="map1" value="mapvalue1"></entry>
<entry key="map2" value="mapvalue2"></entry>
<entry key="map3" value="mapvalue3"></entry>
</map>
</property>
<!-- 注入properties集合 -->
<property name="myPro">
<props>
<prop key="pro1">provalue1</prop>
<prop key="pro2">provalue2</prop>
<prop key="pro3">provalue3</prop>
</props>
</property>
3.注解注入.