Java进阶笔记(五):SpringBoot相关

​一、springboot是什么

是一种快速使用spring的方式,简化了大量配置文件。

SpringBoot是所有基于spring开发的项目的起点。SpringBoot的目的是为了让用户尽可能快的跑起来Spring应用程序并尽可能减少配置文件。

 

=========================

 

二、springboot原理

基于"约定优于配置"(Convention over Configuration)思想,使用默认值简化配置,开发人员仅需规定应用中不符约定的部分。(如果不想用默认值,则需要手动配置)

 

-------------------------

 

Spring优点:组件代码是轻量级的。

Spring缺点:配置是重量级的(applicationContext.xml);项目的依赖管理耗时耗力。

 

-------------------------

 

SpringBoot解决了spring缺点:

1.起步依赖:springboot将需要依赖的一系列jar包的信息整合成了一个个.pom文件,使用时,只需要引入整合后的pom文件即可。(之前是在pom.xml中引入一堆jar包,现在可以在pom.xml中引入一个整合好的.pom。)

 

2.自动配置:springboot会自动将一些配置类的bean注入ioc容器;使用时,只需要引入jar包,在需要的地方写@autowired或者@resource从ico容器中拿出来使用即可。

(而原来spring中,除了引入jar包,还需要在applicationContext.xml中配置<bean>标签,或者使用@Bean注解,才能注入ioc容器。例如mybatis,driud连接池等)

 

=========================

 

三、springboot源码分析

1.SpringBoot中,使用dependency导入常用jar包时不需要指定版本,因为pom.xml中的spring-boot-starter-parent有一个<version>指定了自己的版本号,而spring-boot-starter-parent中的spring-boot-dependencies中的<properties>标签中,指定了一系列的常用jar包的版本号。(所以自己使用dependency引入常用jar包时不用指定版本,避免了版本冲突)

 

2.SpringBoot中,对于常用的框架,只需要引入需要的spring-boot-starter即可(例如spring-boot-starter-web),其中已包含需要的一系列jar包,而不需要自己手动一个个导入jar包,简化了配置信息。

 

3.SpringBoot能够在添加jar包依赖时,使用默认值自动配置一些信息,我们无需配置或只需少量配置就能运行编写的项目。(而在Spring中,我们需要自己对每个导入的jar包设置配置信息,通过<bean>标签或@bean注解)

 

4.SpringBoot自动配置原理:

(1)SpringBoot引用的启动入口是@SpringBootApplication注解标注的main()方法,@SpringBootApplication能够扫描Spring组件并自动配置SpringBoot

 

(2)@SpringBootApplication注解是一个组合注解,其中重要的有3个:@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan。

 

(3)@SpringBootConfiguration注解中,包含@Configuration注解,标明该类为配置类;当SpringBoot启动时,扫描到带有该注解的类,就当做配置类进行特殊处理。

 

(4)●@EnableAutoConfiguration注解,表示开启自动配置功能,其中有@AutoConfigurationPackage与@Import({AutoConfigurationImportSelector.class})注解;

●@AutoConfigurationPackage注解中有@Import({Registrar.class}),当SpringBoot启动时,就会把Registrar类实例化并存入spring容器中;

●Registrar类中有registerBeanDefinitions()方法,将主程序类所在包及其所有子包下的组件扫描到spring容器中;【因此SpringBoot启动类的位置要在最外层的根目录下】

●@Import({AutoConfigurationImportSelector.class})注解,会将AutoConfigurationImportSelector导入spring容器,这个类可以将所有符合条件的@Configuration配置加载到spring容器中;

●AutoConfigurationImportSelector类中有一个loadFactoryNames()方法,这个方法中,会读取"META-INF/spring.factories"配置文件;

●spring.factories文件有多个,在pom.xml中引入的"spring-boot-starter-xxx"中,有些会对应一个jar包,其中有spring.factories文件,这个文件中又配置了一些配置类的信息;

●然后对应的配置类中使用了@Bean注解将默认的配置信息存入了spring容器。(这个配置类也在starter对应jar包中;这样就实现了自动配置)

 

(5)@ComponentScan注解,配置后默认会扫描该类所在的包下所有的配置类。上方的Registrar类中的registerBeanDefinitions()方法会结合@ComponentScan具体实现配置类扫描。

 

5.启动方法:SpringApplication.run()

这个方法中主要完成了:

(1)SpringApplication实例的初始化创建(SpringApplication类用于引导和启动一个Spring应用程序),其中通过读取spring.factores文件,实现了SpringBoot自动配置。

(2)调用SpringApplication.run()启动项目(返回的对象属于ApplicationContext,即spring容器)。

其中包含获取并启动SpringApplication监听器、准备运行环境、创建Spring容器、Spring容器前置处理、刷新Spring容器、Spring容器后置处理、执行自定义执行器Runners等步骤。

 

=========================

 

四、springboot单元测试

可以不启动整个项目,只初始化ioc容器,只运行测试类进行测试。

 

步骤:

1.在pom.xml中引入配置(如果有了就不用重复添加):

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency>

2.在src/test/java/com.xxx.xxx/XXXApplicationTests.java中编写测试方法(创建项目会自动生成;也可以手动创建一个):

@RunWith(SpringRunner.class)@SpringBootTestclass XXXApplicationTests { //注入controller,测试url的返回值是否正确 @Autowired private MyController myController; @Test void controllerTest() { String back = myController.getMsg(); System.out.println(back); }}

=========================

 

五、springboot热部署

当代码有改动时,可以自动部署并生效,而不用手动重启项目。

 

步骤:

1.在pom.xml中引入配置:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId></dependency>

2.如果是IDEA开发工具,则需要打开settings继续配置:

左侧栏中选择:Build,Execution,Deployment->Compiler

右侧选中:Build project automatically

 

3.如果是IDEA开发工具,还需要使用快捷键"Ctrl+Shift+Alt+/"打开Maintenance选项框,选择Registry...,然后在打开的界面中找到"compiler.automake.allow.when.app.running",将对应的value值打钩。

 

4.如果IDEA快捷键打不开Maintenance,可以查看settings->Keymap,搜索Maintenance。

 

5.重启IDEA。

 

=========================

 

六、springboot整合thymeleaf

SpringBoot不能很好地使用jsp,因此常用thymeleaf模板。

Thymeleaf是一种基于服务器端的Java模板引擎技术,也是一个优秀的面向Java的XML、XHTML、HTML5的页面模板,具有丰富的标签语言、函数和表达式。

在HTML页面上使用Thymeleaf标签,能够动态地替换掉静态内容,使页面动态展示。

 

使用方式:

1.pom.xml中引入依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>

2.在application.properties中配置:

spring.thymeleaf.cache = true #启用模板缓存,生产为true;开发时设置为false方便调试spring.thymeleaf.encoding = UTF-8 #模板编码spring.thymeleaf.mode = HTML5 #应用于模板的模板模式spring.thymeleaf.prefix = classpath:/templates/ #页面存放路径,前缀(html路径)spring.thymeleaf.suffix = .html #页面后缀(代码中可以省略.html了)

3.在resources/static下放静态资源,例如js、css、img等

4.在resources/templates下存放html,访问这些html时需要从controller跳转到才可以

5.在html中使用thymeleaf标签实现动态页面,常用标签有:

th:fragment  标记一个标签并起一个名称,后续替换用。th:insert  将目标标签中的内容(含标签名)放在这个标签中。(这个自身标签还在)th:include  (3.0版本后已不推荐使用)将目标标签中的内容(不含标签名)放在这个标签中。(这个自身标签还在)th:replace  将目标标签(含标签名)整个替换掉这个标签。(这个自身标签不在了,被结点标签替换)th:each  元素遍历th:if  条件判断,如果符合条件则显示th:unless  条件判断,如果不符合条件才显示th:switch  条件判断,进行选择性匹配th:case  条件判断,进行选择性匹配th:value  属性值修改,指定标签属性值th:href  用于设定链接地址th:src  用于设定链接地址th:text  用于指定标签显示的文本内容

6.在html中使用thymeleaf标签时,同时会用到标准表达式,格式为:

${...}  变量表达式,主要用于获取上下文中的变量值。*{...}  选择变量表达式,主要用于从被选定对象中获取属性值;如果没有选定对象,则和变量表达式一样。#{...}  消息表达式,用于从properties中取值,常用来做页面国际化。@{...}  链接表达式,一般用于页面跳转或者资源的引入;可以嵌套变量表达式进行变量拼接。~{...}  片段表达式,用来标记一个片段模板,并根据需要移动或传递给其它模板;常结合th:insert或th:replace使用。

7.国际化页面配置方法

(1)在项目的resources文件夹下创建一个language文件夹,在这个文件夹中编写多个对应不同语言的国际化文件:language.properties、language_zh_CN.properties、language_en_US.properties

 

language.properties样例(默认用这个):

language.username=用户名language.password=密码language.button=登录

language_zh_CN.properties样例(中文):

language.username=用户名language.password=密码language.button=登录

language_en_US.properties样例(英文):

language.username=usernamelanguage.password=passwordlanguage.button=login

(2)在application.properties中增加配置:

#配置国际化文件基础名;格式为:包名.文件前缀名spring.messages.basename=language.language

(3)在html页面中使用标签例如:

<button type="submit" th:text="#{language.button}">登录</button>

当th:text有值时,标签内容优先使用对应值;没有值时,才使用页面标签内编写的值。

 

这样就实现了页面国际化,当请求头中的语言信息不同时,就会读取不同的properties文件中对应的value;

例如Accept-Language:en-US,则读取language_en_US.properties;

如果没有匹配到,则使用默认的language.properties

 

(4)如果想增加手动切换语言的动能,则可以自定义区域解析器,并在html中增加修改按钮。

●在项目中增加一个配置类(使用@Configuration注解),实现LocaleResolver接口:

@Configuration
public class MyLocaleResovel implements LocaleResolver {
 @Override
 public Locale resolveLocale(HttpServletRequest httpServletRequest) {
 //获取自定义参数l,前端传来的(例如zh_CN,语言_地区)
 String l = httpServletRequest.getParameter("l");
 //获得请求头中的Accept-Language
 String header = httpServletRequest.getHeader("Accept-Language");
 Locale locale=null;
 //如果不为空,则根据参数new一个Locale对象
 if(!StringUtils.isEmpty(l)){
 String[] split = l.split("_");
 locale=new Locale(split[0],split[1]);
 }
 //否则根据请求头中默认的new一个Locale对象
 else {
 // Accept-Language: en-US,en;q=0.9,zh-CN;q=0.8,zh;q=0.7
 String[] splits = header.split(",");
 String[] split = splits[0].split("-");
 locale=new Locale(split[0],split[1]);
 }
 return locale;
 }
 @Override
 public void setLocale(HttpServletRequest httpServletRequest, @Nullable HttpServletResponse httpServletResponse, @Nullable Locale locale) {
 }
 //将自定义的MyLocaleResovel类重新注册为一个类型为LocaleResolver的Bean组件(把bean放入Spring容器,覆盖默认的LocaleResolver组件)
 @Bean
 public LocaleResolver localeResolver(){
 return new MyLocalResovel();
 }
}

●在html中使用a标签与th:href,传递参数l:

<a class="btn btn-sm" th:href="@{/toLoginPage(l='zh_CN')}">中文</a><a class="btn btn-sm" th:href="@{/toLoginPage(l='en_US')}">English</a>

点击前端按钮,自定义区域解析器就会收到参数l,new一个Locale对象并存入spring容器,后续解析html中的Thymeleaf标签时就会使用不同的properties语言配置文件,就实现了手动切换语言。

 

=========================

 

七、springboot整合mybatis

1.准备好数据库与表

2.创建SpringBoot项目,pom.xml中要引入以下依赖:

<dependency>    <groupId>org.springframework.boot</groupId>    <artifactId>spring-boot-starter-web</artifactId></dependency><dependency>    <groupId>org.mybatis.spring.boot</groupId>    <artifactId>mybatis-spring-boot-starter</artifactId>    <version>2.0.0</version></dependency><dependency>    <groupId>com.alibaba</groupId>    <artifactId>druid-spring-boot-starter</artifactId>    <version>1.1.10</version></dependency><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>5.1.28</version>    <scope>runtime</scope></dependency>

3.在application.properties中配置数据库连接信息(或者在yaml,yml中配置):

spring.datasource.url=jdbc:mysql://localhost:3306/MyDataBaseName?useUnicode=true&characterEncoding=utf-8spring.datasource.username=rootspring.datasource.password=rootspring.datasource.type=com.alibaba.druid.pool.DruidDataSource

4.创建数据库表对应的pojo类

5.编写service层代码

6_1.编写dao层代码,可以使用@Mapper注解标注类,使用@Select等注解标注方法并编写sql语句;

6_2.或者仅使用@Mapper注解标注dao层的类,在resources目录下创建xxxMapper.xml文件,在xml文件中编写sql语句;

同时,需要在配置文件application.properties中增加扫描xml的语句:

#配置mapper.xml的路径mybatis.mapper-locations=classpath:mapper/*.xml#配置xml文件中指定的实体类别名路径(可以省略xml中配置bean时的前缀,如com.xxx等)mybatis.type-aliases-package=com.lagou.pojo

7.对于数据库中下划线命名的字段与pojo中驼峰命名的字段无法对应的问题,可以在application.properties中增加配置,开启驼峰命名匹配映射:

mybatis.configuration.map-underscore-to-camel-case=true

 

=========================

 

八、springboot整合jpa

1.pom.xml中添加依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId></dependency>

2.编写ORM实体类,使用@Entity注解标注类并指定对应的数据库表名;使用@Id标注主键,使用@GeneratedValue设置主键自增策略,使用@Column指定对应的表名(如果变量名与表名完全一致则可以省略),例:

@Entity(name = "m_user")  //对应表名public class User { @Id //主键 @GeneratedValue(strategy = GenerationType.IDENTITY) // 主键自增策略 private Integer id; private String username; private String password;  @Column(name = "a_id") private Integer aId; //省略get、set方法,实际使用时要加上 //省略toString方法 }

3.编写service层;如果涉及事务,可以在类上加@Transactional注解,表示其中的每个方法都是一个事务。

 

4.编写Repository接口(类似dao层)

(1)使用这个接口实例化的对象时,可以使用其中内置的方法

(2)如果内置的方法不够,可以新增方法,按照一定的语法规则编写方法名,即可在不写sql的情况下实现功能

(3)也可以新增方法,使用@Query注解+sql的形式实现;如果要使用原生sql,需要增加nativeQuery=true;如果要使用jpa的sql则不用写nativeQuery(默认false);如果涉及到数据的增删改,需要在方法上加@Modifying注解(只是查询则不要加,会报错)

例子如下:

public interface ResumeDao extends JpaRepository<Resume,Long>, JpaSpecificationExecutor<Resume> {    //jpa的sql    @Query("from Resume  where id=?1 and name=?2")    public List<Resume> findByJpql(Long id, String name);    //原生sql,nativeQuery属性设置为true    @Query(value = "select * from tb_resume  where name like ?1 and address like ?2",nativeQuery = true)    public List<Resume> findBySql(String name, String address);    //省略sql,直接按规则写方法名查询    public List<Resume> findByNameLikeAndAddress(String name, String address);    //原生sql,修改操作    @Modifying    @Query(value = "insert into tb_resume(name,address,phone) values(?1,?2,?3)",nativeQuery = true)    public void insertOne(String name, String address, String phone);    //省略sql的删除操作    @Modifying    public int deleteById(String id);}

 

=========================

 

九、springboot整合redis(只操作redis)

1.下载redis并启动,同时可以下载RedisDesktopManager来管理redis

 

2.pom.xml中增加依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

 

3.在application.properties中配置以下信息:

#redis地址spring.redis.host=127.0.0.1#redis端口spring.redis.port=6379#redis密码,默认是空密码spring.redis.password=

4.编写pojo类,使用@RedisHash、@Id、@Indexed注解标注,指定操作这个javabean时在redis中的存储空间

 

例:

//指定这个javabean在redis数据库中的存储空间//此处表示针对Person这个实体类的数据操作都存储在redis中名为persons的存储空间下@RedisHash("persons") public class Person { @Id //主键,在redis数据库中会默认生成字符串形式的HashKey表示唯一实体对象id(也可以手动指定id) private String id; @Indexed //标识对应属性在redis数据库中生成二级索引,索引名称就是属性名,方便进行数据条件查询 private String firstname; @Indexed private String lastname; //省略get、set方法,实际使用时要加上 //省略构造方法,使用时要加无参构造方法 //省略toString()方法 }

5.编写Repository接口。需要注意,继承的是CrudRepository(而不是整合jpa时的CrudRepository)

public interface PersonRepository extends CrudRepository<Person,String> { List<Person> findByFirstname(String name);}

6.编写测试类,使用@Autowired将PersonRepository注入,new一个Person对象,即可进行redis的存取操作。

PersonRepository的使用方法同JpaRepository。

 

=========================

 

十、springboot整合jpa与redis实现缓存,基于注解方式

1.按照springboot整合jpa的步骤,搭建好环境。(Repository继承JpaRepository即可)

 

2.下载redis并启动,同时可以下载RedisDesktopManager来管理redis

 

3.pom.xml中增加依赖:

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>

4.在application.properties中配置以下信息:

# MySQL数据库配置spring.datasource.url=jdbc:mysql://localhost:3306/mydatabasename?serverTimezone=UTCspring.datasource.username=rootspring.datasource.password=root#显示使用jpa进行数据库查询的sql语句,方便调试spring.jpa.show-sql=true #开启驼峰命名匹配映射mybatis.configuration.map-underscore-to-camel-case=true#解决http乱码用spring.http.encoding.force-response=true #redis地址spring.redis.host=127.0.0.1#redis端口spring.redis.port=6379#redis密码,默认是空密码spring.redis.password=

5.在启动类XXXApplication.java中的类上加注解@EnableCaching,开启基于注解的缓存支持。

 

6.在service层中的查询方法上使用@Cacheable注解,程序在执行查询方法时,就会先查redis缓存,如果不存在,则查数据库并将结果存入redis缓存,返回查询结果;如果存在,则直接从redis缓存中获得结果返回,不再查询数据库。

//cacheNames是redis中对应结果的存储空间名//unless是说,返回为null时,则不存入缓存//key是指redis的cacheNames指定的存储空间中的key,对应的value是结果;查询时,会根据传入的参数,按照一定的规则转换为key,然后从redis中寻找有没有value@Cacheable(cacheNames = "article",unless = "#result==null",key = "#pageable.pageNumber+'_'+#pageable.pageSize")

注意,也可以只使用@Cacheable注解,不配置cacheNames、unless、key,此时会按照默认方法把入参按照一定规则自动生成key。

 

7.在service层中的更新方法上使用@CachePut注解,程序执行该方法时,会先更新数据库,成功后更新缓存。(注意cacheNames与key要与@Cacheable中的对应)

 

8.在service层中的删除方法上使用@CacheEvict注解,程序执行该方法时,会先删除数据库中的数据,成功后删除缓存数据。(注意cacheNames与key要与@Cacheable中的对应)

 

9.如果将pojo类存入redis时报错,则要在pojo类上实现序列化接口,implements Serializable。

 

=========================

 

十一、springboot整合jpa与redis实现缓存,基于API方式

1.基本流程同上,只是service中不使用@Cacheable、@CachePut、@CacheEvict注解;启动类不使用@EnableCaching注解。

2.service层中,使用@Autowired注解将XXXRepository注入,使用这个进行数据库存取。

3.service层中,使用@Autowired注解将RedisTemplate注入,使用这个进行redis存取,例:

//从redis中根据id获取值Object o = redisTemplate.opsForValue().get("user_" + id);//从数据库中获取User,然后存入redis,并设置这个值的缓存有效期为1天Optional<User> byId = userRepository.findById(id);if(byId.isPresent()){User user = byId.get();   redisTemplate.opsForValue().set("user_"+id,user,1,TimeUnit.DAYS);}

4.然后就可以自己实现数据库与redis缓存之间的代码逻辑了。

 

=========================

 

十二、自定义redis缓存序列化机制

当使用Redis可视化管理工具Redis Desktop Manager查看缓存数据时,redis缓存数据的默认格式是HEX,不方便阅读;

此时可以通过自定义redis缓存序列化的方法,让redis缓存保存为方便阅读的形式,例如json格式。

 

1.如果基于注解实现了redis缓存,则需要新建一个用@Configuration注解标注的类,使用@Bean注解将RedisCacheManager对象自定义并装入spring容器,实现自定义序列化方式。(当存在自定义序列化方式时,则不会使用默认序列化方式)

 

2.如果基于API实现了redis缓存,则需要新建一个用@Configuration注解标注的类,使用@Bean注解将RedisTemplate对象自定义并装入spring容器,实现自定义序列化方式。(当存在自定义序列化方式时,则不会使用默认序列化方式)

 

例:

@Configurationpublic class RedisConfig {    // 自定义一个RedisTemplate,对API实现redis缓存生效    @Bean    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {        RedisTemplate<Object, Object> template = new RedisTemplate<>();        template.setConnectionFactory(redisConnectionFactory);        // 创建JSON格式序列化对象,对缓存数据的key和value进行转换        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);        // 解决查询缓存转换异常的问题        ObjectMapper om = new ObjectMapper();        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jackson2JsonRedisSerializer.setObjectMapper(om);        //设置redisTemplate模板API的序列化方式为json        template.setDefaultSerializer(jackson2JsonRedisSerializer);        return template;    }    // 自定义一个RedisCacheManager,对注解实现redis缓存生效    @Bean    public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {        // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换        RedisSerializer<String> strSerializer = new StringRedisSerializer();        Jackson2JsonRedisSerializer jacksonSeial =                new Jackson2JsonRedisSerializer(Object.class);        // 解决查询缓存转换异常的问题        ObjectMapper om = new ObjectMapper();        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);        jacksonSeial.setObjectMapper(om);        // 定制缓存数据序列化方式及时效        RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()                .entryTtl(Duration.ofDays(1))                .serializeKeysWith(RedisSerializationContext.SerializationPair                        .fromSerializer(strSerializer))                .serializeValuesWith(RedisSerializationContext.SerializationPair                        .fromSerializer(jacksonSeial))                .disableCachingNullValues();        RedisCacheManager cacheManager = RedisCacheManager                .builder(redisConnectionFactory).cacheDefaults(config).build();        return cacheManager;    }}

 

posted @ 2020-10-28 15:39  codeToSuccess  阅读(123)  评论(0编辑  收藏  举报