Day03项目介绍、云服务
3.1云服务的三种模式
- IaaS (基础设施即服务): 基础零配件(内存,硬盘,网络,计算机硬件) — 计算机(mysql,jdk) ----软件
- PaaS(平台即服务):不关注基础设计 计算机(mysql,jdk) ——软件
- SaaS(软件即服务):不开发软件(租用别人的软件)
- 面向企业的saas产品:oa,crm,erp,hrm,财务
- 面向个人的saas产品:在线文档,云盘
3.2项目介绍
- saas-export
- saas模式的货物代理(国际物流)运输的平台
- 工作内容是接受客户的委托完成货物运输有关的环节以节省资本。
- 指在流通领域专门为货物运输需求和运力供给者提供各种运输服务业务的总称,是货主和运力供给者之间的桥梁和纽带
- 国际物流的业务闭环涉及多个方面,业务十分复杂,因此货代平台服务于货代企业,帮助中小企业简单便利的对国际物流全环节进行流程控制。
- 项目重点围绕四个方面:
- 权限管理:对登入系统的员工进行细粒度的权限控制
- 货物管理:提供货物的全流程管理,包含商品详情,报价
- 报运管理:包括购销合同,出口报运,装箱,委托,发票
- 统计管理:以图形化界面的方式对销售,财务数据展示
3.3工程开发流程
- 项目完整架构
- 后端:Spring+springmvc+myatis+dubbo
- 前端:adminLte
- 系统用例图
- 需求调研,静态原型,用例图展示完整需求
- UML统一建模语言:通过一系列的图形展示系统需求
- 用例图(user case) : 展示系统功能,和功能参与人之间的关系
- 前端模板工程AdminLTE
- 搭建前端环境
- 完成企业管理
- 企业列表
- 企业新增
- 企业删除
- 企业修改
3.4代码相关
- 日期处理
- 解决方案1:自定义转换器
- 解决方案2:在实体类上添加注解@DateTimeFormat(pattern=“yyyy-MM-dd”)
- 统一异常处理
- 定义一个异常处理器
- 实现HandlerExceptionResolver接口
- 实现resolveException方法完成页面跳转和错误信息提示)
- 异常处理器需要交给spring容器管理
- 定义一个错误的页面
- 定义一个异常处理器
- 抽取BaseController
- 定义BaseController,抽取公共对象或方法
- 自定义的Controller继承BaseController
Day04分页处理、数据库设计
4.1逻辑删除
- 在数据库中添加一个是否删除的标记字段
- 通过更改标记字段的状态决定是否展示
4.2分页处理
传统分页
- 修改之前的controller代码,添加请求参数(当前页,每页条数)
- 为了方便在页面展示数据,封装PageBean对象
- controller调用service,返回PageBean
- service层(查询总记录数,通过limit完成分页数据列表查询)
- 在页面上解析PageBean
PageHelper插件
-
是国内优秀的基于,mybatis的分页查询。支持主流数据库(mysql),自动的生成查询总记录数和分页语句,PageHelper内置了PageBean对象(PageInfo)
-
使用步骤(简化service和dao层代码开发)
- 在工程中导入PageHelper的依赖(已导入)
- 在mybatis和spring整合的配置文件中配置(数据库方言)
- 在
export_dao
工程中applicationContext-dao.xml中
添加PageHelper的配置
- 在
- 在service中使用PageHelper完成分页
- 设置分页参数:PageHelper.startPage()
- 调用dao查询全部:
- 构造返回值PageInfo
-
PageHelper的执行过程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WA3BYmco-1605668991333)(assets\14.png)]
4.3SaaS软件的数据库设计
-
多租户的数据库设计方法(saas模式的数据库选型)
- 成本
- 数据安全性
- 数据隔离性
-
数据库类型
- 独立mysql服务器
- 共享mysql服务器,独立database
- 共享数据库表
-
三范式
- 第一范式(1NF):确保每一列的原子性(做到每列不可拆分)
- 第二范式(2NF):在第一范式的基础上,非主字段必须依赖于主字段(一个表只做一件事)
- 第三范式(3NF):在第二范式的基础上,消除传递依赖
-
反三范式
- 基于第三范式所调整的,没有冗余的数据库未必是最好的数据库,有时为了提高运行效率,就必须降低范式标准,适当保留冗余数据。
-
数据库建模
- 通过图形化的形式展示表中的字段和关系。通过模型可以自动的生成数据库建表语句。
4.4部门管理
- 开发步骤(环境搭建):
- 编写实体类
- 编写controller
- 编写service和实现类
- 编写dao接口
- 编写映射文件
- 部门分页查询
- 部门保存
- 部门修改
- 部门删除o
Day05模块、权限、Ztree
5.1用户管理
- 搭建环境
- 实体类
- dao接口和映射文件
- service接口和实现类
- controller对象
- 用户列表
- 保存用户
- 更新用户
- 删除用户
5.2角色管理
- 搭建环境
- 角色分页
- 保存角色
- 更新角色
- 删除角色
5.3模块(菜单,资源)管理
- 模块
- 菜单,按钮,超链接等页面资源,需要存储到数据库中
- 模块类型
- ctype:0 一级菜单/1 二级菜单/2按钮/3 链接
- 父子关系
- 为了方便描述父子关系,采用反三范式添加一个父模块名称的字段,方便查询
- 资源对应跳转的URL链接
- curl
- 从属关系
- 0:sass系统内部菜单
- 1:租用企业菜单
- 搭建环境、模块分页、保存模块、更新模块、删除模块
5.4RBAC权限模型
- 基于角色的访问控制。通过在用户和模块之间添加了角色,通过角色间接的控制不同人员的访问权限。
- 用户表
- 角色表
- 模块(菜单)表
- 用户角色中间表
- 角色模块中间表
- 对用户分配角色
- 维护用户和角色的关系(操作中间表)
5.5前端树形框架Ztree
<script>
//1.ztree的设置
var setting = {
check: {
enable: true
},
data: {
simpleData: {
enable: true
}
}
};
//2.ztree的数据
var zNodes =[
{ id:11, pId:1, name:"随意勾选 1-1"},
{ id:111, pId:11, name:"随意勾选 1-1-1"},
{ id:112, pId:11, name:"随意勾选 1-1-2"},
{ id:12, pId:1, name:"随意勾选 1-2"},
{ id:121, pId:12, name:"随意勾选 1-2-1"},
{ id:122, pId:12, name:"随意勾选 1-2-2"},
{ id:1, pId:0, name:"随意勾选 1"}
];
//3.页面初始化的时候,加载ztree树
var zTreeObj;
$(document).ready(function(){
//init方法,初始化ztree树
zTreeObj = $.fn.zTree.init($("#treeDemo"), setting, zNodes); //1.dom域,2、ztree的设置,3、数据
//可以调用zTreeObj对象的方法
zTreeObj.expandAll(true); //true:全部展开,false:折叠
});
/**
* 调用Ztree的API获取到选中节点的数据
*/
function getNodesId() {
//获取所有被勾选节点的集合
var nodes = zTreeObj.getCheckedNodes(true); //true:被勾选数据,false:未被勾选的
for(var i=0;i<nodes.length;i++) {
var node = nodes[i];
console.log(node.id);
}
}
</script>
//展示的位置
<div>
<ul id="treeDemo" class="ztree"></ul>
<button onclick="getNodesId()">保存</button>
</div>
Day06日志管理
6.1对角色分配权限
进入分配页面
- 根据id查询角色,跳转到分配的页面
- 在分配页面中构造ztree
- 页面初始化的时候,发送ajax请求到服务端查询所有的模块
- java代码编写方法接受ajax请求
- 在SpringMVC中,不需要处理json,通过注解配合对象自动转化
- 方法返回值需要通过@ResponseBody修饰
- 通过ztree的API构造ztree树
- 进行数据的回显(默认勾选已有的权限)
执行权限分配
- web页面发送数据
- 点击保存按钮触发js方法
- 调用ztree的API获取所有被勾选的节点对象的数组(构建的节点 id,pid,name)
- 循环数组获取所有的节点id,构建字符串
- 将字符串赋值到对应的input表单上
- 自动提交form表单
- java代码接受数据完成权限分配
6.2用户登录
- 判断邮箱和密码是否为空
- 根据邮箱查询用户
- 判断用户对象的密码和输入密码是否一致
- 使用
new Md5Hash(password,salt,2).toString()
加密
- 使用
- 跳转页面
6.3动态构造菜单
当用户登录成功之后,根据用户的角色,权限等数据查询出此用户可操作的所有模块(菜单),在页面端构造菜单数据
-
数据分析
- 用户类型degree
- 0:SaaS管理员
- 1:企业管理员
- 其他:企业普通员工
- 模块从属belong
- 0:Saas管理的内部菜单
- 1:企业使用的业务菜单
- 不同的用户类型访问不同的模块
- 用户类型degree
-
代码实现
- 在用户登录代码中插入操作权限查询代码,将数据保存到session域中
- 根据belong查询模块
- 根据用户id,多个表联合查询用户的模块列表
-
页面处理
<c:forEach items="${sessionScope.modules}" var="item"> <c:if test="${item.ctype==0}"> <li class="treeview"> <a href="#"> <i class="fa fa-cube"></i> <span>${item.name}</span> <span class="pull-right-container"><i class="fa fa-angle-left pull-right"></i></span> </a> <ul class="treeview-menu"> <c:forEach items="${sessionScope.modules}" var="item2"> <c:if test="${item2.ctype==1 && item2.parentId == item.id}"> <li id="${item2.id}"> <a onclick="setSidebarActive(this)" href="${ctx}/${item2.curl}" target="iframe"> <i class="fa fa-circle-o"></i>${item2.name} </a> </li> </c:if> </c:forEach> </ul> </li> </c:if> </c:forEach>
6.4日志管理
-
将用户的所有请求(执行的controller中的方法),保存到数据库中。
-
通过spring中的AOP的形式对所有controller中的方法进行增强
-
AOP的使用方式(xml + 注解)
-
开启AOP注解的支持
<!--web模块种的Springmvc.xml文件--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
配置切面类
package cn.itcast.web.aspect;//web模块 @Component @Aspect public class LogAspect { @Around(value="execution(* cn.itcast.web.controller.*.*.*(..))") public Object aroud(ProceedingJoinPoint pjp){ //通过反射获取标记对象,内部封装了方法对象Method MethodSignature ms =(MethodSignature)pjp.getSignature(); //获取方法 Method method = ms.getMethod(); //从方法的注解上获取name if(method.isAnnotationPresent(RequestMapping.class)){ //有指定注解 RequestMapping annotation = method.getAnnotation(RequestMapping.class); String name = annotation.name(); log.setAction(name); } //执行被代理对象的方法 return pjp.proceed(); } }
-
Day07shiro
7.1概述
- Apache Shiro是Java的一个安全框架。功能强大,使用简单的Java安全框架,它为开发人员提供一个直观而全面的认证,授权,加密的解决方案。
- 认证(Authentication):认证(用户登录)
- 授权(Authorization) :授权(权限校验)
- 加密:密码比较和加密
- 内部结构
- subject:工具类,负责和shiro交互
- securityManager(安全管理器):shiro的核心,统一调度shiro框架运行
- realm域:和数据库交互
- 密码比较器:在认证时,对密码加密比较
- 过滤器
- shiro中所有的权限处理是通过过滤器的形式配置,内置了10个过滤器。
- anon 匿名过滤器 所有人都可以访问URL
- authc 认证过滤器 登录之后才能访问URL
- perms 权限过滤器 具备某个权限才能访问URL
7.2搭建运行环境
- 引入依赖
- 配置shiro和spring整合
- web项目的
resource/spring/applicationContext-shiro.xml
- web项目的
- 在web项目中创建自定义Realm域
cn.itcast.web.shiro.AuthRealm
- 继承父类AuthorizingRealm
- 实现父类中的两个抽象方法(认证,授权)
- 自定义密码比较器
- 在web项目中创建自定义密码比较器
cn.itcast.web.shiro.CustomCredentialsMatcher
- 继承父类SimpleCredentialsMatcher
- 重写密码比较方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tvbAHqp0-1605668991334)(assets\17.png)]
7.3用户认证
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oM8gY21J-1605668991335)(assets\15.png)]
-
更改LoginController
- 获取subject工具类
- 调用subject的login方法进入shiro登录
- 如果正常执行则登录成功
- 从shiro中获取安全数据(登录用户)
- 将用户和模块列表存入session中
- 如果抛出异常则登录失败
- 捕获并处理异常(跳转到登录页面)
-
在AuthRealm域对象中补充认证方法
/** * 认证方法 * 参数:AuthenticationToken(UsernamePasswordToken) * 用户登录时封装的用户名(邮箱)和密码 * 返回值: * AuthenticationInfo :认证数据 * 1、安全数据(用户对象) * 2、密码(非必须):为了方便操作数据库密码 * 3、realm域名称:当前类名 * 业务: * 1、获取登录输入的用户名和密码 * 2、调用service查询 * 3、判断用户是否存在 * 3.1 用户存在,构造info返回值 * 3.2 用户不存在,返回null,自动抛出异常 * */ protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken authenticationToken) throws AuthenticationException { //1、获取登录输入的用户名和密码 UsernamePasswordToken upToken = (UsernamePasswordToken) authenticationToken; String email = upToken.getUsername(); String password = new String(upToken.getPassword()); //2、调用service查询(根据邮箱查询) User user = userService.findByEmail(email); //3、判断用户是否存在 if(user != null) { //3.1 用户存在,构造info返回值 return new SimpleAuthenticationInfo(user,user.getPassword(),getName()); }else { //3.2 用户不存在,返回null,自动抛出异常 return null; } }
-
在密码比较器中实现密码比较方法
doCredentialsMatch
public boolean doCredentialsMatch(AuthenticationToken token, AuthenticationInfo info) { //1、获取用户登录输入的邮箱密码 UsernamePasswordToken upToken = (UsernamePasswordToken) token; String email = upToken.getUsername(); String password = new String(upToken.getPassword()); //2、获取数据库密码 String dbPassword = (String)info.getCredentials(); //3、对用户输入的密码加密 password = Encrypt.md5(password,email); //4、比较两个密码 return dbPassword.equals(password); } }
-
修改web.xml的servlet配置
- 配置DispatchserSerlver的启动顺序为1
-
修改Logincontroller中logout方法
//退出 @RequestMapping(value = "/logout",name="用户登出") public String logout(){ Subject subject = SecurityUtils.getSubject(); subject.logout(); //shiro的用户退出登录 return "forward:login.jsp"; }
-
过程分析
- 用户在浏览器输入或者点击某个URL
- 通过web.xml中代理过滤器的设置找到spring和shiro整合的配置
- 找符合此链接的过滤器配置(/**=authc)
- authc:登录成功才能访问,内部会自动的判断当前请求是否已经登录
- 如果没有登录:跳转到指定的登录页面(loginUrl)配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vYQyw8ga-1605668991337)(assets\16.png)]
7.4用户授权
-
授权:
- 判断用户权限(权限校验)
- 整个权限判断过程是shiro内部自动完成的。
-
步骤:
-
完成Realm域的授权方法
/** * 授权方法 * 本质:提供操作用户的权限数据(权限名称集合) * 参数:principalCollection(安全数据集合),只能获取唯一的安全数据(User用户对象) * 返回值: * AuthorizationInfo :授权数据(权限名称集合) */ protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principalCollection) { //1、获取当前登录用户(安全数据) User user = (User) principalCollection.getPrimaryPrincipal(); //2、根据当前登录用户,查询此用户的所有模块权限 List<Module> List<Module> modules = moduleService.findByUser(user); //3、提取所有的模块名称:set集合(自动去重) Set<String> permissions = new HashSet<>(); for (Module module : modules) { permissions.add(module.getName()); } //4、构造返回值 SimpleAuthorizationInfo info = new SimpleAuthorizationInfo(); info.setStringPermissions(permissions); return info; }
-
基于XML的权限配置
- 在spring整合shiro的配置文件中通过perms过滤器声明请求URL的权限
- 语法规则: **URL=perms[‘所需权限’]
-
基于注解的权限配置
- 注解配置到请求URL对应的控制器方法上
-
通过注解的形式替代xml的配置
- @RequiresPermissions注解,注解的value属性中执行所需权限
- 使用注解形式配置权限,如果权限不足抛出异常
-
页面标签库控制页面资源
-
根据当前登录用户的权限,动态控制按钮的隐藏与展示
-
在需要控制的资源(按钮,超链接)页面上引入shiro标签库
<%@ taglib prefix="shiro" uri="http://shiro.apache.org/tags" %>
-
通过shiro:hasPermission控制显示对应的按钮
<shiro:hasPermission name="删除部门"> <button type="button" ><i class="fa fa-trash-o"></i> 删除</button> </shiro:hasPermission>
-
-
自定义异常处理器
- 完成权限不足的页面跳转
-
shiro内部会根据用户的权限数据和所需权限自动判断
-
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O6RSBBsD-1605668991338)(assets\18.png)]
7.5shiro优化
缓存优化
- 用户的权限数据一旦确定很少修改,为了缓解数据库压力。可以将权限数据保存到redis当中。
- 在shiro中redis缓存不需要编写代码,需要配置即可
- 借助shiro中内置缓存管理器实现(内置缓存管理器:集成多种缓存)
- 第一次访问的时候,查询数据库将数据保存到redis
- 第二次访问的时候,直接返回redis的数据
- redis中权限数据的实效:
- 用户登录成功之后,第一次获取权限数据存入redis
- 用户退出登录之后,清空redis
自定义过滤器
- 自定义过滤器继承父类AuthorizationFilter
- 获取配置的filter的参数,数据转型获取数组
- 获取subject
- 循环判断权限,满足其一即可返回true
- 返回值:true:具有权限 false:没有权限
- 配置
- 自定义过滤器交给spring容器管理
- 指定过滤器的简称
- 配置过滤器
Day08Dubbo
8.1软件架构的演化过程
- 单一应用架构
- 垂直应用架构
- 分布式架构SOA面向服务的架构)
- web层:专注于页面处理和跳转,发起远程调用,调用service完成业务处理
- service层:web工程部署到tomcat中,提供和业务相关数据。(提供服务)
8.2Dubbo的概述
-
前身是阿里巴巴开源的框架,现在是apache提供的一个RPC框架(远程调用框架),解决分布式系统中web层和service层的调用。
-
dubbo的结构
- 服务提供者: service工程,对外提供服务
- 服务消费者:web工程,调用服务提供者获取数据
- 注册中心:zookeeper注册中心
- 监控中心:dubbo-admin
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dZKTnYXL-1605668991339)(assets\19.png)]
-
相关软件的安装
-
注册中心zookeeper
-
windows系统:双击zkServer.cmd运行
-
Linux虚拟机
第一步:安装 jdk(略) 第二步:把 zookeeper 的压缩包(zookeeper-3.4.6.tar.gz)上传到 linux 系统 第三步:解压缩压缩包 tar -zxvf zookeeper-3.4.6.tar.gz 第四步:进入zookeeper-3.4.6目录,创建data目录 mkdir data 第五步:进入conf目录 ,把zoo_sample.cfg 改名为zoo.cfg cd conf mv zoo_sample.cfg zoo.cfg 第六步:打开zoo.cfg文件, 修改data属性: dataDir=/root/zookeeper-3.4.6/data 进入bin目录,启动服务命令 ./zkServer.sh start 停止服务命令 ./zkServer.sh stop 查看服务状态: ./zkServer.sh status 防火墙开2181端口 * firewall-cmd --zone=public --add-port=2181/tcp --permanent * firewall-cmd --reload zookeeper安装成功之后暴露一个请求的地址:zookeeper://ip:2181
-
-
监控中心dubbo-admin
-
是dubbo官方提供的一个dubbo框架的监控中心,本质是一个war包。将此war包修改配置之后,部署到tomcat中即可
-
只支持jdk1.8
-
解压war包修改WEB-INF下的dubbo.properties
dubbo.registry.address=zookeeper://ip:2181 dubbo.admin.root.password=root dubbo.admin.guest.password=guest
-
将文件夹拖拽到tomcat中启动
-
-
8.3Dubbo的入门案例
@Service:配置到提供者service实现类上(dubbo包下的)
@Reference:配置到需要注入的对象上(替换@Autowired)
传递数据时,对象需要实现序列化接口
服务提供者:
- 创建工程导入依赖
- 创建service接口和实现类
- @Service :alibaba包下的注解
- 注册到注册中心的服务名称:当前接口的全类名
- 配置spring和dubbo提供者的整合文件(了解)
- 应用名称 = 当前工程名
- 注册中心地址
- 协议端口(协议:dubbo,端口:从20881开始)
- 包扫描
- 配置web.xml(可以省略)
服务消费者:
- 创建工程导入依赖
- 配置service接口
- 将提供者中的HelloService接口copy到消费者工程的对应目录下
- 配置controller
- 如果需要调用dubbo服务提供者
- 配置接口的私有属性,在属性上通过@Reference注解配置
- 根据接口的全类名找到远程dubbo服务并调用
- 配置springmvc配置文件
- mvc的常规配置(包扫描,视图解析器,mvc的注解驱动)
- spring整合dubbo消费者(应用名,注册中心,dubbo注解的包扫描)
- 配置web.xml
- 和传统mvc工程一模一样
启动方式:
-
将工程部署到tomcat中运行。优势:安全可靠,缺点:不利于开发
- tomcat–》web.xml–》spring配置
-
通过java的启动类。缺点:不安全,不可靠,优点:适用于开发阶段
-
不需要配置web.xml
/** * 启动类: * 加载spring配置文件,引导dubbo服务提供者启动 */ public class HelloProvider { public static void main(String[] args) throws IOException { //1.加载spring配置文件 ClassPathXmlApplicationContext ac = new ClassPathXmlApplicationContext("classpath:applicationContext-provider.xml"); //2.启动spring容器 ac.start(); //3.输入任一项关闭 System.in.read(); } }
-
dubbo的maven结构:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FnNMFr4u-1605668991340)(assets\20.png)]
8.4总结配置文件
提供者配置文件
-
spring和dubbo整合提供者配置
- 应用名称:当前工程名
- 注册中心:配置zookeeper的请求路径
- 暴露的请求协议和端口:dubbo,端口从20881开始
- 注解扫描:扫描使用了dubbo注解的包(和spring包扫描共同使用)
-
serivce事务的配置文件
-
web.xml(启动方式:如果tomcat启动配置,通过监听加载spring配置文件)
消费者配置文件
-
springmvc的配置文件
- springmvc的相关配置
- spring整合dubbo消费者的配置
- 应用名称:当前工程名
- 注册中心:配置zookeeper的请求路径
- 注解扫描:扫描使用了dubbo注解的包(和spring包扫描共同使用)
-
web.xml的配置文件
- 和之前单独使用springmvc一样,在dispatcherServlet中加载springmvc的配置文件
8.5dubbo使用的注意事项
(1) 操作数据传输的对象,必须实现序列化接口
(2) 消费者的启动检查
- 默认情况下:消费者工程在启动的时候,会自动的检查服务提供者程序是否运行,如果提供者没有正常启动,消费者会抛出异常,(后续的所有配置和加载过程停止),导致项目启动异常
- 在正式系统使用默认即可,在开发阶段建议关闭启动检查
- 通过在消费者端加入关闭启动检查
<dubbo:consumer check="false"></dubbo:consumer>
(3) 错误重试和超时
在消费者调用提供者过程中,由于网络波动,可能造成暂时的链接失败
通过在消费者端配置重试次数=0
<dubbo:consumer check="false" retries="0"></dubbo:consumer>
(4) 集群和负载均衡
集群:部署多个提供者,共同承担系统压力
负载均衡 :消费者通过负载均衡算法自动的从集群中找到合适的提供者调用
Day09Mybatis逆向工程
Mybatis Generator: mybatis官方提供的一套工具(jar包),可以方便的根据数据库表创建实体类、dao接口、映射文件(只能生成最基础的CRUD操作的代码)
9.1搭建逆向工程
- 创建工程导入依赖
- 编写逆向工程的配置文件
- mysql数据库相关配置(mysql驱动包路径,数据库相关信息)
- 实体类生成位置(包,支持相对和绝对路径)
- dao接口和映射文件的位置
- 指定数据库表,生成实体类和文件的名字
- 编写一个逆向工程的java类
- 测试
9.2 生成方法的介绍
在企业中开发的时候
- 可以直接将代码生成到主工程中,
- 生成好代码之后,copy到主工程
注意
- 根据逆向工程生成的实体类,默认没有实现序列化接口
- 自己去生成实体类的话(需要手动设置实现序列化接口)
- 实体类需要实现序列化接口,XXXExample也需要实现序列化接口(内部类也需要实现)
public interface FactoryDao {
// 根据id删除
int deleteByPrimaryKey(String id);
//根据id查询
Factory selectByPrimaryKey(String id);
/**
* 可选属性保存
* 保存实体类的时候,将实体类中的非空数据构造为SQL语句,发送到mysql数据库
*/
int insertSelective(Factory record);
/**
* 可选属性更新(非空属性更新)
*/
int updateByPrimaryKeySelective(Factory record);
//条件查询
List<Factory> selectByExample(FactoryExample example);
}
条件查询的特点:
public void testSelectByExample() {
/**
* 条件查询:
* 1、创建example对象
* 2、通过example创建条件封装对象criteria
* 3、向criteria设置查询条件
* 4、调用selectByExample发起查询
* 案例:根据工厂名称查询
* SQL:SELECT * FROM co_factory WHERE factory_name = "升华"
* SELECT * FROM co_factory WHERE factory_name LIKE "升%"
* SELECT * FROM co_factory WHERE factory_name LIKE "升%" AND contacts='刘生'
*/
//1、创建example对象
FactoryExample example = new FactoryExample();
//2、通过example创建条件封装对象criteria
FactoryExample.Criteria criteria = example.createCriteria();
//3、向criteria设置查询条件 (查询字段,查询方法,数据)
//criteria中方法的语法:and + 属性名 + 查询方式(参数数据)
//criteria.andFactoryNameEqualTo("升华");
criteria.andFactoryNameLike("升%");
criteria.andContactsEqualTo("刘生");
//4、调用selectByExample发起查询
//Example如果是空,查询所有
List<Factory> list = factoryDao.selectByExample(example);
for (Factory factory : list) {
System.out.println(factory);
}
}
}
9.3购销合同业务概述
- 公司销售和海外客户签订订单(合同),客户订单中的货物,公司就联系这些(多个)厂家来生产,和生产厂家签订合同,这个合同就叫“购销合同”。主要由三部分组成
- 购销合同的主信息,
- 多个货物的信息,
- 多个附件的信息。(附件实际就是货物)
搭建环境:
- 代码生成
- dao接口
- 映射文件
- dubbo的工程结构
- 创建子模块export_cargo_service : 服务提供者(web)
- 创建子模块export_cargo_interface : 公共接口
- 配置各个模块之间的依赖关系
- 配置购销合同的提供者
- 配置spring整合提供者
- 配置启动类
9.4购销合同管理
- 配置controller
- 在springmvc文件中配置整合消费者
- 保存和修改
- 合同提交与取消
- 合同删除
Day10七牛云存储
10.1货物管理
- 搭建环境
- 搭建提供者环境
- 搭建消费者环境
- 分页查询
- 保存货物
- 在springmvc中设置文件解析器
- 更新货物
- 删除货物
10.2七牛云存储
- 用于解决图片上传的需求
- 电商应用(自建图片服务器)
- 传统软件:存放到一些服务商的服务器上(方便,成本低)
- 使用方法
- 账户注册和实名认证
- 创建存储空间
- 记录关键参数
- 空间名称
- AccessKey
- SecretKey
- 测试域名(使用一个月)
- 抽取工具类
- 货物图片上传
- 页面端
- form表达的enctype属性
multipart/form-data
- form表单的提交方式:post提交
- 文件input属性必须type=file
- form表达的enctype属性
- java端
- 在springmvc中处理图片:可以在方法上通过MultipartFile表示文件资源对象
- 如果使用MultipartFile参数,参数的名称和页面input的名称一致
- springmvc中文件上传:需要配置文件解析器
- 页面端
9.7附件管理
- 搭建环境
- 搭建提供者环境
- 创建附件的ExtCproductService接口
- 创建附件的ExtCproductServiceImpl实现类
- 搭建消费者环境
- 创建附件的Contoller对象
- 搭建提供者环境
- 分页列表
- 保存附件
- 修改附件
- 删除附件
Day11POI框架
11.1细粒度权限控制
细粒度权限:根据不同用户的分类,展示不同的数据列表
细粒度权限控制:数据库表的设计
管理本部门
- 在用户数据库表中的degree字段(4:普通员工,3:部门经理,2:下属部门,1:管理员)
- 在购销合同表中有两个字段(create_by : 创建人id,create_dept:创建人所在部门id)
- 保存合同添加创建人和部门
- 查询购销合同拼接条件
管理下属部门
- 部门id的生成规则(100100100三段式)
11.2POI框架
-
原生POI框架
- apache提供的一套专门用于处理office的框架,通过POI完成Excel文件的读写。
-
EasyPOI
- 阿里巴巴对POI的封装框架
-
excel
- xls
- 2003版本
- 二进制格式,核心结构是复合文档类型的结构
- 单sheet:65535行,256列
- 存储容量有限
- xlsx
- 2007及以上版本
- xml类型结构
- 单sheet:1048576行,16384列
- 占用空间小,操作效率高
- xls
-
搭建环境
- poi、poi-ooxml、poi-ooxml-schemas
-
创建Excel文件
public class PoiTest01 { public static void main(String[] args) throws Exception { //1、创建工作表(工作簿) /** * HSSFWorkbook : 处理2003版本EXCEL * XSSFWorkbook : 处理2007版本EXCEL * SXSSFWorkbook : 处理2007版本大数据量Excel */ Workbook wb = new XSSFWorkbook(); //2、创建第一页sheet Sheet sheet = wb.createSheet("heima31"); //3、创建行对象(第四行) Row row = sheet.createRow(3);//行索引,从0开始 //4、创建单元格对象(第五个单元格) Cell cell = row.createCell(4);//单元格索引,从0开始 //5、向单元格写入数据(传智播客) cell.setCellValue("传智播客"); //补充单元格和字体样式(封装样式对象) CellStyle style = wb.createCellStyle(); //设置边框 style.setBorderLeft(BorderStyle.THIN); //左边框细线 //设置字体(字体对象) Font font = wb.createFont(); font.setFontName("华文楷体"); font.setFontHeightInPoints((short)16); style.setFont(font); //单元格设置样式 cell.setCellStyle(style); //6、通过API方法,生成Excel文件 wb.write(new FileOutputStream("D:\\demo.xlsx")); } }
-
解析Excel文件
public static void main(String[] args) throws Exception { //1.根据Excel文件,创建工作簿 Workbook wb = new XSSFWorkbook("E:\\demo.xlsx"); //2.获取excel中的第一页 Sheet sheetAt = wb.getSheetAt(0);//页索引,从0开始 //3.循环所有的数据行 // sheetAt.getLastRowNum() :获取最后一个数据行的索引 for(int i=1 ;i<sheetAt.getLastRowNum() + 1;i++) { //4.获取每一行 Row row = sheetAt.getRow(i); //5.获取每行中的第4个单元格 Cell cell = row.getCell(3); //6.获取单元格中的数据内容 String name = cell.getStringCellValue(); System.out.println(name); } }
11.3货物的批量上传
- 进入到上传页面
- 批量上传货物
- 解析excel文件,构造出上传的货物对象的list集合
- 调用service完成批量的保存
11.4出货表打印
- 进入到出货表选择日期的下载页面
- 完成出货表打印
- 根据船期查询数据库获取出货表数据
- 使用POI框架生成Excel文件
- 通过工具类完成Excel文件下载
Day12EasyExcel
12.1百万数据的Excel操作
- JDK内置的性能监控工具
- jdk安装目录下的 bin/jvisualvm.exe。双击打开
- 解决方案
- **用户模式:**用户模式有许多封装好的方法操作简单,但创建太多的对象,非常耗内存
- **事件模式:**基于SAX方式解析XML,SAX全称Simple API for XML,它是一个接口,也是一个软件包。它是一种XML解析的替代方法,不同于DOM解析XML文档时把所有内容一次性加载到内存中的方式,它逐行扫描文档,一边扫描,一边解析。
12.2EasyExcel框架
EasyExcel:阿里巴巴开源的操作Excel的框架,底层封装的POI
EasyPOI:国内开源的一个操作Excel的框架,底层也是POI
基于Java的简单、省内存的读写Excel的开源项目。在尽可能节约内存的情况下支持读写百M的Excel。
12.3创建Excel文件
创建实体类对象
-
在实体类对象上通过注解配置 :标题,行高,列宽等数据
/** * 在EasyExcel中,通过java代码生成或者解析Excel文件很简单。不需要创建繁琐的表头 * @ExcelProperty : 在创建Excel的时候,自动的读取实体类中的注解配置,生成表头 */ @ContentRowHeight(20) //数据行高数 @HeadRowHeight(20) //表头高度 @ColumnWidth(15) //列宽 public class ContractProductVo implements Serializable { @ExcelProperty("客户名称") private String customName; //客户名称 ...... @ExcelProperty("船期") @DateTimeFormat("yyyy-MM-dd") private Date shipTime; //船期 @ExcelProperty("贸易条款") private String tradeTerms; //贸易条款 //省略get,set }
java代码
-
在java代码中通过EasyExcel工具类完成excel文件的生成和下载
/** * 使用EasyExcel完成excel的生成和下载 * 1、数据查询 * 2、设置下载信息 * 3、调用EasyExcel的工具类完成生成下载 */ @RequestMapping("/printEasyExcel") public void printEasyExcel(String inputDate) throws IOException { //1、数据查询 List<ContractProductVo> list = contractService.findByShipTime(inputDate+"%"); //2、设置下载信息 response.setContentType("application/vnd.ms-excel"); //下载excel response.setCharacterEncoding("utf-8"); String fileName = URLEncoder.encode("出货表", "UTF-8"); response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx"); //3、调用EasyExcel的工具类完成生成下载 EasyExcel.write(response.getOutputStream()) .head(ContractProductVo.class) //设置表头 .sheet("heima127") //指定页名称 .doWrite(list); //设置数据 }
模板打印
- 对于复杂样式的Excel文件创建,EasyExcel支持模板打印。已一个Excel文件作为模板,自动的进行数据的拓展和填充。
- 定义模板 ( 配置好了所有的样式 )
- 配置实体类(表头)
- java代码填充模板并下载Excel
定义模板
- 在easyExcel中,支持两个模板语言
- 语法:{map中的key}
- 传入一个map集合:往往是用于处理非list集合的数据
- 语法:{.对象中的属性名}
- 传入一个list集合,会自动的循环list集合构造数据列表
java代码填充模板并下载Excel
/**
* 模板打印
*
*/
@RequestMapping("/printTemplate")
public void printTemplate(String inputDate) throws IOException {
//1.准备数据
List<ContractProductVo> list = contractService.findByShipTime(inputDate+"%");
Map map = new HashMap<>();
inputDate = inputDate.replaceAll("-0","-").replaceAll("-","年");
map.put("time",inputDate);
map.put("title1","客户名称");
//2.设置下载信息
response.setContentType("application/vnd.ms-excel"); //下载excel
response.setCharacterEncoding("utf-8");
String fileName = URLEncoder.encode("出货表", "UTF-8");
response.setHeader("Content-disposition", "attachment;filename=" + fileName + ".xlsx");
//3.加载excel模板
String path = session.getServletContext().getRealPath("/")+"/make/tOUTPRODUCT.xlsx";
//4.创建EasyExcel的excelWtire对象( 用于数据填充)
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.head(ContractProductVo.class) //设置表头
.withTemplate(path) //加载模板
.build();
//获取sheet对象
WriteSheet sheet = EasyExcel.writerSheet().build();
//5.调用方法完成填充map数据
excelWriter.fill(map,sheet);
//6.调用方法完成填充list数据
excelWriter.fill(list,sheet);
//7.属性资源,完成下载
excelWriter.finish(); //下载excel文件,释放内存资源
}
12.4解析Excel文件
-
创建实体类对象(用于解析封装对象)
-
在实体类上配置注解(EasyExcel自动的根据注解配置获取文件中对应行的内容)
package cn.itcast.domain.cargo; import cn.itcast.domain.BaseEntity; import com.alibaba.excel.annotation.ExcelIgnoreUnannotated; import com.alibaba.excel.annotation.ExcelProperty; import java.io.Serializable; import java.util.List; /** * 合同下货物的实体类 * 通过EasyExcel完成文件解析,在实体类上通过注解配置表头 * 如果实体类中的属性,未配置@ExcelProperty * 默认按照属性的编写顺序,从excel中获取数据 * 默认情况下,为配置注解的属性会影响数据封装 * 在类上配置一个注解:忽略未配置注解的属性 * * @ExcelIgnoreUnannotated 忽略默认表头配置 * @ExcelProperty配置表头 */ @ExcelIgnoreUnannotated public class ContractProduct extends BaseEntity implements Serializable { private String id; @ExcelProperty("货号") ...... @ExcelProperty("生产厂家") private String factoryName; //厂家名称,冗余字段 private String factoryId; private List<ExtCproduct> extCproducts ; //货物和附件,一对多 //省略,get,set方法 }
-
调用EasyExcel工具类完成文件解析
/** * 通过EasyExcel完成文件上传解析,批量货物保存 * 参数:购销合同id * 参数:上传的excel文件对象 */ @RequestMapping("/import") public String importExcel(String contractId,MultipartFile file) throws Exception { List<ContractProduct> list = EasyExcel.read(file.getInputStream()) .head(ContractProduct.class) //设置表头,将数据转化为目标对象 .sheet(0) //读取第一页数据 .doReadSync(); //解析excel,获取所有的数据 for (ContractProduct contractProduct : list) { System.out.println(contractProduct); contractProduct.setContractId(contractId); contractProduct.setCompanyId(getLoginCompanyId()); contractProduct.setCompanyName(getLoginCompanyName()); } contractProductService.saveAll(list); return "redirect:/cargo/contractProduct/list.do?contractId="+contractId; }
12.5合同管理
购销合同–> 提交—>合同管理—>勾选合同,添加出口报运单(向报运单表添加数据)–>展示出口报运单---->勾选出口报运单,完成海关保运工作(电子保运)
合同管理:展示出所有已上报的购销合同(查询购销合同中,状态=1)
- 合同取消与提交
- 查询所有状态=1的购销合同。
12.6数据库的打断设计
- 在数据库表出现多层表关系嵌套时,可以通过打断设计,简化开发
- 表关系多余4层时,使用打断设计
- 一对一打断:多表间公用数据库主键ID
- 一对多打断:在主表上通过打断字段维护从表关系(打断:从表id的集合,字符串)
12.7出口报运
将数据保存到数据库表(出口报运单)
将数据发送到海关(电子报运单)
报运单创建为PDF文件
- 搭建环境
- 搭建提供者
- 代码生成
- 在interface工程中配置报运管理的service接口
- 在service配置对应的接口实现类
- 搭建消费者
- 修改web工程的Controller添加分页查询方法
Day13webservice
13.1报运单管理
-
搭建环境
- 搭建提供者环境
- 搭建消费者环境
-
数据准备
-
新增报运单页面进入代码
/** * 进入到新增页面 * /cargo/export/toExport.do * 参数: * 同名参数id,多个id * 通过springmvc可以将多个id,自动转化为数组 : String [] id * 在web工程中,如果有多个同名参数请求,自动的转化为字符串(多个参数间通过“,”隔开) * 业务逻辑 * 1、将多个id,转化为字符串,多个id间通过“,”隔开。存入request即可 * 2、页面跳转 */ @RequestMapping("/toExport") public String toExport(String id) { request.setAttribute("id",id); return "cargo/export/export-toExport"; }
-
新增报运单业务层逻辑
-
保存报运单数据
- 设置报运单的合同号集合
- 已勾选的合同,需要设置状态为以保运(state=2)
-
根据购销合同货物生成报运单商品数据
- 根据购销合同id查询,所有的货物(in查询)
- 循环所有货物构造报运单商品
- 商品的属性赋值(基本属性从货物中copy,id,报运单id)
-
根据购销合同附件生成报运单附件数据
-
根据某一个货物查询所有货物的附件
-
每个附件构造一个报运单附件
BeanUtils.copyProperties(ecp, eep);//属性复制
-
报运单附件的属性赋值
-
-
-
修改报运单
- 进入修改页面
- 完成报运单修改
13.2webservice简介
- 简介
- Web Service(WEB服务)能够快捷和方便地综合结合各种系统、商务和任何应用平台。利用最新的Web Service 标准能够使任何软件系统和系统之间的应用互通互联,方便,而且更加廉价。(跨系统调用)
- 三个规范
- JAX-WS:早期盛行的webservice的开发技术
- wsdl : 文档说明书(server端提供的文件,知道client调用)
- soap协议 : 数据传输规范 (http + xml数据)
- uddi:文档目录
- JAX-RS
- 通过jax-rs规范,搭建一个符合rest风格URL的网站(RestFull)
- rest:对同一个url,根据不同的请求方式,完成不同的业务处理
- 自动将数据转化为json或者xml
- JAXM&SAAJ(已废弃)
- JAX-WS:早期盛行的webservice的开发技术
- apache的CXF框架
13.3JAX-WS的入门
-
提供者
-
创建工程导入依赖
@WebService //当写了此接口时,表明当前接口是一个webservice接口 public interface WeatherService { String getWeatherByCityName(String cityName); }
-
创建service接口和实现类
-
创建spring整合配置文件applicationContext-cxf-server.xml
- 配置jaxws的服务端对外暴露的请求url
- 配置处理请求的service接口的实现类
-
-
配置web.xml
- 监听器加载spring配置文件
- CXF框架的核心控制器:CXFServlet
-
消费者
- 通过jdk自带的工具:wsimport工具,根据文档说明书帮助我们生成java代码
- 配置整合
13.4JAX-RS的入门
-
服务端
- 创建工程导入依赖
- 配置实体类和service
- sping整合配置
- web.xml配置
-
客户端 (重点)
-
和服务端开发商索要开发文档(word文档)
-
规定请求接口的信息
- http://localhost:8084/ws/us/user/1
- get方式
- 参数 id 和 返回值
-
根据文档配置实体类
-
通过WebClient工具类发送请求即可
public class ClientTest { public static void main(String[] args) { //根据id调用提供者的方法 :http://localhost:8084/ws/us/userService/user/1,get WebClient wc = WebClient.create("http://.../user/1"); User user = wc.get(User.class); System.out.println(user); WebClient wc = WebClient.create("http://.../user/1"); wc.delete(); } }
-
Day14quartz、Echarts
14.1海关保运
-
模拟海关平台
- 数据库准备
- 项目准备
- 启动海关平台
- 启动测试
-
海关保运业务
- controller调用service完成操作
- service编写所有的业务代码
- 页面修改
- dubbo重试
-
保运结果查询
-
发送报运数据
- 判断(报运单状态已经发送,已经有保运结果)
- 查询saas项目中的保运数据(报运单和报运单商品信息)
- 构造海关所需要的数据格式(参考接口文档)
- 将数据通过webservice(WebClient)发送到海关
- 修改报运单状态(状态=2,以将数据发送到海关,没有保运结果)。
-
定时查询保运结果
- 查询没有报运结果的数据(查询状态=2)
- 调用海关平台查询保运结果(通过WebClient调用)
- 对于保运结果状态!=2 报运单,完成数据更新(报运单的状态,更新商品的关税)
-
14.2定时任务调度
定时任务:根据一定的时间规则自动的执行java方法。
定时任务框架:spring task ,quartz
quart介绍:
-
job (任务):定时执行的java类对象 (类)
-
jobdetail (任务描述):定时执行的类和方法(类.方法)
-
trigger (触发器):定义时间规则和待执行的任务描述
-
调度容器:协调管理所有的触发器
quartz入门
-
定义一个定时执行的java类和方法
- 和普通的java类方法一致
- 类名:ExportJob(任意)
- 方法名:execute(任意)
- 和普通的java类方法一致
-
配置spring和quartz整合
-
在web工程创建
applicationContext-job.xml的
整合配置文件<!--1.将定时任务类交给spring容器--> <bean id="exportJob" class="cn.itcast.web.task.ExportJob"></bean> <!--2.配置jobDetal : 配置定时执行的类和方法--> <bean id="jobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean"> <property name="targetObject" ref="exportJob"></property> <property name="targetMethod" value="execute"></property> </bean> <!--3.配置trigger: (触发器)配置时间以及jobdetal关系 --> <bean id="trigger" class="org.springframework.scheduling.quartz.CronTriggerFactoryBean"> <!--时间表达式--> <property name="cronExpression" value="0/5 * * * * ? *"></property> <property name="jobDetail" ref="jobDetail"></property> </bean> <!--4.配置定时任务管理器--> <bean id="startQuartz" class="org.springframework.scheduling.quartz.SchedulerFactoryBean"> <property name="triggers"> <list> <ref bean="trigger"></ref> </list> </property> </bean>
-
quartz时间表达式
cron 七子表达式
秒 分 时 日 月 周 年
0-59 0-59 0-23 1-31 1-12 1-7 1970-2099
常用的符号
* 任意
? (忽略)任意(日 和 周 同时只能出现一个* 和一个?)年的配置(可以省略)
m/n 从M开始,每间隔N执行
m-n 从M开始,到N执行,整数执行
q,w,e,r,t,y 指定时间
L=Last
m#n 第n个m
14.3图形报表框架Echarts
- 简介
- 百度前团队卡法的基于js图形报表组件,使用JavaScript实现的开源可视化库。
- 容易运行,兼容强,底层依赖轻量级矢量图形库ZRender
- 官网:https://echarts.apache.org/zh/index.html
14.4统计分析
-
页面跳转
@RequestMapping("/toCharts") public String toCharts(String chartsType) { return "stat/stat-"+chartsType; }
-
搭建环境
- 创建工程模块,导入依赖
- 创建export_stat_service提供者模块
- 创建export_stat_interface提供者模块
- 添加各个模块的依赖关系
- mybatis查询返回map集合
- 在mybatis中支持map类型返回值: map替换实体类对象
- map中key表示查询的字段(对象属性)
- map中的value表示此字段对应的数据值
- 创建dao接口和映射文件
- 创建service接口及实现类,对应页面上的数据来源
- 配置spring整合文件
- dubbo配置文件
- 启动类
- 创建spring容器,加载spring配置文件
- 创建工程模块,导入依赖
Day15PDF报表
15.1jasper report
概述
- 强大、灵活的报表生成工具,能够展示丰富的页面内容,并将之转换成PDF,HTML,XML格式.
- 完全由Java写成,可以用于在各种Java应用程序,包括J2EE,Web应用程序中生成动态内容。
生命周期
- 设计阶段(pdf模板)
- 通过JaperSoft-studio设计pdf文件样式
- 设计yanghsi:jrxml文件
- 编译成jasper文件,才能在Java代码中使用
- 数据填充
- 通过java代码查询数据
- 借助jasper report提供的API向PDF模板填充数据
- 文件导出
- 通过Java API完成文件预览或下载
JaperSoft-studio:模板设计工具
- 打开Jaspersoft Studio,新建一个project
- File -> New -> Project-> JasperReportsProject
- 新建一个lasper Report模板,在Stidio的左下方Project Explorer找到刚才新建的Project
- 步骤:项目右键-> New ->jJasper Report
15.2中文处理
- 导入文件到web下的resources文件夹中
- stsong
- fonts.xml
- stsong.TTF
- jasperreports_extension.properties
- stsong
- 创建模板时,选择配置过的字体
15.3入门案例
-
制作模板
- Parameters
- Fields
-
修改页面
-
java代码填充数据
@RequestMapping("/exportPdf") public void exportPdf(String id) throws Exception { //根据id查询报运单对象 Export export = exportService.findById(id); //根据报运单id查询报运单商品 ExportProductExample example = new ExportProductExample(); ExportProductExample.Criteria criteria = example.createCriteria(); criteria.andExportIdEqualTo(id); List<ExportProduct> list = exportProductService.findAll(example); //将报运单对象转化为map集合 Map<String, Object> map = BeanMapUtils.beanToMap(export); //获取模板路径 String path = session.getServletContext().getRealPath("/")+"/jasper/export.jasper"; //加载模板,数据填充 JasperPrint print = JasperFillManager.fillReport(path, map, new JRBeanCollectionDataSource(list)); //预览pdf //JasperExportManager.exportReportToPdfStream(print,response.getOutputStream()); byte[] bytes = JasperExportManager.exportReportToPdf(print); //获取到pdf文件的byte数组 ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); //创建ByteArrayOutputStream输出流 // 把pdf字节数组写入到缓存流中 byteArrayOutputStream.write(bytes); new DownloadUtil().download(byteArrayOutputStream,response,"报运单.pdf"); }
-
中文处理
Day16RabbitMQ
16.1发送邮件
-
发送邮件包含两种协议:
- smtp协议:邮件发送协议
- pop协议:邮件接收协议
-
发送邮件准备工作
- 打开smtp服务和pop3服务
-
发送邮件的工具类
import javax.mail.Address; import javax.mail.Session; import javax.mail.Transport; import javax.mail.internet.InternetAddress; import javax.mail.internet.MimeMessage; import java.util.Properties; public class MailUtil { /** * 发送邮件工具类 * @param to : 邮件的接收人地址 * @param subject : 邮件的主体 * @param content : 邮件的正文 */ public static void sendMsg(String to ,String subject ,String content) throws Exception{ //1.邮件服务器设置 Properties props = new Properties(); props.setProperty("mail.smtp.host", "smtp.sina.com"); //设置主机地址 smtp.qq.com smtp.sina.com props.setProperty("mail.smtp.auth", "true");//认证 //2.产生一个用于邮件发送的Session对象 Session session = Session.getInstance(props); //3.产生一个邮件的消息对象 MimeMessage message = new MimeMessage(session); //4.设置消息的发送者 Address fromAddr = new InternetAddress("itcast004@sina.com"); message.setFrom(fromAddr); //5.设置消息的接收者 Address toAddr = new InternetAddress(to); //TO 直接发送 CC抄送 BCC密送 message.setRecipient(MimeMessage.RecipientType.TO, toAddr); //6.设置主题 message.setSubject(subject); //7.设置正文 message.setText(content); //8.准备发送,得到火箭 Transport transport = session.getTransport("smtp"); //9.设置火箭的发射目标 transport.connect("smtp.sina.com", "itcast004@sina.com", "3b8823dd9d424f6f"); //10.发送 transport.sendMessage(message, message.getAllRecipients()); //11.关闭 transport.close(); } public static void main(String[] args) throws Exception { MailUtil.sendMsg("2921232227@qq.com","我们一起来画龙","左手和我一起画个龙,右手画一道彩虹,走起"); } }
-
发送邮件
16.2消息中间件
常见的消息中间件
- ActiveMQ,rabbitmq,rocketmq,kafka等
应用场景
- 消息队列中间件是分布式系统中重要的组件,主要解决应用解耦,异步消息,流量削锋等问题,实现高性能,高可用,可伸缩和最终一致性架构。
消息中间件的角色
- 生产者:向消息中间件发送消息的一方
- 消费者:从中间获取消息使用的一方
- broker: 消息中间件
16.3RabbitMQ
RabbitMQ的准备:
- 环境安装
- RabbitMQ安装
- 启动rabbitMQ服务
- 开启web控制台的访问
管理后台:
- 访问http://127.0.0.1:15672/
- 创建虚拟主机
- 创建用户
- 对用户分配虚拟主机的操作权限
- 点击用户名
- 授权
消息类型:
- 队列消息
- 分类
- 简单模式消息
- 工作模式消息Task queues/Work queues
- 特点
- 消息不会丢失
- 满足先进先出
- 只能同时被一个消费者获取
- 分类
- 发布订阅消息
- 流程
- 可以有多个消费者
- 每个消费者有自己的queue(队列)
- 每个队列都要绑定到Exchange(交换机)
- 生产者发送的消息,只能发送到交换机,交换机来决定要发给哪个队列,生产者无法决定。
- 交换机把消息发送给绑定过的所有队列
- 队列的消费者都能拿到消息。实现一条消息被多个消费者消费
- 交换机分类
- fanout
- Direct
- Topic
- 特点
- 生产者和消费者需要订阅同一个频道
- 消息会丢失
- 消息可以被多个消费者同时获取
- 流程
- 流程
16.4五种工作模式
- 简单模式
- 消息生产者-----消息消费者-----消息队列
- ack消息回执:保证消息中间件和消息消费者之间数据安全
- 自动回执
- 手动回执
- 工作模式
- 多个消费者绑定到一个队列,共同消费队列中的消息,队列中的消息一旦消费,就会消失,因此任务是不会被重复执行的
- 任务分配
- fanout模式
- 最基本的模式
- Direct模式:直链模式(路由模式)
- 可以有选择性的接收生产者发送的消息(可以对消息进行过滤器)
- 队列与交换机的绑定,不能是任意绑定了,而是要指定一个
RoutingKey
(路由key) - 消息的发送方在 向 Exchange发送消息时,也必须指定消息的
RoutingKey
。 - Exchange不再把消息交给每一个绑定的队列,而是根据消息的
Routing Key
进行判断,只有队列的Routingkey
与消息的Routing key
完全一致,才会接收到消息
- Topic模式
- 在配置RoutingKey的是时候可以支持通配符配置
Routingkey
一般都是有一个或多个单词组成,多个单词之间以”.”分割,例如:item.insert
- 通配符规则:
#
:匹配一个或多个词*
:匹配不多不少恰好1个词- 例子:
item.#
:能够匹配item.spu.insert
或者item.spu
- ``item.*
:只能匹配
item.spu`
16.5Spring和RabbitMQ的整合
-
创建export_email工程配置依赖
- export_commons
-
搭建生产者环境
- 配置spring和rabbitMQ的整合文件
- 抽取工具类发送消息
-
搭建消费者环境
- 配置消息监听器
-
配置spring整合消息消费者
-
配置启动类
Day17总结
day1:maven高级
- maven的传递依赖,依赖冲突(1.约定俗称,2.排除依赖,3.锁定版本)
- 聚合和继承(父工程:定义公共依赖,管理子模块,子模块:继承公共依赖)
day2:git
- git和svn的区别(笔试题):svn集中式版本控制工具,git分布式版本控制工具
- git的相关操作:(本地仓库和远程服务器仓库(gitee,自建服务器))
- 本地(暂存区,本地仓库)
- 添加暂存区,提交到本地仓库,push到远程服务器仓库
- 克隆代码
- 拉取代码pull
- 协同开发时:代码冲突(合并冲突)
day3:项目介绍和企业管理
- 云服务的三种模式(iaas,paas,saas)
- saas-export项目的介绍
- pd工具完成用例图的绘制
- 企业CRUD
day4:分页和RBAC权限模型
- 分页使用的:PageHelper插件(原理面试问题:拦截器拦截SQL,对SQL改造发送查询)
- 部门管理(一对一配置)
- RBAC权限模型(权限5表:用户,角色,模块,两个中间表)
day5:CRUD
- 用户管理,角色管理,模块管理,对用户分配角色
day6:分配权限和日志记录
- 对角色分配可操作权限(ztree前端框架)
- 登录 + 动态构造菜单
- 记录日志(通过AOP完成)
day7:shiro权限框架(面试题)
- shiro框架的作用和应用场景
- shiro中的内部结构和执行过程(subject,安全管理器,realm域,密码比较器,缓存管理器)
- shiro中的认证(步骤)和shiro中的授权(步骤)
- 自定义过滤器,redis优化
day8:dubbo框架(面试)
- 分布式SOA结构,实现框架(dubbo,webservice,rmi)
- dubbo:4个角色和执行过程
- 提供者
- 消费者
- 注册中心
- 监控中心
- 配置:重试,超时时间,启动检查
- zookeeper(注册中心,配置中心,分布式锁)
- 节点类型
- watch机制
- 集群选主
day9:逆向工程和购销合同管理
- 购销合同业务(描述)
- 业务
- 数据库表关系
day10:七牛云和附件管理
- 数据库设计三范式和反三范式(总金额,附件数,货物数)
- 七牛云存储(货物图片,委托上传到七牛云)
day11:细粒度权限控制和POI操作
- 细粒度权限(根据用户不同的级别,展示不一样的数据列表)
- POI:poi介绍,poi的对象(面试)
day12:EasyExcel和出口报运
- 百万数据Excel处理(面试)
- Sxssfworkbook : 创建
- 用户模式,事件模式(处理百万数据excel文件解析)
- EasyExcel(模板打印)
- 海关保运的完成业务逻辑
- 打断数据库设计(面试)
day13:海关保运和webservice
- 海关保运(准备数据)
- webservice(面试)
- webservice的三大规范(jax-ws,jax-rs)
- jax-ws中的三要素(wsdl,soap(http + xml)+uddi)
- jax-rs(restfull)
day14:海关保运和定时任务(面试)
-
整体业务逻辑
-
海关保运(将数据发送到海关平台):webservice中的jax-rs的客户端 webclient
-
获取保运结果:为了及时获取结果,通过定时任务扫描查询
-
定时任务框架quartz(4个元素,cron表达式)
day15:统计分析和pdf
- 前端页面使用echarts构造图标(SQL语句)
- jasper reports处理PDF(面试)
- 生命周期(设计,填充,导出)
- 数据填充方式(list和map)
day16:rabbitMQ(面试)
- 应用场景:发送邮件短信
- rabbitmq的结构(消息生产者,消息提供者,broker)
- 消息中的俩大消息模式(队列消息,发布/订阅)
- 队列消息
- 简单模式
- 工作模式
- 发布/订阅
- faunt模式
- direct模式
- topic模式
- 工作模式:任务分配
- ack确认机制:保证rabbitmq和消费者
Day18SpringBoot
18.1概念
- 主要解决传统项目中存在的配置文件多和依赖复杂问题
18.2Spring initializr
-
Spring initializr 介绍
- Spring 官方提供的一个创建Spring boot项目的工具
- 可以选择使用Maven管理或者使用Gradle管理
- 根据需要选择Spring Boot的版本。
-
SpringBoot程序搭建:
- pom文件
- 声明父工程
- 引入起步依赖
- 指定jdk版本:1.8
- application.properties
- 启动类(引导类)
- 类上注解:
@SpringBootApplication
- main方法:
SpringApplication.run(类.class,args);
- 类上注解:
- pom文件
18.3依赖分析
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EUxd4iD7-1605668991341)(assets/1576124211841.png)]
- spring-boot-starter-parent
- 父工程:内部定义所有的版本号,并锁定
- 定义了各种技术的版本信息,组合了一套最优搭配的技术版本。
- spring-boot-starter-web
- 起步依赖:定义所用到的所有依赖jar包
- 在各种starter中,定义了完成该功能需要的坐标合集,其中大部分版本信息来自于父工程。
- 我们的工程继承parent,引入starter后,通过依赖传递,就可以简单方便获得需要的jar包,并且不会存在版本冲突等问题。
18.4手动搭建SpringBoot
-
pom文件(父工程,jdk版本,起步依赖)
<!--父工程依赖--> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.3.RELEASE</version> </parent> <!--jdk版本1.8--> <properties> <java.version>1.8</java.version> </properties> <!--起步依赖--> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
-
在resoure/application.properties(空)
-
配置启动类(类上配置注解@SpringBootApplication, 配置main方法)
/** * 引导类: * 命名规则: xxxApplication * 类上:@SpringBootApplication * main:引导启动 * 业务代码: * 需要放到引导类所在包及其子包下 */ @SpringBootApplication public class ConfigApplication { public static void main(String[] args) { SpringApplication.run(ConfigApplication.class,args); } }
-
定义Controller
- 配置controller注解
- 配置请求对应的方法@RequestMapping
18.5配置文件
- 用于用自己的配置替换默认配置
- springboot的配置文件两种类型:
- application.properties
- application.yaml (yaml – yml)
- 配置文件需要放置到 resources目录下,并且以“application”命名
- 多个配置项优先级:
- properties>yml>yaml
18.6yaml
-
介绍
- 全称是 YAML Ain’t Markup Language
- 一种直观的能够被电脑识别的的数据数据序列化格式,并且容易被人类阅读
- 容易和脚本语言交互的,可以被支持YAML库的不同的编程语言程序导入
-
基本语法
- 数据值前边必须有空格,作为分隔符
- 使用空格缩进表示层级关系,相同缩进表示同一级
-
数据格式
- 对象
- 数组: 使用 “- ”表示数组每个元素
-
参数引用
- ${key}
-
回顾spring注解
@Configuration
:声明一个类作为配置类,代替xml文件@Bean
:声明在方法上,将方法的返回值加入Bean容器,代替<bean>
标签@Value
:属性注入
-
读取配置文件内容
-
@Value
- 配置到属性上:通过spel表达式获取配置项 @Value("${配置项名称}")
-
Environment工具类
- 注入工具类
- 调用工具类方法:getPropertiy()
-
@ConfigurationPropetties
- 直接将配置项赋值到某个对象上
-
18.7SpringBoot启动流程
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Asp3DdvJ-1605668991342)(C:/Users/skping/Desktop/assets/1575947723407.png)]
18.8整合springmvc
日志控制
-
springboot中内置了日志的处理。
-
需要在类上:@Slf4J (开启日志支持)
-
调用log.xxx方法,输出日志
-
在application.yml中配置输出的日志级别
#logging.level:固定 #error,warn,info,debug 输出范围,从右向左包含 logging: level: cn.itcast: info
静态资源
- 静态资源放入4个任意一个文件夹下即可
- “classpath:/META-INF/resources/”,
- “classpath:/resources/”,
- “classpath:/static/”,
- “classpath:/public/”
- 通过
IP:端口/静态资源名称
访问
拦截器
-
自定义拦截器
- 实现HandlerInterceptor接口
- 重写三个方法
-
配置拦截器(注册拦截器)
-
定义一个配置类,继承webmvcconfigurer。重写方法
/** * 注册拦截器 * 实现 WebMvcConfigurer 接口 * 重写 addInterceptors 方法 */ @Configuration public class MvcConfig implements WebMvcConfigurer { /** * registry : 注册器 * 所有的拦截器添加到注册器中即可 * 指定拦截的URL * 不拦截的URL */ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new LoginInterceptor()) .addPathPatterns("/user/**") .excludePathPatterns("/hello/**"); } }
-
整合jdbc和事务
- jdbc连接不需要处理,只要找到SpringBoot提供的启动器即可
- SpringBoot中通过注解
@Transactional
来控制事务
整合连接池
-
SpringBoot中内置了HikariCP数据库连接池
-
只需要在SpringBoot配置文件中加入配置
#配置数据库连接池 spring: datasource: username: root password: root url: jdbc:mysql:///springbootdb driver-class-name: com.mysql.jdbc.Driver
整合Mybatis
-
dao接口
-
dao映射文件
-
配置文件
#配置mybatis mybatis: mapper-locations: /mappers/** type-aliases-package: cn.itcast.user.domain configuration: map-underscore-to-camel-case: true #自动识别驼峰标识 user_name -- userName
-
自动扫描(在引导类指定dao接口的包)
- 在引导类上添加注解
@MapperScan(cn.itcast.user.dao)
- 自动创建指定包中dao接口的动态代理对象
- 在引导类上添加注解
-
service实现类,注入dao查询
单元测试
-
起步依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency>
-
创建一个单元测试类
-
指定单元测试环境和引导类
//指定单元测试环境 @RunWith(SpringRunner.class) //指定引导类 @SpringBootTest(classes = BootMapperApplication.class) public class UserDaoTest { @Autowired private UserDao userDao; @Test public void test() { User user = userDao.findByUsername("wangwu"); System.out.println(user); } }
通用Mapper
-
导入依赖
mapper-spring-boot-starter
-
配置实体类
- 在实体类上通过JPA注解的形式配置实体类和数据库表之间的映射关系
@Table(name="tb_user")
配置到类上@ID
配置在主键属性上Column
配置在属性上,可省略,遵循驼峰标志规则
-
配置dao接口
- 配置空的dao接口通过继承父接口Mapper<实体类泛型>
- 如果需要有特殊业务的需求,sql语句的编写和使用mybatis一模一样
-
配置service和controller
-
配置SpringBoot
server: port: 8088 #配置输出的日志级别 #error,warn,info,debug logging: level: cn.itcast: debug #配置数据库连接池 spring: datasource: username: root password: root url: jdbc:mysql:///springbootdb driver-class-name: com.mysql.jdbc.Driver
@SpringBootApplication @MapperScan("cn.itcast.user.dao") public class MapperApplication { public static void main(String[] args) { SpringApplication.run(MapperApplication.class, args); } }
18.9整合Thymeleaf
Thymeleaf简介
- 用来开发Web和独立环境项目的现代服务器端Java模板引擎
- Thymeleaf的主要目标是为您的开发工作流程带来优雅的自然模板 - HTML。可以在直接浏览器中正确显示,并且可以作为静态原型,从而在开发团队中实现更强大的协作。借助Spring Framework的模块,可以根据自己的喜好进行自由选择,可插拔功能组件,Thymeleaf是现代HTML5 JVM Web开发的理想选择 - 尽管它可以做的更多。
- Spring官方支持的服务的渲染模板中,并不包含jsp。而是Thymeleaf和Freemarker等,而Thymeleaf与SpringMVC的视图技术,及SpringBoot的自动化配置集成非常完美,几乎没有任何成本,你只用关注Thymeleaf的语法即可。
- xxx.html + thymeleaf的语法规则
- java代码跳转页面和跳转jsp一样
- springmvc:视图解析器(thymeleaf重写了视图解析器)
- 前缀
- 后缀
操作步骤:
- 导入thymeleaf的起步依赖
- 导入资料中的页面(html页面,静态资源(csss))
- thymeleaf中的所有页面需要放到: resource/templates目录下
- 所有的静态资源(css,js,图片)需要放置到: resource/static目录下
- 修改controller(添加两个方法对应列表和详情),返回值是String对应跳转页面
- ThymeleafViewResolver : thymeleaf的视图解析器
- 不在使用@RestController(内置已json的形式在浏览器打印数据)
- 在页面上通过thymeleaf语法获取数据并展示
基础语法
-
语法配置到html标签上 : th:
<span th:text="${}"></span>
-
页面名称解析器
<html lang="en" xmlns:th="http://www.thymeleaf.org">
-
使用规则
-
Thymeleaf通过
${}
来获取model中的变量,注意这不是el表达式,而是ognl表达式,但是语法非常像。 -
我们在页面获取user数据:
<h1> 欢迎您:<span th:text="${user.name}">请登录</span> </h1>
-
感觉跟el表达式几乎是一样的。不过区别在于,我们的表达式写在一个名为:
th:text
的标签属性中,这个叫做指令
-
-
循环
<tr th:each="user : ${users}"> <td th:text="${user.name}">Onions</td> <td th:text="${user.age}">2.41</td> </tr>
-
${users} 是要遍历的集合,可以是以下类型:
- Iterable,实现了Iterable接口的类
- Enumeration,枚举
- Interator,迭代器
- Map,遍历得到的是Map.Entry
- Array,数组及其它一切符合数组结果的对象
-
在迭代的同时,我们也可以获取迭代的状态对象:
<tr th:each="user,stat : ${users}"> <td th:text="${user.name}">Onions</td> <td th:text="${user.age}">2.41</td> </tr>
-
stat对象包含以下属性:
- index,从0开始的角标
- count,元素的个数,从1开始
- size,总元素个数
- current,当前遍历到的元素
- even/odd,返回是否为奇偶,boolean值
- first/last,返回是否为第一或最后,boolean值
18.10整合RabbitMQ
消息生产者
-
搭建springboot环境
- spring-boot-starter-amqp
-
配置rabbitmq的访问信息
spring: rabbitmq: virtual-host: saas #虚拟主机 username: saas password: saas host: 127.0.0.1 port: 5672
-
通过注入RabbitTemplate对象发送消息
rabbitTemplate.convertAndSend("springboot-rabbi2","a.b","802不见不散");
消息消费者
-
搭建springboot环境
-
配置rabbitmq的访问信息
- 和提供者一致
-
配置一个消息监听器
@RabbitListener( bindings = @QueueBinding( value=@Queue(value="queue2",durable = "true"), exchange=@Exchange(value="springboot-rabbi2",type= ExchangeTypes.TOPIC), key="*.*" ) ) public void message(String message) { System.out.println("获取到消息:"+message); }
-
@RabbitListener:
- bindings: 接收一个绑定注解
注解
- @RabbitListener:
- bindings: 接收一个绑定注解
- @QueueBinding : 描述队列,交换机和可处理路由key的关系
- key:队列中可处理的路由key(String)
- value : 队列
- @Queue
- value : 队列名称(随便写)
- durable: 持久化(true(保存),false)
- @Queue
- exchange:交换机
- @Exchange
- value:交换机名称(和消息发送的交换机名称保持一致)
- type:交换机类型(fanout,direct,topic)
- @Exchange
18.11整合Redis
-
导入起步依赖
spring-boot-starter-data-redis
-
在application.yml 配置redis访问信息
spring: redis: host: 127.0.0.1 port: 6379 database: 0
-
测试(通过注入RedisTemplate,操作redis数据库)
@RunWith(SpringRunner.class) @SpringBootTest public class RedisTest { /** * 此对象默认调用java的序列化接口对传输的数据进行序列化操作 * java底层的序列化操作效率低 * private RedisTemplate redisTemplate; */ /** * 专门用于操作String类型数据的工具类 * private StringRedisTemplate redisTemplate; * 需要将传输的对象,转化为json字符串的形式发送 */ @Autowired private StringRedisTemplate redisTemplate; @Test public void test() { //存入数据 //redisTemplate.opsForValue().set("key1","heima115"); //取出基本数据 //String value = redisTemplate.opsForValue().get("key1"); //System.out.println(value); //redisTemplate.opsForValue().set("key2","heima115",10, TimeUnit.SECONDS); // Map map = new HashMap(); // map.put("name","zhangsan"); // map.put("age","12"); // redisTemplate.opsForHash().putAll("user",map); //System.out.println(redisTemplate.opsForHash().get("user","name")); Map<Object, Object> map = redisTemplate.opsForHash().entries("user"); map.forEach((key,value)-> System.out.println(key+"--"+value)); }
18.12SpringBoot核心原理
@Conditional注解
-
Spring4.0 增加的条件判断功能,通过这个可以功能可以实现选择性的创建Bean操作
-
自定义条件
-
定义条件类:自定义类实现Condition接口,重写 matches 方法,在 matches 方法中进行逻辑判断,返回boolean值 。 matches 方法两个参数:
- context:上下文对象,可以获取属性值,获取类加载器,获取BeanFactory等。
- metadata:元数据对象,用于获取注解属性。
-
判断条件: 在初始化Bean时,使用 @Conditional(条件类.class)注解
2、条件注解
- ConditionalOnProperty:判断配置文件中是否有对应属性和值才初始化Bean
-
- ConditionalOnClass:判断环境中是否有对应字节码文件才初始化Bean
- ConditionalOnMissingBean:判断环境中没有对应Bean才初始化Bean
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uKK2GqgO-1605668991343)(C:/Users/skping/Desktop/assets/2-condition注解.png)]
@Import注解
- SpringBoot中提供了很多Enable开头的注解,这些注解都是用于动态启用某些功能的。而其底层原理是使用==@Import==注解导入一些配置类,实现Bean的动态加载。
- @Import提供4中用法:
- 导入Bean
- 导入配置类
- 导入 ImportSelector 实现类。一般用于加载配置文件中的类
- 导入 ImportBeanDefinitionRegistrar 实现类。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wM6Djrks-1605668991343)(C:\Users\Skping\Desktop\assets\3-enable注解.png)]
@EnableAutoConfiguration注解
- @EnableAutoConfiguration 注解内部使用 @Import(AutoConfigurationImportSelector.class)来加载配置类。
- 配置文件位置:META-INF/spring.factories,该配置文件中定义了大量的配置类,当 SpringBoot 应用启动时,会自动加载这些配置类,初始化Bean
- 并不是所有的Bean都会被初始化,在配置类中使用Condition来加载满足条件的Bean
18.13自定义启动器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOOVlFzk-1605668991344)(assets/1576124211841.png)]
- 依赖关系
- 只需要在工程中引入起步依赖即可
- 起步依赖中包含了所有需要参与工作的jar包
- 导入的工程中包含一个 xxxx.autoconfigure.jar
- 此jar包中包含META-INF/spring.factoties的文件
- 定义了需要自动装配的类
- 自动装配类中可以通过@Bean的形式定义对象
- 当容器启动的时候,会执行@Bean的方法,创建对象将对象存入容器
- @SpringBootApplication
- @ComponentScan:扫描程序员自己定义的对象
- @SpringBootConfiguration:将引导类作为一个配置类,可以在引导类中@Bean创建对象
- @EnableAutoConfiguration:加载所有工程中的META-INF/spring.factoties的文件
18.14Spring Boot Admin
-
Spring Boot Admin是一个开源社区项目,用于管理和监控SpringBoot应用程序。
-
Spring Boot Admin 有两个角色,客户端(Client)和服务端(Server)。
-
应用程序作为Spring Boot Admin Client向为Spring Boot Admin Server注册
-
Spring Boot Admin Server 的界面将Boot Admin ClientActuatorEndpoint
18.15项目部署
-
jar包发布
-
配置打包插件
- spring-boot-maven-plugin
-
通过maven命令package完成项目打包。
-
找到jar包位置,以命令行窗口执行
java -jar xxx.jar
-
-
war包发布
- 修改pom的打包方式为war
- 修改引导类配置父类和重写方法
- 通过maven打包
- 将war包放到tomcat中运行
- 使用war的话,内置tomcat失效
- 启动类需要继承SpringBootServletInitializer
- 重写configure方法
- 执行maven的package命令,将打好的war包放入tomcat中运行即可
Day19VUE
前端框架三巨头:Vue.js、React.js、AngularJS,vue.js以期轻量易用著称,vue.js和React.js发展速度最快,AngularJS还是老大。
19.1MVVM
- Model-View-ViewModel的简写。它本质上就是MVC 的改进版。 html和中的数据模型可以做双向绑定
- MVVM模式
- M:即Model,模型,包括数据和一些基本操作
- V:即View,视图,页面渲染结果
- VM:即View-Model,模型与视图间的双向操作(无需开发人员干涉,即把DOM操作完全封装起来)
19.2Vue概念
- 一套用于构建用户界面的渐进式框架,自底向上逐层应用。
- Vue 的核心库只关注视图层,不仅易于上手,还便于与第三方库或既有项目整合
- 当与现代化的工具链以及各种支持类库结合使用时,Vue 也完全能够为复杂的单页应用提供驱动。
19.3Node和NPM
-
npm:前端世界的maven,自动的下载前端依赖的js组件
-
npm是nodejs附带的功能,需要安装nodejs
-
npm默认从国外下载,需要切换国内镜像(nrm镜像切换工具)
-
安装nrm切换工具
19.4安装vue
-
下载安装解压,得到vue.js文件。
-
直接使用公共的CDN服务
-
推荐npm安装
- 在idea的左下角,有个Terminal按钮,点击打开控制台:
- 先输入:
npm init -y
进行初始化 - 安装Vue,输入命令:
npm install vue --save
- 生成的node_modules是通过npm安装的所有模块的默认位置。
19.5VUE指令
- 指令 (Directives) 是带有
v-
前缀的特殊特性。 - 指令特性的预期值是:单个 JavaScript 表达式。
- 指令的职责是,当表达式的值改变时,将其产生的连带影响,响应式地作用于 DOM。
插值表达式
-
花括号
{{表达式}}
- 该表达式支持JS语法,可以调用js内置函数(必须有返回值)
- 表达式必须有返回结果。例如 1 + 1,没有结果的表达式不允许使用,如:var a = 1 + 1;
- 可以直接获取Vue实例中定义的数据或函数
-
v-text和v-html
<div id="app"> v-text:<span v-text="hello"></span> <br/> v-html:<span v-html="hello"></span> </div>
- 使用v-text和v-html指令来替代
{{}}
,在html标签上使用 - v-text:将数据输出到元素内部,如果输出的数据有HTML代码,会作为普通文本输出
- v-html:将数据输出到元素内部,如果输出的数据有HTML代码,会被渲染
- 使用v-text和v-html指令来替代
v-on
-
v-on指令用于给页面元素绑定事件。
-
事件绑定可以简写,例如
v-on:click='add'
可以简写为@click='add'
<div id="app"> <button v-on:click="func">查看</button><br/> </div>
v-model
-
双向绑定,视图(View)和模型(Model)之间会互相影响。
<div id="app"> 用户名 <input v-model="name"> <br> {{name}} </div>
-
目前v-model的可使用视图元素有:
- input
- select
- textarea
- checkbox
- radio
v-for
-
遍历数组
<div id="app"> <ul> <li v-for="item in items"> {{item.name}} : {{item.gender}} : {{item.age}} </li> </ul> </div>
-
在遍历的过程中,如果我们需要知道数组角标,可以指定第二个参数
-
index:迭代到的当前元素索引,从0开始。
<div id="app"> <ul> <li v-for="(item,index) in items"> {{item.name}} : {{item.gender}} - {{index}} </li> </ul> </div>
v-if和v-show
-
v-if:当得到结果为true时,所在的元素才会被渲染。
<div id="app"> {{age}} <div v-if="age<18">未成年人</div> <div v-else-if="age>=60">老年人</div> <div v-else>成年人</div> </div>
-
v-show
只是简单地切换元素的 CSS 属性display
。<div v-show="flag"> 我是v-show </div>
v-bind
-
动态渲染页面标签中属性值
-
v-bind:class
可以简写为:class
<div id="app"> <font v-bind:color="ys1">刘备</font><br/> <font :color="ys2">关羽</font> </div>
watch
-
可以让我们监控一个值的变化。从而做出相应的反应。
<!--页面--> <div id="app"> <input type="text" v-model="message"> </div> <!--导入vue文件--> <script src="./node_modules/vue/dist/vue.js"></script> <!--js代码--> <script type="text/javascript"> var vm = new Vue({ el:"#app", data:{ message:"" }, watch:{ message(newVal, oldVal){ console.log(newVal, oldVal); } } }) </script>
19.6组件化
- 把页面的不同部分拆分成独立的组件
- 然后在不同页面就可以共享这些组件,避免重复开发。
定义全局组件
-
组件其实也是一个Vue实例,因此它在定义时也会接收:data、methods、生命周期函数等
-
不同的是组件不会与页面的元素绑定,否则就无法复用了,因此没有el属性。
-
但是组件渲染需要html模板,所以增加了template属性,值就是HTML模板
-
全局组件定义完毕,任何vue实例都可以直接在HTML中通过组件名称来使用组件了。
-
data的定义方式比较特殊,必须是一个函数。
-
定义好的组件,可以任意复用多次
-
执行代码流程时,无论是否使用都会加载
<div id="app"> <!--使用定义好的全局组件--> <counter></counter> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> // 定义全局组件,两个参数:1,组件名称。2,组件参数 Vue.component("counter",{ template:` <div> <button v-on:click="count++">你点了 {{ count }} 次</button> <div/> `, data(){ return { count:0 } } }) var app = new Vue({ el:"#app" }) </script>
定义局部组件
-
在声明时不知道自己是组件,只是一个普通的js对象
-
注册给根对象后才能使用
-
执行代码流程时,只有注册了才会加载,因此用的更多
<div id="app"> <!--使用局部组件--> <!--<abc></abc>--> <heima></heima> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script type="text/javascript"> // 定义全局组件,两个参数:1,组件名称。2,组件参数 var heima = { template: <div> <button @click="num++">我是按钮,值为{{num}}</button> </div>, data(){ return{ num:18 } } } var app = new Vue({ el:"#app", compontents:{ //abc:heima 此时标签只能写abc //heima:heima 一般写成一样的 heima //key和value一致时,可以简写 } }) </script>
组件通讯
-
父向子通讯使用子的props属性,子需要什么就传什么
<div id="app"> <!--v-bind后面是值直接取值,是变量从变量取值,true是关键字,所以只能是值--> <heima v-bind:"info" v-bind:"true" v-bind:"80"></heima> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script> var heima = { template:` <div> {{decide?"是":"否"}}<br/> num:{{num}}<br/> <button>姓名:{{user.name}}</button> </div>`, props:{ user:{ type:Object, default:{} }, decide:{ type:Boolean, default:false }, num:{ type:Number, default:15 } } } var app = new Vue({ el:"#app", components:{ heima }, data:{ info:{ name:"skping" } } }) </script>
-
子向父通讯需要父向子传递一套方法,被子调用
-
执行步骤
- 点击按钮,触发的是子的add方法
- 然后引用了父向子传递的jia方法
- 最后执行了对应的increment方法
-
一般实际使用时,三个方法名取一致的一个
<div id="app"> <heima @jia="increment" @jian="decrement"></heima> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script> var heima = { template:` <div> <button @click="add">点我变大</button> <button @click="reduce">点我变小</button> </div>`, methods:{ add(){ this.$emit("jia") }, reduce(){ this.$emit("jian") } } } var app = new Vue({ el: "#app", components:{ heima }, data:{ num:18 }, methods:{ increment(){ this.num++ }, decrement(){ this.num-- } } }) </script>
19.7生命周期和钩子函数
生命周期
- 每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板等等
- Vue为生命周期中的每个状态都设置了钩子函数(监听函数)
- 每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rCGOX3Ew-1605668991345)(assets/生命周期详解.jpg)]
this
- 当前vue对象
钩子函数
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
name:"abc"
},
beforeCreate:function(){
console.log("beforeCreate:Vue实例创建之前");
console.log(this.$el);//undefind
console.log(this.$data);//undefind
},
created:function () { //非常重要
console.log("created:Vue实例创建之后");
console.log(this.$el);//undefind
console.log(this.$data);//有数据
},
//渲染数据
beforeMount:function () {
console.log("beforeMount:VUE的data中数据挂载到html之前");
},
//渲染之后
mounted:function () { //非常重要
console.log("mounted:VUE的data中数据挂载到html之后");
},
beforeUpdate:function () {
console.log("数据变化之前");
},
updated:function () {
console.log("数据变化之后");
},
beforeDestroy:function () {
console.log("VUE实例销毁前");
},
destroyed:function () {
console.log("VUE实例销毁后");
}
})
</script>
19.8Ajax异步请求数据
//可以通过Terminal中通过npm install axios --save
//参数1是请求的地址 参数2是请求的内容
axios.post('company',this.dept).then(resp=>{
console.log(resp)
this.companyList = resp.data;
})
//原生ajax请求对比
$.ajax({
type: "post", //数据提交方式(post/get)
url: "/company", //提交到的url
data: {"dept.deptName": this.dept.deptName, "dept.companyName":
this.dept.companyName},//提交的数据
dataType: "json",//返回的数据类型格式
success: function (msg) {
if (msg.success) {
app.findAll();
} else {
alert(response.data.message);
}
}
});
19.9解构
let arr = [1,2,3,4,5];
let [a,b,c,d...others] = arr;//others是一个数组
{User}如果写成{name:n,id:i,sex}
意思是直接把User对象中的三个属性取出来,并且可以选择性的赋给一个新的变量
19.10属性
- 属性只有三个地方可以声明
- data
- computed
- properties
传递的jia方法
-
最后执行了对应的increment方法
-
一般实际使用时,三个方法名取一致的一个
<div id="app"> <heima @jia="increment" @jian="decrement"></heima> </div> <script src="./node_modules/vue/dist/vue.js"></script> <script> var heima = { template:` <div> <button @click="add">点我变大</button> <button @click="reduce">点我变小</button> </div>`, methods:{ add(){ this.$emit("jia") }, reduce(){ this.$emit("jian") } } } var app = new Vue({ el: "#app", components:{ heima }, data:{ num:18 }, methods:{ increment(){ this.num++ }, decrement(){ this.num-- } } }) </script>
19.7生命周期和钩子函数
生命周期
- 每个 Vue 实例在被创建时都要经过一系列的初始化过程 :创建实例,装载模板,渲染模板等等
- Vue为生命周期中的每个状态都设置了钩子函数(监听函数)
- 每当Vue实例处于不同的生命周期时,对应的函数就会被触发调用。
[外链图片转存中…(img-rCGOX3Ew-1605668991345)]
this
- 当前vue对象
钩子函数
<script type="text/javascript">
var app = new Vue({
el:"#app",
data:{
name:"abc"
},
beforeCreate:function(){
console.log("beforeCreate:Vue实例创建之前");
console.log(this.$el);//undefind
console.log(this.$data);//undefind
},
created:function () { //非常重要
console.log("created:Vue实例创建之后");
console.log(this.$el);//undefind
console.log(this.$data);//有数据
},
//渲染数据
beforeMount:function () {
console.log("beforeMount:VUE的data中数据挂载到html之前");
},
//渲染之后
mounted:function () { //非常重要
console.log("mounted:VUE的data中数据挂载到html之后");
},
beforeUpdate:function () {
console.log("数据变化之前");
},
updated:function () {
console.log("数据变化之后");
},
beforeDestroy:function () {
console.log("VUE实例销毁前");
},
destroyed:function () {
console.log("VUE实例销毁后");
}
})
</script>
19.8Ajax异步请求数据
//可以通过Terminal中通过npm install axios --save
//参数1是请求的地址 参数2是请求的内容
axios.post('company',this.dept).then(resp=>{
console.log(resp)
this.companyList = resp.data;
})
//原生ajax请求对比
$.ajax({
type: "post", //数据提交方式(post/get)
url: "/company", //提交到的url
data: {"dept.deptName": this.dept.deptName, "dept.companyName":
this.dept.companyName},//提交的数据
dataType: "json",//返回的数据类型格式
success: function (msg) {
if (msg.success) {
app.findAll();
} else {
alert(response.data.message);
}
}
});
19.9解构
let arr = [1,2,3,4,5];
let [a,b,c,d...others] = arr;//others是一个数组
{User}如果写成{name:n,id:i,sex}
意思是直接把User对象中的三个属性取出来,并且可以选择性的赋给一个新的变量
19.10属性
- 属性只有三个地方可以声明
- data
- computed
- properties