已知當前頁pageNum和總頁數totalPages,根據每頁要求顯示的頁碼列表的條數,得出頁碼列表的頭startPage和尾endPage,在頁面循環列表,便可展現出分頁條。javascript
分頁列表頭和尾的肯定:假設頁面要求顯示10個頁碼,那麼能夠選擇以當前頁前4後5的顯示方式,肯定顯示方式後,即可以計算頭尾以構建分頁列表。html
Integer startPage = pageNum - 4;
Integer endPage = pageNum + 5;
if (startPage < 1){ //判斷列表頭startPage是否 < 1
startPage = 1;
endPage = startPage + 9;
}
if (endPage > totalPage){ //判斷列表尾endPage是否 > totalPages
endPage = totalPage;
startPage = endPage - 9;
}
在頁面獲取以startPage爲頭endPage爲尾的列表,並循環展現分頁條前端
前端請求參數:pageNo、pageSize、QueryParamsvue
後端響應數據:java
html異步請求:git
PageBean<T> {pageNo,pageSize,totalCount,totalPage,List<T> result,QueryParams}github
能夠在js中定義startPage和endPageweb
模板頁同步請求:redis
PageBean<T> {pageNo,pageSize,totalCount,totalPage,List<T> result,QueryParams,startPage,endPage}spring
是否選中狀態的通用思路:定義一個變量n記錄選中狀態的狀態值,而後把當前變量與n比較,若是當前變量等於n記錄的選中狀態值,那麼當前變量即選中狀態
這裏循環頁碼列表獲取的變量page和當前頁pageNum(記錄選中狀態的變量n)比較,若是page==pageNum,那麼page對應的頁碼列表中頁碼即開啓選中狀態
<li th:class="${page==pageNo?'active':''}" th:each="page:${#numbers.sequence(startPage,endPage)}">
<a th:href="${#strings.replace(url,'&pageNo='+pageNo,'&pageNo='+page)}" th:text="${page}"></a>
</li>
引入分頁助手
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>3.7.5</version>
</dependency>
加強
thymeleaf與vue.js不一樣之處在於thymeleaf是在服務端渲染完成後,再將靜態頁面發給前臺,相比前端vue.js發送異步請求獲取數據後,再在前臺進行渲染的狀況, thymeleaf不會出現頁面內容延遲加載的情況;
thymeleaf與jsp同樣, 都是服務端渲染頁面, 但thymeleaf相比jsp具備更強大的功能, 同時thymeleaf也是springboot官方推薦的渲染引擎, thymeleaf能夠類比爲更高級的jsp
添加thymeleaf依賴
<dependencies>
<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.11.RELEASE</version>
</dependency>
</dependencies>
在applicationContext-thymeleaf.xml中配置模版引擎、視圖解析器、模版解析器
<!-- 配置模板引擎 -->
<bean id="templateEngine"
class="org.thymeleaf.spring5.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver"/>
</bean>
<!-- 配置視圖解析器 -->
<bean id="viewResolver" class="org.thymeleaf.spring5.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine"/>
<property name="characterEncoding" value="UTF-8"/>
</bean>
<!-- 配置模板解析器 -->
<bean id="templateResolver"
class="org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/"/>
<property name="suffix" value=".html"/>
<property name="characterEncoding" value="UTF-8"/>
<property name="templateMode" value="HTML"/>
</bean>
在webapp/WEB-INF目錄下建立模板資源test.html
建立上下文對象Context, 用於向模版傳遞數據模型Map
//1. 建立上下文對象,傳遞數據模型
Context context = new Context();
Map<String,Object> dataModel = new HashMap<>();
//查詢頁面渲染須要的數據(主要業務邏輯,此處略...)
dataModel.put("name",object);
context.setVariables(dataModel);
建立目標文件輸出流對象PrintWriter,調用模板引擎的process方法向生成靜態頁面目標文件
標籤 | 說明 | 示例 |
---|---|---|
th:text | 指定文本內容 | <p th:text="${collect.description}"></p> |
th:utext | 支持html內容渲染 | <p th:utext="${htmlcontent}"></p> |
th:each | 1. 循環列表,userStat爲循環狀態量, 有index/count/current/even/odd/first/last等屬性;2. 循環map集合,param.key、param.value分別獲取map的鍵值 | <tr th:each="user:${users}"></tr>或者<tr th:each="param : ${params}" th:text = "${param.key + ':' + param.value}"></tr> |
th:if | 判斷是否顯示當前標籤 | <a th:if="${userId == collect.userId}" > |
th:href | 連接地址 | <a th:href="${url}"/> |
th:src | 圖片類地址引入 | <img th:src="${user.image}" /> |
th:inline | 定義js腳本可使用渲染變量數據,使用方法:url = url.replace("&pageNo="+/星號[[${pageNo}]]星號/, "&pageTo="+pageTo) | <script th:inline="javascript"> |
numbers.formatDecimal | 保留兩位小數 | ${#numbers.formatDecimal(arg,0,2) } |
numbers.sequence | 以startPage、endPage爲頭尾生成列表,默認步長值爲1 | ${#numbers.sequence(startPage,endPage)} |
strings.replace | 替換字符串 | ${#strings.replace(url,'abc','cba')} |
strings.startWith | 判斷字符串是否以某子串開頭(boolean) | ${#strings.startsWith(url,'http')} |
strings.substring | 截取字符串子串,指定開始索引 | ${#strings.substring(url,5) |
maps.containsKey | 判斷Map集合是否包含某個key | ${#maps.containsKey(searchMap,'category')} |
<!-- 鏈接redis服務器 -->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>2.9.0</version>
</dependency>
<!-- 引入spring-data-redis -->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>2.0.5.RELEASE</version>
</dependency>
SpringDataRedis底層封裝了jedis,因此先配置JedisConnectionFactory,再注入到RedisTemplate中
<!-- Jedis鏈接配置對象JedisPoolConfig -->
<bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
<property name="maxIdle" value="${redis.maxIdle}" />
<property name="maxWaitMillis" value="${redis.maxWait}" />
</bean>
<!-- Jedis鏈接工廠JedisConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:pool-config-ref="poolConfig"/>
<!-- spring提供的模板工具redisTemplate -->
<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate">
<property name="connectionFactory" ref="jedisConnectionFactory" />
</bean>
對象操做:boundValueOps
public void setValue(){
redisTemplate.boundValueOps("name").set("青橙電商");
}
public void getValue(){
String name = (String) redisTemplate.boundValueOps("name").get();
System.out.println(name);
}
public void deleValue(){
redisTemplate.delete("name");
}
Set集合操做:boundSetOps
public void setValue(){
redisTemplate.boundSetOps("nameSet").add("曹操");
redisTemplate.boundSetOps("nameSet").add("劉備");
redisTemplate.boundSetOps("nameSet").add("孫權");
}
public void getValue(){
Set nameSet = redisTemplate.boundSetOps("nameSet").members();
System.out.println(nameSet);
}
public void deleValue(){
redisTemplate.boundSetOps("nameSet").remove("曹操");
System.out.println(redisTemplate.boundSetOps("nameSet").members());
redisTemplate.delete("nameSet");
}
Map集合操做:boundHashOps
public void setValue(){
redisTemplate.boundHashOps("nameHash").put("0","唐僧");
Map<String,String> map = new HashMap();
map.put("1","悟空");
map.put("2","八戒");
map.put("3","沙僧");
redisTemplate.boundHashOps("nameHash").putAll(map);
}
public void getValue(){
String name = (String) redisTemplate.boundHashOps("nameHash").get("1");
System.out.println(name);
}
public void deleValue(){
redisTemplate.boundHashOps("nameHash").delete("2");
System.out.println(redisTemplate.boundHashOps("nameHash").values());
}
有序隊列操做(存取順序):boundListOps
public void setValue(){
redisTemplate.boundListOps("nameList").rightPush("中間值");
redisTemplate.boundListOps("nameList").rightPush("右壓棧");
redisTemplate.boundListOps("nameList").leftPush("左壓棧");
redisTemplate.boundListOps("nameList").rightPush("測試刪除的索引位置");
redisTemplate.boundListOps("nameList").rightPush("測試刪除");
redisTemplate.boundListOps("nameList").rightPush("測試刪除");
}
public void getValue(){
List nameList = redisTemplate.boundListOps("nameList").range(0, -1);
System.out.println(nameList);
//按索引取值
String valueOfIndex = (String) redisTemplate.boundListOps("nameList").index(1);
System.out.println(valueOfIndex);
}
public void deleValue(){
//remove(刪除的元素個數, 刪除的元素值); 按索引從小到大查詢指定個數的元素並刪除
redisTemplate.boundListOps("nameList").remove(2,"測試刪除");
System.out.println(redisTemplate.boundListOps("nameList").range(0,-1));
}
有序隊列操做(須要常常發生順序變更的狀況):boundZSetOps
public void setValue(){
redisTemplate.boundZSetOps("nameZset").add("曹操",10000);
redisTemplate.boundZSetOps("nameZset").add("劉備",1000);
redisTemplate.boundZSetOps("nameZset").add("孫權",10);
}
public void getValue(){
//分數由低到高排序(默認)
Set<String> nameZset1 = redisTemplate.boundZSetOps("nameZset").range(0, -1);
System.out.println(nameZset1);
//分數由高到低排序(逆向)
Set<String> nameZset2 = redisTemplate.boundZSetOps("nameZset").reverseRange(0, -1);
System.out.println(nameZset2);
//帶分數取值
Set<ZSetOperations.TypedTuple> nameTupleSet = redisTemplate.boundZSetOps("nameZset").rangeWithScores(0, -1);
for (ZSetOperations.TypedTuple nameTuple : nameTupleSet) {
System.out.println(nameTuple.getValue()+"..."+nameTuple.getScore());
}
}
public void incrementScore(){
redisTemplate.boundZSetOps("nameZset").incrementScore("孫權",1190);
System.out.println(redisTemplate.boundZSetOps("nameZset").range(0,-1));
}
public void deleValue(){
redisTemplate.boundZSetOps("nameZset").remove("孫權");
System.out.println(redisTemplate.boundZSetOps("nameZset").range(0,-1));
}
通用操做:
redisTemplate. delete(redisKey); 刪除redisKey-value的方法 ; . redisTemplate. boundXxxOps(). expire(long , TimeUnit); 指定大Key的存活時間(短信驗證碼)
取不到對象, 返回null; 取不到集合, 返回[ ]
LocalDate不可用做前端傳參,能夠用String接收日期對象,再用LocalDate轉換
LocalDate:表示默認格式yyyy-MM-dd的日期對象
LocalDate today = LocalDate.now():獲取當前日期對象
LocalDate firstDayOf2019 = LocalDate.of(2019, Month.JANUARY, 1):獲取指定日期對象
LocalTime:表示默認格式hh:mm:ss.zzz的時間對象
LocalTime time = LocalTime.now():獲取當前時間對象
LocalTime specTime = LocalTime.of(23,23,23,23):獲取指定時間對象23:23:23.023
LocalDateTime:表示日期+時間對象
LocalDateTime time = LocalDateTime.now():獲取當前日期時間對象
LocalDateTime firstDayOf2019 = LocalDate.of(2019, Month.JANUARY, 1,0,0,0,0):獲取指定日期時間對象
API | 說明 |
---|---|
LocalDate#isBefore(LocalDate date) | 判斷是否在指定日期以前 |
LocalDate#plusDays/plusWeeks/plusMonths/minus.. | 從當前日期加減日、周、月後獲得日期 |
Period period LocalDate#until(LocalDate date) | 獲取到指定日期之間的天數 |
LocalDate#format(DateTimeFormatter.ofPattern("dd-MM-yyyy")) | 按指定格式格式化日期對象 |
LocalDate.parse("yyyy-MM-dd") | 解析日期字符串 |
<dependency>
<groupId>com.aliyun</groupId>
<artifactId>aliyun-java-sdk-core</artifactId>
<version>4.0.3</version>
</dependency>
accessKeyId=LTAIfWgh9uJxqyJM
accessSecret=NMzCzy7mGry292XYlaTRxVmRVOsDL7
#短信模版號
smsCode=SMS_173341860
#生成的驗證碼
param={"code":"[value]"}
public class SmsUtil {
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.version}</version>
</dependency>
<!--基本配置:
1. 非目標資源解除攔截規則、目標資源攔截規則(<http>)
2. 認證管理器:自定義認證信息提供者(UserDetailsService);密碼加密策略(BCrypt)-->
<!-- 1. 配置頁面的攔截規則 -->
<!-- 1.1 解除非目標資源的攔截規則 -->
<http pattern="/login.html" security="none"></http>
<http pattern="/login_error.html" security="none"></http>
<!-- 1.2 配置目標資源(全部資源)必須擁有ROLE_ADMIN的角色才能訪問 -->
<http>
<intercept-url pattern="/**" access="hasRole('ROLE_ADMIN')"/>
<!-- 指定登錄頁面/目標訪問頁面/認證失敗頁面, 全部的頁面必須使用絕對路徑配置 -->
<form-login login-page="/login.html" default-target-url="/index.html" authentication-failure-forward-url="/login_error.html" />
<!-- 退出登陸 -->
<logout />
<!-- 關閉csrf驗證: 跨站請求僞造,csrf會隨機產生一個token放到security提供的默認表單中,由於靜態頁沒法動態生成token,因此將此功能關閉。通常靜態頁採用圖形驗證碼的方式實現防止跨域請求僞造的功能。 -->
<csrf disabled="true"/>
</http>
<!-- 2. 配置認證管理器,獲取來自數據庫的認證信息 -->
<authentication-manager>
<authentication-provider user-service-ref="userDetailsService">
<password-encoder ref="bcryptEncoder"></password-encoder>
</authentication-provider>
</authentication-manager>
<!-- 2.1 配置自定義認證信息提供者UserDetailsService -->
<beans:bean id="userDetailsService" class="com.qingcheng.demo.UserDetailsServiceImpl"></beans:bean>
<!-- 2.2 加密策略:指定由spring-security提供的相應的解密類 -->
<beans:bean id="bcryptEncoder" class="org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder"></beans:bean>
</beans:beans>
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import java.util.ArrayList;
import java.util.List;
/** UserDetailsService是spring-security提供的用於從數據庫獲取用戶認證信息,並用UserDatils接口把"認證信息"傳給認證管理器的工具接口
*/
public class UserDetailsServiceImpl implements UserDetailsService {
public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
List<GrantedAuthority> grantedAuthorities = new ArrayList<GrantedAuthority>();
grantedAuthorities.add(new SimpleGrantedAuthority("ROLE_ADMIN"));
//參數三爲用戶所具備的認證角色集合
return new User(username,"$2a$10$amAQ3ClrcXV5K9gg6fsbfuUtdgliLTGvfzZuN3toBDAkbSK1nQliC",
grantedAuthorities);
}
}
Collection體系集合
使用默認方法stream()生成流, default Stream<E> stream()
Map體系集合
把Map轉成Set集合,間接的生成流
數組
經過Stream靜態方法 Stream.of(T... values) 生成流
public class StreamDemo {
public static void main(String[] args) {
//Collection體系的集合可使用默認方法stream()生成流
List<String> list = new ArrayList<String>();
Stream<String> listStream = list.stream();
Set<String> set = new HashSet<String>();
Stream<String> setStream = set.stream();
//Map體系的集合間接的生成流
Map<String,Integer> map = new HashMap<String, Integer>();
Stream<String> keyStream = map.keySet().stream();
Stream<Integer> valueStream = map.values().stream();
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//數組能夠經過Stream接口的靜態方法of(T... values)生成流
String[] strArray = {"hello","world","java"};
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> strArrayStream2 = Stream.of("hello", "world", "java");
Stream<Integer> intStream = Stream.of(10, 20, 30);
}
}
方法名 | 說明 |
---|---|
Stream<T> filter(Predicate predicate) | 用於對流中的數據進行過濾 |
Stream<T> limit(long maxSize) | 返回此流中的元素組成的流,截取前指定參數個數的數據 |
Stream<T> skip(long n) | 跳過指定參數個數的數據,返回由該流的剩餘元素組成的流 |
static <T> Stream<T> concat(Stream a, Stream b) | 合併a和b兩個流爲一個流 |
Stream<T> distinct() | 返回由該流的不一樣元素(根據Object.equals(Object) )組成的流 |
Stream<T> sorted() | 返回由此流的元素組成的流,根據天然順序排序 |
Stream<T> sorted(Comparator comparator) | 返回由該流的元素組成的流,根據提供的Comparator進行排序 |
<R> Stream<R> map(Function mapper) | 返回由給定函數應用於此流的元素的結果組成的流 |
IntStream mapToInt(ToIntFunction mapper) | 返回一個IntStream其中包含將給定函數應用於此流的元素的結果 |
void forEach(Consumer action) | 對此流的每一個元素執行操做 |
List collect(Collectors. toList()) | 把元素收集到List集合中 |
Set collect(Collectors. toSet()) | 把元素收集到Set集合中 |
Map collect(Collectors. toMap(Function keyMapper,Function valueMapper)) | 把元素收集到Map集合中:(keyMapper,valueMapper)分別表示從流中元素獲取key和value的方法 |
filter、skip、limit、forEach、collect
public class StreamDemo01 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("林青霞");
list.add("張曼玉");
list.add("王祖賢");
list.add("柳巖");
list.add("張敏");
list.add("張無忌");
//需求1:把list集合中以張開頭的,長度爲3的元素在控制檯輸出
list.stream().filter(s -> s.startsWith("張")).filter(s -> s.length() == 3).forEach(System.out::println);
//需求2:跳過2個元素,把剩下的元素中前2個取出存入集合中
List newList = list.stream().skip(2).limit(2).collect(Collectors.toList);
}
}
map、mapToInt —— IntStream. min() max() sum();
public class StreamDemo05 {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<String>();
list.add("10");
list.add("20");
list.add("30");
list.add("40");
list.add("50");
//int sum() 返回此流中元素的總和
int result = list.stream().mapToInt(Integer::parseInt).sum();
System.out.println(result);
}
}
Mongodb存儲的數據是JSON格式的,相比較redis而言能夠存儲大的文件數據。一方面mongodb能夠靈活存儲json,另外一方面存儲的數據相比於數據庫的核心數據不過重要,且沒有事務管理要求。
1. model
2. dao
public interface CmsPageRepository extends MongoRepository<CmsPage,String> {
}
3. 基本方法
//1. 查詢全部
List<CmsPage> all = cmsPageRepository.findAll();
//2. 分頁查詢
int page = 0; //頁碼從0開始
int size = 10;
Pageable pageable = PageRequest.of(page,size);
Page<CmsPage> all = cmsPageRepository.findAll(pageable);
List<CmsPage> cmsPageList = all.getContent(); //獲取數據
long total = all.getTotalElements(); //獲取記錄數
//3. 條件查詢
//建立條件值對象
CmsPage cmsPage = new CmsPage();
//設置條件值
cmsPage.setSiteId("5a751fab6abb5044e0d19ea1");
cmsPage.setTemplateId("5a962b52b00ffc514038faf7");
cmsPage.setPageAliase("導航");
//ExampleMatcher.GenericPropertyMatchers.contains() 包含關鍵字"導航"匹配
//ExampleMatcher.GenericPropertyMatchers.startsWith() 前綴匹配
ExampleMatcher exampleMatcher = ExampleMatcher.matching().withMatcher("pageAliase", ExampleMatcher.GenericPropertyMatchers.contains()); // 設置pageAliase字段模糊匹配
//建立條件實例
Example<CmsPage> example = Example.of(cmsPage,exampleMatcher);
Page<CmsPage> all = cmsPageRepository.findAll(example, pagebale);
//4. 添加
CmsPage cmsPage = cmsPageRepository.save(cmsPage);
//根據id查詢
Optional<CmsPage> optional = cmsPageRepository.findById(id);
if (optional.isPresent()){
CmsPage cmsPage = optional.get();
return cmsPage;
}
return null;
GridFS是Mongodb提供的用於持久化存儲文件的模塊。它的工做原理是將文件按256kb的大小分割進行存儲,GridFS用兩個collection存儲文件,一個是chunks,用於存儲文件的二進制數據,一個是files,用於存儲文件的元數據(文件名稱、塊大小、上傳時間等)
1. GridFS存儲文件
GridFS獲取文件
OpenAPI規範(OpenAPI Specification 簡稱OAS)是Linux基金會的一個項目,試圖經過定義一種用來描述API格式或API定義的語言,來規範RESTful服務開發過程
Swagger是全球最大的OpenAPI規範(OAS)API開發工具框架,支持從設計和文檔到測試和部署的整個API生命週期的開發。Spring Boot 能夠集成Swagger,生成Swagger接口。
註解 | 說明 |
---|---|
@Api | 修飾整個類,描述Controller的做用 |
@ApiOperation | 描述一個類的一個方法,或者說一個接口 |
@ApiParam | 單個參數描述 |
@ApiModel | 用對象來接收參數 |
@ApiModelProperty | 用對象接收參數時,描述對象的一個字段 |
@ApiResponse | HTTP響應其中1個描述 |
@ApiResponses | HTTP響應總體描述 |
@ApiError | 發生錯誤返回的信息 |
@ApiImplicitParam | 一個請求參數 |
@ApiImplicitParams | 多個請求參數 |
@ApiImplicitParam的屬性:
屬性 | 取值 | 做用 |
---|---|---|
paramType | 查詢參數類型 | |
path | 以地址的形式提交數據 | |
query | 直接跟參數完成自動映射賦值(地址欄拼接查詢條件,用對象屬性封裝) | |
body | 以流的形式提交 僅支持POST(JSON串提交,用@Requestbody接收) | |
header | 參數在request headers 裏邊提交 | |
form | 以form表單的形式提交 僅支持POST | |
dataType | 參數的數據類型 只做爲標誌說明,並無實際驗證 | |
Long | ||
String | ||
name | 接收參數名 | |
value | 接收參數的意義描述 | |
required | 參數是否必填 | |
true | 必填 | |
false | 非必填 | |
defaultValue | 默認值 |
在xc-service-api工程下建立config.Swagger2Configuration類,啓動項目,訪問localhost:31001/swagger-ui.html便可開啓測試