早就听说spring有很多组件,惭愧之前看视频只是掠过竟连名字也没有记住,刚才在思考我写的public class MyConfig implements WebMvcConfigurer类里的 public void addViewControllers(ViewControllerRegistry registry) 方法:
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/login").setViewName("login");
registry.addViewController("/show").setViewName("show");
}
这个方法也抢着映射了一些URL,和之前的controller的requestmapping有什么不同,查阅一番发现这个方法是因为总有一些请求不想过controller只想跳转到另一个页面,就可以在这个方法里设置,就相当于在controller里像下面这样写:
@RequestMapping("/login")
public String login(){
return "login";
}
不过是在controller里没有找到才会来这里找,返回的字符串或者上面方法的setviewname会交给viewresolver也就是视图解析器加上前缀后缀解析成具体的资源视图。
springboot和mvc都有默认的这个 InternalResourceViewResolver类,但是他们的前后缀没有默认值要自己配置,而且好像只能支持jsp?
我们自己配置了thymeleaf的模板引擎之后就全部都交给他解析了。
好!又认识两个组件viewcontroller和viewresolver!
首先观察作者的大体架构是什么样的,可以点左边的减号把小的标签折叠起来只留最大的标签,这样一层层的看:
可以看到结构非常的清晰,同时用到了4个模板,位置在哪呢?比如head的
th:replace=“mall/header::header-fragment”
mall/header就是模板的视图名,交给thymeleaf引擎解析后去classpath:templates/mall/header.html找模板,在这些地方也确实找到了就不截图了,thmyeleaf不知道的去网上随便看看半个小时就能看完,就像学if,for语句一样没什么难的,记住就行,thymeleaf的语法以后我就直接用了。
看到作者的模板后我们也在自己的项目templates文件夹下按他的目录结构把header和footer粘贴过来,同时下面的javascript也引用了很多资源我们也一并沾到static文件夹下:
然后随便打开一个标签比如那个id叫banner的:
会发现一个EL表达式,我们知道El表达式或者什么SPEl表达式是从上下文(page,cookie,session,context)或者配置文件拿值用的,这是在什么地方给他传了值,直接告诉你们是在作者的controller包下,让我们一起去看看:
作者的文件夹对应十分工整,名字也很好理解,我们一下就找到这里发现是拦截了一些请求,然后往里面塞了很多数据,先看塞的第一个数据
@Resource
private NewBeeMallCategoryService newBeeMallCategoryService;
List<NewBeeMallIndexCategoryVO> categories = newBeeMallCategoryService.getCategoriesForIndex();
是通过@resource注解自动装配的,点进去一看发现NewBeeMallCategoryService居然是个接口,这可把我搞慌了一下,后来发现service文件夹下还有一个imp文件夹,里面放着都是这些接口的实现类,正好8个对8个:
莫非自动注入的时候是接口的实现类也可以吗,我怎么从来没学过这个知识点(好像我在学aop的时候,当要代理的类实现了一个接口又使用jdk动态代理(除了cglib以外的那个,名字不记得了)的时候,@autowired注解的必须要是那个接口,动态代理才能生效,当时没想这里也是一个坑),在我实验过后发现实现类对象是可以装配给接口对象。又知道一个知识点,至于@autowired和@resource的区别我们就先不关心了,反正是装配上了。
进实现类一看发现装配了一个 GoodsCategoryMapper ,这就是和MyBatis挂钩的知识点了,学springboot项目的大家肯定都学习过练习过,我们直接看他的getCategoriesForIndex方法:
里面也是别有洞天,随便就看到两个一般我们都没见过的用法,去查也能明白,而且我觉得查到之后听他们说怎么用或者这个方法怎么怎么好我们也不一定能记住,还是以后工作了遇到再说,多在实践中遇到自然就熟了,以后controller层的查数据我们就跳过了,还有我发现现在的mapper.xml文件里的foreach语句的collection属性可以写参数名了,看好多博客都说只能写list或者array,可能2020年不一样了吧:
我们来看网页的登录拦截流程,发现是在 NeeBeeMallWebMvcConfigurer.java类里添加了拦截器:
@Autowired
private NewBeeMallLoginInterceptor newBeeMallLoginInterceptor;
public void addInterceptors(InterceptorRegistry registry) {
// 商城页面登陆拦截
registry.addInterceptor(newBeeMallLoginInterceptor)
.excludePathPatterns("/admin/**")
.excludePathPatterns("/register")
.excludePathPatterns("/login")
.excludePathPatterns("/logout")
.addPathPatterns("/goods/detail/**")
.addPathPatterns("/shop-cart")
.addPathPatterns("/shop-cart/**")
.addPathPatterns("/saveOrder")
.addPathPatterns("/orders")
.addPathPatterns("/orders/**")
.addPathPatterns("/personal")
.addPathPatterns("/personal/updateInfo")
.addPathPatterns("/selectPayType")
.addPathPatterns("/payPage");
}
进拦截器看看:
@Component
public class NewBeeMallLoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
if (null == request.getSession().getAttribute(Constants.MALL_USER_SESSION_KEY)) {
response.sendRedirect(request.getContextPath() + "/login");
return false;
} else {
return true;
}
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
终于又见我们的老朋友重定向,同时也伴随着一个小坑,这里为什么要加request.getContextPath() 呢,看这篇博客你就明白了。
然后在 /newbee-mall/src/main/java/ltd/newbee/mall/controller/mall/PersonalController.java 发现了:
@GetMapping({"/login", "login.html"})
public String loginPage() {
return "mall/login";
}
进入这个页面直接看登录按钮发现执行了一个login()函数,在函数里发了Ajax请求
//验证
var params = $("#loginForm").serialize();
var url = '/login';
$.ajax({
type: 'POST',//方法类型
url: url,
data: params,
success: function (result) {
if (result.resultCode == 200) {
window.location.href = '/index';
} else {
swal(result.message, {
icon: "error",
});
}
;
},
这里的url写的是一个绝对路径,前台的 / 默认是服务器的根目录是不加项目名的,这里应该是作者的小瑕疵,url前面应该加request.getContextPath() 还有这个window.location.href,如果配置了contextpath这里就会出错。
看了半天的相对路径绝对路径前台路径后台路径,现在对这方面的知识已经比较清晰了,希望不要明天就忘记了,再见 ヾ( ̄▽ ̄)Bye~Bye~