SpringBoot详细笔记记录

什么是微服务?

  • 微服务就是要倡导大家尽量将功能进行拆分,将服务粒度做小,使之可以独立承担对外服务的职责,沿着这个思路开发和交付的软件服务实体就叫作“微服务”,而围绕着这个思路和理念构建的一系列基础设施和指导思想,将它称为“微服务体系”。

“火车模型”

  • “火车模型”:比如我们需要交付一个造火车的项目,为了使团队并行开发不冲突,可以采用微服务的开发思路,把火车拆分成一节一节的车厢,让不同的团队开发各自的车厢,所有都开发完后进行整合交付,就是火车模型。

微服务优缺点

  • 微服务因为拆分成了很多的独立单元,缺点就是项目的复杂度增加了。好处呢?我们说微服务打响的是各自的独立战争,所以,每一个微服务都是一个小王国,这些微服务跳出了“大一统”(Monolith)王国的统治,开始从各个层面打造自己的独立能力,从而保障自己的小王国可以持续稳固的运转。
  • 在微服务治理体系下,各个微服务交付期间也是各自独立交付的,从而使得每个微服务从开发到交付整条链路上都是独立进行,这大大加快了微服务的迭代和交付效率。

Spring IoC介绍

  •  IoC(Inversion Of Control)控制反转:是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI)。
  • 以上是官方介绍,现在简单来认识一下。
  • 比如创建一个对象,传统的方法是直接new对象就行,也就是我们主动去控制对象,这样呢就造成了对象管理上的难度,也就是 操心操肺的,很累人。
  • IOC呢,就是为此而生,IOC有一个容器,这个容器就像是工厂一样,专门负责生产对象,而不用我们去new,那么这个容器是如何获得对象的呢?在定义一个JavaBean的时候,通过一些配置,把它注册到容器中。当我们需要获取时,直接通过自动装配@Autowired注解获得。
  • 那是如何把Bean注入IOC的呢?spring中好像是通过xml来实现的,就是在xml中设置Bean所在的包等等,来注入IOC中,因为没看spring,直接看了SpringBoot,所以不太清楚。
  • 在SpringBoot中呢,因为基本不再使用xml,而是用配置类来代替了xml。具体过程呢,就是先定义一个Javabean,然后把这个Javabean在配置类中注入IOC。
  • 如下先定义一个bean
  • @NoArgsConstructor
    @AllArgsConstructor
    @Data
    public class User
    {
        private String name;
        private Integer id;
    }

     

  • 然后把这个Bean拿到配置类中注入,注入不一定是类,可以是方法的各种返回值,但是在没有备注Bean的名称时,同一类型的返回值只能有一个,因为从IOC里取出的时候默认按类型取值,如果注入了两个不同方法名,但是返回值相同的Bean,就会提示错误,提示只能有一个才能取出。
  • package com.heyexi.helloworld.config;
    
    import com.heyexi.helloworld.entity.User;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    @Configuration
    public class ConfigBean
    {
        @Bean   //代表注入一个Bean  用这个注解代码着Spring将接管这个Bean
        public User getUser()
        {
            return new User("何夜息",1);  //返回我们的User对象
        }
    }

     

 

  • 然后我们可以通过自动装配@AutoWired注解取出刚才注入的对象。
@SpringBootTest
class HelloworldApplicationTests
{
    @Autowired  //自动装配注解从IOC取出相应类型的注入对象
    private User u;
    
    @Test
    void contextLoads()
    {
        System.out.println(u); //输出
    }

}
  • 输出结果如下:

 

2020-05-14 19:30:34.104  INFO 19140 --- [           main] c.h.h.HelloworldApplicationTests         : Starting HelloworldApplicationTests on 何夜息 with PID 19140 (started by Administrator in E:\source\java\SpringBoot\helloworld)
2020-05-14 19:30:34.105  INFO 19140 --- [           main] c.h.h.HelloworldApplicationTests         : No active profile set, falling back to default profiles: default
2020-05-14 19:30:36.495  INFO 19140 --- [           main] o.s.s.concurrent.ThreadPoolTaskExecutor  : Initializing ExecutorService 'applicationTaskExecutor'
2020-05-14 19:30:36.624  INFO 19140 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
2020-05-14 19:30:36.929  INFO 19140 --- [           main] c.h.h.HelloworldApplicationTests         : Started HelloworldApplicationTests in 3.575 seconds (JVM running for 6.88)
User(name=何夜息, id=1)
  • 那么有两个或多个返回值相同的时候怎么做呢?我们可以在获取bean是,再加一个@Qualifier("xx")注解,里面的xx代表的是bean的名称,通过bean的名称获取就不会报错。如果我们不给bean取名,那么默认就是方法名。那么如何给bean取别名呢?可以使用@bean的name或者value属性,指定名称,然后通过@Qualifier注解就能获得了。如下定义三个返回值都是String的bean,我们分别依次获取值。
  • @Configuration
    public class ConfigBean
    {
        @Bean
        public User getUser()
        {
            return new User("何夜息",1);
        }
        
        @Bean
        public String uname()
        {
            return "我是何夜息";   //这个我们直接通过@AutoWire获取
        }
    
        @Bean
        public String zhangsan()  //这个我们通过方法名获取,因为默认Bean的名称时方法名
        {
            return "我是张三";
        }
    
        @Bean(name = "lisi")
        public String uname3()
        {
            return "我是李四";  //这个我们通过bean的别名获取
        }
    
    }

     

  • 然后在测试类中测试一下:
  • @SpringBootTest
    class HelloworldApplicationTests
    {
    
        @Autowired
        private User u;
    
        @Autowired
        private String uname;//这里好像只能用uname,因为有多个返回值为String的Bean
    
        @Autowired
        @Qualifier("zhangsan") //通过方法名获取
        private String zs;
    
        @Autowired
        @Qualifier("lisi")  //通过bean的name属性获取
        private String ls;
    
        @Test
        void contextLoads()
        {
            System.out.println(uname);
            System.out.println(zs);
            System.out.println(ls);
            System.out.println(u);
        }
    }

     

  • 运行结果如下:确实都取出来了,但为了辨别,最好每一个bean都取别名。
  • 2020-05-14 20:01:42.862  INFO 18996 --- [           main] o.s.b.a.w.s.WelcomePageHandlerMapping    : Adding welcome page template: index
    2020-05-14 20:01:43.201  INFO 18996 --- [           main] c.h.h.HelloworldApplicationTests         : Started HelloworldApplicationTests in 4.006 seconds (JVM running for 6.573)
    我是何夜息
    我是张三
    我是李四
    User(name=何夜息, id=1)
    2020-05-14 20:01:43.857

     

  •  

 

Spring Boot 核心功能

  • 1)独立运行的 Spring 项目:Spring Boot 可以以 jar 包的形式独立运行,运行一个 Spring Boot 项目只需通过 java–jar xx.jar 来运行。
  • 2)内嵌 Servlet 容器:Spring Boot 可选择内嵌 Tomcat、Jetty 或者 Undertow,这样我们无须以 war 包形式部署项目。
  • 3)提供 starter 简化 Maven 配置:Spring 提供了一系列的 starter pom 来简化 Maven 的依赖加载,例如,当你使用了spring-boot-starter-web 时,会自动加入如图 1 所示的依赖包。
  • 4)自动配置 Spring:Spring Boot 会根据在类路径中的 jar 包、类,为 jar 包里的类自动配置 Bean,这样会极大地减少我们要使用的配置。当然,Spring Boot 只是考虑了大多数的开发场景,并不是所有的场景,若在实际开发中我们需要自动配置 Bean,而 Spring Boot 没有提供支持,则可以自定义自动配置。
  • 5)准生产的应用监控:Spring Boot 提供基于 http、ssh、telnet 对运行时的项目进行监控。
  • 6)无代码生成和 xml 配置:Spring Boot 的神奇的不是借助于代码生成来实现的,而是通过条件注解来实现的,这是 Spring 4.x 提供的新特性。Spring 4.x 提倡使用 Java 配置和注解配置组合,而 Spring Boot 不需要任何 xml 配置即可实现 Spring 的所有配置。

 

 

Spring Boot的优缺点

1)优点

  • 快速构建项目。
  • 对主流开发框架的无配置集成。
  • 项目可独立运行,无须外部依赖Servlet容器。
  • 提供运行时的应用监控。
  • 极大地提高了开发、部署效率。
  • 与云计算的天然集成。

2)缺点

  • 版本迭代速度很快,一些模块改动很大。
  • 由于不用自己做配置,报错时很难定位。
  • 网上现成的解决方案比较少。

 

 

pom.xml中引入依赖

  • 按Alt+ins选择dependency然后搜搜包导入即可,但也可以取maven官网搜搜然后复制dependency结构就行。

 

SpringBoot详细笔记记录_第1张图片

注解详情:

  • @Data 注解的主要作用是提高代码的简洁,使用这个注解可以省去代码中大量的get()、 set()、 toString()等方法;
  • @AllArgsConstructor //提供所有参数的构造器
  • @NoArgsConstructor //提供无参数的构造器吧
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data  //使用date注解后不需在下面定义set和get方法,可以直接调用get和set还有toString方法
@AllArgsConstructor //提供所有参数的构造器
@NoArgsConstructor //提供无参数的构造器吧
public class User
{
    private int id;
    private String name;
    private int age;
    private String qq;
    private String address;
}
package com.com.heyexi.controller;
import com.heyexi.entity.*;

public class Test
{
    public static void main(String[] args)
    {
        User zhangsan = new User();
        zhangsan.setAddress("云南昆明");
        zhangsan.setAge(18);
        System.out.println(zhangsan.toString());

        User lisi = new User(1,"李四",18,"18548787","北京");
        System.out.println(lisi.toString());

    }
}

@Configuration 注解相当于一个spring的xml文件配置,通常和@Bean注解一起使用,@Bean注解是代表一个JavaBean。

package com.heyexi.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.heyexi.entity.*;

@Configuration //相当于是一个xml配置文件
public class JavaConfigA
{
    @Bean
    public User getUser()
    {
        return new User(1,"李四",22,"14874","杭州");
    }
}

SpringBoot的核心:自动装配

注意:包必须建在主程序入口同级目录,不然无法识别

 

SpringBoot详细笔记记录_第2张图片

第一个HelloWorld

 

package com.heyexi.helloworld.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class Hello
{
    //http://localhost:8080/hello  这个是接口
    @RequestMapping("/hello")
    public String hello()
    {
        return "hello,world";
    }
}
  • 其中在Spring中@RestController的作用等同于@Controller + @ResponseBody。@ResponseBody的作用其实是将java对象转为json格式的数据。
  • @RequestMapping的作用其实和tp5中的Route也就是路由规则是一样的。

SpringBoot详细笔记记录_第3张图片

在应用配置中可以修改端口号

SpringBoot详细笔记记录_第4张图片

启动器:这些启动器会自动引用需要的配置,比如spring-boot-starter-web就是负责启动web环境,自动启动Tomcat,达到了自动装配的功能。


    org.springframework.boot
    spring-boot-starter-web

SpringBoot使用一个全局的配置文件,配置文件名也是固定的:

application.properties 或者是application.yaml

 

 

yaml语法:

key: 值 ----->>>> 冒号后面必须跟一个空格

# 普通
name: 何夜息

# 对象
Student:
  name: zhangsan
  age: 18

# 行内写法
Teacher: {name: kingsley,age: 20}

# 数组
books: [家,春,秋,飘,挪威的森林]

进入Web开发

 

  • 静态资源的访问:resources目录下的resources、public、static下的文件都能被识别到,可以直接通过根目录来访问。http://localhost:8080/static.js 优先级顺序为:resources > static > public

SpringBoot详细笔记记录_第5张图片

SpringBoot详细笔记记录_第6张图片

首页的设定

templates是模板目录,这里的文件不能直接被访问,只能通过控制器来访问。

  • index.xxx文件可以放在resources目录下的resources、public、static任意目录中,都能被识别到,一般放在public中。

 

模板引擎

模板引擎:模板的诞生是为了将显示与数据分离,模板技术多种多样,但其本质是将模板文件和数据通过模板引擎生成最终的HTML代码。

简单来说,就是模板文件,也就是PHP中的html文件,里面有些数据绑定的地方,我们使用{$xxx}来标记,这就是前端页面。而需要的数据则来自后台,然后通过模板引擎,把这个模板文件和数据重新组合,得到最终的html文档显示给用户,这就是模板引擎的功能。

SpringBoot详细笔记记录_第7张图片

用途:

模板引擎可以让(网站)程序实现界面与数据分离,业务代码与逻辑代码的分离,这就大大提升了开发效率,良好的设计也使得代码重用变得更加容易。

 

遇到的小问题,浏览器调用“test”时没有跳到模板文件test.html中

 

@RestController
public class TestController
{
    @RequestMapping("/test")
    public String test()
    {
        return "test";
    }
}

原因是@RestController返回的是json对象,而不是跳到模板文件。需要改为

@Controller
public class TestController
{
    @RequestMapping("/test")
    public String IsTest()
    {
        return "test";
    }
}

为什么能跳到test.html文件中呢?不是和这个类的名字有关,和tp5一样,和return后面的字符串有关,会吧"test"字符串自动通过thymeleaf渲染成"test.html",这个文件会去"templates"目录中去找。

 

要使用thymeleaf模板引擎,需要在pom.xml中导入thymeleaf的依赖。


    org.springframework.boot
    spring-boot-starter-thymeleaf

thymeleaf语法学习

 

  1. 要想在html页面中使用thymeleaf语法,需要引入:
  1. 绑定字符串
  2. 控制器中:
    @Controller
    public class TestController
    {
        @RequestMapping("/test")
        public String IsTest(Model model)
        {
            model.addAttribute("name","何夜息");
            return "test";
        }
    }
    
    模板文件中:
    

     

  3. 遍历使用th:each
  4. LinkedList list = new LinkedList<>();
    list.add("何夜息");
    list.add(99);
    list.add(true);
    model.addAttribute("user",list);
    
    可以一次遍历:
    

    也可以取单个值:

     

     

    1. 访问链接使用th:herf="@{/地址}" 或者 th:src="@{/地址}"

     

    使用thymeleaf抽取公共部分

     

    • 网站的有些东西,比如导航栏,底部,侧边栏都是一样了,为了一改多改,我们可以使用thymeleaf的fragment来达到效果。
    • 首先我们帮公共部分复制到单独的一个页面,比如common.html,然后在页面上写上th:fragment="名称",原理就是其他页面可以通过这个名称,直接复制这个标签里面的所有代码。

    SpringBoot详细笔记记录_第8张图片

    • 然后我们从首页获取它:使用th:insert="~{xxx::yyy}",其中XXX代表的是要复制的网页的名字,这个就是代表view目录下common.html下的view_header标签。

    • 效果如下:

    SpringBoot详细笔记记录_第9张图片

    获取请求参数:

    • 当Form表单提交请求后,我们为了获取表单组件的值,需要使用@RequestParam注解来获取值,它的作用就是将请求参数绑定到控制器的方法参数上。

     

    @RequestMapping("/hd_login")
    public String hd_login(@RequestParam("uname") String name, @RequestParam("pwd") String pwd, Model model)
    {
        if(!name.isEmpty() && !pwd.isEmpty())
        {
            model.addAttribute("name",name);
            model.addAttribute("pwd",pwd);
        }else {
            model.addAttribute("name","空空如也");
            model.addAttribute("pwd","nothing");
        }
        return "index";
    }

    SpringBoot详细笔记记录_第10张图片

     

    WebMvcConfigurer学习

     

    • 什么是WebMVCConfigure呢?其实它是一个接口,实现这个接口能够实现里面的一些重要的方法。一般在config包中配置它,然后实现我们需要用到的接口方法。

     

    • 常用的方法:
    /* 拦截器配置 */
    void addInterceptors(InterceptorRegistry var1);
    /* 视图跳转控制器 */
    void addViewControllers(ViewControllerRegistry registry);
    /***静态资源处理**/
    void addResourceHandlers(ResourceHandlerRegistry registry);
    /* 默认静态资源处理器 */
    void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer);
    /*** 这里配置视图解析器**/
    void configureViewResolvers(ViewResolverRegistry registry);
    /* 配置内容裁决的一些选项*/
    void configureContentNegotiation(ContentNegotiationConfigurer configurer);
    /** 解决跨域问题 **/
    public void addCorsMappings(CorsRegistry registry) ;
    • 先来了解下 视图跳转控制器:addViewControllers 这个方法作用是实现一个路径自动跳转到一个页面,简单来说:当地址为***时,跳转到***的页面。例如我们让当地址为"/nice"时,自动跳转到"/login"页面。
    @Configuration
    public class MyWebMvcConfigurer implements WebMvcConfigurer
    {
        @Override
        public void addViewControllers(ViewControllerRegistry registry)
        {
            registry.addViewController("/nice").setViewName("/login");
        }
    }

    SpringBoot详细笔记记录_第11张图片

    • 再来了解下 拦截器:以前做PHP,假设有2个页面,A页面和B页面,为了禁止用户直接访问B页面,我都是弄一个验证,可以理解为对称加密。也就是A页面随机产生验证码,然后保存在Cookie或session中,然后在B页面中验证是否有这个私钥,有才能访问,然后注销这个Cookie,相当于一把钥匙开一把锁。然后了解了下这个SpringBoot的拦截器,原理差不多,都是验证session获取其他缓存,但是这个可以指定哪些页面不能被访问,可以设置一个拦截器,只有通过拦截器了,才能获得拦截器的“放行”,才能访问,就不需要一对一的设置,直接拦截,还是很强大的。
    • 那么如何实现呢?
    1. 首先需要定义一个拦截器,这个拦截器需要实现HandlerInterceptorAdapter这个接口,比如定义一个登陆拦截器。
    2. 这个接口只有三个方法,重写perHandle方法就可以了。

     

    SpringBoot详细笔记记录_第12张图片

    public class LoginInterceptor implements HandlerInterceptor
    {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
        {
            return false;
        }
    }
    1. 接着就需要写拦截规则。一般是用户登陆成功后,设置用户session,那么久获取session就行,有就返回true,代表放行,没有就设置false,代表拦截。
    public class LoginInterceptor implements HandlerInterceptor
    {
        @Override
        public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception
        {
            //获取用户session
            Object user = request.getSession().getAttribute("user");
    
            if(user==null)
            {
                request.setAttribute("msg","登录失败!");
                request.getRequestDispatcher("{/index.html}").forward(request,response);
                return false;
            }else {
                return true;
            }
        }
    }

    如上:request.getRequestDispatcher("{/index.html}").forward(request,response);是代表跳转页面,getRequestDispatcher是代表要跳转的网址,forward()是代表继续转发或者说是携带request,response中的数据给转发后的网址继续处理。

    1. 配置好拦截器规则后,为了让拦截器生效,需要添加到WebMvcConfigurer中进行生效。
    @Configuration
    public class MyWebMvcConfigurer implements WebMvcConfigurer
    {
        @Override
        public void addViewControllers(ViewControllerRegistry registry)
        {
            registry.addViewController("/nice").setViewName("/login");
        }
    
        @Override
        public void addInterceptors(InterceptorRegistry registry)
        {
            registry.addInterceptor(new LoginInterceptor())
                    .addPathPatterns("/test").excludePathPatterns("/login");
        }
        //这其中呢,addPathPatterns是代表要拦截的页面,也就是说,拦截器没有放行之前
        //这个页面是无法进行访问,只有放行后才能访问。
        //excludePathPatterns("/login");方法代表除了...,也就是这个页面可以访问。
    }

    处理404页面

     

    • 这个很简单,只需要在templates目录或者public目录下再创建一个error目录,然后在这个目录里面建一个404.html文件就行了,当访问的页面不存在时会自动跳转该页面。

    SpringBoot详细笔记记录_第13张图片

    整合mybatis

     

    • 去maven官网搜mybatis,复制依赖
    • 导入到pom.xml中就行了

    SpringBoot详细笔记记录_第14张图片

     

    处理Ajax请求

     

    • 这里举个注册的例子,前端请求创建一个用户,那么后台我们需要根据用户注册的用户名,判断一下该用户名是否存在,以下是写简单的请求:

     

    function login() {
        if(check_regist()) {
            var uname = document.getElementById("user_name").value;
            var pwd = document.getElementById("pwd").value;
            $.ajax({
                url:'/adduser',
                type:'post',
                data:{name:uname,pwd:pwd},
                success:function (res) {
                },
                error:function (res) {
                    alert("获取数据失败!");
                }
            });
        }
    }

    然后在控制器里我写了如下:

     

    @RestController
    public class AddUserController
    {
        @Autowired
        private Users user;
    
        @Autowired
        private UserDao userDao;
    
        @PostMapping("/adduser")
        @ResponseBody
        public Object adduser(@RequestParam("name") String name,@RequestParam("pwd") String pwd)
        {
            //先查询是否存在用户
            user = userDao.findUser(name);
            if(user != null)
            {
                System.out.println(user.getU_name());
                System.out.println("用户存在");
            }else {
                System.out.println("用户不存在");
            }
            return user;
        }
    • 这里我们为了给前端返回一个json对象,SpringBoot的做法是,可以把返回的实体对象User通过@ResponseBody注解可以转为为json对象。
    • 然后测试一下:

    SpringBoot详细笔记记录_第15张图片

     SpringBoot详细笔记记录_第16张图片

    • 可以看到获取用户成功了!然后就是在前端处理这个json了。
    • 我们可以看到前端确实接受到了json对象。

    SpringBoot详细笔记记录_第17张图片

    遇到问题:

    今天遇到一个SQL问题:org.springframework.dao.EmptyResultDataAccessException: Incorrect result size: expected 1, actual 0

    问题出在这句代码上:

    Map map = jdbcTemplate.queryForMap(sql);

    这是查询数据记录的,后来百度了才知道,jdbcTemplate的queryForMap方法是查询数据中一条记录的,而且这条记录必须存在!不然就报错了,又是一个坑!

    Incorrect result size: expected 1, actual 0 -》 不准确的结果大小,预期是1,结果是0,代表了返回为空,这个必须返回是1,如果有多条记录机会变成 Incorrect result size: expected 1, actual x,其中x就是代表返回的结果数。

     

    解决办法就是老老实实用如下代码

     

    try
    {
        SqlRowSet res = jdbcTemplate.queryForRowSet(sql);
        while (res.next())
        {
            user.setU_id(res.getInt("u_id"));
            user.setU_name(res.getString("u_name"));
            user.setU_gender(res.getInt("u_gender"));
            user.setU_wxid(res.getString("u_wxid"));
            user.setU_pwd(res.getString("u_pwd"));
            user.setU_rdate(String.valueOf((res.getString("u_rdate"))));
            return user;
        }
        return null;
    }catch (Exception e)
    {
        e.printStackTrace();
        return null;
    }

    又遇坑:

    • 这个坑是在写松松过项目时,在视图跳转控制器中遇到的,看如下代码,我是想到网站根目录是自动跳到首页,setViewName("/index");里面我设置的是首页的路由,然后一直提示找不到模板,第二天才发现后面写的不是控制器的(路由)的地址,而是模板文件的具体地址。
    @Override
    public void addViewControllers(ViewControllerRegistry registry)
    {
        registry.addViewController("/").setViewName("/index");
    }
    
    • 然后我改成了如下代码就行了。
    @Override
    public void addViewControllers(ViewControllerRegistry registry)
    {
        registry.addViewController("/").setViewName("/view/index");
    }

    注意:SpringBoot中控制器返回页面,页面必须还有thymeleaf的引用,不然普通网页的话,控制器不能跳转到改页面。

     

    又是记录bug的一天:在Windows下控制器的跳转页面可以写成return "/view/**",但是发布到linux服务器却出现了问题,所以应该写成"return "view/**",也就是吧前面的/去掉,可以是路径的问题吧。还有就是前端问题,有一个标签是textarea文本域标签,如果想显示placeholder内容,这个标签之间不能有内容,因为它会把里面的空格也当做textarea的内容。

     

     

     

     

     

    你可能感兴趣的:(java)