Spring入门案例
一、Spring基本介绍
1.什么是Spring
Spring 是分层的 Java SE/EE 应用 full-stack 轻量级开源框架,以 IoC(Inverse Of Control: 反转控制)AOP(Aspect Oriented Programming:面向切面编程)为内核,提供了展现层 Spring MVC 和持久层 Spring JDBC 以及业务层事务管理等众多的企业级应用技术,还能整合开源世界众多 著名的第三方框架和类库,逐渐成为使用最多的 Java EE 企业应用开源框架。
2.Spring的优势
- 方便解耦,简化开发
通过 Spring 提供的 IoC 容器,可以将对象间的依赖关系交由 Spring 进行控制,避免硬编码所造 成的过度程序耦合。用户也不必再为单例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于上层的应用。
- AOP 编程的支持
通过 Spring 的 AOP 功能,方便进行面向切面的编程,许多不容易用传统 OOP 实现的功能可以通过 AOP 轻松应付。
- 声明式事务的支持
可以将我们从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活的进行事务的管理,提高开发效率和质量。
- 方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,测试不再是昂贵的操作,而是随手可做的事情。
- 方便集成各种优秀框架
Spring 可以降低各种框架的使用难度,提供了对各种优秀框架(Struts、Hibernate、Hessian、Quartz 等)的直接支持。
- 降低 JavaEE API 的使用难度
Spring 对 JavaEE API(如 JDBC、JavaMail、远程调用等)进行了薄薄的封装层,使这些 API 的使用难度大为降低。
二、程序的耦合和解耦
1.什么是耦合
耦合性(Coupling),也叫耦合度,是对模块间关联程度的度量。耦合的强弱取决于模块间接口的复杂性、调用模块的方式以及通过界面传送数据的多少。模块间的耦合度是指模块之间的依赖关系,包括控制关系、调用关系、数据传递关系。模块间联系越多,其耦合性越强,同时表明其独立性越差( 降低耦合性,可以提高其独立 性)。耦合性存在于各个领域,而非软件设计中独有的,但是我们只讨论软件工程中的耦合。
在软件工程中,耦合指的就是就是对象之间的依赖性。对象之间的耦合越高,维护成本越高。因此对象的设计应使类和构件之间的耦合最小。软件设计中通常用耦合度和内聚度作为衡量模块独立程度的标准。划分模块的一个准则就是高内聚低耦合。
2.耦合的例子
下面这段代码就演示了程序的耦合:
public class JdbcDemo1 {
public static void main(String[] args) throws SQLException {
//1.注册驱动
DriverManager.registerDriver(new com.mysql.cj.jdbc.Driver());
//2.获取连接
Connection connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/spring", "root","12345678");
//3.获取操作数据库的预处理对象
PreparedStatement preparedStatement = connection.prepareStatement("select * from account");
//4.执行SQL语句,得到结果
ResultSet resultSet = preparedStatement.executeQuery();
//5.遍历结果集
while (resultSet.next()){
System.out.println(resultSet.getString("name"));
}
//6.释放资源
resultSet.close();
preparedStatement.close();
connection.close();
}
}
创建数据库的SQL语句如下:
create table account(
id int primary key auto_increment,
name varchar(40),
money float
)character set utf8 collate utf8_general_ci;
insert into account(name,money) values('aaa',1000);
insert into account(name,money) values('bbb',1000);
insert into account(name,money) values('ccc',1000);
运行结果如下:
3.解决方案
在上述代码中,我们采用DriverManager.registerDriver方法来注册驱动,这会导致一个编译期依赖,如果我们的项目中没有导入对应的jar包或者更换了数据库品牌(比如 Oracle),项目在编译时就会出错,需要修改源码来重新数据库驱动。这显然不是我们想要的。因此,我们通常采用Class.forName("com.mysql.jdbc.Driver")的方式来注册驱动。
4.解耦合
所谓解耦合,我们可以简单理解为降低程序间的依赖。在实际开发中,应该做到尽量避免编译器依赖,而尽可能是运行时依赖。上述案例,当采用Class.forName方式之后,我们就可以使用反射类创建对象,而避免使用new关键字。此时,我们的类中不再依赖具体的驱动类,此时就算删除 mysql 的驱动jar包,依然可以编译(运行还是会报错,没有驱动不可能运行成功的)。但是,这也产生了一个新的问题,mysql 驱动的全限定类名字符串是在 java 类中写死的,一旦要改还是要修改源码。我们可以通过读取配置文件的方式来获取要创建对象的权限定类名来解决这个问题。
三、工厂模式解耦
1.传统三层架构存在的问题
新建一个IDEA工程用于模拟传统的三层架构,在src/mian/java/目录下创建如下内容:
具体代码如下:
- 数据访问层:
//账户的持久层接口IAccountDao
public interface IAccountDao {
/**
* 模拟和数据库进行交互
*/
void saveAccounts();
}
//账户的持久层接口的实现类AccountDaoImpl
public class AccountDaoImpl implements IAccountDao {
/**
* 模拟和数据库进行交互
*/
public void saveAccounts() {
System.out.println("向数据库写入账户数据!!!");
}
}
- 业务逻辑层
//账户的业务层接口IAccountService
public interface IAccountService {
/**
* 模拟保存账户操作
*/
void saveAccounts();
}
//账户的业务层接口的实现类AccountServiceImpl
public class AccountServiceImpl implements IAccountService {
//持久层接口对象的引用
private IAccountDao accountDao = new AccountDaoImpl();
/**
* 模拟保存账户操作
*/
public void saveAccounts() {
System.out.println("执行保存账户操作");
//调用持久层接口函数
accountDao.saveAccounts();
}
}
- 表现层
//模拟表示层Client,用于调用业务层
public class Client {
public static void main(String[] args) {
IAccountService accountService = new AccountServiceImpl();
accountService.saveAccounts();
}
}
运行结果如下:
2.存在的问题
在上述案例中,业务层调用持久层,并且此时业务层在依赖持久层的接口和实现类。如 果此时没有持久层实现类, 编译将不能通过。这种编译期依赖关系,应该在我们开发中杜绝。我们需要优化代码解决。
3.解决方法
在实际开发中,我们可以用工厂模式来解决这个问题。具体来说就是,将三层架构的全限定类名都使用配置文件配置起来,当启动服务器应用加载的时候,让一个类中的方法通过读取配置文件,运用反射把对象创建出来并存起来。在接下来的使用的时候,直接拿过来用就好了。那么,这个读取配置文件,创建和获取三层对象的类就是工厂类。或者,我们可以简单地理解为工厂是用来创建Bean对象的。配置文件的内容应该包含两个部分:唯一标识符和全限定类名(key-value的结构)。配置文件可以是XML文件或者properties文件。
4.工厂模式解耦合
a.在src/java/main目录下,新建包factory,在factory包下新建类BeanFactory:
//创建Bean对象的工厂类
public class BeanFactory {
//Properties对象,用于读取配置文件
private static Properties properties;
//使用静态代码为Properties对象赋值
static {
try {
//实例化对象
properties = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
//加载配置文件
properties.load(in);
} catch (IOException e) {
throw new ExceptionInInitializerError("初始化properties对象失败");
}
}
/**
* 根据Bean的名称获取Bean对象
* @param beanName Bean对象的名称
* @return
*/
public static Object getBean(String beanName) {
Object bean = null;
//获取Bean对象的全限定类名
String beanPath = properties.getProperty(beanName);
//从Properties对象中获取Bean对象
try {
bean = Class.forName(beanPath).getDeclaredConstructor().newInstance();
} catch (Exception e) {
e.printStackTrace();
}
return bean;
}
}
b.在src/resources目录下,新建bean.properties文件,内容如下:
#账户的业务层接口的实现类
accountService = service.impl.AccountServiceImpl
#账户的数据访问层接口的实现类
accountDao = dao.impl.AccountDaoImpl
c.改造数据访问层和业务逻辑层
更改ui/Client类中main方法的第一行代码为:
IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
更改service/impl/AccountServiceImpl类中实例化持久层接口对象的代码为:
private IAccountDao accountDao = (IAccountDao) BeanFactory.getBean("accountDao");
d.运行结果
当进行上述改造之后,就不再通过new关键字创建对象,而是通过对象名来找到配置文件中对应的全限定类名,然后通过反射创建对象。这就降低了程序之间的依赖,运行结果与之前完全一致。
5.单例和多例模式
a.多例模式
更改ui/Client类中main方法为:
public static void main(String[] args) {
for(int i = 0; i < 5; i++){
//采用工厂模式创建对象
IAccountService accountService = (IAccountService) BeanFactory.getBean("accountService");
System.out.println(accountService);
}
}
可以看到运行结果为:
很明显这是5个不同的对象,这是因为我们在BeanFactory.getBean方法中创建Bean对象的方式为:Class.forName(beanPath).getDeclaredConstructor().newInstance(),这样获取Bean对象时,都会调用默认的构造函数创建一个新的对象。多例模式由于对象被创建多次,因此执行效率没有单例模式高。
b.单例模式
更改factory/BeanFactory类的代码如下:
public class BeanFactory {
//Properties对象,用于读取配置文件
private static Properties properties;
//定义一个Map,用于存放要创建Bean对象,也就是一个容器
private static Map<String, Object> beansMap = null;
//单例模式
static {
try {
//实例化对象
properties = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
//加载配置文件
properties.load(in);
//实例化容器
beansMap = new HashMap<String, Object>();
//取出配置文件中所有的key
Enumeration<Object> beanKeys = properties.keys();
//遍历keys
while(beanKeys.hasMoreElements()){
//取出每个key,也就是对象名
String beanName = beanKeys.nextElement().toString();
//根据key获取value,也就是全限定类名
String beanPath = properties.getProperty(beanName);
//反射创建对象
Object beanObject = Class.forName(beanPath).getDeclaredConstructor().newInstance();
//存入容器
beansMap.put(beanName,beanObject);
}
} catch (Exception e) {
e.printStackTrace();
throw new ExceptionInInitializerError("初始化创建Bean对象失败!");
}
}
//单例模式
public static Object getBean(String beanName) {
return beansMap.get(beanName);
}
}
运行结果为:
可以看到这时5个对象都是同一个对象,当BeanFacyory类加载时,就会创建Bean对象并存入Map结构中。当需要对象时,就会从这个Map结构中去取,因此都是同一个对象。
Spring入门案例的更多相关文章
- spring入门--spring入门案例
spring是一个框架,这个框架可以干很多很多的事情.感觉特别吊.但是,对于初学者来说,很难理解spring到底是干什么的.我刚开始的时候也不懂,后来就跟着敲,在后来虽然懂了,但是依然说不明白它到底是 ...
- Spring入门案例 idea创建Spring项目
spring入门案例 idea创建spring项目 Spring介绍 Spring概述 Spring是一个开源框架,Spring是2003年兴起的轻量级java开发框架,由Rod Johnson 在其 ...
- Spring(二)--Spring入门案例
Spring入门案例 1.需要的实体类 2.需要的接口和实现类 3.需要的service和实现类 /** * service层的作用 * 在不改变dao层代码的前提下,增加业务逻辑操作 */ publ ...
- spring入门案例分析及原理
Springmvc执行原理: 一. 入门案例的执行流程 1. 当启动Tomcat服务器的时候,因为配置了load-on-startup标签,所以会创建DispatcherServlet对象,就会加载s ...
- 分分钟教会大家第一个Spring入门案例
1.下载Spring jar包,并添加到项目中. 官网地址http:springsource.org 2.在项目中新建一个类 package cn.test; public class He ...
- Spring_第一个Spring入门案例IOC
今天我们来写我们的第一个spring 第一步 建立一个java project 第二步 添加我们的五个jar文件 第三步 在项目中建立一个com.zk.spring包 第四步 建立我们的userser ...
- SSM-Spring-01:Spring的概念+入门案例
------------吾亦无他,唯手熟尔,谦卑若愚,好学若饥------------- Spring 提起Spring,就会想到企业级框架这个词 企业级系统: 1.大规模:用户数量多,数据规模庞大, ...
- spring-cloud-Zuul学习(一)【基础篇】--入门案例【重新定义spring cloud实践】
-- 2 ...
- 1.Spring框架入门案例
一.简单入门案例 入门案例:IoC 1.项目创建与结构 2.接口与实现类 User.java接口 package com.jd.ioc; /** * @author weihu * @date 201 ...
随机推荐
- TLS1.2协议设计原理
目录 前言 为什么需要TLS协议 发展历史 协议设计目标 记录协议 握手步骤 握手协议 Hello Request Client Hello Server Hello Certificate Serv ...
- spring boot 配置虚拟静态资源文件
我们实现的目的是:通过spring boot 配置静态资源访问的虚拟路径,可实现在服务器,或者在本地通过:http://ip地址:端口/资源路径/文件名 ,可直接访问文件 比如:我们本地电脑的:E: ...
- .Net Core Configuration源码探究
前言 上篇文章我们演示了为Configuration添加Etcd数据源,并且了解到为Configuration扩展自定义数据源还是非常简单的,核心就是把数据源的数据按照一定的规则读取到指定的字 ...
- springcloud2.0 添加配置中心遇到的坑
新手入门,在springcloud 配置config的时候遇到了几个比较烦的坑 先说1.5x版本的一些配置吧 首先是端点暴露的方式 management: security: enabled: fal ...
- skywalking中表字段的信息
https://skyapm.github.io/document-cn-translation-of-skywalking/zh/6.2.0/concepts-and-designs/scope-d ...
- SpringBoot中VO,DTO,DO,PO的概念、区别和用处
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/zhuguang10/article/de ...
- Linux下nginx反向代理服务器安装与配置实操
1.我们只要实现访问nginx服务器能跳转到不同的服务器即可,我本地测试是这样的, 在nginx服务器里面搭建了2个tomcat,2个tomcat端口分别是8080和8081,当我输入我nginx服务 ...
- XDocument常用属性
XDocument常用属性: 1) BaseUri 获取此 XObject 的基 URI. (继承自 XObject.) 2) Declaration 获取或设置此文档的 XML 声明. 3) Doc ...
- 洛谷 P4042 [AHOI2014/JSOI2014]骑士游戏
题意 有\(n\)个怪物,可以消耗\(k\)的代价消灭一个怪物或者消耗\(s\)的代价将它变成另外一个或多个新的怪物,求消灭怪物$的最小代价 思路 \(DP\)+最短路 这几天做的第一道自己能\(yy ...
- 我终于弄懂了Python的装饰器(一)
此系列文档: 1. 我终于弄懂了Python的装饰器(一) 2. 我终于弄懂了Python的装饰器(二) 3. 我终于弄懂了Python的装饰器(三) 4. 我终于弄懂了Python的装饰器(四) 一 ...