spring MVC
- DisparcherServlet
Java针对web开发制定的标准。解析前端请求的路径背后是哪个controller处理的。
扫描controller并储存。返回HandlerExecutionChain,包含bean和拦截器。
如果adapter返回的是json字符串,可以直接返回给前端。通过@responsebody注解
-
用户向服务器发送请求,请求会到DispatcherServlet,DispatcherServlet 对请求URL进行解析,得到请求资源标识符(URI)
-
然后根据该URI,调用HandlerMapping获得该Handler配置的所有相关的对象(包括一个Handler处理器对象、多个HandlerInterceptor拦截器对象),最后以HandlerExecutionChain对象的形式返回。
-
DispatcherServlet 根据获得的Handler,选择一个合适的HandlerAdapter【适配器,持有对controller的引用,它调用】。
提取Request中的模型数据,填充Handler入参开始执行Handler(Controller)。在填充Handler的入参过程中,根据你的配置,Spring将帮你做一些额外的工作: -
Handler执行完成后,向DispatcherServlet 返回一个ModelAndView对象
-
根据返回的ModelAndView,选择一个适合的ViewResolver【视图解析器】返回给DispatcherServlet
-
ViewResolver 结合Model和View,来渲染视图,最后将渲染结果以view形式封装,返回给客户端。
处理mv processdispatchresult——如果mv不为空,调render()方法【渲染】——调resolveViewName()方法的viewResolvers找模板引擎,渲染到上面。
mybatis
安装插件,P4,45min左右。只需要在mapper类上加@mapper注解
- 自动配置,都是以autoconfiguration结尾
程序启动,auto配置类被调用,会主动创建sqlxxxfactory,调用configuration读取配置文件包括【application.properties和扫描mapper.xml】。
sqlxxxfactory就加载好了与配置类有关的内容。
auto再去调用mapperautoconfig,会扫描带有@mapper注解的接口,由mapperproxy动态代理来实现他们。@autowrite注入的mapper,传入的是mapperproxy实现的实现类bean
sqlxxfactory创建sqlxx,依赖于datasourse 【就是图右下角configuration】
源码流程:
自动配置
datasourceautoconfiguration数据源自动配置【抽象的/spring默认的】
看注解:配置类//有xx时实例化//无xx时实例化//启用(加载)xx配置类//注入xxbean
datasourceproperties就是就是图右下角configuration配置类(全局配置类)
注解表示:前缀是spring.datasource。把所有前缀是spring.datasource的加载到datasourceproperties这个bean里
druid连接池自动配置【具体的】
方法名跟返回值同名
@bean注解 把方法返回的对象,加载到容器里
@conditiononmissingbean注解 程序里面没有这个方法的实例时,会调用这个方法,创建实例
mybatis自动配置
P3.1h26min左右
公共代码
-
businessexception:定义的公共异常,后端所有异常捕获后,包装成这个统一异常抛
-
errocode:定义异常的编号
-
responsemodel:统一生成返回对象,规范json格式。前后端分离都返回json数据。把数据封装成对象,序列化成json。
-
toolbox:工具箱。md5加密方案(spring自带)+盐
异常统一处理
基于spring mvc实现
后端代码分三层:视图/业务/数据。在哪层抛都会在controller层处理。怎么让三层的异常都在controller统一处理?
-
spring mvc的机制@controlleradvice 控制层通知
- 建advice类,加上@controlleradvice注解,里面的方法名无所谓,方法参数传入异常父类
- @exceptionhandler 在异常发生时调用方法
- @responesebody 返回的是json字符串
项目可以有多个环境的配置文件/日志的配置文件,可以在此选择激活哪个【哪组】
日志的配置文件可以有多个,需要配置日志存储路径
还可配置不同包的,日志打印级别。 要看SQL配置debug级别,debug级别会打印sql
表示出现404异常由springmvc处理
模块代码
entity/user
后台校验注解,属性不能为空。加在定义属性的变量上
@notblank:string不能为null且不能为空字符串
@notnull
前台注册会有校验,不能为空,但是后台也要校验,防止postman的模拟请求击破接口。
在配置xml文件中,引入校验工具hibernate.validator
@min,@max注解,最值范围校验,由objectvalidator公用的组件封装
objectvalidator组件/类
- 公共组件,受容器管理。
- 通过validation实例化了validator对象。
- 提供validate方法,传入参数为object对象。
- 返回的map,存的key属性名+value消息
- 底层调用实例化的validator的validate方法,会根据注解,检查传入的object,哪些字段有问题,返回元素为对象的集合。
- 解析出来哪个属性有问题,问题消息是什么,存到map中
dao/usermapper
注册登录功能,插件生成的,要加@mapper注解。
增删改查方法,根据手机号查询方法。
每个方法,需要SQL文件与之对应在resources/mappers包下
resources/mappers/usermapper.xml
写dao包下类的sql语句。
parametertype:定义传入参数的类型
resultmap:返回jdbc的结果集
- 返回的是结果集,装到user对象里。
- 数据库字段跟对象属性的映射关系,已经写好。
- 编译时#{}会被替换成?号,执行时?号会被替换成form参数
- jdbctype=varchar表示,数据库字段的varchar类型映射成string类型
service/UserService
注册方法、登录方法、根据id查询用户
service/impl/UserServiceimpl
@transaction注解,表示方法受事务保护,在一个事务内。有dml语句都加。
- 注入userMapper和validator
- 注册方法,参数是用户
- 先判断是否空,否继续
- 校验一下,校验返回结果非空则有问题,抛异常。否继续
- 尝试插入(注册)方法,可能手机号重复,就插入失败,则捕获到异常。
- 否,插入成功
@Service
public class UserServiceImpl implements UserService, ErrorCode {
@Autowired
private UserMapper userMapper;
@Autowired
private ObjectValidator validator;
@Transactional
public void register(User user) {
if (user == null) {
throw new BusinessException(PARAMETER_ERROR, "参数不能为空!");
}
Map<String, String> result = validator.validate(user);
if (result != null && result.size() > 0) {
throw new BusinessException(PARAMETER_ERROR,
StringUtils.join(result.values().toArray(), ", ") + "!");
}
try {
userMapper.insert(user);
} catch (DuplicateKeyException e) {
throw new BusinessException(PARAMETER_ERROR, "该手机号已注册!");
}
}
public User login(String phone, String password) {
if (StringUtils.isEmpty(phone) || StringUtils.isEmpty(password)) {
throw new BusinessException(PARAMETER_ERROR, "参数不合法!");
}
User user = userMapper.selectByPhone(phone);
if (user == null || !StringUtils.equals(password, user.getPassword())) {
throw new BusinessException(USER_LOGIN_FAILURE, "账号或密码错误!");
}
return user;
}
public User findUserById(int id) {
return userMapper.selectByPrimaryKey(id);
}
}
controller/UserController
前端
.html前端表单外观页面,代码文件/.js前端数据,代码文件
- 页面加载完成后,给按钮绑定单击事件,调用xx方法
- 获取xx,xxx,验证是否为空。为空不通过
- 不为空发送ajax异步请求,固定格式
get请求,发送ajax请求,是异步的,通过路径传数据,成功时回调
- 跨域
post传址路径上看不到,更安全。post需要传明确的数据,不是通过路径传,要声明数据,json格式
- type/url/data/xhrFields
- 成功时回调方法,也叫成功时的响应。失败给一个提示,成功页面跳转。
报错是advice统一报的
$(document).ready(function () {
// 立即登录单击事件
$("#btn-login").click(function (e) {
login(e);
});
});
function login(e) {
var phone = $("#phone").val();
var password = $("#password").val();
if (!phone) {
alertBox("手机号不能为空!");
return false;
}
if (!password) {
alertBox("密码不能为空!");
return false;
}
$.ajax({
type: "POST",
url: SERVER_PATH + "/user/login",
data: {
"phone": phone,
"password": password
},
xhrFields: {withCredentials: true},
success: function (result) {
if (result.status) {
alertBox(result.data.message);
return false;
}
alertBox("登录成功!", function() {
window.location.href = "seckill.html";
});
}
});
}
小结:
"#{}"会经过预编译。常用 "${}"直接拼上值,会有注入攻击风险