目录
- Spring概述
- Spring是什么
- 为什么要学习Spring
- spring 的体系结构
- Spring相关概念
- 程序的耦合:
- 工厂模式和控制反转
- 手写工厂模式解耦案例
- spring 的 IOC 解决程序耦合
- 准备 spring 的开发包
- 基本概念
- 创建bean对象的三种方式
- bean的作用范围调整
- bean对象的生命周期
- 基于 XML 的配置IOC 耦合
Spring概述
Spring是什么
Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control:
反转控制)和 AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring
MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多
著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架.。
为什么要学习Spring
- 方便解耦,简化开发:Spring就是一个大工厂,可以将所有对象的创建和依赖关系交给Spring来管理,避免硬编码所造成的过度程序耦合。
- AOP编程的支持:Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能。
- 声明式事务的支持:只需要通过配置就可以完成对事务的管理,而无需手动编程
- 方便程序的测试:Spring对Junit4支持,可以通过注解方便的测试Spring程序
- 方便集成各种优秀框架:Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、Mybatis、Quartz等)的直接支持
- 降低JavaEE API的使用难度:Spring对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低。
spring 的体系结构
Spring相关概念
程序的耦合:
耦合:程序间的依赖关系
- 类之间的依赖
- 方法间的依赖
解耦:降低程序间的依赖关系
实际开发中:应该做到编译器不依赖,运行时才依赖。
解耦的思路:
- 使用反射来创建对象,而避免使用new关键字。
- 读取配置文件来获取来创建的对象的全限定类名
总结:
耦合是影响软件复杂程度和设计质量的一个重要因素,在设计上我们应采用以下原则:如果模块间必须存在耦合,就尽量使用数据耦合,少用控制耦合,限制公共耦合的范围,尽量避免使用内容耦合。
JavaBean:Java语言中可重复使用的组件
工厂模式和控制反转
工厂模式解耦:在实际开发中我们可以把三层的对象都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,把这些对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件,创建和获取三层对象的类就是工厂。
当某一个接口删除后,编译不会报错,依然可以正常执行,但是会报异常“找不到文件”。
降低依赖关系可以用工厂模式来实现。工厂模式可以手写,但是这样开发效率很低,所以把工厂模式解耦都交给了Spring框架去做。
控制反转 IOC
控制反转把创建的对象的权力交给框架的重要特征,并非面向对象编程的专业术语,它包括依赖注入(DI)和依赖查找(DL)
作用:削减计算机程序的耦合(解除我们代码中的依赖关系)
手写工厂模式解耦案例
- IAccountDao
package dao;
/**
* 账户的持久层接口
*/
public interface IAccountDao {
/**
* 模拟保存账户
*/
void saveAccount();
}
- IAccountDaoImpl
package dao.impl;
import dao.IAccountDao;
/**
* 账户的持久层实现类
*/
public class AccountDaoImpl implements IAccountDao {
public void saveAccount(){
System.out.println("保存了账户");
}
}
- BeanFactory(工厂方法)
package factory;
import java.io.InputStream;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
/**
* 一个创建Bean对象的工厂
*
* Bean:在计算机英语中,有可重用组件的含义。
* JavaBean:用java语言编写的可重用组件。
* javabean > 实体类
*
* 它就是创建我们的service和dao对象的。
*
* 第一个:需要一个配置文件来配置我们的service和dao
* 配置的内容:唯一标识=全限定类名(key=value)
* 第二个:通过读取配置文件中配置的内容,反射创建对象
*
* 我的配置文件可以是xml也可以是properties
*/
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中所有的Key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据bean的名称获取对象
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
/**
* 根据Bean的名称获取bean对象
* @param beanName
* @return
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
// System.out.println(beanPath);
bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
}catch (Exception e){
e.printStackTrace();
}
return bean;
}*/
}
- IAccountService
package service;
/**
* 账户业务层的接口
*/
public interface IAccountService {
/**
* 模拟保存账户
*/
void saveAccount();
}
- AccountServiceImpl
package service.impl;
import dao.IAccountDao;
import factory.BeanFactory;
import service.IAccountService;
/**
* 账户的业务层实现类
*/
public class AccountServiceImpl implements IAccountService {
// private IAccountDao accountDao = new AccountDaoImpl();
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
// private int i = 1;
public void saveAccount(){
int i = 1;
accountDao.saveAccount();
System.out.println(i);
i++;
}
}
- 配置文件 bean.properties
accountService=com.itheima.service.impl.AccountServiceImpl
accountDao=com.itheima.dao.impl.AccountDaoImpl
- 测试文件 Client
package ui;
import factory.BeanFactory;
import service.IAccountService;
/**
* 模拟一个表现层,用于调用业务层
*/
public class Client {
public static void main(String[] args) {
//IAccountService as = new AccountServiceImpl();
for(int i=0;i<5;i++) {
IAccountService as = (IAccountService) BeanFactory.getBean("accountService");
System.out.println(as);
as.saveAccount();
}
}
}
运行结果:
通过手写工厂模式清楚的展现了上述图中的工厂代理模式,这种模式可以很有效的降低程序之间的依赖关系,sping就是采用了这种模式来达到解耦的效果。
spring 的 IOC 解决程序耦合
准备 spring 的开发包
官网:http://spring.io/
下载地址:
http://repo.springsource.org/libs-release-local/org/springframework/spring
基本概念
ApplicationContext的三个常用实现类:
ClassPathXmlApplicationContext:它可以加载类路径下的配置文件,要求配置文件必须在类路径下。不在的话,加载不了。(更常用)
FileSystemXmlApplicationContext:它可以加载磁盘任意路径下的配置文件(必须有访问权限)
AnnotationConfigApplicationContext:它是用于读取注解创建容器的。
核心容器的两个接口引发出的问题:
ApplicationContext: 单例对象适用 采用此接口
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
BeanFactory: 多例对象使用
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
什么是单例和多例
单例:创建对象时在不同的地方获取的Bean对象是同一个
多例:创建对象时在不同的地方获取的Bean对象是不同的对象
创建bean对象的三种方式
<!--创建Bean的三种方式 -->
<!-- 第一种方式:使用默认构造函数创建。
在spring的配置文件中使用bean标签,配以id和class属性之后,且没有其他属性和标签时。
采用的就是默认构造函数创建bean对象,此时如果类中没有默认构造函数,则对象无法创建。
-->
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"></bean>
<!-- 第二种方式: 使用普通工厂中的方法创建对象(使用某个类中的方法创建对象,并存入spring容器)
-->
<bean id="instanceFactory" class="com.itheima.factory.InstanceFactory"></bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService"></bean>
<!-- 第三种方式:使用工厂中的静态方法创建对象(使用某个类中的静态方法创建对象,并存入spring容器)
-->
<bean id="accountService" class="com.itheima.factory.StaticFactory" factory-method="getAccountService"></bean>
bean的作用范围调整
bean标签的scope属性:
作用:用于指定bean的作用范围
取值: 常用的就是单例的和多例的
- singleton:单例的(默认值)
- prototype:多例的
- request:作用于web应用的请求范围
- session:作用于web应用的会话范围
- global-session:作用于集群环境的会话范围(全局会话范围),当不是集群环境时,它就是session
<bean id="accountService" class="com.itheima.service.impl.AccountServiceImpl"scope="singleton" init-method="init" destroy-method="destroy"></bean>
bean对象的生命周期
- 单例对象
- 出生:当容器创建时对象出生
- 活着:只要容器还在,对象一直活着
- 死亡:容器销毁,对象消亡
- 总结:单例对象的生命周期和容器相同
- 多例对象
- 出生:当我们使用对象时spring框架为我们创建
- 活着:对象只要是在使用过程中就一直活着。
- 死亡:当对象长时间不用,且没有别的对象引用时,由Java的垃圾回收器回收
基于 XML 的配置IOC 耦合
- 导入jar包依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
- :在类的根路径下创建一个任意名称的 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">
<!--把对象的创建交给spring来管理-->
<!-- 配置 service -->
<bean id="userService" class="service.impl.UserServiceImpl" scope="singleton"
init-method="init" destroy-method="destroy"></bean>
<!-- 配置 dao -->
<bean id="userDao" class="dao.impl.AccountDaoImpl"></bean>
</beans>
- 写Service接口,和接口实现类方法
UserService
package service;/*
*Created by tao on 2020-03-29.
*/
public interface UserService {
/**
* 模拟保存账户
*/
void saveAccount();
}
UserServiceImpl
package service.impl;/*
*Created by tao on 2020-03-29.
*/
import service.UserService;
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
System.out.println("对象创建了");
}
public void saveAccount() {
System.out.println("service中的saveAccount方法执行了。。。");
}
public void init() {
System.out.println("对象初始化了。。。");
}
public void destroy() {
System.out.println("对象销毁了。。。");
}
}
- 测试配置是否成功
package test;/*
*Created by tao on 2020-03-29.
*/
import org.springframework.context.support.ClassPathXmlApplicationContext;
import service.UserService;
public class Client {
public static void main(String[] args) {
//1.获取核心容器对象
ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//2.根据id获取Bean对象
UserService us1 = (UserService) ac.getBean("userService");
UserService us2 = (UserService) ac.getBean("userService");
System.out.println(us1);
System.out.println(us1 == us2);
us1.saveAccount();
}
}
运行结果
使用spring框架基于xml配置,很容易的就实现了上述很复杂的工厂模式解耦。