笔记:SpringBoot Frist

1.建立项目

生成项目:https://start.spring.io
上面的网站有时候会很慢,进不去可以使用阿里的网站:https://start.aliyun.com/
在这里插入图片描述
使用idea生成项目 File——New——Project,可以看到也是从这个网站下载的模板
在这里插入图片描述
点击Next在这个页面配置相关的环境
在这里插入图片描述
点击Next给项目命名,点击Finish完成
出现问题:
在这里插入图片描述
原因是springboot 和 jdk的版本不同
网站上下载的项目jdk是1.8,idea中使用的jdk是11,将idea中的jdk换成1.8就行
菜单栏 File —— Project Structure
在这里插入图片描述
如果出现大量找不到相关方法的错误,很可能是gradle的依赖没有下载好,建议使用国内阿里的镜像下载
打开build.gradle文件,设置好后重新构建项目

repositories {
	//mavenCentral() 注释掉这一行,这是从国外maven库下载
	maven { url 'https://maven.aliyun.com/repository/gradle-plugin' }
    maven { url 'https://maven.aliyun.com/repository/google' }
    maven { url 'http://maven.aliyun.com/nexus/content/groups/public/' }
    maven { url 'https://maven.aliyun.com/repository/jcenter'}
}

相关依赖

天气请求服务:有两种方式,一种是根据城市的名称,一种是根据城市的id,若没有该城市天气信息返回 {“status”:1002,“desc”:“no data”} json字符串
http://wthrcdn.etouch.cn/weather_mini?city=北京
http://wthrcdn.etouch.cn/weather_mini?citykey=101010100

根据返回的json字符串,建立json的实例对象,注意名字要和json相同,不然后面会转化出错

{
    "data":{
        "yesterday":{
            "date":"12日星期日",
            "high":"高温 22℃",
            "fx":"东北风",
            "low":"低温 16℃",
            "fl":"<![CDATA[3-4级]]>",
            "type":"多云"
        },
        "city":"北京",
        "forecast":[
                {"date":"13日星期一",
                 "high":"高温 24℃",
                 "fengli":"<![CDATA[<3级]]>",
                 "low":"低温 15℃",
                 "fengxiang":"无持续风向",
                 "type":"多云"},
                {"date":"14日星期二",
                 "high":"高温 23℃",
                 "fengli":"<![CDATA[<3级]]>",
                 "low":"低温 16℃",
                 "fengxiang":"无持续风向",
                 "type":"阴"},
                {"date":"15日星期三",
                 "high":"高温 24℃",
                 "fengli":"<![CDATA[<3级]]>",
                 "low":"低温 18℃",
                 "fengxiang":"无持续风向",
                 "type":"阴"},
                {"date":"16日星期四",
                 "high":"高温 26℃",
                 "fengli":"<![CDATA[<3级]]>",
                 "low":"低温 20℃",
                 "fengxiang":"无持续风向",
                 "type":"多云"},
                {"date":"17日星期五",
                 "high":"高温 27℃",
                 "fengli":"<![CDATA[<3级]]>",
                 "low":"低温 21℃",
                 "fengxiang":"无持续风向",
                 "type":"多云"}],
        "ganmao":"各项气象条件适宜,无明显降温过程,发生感冒机率较低。",
        "wendu":"19"
        },
    "status":1000,
    "desc":"OK"
}

接下来就是需要的一些依赖包,在gradle中配置:

	implementation 'org.springframework.boot:spring-boot-starter-web'
    //Httpclient
    implementation 'org.apache.httpcomponents:httpclient:4.5.12'
    // redis
    implementation 'org.springframework.boot:spring-boot-starter-data-redis'
    //Quartz定时器
    implementation 'org.springframework.boot:spring-boot-starter-quartz'
    //Spring Boot Thymeleaf Starter依赖
    implementation 'org.springframework.boot:spring-boot-starter-thymeleaf'
    //测试模块
	testImplementation('org.springframework.boot:spring-boot-starter-test') {
		exclude group: 'org.junit.vintage', module: 'junit-vintage-engine'
	}
    implementation 'junit:junit:4.12'

Model层

WeatherResponse .java

package com.zwt.weather.model;

import java.io.Serializable;

public class WeatherResponse implements Serializable {
    private Weather data;
    private String status;
    private String desc;

    // 省略Getter、Setter和toString方法
}

Weather.java

package com.zwt.weather.model;

import java.io.Serializable;
import java.util.List;

public class weather implements Serializable {
    private Yesterday yesterday;
    private String city;
    private List<Forecast> forecast;
    private String ganmao;
    private String wendu;

    // 省略Getter、Setter和toString方法
}

Yesterday.java

package com.zwt.weather.model;

import java.io.Serializable;

public class Yesterday implements Serializable {
    private String date;
    private String high;
    private String fx;
    private String low;
    private String fl;
    private String type;

   	// 省略Getter、Setter和toString方法
}

Forecast.java

package com.zwt.weather.model;

import java.io.Serializable;

public class Forecast implements Serializable {
    private String date;
    private String high;
    private String fengli;
    private String low;
    private String fengxiang;
    private String type;

    // 省略Getter、Setter和toString方法
}

Service层

之后就是根据外部api获取数据,属于service层的内容
定义接口WeatherDataService.java

package com.zwt.weather.service;

import com.zwt.weather.model.WeatherResponse;

public interface WeatherDataService {
    /**
     * 根据城市的id查询天气数据
     * @param cityId
     * @return com.zwt.weather.model.WeatherResponse
     */
    WeatherResponse getDataByCityId(String cityId);

    /**
     * 根据城市的名称查询天气数据
     * @param cityName
     * @return com.zwt.weather.model.WeatherResponse
     */
    WeatherResponse getDataByCityName(String cityName);
}

定义接口实现类

package com.zwt.weather.service;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.zwt.weather.model.WeatherResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.concurrent.TimeUnit;


/**
 * WeatherDataService实现类
 * @since 2020-4-13
 * @author zwt
 */
@Service
public class WeatherDataServiceImpl implements WeatherDataService{
    private final static Logger logger =  LoggerFactory.getLogger(WeatherDataServiceImpl.class);

    private static final String WEATHHER_UIL = "http://wthrcdn.etouch.cn/weather_mini?";

    @Autowired
    private RestTemplate restTemplate;  // 注入Httpclient客户端,用于http请求

    @Autowired
    private StringRedisTemplate stringRedisTemplate;  // 注入操作Redis的类

    private static final long TIME_OUT = 10800L;  //超时时间设置为3个小时(中国天气网是三个小时一次更新)


    @Override
    public WeatherResponse getDataByCityId(String cityId) {
        /***
         * @Description:
         *    根据城市编号查询天气情况
         * @Return: com.zwt.weather.model.WeatherResponse (json字符串的对象)
         * @Author: zwt
         * @param cityId: 城市的id
         * @Date: 2020-04-13
         */
        //拼接http请求url
        String url = WEATHHER_UIL + "citykey=" + cityId;
        WeatherResponse resp = this.GetWeather(url);
        if (resp == null){
            //TODO
            return null;
        }
        return resp;
    }

    @Override
    public WeatherResponse getDataByCityName(String cityName) {
        /**
         * @Description:
         *    根据城市名称查询天气情况
         * @Return: com.zwt.weather.model.WeatherResponse
         * @Author: zwt
         * @param cityName: 城市的名称
         * @Date: 2020-04-13 
         */
        String url = WEATHHER_UIL + "city=" + cityName;
        WeatherResponse resp = this.GetWeather(url);
        if (resp == null){
            //TODO
            return null;
        }
        return resp;
    }

    private WeatherResponse GetWeather(String url){
        /***
         * @Description:
         *    根据url地址请求天气数据,并将json字符串转换成对象
         * @Return: com.zwt.weather.model.WeatherResponse
         * @Author: zwt
         * @param url: 需要请求的url地址
         * @Date: 2020-04-13
         */
        WeatherResponse resp = null;
        //获取到的json字符串
        String strBody = null;
        //Redis管理
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();
        //先查找缓存,缓存有数据就从缓存中取出
        if(stringRedisTemplate.hasKey(url)){
            logger.info("Redis has data,从Redis获取数据");
            strBody = ops.get(url);
        }else{
            logger.info("Redis has not data,使用http请求数据");
            //没有缓存
            //使用客户端去请求数据
            ResponseEntity<String> respString = restTemplate.getForEntity(url, String.class);
            //判断状态是否请求成功
            if(respString.getStatusCodeValue() == 200){
                strBody = respString.getBody();
                //判断返回的json字符串是否有数据,中华天气网返回json字符串中的status为1000时为正确请求,否则是错误的请求
                if (strBody.indexOf("\"status\":1000")==-1){
                    logger.info("城市查询数据失败");
                    return null;
                }
                //请求成功后写入Redis缓存
                logger.info("数据请求成功写入Redis缓存");
                ops.set(url, strBody, TIME_OUT, TimeUnit.SECONDS);
            }else {
                logger.info("数据请求失败");
                return null;
            }
        }
        ObjectMapper mapper = new ObjectMapper();
        //将json字符串转换成对象
        try{
            resp = mapper.readValue(strBody, WeatherResponse.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return resp;
    }
}

这里对于Httpclient RestTemplate需要做配置

package com.zwt.weather.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.web.client.RestTemplateBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate;

/**
 * Rest配置类
 * @since 2020-4-13
 * @author zwt
 */
@Configuration
public class RestConfiguration {

    @Autowired
    private RestTemplateBuilder builder;

    @Bean
    public RestTemplate restTemplate(){
        return builder.build();
    }
}
出现问题:Redis Windows启动失败

控制台移动到redis文件夹下
输入:redis-server.exe redis.windows.conf
若出现 [10484] 14 Apr 12:17:59.811 # Creating Server TCP listening socket 127.0.0.1:6379: bind: No error
则是启动失败

输入命令:
redis-cli.exe
shutdown
exit
再次输入 redis-server.exe redis.windows.conf

启动成功

Controller 层

WeatherReportController.java

package com.zwt.weather.controller;

import com.zwt.weather.model.WeatherResponse;
import com.zwt.weather.service.WeatherDataService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/report")
public class WeatherReportController {
    @Autowired
    private WeatherDataService weatherDataService;

    @GetMapping("/cityId/{cityId}")
    public ModelAndView getReportByCityId(@PathVariable("cityId") String cityId, Model model){
        WeatherResponse cityWeather = weatherDataService.getDataByCityId(cityId);
        if (cityWeather==null){
            return new ModelAndView("weather/error", "reportModel", model);
        }
        model.addAttribute("title", "天气预报");
        model.addAttribute("cityId", cityId);
        model.addAttribute("citiyList", "");
        model.addAttribute("report", cityWeather.getData());
        return new ModelAndView("weather/report", "reportModel", model);
    }
}

定时器Quartz(Utils)

用户请求的时候再去第三方提供的接口拉取,第一个访问的用户会等待较长的时间,不妨做一个定时器每隔一段时间去同步一下数据,用户访问的时候直接从本地拿取就行
gradle配置文件中添加依赖:

//Quartz定时器
implementation ‘org.springframework.boot:spring-boot-starter-quartz’

Quartz分为几大模块:
1、Job
表示一个工作,要执行的具体内容。此接口中只有一个方法
void execute(JobExecutionContext context)
这里使用QuartzJobBean继承于Job,唯一的方法是executeInternal

2、JobDetail
JobDetail表示一个具体的可执行的调度程序,Job是这个可执行程调度程序所要执行的内容,另外JobDetail还包含了这个任务调度的方案和策略。
这里通过JobBuilder建立 .withIdentity(“weatherDataSyncJobDetail”)为这个JobDetail起个名字

3、Trigger代表一个调度参数的配置,什么时候去调。

4、Scheduler代表一个调度容器,一个调度容器中可以注册多个JobDetail和Trigger。当Trigger与JobDetail组合,就可以被Scheduler容器调度了。

从boot2.0开始,增加了对Quartz的自动装配,以前需要自己处理。从boot2.0开始,增加了对Quartz的自动装配,以前需要自己处理。

package com.zwt.weather.utils;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.slf4j.LoggerFactory;
import org.springframework.scheduling.quartz.QuartzJobBean;

/**
 * 天气数据同步
 * @since 2020-4-13
 * @author zwt
 */
public class WeatherDataSyncjob extends QuartzJobBean {
    private static org.slf4j.Logger logger =  LoggerFactory.getLogger(WeatherDataSyncjob.class);

    @Override
    protected void executeInternal(JobExecutionContext context) throws JobExecutionException {
        //免费的第三方接口有限制每天的访问量,如果过多访问可能会封ip,所以这里只打印输出
        Loggers.printInfo(WeatherDataSyncjob.class, "同步天气数据");
        //这里所做的就是service中的http请求在同步到Redis数据库中
        //城市id数据 链接:https://pan.baidu.com/s/15dMmSuh4bGFtUfAw0wRjLQ 提取码:jqw8
    }
}
package com.zwt.weather.config;

import com.zwt.weather.utils.WeatherDataSyncjob;

import org.quartz.*;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * Quartz定时器配置类
 * @since 2020-4-13
 * @author zwt
 */
@Configuration
public class QuartzConfiguration {
    private static final int TIME_OUT = 10800;
//    private static final int TIME_OUT = 5;
    // 定义JobDetail, 把Job的class类型传入。Spring自动处理
    @Bean
    public JobDetail weatherDataSyncJobDetail(){
        return JobBuilder.newJob(WeatherDataSyncjob.class)
        	.withIdentity("weatherDataSyncJobDetail")
        	.storeDurably()
        	.build();
    }
    //定义Trigger, 通过Key关联JobDetail。Spring 自动处理。
    @Bean
    public Trigger weatherDataSyncJonTrigger(){
        SimpleScheduleBuilder schedBuilder = SimpleScheduleBuilder.simpleSchedule()
        	.withIntervalInSeconds(TIME_OUT)
        	.repeatForever();
        return TriggerBuilder.newTrigger()
        	.forJob(weatherDataSyncJobDetail())
        	.withIdentity("weatherDataSyncJobTrigger")
        	.withSchedule(schedBuilder)
        	.build();
    }
}

网页使用的是springboot推荐的Thymeleaf
HTML

<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <!-- 上述3个meta标签*必须*放在最前面,任何其他内容都*必须*跟随其后! -->
    <title>Title</title>
    <!-- Bootstrap -->
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/css/bootstrap.min.css" rel="stylesheet">

</head>
<body>
    <div class="container">
        <div class="row">
            <h3 th:text="${reportModel.title}"></h3>
        </div>
        <div class="row">
            <h1 th:text="${reportModel.report.city}"></h1>
        </div>
        <div class="row">
            <p>
                当前温度:<span th:text="${reportModel.report.wendu}"></span>
            </p>
        </div>
        <div class="row">
            <p>
                温馨提示:<span th:text="${reportModel.report.ganmao}"></span>
            </p>
        </div>
        <div class="row">
            <div  class="col-md-1"></div>
            <div  class="col-md-2 card" th:each="forecast:${reportModel.report.forecast}">
                <div class="card-body">
                    <p class="card-text" th:text="${forecast.date}"></p>
                    <p class="card-text" th:text="${forecast.high}"></p>
                    <p class="card-text" th:text="${forecast.fengli}"></p>
                    <p class="card-text" th:text="${forecast.low}"></p>
                    <p class="card-text" th:text="${forecast.fengxiang}"></p>
                    <p class="card-text" th:text="${forecast.type}"></p>
                </div>
            </div>
            <div  class="col-md-1"></div>
        </div>
    </div>
    <!-- jQuery (Bootstrap 的所有 JavaScript 插件都依赖 jQuery,所以必须放在前边) -->
    <script src="https://cdn.jsdelivr.net/npm/jquery@1.12.4/dist/jquery.min.js"></script>
    <!-- 加载 Bootstrap 的所有 JavaScript 插件。你也可以根据需要只加载单个插件。 -->
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@3.3.7/dist/js/bootstrap.min.js"></script>
</body>
</html>

在这里插入图片描述

mybatis配置

依赖build.gradle:

	//Spring Boot Mybatis
    implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.1.2'
    //mysql驱动
    implementation 'mysql:mysql-connector-java:8.0.19'

测试我就用了以前已经创建的数据库来测试
表结构很简单就只有两个字段,id自动增长
在这里插入图片描述
application.properties

# 数据库配置
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# jdbc:mysql://【数据库ip地址】:【数据库端口】/【数据库名字】?serverTimezone=Asia/Shanghai
spring.datasource.url=jdbc:mysql://localhost:3306/worktest?useUnicode=true&useJDBCCompliantTimezoneShift=true&useLegacyDatetimeCode=false&serverTimezone=Asia/Shanghai
spring.datasource.username=root
spring.datasource.password=root

注意:
在这里插入图片描述
出现这个警告,说明的很清楚,原来驱动包com.mysql.jdbc.Driver被弃用,使用新的com.mysql.cj.jdbc.Driver就可以
在这里插入图片描述

报了一个时区的错误,原因是使用了Mysql Connector/J 6.x以上的版本 MySQL jdbc 6.0 版本以上必须配置 serverTimezone=UTC 参数,但是只有这样配置用java代码插入到数据库时间的时候会出现问题:时间与现在时区的时间不匹配,原因是UTC代表的是全球标准时间 ,但是我们使用的时间是北京时区也就是东八区,领先UTC八个小时,配置成下面的就行

 serverTimezone=Asia/Shanghai

需要文件
一个model、mapper、service、controller
model:一个与数据库表对应的实例

public class Educational {
    private int id;
    private String name;
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

mapper:定义查询语句(注解的方式)

import com.zwt.weather.model.Educational;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface EducationalMapper {
    @Select("SELECT * FROM t_educational WHERE id = #{id}")
    Educational selectEducational(int id);
}

service:注入mapper

@Service
public class EducationalServiceImpl implements EducationalService {

    @Autowired
    private EducationalMapper educationalMapper;

    @Override
    public Educational getEducationalById(int id) {
        return educationalMapper.selectEducational(id);
    }
}

controller:定义访问的路径

@RestController
@RequestMapping("/edu")
public class EducationalController {
    @Autowired
    private EducationalService educationalService;

    @GetMapping("/id/{id}")
    public String getEducational(@PathVariable("id") String id){
        Educational educational = educationalService.getEducationalById(Integer.parseInt(id));
        return "hello" + educational.getId() + "==" + educational.getName();
    }
}

注意在
Application.java中需要配置mybatis的扫描路径

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
@MapperScan("com.zwt.weather.mapper")  // 配置mybatis的扫描路径
public class Application {
    public static void main(String[] args){
        SpringApplication.run(Application.class, args);
    }
}

访问地址就能看到结果:http://localhost:8080/edu/id/3

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值