Spring框架以及基于XML的IOC配置
本篇介绍Spring的基础概念、耦合的概念以及Bean和解耦、Ioc容器的概念
本篇偏理论
Spring是什么
Spring 就是“以 IoC 容器和 AOP 引擎为核心,一站式整合 Web、数据、事务及其他第三方框架的 Java 企业级轻量级开发平台”。
Ioc容器:控制反转:负责“生对象、管对象、给对象”。
生对象:根据配置/扫描把类实例化成 Bean。
管对象:决定 Bean 的作用域(单例、原型…)、生命周期(初始化、销毁)。
给对象:当某个 Bean 需要依赖时,容器自动把依赖“注射”(DI)进去
AOP(面向切面编程):把“横切关注点”从业务代码里剥离出来,单独写成“切面”,再动态织入到指定连接点。
横切关注点:日志、权限、事务、监控等重复代码
关键概念:切面、连接点、切点、通知
实现方式:Spring 用动态代理(JDK/CGLIB)或 AspectJ 编译时/加载时织入
Spring的优势
用 Spring 的 IoC 容器接管对象创建与依赖,省掉样板代码,专注业务。
支持AOP编程
通过Spring的AOP功能,方便进行面向切面的编程,一些传统的OOP(面向对象编程)不容易实现的功能可以通过AOP轻松实现了。
支持声明式事务
用配置(注解或 XML)而不是手写代码,把“事务开始、提交、回滚”织入到业务方法。
帮我们从单调的事务管理代码中解脱,通过声明的方式灵活管理事务,提高开发的效率和质量。
方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的工作,而是随手可做的事情。
方便集成各种优秀的框架
Spring可以降低各种框架的使用难度,提供了对各种优秀框架的直接支持。
降低JavaEE API 的使用难度
Spring对JavaEE API(如JDBC\JavaMail、远程调用)进行了简单封装,降低了它们的使用难度。
Java源码是经典学习典范
Spring 的源代码设计精妙、结构清晰、匠心独用,处处体现着大师对 Java 设计模式灵活运用以及对 Java 技术的高深造诣。它的源代码无意是 Java 技术的最佳实践的范例。
程序中的耦合
使用反射解耦
什么是耦合?
耦合指的是程序之间的依赖关系
类的依赖关系
方法的依赖关系
解耦
降低程序、类以及方法之间的依赖关系叫做解耦,但注意:我们不能消除程序之间的依赖关系,只能尽可能的降低程序的依赖关系。
如何解耦
在实际开发中,我们应该做到在编译期不依赖,在运行期依赖(比如灵活运用反射机制,避免使用new关键字)
一些概念
Bean
在 Spring 语境里,Bean = 被 Spring IoC 容器实例化、组装并管理的对象。
不论它是 DAO、Service、Entity 还是工具类,只要由容器创建并注册到容器中,都叫 Bean。
JavaBean
是 Java 语言的一种组件规范:
• 必须有无参构造器
• 属性私有化,暴露 public 的 get/set
• 实现 Serializable(可选)
实体类、DTO、VO 只是符合该规范的典型例子,但任何满足上述规则的类都算 JavaBean。
工厂
BeanFactory/ApplicationContext 是**“对象工厂”**,职责只有一条:
根据配置信息把类实例化成 Bean,并放进容器单例池。
调用者通过名字拿 Bean,实现解耦。
Bean 与 JavaBean 的关系
所有被 Spring 管理的 Bean 都是 Java 对象,但并不一定非得符合 JavaBean 规范;而符合 JavaBean 规范的类,也只有被 Spring 容器实例化后才能称为 Bean。
耦合性分析及解决方案
耦合性分析
我们发现:在业务层里面(service层)会去调用dao层。这就是刚刚提到的耦合性问题。
如果此时删除掉接口的实现类,必然编译报错。
那我们如何解决呢?
解决方案
需要一个配置文件(bean.properties)来配置我们的service和dao。
配置的内容:唯一标志=全限定类名(key=value)。
通过读取配置文件中配置的内容,反射创建对象。
使用工厂解耦
示例:
建 bean.properties,在src/main/resources
accountDao=com.noy.dao.impl.AccountDaoImpl
accountService=com.noy.service.impl.AccountServiceImpl
工厂启动时一次性读完
InputStream in = BeanFactory.class.getClassLoader()
.getResourceAsStream("bean.properties");
Properties p = new Properties();
p.load(in);
1.拿到当前类的“类加载器” 2. 让类加载器把 bean.properties 当成字节输入流读出来
3.新建一个空的 Properties 对象(本质是 HashTable,专门存 k=v)
4.一次性解析并塞进 p ,后面就能 p.getProperty("accountDao") 取值了
反射 + 缓存(单例)
Object obj = Class.forName(p.getProperty(key)).newInstance();
beans.put(key, obj); // Map<String,Object> beans
用时直接拿
AccountDao dao = (AccountDao) BeanFactory.getBean("accountDao");
单例模式升级解耦
在工厂模式里我们循环调用5次service会出现什么结果?
我们发现每一次对象的内存地址都不一样,这说明每次调用的时候都重写创建了dao对象。
这说明 该业务实现类的对象是多例的。
我们的”工厂类”之所以是多例模式,是因为下面这句代码:
每调用一次,都要重新创建新的对象,比较耗时
我们通过创建一个容器,用来存放对象来将其改造为单例模式
此时再测试发现对象的地址都是一样的了
Ioc容器的概念和作用
前言
现在我们是时候揭晓IOC的谜底了。
通过以上分析,我们创建对象的方式有两种:
第一种: AccountDao dao = new AccountDaoImpl();
第二种: AccountDao dao = (AccountDao) BeanFactory.getBean("accountDao");
这两种有什么不同?
第一种创建对象的方式是我们主动创建的,控制权在我们手里。但是程序的耦合性高
第二种创建对象的方式是交给工厂帮我们创建的,控制权交给工厂了。这样降低了程序的耦合性。
但是我们每次都自己通过工厂+配置的方式创建对象的过程过于繁琐,那我们看看Spring是如何帮我们 做的。
基于XML的IOC环境搭建以及入门
创建一个maven工程,导入spring的依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
</dependencies>
后面的步骤
1.创建Dao和service接口及其实现类,这里省略图片了。
2.创建Spring的配置文件
在resources中new一个Spring Config(在XML Configuration File中)
尝试用IOC容器管理bean吧
做一个测试
测试试试看吧
详解ApplicationContext
ApplicationContext是Spring给我们提供的核心容器。我们可以去查看它的依赖关系。
ApplicationContext 是 Spring 的 “高级 IoC 容器”,它在启动时 一次性读完配置并立即把所有单例 Bean 实例化,因此程序一启动对象就全部准备就绪,随时可 getBean() 。
IoC 是“设计思想”;ApplicationContext 是 Spring 提供的“IoC 思想的具体实现”
ApplicationContext 是一个接口,它有各种实现类,如 ClassPathXmlApplicationContext、AnnotationConfigApplicationContext。
它内部真正完成了“容器”该做的事:读取配置、实例化 Bean、注入依赖、销毁 Bean。
只要一读取完配置文件马上就创建配置文件中配置的对象
我们发现ApplicationContext继承了BeanFactory。BeanFactory才是顶级容器。
但是,BeanFactory 是 Spring 的“轻量 IoC 容器”,它在启动时只读取配置并不立即实例化,而是在首次调用 getBean() 时才创建单例 Bean,实现按需加载、启动更快。
什么时候根据id获取对象了,什么时候才真正的创建对象
ApplicationContext是一个接口,它的实现类是
ClassPathXmlApplicationContext
它可以加载类路径下的配置文件,要求配置文件必须在类路径下,否则加载不了(这种比较常用)。
FileSystemXmlApplicationContext
它可以加载磁盘任意路径下的配置文件(必须有访问权限)。
AnnotationConfigApplicationContext
它是用于读取注解创建容器的,后面再介绍