• 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;    }}
  • 相关阅读:
    oracle取字符串长度的函数length()和hengthb()
    文件操作
    numpy 库使用
    numpy 与 matplotlib 的应用过程
    使用numpy与matplotlib.pyplot画图
    面向对象的解读
    Python PIL
    Note of Jieba
    python 游戏 —— 汉诺塔(Hanoita)
    有进度条圆周率Π计算
  • 原文地址:https://www.cnblogs.com/codeToSuccess/p/13906189.html
Copyright © 2020-2023  润新知