day01
git和svn的区别:
- git是分布式版本控制工具。
- svn是集中式版本控制工具。
什么是SOA架构样式:
- 是一种面向服务的架构样式。
RPC(Remote Procedure Call)—远程过程调用:
Dubbo一款高性能、轻量级的开源Java RPC框架,它提供了三大核心能力:
- 面向接口的远程方法调用。
- 智能容错和负载均衡。
- 以及服务自动注册和发现。
服务提供者层暴露服务用@Service注解:
- 注意使用alibaba.dubbo那个@Service
服务消费者注入接口使用@Reference注解
- 注意使用alibaba.dubbo那个@Reference
Dubbo根据服务名称的接口,动态代理一个代理对象,使用的是JDK代理。
服务器消费者如果要使用事务,只能使用cglib代理方式,才不会与dubbo冲突。
<tx:annotation-driven proxy-target-class = “true”/>
True:表示cglib代理
False:表示JDK代理
通用Mapper常用的注解:
@Table(name="tb_goods")
- 作用在类上
@ID @GeneratedValue(strategy=GenerationType.IDENTITY)
@Column(name="id")
- 作用在主键id上
@Column(name="对应数据库的列名称")
- 对应数据库普通的字段上
@Transient(name="tb_goods")
- 作用:通用Mapper不会把它当作tb_goods表中的列(表示不关心)
通用Mapper常用的增删查改:
增加
- 一般都只是使用insertSelective(Oject o)进行增加。
@Override
public void save(ItemCat itemCat) {
itemCatMapper.insertSelective(itemCat);
}
删除
- 简单介绍用Example进行删除
@Override
public void deleteAll(Long[] ids) {
Example example = new Example(ItemCat.class);
Example.Criteria criteria = example.createCriteria();
criteria.andIn("id",list);
itemCatMapper.deleteByExample(example);
}
-
如果参数是数组,那么使用 Arrays.asList(数组) 把数组转换成List集合。
查询
- 简单介绍用Example进行查询
@Override
public List<Content> findContentByCategoryId(Integer categoryId) {
Example example = new Example(Content.class);
Example.Criteria criteria = example.createCriteria();
criteria.andEqualTo("categoryId",categoryId);
criteria.andEqualTo("status",1);
example.orderBy("sortOrder").asc();
contentList = contentMapper.selectByExample(example);
return contentList;
}
修改
updateByPrimaryKeySelective
- 只是更新新的model中不为空的字段。
updateByPrimaryKey
- 将为空的字段在数据库中置为NULL
day02
nginx
nginx配置:
server {
listen 80;
server_name manager.pinyougou.com;
proxy_set_header X-Forwarded-Host $host;
proxy_set_header X-Forwarded-Server $host;
location / {
proxy_pass http://127.0.0.1:9101;
proxy_connect_timeout 600;
proxy_read_timeout 600;
}
}
nginx反向代理流程图:
angular
angular的四大特性:
- 模块化设计
- MVC模式
- 双向绑定
- 依赖注入
angular前端返回值的数据接收:
- 前端发送异步请求后接受的数据response不能直接使用,response里面封装了两种数据。
- response.data 封装的是后端传来的数据。
- response.status 封装的是请求响应数据的状态码,当状态码为200时,表示响应成功。
$http异步请求服务三种格式:
1) $http()发送get|post请求
$http({
method : 'get|post', // 请求方式
url : '', // 请求URL
params : {'name':'admin'} // 请求参数
}).then(function(response){ // 请求成功
// response: 响应对象封装了响应数据、状态码
},function(response){ // 请求失败
// response: 响应对象封装了响应状态码
});
2) $http.get()发送get请求
// 第一种格式
$http.get(URL,{
params: {
"id":id
}
}).then(function(response){// 请求成功
// response: 响应对象封装了响应数据、状态码
}, function(response){ // 请求失败
// response: 响应对象封装了响应状态码
});
// 第二种格式
$http.get(URL).then(function(response){ // 请求成功
// response: 响应对象封装了响应数据、状态码
},function(response){ // 请求失败
// response: 响应对象封装了响应状态码
});
3) $http.post()发送post请求
// 第一种方式
$http.post(URL,{
"id" : id
}).then(function(response){ // 请求成功
// response: 响应对象封装了响应数据、状态码
},function(response){ // 请求失败
// response: 响应对象封装了响应状态码
});
总结:
- 只有$http.post()发送的请求是json格式,控制端若要接收,参数需要加上@RequestBody注解。
- 其他方法都以拼接字符串的形式发送请求到后台。
angular注意事项:
- ng-checked 与 ng-model 不能同时使用,会有冲突。
$watch
- $watch方法用于监控某个变量的值,被监控的值发生变化,就自动执行相应的函数。
/** 监控 goods.category1Id 变量,查询二级分类 */
$scope.$watch('goods.category1Id', function(newValue, oldValue){
if (newValue){
/** 根据选择的值查询二级分类 */
$scope.findItemCatByParentId(newValue, "itemCatList2");
}else{
$scope.itemCatList2 = [];
}
});
angular的MVC模式:
1)前端基础层
不带模块自定义模块:
/** 定义基础模块(不带分页模块) */
var app = angular.module('pinyougou',[]);
带模块自定义模块:
/** 定义基础模块(带分页模块) */
var app = angular.module('pinyougou',['pagination']);
2)前端服务层
/** 品牌服务层 */
app.service("brandService", function($http){
});
3)前端控制层
基础控制器:
/** 定义品牌控制器层 */
app.controller('brandController', function($scope, baseService){
});
继承基础控制器:
/** 定义控制器层 */
app.controller('brandController', function($scope, $controller, baseService){
/**说明:$controller也是AngularJS提供的一个服务,
可以实现伪继承,实际上就是与baseController共享$scope。*/
/** 指定继承baseController */
$controller('baseController',{$scope:$scope});
});
关于Angular发送请求的知识点
- 查询用get比较好。
- 过滤器只解决post请求,不解决get的请求。
- 只有tomcat8或以上版本get才不会乱码,其他都会乱码。
在控制器处理乱码问题:
@GetMapping("/findByPage")
public PageResult findByPage(Brand brand,Integer page, Integer rows){
if(brand!=null && StringUtils.isNoneBlank(brand.getName())){
try {
brand.setName(new String(brand.getName().getBytes("ISO8859-1"),"UTF-8"));
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return brandService.findByPage(brand,page,rows);
}
day03
分页
若使用分页时,可以创建通用实体类封装数据:
/** 分页结果实体 */
public class PageResult implements Serializable {
/** 总记录数 */
private long total;
/** 分页数据 */
private List<?> rows;
}
pageHelper指定分页的语句:
/** 分页查询品牌 */
@Override
public PageResult findByPage(Brand brand, int page, int rows) {
// 开始分页
PageInfo<Brand> pageInfo = PageHelper.startPage(page, rows)
.doSelectPageInfo(new ISelect() {
@Override
public void doSelect() {
brandMapper.selectAll();
}
});
return new PageResult(pageInfo.getTotal(),pageInfo.getList());
}
js关于数组的操作:
- 数组的push方法:向数组中添加元素。
- 数组的splice方法:从数组的指定位置溢出指定个数的元素,参数1为位置,参数2为移除的个数。
- 数组的join方法:返回数组中的元素用指定的符号分隔的字符串。
- 复选框的checked属性:用于判断是否被选中。
- $event时间对象,是内置对象,封装了当前的html元素对应的dom元素
- $event.target.checked 返回true和false
前端:
<td><input type="checkbox"
ng-click="updateSelection($event,entity.id)"></td>
示例:
/** 定义选中的ids数组 */
$scope.ids = [];
/** 为复选框绑定点击事件 */
$scope.updateSelection = function($event, id) {
/** 如果是被选中,则增加到数组 */
if($event.target.checked){
$scope.ids.push(id);
}else{
var idx = $scope.ids.indexOf(id);
/** 删除数组中的元素 */
$scope.ids.splice(idx, 1);
}
};
在前端json对象与json字符串转换格式的方法:
JSON.stringify(json对象)
- 把json对象转换成json字符串。
JSON.parse(json字符串)
- 把json字符串转换成json对象。
在后端 json对象与json字符串转换格式的方法:
把对象转换成json字符串:
- String s = JSON.toJSONString(contentList);
把json字符串转化成Map集合:
- Map<String,Object> spec = JSON.parseObject(item.getSpec());
把json字符串转化成List集合:
List<Map> imageList = JSON.parseArray(json字符串,Map.class);
day04
做面包屑导航条的思想:
- 定义grade级别。
- 每个级别都对应一个$scope域对象,方便于数据的绑定。例:$scope.item1,$scope.item2
下拉列表select2的数据格式:
- {data:[{id:1,text:'联想'},{id:2,text:'华为'},{id:3,text:'小米'}]};
- id和text都是固定格式,且区分大小写。故select语句尽量不要在客户端写,因为客户端关键字会自动大写。
FastDFS
什么是FastDFS:
- FastDFS是用c语言编写的一款开源的分布式文件系统,使用FastDFS很容易搭建一套高性能的文件服务器集群提供文件上传、下载等服务。
FastDFS的架构:
- FastDFS架构包括 Tracker server和Storage server。客户端请求Tracker server进行文件上传、下载,通过Tracker server调度最终由Storage server完成文件上传和下载。
- Tracker server作用是负载均衡和调度。
- Storage server作用是文件存储,客户端上传的文件最终存储在Storage服务器上,Storage server没有实现自己的文件系统而是利用操作系统的文件系统来管理文件。
day05
SpringSecurity
- 配置文件自己模仿文档可得,不需要记。
电商概念SKU与SPU
SPU = Standard Product Unit (标准产品单位)
SKU = Stock Keeping Unit (库存量单位)SKU = SPU + (规格、颜色、款式、内存、版本、套餐等)的组合
day06
富文本编辑器
- 见文档
FastDFS文件上传
- 见文档
day07
- 一般商品的状态,在数据库当中以状态码的形式上表现,但是在网页在网页前端不可能用状态码来表示,需要用字符串来表示,那么可以定义一个数据,然后一个状态码对应数组的一个索引。
示例:
<td>{{status[entity.auditStatus]}}</td>
/** 定义商品状态数组 */
$scope.status = ['未审核','已审核','审核未通过','关闭'];
day08
- 商品的上下架,删除,一般都是修改数据库中的状态码,并是真的从数据库中删除数据。
classpath*:代表加载多个jar包中的属性文件。
classpath:代表加载一个jar包中的属性文件。
day09
SpringDataRedis
Spring-Data-Redis针对jedis提供了如下功能:
- 连接池自动管理,提供了一个高度封装的“RedisTemplate”类
- 针对jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation
接口如下:
- ValueOperations:简单K-V操作
- SetOperations: set类型数据操作
- ZSetOperations: zset类型数据操作
- HashOperations: hash类型的数据操作
- ListOperations: list类型的数据操作
redis配置放置的位置:
- 因为缓存对于整个的系统来说是通用功能,可能很多数据都要使用缓存。所以redis的配置放在公共组建层中较为合理。
更新redis缓存的方案:
- 添加,修改,删除都需要把Redis数据库中的key删除。
value五种数据类型:
字符串类型
- redisTemplate.boundValueOps(key).xxx();
set类型
- redisTemplate.boundSetOps(key).xxx();
zset类型
- 基本不使用
list类型
- redisTemplate.boundListOps(key).xxx();
hash类型
- redisTemplate.boundHashOps(key).xxx();
day10
Solr建立在Lucene(全文搜索引擎)之上。Solr可以用于存储目的。像其他NoSQL数据库一样,它是一种非关系数据存储和处理技术。
动态域配置文件用dynamicField标签
<uniqueKey>id</uniqueKey>:id不是String类型,注释solrconfig.xml的以下内容:
<!--
<searchComponent name="elevator"
class="solr.QueryElevationComponent" >
<str name="queryFieldType">string</str>
<str name="config-file">elevate.xml</str>
</searchComponent> -->
动态域只能用map集合。
若要数字精确,使用BigDecimal,BigDecimal比Double更加精确
什么是动静分离:
- nginx处理用户请求的静态页面,tomcat处理用户请求jsp页面,来实现动态分离,nginx处理静态页面效率远高于tomcat,这样一来就能更好的提高并发,处理性能。
solrTemplate的方法
单个增加:
UpdateResponse updateResponse = solrTemplate.saveBean(item);
- updateResponse.getStatus()的值等于0时,表示添加成功
批量增加:
UpdateResponse updateResponse = solrTemplate.saveBeans(list);
修改方法
- 修改跟增加的方法一样,都是用saveBeans()或saveBeans(),当solrTemplate检查到没有这个key时就增加,有这个key时就修改。
提交:
sorlTemplate.commit();
回滚:
solrTemplate.rollback();
根据主键id(唯一约束域)删除:
UpdateResponse updateResponse = solrTemplate.deleteById("3");
- updateResponse.getStatus()的值等于0时,表示添加成功
批量删除:
/** 删除商品索引 */
public void delete(List<Long> goodsIds){
Query query = new SimpleQuery();
Criteria criteria = new Criteria("goodsId").in(goodsIds);
query.addCriteria(criteria);
UpdateResponse updateResponse = solrTemplate.delete(query);
if (updateResponse.getStatus() == 0){
solrTemplate.commit();
}else{
solrTemplate.rollback();
}
}
根据主键id查询:
Item item = solrTemplate.getById("1", Item.class);
多条件分页查询:
// 创建查询对象 filedName:term
Query query = new SimpleQuery("*:*");
// 创建条件对象 标题中包含2
Criteria criteria = new Criteria("title").contains("2");
// 添加条件
query.addCriteria(criteria);
// 设置分页起始的记录数(limit的第一问号) (当前页码 - 1) * 页大小
query.setOffset(0);
// 设置页大小(limit的第二问号)
query.setRows(5);
// 分页查询,得到分数分页对象
ScoredPage<Item> scoredPage = solrTemplate.queryForPage(query, Item.class);
System.out.println("总记录数:" + scoredPage.getTotalElements());
// 获取查询的内容
List<Item> content = scoredPage.getContent();
is与contains的区别:
Criteria criteria = new Criteria("keywords").is(keywords)
- 表示将keywords分词后再查询。
Criteria criteria = new Criteria("keywords").contains(keywords)
- 表示域keywords中,一定要包含keywords值
day11
删除json对象的键值对:
- delete $scope.searchParam.spec[key];
遍历json对象(非数组):
<!-- spec : {"网络":"电信3G","机身内存":"128G"}-->
<li class="tag" ng-repeat="(key,value) in searchParam.spec">
{{key}}:{{value}}
<i class="sui-icon icon-tb-close"></i>
</li>
高亮显示的前端代码:
- $sce服务的trustAsHtml方法来html字符串转html标签。
- ng-bind-html指令用于显示html内容
操作步骤:
第一步:ng-bind-html="trustHtml(html)"
<div class="attr" style="line-height: 20px;"
ng-bind-html="trustHtml(item.title)">
</div>
第二步:在searchController.js中定义trustHtml方法,引用$sce服务
/** 定义搜索控制器 */
app.controller("searchController",function($scope,$sce, baseService) {
// 将文本转化成html
$scope.trustHtml = function (html) {
return $sce.trustAsHtml(html);
};
}
高亮查询:
查询方法:
solrTemplate.queryForHighlightPage()
步骤:
第一步:调用solrTemplate中的高亮分页查询方法
第二步:创建高亮查询对象HighlightQuery
第三步:创建高亮选项参数对象HighlightOptions,设置高亮相关的参数
第四步:在HighlightQuery对象中设置HighlightOptions
第五步:获取标题的高亮内容
// 创建高亮查询对象
HighlightQuery query = new SimpleHighlightQuery();
// 创建条件对象 keywords(复制域)
Criteria criteria = new Criteria("keywords").is(keywords);
// 添加条件对象
query.addCriteria(criteria);
// 创建高亮选项参数对象
HighlightOptions highlightOptions = new HighlightOptions();
// 设置哪个Field中出现关键字需要高亮
highlightOptions.addField("title"); // 标题
// <font color='red'>iphone</font>
// 设置高亮格式器前缀(在关键字前面加html标签)
highlightOptions.setSimplePrefix("<font color='red'>");
// 设置高亮格式器后缀(在关键字后面加html标签)
highlightOptions.setSimplePostfix("</font>");
// 添加高亮选项参数对象
query.setHighlightOptions(highlightOptions);
// 高亮分页查询
HighlightPage<SolrItem> highlightPage = solrTemplate
.queryForHighlightPage(query, SolrItem.class);
System.out.println("=============");
// 获取高亮选项集合
List<HighlightEntry<SolrItem>> highlighted = highlightPage.getHighlighted();
// 迭代高亮选项集合
for (HighlightEntry<SolrItem> highlightEntry : highlighted){
// 获取文档对应的实体
SolrItem solrItem = highlightEntry.getEntity();
// 获取高亮内容集合
List<HighlightEntry.Highlight> highlights = highlightEntry.getHighlights();
// 判断集合是否为空
if (highlights != null && highlights.size() > 0){
// 获取标题的高亮内容
String title = highlights.get(0).getSnipplets().get(0).toString();
System.out.println(title);
// 设置高亮后的标题容
solrItem.setTitle(title);
}
}
条件查询:
// 创建条件对象 keywords(复制域)
Criteria criteria = new Criteria("keywords").is(keywords);
// 添加条件对象
query.addCriteria(criteria);
过滤查询:
// 过滤条件
Criteria criteria1 = new Criteria("brand").is(brand);
// 添加过滤查询
query.addFilterQuery(new SimpleFilterQuery(criteria1));
价格过滤查询:
// 判断起始价格是不是零,不是零添加过滤条件
if (!"0".equals(priceArr[0])){
// 过滤条件 price >= ?
Criteria criteria1 = new Criteria("price").greaterThanEqual(priceArr[0]);
// 添加过滤查询
query.addFilterQuery(new SimpleFilterQuery(criteria1));
}
// 判断结束价格是不是星号,不是星号添加过滤条件
if (!"*".equals(priceArr[1])){
// 过滤条件 price <= ?
Criteria criteria1 = new Criteria("price").lessThanEqual(priceArr[1]);
// 添加过滤查询
query.addFilterQuery(new SimpleFilterQuery(criteria1));
}
排序查询:
// 添加排序代码
String sortField = (String)params.get("sortField");
String sortValue = (String)params.get("sort");
if (StringUtils.isNoneBlank(sortField) && StringUtils.isNoneBlank(sortValue)){
// 添加排序
query.addSort(new Sort("ASC".equals(sortValue) ?
Sort.Direction.ASC : Sort.Direction.DESC, sortField));
}
页面浏览器栏传来的数据:
步骤一:在页面添加标签
- angular自带$locationProvider服务
<!-- 定义该页面的基准的URL (http://search.pinyougou.com/) -->
<base href="/"/>
<script type="text/javascript">
// 配置位置提供者
app.config(function($locationProvider){
// 设置当前页面的模式 html5
$locationProvider.html5Mode(true);
});
</script>
步骤二:获取值
- 要先添加$location服务
// 获取首页传过来的参数keywords
$scope.getKeywords = function () {
// ?keywords=小米
// location.search
// $location.search() 获取请求url?后台的参数,是json对象
$scope.searchParam.keywords = $location.search().keywords;
// 执行搜索
$scope.search();
};
day12
什么是FreeMarker:
- FreeMarker 是一个用 Java 语言编写的模板引擎,它基于模板来生成输出各种文件。它不仅可以用作表现层的实现技术,而且还可以用于生成 XML,html 或 Java 等。
如果想在循环中得到索引,使用 循环变量_index就可以得到。
为什么要使用伪静态:
- SEO优化,提升网站排名 。
- html在搜索引擎中,排名高于jsp页面。
day13
当页面访问频率非常高时,可以对页面进行优化,提高访问速度:
1)使用redis添加缓存
- redis的访问速度快,能够较大的提升查询数据的速度,减轻MySQL数据库的访问压力。
2)使用静态化
- 把动态页面(jsp,php,asp等),转变成静态页面(html)
静态化的优点:
- 访问静态页面不需要经过程序处理,可以提高速度
- 稳定性高
- 静态页面相对于动态页面更容易被搜索引擎收录(SEO)
day14
HttpClient
HttpClient 提供的主要的功能:
(1)实现了所有 HTTP 的方法(GET,POST,PUT,DELETE等)
(2)支持自动转向
(3)支持 HTTPS协议
(4)支持代理服务器
阿里大于
需要注意的知识点:
- properties配置文件不能加载中文,我们需要把中文用Unicode转码器转换成Unicode编码形式,再写入配置文件中。
- 在短信验证中,我们一般把验证码放入redis中。并设置消失时间。
当我们调用发送短信接口的时候,尽量使用post请求的方式:
- 因为我们要传入签名等中文数据,用post请求,web.xml配置的编码过滤器默认解决,
- 如果要使用get请求,我们需要把传入的中文参数,用utf8进行转换格式。
new String(中文参数.getBytes("ISO8859-1"),"UTF-8");
day15
CAS单点登录
为什么要使用单点登录:
如果一个系统中存在诸多子系统,而这些子系统分别部署在不同的服务器中,若要实现用户只需要登录一次就可以访问所有相互信任的应用系统。用传统方式的session无法解决,所有要使用单点登录的技术。
CAS客户端四个核心过滤器:
SingleSignOutFilter:单点退出过滤器(用户退出)
AuthenticationFilter:身份认证过滤器(登录才能访问的资源)
Cas20ProxyReceivingTicketValidationFilter:票据验证过滤器(验证ST)
HttpServletRequestWrapperFilter:请求包裹过滤器(获取登录用户名)
CAS可以整合SpringSecurity:
- 需要在里面配置7个bean,配置文件比较麻烦,不需要记忆。
购物车
购物车的逻辑:
- 当用户在未登录的情况下,将购物车数据存入Cookie。(用户浏览器)
- 当用户在登录的情况下,将购物车数据存入Redis。(适合长时间存储,读写速度快)
- 如果用户登录时,需要将Cookie中的购物车数据合并到Redis中存储,并删除Cookie中的购物车数据。
注意事项:
- Cookie存储集合时,先把集合转换成json格式的字符串,再存储。
- Cookie中不能存储中文数据,所以如果数据中有中文, 需要存的时候将数据进行编码,取的时候将数据进行解码。
- 一般我们对Cookie操作时,都会用Cookie工具类。
Day16
如果项目整合了CAS,如果要获取用户名,可以配置不需要跳转CAS登录系统的身份认证过滤器,采用网关异步方式认证:
- 采用异步请求的方式,如果用户没有登录,不会跳转到CAS登录系统,只是用来过去ST服务票据,再获取用户名。
获取用户名的方法:
- request.getRemoteUser()
注意要配置:
<!-- 配置gateway=true”时,启用异步方式认证 -->
<init-param>
<param-name>gateway</param-name>
<param-value>true</param-value>
</init-param>
我们用点击事件或者a标签超链接访问sso系统(CAS),如果访问完了,想重定向回原来的界面,那么需要把重定向的url作为参数,且参数名为service。此时我们需要注意两个点:
1)如果我们访问的地址用nginx进行拦截,那么nginx默认会向tomcat传送端口号。使得我们页面上自己输入的地址,与服务器接受的地址不一样,会发生错误。
解决方案:在nginx的nginx.conf里面,配置:
- proxy_set_header Host $host
- 只传域名进tomcat,不带ip端口。
2)service参数名的url参数,直接传入浏览器栏不会被CAS重定向解析。我们在传入url参数之前,要对url参数进行编码,以便CAS重定向进行解析。且浏览器的url我们可以用:location.href 获取。
<a href="http://sso.pinyougou.com/logout?service={{redirectUrl}}">退出</a>
<a href="http://sso.pinyougou.com?service={{redirectUrl}}">登录</a>
// 定义重定向URL
$scope.redirectUrl = window.encodeURIComponent(location.href);
用:window.encodeURIComponent( )进行编码
前端设置显示数字精确度
使用toFixed()方法
- 数值类型.toFixed(i);
- 保留小数点后几位。
day17
什么是跨域请求:
- 协议、域名、端口有任何一个不同,都被当作是不同的域,就是跨域请求。
- 只有发送异步请求才会有跨域请求。
- JavaScript同源策略的限制,js只能获取自己域名下的资源,不能跨域请求资源。
JS跨域请求解决方案:
- JSONP(JSON的一种使用模式)
- CORS(跨域资源共享)
CORS
前端:
1)如果要操作Cookie,必须在AJAX请求中设置withCredentials属性为true,同意浏览器发送Cookie。这样才能操作cart.pinyougou.com域名下的Cookie。即使服务器端同意,也不能访问。
/** 添加商品到购物车 */
$scope.addToCart = function(){
$http.get("http://cart.pinyougou.com/cart/addCart?itemId="
+ $scope.sku.id + "&num="
+ $scope.num, {"withCredentials":true})
.then(function(response){
if (response.data){
/** 跳转到购物车页面 */
location.href='http://cart.pinyougou.com/cart.html';
}else{
alert("请求失败!");
}
});
};
2)如果不操作Cookie则可以不设置。
后端:
有两种方法:
1)编码设置响应头
在要访问的跨域的方法添加代码:
/** 设置允许访问的域名 */
response.setHeader("Access-Control-Allow-Origin","http://item.pinyougou.com");
/** 设置允许操作Cookie */
response.setHeader("Access-Control-Allow-Credentials","true");
2)@CrossOrigin():SpringMVC跨域请求注解。
在需要跨域的方法上添加注解@CrossOrigin
/** 添加商品到购物车 */
@GetMapping("/addCart")
@CrossOrigin(origins="http://item.pinyougou.com",
allowCredentials="true")
public boolean addCart(Long itemId, Integer num){
}
- allowCredentials="true" 允许访问Cookie,可以缺省。
- 若origins设置多个值,可以用大括号括起来。例如: origins={" "," "}
分布式ID生成器
一般在高并发的情况,例如添加订单表数据的时候,我们订单表的id一般都不会在数据库设置自增长,因为高并发,数据库自增长线程不安全,容易引起订单主键的重复。故要使用分布式ID生成器,因为线程安全。
- 自身有分布式ID生成器的工具类。
- idWorker.nextId( ) : 线程安全,且生成的数字有顺序。
day18
生成二维码有两种方式:
- Qrious插件
- zxing框架(推荐使用)
使用zxing框架生成二维码时,获取中间的商标图片的方法:
BufferedImage logo = ImageIO.read(new File(this.getClass().getResource("/logo.jpg").getPath()));
微信支付接口调用的整体思路:
- 按API要求组装参数,以XML方式发送(POST)给微信支付接口(URL),微信支付接口也是以XML方式给予响应。程序根据返回的结果(其中包括支付URL)生成二维码或判断订单状态。
微信支付开发文档:
我们一般常用两组API:
统一下单
返回的主要参数:
- 二维码连接,code_url
查询订单
返回的主要参数:
- 交易状态,trade_state
关闭订单
返回的主要参数:
- 业务结果,result_code
我们主要会用到微信支付SDK的WXPayUtil.java工具类的以下功能:
1)生成随机字符串
- WXPayUtil.generateNonceStr()
2)Map集合转换为XML字符串(自动添加签名)
- WXPayUtil.generateSignedXml(param, partnerkey)
3)XML字符串转换为Map集合
- WXPayUtil.xmlToMap(result)
支付日志的思路:
- 支付日志对象放入redis中,以用户ID作为key。并把日志对象存入数据库中。
- 支付成功后,修改日志对象与订单在数据库中的支付状态,并把redis中的日志对象删除。
day19
秒杀商品通常有两种限制:
- 库存限制
- 时间限制
秒杀技术实现核心思想是:
- 运用缓存减少数据库瞬间的访问压力。
关于库存的注意事项:
- 减库存时,有要注意高并发的问题。最好加上线程锁还有分布式锁,采用消息中间件的方法,点对点的模式。
- 结算,加入购物车时不用减库存。提交订单时,再减库存。
分布式锁有三种:
- redis
- zookeeper
- 数据库
angular的定时服务:
1)$interval (不停的调用)(定时器服务)
- var timer = $interval(调用的函数,间隔的毫秒数,调用的总次数);
取消定时器:
- $interval.cancel(timer);
执行完定时器的回调函数:
- timer.then(调用的函数);
2)$timeout(只执行一次)(延迟定时服务)
- $timeout(执行的函数,间隔的毫秒数);
取消定时器:
- $timeout.cancel(函数);
day20
常见的任务调度框架有:
- Quartz
- SpringTask
使用SpringTask使用注解:@Scheduled
Cron表达式是一个字符串,字符串分6个域,每一个域代表一个含义,Cron语法格式:
- Seconds Minutes Hours DayofMonth Month DayofWeek
网上有生成器,不需要会写。
集群的概述:
- 通过一组松散集成的计算机软件与硬件连接起来高度紧密协作完成计算工作。
- 某种意义上集群也被看成一台计算机,集群中的单个系统被称为一个节点。
- 集群的服务器都只做一件事。
集群的特点:
1)高扩展性
- 新的服务实体可以动态添加到集群,从而增强集群性能。
2)高可用性
- 集群中的一个节点发生故障时,这台节点上面运行的应用程序将在另一台结点自动接管,消除单点故障,增强数据可用性、可达性和可靠性。
集群的两大能力:
1)负载均衡
- 负载均衡把任务比较均匀的分布到集群环境下的计算机和网络资源,以提高数据吞吐量。
2)错误恢复
- 集群中的某一台服务器由于故障过着维护无法使用,资源和应用程序将转移到可用的集群节点上。
集群与分布式的区别:
相同点:
- 分布式和集群都是需要有多个节点服务器通过网络协同工作完成整体的任务目标。
不同点:
- 分布式是指将业务系统进行拆分,即分布式的每一个节点都是实现不同的功能。
- 而集群每个节点做的是同一件事。
Nginx负载均衡调度算法:
1)轮询(默认)
- 每个请求按时间顺序逐一分配到不同的服务器,如果当前服务器down掉,则会跳转到下一胎服务器。
upstream pinyougou-manager-web {
server 192.168.12.132:8080;
server 192.168.12.132:8081;
}
2)weight(权重)
- 在轮询的基础上加上权重,weight和访问比成正比,即用于表明服务器的性能好坏,若服务器性能较好则可将大部分请求分配给它,已实现其力所能及。
- 分配的权值只能为整数。
upstream pinyougou-manager-web {
server 192.168.12.132:8080 weight=2;
server 192.168.12.132:8081 weight=1;
}
3)ip_hash(ip哈希值)
- 每个请求按访问ip的hash结果分配,当新的请求到达时,先将其客户端IP通过哈希算法进行哈希出一个值,在随后的请求客户端Ip的哈希值只要相同,就会被分配至同一个后端服务器。该调度算法可以解决session的问题。
upstream pinyougou-manager-web {
ip_hash;
server 192.168.12.132:8080;
server 192.168.12.132:8081;
}
什么是Mycat:
- 数据库分库分表中间件。
Mycat的特点:
- 支持事务,ACID,可以替代MySQL的加强版数据库。
- 可以视为MySQL集群的企业级数据库。
- 融合内存缓存技术,NoSQL技术,HDFS大数据的新型SQL Server。
Mycat注意事项:
- MyCat的帐号密码在server.xml中进行配置。
- 程序操作MyCat实际上操作的是Mysql。