微服务阶段 javase:OOP
MySQL:持久化
html+css+js+jquery+框架:视图,框架不熟练,css不好
javaweb:独立开发MVC三层架构的网站:原始
ssm:框架:简化了我们的开发流程,配置也开始较为复杂;
在此之前项目打包都是war包,程序在Tomcat中运行
spring再简化:springBoot-jar包,内嵌Tomcat;微服务架构! 服务越来越多:springCloud
1.SpringBoot 初识 1.1 回顾什么是SpringSpring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson 。
Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。
Spring是如何简化Java开发的
为了降低Java开发的复杂性,Spring采用了以下4种关键策略:
1、基于POJO的轻量级和最小侵入性编程,所有东西都是bean; 2、通过IOC,依赖注入(DI)和面向接口实现松耦合; 3、基于切面(AOP)和惯例进行声明式编程; 4、通过切面和模版减少样式代码,RedisTemplate,xxxTemplate;1.2 什么是SpringBoot
学过javaweb的同学就知道,开发一个web应用,从最初开始接触Servlet结合Tomcat, 跑出一个Hello Wolrld程序,是要经历特别多的步骤;后来就用了框架Struts,再后来是SpringMVC,到了现在的SpringBoot,过一两年又会有其他web框架出现;你们有经历过框架不断的演进,然后自己开发项目所有的技术也在不断的变化、改造吗?建议都可以去经历一遍;
言归正传,什么是SpringBoot呢,就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。
所有的技术框架的发展似乎都遵循了一条主线规律:从一个复杂应用场景 衍生 一种规范框架,人们只需要进行各种配置而不需要自己去实现它,这时候强大的配置功能成了优点;发展到一定程度之后,人们根据实际生产应用情况,选取其中实用功能和设计精华,重构出一些轻量级的框架;之后为了提高开发效率,嫌弃原先的各类配置过于麻烦,于是开始提倡“约定大于配置”,进而衍生出一些一站式的解决方案。
是的这就是Java企业级应用->J2EE->spring->springboot的过程。
随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。
Spring Boot的主要优点:
为所有Spring开发者更快的入门
开箱即用,提供各种默认配置来简化项目配置
内嵌式容器简化Web项目
没有冗余代码生成和XML配置的要求
真的很爽,我们快速去体验开发个接口的感觉吧!
1.3 微服务架构微服务是一种架构风格,他要求我们在开发一个应用的时候,这个应用必须建成一系列小服务组合,可以通过http方式进行通信。
所谓微服务加购,就是打破之前all in one的架构方式,把每个功能元素独立出来,把独立出来的功能元素的动态组合,需要的功能元素才去拿来组合,需要多一些可以整合多个功能元素,所以微服务架构是对功能元素进行赋值,而没有对整个应用进行复制,这样做的好处是:
节省了调用资源 每个功能元素的服务都是一个可替换的,可独立升级的软件代码 程序核心:高内聚(在划分模块时,要把功能关系紧密的放到一个模块中) 低耦合(模块之间的联系越少越好,接口越简单越好)
论文地址:Microservices
构建一个个功能独立的微服务应用单元,可以使用springboot,可以帮我们快速构建一个应用 大型分布式网络服务的调用,这部分springcloud来完成,实现分布式 在分布式中间,进行流式数据计算,批处理,我们有spring cloud data flow spring为我们想清楚了整个开始构建应用到大型分布式应用全流程方案
2,第一个SpringBoot 程序通过Idea创建SpringBoot项目, 新建一个项目, 选择Spring Initalizr.
idea帮我们创建好了项目之后,在项目目录下会有一个主程序的启动入口, 我们直接运行这个主程序。 第一个SpringBoot 程序就跑起来了。
@SpringBootApplication
public class Boot01Application {
public static void main(String[] args) {
// SpringApplication app = new SpringApplication();
SpringApplication.run(Boot01Application.class, args);
}
}
写个接口玩一玩?
新建controller包, 要和项目的主程序在同一目录!
新建我们自定义的Controller类。
@RestController
@RequestMapping("/user")
public class MyController {
@RequestMapping("/getUserList")
public String getUserList() throws JsonProcessingException {
ArrayList user = new ArrayList<>();
user.add("zyd");
user.add("万众期待的SpringBoot");
user.add("老子终于学到SpringBoot了!!");
user.add("zyd的第一个SpringBoot程序");
ObjectMapper mapper = new ObjectMapper();
return mapper.writevalueAsString(user);
}
}
之后,不需要做任何配置,重新启动SpringBoot主程序即可!
SpringBoot 内嵌了tomcat , SpringMVC,将服务启动在http://localhost:8080
打开浏览器,输入http://localhost:8080/user/getUserList, 就可以看到接口返回的结果了。是不是用SpringBoot开发一个接口特别爽,不需要我们使用xml文件去配置了,从头到尾,我们只新建了一个包,写了一个接口, 其余的啥也不用配置,SpringBoot 都帮我们配置好了,爽~
3,SpringBoot自动配置原理从SpringBoot 的程序入口上来看,主类上会有@SpringBootApplication 这个注解,这就SpringBoot 的核心配置, 它可以帮我们自动装配好所有的依赖。
@SpringBootApplication
public class Boot01Application {
public static void main(String[] args) {
SpringApplication.run(Boot01Application.class, args);
}
}
点进去这个注解:
还有三个核心的注解:
@SpringBootConfiguration
这个注解见名知意,就是SpringBoot 的配置
这个注解上还有一个注解: @Configuration, 我们点进去看一下
@Component 实际上就是一个组件。
@EnableAutoConfiguration
以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置 ;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能,这样自动配置才能生效;
这个注解中最重要的是导入的AutoConfigurationimportSelector这个类,我们点进去看看。
先说@import:
@import
Spring底层注解@import , 给容器中导入一个组件
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@documented
public @interface import {
Class>[] value();
}
@import(AutoConfigurationimportSelector.class)
给容器导入组件 ;
而这个注解也是一个派生注解,其中的关键功能由@import提供,其导入的AutoConfigurationimportSelector的selectimports()方法通过SpringFactoriesLoader.loadFactoryNames()扫描所有具有meta-INF/spring.factories的jar包。spring-boot-autoconfigure-x.x.x.x.jar里就有一个这样的spring.factories文件。
AutoConfigurationimportSelector :自动配置导入选择器,那么它会导入哪些组件的选择器呢?我们点击去这个类看源码:
其中有这样一个方法:
protected ListgetCandidateConfigurations(Annotationmetadata metadata, AnnotationAttributes attributes) { //这里的getSpringFactoriesLoaderFactoryClass()方法 //返回的就是我们最开始看的启动自动导入配置文件的注解类;EnableAutoConfiguration List configurations = SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()); Assert.notEmpty(configurations, "No auto configuration classes found in meta-INF/spring.factories. If you are using a custom packaging, make sure that file is correct."); return configurations; }
这个方法又调用了SpringFactoriesLoader 类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法
public static ListloadFactoryNames(Class> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoader == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
我们继续点击查看 loadSpringFactories 方法
private static Map> loadSpringFactories(ClassLoader classLoader) { Map > result = (Map)cache.get(classLoader); // 先从缓存中拿 if (result != null) { return result; } else { // 如果缓存中拿不到,则重新导入 HashMap result = new HashMap(); try { // 读取配置,去获取一个资源 "meta-INF/spring.factories" Enumeration urls = classLoader.getResources("meta-INF/spring.factories"); // 遍历这些配置,将其转换未Properties while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); Iterator var6 = properties.entrySet().iterator(); while(var6.hasNext()) { Entry, ?> entry = (Entry)var6.next(); String factoryTypeName = ((String)entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); String[] var10 = factoryImplementationNames; int var11 = factoryImplementationNames.length; for(int var12 = 0; var12 < var11; ++var12) { String factoryImplementationName = var10[var12]; ((List)result.computeIfAbsent(factoryTypeName, (key) -> { return new ArrayList(); })).add(factoryImplementationName.trim()); } } } result.replaceAll((factoryType, implementations) -> { return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)); }); cache.put(classLoader, result); return result; } catch (IOException var14) { throw new IllegalArgumentException("Unable to load factories from location [meta-INF/spring.factories]", var14); } } }
资源是从spring.factories读取的,我们全局搜索这个文件
看到了很多自动配置的文件;这就是自动配置根源所在! 这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示
WebMvcAutoConfiguration
可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!
所以,自动配置真正实现是从classpath中搜寻所有的meta-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
springboot所有的自动配置都是在启动的时候扫描并加载,扫描了spring.properties配置文件,所有的自动配置类都在这里面,但是不定生效,因为要判断条件是否成立,只要导入了对应的start,就有对应的启动器,有了启动器我们自动装配就会生效,然后就配置成功
步骤:
SpringBoot在启动的时候从类路径下的meta-INF/spring.factories中获取EnableAutoConfiguration指定的值 将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
以前需要我们配置的文件,springboot帮我们配置了!
整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration)@Bean, 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 @Configuration;
有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作
SpringApplication.run分析
分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;
SpringApplication
这个类主要做了以下四件事情:
1、推断应用的类型是普通的项目还是Web项目
2、查找并加载所有可用初始化器 , 设置到initializers属性中
3、找出所有的应用程序监听器,设置到listeners属性中
4、推断并设置main方法的定义类,找到运行的主类
查看构造器:
public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) {
// ......
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.setInitializers(this.getSpringFactoriesInstances();
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
关于springboot,谈谈你的理解:
springboot的自动装配 run(): ①判断当前项目是普通项目还是web项目 ②推断并设置main方法的定义类,找到运行的主类 ③run方法里面有一些监听器,这些监听器是全局存在的,它的作用是获取上下文处理一些bean,所有的bean无论是加载还是生产初始化多存在。
4, YamlYAML是 "YAML Ain't a Markup Language" (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)
这种语言以数据作为中心,而不是以标记语言为重点!
以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml
传统xml配置:
8081
yaml配置:
server: prot: 8080基础语法
说明:语法要求严格!
1、空格不能省略
2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
3、属性和值的大小写都是十分敏感的。
# 普通的键值对
# 格式: key: value 空格不能省略
# 我们在这里配置的值可以注入到Java对象中, 可以直接给实体类赋值, 这就是yaml的强大之处!
name: zyd
# 对象
student:
name: zyd
age: 18
# 行内对象
student2: { name: zyd,age: 18 }
# 数组
pets:
- cat
- og
- pig
# 数组的行内语法
studentList: [ zyd, djy, cmy ]
Yaml属性配置注入
使用@ConfigurationProperties(prefix = ”对象名称“),可以注入属性到实体类中,前提就是实体类的属性名和Yaml对象的属性名是一致的!
@Data
@Component
@ConfigurationProperties(prefix = "person") // 使用yaml 注入到实体类中,前提是实体类的属性和yaml对象的属性名是对应的!
public class Person {
private String name;
private String age;
private String school;
private Dog dog;
private Map map;
private List
YAML配置文件中配置对象。
person:
name: zyd${random.uuid}
age: 18-${random.int}
school: ${person.age:other}_TUST # 如果有person这个属性,则拿到, 如果没有则使用:后面的值
dog:
name: zyj
age: 10
host: zyd
map:
contry: CHINA
list: [ code, eat, sleep ]
测试类:
@Autowired
private Person person;
@Test
void contextLoads() {
// System.out.println(dog.toString());
System.out.println(person.toString());
}
5, 多环境切换
我们日常开发时,用到的环境是开发环境。部署到服务器上是生产环境,那么这两个环境的配置属性(如路径。。。)一定是不同的, SpringBoot 提供了多环境配置,我们指定哪个环境就可以使用哪个环境的配置。
# 激活的环境配置 spring: profiles: active: dev --- server: port: 8083 spring: profiles: dev --- server: port: 8084 spring: profiles: prod #配置环境的名称 6, 自动配置原理加深
在SpringFactories中的依赖项,我们点开一个配置CodecsAutoConfiguration。
//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration
//启动指定类的ConfigurationProperties功能;
//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
//并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({HttpProperties.class})
//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({CharacterEncodingFilter.class})
//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
//如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean //判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
//。。。。。。。
}
关于这个注解@EnableConfigurationProperties(),将传入的类加载成properties添加到IOC容器中, 我们点进去看一下ServerProperties。
@ConfigurationProperties 正是从yaml配置文件中拿出对应的配置。
我们可以配置SpringBoot 的yaml文件来配置这些属性。
7, 静态资源处理原理我们在做web开发时,肯定会设计到很多静态资源的,这里我们看一下SpringBoot是怎样处理静态资源的。
在SpringFactories,点开WebMvcAutoConfiguration。
加载资源的方法
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
// 首先判断是否适配
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
// 加载资源 /webjars/** 的资源会被加载
addResourceHandler(registry, "/webjars/**", "classpath:/meta-INF/resources/webjars/");
// staticPathPattern 下的资源
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
registration.addResourceLocations(resource);
}
});
}
这个方法使用了EnableConfigurationProperties注解,说明这个注解自动帮我们导入了一些properties。
@SuppressWarnings("deprecation")
@Configuration(proxyBeanMethods = false)
@import(EnableWebMvcConfiguration.class)
@EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware {
private static final Log logger = LogFactory.getLog(WebMvcConfigurer.class);
private final Resources resourceProperties;
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final ObjectProvider messageConvertersProvider;
private final ObjectProvider dispatcherServletPath;
private final ObjectProvider> servletRegistrations;
private final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
private ServletContext servletContext;
public WebMvcAutoConfigurationAdapter(WebProperties webProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider messageConvertersProvider,
ObjectProvider resourceHandlerRegistrationCustomizerProvider,
ObjectProvider dispatcherServletPath,
ObjectProvider> servletRegistrations) {
this.resourceProperties = webProperties.getResources();
this.mvcProperties = mvcProperties;
this.beanFactory = beanFactory;
this.messageConvertersProvider = messageConvertersProvider;
this.resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider.getIfAvailable();
this.dispatcherServletPath = dispatcherServletPath;
this.servletRegistrations = servletRegistrations;
this.mvcProperties.checkConfiguration();
}
其中@EnableConfigurationProperties帮我们将配置加载成properties,点开WebProperties。
在springboot,我们可以使用一下方式处理静态资源 webjars localhost:8080/webjars/ public , static , /** , resources localhost:8080/ 优先级 : resources > static > public



