對于LocaleResolver,其主要作用在于根據(jù)不同的用戶區(qū)域展示不同的視圖,而用戶的區(qū)域也稱為Locale,該信息是可以由前端直接獲取的。通過這種方式,可以實現(xiàn)一種國際化的目的,比如針對美國用戶可以提供一個視圖,而針對中國用戶則可以提供另一個視圖。本文主要講解如果使用LocaleResolver來實現(xiàn)對用戶不同視圖切換的目的。
LocaleResolver是Spring提供的一個接口,其聲明如下:
public interface LocaleResolver { // 根據(jù)request對象根據(jù)指定的方式獲取一個Locale,如果沒有獲取到,則使用用戶指定的默認(rèn)的Locale Locale resolveLocale(HttpServletRequest request); // 用于實現(xiàn)Locale的切換。比如SessionLocaleResolver獲取Locale的方式是從session中讀取,但如果 // 用戶想要切換其展示的樣式(由英文切換為中文),那么這里的setLocale()方法就提供了這樣一種可能 void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale); }
針對LocaleResolver,Spring提供了幾種實現(xiàn)方式,分別如下:
需要說明的是,Spring雖然提供的幾個不同的獲取Locale的方式,但這些方式處理FixedLocaleResolver以外,其他幾個也都支持在瀏覽器地址欄中添加locale參數(shù)來切換Locale。對于Locale的切換,Spring是通過攔截器來實現(xiàn)的,其提供了一個LocaleChangeInterceptor,在該攔截器中的preHandle()方法中,Spring會讀取瀏覽器參數(shù)中的locale參數(shù),然后調(diào)用LocaleResolver.setLocale()方法來實現(xiàn)對Locale的切換。
這里我們以CookieLocaleResolver為例來講解如何通過不同的Locale展示不同的視圖。首先是我們的xml文件配置:
<context:component-scan base-package="mvc"/> <mvc:annotation-driven/> <mvc:interceptors> <bean class="org.springframework.web.servlet.i18n.LocaleChangeInterceptor"/> <bean class="mvc.interceptor.MyHandlerInterceptor"/> </mvc:interceptors> <bean class="org.springframework.web.servlet.view.ResourceBundleViewResolver"/> <bean id="localeResolver" class="org.springframework.web.servlet.i18n.CookieLocaleResolver"> <property name="defaultLocale" value="zh_CN"/> </bean>
關(guān)于上述配置有三點需要說明:
對于后臺接口的聲明,其與一般的接口聲明是沒有區(qū)別的。如下是我們聲明的一個接口:
@Controller @RequestMapping("/user") public class UserController { @Autowired private UserService userService; @RequestMapping(value="/detail", method=RequestMethod.GET) public ModelAndView detail(@RequestParam("id") long id, @ModelAttribute("message") String message, Locale locale) { System.out.println(message); ModelAndView view=new ModelAndView("user"); User user=userService.detail(id); view.addObject("user", user); view.addObject("locale", locale); return view; } }
上述接口返回的視圖為user,并且將當(dāng)前用戶的Locale信息返回給了前端,可以看到,這里獲取Locale數(shù)據(jù)的方式就只需要簡單的聲明一個類型為Locale的參數(shù)即可。
關(guān)于視圖的展示,由于我們需要根據(jù)不同的Locale展示不同的視圖,而在上述接口中,我們暫時沒發(fā)現(xiàn)這樣的路由。實際上,這個路由是通過ResourceBundleViewResolver類實現(xiàn)的,在使用該ViewResovler時,其會到class路徑下查找名稱為views的Resource Bundle,并且通過用戶指定的Locale,唯一定位到某個Resource Bundle。然后在該Resource Bundle中查找指定的視圖信息,比如這里接口返回的視圖為user,那么就會在獲取到的Resource Bundle查找user.(class)和user.url信息,這里user.(class)指定了展示該視圖所需要的View對象的,而user.url則指定了具體的視圖位置。比如如下配置的就是Locale分別為zh_CN和en_US的視圖:
# views_zh_CN.properties user.(class)=org.springframework.web.servlet.view.InternalResourceView user.url=/WEB-INF/view/user_zh_CN.jsp # views_en_US.properties user.(class)=org.springframework.web.servlet.view.InternalResourceView user.url=/WEB-INF/view/user_en_US.jsp
通過這種方式,ResourceBundleViewResolver就實現(xiàn)了針對不同的Locale來展示不同的視圖的目的。如下是我們編寫的兩個分別用于zh_CN和en_US視圖展示的jsp頁面:
<!-- user_zh_CN.jsp --> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>User Jsp-zh CN</title> </head> <body> ${user.id} ${user.name} ${user.age} ${locale} </body> </html> <!-- user_en_US.jsp --> <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>User Jsp-en US</title> </head> <body> ${user.id} ${user.name} ${user.age} ${locale} </body> </html>
啟動上述程序,我們在瀏覽器中鍵入http://localhost:8080/user/detail?id=1,可以看到其展示了如下視圖:
1 Bob 27 zh_CN
如果我們添加名稱為org.springframework.web.servlet.i18n.CookieLocaleResolver.LOCALE,值為en_US的cookie,那么其展示的頁面切換如下:
1 Bob 27 en_US
這說明我們成功使用Cookie對Locale進(jìn)行了切換。如果我們在瀏覽器地址欄中添加locale=zh_CN的參數(shù),可以看到,頁面展示又切換為了前面那種情況。
本文主要對LocaleResolver進(jìn)行了講解,并且演示了如何通過配置不同的LocaleResolver來達(dá)到實現(xiàn)展示不同的視圖的目的。需要注意的是,我們的LocaleResolver的bean名稱必須為localeResolver,并且需要指定的ViewResolver輔以支持,否則切換的視圖可能無法正常工作。
雖然但是聽到這個消息的時候,內(nèi)心還是挺震驚的,畢竟是一個完整的管理系統(tǒng),功能界面還不能太過簡陋。而且從數(shù)據(jù)庫設(shè)計到整個系統(tǒng)的交付全由自己一人完成,挑戰(zhàn)效果直接拉滿!但是冷靜下來思考一下,其實也并不是很難,整體的項目流程即為:設(shè)計——>文檔——>編碼——>交付。整體的流程劃清之后,就開始一步步從無到有的實現(xiàn),沒想到到最后一步的時候,我竟然才用一天半的時間!!后面又用了半天的時間對整體的項目做了一個優(yōu)化處理!
登錄模塊、用戶模塊管理以及對用戶的角色分配,新聞公告模塊的管理、商品模塊(包括對商品、商品分類、訂單)的管理、角色模塊的管理;對于前端某資源是否有權(quán)限操作該資源,使用的是thymeleaf模板語法進(jìn)行判斷鑒別以及文件上傳等基本功能。
引入相應(yīng)的依賴,構(gòu)建所需文件目錄
server:
port: 8080
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/supplier?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
username: root
password: root
# thymeleaf 配置
thymeleaf:
# 關(guān)閉緩存
cache: false
prefix: classpath:/templates/
mybatis-plus:
mapper-locations: classpath*:/mapper/**/*.xml
在搭建一個項目的初期,為了讓系統(tǒng)顯得更規(guī)范化,我一般會提前做好基礎(chǔ)的配置和聲明,一個項目從開始設(shè)想時所涉及到技術(shù)以及這些技術(shù)對應(yīng)的一些基礎(chǔ)配置,都要提前規(guī)劃清楚(個人習(xí)慣)。比如:異常處理、攔截器、過濾器、常量類等等。
①異常處理
@ControllerAdvice
public class ExceptionHandler {
private final org.slf4j.Logger logger=LoggerFactory.getLogger(this.getClass());
@org.springframework.web.bind.annotation.ExceptionHandler(Exception.class)
public ModelAndView exception(HttpServletRequest request, Exception e ) throws Exception {
logger.error("Request URL:{},Exception:{}",request.getRequestURL(),e);
if (AnnotationUtils.findAnnotation(e.getClass(), ResponseStatus.class )!=null){
throw e;
}
ModelAndView mv=new ModelAndView();
mv.addObject("url",request.getRequestURL());
mv.addObject("exception",e);
mv.setViewName("error/error");
return mv;
}
}
② 攔截器
攔截器主要是對一些資源做的處理,類似于某些資源需要用戶登錄后才能訪問的,某些是不需要的,比如:登錄功能就不需要有所攔截,而對用戶的各種管理就需要添加攔截操作,這樣才能使系統(tǒng)的安全性有所提高。
登錄攔截
public class LoginInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
if (request.getSession().getAttribute("user")==null){
response.sendRedirect("/api");
return false;
}
return true;
}
}
資源放行
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
.addPathPatterns("/api/**")
.excludePathPatterns("/api","/api/doLogin");
}
}
首先創(chuàng)建一個FileController類
① 跳轉(zhuǎn)文件上傳的頁面
//跳轉(zhuǎn)文件上傳的頁面
@RequestMapping("/file-upload")
public String userList(){
return "file-upload";
}
② 實現(xiàn)文件上傳的功能
@RequestMapping("/doAddForUser")
public String doAdd(User user, @RequestParam("file") MultipartFile files, HttpServletRequest request) throws IOException {
//String path=null;
if (files !=null && !files.isEmpty()){
String name=UUID.randomUUID().toString().replace("-","");
//獲取文件的擴(kuò)展名
String ext=FilenameUtils.getExtension(files.getOriginalFilename());
//設(shè)置文件上傳的路徑
String url=request.getSession().getServletContext().getRealPath("/upload/");
File file=new File(url);
if (!file.exists()){
file.mkdir();
}
//測試路徑
System.out.println(request.getServletPath()+ "/upload");
System.out.println(request.getContextPath() + "/upload/");
//以絕對路徑保存重命名后的文件
files.transferTo(new File(url+"/"+name+"."+ext));
user.setAvatar(request.getContextPath() + "/upload/"+name+"."+ext);
}
user.setId(UUID.randomUUID().toString());
String salt=PasswordUtils.getSalt();
String password=user.getPassword();
String encode=PasswordUtils.encode(password, salt);
user.setSalt(salt) ;
user.setPassword(encode);
user.setCreateTime(new Date());
userService.save(user);
return "redirect:/api/users";
}
注:如何想要實現(xiàn)多文件上傳需要更改的地方如下:
③ 實現(xiàn)多文件上傳功能
在這個項目中并未實現(xiàn)多文件上傳功能
private void commons(Object obj, @RequestParam("file") CommonsMultipartFile[] files, HttpServletRequest request) throws IOException {
//String path=null;
for (int i=0; i < files.length; i++) {
if (files[i] !=null && !files[i].isEmpty()){
String name=UUID.randomUUID().toString().replace("-","");
//獲取文件的擴(kuò)展名
String ext=FilenameUtils.getExtension(files[i].getOriginalFilename());
//設(shè)置文件上傳的路徑
String url=request.getSession().getServletContext().getRealPath("/upload/");
File file=new File(url);
if (!file.exists()){
file.mkdir();
}
//測試路徑
System.out.println(request.getServletPath()+ "/upload");
System.out.println(request.getContextPath() + "/upload/");
//以絕對路徑保存重命名后的文件
files[i].transferTo(new File(url+"/"+name+"."+ext));
if (i==0){
obj.setUrl1(request.getContextPath() + "/upload/"+name+"."+ext);
}
if (i==1){
obj.setUrl2(request.getContextPath() + "/upload/"+name+"."+ext);
}
if (i==2){
obj.setUrl3(request.getContextPath() + "/upload/"+name+"."+ext);
}
if (i==3){
obj.setUrl4(request.getContextPath() + "/upload/"+name+"."+ext);
}
if (i==4){
obj.setUrl5(request.getContextPath() + "/upload/"+name+"."+ext);
}
}
}
}
對于前后端不分離的項目,多數(shù)使用的是頁面緩存優(yōu)化,當(dāng)系統(tǒng)某一瞬間遭受巨大流量時,當(dāng)?shù)谝粋€用戶進(jìn)行頁面訪問時可以將該頁面數(shù)據(jù)進(jìn)行緩存,這樣,后來的用戶訪問到的頁面都是從緩存中獲取的,這樣就減少了 對數(shù)據(jù)庫的操作,減輕了數(shù)據(jù)庫的壓力,從而達(dá)到優(yōu)化的處理。
① 導(dǎo)入依賴
<!--Redis-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!--commons-pools2 對象池依賴-->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>
② yaml配置
## Redis配置
redis:
# 服務(wù)器地址
host: localhost
# 端口
port: 6379
# 數(shù)據(jù)庫
database: 0
# 超時時間
connect-timeout: 10000ms
lettuce:
pool:
# 最大連接數(shù)
max-active: 8
# 最大連接阻塞等待時間 默認(rèn) -1
max-wait: 10000ms
# 最大空閑時間 默認(rèn)8
max-idle: 200
# 最小空閑連接 默認(rèn)8
min-idle: 5
④ Redis序列化處理
@Configuration
public class RedisConfig {
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate<String,Object> redisTemplate=new RedisTemplate<>();
//key序列化
redisTemplate.setKeySerializer(new StringRedisSerializer());
//value序列化
redisTemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());
//hash類型key的序列化
redisTemplate.setHashKeySerializer(new StringRedisSerializer());
//hash類型value的序列化
redisTemplate.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());
redisTemplate.setConnectionFactory(redisConnectionFactory);
return redisTemplate;
}
}
③ 優(yōu)化處理
@Autowired
private NewsService newsService;
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private ThymeleafViewResolver viewResolver;
@RequestMapping(value="/news",produces="text/html;charset=utf-8")
@ResponseBody
public String roles(Model model, @RequestParam(value="pageNo",defaultValue="1")Integer pageNo
, @RequestParam(value="pageSize",defaultValue="10")Integer pageSize
, HttpServletRequest request, HttpServletResponse response){
//Redis中獲取頁面,如果不為空,則直接返回頁面
ValueOperations valueOperations=redisTemplate.opsForValue();
String html=(String) valueOperations.get("news-list");
if (!StringUtils.isEmpty(html)){
return html;
}
PageHelper.startPage(pageNo,pageSize);
List<News> list=newsService.list();
PageInfo<News> pageInfo=new PageInfo<>(list);
model.addAttribute("news",list);
model.addAttribute("pageInfo",pageInfo);
//如果為空,手動渲染,存入Redis中并返回
WebContext context=new WebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap());
html=viewResolver.getTemplateEngine().process("news-list", context);
if (!StringUtils.isEmpty(html)){
//給緩存設(shè)置過期時間
valueOperations.set("news-list",html,60, TimeUnit.SECONDS);
}
return html;
}
④ Redis查看
注意@Controller和@RestController的區(qū)別,本項目使用的是模板渲染頁面,而@Controller就是用來響應(yīng)頁面的;而@RestController是用來返回Json
在項目優(yōu)化階段需要在方法上添加注解@ResponseBody,因為我們是將整個頁面進(jìn)行緩存 ,所以要將頁面轉(zhuǎn)換成JSON進(jìn)行存儲。
注入Thymeleaf解析器,將具體的 頁面進(jìn)行解析成Json字符串進(jìn)行存儲
將存入Redis中的數(shù)據(jù)加上過期時間,因為頁面中的數(shù)據(jù)要和數(shù)據(jù)庫保持一致,如果用戶看到是幾十秒之前或一分鐘之前的數(shù)據(jù)還是勉強(qiáng)可以接受的。
目前代碼已經(jīng)同步到Gitee:
https://gitee.com/gao-wumao/supplier
來源:blog.csdn.net/Gaowumao?type=blog
視圖解析器(ViewResolver)是 Spring MVC 的重要組成部分,負(fù)責(zé)將邏輯視圖名解析為具體的視圖對象。
Spring MVC 提供了很多視圖解析類,其中每一項都對應(yīng)著 Java Web 應(yīng)用中特定的某些視圖技術(shù)。
viewResolver組件會將viewName解析成view對象,view對象會調(diào)用render完成結(jié)果的處理。
一些可參閱的博客:
http://c.biancheng.net/spring_mvc/view-resolver.html
https://blog.csdn.net/fengyuhan123/article/details/79723310
一些可參閱的博客:
https://blog.csdn.net/u013541707/article/details/108511017
官方文檔:https://docs.spring.io/spring-framework/docs/5.1.5.RELEASE/spring-framework-reference/web.html#mvc-view
?持的視圖列表:
在之前的RequestMapping的Handler也有提到過Converter,在視圖的部分也有自己對應(yīng)的HttpMessageConverter
“Thymeleaf is a modern server-side Java template engine for both web and standalone environments.” – https://www.thymeleaf.org/
*請認(rèn)真填寫需求信息,我們會在24小時內(nèi)與您取得聯(lián)系。