IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发测试 -> 自定义MVC框架 -> 正文阅读

[开发测试]自定义MVC框架

本期内容:

?

一、概念?

?为什么学习自定义MVC框架?

MVC怎么出现:各司其职

?MVC含义:模型层(model) 视图层(view) 控制层(controller)

? ? ? ? 以前问题:

????????????????1、数据处理Dao?? ??? ?(代码思路基本重复)
? ? ? ? ? ? ? ? 2、servlet?? ??? ?(代码思路基本重复)
? ? ? ? ? ? ? ? ? ? ? ①doget/dopost,实际上doget基本用不上
? ? ? ? ? ? ? ? ? ?? ②实体类参数接受代码冗余(req.getParammeter(""),尤其当实体类属性多的情况下,封装到实体类中)
? ? ? ? ? ? ? ? ? ? ? ③关于结果页面的跳转(转发、重定向)
? ? ? ? ? ? ? ? ????????req.getdispather("/index.jsp").forward(req,resp);
? ? ? ? ? ? ? ? ? ????????resp.sendredirect("/index.jsp");
? ? ? ? ? ? ? ? 3、jsp?? ?(代码思路基本重复HTML、js)?? ?

<!-- 绝大多数人开发采用这种方式做增删改查(servlet层) -->
?? ?<a href="${pageContext.request.contextPath }/book/add">新增</a>
?? ?<a href="${pageContext.request.contextPath }/book/delete">删除</a>
?? ?<a href="${pageContext.request.contextPath }/book/edit">修改</a>
?? ?<a href="${pageContext.request.contextPath }/book/list">查询</a>?

?每个方法还要写一个servlet:

第二种:

优点:相较于前一种,代码量减少,由原来的四个类变成一个类
缺点:每一次新增一个方法,都要改动原有逻辑,是代码过于冗余

?<!-- 少数人开发采用这种方式做增删改查(servlet层) -->
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=add">新增</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=delete">删除</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>

?只要写一个servlet

package com.lv.servlet;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/book.action")
public class BookServlet extends HttpServlet{
?? ?@Override
?? ?protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
?? ??? ?doPost(req, resp);
?? ?}
?? ?
?? ?@Override
?? ??? ?protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

?? ??? ?String methodName = req.getParameter("methodName");
? ? ? ? if("add".equals(methodName)) {
? ? ? ? ? ? add(req,resp);
? ? ? ? }else if("edit".equals(methodName)) {
? ? ? ? ? ? edit(req,resp);
? ? ? ? }else if("delete".equals(methodName)) {
? ? ? ? ? ? delete(req,resp);
? ? ? ? }else if("list".equals(methodName)) {
? ? ? ? ? ? list(req,resp);
? ? ? ? }else if("load".equals(methodName)) {
? ? ? ? ? ? load(req,resp);
? ? ? ? }

?? ??? ?}

?? ?private void load(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.load()");
?? ?}

?? ?private void list(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.list()");
?? ?}

?? ?private void delete(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.delete()");
?? ?}

?? ?private void edit(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.edit()");
?? ?}

?? ?private void add(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.add()");
?? ?}
?? ?private void ref(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.ref()");
?? ?}

}

框架:反射+设计模式(极大的减少了代码量,把重复性的代码交给框架完成,让程序员关注点放在项目业务上)

????????1、通用分页指点+通用的增删改
? ? ? ? 2、各层(主要体现在mc层)数据Dao层、控制层代码缩减
? ? ? ? 3、前台代码的缩减优化

对以上第二种的解决方案:
? ? ? ? ? ? ? 调用哪一个方法,实际上是取决于methodName,加if不是必要条件
? ? ? ? ? ? ? 直白一点,也就是说动态调用methodName方法,并且是当前类实例的methodName方法

<!-- 反射优化 -->
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=add">新增</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=delete">删除</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=edit">修改</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=list">查询</a>
?? ?<a href="${pageContext.request.contextPath }/book.action?methodName=load">回显</a>

每一次新增一个方法,不需要改动原有逻辑,只要在最下增加一个方法就好

package com.lv.servlet;

import java.io.IOException;
import java.lang.reflect.Method;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
@WebServlet("/book.action")
public class BookServlet extends HttpServlet{
?? ?@Override
?? ?protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
?? ??? ?doPost(req, resp);
?? ?}
?? ?
?? ?@Override
?? ??? ?protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
?? ??? ?String methodName = req.getParameter("methodName");

?? ??? ?try {
?? ??? ??? ?Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
?? ??? ??? ?m.setAccessible(true);
?? ??? ??? ?m.invoke(this,req, resp);
?? ??? ??? ?
?? ??? ?} catch (Exception e) {
?? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ?e.printStackTrace();
?? ??? ?}?
?? ??? ?}

?? ?private void load(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.load()");
?? ?}

?? ?private void list(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.list()");?
?? ?}

?? ?private void delete(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.delete()");
?? ?}

?? ?private void edit(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.edit()");
?? ?}

?? ?private void add(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.add()");
?? ?}
?? ?private void ref(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.ref()");
?? ?}

}
?

总结:
? ? ? ? ? ? ? ? ? 反射可以修复上面改动代码才能解决需求问题的缺陷
? ? ? ? ? ? ? ? ? 反射这段代码,相当于中央控制器,并不直接处理浏览器请求
? ? ? ? ? ? ? ? ? 处理浏览器请求的是子控制器

二、完成自定义MVC框架

1、自定义MVC框架工作原理:

?2、中央控制器的优化及子控制器的优化

需解决的问题:

1.BookServlet中要做增删改查,那么必须要在doPost方法中写反射动态调用新增方法
? ? ? ?? GoodsServlet要做增删改查,那么必须要在doPost方法中写反射动态调用新增方法
结论:反射动态调用新增方法代码是重复的的,但是这段又是必须的,也就是还需要进一步优化,优化中央控制器

2.对于BookServlet中要做增删改查而言,依然是每一个Servlet都要写doget/doPost方法
? ? ? ? ? 但是实际上对处理业务有用的代码,往往是我们新加的,只需关注业务(add/delete/...)
? ? ? ? ?? 优化子控制器

ActionSupport类中放入放射动态调用方法:

package com.lv.framework;

import java.lang.reflect.Method;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class ActionSupport implements Action{

?? ?@Override
?? ?public String execute(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?String methodName = req.getParameter("methodName");
?? ??? ?String res=null;
?? ??? ?
?? ??? ?try {
?? ??? ??? ?Method m = this.getClass().getDeclaredMethod(methodName, HttpServletRequest.class,HttpServletResponse.class);
?? ??? ??? ?m.setAccessible(true);
?? ??? ??? ?res=(String) m.invoke(this,req, resp);
?? ??? ??? ?
?? ??? ?} catch (Exception e) {
?? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ?e.printStackTrace();
?? ??? ?}
?? ??? ?return res;?
?? ?}
}
?

BookAction相当于以前的BookServlet,从父类继承了execute方法,就把反射动态调用方法的代码继承过来了

package com.lv.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lv.entity.Book;

public class BookAction extends ActionSupport implements ModelDriver<Book>{

?? ?private String add(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.add(book)");
?? ??? ?return "list";
?? ?}
?? ?
?? ?private void edit(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.edit()");
?? ??? ?
?? ?}
?? ?
?? ?private String toEdit(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.toEdit()");
?? ??? ?return "toEdit";
?? ?}
?? ?private void list(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.list()");
?? ??? ?
?? ?}

?? ?private void load(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.load()");
?? ??? ?
?? ?}

?? ?@Override
?? ?public Book getModel() {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?return book;
?? ?}

}
?

?我们现在还需要之前所讲的建模有关的知识,引用

?config.xml文件中:

path内为跳转的界面路径,

false代表转发,true则为重定向

<?xml version="1.0" encoding="UTF-8"?>
<config>
?? ?<!--?
?? ??? ?在这里每加一个配置,就相当于actions.put("/goods", new GoodsAction());
?? ??? ?这样就解决了代码灵活性的问题

?? ? -->
?? ?<action path="/book" type="com.lv.framework.BookAction">
?? ??? ?<forward name="list" path="/bookList.jsp" redirect="false" />
?? ??? ?<forward name="toEdit" path="/bookEdit.jsp" redirect="true" />
?? ?</action>
</config>

DispatchServlet中央控制器

package com.lv.framework;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.beanutils.BeanUtils;

import com.lv.xml.ActionModel;
import com.lv.xml.ConfigModel;
import com.lv.xml.ConfigModelFactory;
import com.lv.xml.ForwardModel;

//jsp跳转为/book.action/goods.action/order.action……都会跳转到这个类
@WebServlet("*.action")
public class DispatchServlet extends HttpServlet{
//?? ?再当前中央控制器中必然会有所有子控制器的集合
//?? ?缺陷:如果有商品的增删改查-->意味着要改动代码-->代码的设计不够灵活
//?? ?思考:在不改动代码的情况下,中央控制器也能找到对应的子控制器去处理浏览器请求
//?? ?方案:把加子控制器的逻辑/动作,放到配置文件中完成(Dbutil改连接信息是放在代码中完成/现在是放在Properties)
//?? ?放在配置文件中完成的好处在于:代码更加灵活,修改相关信息不用动代码
//?? ?private Map<String, ActionSupport> actions=new HashMap<String, ActionSupport>();
//?? ?ConfigModel对象又通过建模的知识,把所有的的配置信息给读取过来
?? ?private ConfigModel configModel=null;?? ?
//?? ?初始化所有的子控制器到当前的中央控制器中
?? ??? ?public void init() throws ServletException {
//?? ??? ??? ?在集合中就有了一个子控制器
//?? ??? ??? ? actions.put("/book", new BookAction());
//?? ??? ??? ? actions.put("/goods", new GoodsAction());
//?? ??? ??? ? ……
?? ??? ??? ? try {
?? ??? ??? ??? ?configModel=ConfigModelFactory.build();
?? ??? ??? ?} catch (Exception e) {
?? ??? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ??? ?e.printStackTrace();
?? ??? ??? ?}
?? ??? ?}
?? ?
?? ?@Override
?? ?protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
?? ??? ?doPost(req, resp);
?? ?}
?? ?
?? ?@Override
?? ??? ?protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//?? ??? ??? ?将子控制器与浏览器请求关联起来,寻找能够处理请求的子控制器
//?? ??? ?如何找到浏览器这个路径:http://localhost:8080/book.action?methodName=add-->BookAction.add();
?? ??? ?/*
?? ??? ? * 思路
?? ??? ? * 1.url-->/book
?? ??? ? * 2.通过/book字符串在actions找到BookAction
?? ??? ? * 3.调用BookAction的add,想要调用add,实际上只要统一调用execute就可以了
?? ??? ? */
//?? ??? ?获取到浏览器的请求地址
?? ??? ?String url = req.getRequestURI();
//?? ??? ?1.url-->/book
?? ??? ?url=url.substring(url.lastIndexOf("/"), url.lastIndexOf("."));
?? ??? ?
//?? ??? ?2.通过/book字符串在actions找到BookAction
//?? ??? ?ActionSupport actrion = actions.get(url);
//?? ??? ?原来在Map中寻找子控制器——>现在在配置文件中寻找子控制器
?? ??? ?/**
?? ??? ? * 思路:
?? ??? ? * 1.通过/book找到对应的ActionModel对象
?? ??? ? * 2.通过ActionModel对象拿到类的全路径名com.lv.framework.BookAction
?? ??? ? * 3.反射实例化对象
?? ??? ? */
?? ??? ?ActionModel actionModel = configModel.pop(url);
//?? ??? ?拿到类的全路径名
?? ??? ?String type = actionModel.getType();
?? ??? ?ActionSupport action;
?? ??? ?try {
?? ??? ??? ?action = (ActionSupport) Class.forName(type).newInstance();
//?? ??? ??? ?完成实体类参数的封装
?? ??? ??? ?if(action instanceof ModelDriver) {
//?? ??? ??? ??? ?当前子控制器实现了模型驱动接口
?? ??? ??? ??? ?ModelDriver m=(ModelDriver) action;
//?? ??? ??? ??? ?Book/Goods……
?? ??? ??? ??? ?Object bean=m.getModel();
//?? ??? ??? ??? ?所有的请求参数都在这,需要将所有的请求参数封装到Book/Goods……
?? ??? ??? ??? ?BeanUtils.populate(bean, req.getParameterMap());
?? ??? ??? ?}
//?? ??? ??? ?执行业务逻辑 BookAction.add方法的返回值"list"
?? ??? ??? ?/**
?? ??? ??? ? * 1、书籍新增那么跳转书籍展示页面BookList.jsp 转发
?? ??? ??? ? * 2、书籍编辑跳转编辑页面BookEdit.jsp 重定向
?? ??? ??? ? */
?? ??? ??? ?String res=action.execute(req, resp);
?? ??? ??? ?ForwardModel forwardModel=actionModel.pop(res);
?? ??? ??? ?String path=forwardModel.getPath();
?? ??? ??? ?boolean isredirect = forwardModel.isRedirect();
?? ??? ??? ?if(isredirect) {
?? ??? ??? ??? ?resp.sendRedirect(req.getContextPath()+path);
?? ??? ??? ?}else {
?? ??? ??? ??? ?req.getRequestDispatcher(path).forward(req, resp);
?? ??? ??? ?}
?? ??? ??? ?
?? ??? ?} catch (Exception e) {
?? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ?e.printStackTrace();
?? ??? ?}
? ? ? ? ? ? ? ? }

}
?

3、建立模型驱动接口

解决的问题:

实体类参数接受代码冗余(req.getParammeter(“”),尤其当实体类属性多的情况,封装到实体类中

如:

book.setBid(req.getParameter("bid"));
book.setBname(req.getParameter("bname"));
book.setPrice(req.getParameter("price"));
book.setAthor(req.getParameter("athor"));
book.setPublish(req.getParameter("publish"));

ModelDriver模型驱动接口:

package com.lv.framework;

/**
?* 模型驱动接口
?* 作用:帮助“中央控制器”完成参数封装的工程
?*/
public interface ModelDriver<T> {
?? ?/**
?? ? * GoodsAction-->goods
?? ? * BookAction-->book
?? ? * ...
?? ? * @return
?? ? */
?? ?T getModel();
}

?

在中央控制器的操作:

//?? ?ConfigModel对象又通过建模的知识,把所有的的配置信息给读取过来
?? ?private ConfigModel configModel=null;?? ?
//?? ?初始化所有的子控制器到当前的中央控制器中
?? ??? ?public void init() throws ServletException {
//?? ??? ??? ?在集合中就有了一个子控制器
//?? ??? ??? ? actions.put("/book", new BookAction());
//?? ??? ??? ? actions.put("/goods", new GoodsAction());
//?? ??? ??? ? ……
?? ??? ??? ? try {
?? ??? ??? ??? ?configModel=ConfigModelFactory.build();
?? ??? ??? ?} catch (Exception e) {
?? ??? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ??? ?e.printStackTrace();
?? ??? ??? ?}
?? ??? ?}

?中央控制器内中doPast:

//?? ??? ?2.通过/book字符串在actions找到BookAction
//?? ??? ?ActionSupport actrion = actions.get(url);
//?? ??? ?原来在Map中寻找子控制器——>现在在配置文件中寻找子控制器
?? ??? ?/**
?? ??? ? * 思路:
?? ??? ? * 1.通过/book找到对应的ActionModel对象
?? ??? ? * 2.通过ActionModel对象拿到类的全路径名com.lv.framework.BookAction
?? ??? ? * 3.反射实例化对象
?? ??? ? */
?? ??? ?ActionModel actionModel = configModel.pop(url);
//?? ??? ?拿到类的全路径名
?? ??? ?String type = actionModel.getType();
?? ??? ?ActionSupport action;
?? ??? ?try {
?? ??? ??? ?action = (ActionSupport) Class.forName(type).newInstance();
//?? ??? ??? ?完成实体类参数的封装
?? ??? ??? ?if(action instanceof ModelDriver) {
//?? ??? ??? ??? ?当前子控制器实现了模型驱动接口
?? ??? ??? ??? ?ModelDriver m=(ModelDriver) action;
//?? ??? ??? ??? ?Book/Goods……
?? ??? ??? ??? ?Object bean=m.getModel();
//?? ??? ??? ??? ?所有的请求参数都在这,需要将所有的请求参数封装到Book/Goods……
?? ??? ??? ??? ?BeanUtils.populate(bean, req.getParameterMap());
?? ??? ??? ?}

BookAction中实现ModelDriver<Book>:

package com.lv.framework;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import com.lv.entity.Book;

public class BookAction extends ActionSupport implements ModelDriver<Book>{
//?? ?当前子控制器在哪里调用?把子控制器与浏览器请求关联起来
?? ?public Book book=new Book();
?? ?private String add(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println(book);
?? ??? ?System.out.println("bookDao.add(book)");
?? ??? ?return "list";
?? ?}
?? ?

?? ?@Override
?? ?public Book getModel() {
?? ??? ?// TODO Auto-generated method stub
?? ??? ?return book;
?? ?}

}
?

界面:?

输出结果:

?4、页面的跳转

子控制器:?处理浏览器请求,针对于add/ref/other进行向上抽取、抽象

package com.lv.framework;

import java.io.IOException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

/**
?* 子控制器
?* 针对于add/ref/other进行向上抽取、抽象
?*/
@WebServlet("/Action")
public interface Action{
?? ?/*private void add(HttpServletRequest req, HttpServletResponse resp) {
?? ??? ?System.out.println("bookDao.edit()");
?? ??? ?
?? ?}*/
//?? ?这个方法就是add/ref/other进行向上抽取的抽象方法
//?? ?作用:能够处理浏览器的“所有”请求,包括add/ref/other
//?? ?通过返回值来决定跳转哪一个页面,至于重定向还是转发,由中央控制器决定
?? ?public String execute(HttpServletRequest req, HttpServletResponse resp);
?? ?
}
?

?中央控制器决定:

/**
?? ??? ??? ? * 1、书籍新增那么跳转书籍展示页面BookList.jsp 转发
?? ??? ??? ? * 2、书籍编辑跳转编辑页面BookEdit.jsp 重定向
?? ??? ??? ? */
?? ??? ??? ?String res=action.execute(req, resp);
?? ??? ??? ?ForwardModel forwardModel=actionModel.pop(res);
?? ??? ??? ?String path=forwardModel.getPath();
?? ??? ??? ?boolean isredirect = forwardModel.isRedirect();

// ? ? ? ? isredirect指是重定向
?? ??? ??? ?if(isredirect) {
?? ??? ??? ??? ?resp.sendRedirect(req.getContextPath()+path);
?? ??? ??? ?}else {
?? ??? ??? ??? ?req.getRequestDispatcher(path).forward(req, resp);
?? ??? ??? ?}
?? ??? ??? ?
?? ??? ?} catch (Exception e) {
?? ??? ??? ?// TODO Auto-generated catch block
?? ??? ??? ?e.printStackTrace();
?? ??? ?}

内容结束~~~

  开发测试 最新文章
pytest系列——allure之生成测试报告(Wind
某大厂软件测试岗一面笔试题+二面问答题面试
iperf 学习笔记
关于Python中使用selenium八大定位方法
【软件测试】为什么提升不了?8年测试总结再
软件测试复习
PHP笔记-Smarty模板引擎的使用
C++Test使用入门
【Java】单元测试
Net core 3.x 获取客户端地址
上一篇文章      下一篇文章      查看所有文章
加:2021-08-30 12:30:37  更:2021-08-30 12:30:39 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/17 22:18:26-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码