第十章:SpringBoot
第1节:概述
1.1 微服务 SpringCloud
要搭建一个微服务,运维和部署都变得非常复杂,spring提供了一套解决方案:
springBoot: 快速构建单个服务;
springcloud: 是一系列有序框架的集合,其主要的设施有,服务发现与注册,配置中心,消息总线,负载均衡,断路器,数据监控等,通过Spring Boot 的方式,可以实现一键启动,和部署。
Spring cloud data flow: 为基于微服务的分布式流处理和批处理数据通道提供了一系列模型和最佳实践.
1.2 简介
官方网站:
https: / / spring. io/ projects/ spring- boot
Spring - Boot 是由Pivotal 团队提供的全新框架,其设计目的是用来简化新Spring 应用的初始搭建以及开发过程。个人理解来说Spring - Boot 其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,Spring - Boot 整合了其他相关联框架。
总结: springboot 全新框架,底层是spring springmvc, 并且提供了很多关联框架。 简化spring项目的开发。
1.3 优势
1 、快速构建项目。
2 、对主流开发框架的无配置集成。 内部集成了优秀的框架
3 、项目可独立运行,无须外部依赖Servlet 容器。内置了 tomcat jetty
4 、提供运行时的应用监控。
5 、极大的提高了开发、部署效率。
6 、与云计算的天然集成。
云原生~
1.4 核心功能
1 、独立运行Spring 项目
Spring Boot 可以以jar包形式独立运行,运行一个Spring Boot 项目只需要通过java - jar xx. jar来运行。
2 、内嵌servlet容器
Spring Boot 可以选择内嵌Tomcat 、jetty或者Undertow , 这样我们无须以war包形式部署项目。
3 、提供starter简化Maven 配置[ 重点]
spring提供了一系列的start pom来简化Maven 的依赖加载,例如,当你使用了spring- boot- starter- web,会自动加入如图5 - 1 所示的依赖包。
4 、自动装配Spring 【重点】
Spring Boot 会根据在类路径中的jar包,类、为jar包里面的类自动配置Bean ,这样会极大地减少我们要使用的配置。当然,Spring Boot 只考虑大多数的开发场景,并不是所有的场景,若在实际开发中我们需要配置Bean ,而Spring Boot 灭有提供支持,则可以自定义自动配置。
5 、准生产的应用监控
Spring Boot 提供基于http ssh telnet对运行时的项目进行监控。
6 、无代码生产和xml配置
Spring Boot 不是借助与代码生成来实现的,而是通过条件注解来实现的,这是Spring4 . x提供的新特性。
第2节:入门程序
2.1 创建工程导入依赖
< parent>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-parent</ artifactId>
< version> 2.4.3</ version>
</ parent>
< dependencies>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
</ dependencies>
Spring Boot 将所有的功能场景都抽取出来,做成一个个的starters(启动器),只需要在项目里面引入这些starter 相关场景的所有依赖都会导入进来。要用什么功能就导入什么场景的启动器 。
2.2 编写主程序
2.2.1 项目结构
2.2.2 主程序代码
@SpringBootApplication
public class Example {
public static void main ( String [ ] args) {
SpringApplication . run ( Example . class , args) ;
}
}
@SpringBootApplication : 注解说明这个类是SpringBoot 的主配置类,SpringBoot 就应该运行这个类的main方法来启动SpringBoot 应用;并将主配置类(@SpringBootApplication 标注的类)的所在包及下面所有子包里面的所有组件扫描到Spring 容器;
2.3 编写Controller代码
@Controller
public class TestController {
@RequestMapping ( "/hello" )
@ResponseBody
public String helloWorld ( ) {
return "Hello world!" ;
}
}
2.4 运行主程序进行测试
第3节:简化部署
3.1 添加maven插件
< build>
< plugins>
< plugin>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-maven-plugin</ artifactId>
</ plugin>
</ plugins>
</ build>
3.2 执行项目打包
执行打包命令后,会在target目录下生成项目对应的jar包。
3.3 执行命令运行程序
第4节:快速创建项目
4.1 创建Spring Boot项目
4.2 项目结构解析
1 、java文件夹目录结构中自动创建好指定包和Spring Boot 启动主程序SpringbootApplication . class ;
2 、resources文件夹中目录结构
static :保存所有的静态资源; js css images;
templates:保存所有的模板页面,(Spring Boot 默认jar包使用嵌入式的Tomcat ,默认不支持JSP 页面),可以使用模板引擎(freemarker、thymeleaf);
application. properties:Spring Boot 应用的配置文件;可以修改一些默认设置;
该配置文件能够被自动读取。
第5节:配置文件
5.1 配置文件的作用及规范
Spring Boot 使用一个全局的配置文件,配置文件名是固定的;默认使用以下两种格式:
•application. properties
•application. yml
配置文件的作用:修改SpringBoot 自动配置的默认值;Spring Boot 启动时会根据配置文件自动注册相关的应用组件;
5.2 yaml配置文件
YAML :以数据为中心,比json、xml等更适合做配置文件;
5.2.1 基本语法
– 使用缩进表示层级关系
– 缩进的空格数目不重要,只要相同层级的元素左侧对齐即可 – 大小写敏感
- 键值对中间必须要有空格k:(空格)v
5.2.2 值的写法
YAML 支持的三种数据结构:
1. 字面量:普通的值(数字,字符串,布尔)
server :
port : 8081
注意:字符串默认不用加上单引号或者双引号;
双引号:特殊符号表示转义符本身;
name : "zhangsan \n lisi":输出;zhangsan 换行 lisi
单引号;特殊字符就表示字符本身;
name : ‘zhangsan \n lisi’:输出;zhangsan \n lisi
2. 对象、Map(属性和值)(键值对)
person :
name : zhangsan
age : 12
另一种行内写法:
person : { name : zhangsan, age : 12 }
3. 数组(List,Set)
hobbies :
- singing
- dancing
- running
用- (空格)值表示数组中的一个元素
另一种行内写法:
hobbies : [ singing, dancing, running]
yaml参考文档
5.2.3 配置文件值的注入
1. 构建bean对象
@Component
@ConfigurationProperties ( prefix = "person" )
public class Person {
private String pname;
private int age;
private boolean success;
private Date birth;
private Car car;
private Map < String , Object > maps;
private List < Object > lists;
}
总结: @ConfigurationProperties ( prefix = "person" ) + @Component 结合使用
或是
@ConfigurationProperties ( prefix = "person" ) + @EnableConfigurationProperties ( Person . class ) 注解共同使用
注:@EnableConfigurationProperties ( Person . class ) 加在启动类或测试类上
@ConfigurationProperties ( prefix = "person" ) 加在要获取数据的类上
2. 构建配置文件
person :
pname : zhangsan
age : 12
birth : 2020/12/12
success : true
car :
cname : 奔驰
cprice : 200.0
lists : [ 唱歌, 跳舞]
maps : { key1 : v1, key2 : v2}
3. 执行单元测试
@SpringBootTest
class SpringbootApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads ( ) {
System . out. println ( person) ;
}
}
4. 引入配置文件处理器插件
< dependency>
< groupId> org.springframework.boot </ groupId>
< artifactId> spring-boot-configuration-processor </ artifactId>
< optional> true </ optional>
</ dependency>
总结:
(1)解决实体当中报错问题
(2)yml配置文件当中, 可以根据前缀提示~~
5.3 properties配置文件
5.3.1 properties语法
以KEY=VALue键值对的方式设置值
1. 字面量:普通的值(数字,字符串,布尔)
name=张三
2. 对象、Map(属性和值)(键值对)
person.name=张三
person.age=12
maps.key1=value1
maps.key2=value2
3. 数组(List,Set)
hobbies=singing,dancing,running
5.3.2 配置文件值的注入
1. 构建bean对象
@Component
@ConfigurationProperties ( prefix = "person" )
public class Person {
private String pname;
private int age;
private boolean success;
private Date birth;
private Car car;
private Map < String , Object > maps;
private List < Object > lists;
}
2. 构建配置文件
person.pname=张三
person.age=12
person.birth=2019/12/12
person.success=true
person.car.cname=宝马
person.car.cprice=100.0
person.lists=唱歌,跳舞,跑步
person.maps.k1=value1
person.maps.k2=value2
3. 执行单元测试
@SpringBootTest
class SpringbootApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads ( ) {
System . out. println ( person) ;
}
}
5.4 说明
5.4.1 注解区别
@ConfigurationProperties 注解和@Value 注解的区别
1 、@ConfigurationProperties 注解用于根据配置文件批量注入值,springboot默认配置文件application. yml/ application. properties;
2 、@Value 注解用于根据配置文件单个注入值;
区别 @ConfigurationProperties @Value SpEL 不支持 支持:@Value(“${jdbc.url}”) 复杂类型封装 支持 不支持
案例:
@Component
public class Person {
@Value ( "${person.pname}" )
private String pname;
@Value ( "#{12*2}" )
private int age;
private boolean success;
private Date birth;
private Car car;
private Map < String , Object > map;
private List < Object > hobbies;
}
5.4.2 配置文件中的占位符
1、springboot全局配置文件中可以使用随机数
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}
2、springboot全局配置文件中可以使用占位符
通过${属性名}获取已经在配置文件中加载过的属性值;
通过${属性名:默认值}来指定找不到属性时的默认值;
案例:
person.pname=张三
person.age=${random.int}
person.success=true
person.birth=2012/12/12
person.car.cname=${car.cname:奔驰}
person.car.cprice=200.0
person.map.key1=value1
person.map.key2=value2
person.hobbies=唱歌,跳舞,跑步
5.4.3 多环境支持
Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境;
1、多文件多环境形式:
格式:application- { profile} .properties/yml
例如:可以在项目中创建如下主配置文件:application- dev.properties、application- test.properties、application- prod.properties、application.properties, 默认使用application.properties, 可以通过配置spring.profiles.active=profile指定使用某个环境的配置文件。
2、yaml支持单文件多环境形式:
spring :
profiles :
active : test
---
spring :
profiles : dev
server :
port : 8081
spring :
config :
activate :
on-profile : dev
server :
port : 8081
---
spring :
profiles : test
server :
port : 8082
通过--- 来区分不同的profile环境。
3、激活方式:
在配置文件中指定 spring.profiles.active=dev
命令行 java - jar springboot- 0.0.1- SNAPSHOT.jar - - spring.profiles.active=dev
jvm参数 - Dspring.profiles.active=dev
5.4.4 配置文件加载位置
springboot默认会从以下位置加载配置文件:application. properties/ application. yml
项目所在磁盘路径file: . /config/
项目所在磁盘路径file: . /
项目类路径classpath: / config/
项目类路径classpath: /
优先级由高到底,高优先级的配置会覆盖低优先级的配置;SpringBoot 会从这四个位置全部加载主配置文件,互补配置;
如果要加载其他位置的配置文件,可以通过-- spring. config. location(只能加到命令行)来指定要加载的配置文件的位置。
第6节:Spring Boot的web开发
6.1 静态资源的处理
springboot启动时会根据配置文件自动配置相应场景的组件xxxAutoConfiguration,web项目启动时会初始化WebMvcAutoConfiguration 组件处理请求相关的操作,其中有默认处理静态资源的方法:
@Override
protected void addResourceHandlers ( ResourceHandlerRegistry registry) {
super . addResourceHandlers ( registry) ;
if ( ! this . resourceProperties. isAddMappings ( ) ) {
logger. debug ( "Default resource handling disabled" ) ;
return ;
}
ServletContext servletContext = getServletContext ( ) ;
addResourceHandler ( registry, "/webjars/**" , "classpath:/META-INF/resources/webjars/" ) ;
addResourceHandler ( registry, this . mvcProperties. getStaticPathPattern ( ) , ( registration) -> {
registration. addResourceLocations ( this . resourceProperties. getStaticLocations ( ) ) ;
if ( servletContext != null ) {
registration. addResourceLocations ( new ServletContextResource ( servletContext, SERVLET_LOCATION ) ) ;
}
} ) ;
}
从该方法中可以发现:
1 、默认情况下:
(1 )匹配/ webjars
6.1.1 webjars的使用
我们可以通过webjars以jar包的方式引入静态资源。
js: js库文件下载, copy当中的工程: js vue elementUI:
静态资源坐标参考
1. 引入依赖
< dependency>
< groupId> org.webjars</ groupId>
< artifactId> jquery</ artifactId>
< version> 3.6.0</ version>
</ dependency>
2. 启动服务访问资源
浏览器访问路径:http: / / localhost: 8080 / webjars/ jquery/ 3.6 .0 / jquery. min. js获取相应的静态资源。
6.1.2 静态资源的存放位置
1. 默认存放位置查看
默认静态资源路径:
"classpath:/META‐INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
"/" :当前项目的根路径
案例:在上述任意路径下创建静态资源hello. html, 浏览器访问路径:http: / / localhost: 8080 / hello. html获取相应的静态资源。
2. 自定义位置存放
在配置文件中通过属性spring. web. resources. static - locations= 自定义路径,设置自定义静态资源存放路径;
案例:
a、配置文件中设置:spring. web. resources. static - locations= classpath: / hello/
yml文件:
spring:
web:
resources:
static - locations: [ classpath: / hello/ , classpath: / static / ]
b、在自定义路径下添加静态资源hello. html, 浏览器访问路径:http: / / localhost: 8080 / hello. html获取相应的静态资源。
注意:自定义静态资源路径后,默认静态资源路径失效!
6.2 springboot的自动配置
6.2.1 自动配置原理
1. springboot启动时加载主配值类,解析注解@SpringBootApplication
@SpringBootApplication
public class SpringbootApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( SpringbootApplication . class , args) ;
}
}
2. 在@SpringBootApplication组合注解中解析@EnableAutoConfiguration获取自动配置信息
@Target ( ElementType . TYPE )
@Retention ( RetentionPolicy . RUNTIME )
@Documented
@Inherited
@AutoConfigurationPackage
@Import ( AutoConfigurationImportSelector . class )
public @interface EnableAutoConfiguration {
. . .
}
@Override
public String [ ] selectImports ( AnnotationMetadata annotationMetadata) {
if ( ! isEnabled ( annotationMetadata) ) {
return NO_IMPORTS ;
}
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry ( annotationMetadata) ;
return StringUtils . toStringArray ( autoConfigurationEntry. getConfigurations ( ) ) ;
}
protected AutoConfigurationEntry getAutoConfigurationEntry ( AnnotationMetadata annotationMetadata) {
if ( ! isEnabled ( annotationMetadata) ) {
return EMPTY_ENTRY ;
}
AnnotationAttributes attributes = getAttributes ( annotationMetadata) ;
List < String > configurations = getCandidateConfigurations ( annotationMetadata, attributes) ;
configurations = removeDuplicates ( configurations) ;
Set < String > exclusions = getExclusions ( annotationMetadata, attributes) ;
checkExcludedClasses ( configurations, exclusions) ;
configurations. removeAll ( exclusions) ;
configurations = getConfigurationClassFilter ( ) . filter ( configurations) ;
fireAutoConfigurationImportEvents ( configurations, exclusions) ;
return new AutoConfigurationEntry ( configurations, exclusions) ;
}
protected List < String > getCandidateConfigurations ( AnnotationMetadata metadata, AnnotationAttributes attributes) {
List < String > configurations = SpringFactoriesLoader . loadFactoryNames ( getSpringFactoriesLoaderFactoryClass ( ) ,
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;
}
public static List < String > loadFactoryNames ( Class < ? > factoryType, @Nullable ClassLoader classLoader) {
ClassLoader classLoaderToUse = classLoader;
if ( classLoaderToUse == null ) {
classLoaderToUse = SpringFactoriesLoader . class . getClassLoader ( ) ;
}
String factoryTypeName = factoryType. getName ( ) ;
return loadSpringFactories ( classLoaderToUse) . getOrDefault ( factoryTypeName, Collections . emptyList ( ) ) ;
}
private static Map < String , List < String > > loadSpringFactories ( ClassLoader classLoader) {
Map < String , List < String > > result = cache. get ( classLoader) ;
if ( result != null ) {
return result;
}
result = new HashMap < > ( ) ;
try {
Enumeration < URL> urls = classLoader. getResources ( FACTORIES_RESOURCE_LOCATION ) ;
while ( urls. hasMoreElements ( ) ) {
URL url = urls. nextElement ( ) ;
UrlResource resource = new UrlResource ( url) ;
Properties properties = PropertiesLoaderUtils . loadProperties ( resource) ;
for ( Map. Entry < ? , ? > entry : properties. entrySet ( ) ) {
String factoryTypeName = ( ( String ) entry. getKey ( ) ) . trim ( ) ;
String [ ] factoryImplementationNames =
StringUtils . commaDelimitedListToStringArray ( ( String ) entry. getValue ( ) ) ;
for ( String factoryImplementationName : factoryImplementationNames) {
result. computeIfAbsent ( factoryTypeName, key -> new ArrayList < > ( ) )
. add ( factoryImplementationName. trim ( ) ) ;
}
}
}
result. replaceAll ( ( factoryType, implementations) -> implementations. stream ( ) . distinct ( )
. collect ( Collectors . collectingAndThen ( Collectors . toList ( ) , Collections :: unmodifiableList ) ) ) ;
cache. put ( classLoader, result) ;
}
catch ( IOException ex) {
throw new IllegalArgumentException ( "Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]" , ex) ;
}
return result;
}
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;
获取到配置文件中的自动配置类xxxAutoConfiguration的信息,并将自动配置类中的组件初始化到容器中。
初始化的过程中会加载xxxProperties对象,根据xxxProperties对象中的属性值初始化组件, xxxProperties对象的值可以通过全局配置文件指定。
3. 初始化自动配置组件
以Spring Boot 自动配置SpringMVC 为例,Spring Boot 启动时会做以下操作:
@Configuration ( proxyBeanMethods = false )
@ConditionalOnWebApplication ( type = Type . SERVLET )
@ConditionalOnClass ( { Servlet . class , DispatcherServlet . class , WebMvcConfigurer . class } )
@ConditionalOnMissingBean ( WebMvcConfigurationSupport . class )
@AutoConfigureOrder ( Ordered . HIGHEST_PRECEDENCE + 10 )
@AutoConfigureAfter ( { DispatcherServletAutoConfiguration . class , TaskExecutionAutoConfiguration . class ,
ValidationAutoConfiguration . class } )
public class WebMvcAutoConfiguration {
@Bean
@ConditionalOnBean ( ViewResolver . class )
@ConditionalOnMissingBean ( name = "viewResolver" , value = ContentNegotiatingViewResolver . class )
public ContentNegotiatingViewResolver viewResolver ( BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver ( ) ;
resolver. setContentNegotiationManager ( beanFactory. getBean ( ContentNegotiationManager . class ) ) ;
resolver. setOrder ( Ordered . HIGHEST_PRECEDENCE ) ;
return resolver;
}
@Bean
@ConditionalOnBean ( View . class )
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver ( ) {
BeanNameViewResolver resolver = new BeanNameViewResolver ( ) ;
resolver. setOrder ( Ordered . LOWEST_PRECEDENCE - 10 ) ;
return resolver;
}
@Override
protected void addResourceHandlers ( ResourceHandlerRegistry registry) {
super . addResourceHandlers ( registry) ;
if ( ! this . resourceProperties. isAddMappings ( ) ) {
logger. debug ( "Default resource handling disabled" ) ;
return ;
}
ServletContext servletContext = getServletContext ( ) ;
addResourceHandler ( registry, "/webjars/**" , "classpath:/META-INF/resources/webjars/" ) ;
addResourceHandler ( registry, this . mvcProperties. getStaticPathPattern ( ) , ( registration) -> {
registration. addResourceLocations ( this . resourceProperties. getStaticLocations ( ) ) ;
if ( servletContext != null ) {
registration. addResourceLocations ( new ServletContextResource ( servletContext, SERVLET_LOCATION ) ) ;
}
} ) ;
}
-- -- -- -- -- -- -- -- - 内部类-- -- -- -- -- -- -- -- -- --
@Configuration ( proxyBeanMethods = false )
@Import ( EnableWebMvcConfiguration . class )
@EnableConfigurationProperties ( { WebMvcProperties . class ,
org. springframework. boot. autoconfigure. web. ResourceProperties. class , WebProperties . class } )
@Order ( 0 )
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer {
private final WebMvcProperties mvcProperties;
private final ListableBeanFactory beanFactory;
private final ObjectProvider < HttpMessageConverters > messageConvertersProvider;
private final ObjectProvider < DispatcherServletPath > dispatcherServletPath;
private final ObjectProvider < ServletRegistrationBean < ? > > servletRegistrations;
final ResourceHandlerRegistrationCustomizer resourceHandlerRegistrationCustomizer;
public WebMvcAutoConfigurationAdapter ( WebProperties webProperties, WebMvcProperties mvcProperties,
ListableBeanFactory beanFactory, ObjectProvider < HttpMessageConverters > messageConvertersProvider,
ObjectProvider < ResourceHandlerRegistrationCustomizer > resourceHandlerRegistrationCustomizerProvider,
ObjectProvider < DispatcherServletPath > dispatcherServletPath,
ObjectProvider < ServletRegistrationBean < ? > > servletRegistrations) {
this . mvcProperties = mvcProperties;
this . beanFactory = beanFactory;
this . messageConvertersProvider = messageConvertersProvider;
this . resourceHandlerRegistrationCustomizer = resourceHandlerRegistrationCustomizerProvider. getIfAvailable ( ) ;
this . dispatcherServletPath = dispatcherServletPath;
this . servletRegistrations = servletRegistrations;
this . mvcProperties. checkConfiguration ( ) ;
}
@Override
public void configureMessageConverters ( List < HttpMessageConverter < ? > > converters) {
this . messageConvertersProvider
. ifAvailable ( ( customConverters) -> converters. addAll ( customConverters. getConverters ( ) ) ) ;
}
@Override
public void configureAsyncSupport ( AsyncSupportConfigurer configurer) {
if ( this . beanFactory. containsBean ( TaskExecutionAutoConfiguration . APPLICATION_TASK_EXECUTOR_BEAN_NAME ) ) {
Object taskExecutor = this . beanFactory
. getBean ( TaskExecutionAutoConfiguration . APPLICATION_TASK_EXECUTOR_BEAN_NAME ) ;
if ( taskExecutor instanceof AsyncTaskExecutor ) {
configurer. setTaskExecutor ( ( ( AsyncTaskExecutor ) taskExecutor) ) ;
}
}
Duration timeout = this . mvcProperties. getAsync ( ) . getRequestTimeout ( ) ;
if ( timeout != null ) {
configurer. setDefaultTimeout ( timeout. toMillis ( ) ) ;
}
}
@Override
public void configurePathMatch ( PathMatchConfigurer configurer) {
if ( this . mvcProperties. getPathmatch ( )
. getMatchingStrategy ( ) == WebMvcProperties. MatchingStrategy . PATH_PATTERN_PARSER ) {
configurer. setPatternParser ( new PathPatternParser ( ) ) ;
}
configurer. setUseSuffixPatternMatch ( this . mvcProperties. getPathmatch ( ) . isUseSuffixPattern ( ) ) ;
configurer. setUseRegisteredSuffixPatternMatch (
this . mvcProperties. getPathmatch ( ) . isUseRegisteredSuffixPattern ( ) ) ;
this . dispatcherServletPath. ifAvailable ( ( dispatcherPath) -> {
String servletUrlMapping = dispatcherPath. getServletUrlMapping ( ) ;
if ( servletUrlMapping. equals ( "/" ) && singleDispatcherServlet ( ) ) {
UrlPathHelper urlPathHelper = new UrlPathHelper ( ) ;
urlPathHelper. setAlwaysUseFullPath ( true ) ;
configurer. setUrlPathHelper ( urlPathHelper) ;
}
} ) ;
}
private boolean singleDispatcherServlet ( ) {
return this . servletRegistrations. stream ( ) . map ( ServletRegistrationBean :: getServlet )
. filter ( DispatcherServlet . class :: isInstance ) . count ( ) == 1 ;
}
@Override
public void configureContentNegotiation ( ContentNegotiationConfigurer configurer) {
WebMvcProperties. Contentnegotiation contentnegotiation = this . mvcProperties. getContentnegotiation ( ) ;
configurer. favorPathExtension ( contentnegotiation. isFavorPathExtension ( ) ) ;
configurer. favorParameter ( contentnegotiation. isFavorParameter ( ) ) ;
if ( contentnegotiation. getParameterName ( ) != null ) {
configurer. parameterName ( contentnegotiation. getParameterName ( ) ) ;
}
Map < String , MediaType > mediaTypes = this . mvcProperties. getContentnegotiation ( ) . getMediaTypes ( ) ;
mediaTypes. forEach ( configurer:: mediaType ) ;
}
@Bean
@ConditionalOnMissingBean
public InternalResourceViewResolver defaultViewResolver ( ) {
InternalResourceViewResolver resolver = new InternalResourceViewResolver ( ) ;
resolver. setPrefix ( this . mvcProperties. getView ( ) . getPrefix ( ) ) ;
resolver. setSuffix ( this . mvcProperties. getView ( ) . getSuffix ( ) ) ;
return resolver;
}
@Bean
@ConditionalOnBean ( View . class )
@ConditionalOnMissingBean
public BeanNameViewResolver beanNameViewResolver ( ) {
BeanNameViewResolver resolver = new BeanNameViewResolver ( ) ;
resolver. setOrder ( Ordered . LOWEST_PRECEDENCE - 10 ) ;
return resolver;
}
@Bean
@ConditionalOnBean ( ViewResolver . class )
@ConditionalOnMissingBean ( name = "viewResolver" , value = ContentNegotiatingViewResolver . class )
public ContentNegotiatingViewResolver viewResolver ( BeanFactory beanFactory) {
ContentNegotiatingViewResolver resolver = new ContentNegotiatingViewResolver ( ) ;
resolver. setContentNegotiationManager ( beanFactory. getBean ( ContentNegotiationManager . class ) ) ;
resolver. setOrder ( Ordered . HIGHEST_PRECEDENCE ) ;
return resolver;
}
@Override
public MessageCodesResolver getMessageCodesResolver ( ) {
if ( this . mvcProperties. getMessageCodesResolverFormat ( ) != null ) {
DefaultMessageCodesResolver resolver = new DefaultMessageCodesResolver ( ) ;
resolver. setMessageCodeFormatter ( this . mvcProperties. getMessageCodesResolverFormat ( ) ) ;
return resolver;
}
return null ;
}
@Override
public void addFormatters ( FormatterRegistry registry) {
ApplicationConversionService . addBeans ( registry, this . beanFactory) ;
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
@Bean
@Override
public FormattingConversionService mvcConversionService ( ) {
Format format = this . mvcProperties. getFormat ( ) ;
WebConversionService conversionService = new WebConversionService ( new DateTimeFormatters ( )
. dateFormat ( format. getDate ( ) ) . timeFormat ( format. getTime ( ) ) . dateTimeFormat ( format. getDateTime ( ) ) ) ;
addFormatters ( conversionService) ;
return conversionService;
}
private Resource getWelcomePage ( ) {
for ( String location : this . resourceProperties. getStaticLocations ( ) ) {
Resource indexHtml = getIndexHtml ( location) ;
if ( indexHtml != null ) {
return indexHtml;
}
}
ServletContext servletContext = getServletContext ( ) ;
if ( servletContext != null ) {
return getIndexHtml ( new ServletContextResource ( servletContext, SERVLET_LOCATION ) ) ;
}
return null ;
}
-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -
@Bean
@ConditionalOnMissingBean ( { RequestContextListener . class , RequestContextFilter . class } )
@ConditionalOnMissingFilterBean ( RequestContextFilter . class )
public static RequestContextFilter requestContextFilter ( ) {
return new OrderedRequestContextFilter ( ) ;
}
}
}
a、自动配置视图解析器ViewResolver (ContentNegotiatingViewResolver 和 BeanNameViewResolver ), 作用:根据方法返回值得到视图对象,由视图对象决定请求转发或者重定向到指定视图。(注意:此处会创建获取容器中的所有视图解析器,包括自定义的视图解析器)
b、提供静态资源访问路径和webjars访问路径;
c、自动注册Converter 类型转换器, Formatter 格式化器;
d、提供HttpMessageConverters ,用于Http 请求和响应之间的转换,将json或者xml格式字符串和java类型之间做转换;
(注意:如果要自定义类型转换器,也可以通过该接口注册)
@Configuration ( proxyBeanMethods = false )
public class MyConfiguration {
@Bean
public HttpMessageConverters customConverters ( ) {
HttpMessageConverter < ? > additional = . . .
HttpMessageConverter < ? > another = . . .
return new HttpMessageConverters ( additional, another) ;
}
}
内容了解~
e、自动注册消息代码提示处理器MessageCodesResolver
f、支持静态首页index. html访问
g、自动注册web数据参数绑定对象ConfigurableWebBindingInitializer ,如果我们自己注册了该类型对象,那springboot会自动调用我们注册的对象,替换默认对象将请求参数绑定到javabean(了解)。
h、如果要自定义拦截器interceptors,格式化器formatters,视图控制器view controllers等,可以通过创建类型为WebMvcConfigurer 的配置类来实现,不要加注解@EnableWebMvc 。
i、如果要自定义处理器映射器RequestMappingHandlerMapping , 处理器适配器RequestMappingHandlerAdapter , 或者异常处理器ExceptionHandlerExceptionResolver ,可以在容器中创建类型为WebMvcRegistrations 的组件对象来实现。
j、如果想要完全使用自定义配置,不使用springboot默认配置,可以在配置类上加注解@EnableWebMvc 。(不推荐)
参考官方文档:https: / / docs. spring. io/ spring- boot/ docs/ 2.4 .3 / reference/ html/ spring- boot- features. html#boot- features- spring- mvc
4. 小结
a、SpringBoot启动会加载大量的自动配置类
b、根据配置文件和xxxProperties的默认设置初始化自动配置类中的组件
c、可以在自动配置类中查看我们需要的功能是否已经自动配置,如果配置,我们就不用再做配置了;
6.2.2 自定义配置实现访问拦截
1. 自定义拦截器
public class MyIntercepter implements HandlerInterceptor {
@Override
public boolean preHandle ( HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
HttpSession session = request. getSession ( ) ;
String username= ( String ) session. getAttribute ( "username" ) ;
if ( username!= null ) {
System . out. println ( "登录成功不拦截" ) ;
return true ;
} else {
request. getRequestDispatcher ( "/index.html" ) . forward ( request, response) ;
System . out. println ( "未登录拦截" ) ;
return false ;
}
}
@Override
public void postHandle ( HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion ( HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
2. 注册拦截器
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors ( InterceptorRegistry registry) {
registry. addInterceptor ( new MyIntercepter ( ) ) . addPathPatterns ( "/**" ) . excludePathPatterns ( "/" , "/index.html" , "/user/login" ) ;
}
}
3. 自定义配置视图控制器
< dependency>
< groupId> org. springframework. boot< / groupId>
< artifactId> spring- boot- starter- thymeleaf< / artifactId>
< / dependency>
@Configuration
public class MyConfig implements WebMvcConfigurer {
@Override
public void addInterceptors ( InterceptorRegistry registry) {
registry. addInterceptor ( new MyIntercepter ( ) ) . addPathPatterns ( "/**" ) . excludePathPatterns ( "/" , "/index.html" , "/user/login" ) ;
}
@Override
public void addViewControllers ( ViewControllerRegistry registry) {
registry. addViewController ( "/main" ) . setViewName ( "main" ) ;
}
}
第7节:thymeleaf
7.1 模板引擎概述
thymeleaf是一款用于渲染XML / XHTML / HTML5 内容的模板引擎。类似JSP , Velocity ,FreeMaker 等,它也可以轻易的与Spring MVC 等Web 框架进行集成作为Web 应用的模板引擎。与其它模板引擎相比,Thymeleaf 最大的特点是能够
直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web 应用
Spring Boot 推荐使用Thymeleaf 、Freemarker 等后现代的模板引擎技术;一但导入相
关依赖,会自动配置ThymeleafAutoConfiguration 、FreeMarkerAutoConfiguration 。
模板引擎工作原理图:
7.2 入门案例
7.2.1 引入依赖
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-thymeleaf</ artifactId>
< version> 2.4.3</ version>
</ dependency>
7.2.2 添加配置
spring :
thymeleaf :
cache : false
7.2.3 编写Controller文件
package com. offcn. demo. controller ;
@Controller
public class FirstThymeleafController {
@GetMapping ( "/first" )
public String indexPage ( Model model) {
String message = "Hello, Thymeleaf!" ;
model. addAttribute ( "message" , message) ;
return "index" ;
}
}
7.2.4 编写模板文件
在resources/ templates 下新建 index. html
<! doctype html >
< html xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
< h1 th: text= " ${message}" > </ h1>
</ body>
</ html>
通过 类似EL 表达式将 Model 中的数据填充到 h1 标签中
去掉idea中 对thymeleaf模板格式的检查 setting -- editer --- inpection --- thymeleaf 去掉对勾
7.2.5 运行访问
7.3 语法简介
7.4 数据交互
7.4.1 新建一个实体
package com.offcn.demo.bean;
public class User {
private Integer id;
private String name;
private int age;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}
7.4.2 新建Controller
package com. offcn. demo. controller ;
import java. util. HashMap ;
import java. util. Map ;
import org. springframework. stereotype. Controller ;
import org. springframework. ui. Model ;
import org. springframework. web. bind. annotation. GetMapping ;
import com. offcn. demo. bean. User ;
@Controller
public class SecondThymeleafController {
@GetMapping ( "/second" )
public String indexPage ( Model model) {
String message = "Hello, Thymeleaf!" ;
User u = new User ( ) ;
u. setId ( 1 ) ;
u. setName ( "优就业" ) ;
u. setAge ( 18 ) ;
Map < String , Object > map= new HashMap < > ( ) ;
map. put ( "src1" , "1.jpg" ) ;
map. put ( "src2" , "2.jpg" ) ;
model. addAttribute ( "message" , message) ;
model. addAttribute ( "user" , u) ;
model. addAttribute ( "src" , map) ;
return "index2" ;
}
}
7.4.3 新增模板文件
<! doctype html >
< html xmlns = " http://www.w3.org/1999/xhtml"
xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
< h1 th: text= " ${message}" > </ h1>
< img th: src= " ${src.src1}" />
</ br>
< img th: src= " ${src.src2}" />
</ br>
< span th: text= " ${user.id}" > </ span>
< span th: text= " ${user.name}" > </ span>
< span th: text= " ${user.age}" > </ span>
</ body>
</ html>
7.4.4 访问地址
7.5 循环遍历集合
7.5.1 新建一个Controller,
package com. offcn. demo. controller ;
@Controller
public class ThreeThymeleafController {
@GetMapping ( "/three" )
public String indexPage ( Model model) {
List < User > list= new ArrayList < User > ( ) ;
User u1 = new User ( ) ;
u1. setId ( 1 ) ;
u1. setName ( "优就业" ) ;
u1. setAge ( 18 ) ;
list. add ( u1) ;
User u2 = new User ( ) ;
u2. setId ( 2 ) ;
u2. setName ( "中公教育" ) ;
u2. setAge ( 28 ) ;
list. add ( u2) ;
User u3 = new User ( ) ;
u3. setId ( 3 ) ;
u3. setName ( "IT先锋" ) ;
u3. setAge ( 88 ) ;
list. add ( u3) ;
User u4 = new User ( ) ;
u4. setId ( 4 ) ;
u4. setName ( "JAVA第一" ) ;
u4. setAge ( 888 ) ;
list. add ( u4) ;
model. addAttribute ( "userList" , list) ;
return "index3" ;
}
}
7.5.2 新增模板文件
<! doctype html >
< html xmlns = " http://www.w3.org/1999/xhtml"
xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
< table width = " 200" style = " text-align : center; " >
< tr>
< th> 编号</ th>
< th> 姓名</ th>
< th> 年龄</ th>
< th> index</ th>
</ tr>
< tr th: each= " user,iterStat : ${userList}" >
< td th: text= " ${user.id}" > </ td>
< td th: text= " ${user.name}" > </ td>
< td th: text= " ${user.age}" > </ td>
< td th: text= " ${iterStat.index}" > index</ td>
</ tr>
</ table>
</ body>
</ html>
iterStat 称作状态变量,属性有:
index:当前迭代对象的 index(从 0 开始计算)
count:当前迭代对象的 index(从 1 开始计算)
size:被迭代对象的大小
current:当前迭代变量
even/ odd:布尔值,当前循环是否是偶数/ 奇数(从 0 开始计算)
first:布尔值,当前循环是否是第一个
last:布尔值,当前循环是否是最后一个
7.5.3 访问地址
7.6 赋值、字符串拼接
7.6.1 新建一个Controller
package com. offcn. demo. controller ;
@Controller
public class FourThymeleafController {
@GetMapping ( "/four" )
public String indexPage ( Model model) {
model. addAttribute ( "userName" , "优就业" ) ;
model. addAttribute ( "href" , "http://www.ujiuye.com" ) ;
return "index4" ;
}
}
7.6.2 新增模板文件
<! doctype html >
< html xmlns = " http://www.w3.org/1999/xhtml"
xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
< h1 th: text= " ${userName}" > </ h1>
< input type = " text" name = " names" th: value= " ${userName}" />
</ br>
< em th: size= " ${userName}" > </ em>
< span th: text= " ' 欢迎来:'+${userName}+'学习!'" > </ span>
</ br>
< span th: text= " |欢迎来:${userName}学习!|" > </ span>
</ body>
</ html>
7.6.3 访问地址
7.7 条件判断、选择语句
7.7.1 新建一个Controller
package com. offcn. demo. controller ;
@Controller
public class FiveThymeleafController {
@GetMapping ( "/five" )
public String indexPage ( Model model) {
model. addAttribute ( "flag" , "yes" ) ;
model. addAttribute ( "menu" , "admin" ) ;
model. addAttribute ( "manager" , "manager" ) ;
return "index5" ;
}
}
7.7.2 新增模板文件
<! doctype html >
< html xmlns = " http://www.w3.org/1999/xhtml"
xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
< h1 th: if= " ${flag=='yes'}" > 中公教育</ h1>
< h1 th: unless= " ${flag=='no'}" > 优就业</ h1>
< div th: switch= " ${menu}" >
< p th: case= " ' admin'" > User is an administrator</ p>
< p th: case= " ${manager}" > User is a manager</ p>
</ div>
</ body>
</ html>
7.7.3 访问地址
7.8 片段fragment定义使用
thymeleaf也提供了类似import 的东西,可以将很多代码块抽象成模块,然后在需要的时候引用,非常方便。
fragment介绍
fragment类似于JSP 的tag,在html中文件中,可以将多个地方出现的元素块用fragment包起来使用。
fragment使用
定义fragment, 所有的fragment可以写在一个文件里面,也可以单独存在,例如:
7.8.1 新增模板文件 footer.html
< body>
< h1 th: fragment= " copy" >
© 1999-2022 Offcn.All Rights Reserved
</ h1>
</ body>
注意: 在Springboot 中,默认读取thymeleaf文件的路径是:src/ main/ resource/ templates
7.8.2 编写Controller
package com. offcn. demo. controller ;
@Controller
public class SixThymeleafController {
@GetMapping ( "/six" )
public String indexPage ( Model model) {
return "index6" ;
}
}
7.8.3 新增视图文件
<! doctype html >
< html xmlns = " http://www.w3.org/1999/xhtml"
xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
< div style = " color : #82ff6c" th: insert= " ~{footer :: copy}" > </ div>
</ br>
< div style = " color : #82ff6c" th: replace= " ~{footer :: copy}" > </ div>
</ br>
< div style = " color : #82ff6c" th: include= " ~{footer :: copy}" > </ div>
</ body>
</ html>
fragment的引用
th: insert: 保留自己的主标签,保留th: fragment的主标签。
th: replace: 不要自己的主标签,保留th: fragment的主标签。
th: include: 保留自己的主标签,不要th: fragment的主标签。(官方3.0 后不推荐)
7.8.4 访问地址运行
7.9 内置对象使用
7.9.1 常见内置工具对象
#dates 与java. util. Date 对象的方法对应,格式化、日期组件抽取等等
#numbers 格式化数字对象的工具方法
#strings 与java. lang. String 对应的工具方法
7.9.2 编写Controller
package com. offcn. demo. controller ;
@Controller
public class SevenThymeleafController {
@GetMapping ( "/seven" )
public String indexPage ( Model model) {
Date date = new Date ( ) ;
model. addAttribute ( "date" , date) ;
double price= 128.5678D ;
model. addAttribute ( "price" , price) ;
String str= "Thymeleaf是Web和独立环境的现代服务器端Java模板引擎,能够处理HTML,XML,JavaScript,CSS甚至纯文本。\r\n" +
"Thymeleaf的主要目标是提供一种优雅和高度可维护的创建模板的方式。为了实现这一点,它建立在自然模板的概念上,将其逻辑注入到模板文件中,不会影响模板被用作设计原型。这改善了设计的沟通,弥补了设计和开发团队之间的差距。\r\n" +
"Thymeleaf也从一开始就设计了Web标准 - 特别是HTML5 - 允许您创建完全验证的模板,如果这是您需要的\r\n" ;
model. addAttribute ( "strText" , str) ;
String str2= "JAVA-offcn" ;
model. addAttribute ( "str2" , str2) ;
return "index7" ;
}
}
7.9.3 新增模板文件
<! doctype html >
< html xmlns = " http://www.w3.org/1999/xhtml"
xmlns: th= " http://www.thymeleaf.org" >
< head>
< meta charset = " UTF-8" >
< title> 首页</ title>
</ head>
< body>
时间:< span th: text= " ${#dates.format(date,'yyyy-MM-dd HH:mm:ss')}" > 4564546</ span> </ br>
金额:< span th: text= " ' ¥'+${#numbers.formatDecimal(price, 1, 2)}" > 180</ span> </ br>
< p th: text= " ${#strings.abbreviate(strText,60)}" > 内容内容内容</ p>
< span th: if= " ${!#strings.isEmpty(str2)}" > 字符串str2不为空</ span> </ br>
< span th: text= " ${#strings.substring(str2,0,4)}" > 字符串str2的值</ span>
</ body>
</ html>
7.9.4 运行访问地址
第8节:SpringBoot整合Junit
8.1 junit5 介绍
Spring Boot 2.2 .0 版本开始引入 JUnit 5 作为单元测试默认库
作为最新版本的JUnit 框架,JUnit5 与之前版本的Junit 框架有很大的不同。由三个不同子项目的几个不同模块组成。
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform : Junit Platform 是在JVM 上启动测试框架的基础,不仅支持Junit 自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter : JUnit Jupiter 提供了JUnit5 的新的编程模型,是JUnit5 新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform 上运行。
JUnit Vintage : 由于JUint 已经发展多年,为了照顾老的项目,JUnit Vintage 提供了兼容JUnit4 . x, Junit3 . x的测试引擎。
8.2 整合Junit
8.2.1 构建工程添加依赖
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-test</ artifactId>
< scope> test</ scope>
</ dependency>
< dependency>
< groupId> org.junit.vintage</ groupId>
< artifactId> junit-vintage-engine</ artifactId>
< scope> test</ scope>
< exclusions>
< exclusion>
< groupId> org.hamcrest</ groupId>
< artifactId> hamcrest-core</ artifactId>
</ exclusion>
</ exclusions>
</ dependency>
8.2.2 创建测试类
import org. junit. jupiter. api. Test ;
import org. springframework. boot. test. context. SpringBootTest ;
class TestThymeleafApplicationTests {
@Test
void contextLoads ( ) {
}
}
8.2.3 测试类上添加注解
@SpringBootTest springboot整合junit5 的注解。
8.2.4 测试类注入测试对象
@SpringBootTest
class TestThymeleafApplicationTests {
@Autowired
private ApplicationContext applicationContext;
@Test
void contextLoads ( ) {
System . out. println ( applicationContext) ;
}
}
第9节:SpringBoot整合Mybatis
9.1 创建工程添加依赖
< dependency>
< groupId> org.mybatis.spring.boot</ groupId>
< artifactId> mybatis-spring-boot-starter</ artifactId>
< version> 2.1.4</ version>
</ dependency>
< dependency>
< groupId> mysql</ groupId>
< artifactId> mysql-connector-java</ artifactId>
< version> 5.1.47</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
9.2 建数据库数据库表
CREATE TABLE ` account` (
` id` int ( 11 ) NOT NULL AUTO_INCREMENT ,
` name` varchar ( 255 ) DEFAULT NULL ,
` money` double ( 10 , 2 ) DEFAULT NULL ,
PRIMARY KEY ( ` id` )
) ENGINE = InnoDB AUTO_INCREMENT = 1005 DEFAULT CHARSET = utf8
9.3 创建数据模型
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId ( ) {
return id;
}
public void setId ( Integer id) {
this . id = id;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public double getMoney ( ) {
return money;
}
public void setMoney ( double money) {
this . money = money;
}
}
9.4 创建接口和mapper文件
@Mapper springboot就能够帮助创建接口的代理类对象。
public interface AccountMapper {
Account findById ( Integer id) ;
}
<?xml version="1.0" encoding="UTF-8" ?>
<! DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
< mapper namespace = " com.offcn.AccountMapper" >
< select id = " findById" resultType = " com.offcn.pojo.Account" >
select * from account where id=#{id}
</ select>
</ mapper>
9.5 配置application.yml
spring :
datasource :
url : jdbc: mysql: //localhost: 3306/test
username : root
password : root
driver-class-name : com.mysql.jdbc.Driver
mybatis :
mapper-locations : classpath: mybatis/mapper/*.xml
type-aliases-package : com.offcn.pojo
configuration :
map-underscore-to-camel-case : true
总结:
boot整合mybatis , mapper映射文件的地址必须要指定~ ,其他都是可选择~
9.6 创建业务层接口和实现类
public interface AccountService {
Account findByIdService ( Integer id) ;
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountMapper accountMapper;
@Override
public Account findByIdService(Integer id) {
return accountMapper.findById(id);
}
}
9.7 创建控制器controller
@Controller
@RequestMapping ( "account" )
public class AccountController {
@Autowired
private AccountService accountService;
@GetMapping ( "findById" )
@ResponseBody
public Account findById ( Integer id) {
return accountService. findByIdService ( id) ;
}
}
9.8 启动服务测试结果
访问地址: http: / / localhost: 8080 / account/ findById? id= 1001
测试结果:
第10节:SpringBoot整合Redis
spring- data- redis针对jedis提供了如下功能:
1. 连接池自动管理,提供了一个高度封装的“RedisTemplate ”类
2. 针对jedis客户端中大量api进行了归类封装, 将同一类型操作封装为operation接口
ValueOperations :简单K - V 操作
SetOperations :set类型数据操作
ZSetOperations :zset类型数据操作
HashOperations :针对map类型的数据操作
ListOperations :针对list类型的数据操作
10.1 创建程序引入启动器
或者是pom. xml文件当中引入启动器依赖
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-data-redis</ artifactId>
</ dependency>
10.2 配置连接信息
spring :
redis :
host : 192.168.126.10
port : 6379
10.3 RedisTemplate操作Redis
10.3.1 值类型操作
package com. offcn ;
import org. junit. jupiter. api. Test ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. boot. test. context. SpringBootTest ;
import org. springframework. data. redis. core. RedisTemplate ;
@SpringBootTest
public class BootRedisDemoApplicationTests {
@Autowired
private RedisTemplate < String , String > redisTemplate;
@Test
void test1 ( ) {
ValueOperations < String , String > ops = redisTemplate. opsForValue ( ) ;
ops. set ( "username" , "jack" ) ;
String username = ops. get ( "username" ) ;
System . out. println ( username) ;
redisTemplate. delete ( "username" ) ;
}
}
10.3.2 Set类型操作
package com. offcn ;
import org. junit. jupiter. api. Test ;
import org. springframework. beans. factory. annotation. Autowired ;
import org. springframework. boot. test. context. SpringBootTest ;
import org. springframework. data. redis. core. RedisTemplate ;
import java. util. Set ;
@SpringBootTest
public class BootRedisDemoApplicationTests {
@Autowired
private RedisTemplate < String , String > redisTemplate;
@Test
void test2 ( ) {
SetOperations < String , String > ops = redisTemplate. opsForSet ( ) ;
ops. add ( "set" , "jack" , "marray" , "george" ) ;
Set < String > set = ops. members ( "set" ) ;
System . out. println ( set) ;
ops. remove ( "set" , "jack" ) ;
redisTemplate. delete ( "set" ) ;
}
}
10.3.3 List集合操作
1. 右压栈
@Test
void test3 ( ) {
ListOperations < String , String > ops = redisTemplate. opsForList ( ) ;
ops. rightPush ( "myList" , "a" ) ;
ops. rightPush ( "myList" , "b" ) ;
ops. rightPush ( "myList" , "c" ) ;
List < String > myList = ops. range ( "myList" , 0 , - 1 ) ;
System . out. println ( myList) ;
}
2. 左压栈
@Test
void test4 ( ) {
ListOperations < String , String > ops = redisTemplate. opsForList ( ) ;
ops. leftPush ( "myList" , "A" ) ;
ops. leftPush ( "myList" , "B" ) ;
ops. leftPush ( "myList" , "C" ) ;
List < String > myList = ops. range ( "myList" , 0 , - 1 ) ;
System . out. println ( myList) ;
}
3. 根据索引查询元素
@Test
void test5 ( ) {
ListOperations < String , String > ops = redisTemplate. opsForList ( ) ;
String value = ops. index ( "myList" , 1 ) ;
System . out. println ( value) ;
}
4. 移除某个元素的值
@Test
void test6 ( ) {
ListOperations < String , String > ops = redisTemplate. opsForList ( ) ;
ops. remove ( "myList" , 1 , "A" ) ;
List < String > myList = ops. range ( "myList" , 0 , - 1 ) ;
System . out. println ( myList) ;
}
10.3.4 Hash类型操作
1. 存入值
@Test
void test7 ( ) {
HashOperations < String , Object , Object > ops = redisTemplate. opsForHash ( ) ;
ops. put ( "user" , "username" , "mrzhang" ) ;
ops. put ( "user" , "address" , "beijing" ) ;
ops. put ( "user" , "age" , "18" ) ;
}
2. 提取所有的KEY
@Test
void test8 ( ) {
HashOperations < String , Object , Object > ops = redisTemplate. opsForHash ( ) ;
Set < Object > user = ops. keys ( "user" ) ;
System . out. println ( user) ;
}
3. 提取所有的值
@Test
void test9 ( ) {
HashOperations < String , Object , Object > ops = redisTemplate. opsForHash ( ) ;
List < Object > user = ops. values ( "user" ) ;
System . out. println ( user) ;
}
4. 根据KEY提取值
@Test
void test10 ( ) {
HashOperations < String , Object , Object > ops = redisTemplate. opsForHash ( ) ;
String o = ( String ) ops. get ( "user" , "username" ) ;
System . out. println ( o) ;
}
5. 根据KEY移除值
@Test
void test11 ( ) {
HashOperations < String , Object , Object > ops = redisTemplate. opsForHash ( ) ;
ops. delete ( "user" , "username" ) ;
}
10.4 SpringBoot整合Redis序列化操作
10.4.1 Spring提供的序列化器介绍
1. Spring默认序列化器
RedisTemplate 操作时,默认会采用jdkSerializable序列化机制,使得插入的值在redis客户端看来会有乱码,若想解决这一问题,需要手动指定序列化方式。
public class RedisTemplate < K , V > extends RedisAccessor implements RedisOperations < K , V > , BeanClassLoaderAware {
public void afterPropertiesSet ( ) {
super . afterPropertiesSet ( ) ;
boolean defaultUsed = false ;
if ( this . defaultSerializer == null ) {
this . defaultSerializer = new JdkSerializationRedisSerializer ( this . classLoader != null ? this . classLoader : this . getClass ( ) . getClassLoader ( ) ) ;
}
. . .
}
默认序列化器: JdkSerializationRedisSerializer 存对象的时候, 将对象转换成二进制信息存值。
2. 常见的序列化器
10.4.2 序列化案例实现
1. 创建实体实现序列化接口
public class Account implements Serializable {
private Integer id;
private String name;
private double money;
public Integer getId ( ) {
return id;
}
public void setId ( Integer id) {
this . id = id;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
public double getMoney ( ) {
return money;
}
public void setMoney ( double money) {
this . money = money;
}
@Override
public String toString ( ) {
return "Account{" +
"id=" + id +
", name='" + name + '\'' +
", money=" + money +
'}' ;
}
}
2. 在配置类中设置序列化器
@Configuration
public class RedisConfigurer {
@Bean
public RedisTemplate < String , Account > redisTemplate ( RedisConnectionFactory redisConnectionFactory) {
RedisTemplate < String , Account > template = new RedisTemplate ( ) ;
template. setConnectionFactory ( redisConnectionFactory) ;
Jackson2JsonRedisSerializer serializer = new Jackson2JsonRedisSerializer ( Account . class ) ;
template. setDefaultSerializer ( serializer) ;
return template;
}
}
使用Jackson2JsonRedisSerializer ,需要引入jackson的依赖
如果使用了自定义容器,原有的RedisTemplate 使用将受影响,需要将这个@Bean 上添加一个bean的名字,在使用注入时使用这个名字
@Bean ( "accountTemplate" )
@Resource ( "accountTemplate" )
RedisTemplate redisTemplate;
3. 测试类对象序列化
@SpringBootTest
public class RedisDemo02ApplicationTests {
@Autowired
private RedisTemplate < String , Account > redisTemplate;
@Test
public void serializableAccount ( ) {
Account account = new Account ( ) ;
account. setId ( 1001 ) ;
account. setName ( "张三丰" ) ;
account. setId ( 98765 ) ;
redisTemplate. opsForValue ( ) . set ( "account" , account) ;
System . out. println ( "序列化成功" ) ;
}
}
第11节:SpringBoot整合Dubbo
11.1 启动zookeeper注册中心服务
注意: 启动dubbo- admin ,方便服务的查询~
11.2 构建工程添加依赖
< dependencies>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-test</ artifactId>
< version> 2.6.3</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
< dependency>
< groupId> org.apache.dubbo</ groupId>
< artifactId> dubbo-dependencies-zookeeper</ artifactId>
< version> 2.7.6</ version>
< type> pom</ type>
< exclusions>
< exclusion>
< groupId> org.slf4j</ groupId>
< artifactId> slf4j-log4j12</ artifactId>
</ exclusion>
</ exclusions>
</ dependency>
< dependency>
< groupId> org.apache.dubbo</ groupId>
< artifactId> dubbo-spring-boot-starter</ artifactId>
< version> 2.7.6</ version>
</ dependency>
< dependency>
< groupId> com.offcn</ groupId>
< artifactId> sb_service_interface</ artifactId>
< version> 1.0-SNAPSHOT</ version>
</ dependency>
</ dependencies>
11.3 创建服务提供者
11.3.1 定义服务接口
public interface OrderService {
public Order loadOrdersService ( Integer id) ;
public List < Order > loadOrdersListService ( ) ;
}
11.3.2 定义实现类并暴露服务
@Component
@Service
public class OrderServiceImpl implements OrderService {
@Override
public Order loadOrdersService ( Integer id) {
Order o= new Order ( ) ;
o. setId ( id) ;
o. setRemark ( "备注...." ) ;
o. setTotal ( 123 ) ;
return o;
}
@Override
public List < Order > loadOrdersListService ( ) {
Order o= new Order ( ) ;
o. setId ( 1001 ) ;
o. setRemark ( "新疆大盘鸡一份" ) ;
o. setTotal ( 12 ) ;
Order o1= new Order ( ) ;
o1. setId ( 1002 ) ;
o1. setRemark ( "东北杀猪菜一份" ) ;
o1. setTotal ( 123 ) ;
List < Order > results= new ArrayList < Order > ( ) ;
results. add ( o) ;
results. add ( o1) ;
return results;
}
}
11.3.3 配置application.properties整合dubbo
server:
port: 8088
spring:
application:
name: sb8
#dubbo端口和名称
dubbo:
protocol:
name: dubbo
port: 20880
registry:
address: zookeeper://192.168.1.151:2181
timeout: 60000
scan:
base-packages: com.offcn.service.impl
11.3.4 创建启动类并启动服务
@SpringBootApplication
public class DemoProvederApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( DemoProvederApplication . class , args) ;
}
}
11.4 创建服务器消费者
11.4.1 环境准备
创建boot工程, 引入相关依赖
< dependencies>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-test</ artifactId>
< version> 2.6.3</ version>
</ dependency>
< dependency>
< groupId> org.springframework.boot</ groupId>
< artifactId> spring-boot-starter-web</ artifactId>
</ dependency>
< dependency>
< groupId> org.apache.dubbo</ groupId>
< artifactId> dubbo-dependencies-zookeeper</ artifactId>
< version> 2.7.6</ version>
< type> pom</ type>
< exclusions>
< exclusion>
< groupId> org.slf4j</ groupId>
< artifactId> slf4j-log4j12</ artifactId>
</ exclusion>
</ exclusions>
</ dependency>
< dependency>
< groupId> org.apache.dubbo</ groupId>
< artifactId> dubbo-spring-boot-starter</ artifactId>
< version> 2.7.6</ version>
</ dependency>
< dependency>
< groupId> com.offcn</ groupId>
< artifactId> sb_service_interface</ artifactId>
< version> 1.0-SNAPSHOT</ version>
</ dependency>
</ dependencies>
11.4.2 定义消费端业务接口
public interface OrderService {
public Order loadOrdersService ( Integer id) ;
public List < Order > loadOrdersListService ( ) ;
}
11.4.3 定义消费端
@Controller
public class OrderController {
@Reference
private OrderService orderService;
@RequestMapping ( "findById" )
@ResponseBody
public Order findById ( Integer id) {
Order order = orderService. loadOrdersService ( id) ;
return order;
}
@RequestMapping ( "findAll" )
@ResponseBody
public List < Order > findAll ( Integer id) {
return orderService. loadOrdersListService ( ) ;
}
}
11.4.4 配置application.yml整合dubbo
server:
port: 9000
servlet:
context-path: /
dubbo:
protocol:
name: dubbo
port: 20881
registry:
address: zookeeper://192.168.1.151:2181
timeout: 60000
11.4.5 创建启动类并启动服务
@SpringBootApplication
public class DemoProvederApplication {
public static void main ( String [ ] args) {
SpringApplication . run ( DemoProvederApplication . class , args) ;
}
}
11.4.6 启动浏览器进行测试