servlet编程 reactor响应式编程
springboot内嵌web服务器undertow,完全采用java语言开发,支持阻塞和非阻塞io
对于高并发系统,undertow的性能表现更优
不需要tomcat
简化配置、简化部署(打jar,打war 只需要maven clean package即可)
微服务 去中心化 一个大应用拆成很多小服务 各自单独运行
微服务的困难:远程调用、服务发现、负载均衡、服务容错、配置管理、服务监控、链路追踪、日志管理、任务调度
云原生
上云的困难:服务自愈、弹性伸缩、服务隔离、自动化部署、灰度发布、流量治理
springboot依赖里有很多依赖 名称为XXX starter
这种starter 就能把相关的依赖全部导入
springboot版本仲裁 我们在写依赖时可以不写版本号。但也不是全部,引入非版本仲裁的依赖,必须写版本号
默认主程序所在的包及其所在包的子包都可以被扫到。
各种配置拥有默认值:
默认配置最终都是映射到MultipartProperties
配置文件的值最终会绑定到每个类上,这个类会在容器中创建对象
以前配置需要在bean.xml中手动配置,而有了@Configuration,只需要在类上注明,+@Bean注解,即可实现以前bean.xml和的效果。方法名就是bean的id 当然@Bean(“name”)也可以
如果@Configuration(proxyBeanMethods = true)代表对象调用方法,SpringBoot总会检查这个组件是否在容器中已经有了,如果有就不新创,保证组件单实例。
而proxyBeanMethods为false时,SpringBoot则不会去代理创建对象,而是只要被调用,就直接创建,此时组件就不是单实例了。
false用来解决组件依赖的情形。
@Configuration(proxyBeanMethods = true) 称为自动配置的全模式
@Configuration(proxyBeanMethods = false) 称为自动配置的轻量级模式 不会去检查容器中是否有重复对象,运行也会更快
两种模式的区分主要是取决于组件是否相互依赖
@Bean默认也是单实例的 配置类本身也是组件
外部无论从容器中获取多少次同一个bean,获取到的都是同一个bean
@Import(A.class)同样的作用,导入A组件
@Conditional 条件装配 当满足指定条件时,才进行组件注入
@ConditionalOnBean(name = “fill”) 当容器中有名称为fill的组件时,才注入
@ConditionalOnMissingBean(name = “fill”) 当容器中没有名称为fill的组件时,才注入
@ConfigurationProperties(prefix=“xxx”)
SpringBoot配置文件application.yml或者application.properties中前缀为xxx的配置将会被自动注入给该配置类中名称相同的对象
包含:
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan
内部为@Configuration 代表是一个配置类
指定扫描哪些包
内部包含@AutoConfigurationPackage
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
主要是一个@Import({Registrar.class})
利用Registrar给容器中导入一系列组件
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
spring-boot-autoconfigure-2.3.4.RELEASE.jar/META-INF/spring.factories
此文件写死了SpringBoot启动时要加载的全部配置类
其实就是导入指定包下的所有组件。如果不指定就是Application所在包及其子包下的所有组件。
虽然127个场景的所有自动配置启动的时候默认全部加载,但最终其实并不是全部加载,而是按需配置。
如何实现的呢?得益于@ConditionalOnClass(A.class)
当可以检测到类A时,才装配。
比如我没有类A,(pom文件没有引入),那么此时标着该注解的组件,其实是不会加载的。
注解源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface PathVariable {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
}
使用举例:
当从请求头中取单个元素时,在注解上写明元素名称,如果不写,则使用Map默认接受全部请求头信息。
注解源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestHeader {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean binding() default true;
}
使用: 入参前面注释做好映射名即可
比如:@RequestParam(“id”)
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParam {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
矩阵入参
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MatrixVariable {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
String pathVar() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
boolean required() default true;
String defaultValue()
default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
直接加载入参前即可
注:加在包装类前,且请求的json串的变量名与包装类的变量名一一对应
才能传参成功
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestBody {
boolean required() default true;
}
使用举例:
当从web页面发送请求时,才有cookie
cookie是浏览器缓存,一般是存在session里的。
源码:
@Target({ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CookieValue {
@AliasFor("name")
String value() default "";
@AliasFor("value")
String name() default "";
boolean required() default true;
String defaultValue() default "\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n";
}
@Bean
@ConditionalOnBean({MultipartResolver.class})
@ConditionalOnMissingBean(
name = {"multipartResolver"}
)
public MultipartResolver multipartResolver(MultipartResolver resolver) {
return resolver;
}
当容器中有MultipartResolver这个类型的类,但是却没有multipartResolver这个名称的组件时,触发。
意思其实就是,用户注入的MultipartResolver,名称不是multipartResolver,即命名没有按照SpringBoot默认的组件命名的规范。但此时容器仍然能识别,仍然能把正确的组件返回。(会返回类型为MultipartResolver的组件)
SpringBoot自动配置加载顺序:
xxxxAutoConfiguration —> 组件 -----> xxxProperties里面拿值 -------> application.properties
默认的静态资源路径是在:resources下的static包下
在此类中
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration.WebMvcAutoConfigurationAdapter#addResourceHandlers
配置静态资源的访问规则
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
} else {
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
if (!registry.hasMappingForPattern("/webjars/**")) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{"/webjars/**"}).addResourceLocations(new String[]{"classpath:/META-INF/resources/webjars/"}).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
this.customizeResourceHandlerRegistration(registry.addResourceHandler(new String[]{staticPathPattern}).addResourceLocations(WebMvcAutoConfiguration.getResourceLocations(this.resourceProperties.getStaticLocations())).setCachePeriod(this.getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
}
自动配置有开关控制:resourceProperties.isAddMappings()
在ResourceProperties配置类中可以看到,这个值是读取的spring.resources.addMappings的值
所以选择是否开启,只需要在application.yml中配置spring.resources.addMappings的布尔值即可
所有请求都会请求到DispatcherServlet ,这是和SpringMVC开发一脉相承的
下图是DispatcherServlet 的继承树
调用时序图:
可以看到最终是调到doDispatch()这个方法
org.springframework.web.servlet.DispatcherServlet#doDispatch
源码:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
try {
ModelAndView mv = null;
Object dispatchException = null;
try {
processedRequest = this.checkMultipart(request);
multipartRequestParsed = processedRequest != request;
mappedHandler = this.getHandler(processedRequest);
if (mappedHandler == null) {
this.noHandlerFound(processedRequest, response);
return;
}
HandlerAdapter ha = this.getHandlerAdapter(mappedHandler.getHandler());
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if ((new ServletWebRequest(request, response)).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
this.applyDefaultViewName(processedRequest, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception var20) {
dispatchException = var20;
} catch (Throwable var21) {
dispatchException = new NestedServletException("Handler dispatch failed", var21);
}
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
} catch (Exception var22) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, var22);
} catch (Throwable var23) {
this.triggerAfterCompletion(processedRequest, response, mappedHandler, new NestedServletException("Handler processing failed", var23));
}
} finally {
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else if (multipartRequestParsed) {
this.cleanupMultipart(processedRequest);
}
}
}
那么到底/xxx请求进来,doDispatch是怎么知道把请求转发给哪个方法处理的呢?
SpringBoot能够对注解中的参数进行解析,是得益于底层的五个handlerMapping:
RequestMappingHandlerMapping,底层保存了所有@RequestMapping和handler的映射规则。
以下入参可以直接写到controller的入参列表 无需单独传参
WebRequest、ServletRequest、MultipartRequest、HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
比如常用的HttpSession 的源码:
import java.util.Enumeration;
import javax.servlet.ServletContext;
public interface HttpSession {
long getCreationTime();
String getId();
long getLastAccessedTime();
ServletContext getServletContext();
void setMaxInactiveInterval(int var1);
int getMaxInactiveInterval();
/** @deprecated */
@Deprecated
HttpSessionContext getSessionContext();
Object getAttribute(String var1);
/** @deprecated */
@Deprecated
Object getValue(String var1);
Enumeration<String> getAttributeNames();
/** @deprecated */
@Deprecated
String[] getValueNames();
void setAttribute(String var1, Object var2);
/** @deprecated */
@Deprecated
void putValue(String var1, Object var2);
void removeAttribute(String var1);
/** @deprecated */
@Deprecated
void removeValue(String var1);
void invalidate();
boolean isNew();
}
Map、Errors/BindingResult、Model、RedirectAttributes、ServletResponse、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
可以使用@RequestBody自动类型抓换与格式化,可以级联封装