Hadoop大数据招聘网数据分析综合案例
- Hadoop大数据综合案例1-Hadoop2.7.3伪分布式环境搭建
- Hadoop大数据综合案例2-HttpClient与Python招聘网数据采集
- Hadoop大数据综合案例3-MapReduce数据预处理
- Hadoop大数据综合案例4-Hive数据分析
- Hadoop大数据综合案例5-SSM可视化基础搭建
- Hadoop大数据综合案例6–数据可视化(SpringBoot+ECharts)
Spring Boot 简介
由于Spring是一个轻量级的企业开发框架,主要的功能就是用于整合和管理其他框架。但随着整合的框架越来越多,Spring的整合配置也日益繁琐,一度被人认为“配置地狱”。随着Spring 3.0的发布,Spring IO团队逐渐开始摆脱XML配置文件,并且在开发过程中大量使用约定优先配置的思想来摆脱Spring框架中各类繁复纷杂的配置(即时是Java Config)。
SpringBoot 正是在这样的一个背景下被抽象出来的开发框架,它本身并不提供Spring框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于Spring框架的应用程序。也就是说,它并不是用来替代Spring的解决方案,也不是对Spring功能上的增强,而是提供了一种快速使用Spring的方式。同时它集成了大量常用的第三方库配置(例如Jackson, JDBC, Mongo, Redis, Mail等等),Spring Boot应用中这些第三方库几乎可以零配置的开箱即用(out-of-the-box),大部分的Spring Boot应用都只需要非常少量的配置代码,开发者能够更加专注于业务逻辑。
SpringBoot 的核心功能
起步依赖
起步依赖本质上是一个Maven项目对象模型(Project Object Model,POM),定义了对其他库的传递依赖,这些东西加在一起即支持某项功能。
简单的说,起步依赖就是将具备某种功能的坐标打包到一起,并提供一些默认的功能。
<!-- 继承默认值为Spring Boot -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
</parent>
应用启动器
名称 | 描述 |
---|---|
spring-boot-starter-jdbc | 使用 JDBC 与 Tomcat JDBC 连接池 |
spring-boot-starter-thymeleaf | 使用Thymeleaf 视图构建MVC Web 应用程序的入门者 |
spring-boot-starter-test | 支持常规的测试依赖,包括 JUnit 以及 spring-test 模块 |
spring-boot-starter-web | 支持全栈式 Web 开发,包括 Tomcat 和 spring-webmvc |
spring-boot-starter-data-jpa | 使用 Spring 数据 JPA 与 Hibernate 的启动器 |
mybatis-spring-boot-starter | 提供了MyBatis 与Spring 整合所需配置及依赖项 |
druid-spring-boot-starter | 提供了Druid 数据源的整合配置项 |
mybatis-plus-boot-starter | MyBatisPlus 框架整合了MyBatis 与Spring 相关依赖和配置项 |
自动配置
SpringBoot
在进行 SpringApplication
对象实例化时会加载 META-INF/spring.factories
文件,根据 pom.xml
中加入的坐标,考虑众多因素, 将该配置文件中需要的配置信息载入到 Spring
容器,完成自动配置,该过程是 Spring
自动完成的.
SpringBoot 常用注解
注解名称 | 描述 |
---|---|
@SpringBootApplication | 组合注解,包含了@ComponentScan 、@Configuration 和@EnableAutoConfiguration 注解。 |
@ConfigurationProperties | 方法注解,用于获取核心配置文件中对应以什么开头的多个属性,并自动注入到对象中,需要提供 getting ,setting |
@SpringBootConfiguration | 类注解,与@Configuration 一样,用于区分SpringBoot配置类和Spring配置类 |
关闭某一项的自动配置:
@SpringBootApplication(exclude={RedisAutoCOnfiguration.class})
快速入门
传统基于Spring的Java Web应用,需要配置Web启动器
, SpringIOC容器
,SpringMVC启动配置
,将应用打成war包放入应用服务器Tomcat
中并运行。如果基于Spring Boot,这一切都将变得简单:访问Spring官网提供的项目构建页面:Spring Initializr
把下载后的压缩包解压到项目存放地址,通过IDEA导入到项目工程中,注意一定要选择Maven方式导入。如下图所示:
导入后,IDEA会进行Maven依赖包下载,要等一会,默认生成的项目架构有很多没用的东西需要删除。如下图所示:
修改核心配置文件指定数据源信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.43.134:3306/jobdata
spring.datasource.username=root
spring.datasource.password=root
编写MyBatis整合配置类
@SpringBootConfiguration
@MapperScan(basePackages = "com.example.springboot.mapper")
public class MyBatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean();
factoryBean.setDataSource(dataSource);
return factoryBean;
}
}
编写entity实体映射类
public class CityCountEntity {
private String city;
private int count;
public CityCountEntity() {}
public String getCity() {
return city;
}
public void setCity(String city) {
this.city = city;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "CityCountEntity{" +
"city='" + city + '\'' +
", count=" + count +
'}';
}
}
编写MapperSQL操作类
public interface ICityCountMapper {
@Select("select * from t_city_count ")
List<CityCountEntity> list();
}
编写Controller控制器类
@RestController
@RequestMapping("/city")
public class CityCountController {
@Autowired
private ICityCountMapper cityCountMapper;
@GetMapping("/list")
public List<CityCountEntity> list(){
return cityCountMapper.list();
}
}
启动SpringBoot应用
在浏览器中访问测试
SpringBoot访问静态资源
@SpringBootConfiguration
@EnableWebMvc
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//第一个方法设置访问路径前缀,第二个方法设置资源路径
registry.addResourceHandler("/**").addResourceLocations("classpath:/static/");
}
@Override
public void addViewControllers(ViewControllerRegistry registry) {
// 实现无业务逻辑跳转,减少控制器代码的编写
registry.addRedirectViewController("/","/index.html");
}
}
SpringBoot静态资源存放于
static目录中
,可以通过上述代码进行修改。
浏览器访问http://localhost:8080/ 会自动重定向到 http://localhost:8080/index.html 页面下,访问效果如下:
ECharts简介
ECharts ,一个使用 JavaScript 实现的开源可视化库,可以流畅的运行在 PC 和移动设备上,兼容当前绝大部分浏览器(IE8/9/10/11,Chrome,Firefox,Safari等),提供直观,交互丰富,可高度个性化定制的数据可视化图表。
- 数据可视化主要目的:借助于图形化手段,清晰有效地传达与沟通信息。
- 数据可视化可以把数据从冰冷的数字转换成图形,揭示蕴含在数据中的规律和道理。
入门案例
参考官方文档,入门案例编写一个简单的柱形图,代码如下所示:
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>ECharts入门案例</title>
<script src="lib/echarts.min.js"></script>
</head>
<body>
<!--
1. 下载并引入 echarts.min.js文件
2. 准备一个呈现图表的容器,需要有大小
3. 初始化echarts实例对象
4. 准备配置项
5. 将配置项设置给echarts实例对象
-->
<div id="main" style="width: 600px;height: 400px"></div>
<script>
var myECharts = echarts.init(document.querySelector("#main"));
var option = {
title: {text: '柱形图示例'},
tooltip: {},
legend: {
data:['销量']
},
xAxis: {
data: ["衬衫", "羊毛衫", "雪纺衫", "裤子", "高跟鞋", "袜子"]
},
yAxis: {},
series: [{
name: '销量',
type: 'bar',
data: [5, 20, 36, 10, 10, 20] // y轴数据
}]
};
myECharts.setOption(option);
</script>
</body>
</html>
除了配置项会变化之外,其它的代码都是固定的。配置项的学习和使用参考官方文档和实例
ECharts 基础概念概览
echarts 实例
一个网页中可以创建多个 echarts 实例
。每个 echarts 实例
中可以创建多个图表和坐标系等等(用 option
来描述)。准备一个 DOM 节点(作为 echarts 的渲染容器),就可以在上面创建一个 echarts 实例。每个 echarts 实例独占一个 DOM 节点。
系列(series)
在 echarts 里,系列
(series)是指:一组数值以及他们映射成的图。一个 系列
包含的要素至少有:一组数值、图表类型(series.type
)、以及其他的关于这些数据如何映射成图的参数。
常用的系列类型(series.type
):line(折线图)、bar(柱状图)、pie(饼图)、map(地图)、radar(雷达图)、gauge(仪表盘)
组件(component)
在系列之上,echarts 中各种内容,被抽象为“组件”。echarts 中有这些组件:xAxis(直角坐标系 X 轴)、yAxis(直角坐标系 Y 轴)、dataZoom(数据区缩放组件)、tooltip(提示框组件)、toolbox(工具栏组件)、series(系列)、…
使用dataset 管理数据
Apache ECharts 4 开始支持了 dataset
组件用于单独的数据集声明,从而数据可以单独管理,被多个组件复用,并且可以基于数据指定数据到视觉的映射。
option = {
legend: {},
tooltip: {},
dataset: {
// 用 dimensions 指定了维度的顺序。直角坐标系中,默认把第一个维度映射到 X 轴上,第二个维度映射到 Y 轴上。
dimensions: ['product', '2015', '2016', '2017'],
source: [
{product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7},
{product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1},
{product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5},
{product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1}
]
},
xAxis: {type: 'category'},
yAxis: {},
series: [
{type: 'bar'},
{type: 'bar'},
{type: 'bar'}
]
};
异步加载数据
var myChart = echarts.init(document.getElementById('main'));
// 显示标题,图例和空的坐标轴
myChart.setOption({
title: {text: '异步数据加载示例'},
tooltip: {},
dataset: {
dimensions: ['product', '2015', '2016', '2017'],
source: [
{product: 'Matcha Latte', '2015': 43.3, '2016': 85.8, '2017': 93.7},
{product: 'Milk Tea', '2015': 83.1, '2016': 73.4, '2017': 55.1},
{product: 'Cheese Cocoa', '2015': 86.4, '2016': 65.2, '2017': 82.5},
{product: 'Walnut Brownie', '2015': 72.4, '2016': 53.9, '2017': 39.1}
]
},
xAxis: {type: 'category'},
yAxis: {},
series: [{type: 'bar'}]
});
// 异步加载数据
$.get('data.json').done(function (data) {
// 填入数据
myChart.setOption({
dataset: {
source: data
}
});
});
招聘数据可视化
后台数据接口编写
编写Mapper查询所有数据
由于招娉数据可视化业务逻辑比较简单,只需要从数据库中查看出来,转换为JSON数据,并提供访问接口即可。而且表结构基本一致,这里就不在单独定义每一个数据库表对象实体,只需要提供一个统一的响应实体即可。
public class ResponseData {
private String name;
private int count;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
Mapper操作单一,都是全部查询出即可,这里也不再单独编写Mapper接口,统一在一个Mapper编写。这里需要注意,由于返回实体的属性名和数据库字段名不一致,MyBatis无法成功映射,需要编写ResultMap或使用字段别名进行指定。
public interface ILaGouMapper {
@Select("select city as name,count from t_city_count order by count desc limit 10 ")
List<ResponseData> listCity();
@Select("select company as name,count from t_company_count ")
List<ResponseData> listCompany();
@Select("select kills as name,count from t_kill_count order by count desc limit 5 ")
List<ResponseData> listKill();
@Select("select salary as name,count from t_salary_dist ")
List<ResponseData> listSalary();
}
编写Controller获取请求和响应JSON数据
@RestController
@RequestMapping("/lagou")
public class LaGouController {
@Autowired
private ILaGouMapper laGouMapper;
@GetMapping("/city")
public List<ResponseData> listCity(){
return laGouMapper.listCity();
}
@GetMapping("/company")
public List<ResponseData> listCompany(){
return laGouMapper.listCompany();
}
@GetMapping("/kill")
public List<ResponseData> listKill(){
return laGouMapper.listKill();
}
@GetMapping("/salary")
public List<ResponseData> listSalary(){
return laGouMapper.listSalary();
}
}
启动Boot服务,使用浏览器进行代码测试
前端数据图表展示
使用CSS Flex布局
Flex 布局,可以简便、完整、响应式地实现各种页面布局。目前,它已经得到了所有浏览器的支持。
Flex
是 Flexible Box
的缩写,意为"弹性布局",用来为盒状模型提供最大的灵活性。任何一个容器都可以指定为 Flex
布局。基本语法如下:
.box{
display: inline-flex; /*行内元素使用方式*/
display: flex; /*块状元素使用方式*/
}
使用方式:就是通过给父盒子添加flex
属性,来控制子盒子的位置和排列方式。
属性 | 说明 | 属性值 |
---|---|---|
flex-direction | 容器内项目的排列方向 | row 、row-reverse 、column 、column-reverse |
flex-wrap | 容器内项目换行方式 | nowrap 、wrap |
justify-content | 项目在水平的主轴的对齐方式 | flex-start 、flex-end 、center 、space-around 、space-between |
align-items ** ** | 项目在垂直的侧轴的对齐方式 | stretch (子元素未设置高度生效) 、flex-start 、flex-end 、center |
align-content | 定义多根轴线的对齐方式,分块布局 多个 flex 容器 | stretch ****、flex-start 、flex-end 、center 、space-between 、space-around |
当前招聘网有4个数据,这里规划四个区域进行数据放置。
<!DOCTYPE html>
<html lang="zh_CN">
<head>
<meta charset="UTF-8">
<title>拉勾网招聘数据可视化</title>
<style>
#box{
display: flex;
flex-wrap: wrap;
justify-content: space-between;
align-content: space-between;
height: 740px;
}
#box>div{
border: 1px gray dashed;
width: 47%;height: 44%;
padding: 15px;
}
</style>
</head>
<body>
<div id="box">
<div class="city">城市岗位需求</div>
<div class="salary">薪资分布</div>
<div class="skill">技能要求</div>
<div class="company">公司福利</div>
</div>
</body>
</html>
选择合适的图表模板填充
在resources/static/js
目录下加入echarts.js
、echarts-wordcloud.js
、jquery.min.js
和 index.js
文件,用于图表制作、Ajax异步请求发送和自定义JS图表代码。
$(function(){
// 城市岗位柱形图
let cityChart = echarts.init(document.querySelector(".city"));
cityChart.setOption({
title: {text: '岗位城市分布'},
xAxis: {
type: 'category',
data: ['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun']
},
yAxis: {
type: 'value'
},
series: [{
data: [120, 200, 150, 80, 70, 110, 130],
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}]
});
// 薪资分布条形图
let salaryChart = echarts.init(document.querySelector(".salary"));
salaryChart.setOption({
title: {text: '岗位薪资分布'},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {
type: 'category',
data: ['巴西', '印尼', '美国', '印度', '中国', '世界人口(万)']
},
series: [
{
name: '2011年',
type: 'bar',
data: [18203, 23489, 29034, 104970, 131744, 630230]
}
]
});
// 技能要求数图
let killChart = echarts.init(document.querySelector(".kill"));
killChart.setOption({
title: {text: '岗位技能要求'},
legend: {
right: 'right',
},
series: [
{
name: '面积模式',
type: 'pie',
radius: [20, 120],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 8
},
data: [
{value: 40, name: 'rose 1'},
{value: 38, name: 'rose 2'},
{value: 32, name: 'rose 3'},
{value: 30, name: 'rose 4'},
{value: 28, name: 'rose 5'},
{value: 26, name: 'rose 6'},
{value: 22, name: 'rose 7'},
{value: 18, name: 'rose 8'}
]
}
]
});
// 公司福利词云图
let companyChart = echarts.init(document.querySelector(".company"));
companyChart.setOption({
title: {text: '世界人口总量'},
tooltip: {},
series: [{
type: 'wordCloud',
gridSize: 2,
sizeRange: [12, 50],
rotationRange: [-90, 90],
shape: 'cloud',
width: '160%',
height: '120%',
drawOutOfBound: true,
textStyle: {
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255)
].join(',') + ')';
}
},
emphasis: {
textStyle: {shadowBlur: 10, shadowColor: '#333'}
},
data: [
{name: 'Sam S Club', value: 10000},
{name: 'Macys', value: 6181},
{name: 'Amy Schumer', value: 4386},
{name: 'Jurassic World', value: 4055},
{name: 'Charter Communications', value: 2467},
{name: 'Chick Fil A', value: 2244},
{name: 'Planet Fitness', value: 1898},
{name: 'Pitch Perfect', value: 1484},
{name: 'Express', value: 1112},
{name: 'Home', value: 965},
{name: 'Johnny Depp', value: 847},
{name: 'Lena Dunham', value: 582},
{name: 'Lewis Hamilton', value: 555},
{name: 'KXAN', value: 550},
{name: 'Mary Ellen Mark', value: 462},
{name: 'Farrah Abraham', value: 366},
{name: 'NCAA baseball tournament', value: 273},
{name: 'Rita Ora', value: 360},
{name: 'Serena Williams', value: 282},
{name: 'Point Break', value: 265}
]
}]
});
});
使用Ajax进行异步请求,替换数据
$(function(){
// 城市岗位柱形图
let cityChart = echarts.init(document.querySelector(".city"));
cityChart.setOption({
title: {text: '城市岗位需求前10名'},
tooltip: {
trigger: 'axis',
axisPointer: { // 坐标轴指示器,坐标轴触发有效
type: 'shadow' // 默认为直线,可选为:'line' | 'shadow'
}
},
xAxis: {type: 'category'},
yAxis: {type: 'value'},
dataset: {
// 用 dimensions 指定了维度的顺序。直角坐标系中,默认把第一个维度映射到 X 轴上,第二个维度映射到 Y 轴上。
dimensions: [],
source: []
},
series: [{
type: 'bar',
showBackground: true,
backgroundStyle: {
color: 'rgba(180, 180, 180, 0.2)'
}
}]
});
$.get('/lagou/city').done(function(data){
cityChart.setOption({
dataset: {
dimensions: ['name', 'count'],
source: data
}
});
});
// 薪资分布条形图
let salaryChart = echarts.init(document.querySelector(".salary"));
salaryChart.setOption({
title: {text: '岗位薪资分布'},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'shadow'
}
},
grid: {
left: '3%',
right: '4%',
bottom: '3%',
containLabel: true
},
xAxis: {
type: 'value',
boundaryGap: [0, 0.01]
},
yAxis: {type: 'category'},
series: [
{type: 'bar'}
]
});
$.get('/lagou/salary').done(function(data){
salaryChart.setOption({
dataset: {
dimensions: ['name','count'],
source: data
},
});
});
// 技能要求数图
let killChart = echarts.init(document.querySelector(".kill"));
killChart.setOption({
title: {text: '岗位技能要求前5名'},
tooltip: {
trigger: 'item',
formatter: '{b} : {d}%'
},
legend: {
top: 'bottom'
},
series: [
{
type: 'pie',
radius: [20, 120],
center: ['50%', '50%'],
roseType: 'area',
itemStyle: {
borderRadius: 8
}
}
]
});
$.get('/lagou/kill').done(function(data){
killChart.setOption({
dataset: {
dimensions: ['count','name'],
source: data
}
});
});
// 公司福利词云图
let companyChart = echarts.init(document.querySelector(".company"));
companyChart.setOption({
title: {text: '公司福利待遇情况'},
tooltip: {},
series: [{
type: 'wordCloud',
gridSize: 2,
sizeRange: [12, 50],
rotationRange: [-90, 90],
shape: 'cloud',
width: '160%',
height: '120%',
drawOutOfBound: true,
textStyle: {
color: function () {
return 'rgb(' + [
Math.round(Math.random() * 255),
Math.round(Math.random() * 255),
Math.round(Math.random() * 255)
].join(',') + ')';
}
},
emphasis: {
textStyle: {shadowBlur: 10, shadowColor: '#333'}
},
data: []
}]
});
$.get('/lagou/company').done(function(data){
data = data.map(item=>{return {'name':item.name,'value':Number.parseInt(item.count*1)}});
companyChart.setOption({
series: [{
data: data
}]
});
});
});
最终效果图展示
到此,Hadoop大数据综合案例讲解完毕