闭关修炼180天 -- 手写SpringMVC框架(迷你版)
SpringMvc知识须知
MVC设计模式
- Model(模型):模型包含业务模型和数据模型,数据模型⽤于封装数据,业务模型⽤于处理业 务。
- View(视图): 通常指的就是我们的 jsp 或者 html。作⽤⼀般就是展示数据的。通常视图是依据 模型数据创建的。
- Controller(控制器): 是应⽤程序中处理⽤户交互的部分。作⽤⼀般就是处理程序逻辑的。
springMvc请求执行流程
- ⽤户发送请求⾄前端控制器DispatcherServlet
- DispatcherServlet收到请求调⽤HandlerMapping处理器映射器
- 处理器映射器根据请求Url找到具体的Handler(后端控制器),⽣成处理器对象及处理器拦截 器(如果 有则⽣成)⼀并返回DispatcherServlet
- DispatcherServlet调⽤HandlerAdapter处理器适配器去调⽤Handler
- 处理器适配器执⾏Handler
- Handler执⾏完成给处理器适配器返回ModelAndView
- 处理器适配器向前端控制器返回 ModelAndView,ModelAndView 是SpringMVC 框架的⼀个 底层对 象,包括 Model 和 View
- 前端控制器请求视图解析器去进⾏视图解析,根据逻辑视图名来解析真正的视图。
- 视图解析器向前端控制器返回View
- 前端控制器进⾏视图渲染,就是将模型数据(在 ModelAndView 对象中)填充到 request 域
- 前端控制器向⽤户响应结果
SpringMVC九大核心组件
- 1.HandlerMapping(处理器映射器):找到请求响应的处理器Handler和Interceptor
- 2.HandlerAdapter(处理器适配器):要让固定的Servlet处理方法调用Handler来处理
- 3.HandlerExceptionResolver(异常处理器),用于处理Handler产生的异常情况
- 4.ViewResolver(视图解析器),将Sting类型的视图名解析为View类型的视图
- 5.RequestToViewNameTranslator,在请求域中获取ViewName,当Handler处理完后,没有设置View,那便从这个组件中从请求中获取ViewName
- 6.LocaleResolver,区域化国际化组件
- 7.ThemeResolver 主题解析器
- 8.MultipartResolver多文件上传解析器
- 9.FlashMapManager,用于重定向时的参数传递
自定义SpringMVC框架
大致流程
- 引进javax.servlet坐标,创建servlet包,创建HttpServlet的实现类MyServlet
- 创建annotation包,创建四个注解:@MyController,@MyRequestMapping,@MyService,@MyAutowired
- 重写HttpServlet的几个方法init(),doPost(),doGet()
- 在init()方法中加载解析springMvc.xml文件
- 在init()方法中完成注解的功能增强。
- 在init()方法中初始化ioc容器
- 在init()方法中完成依赖的注入
- 构造一个处理器映射器HandlerMapping,将我们处理好的url和Method建立映射关系
- 处理权限关系
项目结构
代码实现
1.pom文件主要内容
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
</properties> <dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency> <dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency> <dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.9</version>
</dependency>
</dependencies> <build>
<plugins>
<!--编译插件定义编译细节-->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>8</source>
<target>8</target>
<encoding>utf-8</encoding>
<!--告诉编译器,编译的时候记录下形参的真实名称-->
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
</configuration>
</plugin> <plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
</plugins>
</build>
2.web.xml 以及 springmvc.properties
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" > <web-app>
<display-name>Archetype Created Web Application</display-name> <servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.zae.frame.servlet.MyServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>springmvc.properties</param-value>
</init-param>
</servlet> <servlet-mapping>
<servlet-name>MyServlet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping> </web-app>
scanPackage=com.zae
3.五个注解的定义
import java.lang.annotation.*; @Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAutowired {
String value() default "";
}
import java.lang.annotation.*; @Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyController {
String value() default "";
}
import java.lang.annotation.*; @Documented
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyRequestMapping {
String value() default "";
}
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
String value() default "";
}
/**
* 1.当未添加该注解时,表明该方法不加入权限控制,所有请求均可访问
* 2.当该注解放置在某个类上,则表明该类下的所有方法都可以被该注解指定的用户访问
* 3.当该注解放置在某个方法时,则以该方法上的用户指定为准
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface Security {
//配置用户名称,指定的用户可以访问
String [] value() default {"/"};
}
4.handler实体的定义
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import java.util.regex.Pattern; /**
* 封装映射关系
*/
public class Handler {
//controller
private Object controller;
//方法
private Method method;
//url的正则表达式
private Pattern pattern;
//存储参数字段以及其的入参位置
private Map<String,Integer> handlerMapping;
//存储有权限访问的用户
private String [] securityUser; public Handler(Object controller, Method method, Pattern pattern) {
this.controller = controller;
this.method = method;
this.pattern = pattern;
this.handlerMapping = new HashMap<String, Integer>();
} public String[] getSecurityUser() {
return securityUser;
} public void setSecurityUser(String[] securityUser) {
this.securityUser = securityUser;
} public Object getController() {
return controller;
} public void setController(Object controller) {
this.controller = controller;
} public Method getMethod() {
return method;
} public void setMethod(Method method) {
this.method = method;
} public Pattern getPattern() {
return pattern;
} public void setPattern(Pattern pattern) {
this.pattern = pattern;
} public Map<String, Integer> getHandlerMapping() {
return handlerMapping;
} public void setHandlerMapping(Map<String, Integer> handlerMapping) {
this.handlerMapping = handlerMapping;
}
}
5.MyServlet核心类的实现
import com.zae.frame.annotation.*;
import com.zae.frame.pojo.Handler;
import org.apache.commons.lang3.StringUtils;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern; public class MyServlet extends HttpServlet {
//配置文件信息:springmvc.xml
Properties properties = new Properties();
//存放扫描包下的全限类名
List<String> classList = new ArrayList<String>();
//ioc容器,存放实例对象
Map<String,Object> beanMap = new HashMap<String,Object>();
//存放Handler的集合
List<Handler> handlerList = new ArrayList<Handler>(); @Override
public void init(ServletConfig config){
//1.加载配置文件:springmvc.xml
String path = config.getInitParameter("contextConfigLocation");
doLoanConfig(path); //2.扫描包下所有的类
doScan(properties.getProperty("scanPackage"));
//3.初始化bean对象,基于注解,加入ioc容器
doInstance();
//4.完成依赖的注入@MyAutowired
doAutowired();
//5.构建好一个HandlerMapping,完成url和method之间的映射关系
initHandleMapping();
//6.处理用户权限问题
doSecurity(); System.out.println("迷你版SpringMvc初始化完成....");
} /**
* 完成Controller层的用户访问权限问题
*/
private void doSecurity() {
if (handlerList.size() == 0){return;} for(Handler handler:handlerList){
Class<?> aClass = handler.getController().getClass();
String [] securityUserArr = null;
//1.当controller类上有@Security注解时,则改类中所有的方法都加入权限控制,以类上的@Security里面的value为准
if(aClass.isAnnotationPresent(Security.class)){
Security annotation = aClass.getAnnotation(Security.class);
securityUserArr = annotation.value();
}
//2.当方法上存在@Security注解时,则以方法的@Security中的value为准,覆盖类上声明的那个权限注解
Method method = handler.getMethod();
if(method.isAnnotationPresent(Security.class)){
Security annotation = method.getAnnotation(Security.class);
securityUserArr = annotation.value();
}
//3.如果无论controller类还是其中的方法都没有加Security注解,此时securityUserArr==null,那么可以任意访问,不会拦截,
//4.如果controller层加了Security注解,但是没有重新定义value值或者value值声明为{"/"},那么此时securityUserArr=={"/"},会拦截所有用户的请求
handler.setSecurityUser(securityUserArr);
}
} /**
* 完成url与方法之间的映射
*/
private void initHandleMapping() {
if(beanMap.size() == 0){return;}
for(Map.Entry<String,Object> map:beanMap.entrySet()){
Class<?> aClass = map.getValue().getClass();
String baseUrl = "";
//当controller类上有MyRequestMapping注解时,基础url为value值
if(aClass.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = aClass.getAnnotation(MyRequestMapping.class);
baseUrl = annotation.value();
}
//获取Controller的所有方法
Method[] methods = aClass.getMethods();
for (Method method : methods) {
//当方法上有MyRequestMapping注解时,获取其value值
if(method.isAnnotationPresent(MyRequestMapping.class)){
MyRequestMapping annotation = method.getAnnotation(MyRequestMapping.class);
//请求url
String url = baseUrl+annotation.value();
Pattern compile = Pattern.compile(url);
Handler handler = new Handler(map.getValue(),method,compile); //计算方法的参数位置信息
Parameter[] parameters = method.getParameters();
for (int i = 0; i < parameters.length; i++) {
//当参数为HttpServletRequest和HttpServletResponse时,key存储的是参数的类型名
if(parameters[i].getType() == HttpServletRequest.class || parameters[i].getType() == HttpServletResponse.class){
handler.getHandlerMapping().put(parameters[i].getType().getSimpleName(),i);
}else{
//当其他情况的时候,key存储的是参数名称
handler.getHandlerMapping().put(parameters[i].getName(),i);
}
}
//将handler存储起来
handlerList.add(handler);
}
}
}
} /**
* 依赖注入
*/
private void doAutowired() {
if(beanMap.size()==0){return;}
for(Map.Entry<String,Object> mapEntry:beanMap.entrySet()){
String key = mapEntry.getKey();
Object value = mapEntry.getValue();
Class<?> aClass = value.getClass();
//获取该类下所有的属性
Field[] declaredFields = aClass.getDeclaredFields();
for (Field declaredField : declaredFields) {
//判断属性上是否存在MyAutowired注解
if(declaredField.isAnnotationPresent(MyAutowired.class)){
MyAutowired annotation = declaredField.getAnnotation(MyAutowired.class);
//获取注解里面的value值
String name = annotation.value();
//设置暴力访问
declaredField.setAccessible(true);
if("".equals(name.trim())){
//当注解的value值为空时,获取属性类型的全限类名,使用类型注入依赖
name = declaredField.getType().getName();
}
try {
//给该属性赋值
declaredField.set(value,beanMap.get(name));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
//依赖赋值结束后,重新加入map集合中
beanMap.put(key,value);
}
} /**
* 基于注解完成ioc的初始化
*/
private void doInstance() {
if(classList.size()==0){return;} for (String className : classList) { try {
//根据全限类名获取类对象
Class<?> aClass = Class.forName(className);
if(aClass.isAnnotationPresent(MyService.class)){
MyService annotation = aClass.getAnnotation(MyService.class);
String value = annotation.value();
Object beanObj = aClass.newInstance();
if("".equals(value)){
//当service注解为空时,使用首字母小写的类名
value = firstToLower(aClass.getSimpleName()); beanMap.put(value,beanObj);
}else{
//当service的value不为空时,使用注解里面的value值
beanMap.put(value,beanObj);
} //面向接口开发,以接口全限类名作为id放入容器(针对service层存在接口)
Class<?>[] interfaces = aClass.getInterfaces();
if(interfaces!=null){
for (Class<?> anInterface : interfaces) {
beanMap.put(anInterface.getName(),aClass.newInstance());
}
} }else if(aClass.isAnnotationPresent(MyController.class)){
//MyController注解的类使用首字母小写的类名
Object beanObj = aClass.newInstance();
beanMap.put(firstToLower(aClass.getSimpleName()),beanObj);
}else{
continue;
} } catch (Exception e) {
e.printStackTrace();
}
}
} private String firstToLower(String source){
if(source==null){return null;}
char[] chars = source.toCharArray();
if(chars[0]>='A' && chars[0]<='Z'){
chars[0] += 32;
}
return String.valueOf(chars);
}
/**
* 扫描包下所有的类
* @param scanPackage
*/
private void doScan(String scanPackage) {
String scanPackagePath =Thread.currentThread().getContextClassLoader().getResource("").getPath()+scanPackage.replaceAll("\\.","/");
File fileScan = new File(scanPackagePath);
File[] files = fileScan.listFiles();
for (File file : files) {
if(file.isDirectory()){
//如果是文件夹,就继续递归
doScan(scanPackage+"."+file.getName()); }else if(file.getName().endsWith(".class")){
String className = scanPackage+"."+file.getName().replaceAll(".class","");
classList.add(className);
}
}
} /**
* 加载springmvc.properties配置文件
* @param path
*/
private void doLoanConfig(String path) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(path);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
} } @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws IOException {
//设置中文编码格式
resp.setContentType("text/html;charset=UTF-8");
doPost(req,resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws IOException {
Handler handler = getHandler(req);
if(handler == null){
resp.getWriter().write("404 No fount");
return;
}
//检查用户权限问题
if(!checkSecurity(handler,req,resp)){return;} //参数绑定工作
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes(); //根据上述长度创建一个新的数组,参数传递时反射调用
Object[] objParam = new Object[parameterTypes.length]; //为了向参数数组中塞值,保证形参和方法的参数顺序一致
Map<String, String[]> parameterMap = req.getParameterMap();
//拿到方法的入参顺序
Map<String, Integer> handlerMapping = handler.getHandlerMapping();
for(Map.Entry<String,String[]> map:parameterMap.entrySet()){
String join = StringUtils.join(map.getValue(), ",");
String key = map.getKey();
//如果字段名字匹配上了,则进行赋值操作
if(parameterMap.containsKey(key)){
objParam[handlerMapping.get(key)] = join;
}
} //处理HttpServletRequest和HttpServletResponse
int reqIndex = handlerMapping.get(HttpServletRequest.class.getSimpleName()); int respIndex = handlerMapping.get(HttpServletResponse.class.getSimpleName()); objParam[reqIndex] = req; objParam[respIndex] = resp; Method method =handler.getMethod();
Object controllerObj = handler.getController();
try {
//进行方法调用
method.invoke(controllerObj,objParam);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
} } /**
* 检查用户权限问题
* @param req
* @return
*/
private Boolean checkSecurity(Handler handler,HttpServletRequest req,HttpServletResponse resp) throws IOException {
//当securityUser == null时,没有加权限控制,任意请求都能访问
if(handler.getSecurityUser() == null){
return true;
} //当securityUser == {/},任意请求都不能访问
if("/".equals(handler.getSecurityUser()[0])){
resp.getWriter().write("您无权访问!请联系管理员");
return false;
} //前端的参数,访问的用户
String username = req.getParameter("username");
//当securityUser存在值时,则进行校验
List<String> userList = Arrays.asList(handler.getSecurityUser());
if(userList.contains(username)){
return true;
}else{
resp.getWriter().write("您无权访问!请联系管理员");
return false;
}
} /**
* 根据请求获取Handler对象
* @param req
* @return
*/
private Handler getHandler(HttpServletRequest req){
if(handlerList.isEmpty()){return null;} String url = req.getRequestURI(); for (Handler handler : handlerList) {
//判断url和正则是否匹配
Matcher matcher = handler.getPattern().matcher(url);
if(!matcher.matches()){continue;}
return handler;
}
return null;
}
}
6.使用端Service以及Controller(测试使用,不再关联数据库了)
public interface TestService { String start(String username);
}
import com.zae.demo.service.TestService;
import com.zae.frame.annotation.MyService; @MyService
public class TestServiceImpl implements TestService {
@Override
public String start(String username) {
System.out.println("访问的用户为:"+username);
return username;
}
}
import com.zae.demo.service.TestService;
import com.zae.frame.annotation.MyAutowired;
import com.zae.frame.annotation.MyController;
import com.zae.frame.annotation.MyRequestMapping;
import com.zae.frame.annotation.Security; import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse; @Security({"wangsulong","xuezhiqian"})
@MyController
@MyRequestMapping("/demoSecurity")
public class TestSecurityController { @MyAutowired
private TestService testService; /**
* 方法中有@Security,则覆盖类定义的权限注解
* @param request
* @param response
* @param username
* @return
*/
@Security({"xusong"})
@MyRequestMapping("/startSecurityOne")
public String startSecurityOne(HttpServletRequest request, HttpServletResponse response,String username){
return testService.start(username);
} /**
* 方法中没有@Security,则使用类里面的权限注解定义的
* @param request
* @param response
* @param username
* @return
*/
@MyRequestMapping("/startSecurityTwo")
public String startSecurityTwo(HttpServletRequest request, HttpServletResponse response,String username){
return testService.start(username);
} /**
* @Security中的value没有设定值,则拦截所欲
* @param request
* @param response
* @param username
* @return
*/
@Security
@MyRequestMapping("/startSecurityThree")
public String startSecurityThree(HttpServletRequest request, HttpServletResponse response,String username){
return testService.start(username);
}
}
自定义SpringMvc框架至此结束
文章知识点输出来源:拉勾教育Java高薪训练营
2021第一天,我是帝莘,期待和你的技术交流以及思想碰撞
闭关修炼180天 -- 手写SpringMVC框架(迷你版)的更多相关文章
- 闭关修炼180天--手写持久层框架(mybatis简易版)
闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...
- 闭关修炼180天--手写IOC和AOP(xml篇)
闭关修炼180天--手写IOC和AOP(xml篇) 帝莘 首先先分享一波思维导图,涵盖了一些Spring的知识点,当然这里并不全面,后期我会持续更新知识点. 在手写实现IOC和AOP之前(也就是打造一 ...
- (二)springMvc原理和手写springMvc框架
我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程. (一)SpringMVC流程图 (二)Sp ...
- 手写SpringMVC 框架
手写SpringMVC框架 细嗅蔷薇 心有猛虎 背景:Spring 想必大家都听说过,可能现在更多流行的是Spring Boot 和Spring Cloud 框架:但是SpringMVC 作为一款实现 ...
- 手写SpringMVC框架(三)-------具体方法的实现
续接前文 手写SpringMVC框架(二)结构开发设计 本节我们来开始具体方法的代码实现. doLoadConfig()方法的开发 思路:我们需要将contextConfigLocation路径读取过 ...
- 手写SpringMVC框架(二)-------结构开发设计
续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...
- 手写一套迷你版HTTP服务器
本文主要介绍如何通过netty来手写一套简单版的HTTP服务器,同时将关于netty的许多细小知识点进行了串联,用于巩固和提升对于netty框架的掌握程度. 服务器运行效果 服务器支持对静态文件css ...
- 手写SpringMVC框架(一)-------项目搭建
SpringMVC处理请求的大致流程: 我们来开始着手手写一个SpringMVC框架. 新建一个springMVC项目,流程参见 SpringMVC框架搭建流程 引入servlet相关的jar包: & ...
- 纯手写SpringMVC框架,用注解实现springmvc过程
闲话不多说,直接上代码! 1.第一步,首先搭建如下架构,其中,annotation中放置自己编写的注解,主要包括service controller qualifier RequestMapping ...
- 二. 手写SpringMVC框架
1.1 新建DispatcherServlet 1.2 在src目录下,新建applicationContext.xml <?xml version="1.0" encodi ...
随机推荐
- 从零开始配置vim(28)——DAP 配置
首先给大家说一声抱歉,前段时间一直在忙换工作的事,包括但不限于交接.背面试题准备面试.好在最终找到了工作,也顺利入职了.期间也有朋友在催更,在这里我对关注本系列的朋友表示感谢.多的就不说了,我们正式进 ...
- PaddleHub--飞桨预训练模型应用工具{风格迁移模型、词法分析情感分析、Fine-tune API微调}【一】
相关文章: 基础知识介绍: [一]ERNIE:飞桨开源开发套件,入门学习,看看行业顶尖持续学习语义理解框架,如何取得世界多个实战的SOTA效果?_汀.的博客-CSDN博客_ernie模型 百度飞桨:E ...
- C/C++ 实现枚举网上邻居信息
在Windows系统中,通过网络邻居可以方便地查看本地网络中的共享资源和计算机.通过使用Windows API中的一些网络相关函数,我们可以实现枚举网络邻居信息的功能,获取连接到本地网络的其他计算机的 ...
- 7.5 C/C++ 实现链表队列
链表队列是一种基于链表实现的队列,相比于顺序队列而言,链表队列不需要预先申请固定大小的内存空间,可以根据需要动态申请和释放内存.在链表队列中,每个节点包含一个数据元素和一个指向下一个节点的指针,头节点 ...
- 部署MooseFS分布式文件系统
MooseFS是一个分布式文件系统,其本身具有高可用性,高拓展性,开放源代码,高容错,等在数据的读写性能方面,通过dd测试,MooseFS也就是写入的速度稍微好于NFS,读上没有差别. MooseFS ...
- Python 多线程实现爬取图片
前阵子网上看到有人写爬取妹子图的派森代码,于是乎我也想写一个教程,很多教程都是调用的第三方模块,今天就使用原生库来爬,并且扩展实现了图片鉴定,图片去重等操作,经过了爬站验证,稳如老狗,我已经爬了几万张 ...
- Rsync+Inotify 实现数据同步
Rsync 是UNIX及类UNIX-Like平台下一款强大的数据镜像备份软件,它不像FTP或其他文件传输服务那样需要进行全备份,Rsync 可以根据数据的变化进行差异备份,从而减少数据流量,提高工作效 ...
- HTTP请求头引发的注入问题 (SQL注入)
关于请求头中注入问题的演示,这里我写了一些测试案例,用来测试请求头中存在的问题.我们常见的会发生注入的点有 Referer.X-Forwarded-For.Cookie.X-Real-IP.Accep ...
- C/C++可变参数模版和函数指针的结合
目录 1.说明 2.模板类传入固定参数的C函数指针 3.模板类传入固定参数的C++函数指针 3.1.用函数对象替代函数指针存储 4.模板类传入不定参数的C函数指针 5.模板类传入不定参数的C++成员函 ...
- 《ASP.NET Core 微服务实战》-- 读书笔记(第5章)
第 5 章 创建数据服务 选择一种数据存储 由于我坚持要尽可能的跨平台,所以我决定选用 Postgres,而不用 SQL Server 以照顾 Linux 或 Mac 电脑的读者 构建 Postgre ...