1、什么是Spring-boot?
讲原理的话就要要从spring框架开始说起,如果往深一点就是从微服务开始讲起,但为了更好的理解springboot,简单来讲就是它继承了Spring框架的优点并去除了spring最大的缺点:配置文件太多并复杂。极大的简化了Spring应用开发的框架,一站式解决J2EE的开发流程,换句话说就是一条龙服务到位。
1.1、spring的概念
Spring Boot是发展自Spring的基础上的,如果不明白Spring就介绍Spring Boot的话会觉得云里雾里。
所以你需要了解的Spring的核心概念:控制反转(IOC)和面向切面(AOP)
1.2、IOC(控制反转)
1.2.1、什么是IOC?

举一个例子,现有类 A 依赖于类 B,以前是在类 A 中手动通过new 关键字来 new一个B的对象出来。
用IOC方法实现:不通过 new 关键字来创建对象,而是通过 IOC 容器 来帮助我们实例化对象。我们需要哪个对象,直接从 IoC 容器里面过去即可。
如果还理解不明白再举一个简单的例子
1.2.2、IOC代码实例
假设有一个场景:三个角色,喝水的人(用户),饮水机(业务层),水(持久化层)。
先写一个接口

1
2
3
public interface water {
public void get();
}

现在实现两种水的类,方便展示,把它们先写在一起

1
2
3
4
5
6
7
8
9
10
11
12
13
// Hotwater.java
public class Hotwater implements water{
public void get() {
System.out.println("get hotwater");
}
}

// Clodwater.java
public class Clodwater implements water{
public void get() {
System.out.println("get clodwater");
}
}


实现一个业务层,也就是从两种水中获取到热水

1
2
3
4
5
6
7
// UserService.java
public class UserService {
private Water water = new Hotwater();
public void getWater() {
water.get();
}
}

实现一个用户类

1
2
3
4
5
6
7
// User.java
public class User {
public static void main(String[] args) {
UserService user = new UserService();
user.getWater();
}
}

以上是调用业务层UserService获取到热水,试想一下,如果现在我想喝冷水怎么办?这样便要修改业务层代码

1
2
3
4
5
6
7
// UserService.java
public class UserService {
private Water water = new Clodwater();
public void getWater() {
water.get();
}
}

从上述不难发现每当用户需求发生改变时,我们的代码都要做出相应的修改,那么如果我们工程量大,修改的内容很多怎么办?或者说如果修改会对其他业务逻辑产生BUG怎么办?解决的思路应该是在不修改源代码的前提上,我们要考虑的是控制权应该在用户上和不是饮水机(业务层)。
所以需要修改业务层的代码实现控制权的转换

1
2
3
4
5
6
7
8
9
public class UserService {
private Water water;
public void setWater(Water water) {
this.water = water;
}
public void getWater() {
this.water.get();
}
}

加入set方法,使用户层可以注入不同的对象,这样我们就可以在用户层传入哪个对象,就会获得哪个结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Hotwater
public class User {
public static void main(String[] args) {
UserImpl user = new UserImpl();
user.setWater(new HotWater()); //注入对象
user.getWater();
}
}
// Clodwater
public class User {
public static void main(String[] args) {
UserImpl user = new UserImpl();
user.setWater(new ClodWater()); //注入对象
user.getWater();
}
}

以前主动权在业务层,每次用户提出需求业务层就需要跟着做出改变,现在我们把主动权交给了用户,它传进什么值,就得到什么样的结果,这样业务代码就不用跟着改变了。试想一下,以前我们是要求饮水机给我们水,我们还要自己动。现在是拿着一个杯子,大喊一声:水来!饮水机便自动给你水杯中到热水或者冷水。
这便是IOC(控制反转)的核心思想。
1.3、AOP(面向切面)
1.3.1、什么是AOP?
要想了解AOP首先就要了解OOP(面向对象编程)比如照软件重构的思想,如果多个类中出现重复的代码,就应该考虑定义一个共同的抽象类,将这些共同的代码提取到抽象类中,比如Teacher,Student都有username,那么就可以把username及相关的get、set方法抽取到User中,这种情况,我们称为纵向抽取。
但有些功能并不适合定义横向的关系,如认证、日志、事物等,例如日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,这种散布在各处的无关的代码被称为横切,在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。
而AOP它利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。
举一个例子,双十一它有验证有效期,验证是否登录,如果要写业务逻辑验证有效期和验证登录要写两遍,有多少接口,就要多少次代码,所以提出一个公共方法,每个接口都来调用这个接口,同样有个问题,我虽然不用每次都写一遍代码了,但每个接口都要调用这个方法。于是将方法注入到接口调用的某个地方。这样接口只需要关心具体的业务,而不需要关注其他非该接口关注的逻辑。
1.3.2、AOP概述
连接点(Joinpoint) 程序执行的某个特定位置,如某个方法调用前,调用后,方法抛出异常后,这些代码中的特定点称为连接点。简单来说,就是在哪加入你的逻辑增强 连接点表示具体要拦截的方法,上面切点是定义一个范围,而连接点是具体到某个方法
切点(PointCut) 每个程序的连接点有多个,如何定位到某个感兴趣的连接点,就需要通过切点来定位。比如,连接点–数据库的记录,切点–查询条件 切点用于来限定Spring-AOP启动的范围,通常我们采用表达式的方式来设置,所以关键词是范围
增强(Advice) 增强是织入到目标类连接点上的一段程序代码。在Spring中,像BeforeAdvice等还带有方位信息 通知是直译过来的结果,我个人感觉叫做“业务增强”更合适 对照代码就是拦截器定义的相关方法,通知分为如下几种:
前置通知(before):在执行业务代码前做些操作,比如获取连接对象
后置通知(after):在执行业务代码后做些操作,无论是否发生异常,它都会执行,比如关闭连接对象
异常通知(afterThrowing): 在执行业务代码后出现异常,需要做的操作,比如回滚事务
返回通知(afterReturning):在执行业务代码后无异常,会执行的操作
环绕通知(around): 这个目前跟我们谈论的事务没有对应的操作,所以暂时不谈
目标对象(Target):需要被加强的业务对象
织入(Weaving):织入就是将增强添加到对目标类具体连接点上的过程。 织入是一个形象的说法,具体来说,就是生成代理对象并将切面内容融入到业务流程的过程。
代理类(Proxy): 一个类被AOP织入增强后,就产生了一个代理类。
切面(Aspect) 切面由切点和增强组成,它既包括了横切逻辑的定义,也包括了连接点的定义,SpringAOP就是将切面所定义的横切逻辑织入到切面所制定的连接点中。 比如上文讨论的数据库事务,这个数据库事务代码贯穿了我们的整个代码,我们就可以这个叫做切面。 SpringAOP将切面定义的内容织入到我们的代码中,从而实现前后的控制逻辑。 比如我们常写的拦截器Interceptor,这就是一个切面类。
举一个例子 ,以数据库的操作为例来说明:
获取连接对象对应前置通知
执行SQL(核心业务代码)对应连接点
如果有异常
回滚事务对应返回通知
无异常则提交事务对应异常通知
关闭连接对应后置通知
整个的执行过程便是切面
1.3.3、AOP代码实例
动态代理
动态代理指的是动态创建一组指定的接口的实现对象
先写个接口

1
2
3
4
public interface Begin {
//运行方法
public void server();
}

给出该接口的实现类

1
2
3
4
5
6
7
public class ServerBegin implements Begin {

@Override
public void server() {
System.out.println("运行中");
}
}

添加一个前置增强接口

1
2
3
4
5
6
7
8
9
10
/**
* 前置增强
*/
public interface BeforeAdvice {
public void before();
}
再添加一个后置增强接口
public interface AfterAdvice {
public void after();
}

把产生代理对象的代码封装为一个类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import com.sun.org.apache.regexp.internal.RE;
import org.junit.After;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;


/**
* 1、创建代理工厂
* 2、给工厂设置目标对象、前置增强、后置增强
* 3、调用creatProxy()得到代理对象
* 4、执行代理对象方法时,先执行前置增强,然后是目标方法,最后是后置增强
*/

public class ProxyFactory {
private Object targetObject;//目标对象
private BeforeAdvice beforeAdvice;//前值增强
private AfterAdvice afterAdvice;//后置增强

/**
* 用来生成代理对象
* @return
*/
public Object creatProxy() {
/**
* 给出三个参数
*/
ClassLoader classLoader = this.getClass().getClassLoader();
//获取当前类型所实现的所有接口类型
Class[] interfaces = targetObject.getClass().getInterfaces();

InvocationHandler invocationHandler = new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
/**
* 在调用代理对象的方法时,会执行这里的内容
*/
if(beforeAdvice != null) {
beforeAdvice.before();
}
Object result = method.invoke(targetObject, args);//调用目标对象的目标方法
//执行后续增强
afterAdvice.after();

//返回目标对象的返回值
return result;
}
};
/**
* 2、得到代理对象
*/
Object proxyObject = Proxy.newProxyInstance(classLoader, interfaces, invocationHandler);
return proxyObject;

}
//get和set方法略
}
将相关的参数注入到ProxyFactory后就可以通过creatProxy()方法获取代理对象
import org.junit.Test;


public class Demo {
@Test
public void test() {

ProxyFactory proxyFactory = new ProxyFactory();//创建工厂
proxyFactory.setTargetObject(new ServerBegin());//设置目标对象
//设置前置增强
proxyFactory.setBeforeAdvice(new BeforeAdvice() {
@Override
public void before() {
System.out.println("你好");
}
});
//设置后置增强
proxyFactory.setAfterAdvice(new AfterAdvice() {
@Override
public void after() {
System.out.println("再见");
}
});
Begin begin = (Begin) proxyFactory.creatProxy();
begin.server();

}
}

变可得到以下结果
运行中
你好
再见
2、Spring-boot的核心功能
独立运行的 Spring 项目
Spring Boot 可以以 jar 包的形式独立运行,运行一个 Spring Boot 项目只需通过 java–jar xx.jar 来运行。
内嵌 Servlet容器
Spring Boot 可选择内嵌 Tomcat、Jetty 或者 Undertow,这样我们无须以 war 包形式部署项目。
简化Maven配置
Spring 提供了一系列的 starter pom 来简化 Maven 的依赖加载,例如,当你使用了spring-boot-starter-web 时,会自动加入如图 1 所示的依赖包。
自动配置 Spring
Spring Boot 会根据在类路径中的 jar 包、类,为 jar 包里的类自动配置 Bean,这样会极大地减少我们要使用的配置。当然,Spring Boot 只是考虑了大多数的开发场景,并不是所有的场景,若在实际开发中我们需要自动配置 Bean,而 Spring Boot 没有提供支持,则可以自定义自动配置。
准生产的应用监控
Spring Boot 提供基于 http、ssh、telnet 对运行时的项目进行监控。
无代码生成和 xml 配置
Spring Boot 的神奇的不是借助于代码生成来实现的,而是通过条件注解来实现的,这是 Spring 4.x 提供的新特性。Spring 4.x 提倡使用 Java 配置和注解配置组合,而 Spring Boot 不需要任何 xml 配置即可实现 Spring 的所有配置。
3、Spring Boot的优缺点
3.1、优点

快速构建项目。
对主流开发框架的无配置集成。
项目可独立运行,无须外部依赖Servlet容器。
提供运行时的应用监控。
极大地提高了开发、部署效率。
与的天然集成。
3.2、缺点
版本更新速度很快,一些模块改动很大。
由于不用自己做配置,报错时很难定位。
网上现成的解决方案比较少。