Spring
简介
Spring 是一种用来简化企业级应用开发的开源框架,包括Spring Framework, Spring Data, Spring Security,Spring Boot,SpringMVC等。Spring 家族最核心的概念当属 AOP 和 IoC,详解见下节。其中 Spring 优点如下:
1. 降低了组件之间的耦合性 ,实现了软件各层之间的解耦 --------------------》低耦合
2. 可以使用便捷的众多服务,如事务管理,消息服务等 ------------------------》提供很多组件
3. 容器提供了AOP技术,利用它很容易实现如权限拦截,运行期监控等功能
4. Spring对于主流的应用框架提供了集成支持,如Hibernate、JPA等
5. Spring属于低侵入式设计,代码的污染极低
6. Spring的高度开放性,开发者可以自由选择Spring的部分或全部
AOP和IOC
相信看过spring源码的同学对这块已经掌握很好了,可以略过。
AOP(Aspect Oriented Programming,面向切面编程)
AOP简单说就是在目标方法执行前后自定义一些操作,一般都是基于代理模式来实现的,Spring支持两种代理模式,JDK原生代理和CGLib代理。AOP给程序带来良好的扩展性和封装性,可以实现业务代码与非业务代码的隔离。比如可以在不改变目标代码的前提下实现目标方法的增强:埋点业务处理、方法执行时间监控,打印日志,权限控制等等。
1. JDK动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。只能对实现了接口的类生成代理。
2. CGLib动态代理是利用ASM开源包,对代理对象类的Class文件加载进来,通过修改其字节码生成子类来处理。
- 切面(Aspect):类是对物体特征的抽象,切面就是对横切关注点的抽象。
- 切点(Pointcut):对连接点进行拦截的定义。
- 连接点(Joinpoint):被拦截到的点,比如方法(Spring中一般是方法)、字段、构造器。
- 通知(Advice):指拦截到连接点后要执行的代码,通知分为前置、后置、异常、最终、环绕五类。
AOP:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想
IoC(Inversion of Control,控制反转)
IOC (Inversion of Control,控制反转):对象之间的依赖关系由容器来创建。本来对象之间的关系是由开发者自己创建和维护的,在使用Spring框架后,对象之间的关系由容器来创建和维护,将开发者做的事让容器做,这就是控制反转。BeanFactory接口是Spring Ioc容器的核心接口。
DI (Dependecy Injection,依赖注入):我们在使用Spring容器的时候,容器通过调用set方法或者是构造器来建立对象之间的依赖关系。注入方式有设值注入、构造注入、注解注入、接口注入(基本不用
),设值注入直观,自然;构造注入可以在构造器中决定依赖关系的顺序。
控制反转是目标,依赖注入是我们实现控制反转的一种手段。
SpringMVC和Struts
SpringMVC执行流程如下:
1. 客户端向Spring容器发起一个HTTP请求。
2. 发起的请求被前端控制器
(DispatcherServlet)拦截。
3. 查询处理器映射
(HandlerMapping)得到执行链,并请求相应的处理器适配器
(HandlerAdapter)。
4. 执行处理器
(Handler)并处理请求,以ModelAndView(属性值和返回页面)的形式返回。此处Handler即平时我们编写的Controller。
5. 前端控制器
查询视图解析器
(ViewResolver),并返回View。
6. 成功渲染视图则返回给客户端,否则抛异常。
比较点 | SpringMVC | Struts |
---|---|---|
核心控制器 | DispatcherServlet | FilterDispatcher |
配置文件 | 量少(AOP) | 量大(Interceptor机制) |
RESTful API | 易实现(方法级别) | 实现费劲(类级别) |
处理Ajax请求 | @ResponseBody返回响应文本 | 拦截器集成Ajax |
性能 | 稍快 | 稍慢 |
Spring事务
数据库事务是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。事务满足原子性、一致性、持久性、隔离性四大特性。
事务管理
Spring事务管理器的接口是PlatformTransactionManager,通过这个接口,Spring为各个平台如JDBC(DataSourceTransactionManager)、Hibernate(HibernateTransactionManager、JpaTransactionManager)等都提供了对应的事务管理器。
事务的传播特性
当事务方法被另一个事务方法调用时,必须指定事务应该如何传播,Spring定义了7种传播行为:
传播行为(简写) | 含义 |
---|---|
MANDATORY | 支持当前事务;若不存在当前事务,则抛出一个异常 |
NESTED | 若果存在当前事务,则在一个嵌套的事务中执行 |
NEVER | 不支持当前事务;若存在当前事务,则抛出一个异常 |
NOT_SUPPORTED | 不支持当前事务;而总是执行非事务性 |
REQUIRED | 支持当前事务;若不存在事务,则创建一个新的事务 |
REQUIRES_NEW | 创建一个新事务,若存在一个事务,则把当前事务挂起 |
SUPPORTS | 支持当前事务;若不存在,则执行非事务性 |
TIMEOUT_DEFAULT | 使用默认超时的底层事务系统,若不支持超时则没有 |
事务隔离级别
在Spring中定义了5中不同的事务隔离级别,见下表:
1. 脏读:发生在一个事务读取了另一个事务改写但尚未提交的数据时。如果改写在稍后被回滚了,那么第一个事务获取的数据就是无效的。
2. 不可重复读:发生在一个事务执行相同的查询两次或两次以上,但是每次都得到不同的数据时。这通常是因为另一个并发事务在两次查询期间进行了更新。
3. 幻读:发生在一个事务T1读取了几行数据,接着另一个并发事务T2插入了一些数据时。在随后的查询中,事务T1就会发现多了一些原本不存在的记录。
隔离级别(简写) | 含义 |
---|---|
DEFAULT | 使用后端数据库默认的隔离级别 |
READ_UNCOMMITTED | 允许读取尚未提交的更改。可能导致脏读、幻读或不可重复读 |
READ_COMMITTED | 允许从已经提交的并发事务读取。可防止脏读,可能导致幻读或不可重复读 |
REPEATABLE_READ | 对相同字段的多次读取的结果是一致的,除非数据被当前事务本身改变。可防止脏读和不可重复读,可能导致幻读 |
SERIALIZABLE | 完全服从ACID的隔离级别。可防止脏读、不可重复读和幻读 |
Spring Boot
Spring Boot来自于 Spring 大家族,是一套全新的框架,它默认帮我们进行了很多配置,集成了大量常用的第三方库(例如 Jackson、JDBC、MongoDB、Redis、Mail 等),这些第三方库几乎都可以开箱即用。Spring Boot可以帮助我们快速搭建一个项目,从而让开发者能够更加专注于业务逻辑。
Spring扩展
- 实现BeanPostProcess接口在Bean生成前后进行操作。
- 实现BeanFactoryPostProcessor接口配置Bean元属性。
- 实现FactoryBean接口定制个性化的Bean。
MyBatis
MyBatis是一款优秀的持久层框架,它几乎避免了所有的JDBC代码和手动设置参数以及获取结果集,它可以使用XML或注解来将接口和POJO映射成数据库中的记录。
MyBatis和Hibernate
比较点 | MyBatis | Hibernate |
---|---|---|
特点 | 半自动(手写SQL) | 全自动(根据映射生成SQL) |
SQL直接优化 | 方便 | 复杂 |
数据库移植性 | 弱 | 强 |
缓存机制 | 欠缺 | 更优 |
日志系统 | 欠缺 | 完整 |
Statement和PreparedStatement的区别
/***PreparedStatement extends Statement***/
//Statement用法
sql1 = "select * from tbl_user where username='" + u + "' and password='" + p + "'";
statement = conn.createStatement();
result1 = statement.executeQuery(sql1);
//PrepareStatement用法
sql2 = "select * from tbl_user where username=? and password=?";
prepareStatement = conn.prepareStatement(sql2);
pstmt.setString(1, u);
pstmt.setString(2, p);
result2 = prepareStatement.executeQuery();
比较点 | Statement | PreparedStatement |
---|---|---|
用途 | 执行静态SQL语句并返回结果 | 执行已预编译SQL语句并返回结果 |
可读性 | 低(字符串拼接) | 高(Set方法设值) |
效率 | 低(字符串拼接) | 高(占位符) |
安全性 | 低(SQL注入) | 高 |
消息队列
- 消息队列中间件是分布式系统中重要的组件,主要主要解决应用耦合、异步消息、流量削锋等问题,具有异步性、可靠性(存储到本地硬盘)、松耦合、分布式的特性。
- 主要特点是异步处理
- 主要目的是减少请求响应时间、解耦
- 主要使用场景是将比较耗时且不需同步返回结果的操作当做消息存入队列
- 流量削峰的一种解决方案
- MQ推送模式改为定时或者批量拉取模式
- 消息接收方实现批量处理等方式
- RabbitMQ
- RabbitMQ 是一个由 ErLang 开发的AMQP的开源实现。
- 交换机(Exchange)的功能主要是接收消息并且转发到绑定的队列,交换机不存储消息,交换机本质是一张路由查询表。
- Direct:绑定时设定一个路由键,消息的路由键匹配才会被投送到队列中。
- Topic:根据模糊匹配转发消息(最灵活)。
- Headers:设置头部参数类型的交换机。
- Fanout:转发消息到所有绑定队列,消息广播的模式。
路由键(routing_key)在消息中,而绑定键(binding_key)作用于交换机和队列之间。当消息中的路由键和绑定键对应上的时候,交换机就知道将该消息存入哪个队列。
分布式
- 分布式:一个业务分拆多个子业务,部署在不同的服务器上(厨师和配菜师的关系)
- 集群:同一个业务,部署在多个服务器上(两个厨师的关系)
微服务
微服务架构风格是一种使用一套小服务来开发单个应用的方式,每个服务运行在自己的进程中,并使用轻量级机制通信(通常是HTTP API
),这些服务能够通过自动化部署机制来独立部署、可以使用不同的编程语言实现、可以使用不同的数据存储技术,并保持最低限度的集中式管理。
时下热门的微服务开发框架有:Spring Cloud、Dubbo
RESTful
URL定位资源,HTTP动词描述操作
- GET:读取资源
- POST:创建资源
- PUT:更新资源
- DELETE:删除资源
使用PUT方式更新时,必须发送资源所有的属性
Nginx
反向代理
-
正向代理:隐藏真实的请求客户端,服务端不知道真实的客户端是谁,正向代理服务器会代替客户端向服务器发送请求。正向代理代理的对象是客户端。
-
反向代理:隐藏真实的响应服务端,客户端不知道真实的服务端是谁,反向代理服务器会把请求转发到真实的服务器。反向代理代理的对象是服务端。
10086总机就是一种反向代理,客户不知道真正提供服务人的是谁。
负载均衡
- 四层负载均衡:工作在OSI模型的传输层,它在接收到客户端的流量以后通过修改数据包的地址信息将流量转发到应用服务器,因此四层负载均衡的主要工作就是转发。
- 七层负载均衡:工作在OSI模型的应用层,七层负载均衡在接到客户端的流量以后,还需要一个完整的TCP/IP协议栈与客户端建立一条完整的连接,并将应用层的请求流量解析出来,再按照调度算法选择一个应用服务器,并与应用服务器建立另外一条连接将请求发送过去,因此七层负载均衡的主要工作就是代理。
设计模式
设计模式的六大原则
- 单一职责原则:一个类只负责一个功能领域中的相应职责。
- 开闭原则:一个软件实体应当对扩展开放,对修改关闭。
- 里氏替换原则:所有引用父类的地方必须能透明地使用其子类的对象。
- 依赖倒置原则:抽象不应该依赖于细节,细节应当依赖于抽象。(要针对接口编程,而不是针对实现编程)。
- 接口隔离原则:使用多个专门的接口,而不使用单一的总接口。
- 迪米特法则:一个软件实体应当尽可能少地与其他实体发生相互作用。
单例、工厂、观察者、适配器、责任链
- 单例模式:一个类负责创建自己的对象,同时确保只有单个对象被创建,并提供一种访问其唯一对象的方式。
- 工厂模式:在创建对象时不暴露创建逻辑,并通过使用一个共同的接口来指向新创建的对象。
- 观察者模式:定义对象间的一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖于它的对象都得到通知并被自动更新。
- 适配器模式:负责加入独立的或不兼容的接口功能的类,使得原本由于接口不兼容而不能一起工作的那些类可以一起工作。
- 责任链模式:为请求创建了一个接收者对象的链,沿着这条链传递请求,直到有对象处理它为止,对请求的发送者和接收者进行解耦。
写出生产者消费者模式
生产者生产数据到缓冲区中,消费者从缓冲区中取数据。如果缓冲区已经满了,则生产者线程阻塞;如果缓冲区为空,那么消费者线程阻塞。
public class ProducerAndConsumer {
static BlockingQueue resourceQueue = new LinkedBlockingQueue<Resource>(10);
public static void main(String[] args) {
Producer p = new Producer(); //生产者
Consumer c1 = new Consumer(); //消费者1
Consumer c2 = new Consumer(); //消费者2
Consumer c3 = new Consumer(); //消费者3
p.start();
c1.start();
c2.start();
c3.start();
}
}
/**
* 资源
*/
class Resource {
int id;
public Resource(int id) {
this.id = id;
}
}
/**
* 生产者
*/
class Producer extends Thread {
int p = 1;
@Override
public void run() {
while (true) {
try {
Resource resource = new Resource(p++);
System.out.println("生产资源" + resource.id);
ProducerAndConsumer.resourceQueue.put(resource);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
/**
* 消费者
*/
class Consumer extends Thread {
@Override
public void run() {
while (true) {
try {
System.out.println("消费资源" +
((Resource) ProducerAndConsumer.resourceQueue.take()).id);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
高内聚、低耦合
- 耦合性:也称块间联系。指软件各模块之间相互联系紧密程度的一种度量。模块之间联系越紧密,则其耦合性就越强。模块间耦合高低取决于模块间接口的复杂性、调用的方式及传递的信息。
- 内聚性:也称块内联系。指软件模块内部各元素彼此结合紧密程度的一种度量。模块内各元素(语名之间、程序段之间)联系越紧密,则其内聚性就越高。
高内聚、低耦合的系统具有更好的重用性,维护性,扩展性,可以更高效的完成系统的维护开发,持续的支持业务的发展,而不会成为业务发展的障碍。