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框架(迷你版)的更多相关文章

  1. 闭关修炼180天--手写持久层框架(mybatis简易版)

    闭关修炼180天--手写持久层框架(mybatis简易版) 抛砖引玉 首先先看一段传统的JDBC编码的代码实现: //传统的JDBC实现 public static void main(String[ ...

  2. 闭关修炼180天--手写IOC和AOP(xml篇)

    闭关修炼180天--手写IOC和AOP(xml篇) 帝莘 首先先分享一波思维导图,涵盖了一些Spring的知识点,当然这里并不全面,后期我会持续更新知识点. 在手写实现IOC和AOP之前(也就是打造一 ...

  3. (二)springMvc原理和手写springMvc框架

    我们从两个方面了解springmvc执行原理,首先我们去熟悉springmvc执行的过程,然后知道原理后通过手写springmvc去深入了解代码中执行过程. (一)SpringMVC流程图 (二)Sp ...

  4. 手写SpringMVC 框架

    手写SpringMVC框架 细嗅蔷薇 心有猛虎 背景:Spring 想必大家都听说过,可能现在更多流行的是Spring Boot 和Spring Cloud 框架:但是SpringMVC 作为一款实现 ...

  5. 手写SpringMVC框架(三)-------具体方法的实现

    续接前文 手写SpringMVC框架(二)结构开发设计 本节我们来开始具体方法的代码实现. doLoadConfig()方法的开发 思路:我们需要将contextConfigLocation路径读取过 ...

  6. 手写SpringMVC框架(二)-------结构开发设计

    续接前文, 手写SpringMVC框架(一)项目搭建 本节我们来开始手写SpringMVC框架的第二阶段:结构开发设计. 新建一个空的springmvc.properties, 里面写我们要扫描的包名 ...

  7. 手写一套迷你版HTTP服务器

    本文主要介绍如何通过netty来手写一套简单版的HTTP服务器,同时将关于netty的许多细小知识点进行了串联,用于巩固和提升对于netty框架的掌握程度. 服务器运行效果 服务器支持对静态文件css ...

  8. 手写SpringMVC框架(一)-------项目搭建

    SpringMVC处理请求的大致流程: 我们来开始着手手写一个SpringMVC框架. 新建一个springMVC项目,流程参见 SpringMVC框架搭建流程 引入servlet相关的jar包: & ...

  9. 纯手写SpringMVC框架,用注解实现springmvc过程

    闲话不多说,直接上代码! 1.第一步,首先搭建如下架构,其中,annotation中放置自己编写的注解,主要包括service controller qualifier RequestMapping ...

  10. 二. 手写SpringMVC框架

    1.1 新建DispatcherServlet 1.2 在src目录下,新建applicationContext.xml <?xml version="1.0" encodi ...

随机推荐

  1. springboot多模块打包报错问题根因分析:Unable to find main class

    问题背景: 项目结构为springboot多模块,其中有四个模块bean.utils.user.ems,其中user和ems模块为主程序,包含启动类,其他两个模块为其服务,提供依赖 问题分析: 查看u ...

  2. C#/.NET/.NET Core优秀项目和框架2024年1月简报

    前言 公众号每月定期推广和分享的C#/.NET/.NET Core优秀项目和框架(每周至少会推荐两个优秀的项目和框架当然节假日除外),公众号推文中有项目和框架的介绍.功能特点.使用方式以及部分功能截图 ...

  3. Android 子线程更新UI的六种方式

    1.注意: (1)子线程需要切换到UI线程或者主线程去更新线程. (2)但是子线程单独也是可以更新UI的,只是谷歌要求我们需要到UI线程去更新. eg: @Override protected voi ...

  4. Shell中调用可执行文件,手动执行可以执行,crontab执行就报错:exec: java: not found

    今天发现一个很奇怪的问题,就是我编写的shell脚本, 手动执行可以正常执行,但是放到crontab中就报错.line 60: exec: java: not  found 百度搜索发现原来是java ...

  5. NC15532 Happy Running

    题目链接 题目 题目描述 Happy Running, an application for runners, is very popular in CHD. In order to lose wei ...

  6. NC14704 美味菜肴

    题目链接 题目 题目描述 小明是个大厨,早上起来他开始一天的工作.他所在的餐厅每天早上都会买好 \(n\) 件食材(每种食材的数量可以视为无限),小明从到达餐厅开始就连续工作 \(T\) 时间.每道菜 ...

  7. 【Unity3D】激光灯、碰撞特效

    1 需求描述 ​ 本文将模拟激光灯(或碰撞)特效,详细需求如下: 从鼠标位置发射屏幕射线,检测是否与物体发生碰撞 当与物体发生碰撞时,在物体表面覆盖一层激光灯(或碰撞)特效 ​ 本文代码见→激光灯.碰 ...

  8. centos 安装nacos 并以后台服务形式启动

    一.下载解压nacos tar -xvf nacos-server-1.2.0.tar.gz 二.持久化配置(mysql) 修改nacos/conf/application.properties文件, ...

  9. Springboot实现remember-me记住我功能

    1.什么是remeber-me? remeber-me即记住我功能,是我们在登录web系统时的常见勾选项.当我们登录一个web系统时除了输入常规的用户名.密码后还可以勾选记住我选项(假设该系统提供了该 ...

  10. 【Android 逆向】apk反编译后重打包

    1. 执行 apktool b smali_dir smali_dir 为反编译出来的数据目录 执行后可能会报错 I: Building resources... W: /root/Desktop/t ...