前言

这是Strtus的开山篇,主要是引入struts框架...为什么要引入struts,引入struts的好处是什么,以及对Struts2一个简单的入门....

为什么要引入struts?

既然Servlet能够完成的事,我们为啥要用框架呢??

  • 框架帮我们封装了很多常用的功能

    • 把Web带过来的参数自动封装到JavaBean中[以前,我们刚开始学的时候是单个单个来获取参数的,后来我们又使用了BeanUtils写工具方法来帮我们封装]。现在,我们使用了Struts2的话,那么框架内部就能帮我们封装了。
  • 更加灵活[不用把路径等信息写死在程序上],对于路径我们使用配置文件来进行管理,如果目录发生了变化,也不用一个一个去修改每个程序的路径。
  • 每个Servlet中都有doGet和doPost这样的方法,没必要的。我们抽取出来,通过配置文件来把这两个方法替换掉,那么我们的程序就会更加优雅了。

于是乎,struts2就应运而生了。


自定义struts

在正式讲解struts之前,我们来看一下,以我们现在的水平,能够怎么优化它。。

以用户的登陆注册案例来进行说明

传统的用户登陆注册

  • dao
public class UserDao {public User login(User user) {if ("aaa".equals(user.getUsername()) && "123".equals(user.getPsd())) {System.out.println("登陆成功!");return user;} else {System.out.println("登陆失败!");return null;}}public void register(User user) {System.out.println("注册成功!" + user.getUsername());}}
  • service

public class UserService {private UserDao userDao = new UserDao();public User longin(User user) {return userDao.login(user);}public void register(User user) {userDao.register(user);}}
  • loginServlet

@javax.servlet.annotation.WebServlet(name = "LoginServlet",urlPatterns = "/LoginServlet")
public class LoginServlet extends javax.servlet.http.HttpServlet {protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {//得到用户带过来的数据,封装到Bean对象中String username = request.getParameter("username");String psd = request.getParameter("psd");User user = new User();user.setPsd(psd);user.setUsername(username);try {//调用Service方法UserService userService = new UserService();userService.longin(user);//登陆成功跳转到首页request.getRequestDispatcher("/index.jsp").forward(request, response);} catch (Exception e) {e.printStackTrace();//登陆失败,跳转到相关的提示页面request.setAttribute("message","登陆失败了!!!");request.getRequestDispatcher("/message.jsp").forward(request, response);}}protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {this.doPost(request, response);}
}
  • registerServlet

@javax.servlet.annotation.WebServlet(name = "RegisterServlet",urlPatterns = "/RegisterServlet")
public class RegisterServlet extends javax.servlet.http.HttpServlet {protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {//得到用户带过来的数据,封装到Bean对象中String username = request.getParameter("username");String psd = request.getParameter("psd");User user = new User();user.setPsd(psd);user.setUsername(username);try {//调用Service方法UserService userService = new UserService();userService.register(user);//注册成功跳转到登陆界面request.getRequestDispatcher("/login.jsp").forward(request, response);//注册成功,我也可以跳转到首页//request.getRequestDispatcher("/index.jsp").forward(request, response);} catch (Exception e) {e.printStackTrace();//注册失败,跳转到相关的提示页面request.setAttribute("message","注册失败了!!!");request.getRequestDispatcher("/message.jsp").forward(request, response);}}protected void doGet(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {this.doPost(request, response);}
}
  • login.jsp

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><form action="${pageContext.request.contextPath}/LoginServlet" method="post">用户名:<input type="text " name="username">密码:<input type="password " name="psd"><input type="submit" value="登陆"></form></body>
</html>
  • register.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html><head><title>$Title$</title></head><body><form action="${pageContext.request.contextPath}/RegisterServlet" method="post">用户名:<input type="text " name="username">密码:<input type="password " name="psd"><input type="submit" value="注册"></form></body>
</html>

上面的代码已经经过了测试,是可以跑起来的。


①:跳转页面的路径是写死的。我在注册成功了以后,我可以跳转到首页上,也可以跳转到登陆的界面上。如果我要选择其中的一个,就必须修改源代码...

②:一个功能对应一个Servlet,太麻烦了...写了LoginServlet,还要写RegisterServlet....


新型的用户登陆注册

我们会发现,无论什么Servlet上最终还是跳转到相对应的JSP页面的...也就是说,第一和第二步骤【封装数据、调用Service】我们可以封装起来...只要返回uri给Servlet跳转到JSP页面就好了


LoginAction

返回的uri分两种情况:

  • 如果是转发,那么返回的是RequestDispatcher对象
  • 如果是重定向,那么返回的是字符串

/*** Created by ozc on 2017/4/26.* <p>* 一个Action对应一个Servlet,Action负责处理具体的请求*/
public class LoginAction {public Object login(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {Object uri ;//得到用户带过来的数据,封装到Bean对象中String username = request.getParameter("username");String psd = request.getParameter("psd");User user = new User();user.setPsd(psd);user.setUsername(username);try {//调用Service方法UserService userService = new UserService();userService.longin(user);//登陆成功跳转到首页request.getSession().setAttribute("user", user);//跳转到首页的时候需要重定向//response.sendRedirect(request.getContextPath() + "/index.jsp");//如果是重定向,那么返回的是字符串uri = "/index.jsp";return uri;} catch (Exception e) {e.printStackTrace();//登陆失败,跳转到相关的提示页面request.setAttribute("message","登陆失败了!!!");//request.getRequestDispatcher("/message.jsp").forward(request, response);//如果是转发,那么返回的是RequestDispatcher对象uri = request.getRequestDispatcher("/message.jsp");return uri;}}
}
  • LoginServlet就可以写成这样了:
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {//得到LoginAction对象LoginAction loginAction = new LoginAction();Object uri = loginAction.login(request, response);//是重定向if (uri instanceof String) {response.sendRedirect(request.getContextPath() + uri);} else {//是转发,强转成是RequestDispatcher对象((RequestDispatcher) uri).forward(request, response);}}

RegisterAction

  • RegisterAction
/*** Created by ozc on 2017/4/26.* * 一个Action对应一个Servlet,Action负责处理具体的请求*/
public class RegisterAction {public Object register(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {Object uri ;//得到用户带过来的数据,封装到Bean对象中String username = request.getParameter("username");String psd = request.getParameter("psd");User user = new User();user.setPsd(psd);user.setUsername(username);try {//调用Service方法UserService userService = new UserService();userService.register(user);//登陆成功跳转到登陆页面uri = request.getRequestDispatcher("/login.jsp");return uri;} catch (Exception e) {e.printStackTrace();//注册失败,跳转到相关的提示页面request.setAttribute("message","注册失败了!!!");//request.getRequestDispatcher("/message.jsp").forward(request, response);uri = request.getRequestDispatcher("/message.jsp");return uri;}}
}
  • RegisterServlet
protected void doPost(javax.servlet.http.HttpServletRequest request, javax.servlet.http.HttpServletResponse response) throws javax.servlet.ServletException, IOException {//得到RegisterActionRegisterAction registerAction = new RegisterAction();Object uri = registerAction.register(request, response);//是重定向if (uri instanceof String) {response.sendRedirect(request.getContextPath() + uri);} else {//是转发,强转成是RequestDispatcher对象((RequestDispatcher) uri).forward(request, response);}}

思考

到目前为止,我们搞了两个Action类来封装Servlet的逻辑代码,我们再次看回Servlet的代码。

可以很清楚地发现:两个实现不同功能的Servlet仅仅是调用的Action不同....如果是仅仅调用的Action不同【通过反射来调用不同的Action】,那么我们应该想到使用一个Servlet来管理整个项目,也就是说:整个web项目只有一个核心的控制器

问题:

①:我们在之前是直接指明Servlet的映射路径了,现在要ActionServlet处理所有的请求,我们只要定一个规则:只要后缀为.action的,那么都交由核心控制器ActionServlet来控制....

②:现在全部的请求已经交由ActionServlet控制,那怎么知道调用的是哪个Action???我们可以通过请求的uri,比如:http://localhost:8080/login.action,其中login就代表的是调用LoginAction..也就是说login=LoginAction,我们可以通过properties文件来配置..

③:现在我们已经知道了调用的是哪个Action了,但是Action可能不仅仅只有一个方法,我们还要在调用的时候,指定的方法名是什么.这很简单,一般我们都是职责分工明确的,method=login....并且,调用的Action和具体的方法也是有关系的,不可能是孤立存在的。因此,我们的配置文件是不能使用properties的,需要使用XML

④:在调用方法的时候,是返回一个Object的uri的,uri的类型可能是String、也可以能是RequestDispatcher、并且返回的结果可能有几种情况的【可能跳转到首页,也可能跳转到登陆界面】

⑤:Action调用的方法和返回的uri也是是有关系的!.....不同的Action调用不同的方法,返回的uri也是不同的....

⑥:要跳转到哪个页面上,可以通过标识量来识别....比如:success表示成功执行,如果要重定向那么多加个type类型,如果不重定向就没有type类型..路径使用path来表示..因此,在具体的Action中,就不需要返回具体的uri,只要返回一个标识量即可


画一张图来梳理一下思路:


XML配置

我们可以写出这样的XML配置,当ActionServlet初始化的时候,读取XML配置文件,就知道调用的是什么Action,Action中的什么方法,以及跳转到哪个页面上了

<?xml version="1.0" encoding="UTF-8" ?>
<mystruts><package><action name="login" className="zhongfucheng.servlet.LoginServlet" method="login"><!--是否存在type属性,存在则是重定向,不存在则是转发--><!--result的值表示的就是跳转的路径--><result name="success" type="redirect">/index.jsp</result><result name="fail">/message.jsp</result></action><action name="register" className="zhongfucheng.servlet.RegisterServlet" method="register"><!--是否存在type属性,存在则是重定向,不存在则是转发--><!--result的值表示的就是跳转的路径--><result name="success">/message.jsp</result><result name="fail">/message.jsp</result></action></package></mystruts>

为了更好地管理这些信息,我们应该使用JavaBean来对它们封装

  • ActionMappingManager-------管理全部的Action
/*** Created by ozc on 2017/4/26.* * 该类管理着全部的Action** 要管理全部的Action,就需要用一个容器来装载这些Action** 选择Map集合是最合适的,可以通过key来得到Action,key就是<action name=><action/>中的name属性**/
public class ActionMappingManager {private Map<String, ActionMapping> map = new HashMap<>();//注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Managerpublic ActionMapping getActionMapping(String name) {return map.get(name);}}
  • ActionMapping----表示单个的Action

public class ActionMapping {//所有的resultsprivate Map<String, Results> results;//关键字nameprivate String name;//要调用的Action路径private String className;//Action中的方法private String method;public Map<String, Results> getResults() {return results;}public void setResults(Map<String, Results> results) {this.results = results;}public String getName() {return name;}public void setName(String name) {this.name = name;}public String getClassName() {return className;}public void setClassName(String className) {this.className = className;}public String getMethod() {return method;}public void setMethod(String method) {this.method = method;}
}
  • Results---表示的是结果视图
/*** Created by ozc on 2017/4/26.** 该类表示的是结果视图****/
public class Results {//方法返回的标识private String name;//要跳转的方式private String type;//要跳转的页面private String page;public String getName() {return name;}public void setName(String name) {this.name = name;}public String getType() {return type;}public void setType(String type) {this.type = type;}public String getPage() {return page;}public void setPage(String page) {this.page = page;}
}

ActionMappingManager读取配置文件

在ActionMappingManager中,应该读取配置文件,然后把信息全部封装到里边去...


/*** Created by ozc on 2017/4/26.** 该类管理着全部的Action** 要管理全部的Action,就需要用一个容器来装载这些Action** 选择Map集合是最合适的,可以通过key来得到Action,key就是<action name=><action/>中的name属性**/
public class ActionMappingManager {private Map<String, ActionMapping> allAction ;public ActionMappingManager() {this.allAction = new HashMap<>();//读取配置文件信息init();}public void init() {/********通过DOM4J读取配置文件信息*********/try {//得到解析器SAXReader saxReader = new SAXReader();//读取在类目录下的mystruts.xml文件InputStream stream = ActionMappingManager.class.getClassLoader().getResourceAsStream("mystruts.xml");//得到代表XML文件的Document对象Document document = saxReader.read(stream);//通过XPATH直接得到所有的Action节点List list = document.selectNodes("//action");//得到每个Action节点for (int i = 0; i < list.size(); i++) {Element action = (Element) list.get(i);//把得到每个Action的节点信息封装到ActionMapping中ActionMapping actionMapping = new ActionMapping();String name = action.attributeValue("name");String method = action.attributeValue("method");String className = action.attributeValue("className");actionMapping.setName(name);actionMapping.setMethod(method);actionMapping.setClassName(className);//得到action节点下的所有result节点List results = action.elements("result");//得到每一个result节点for (int j = 0; j < results.size(); j++) {Element result = (Element) results.get(j);//把得到每个result节点的信息封装到Results中Results results1 = new Results();//得到节点的信息String name1 = result.attributeValue("name");String type = result.attributeValue("type");String page = result.getText();results1.setName(name1);results1.setType(type);results1.setPage(page);//把result节点添加到ActionMapping的集合中actionMapping.getResults().put(name1, results1);}//最后把得到每个ActionMapping的信息添加到ActionMappingManager中allAction.put(name, actionMapping);}} catch (DocumentException e) {new RuntimeException("初始化的时候出错了!“" + e);}}//注意:外界都是通过name来得到对应的Action的,并不会获取得到整个Managerpublic ActionMapping getActionMapping(String name) {return allAction.get(name);}
}

ActionServlet

使用init()方法只加载创建一个ActionManagerMapping对象,并设置在Web容器启动了该Servlet就启动


/*** Created by ozc on 2017/4/26.*** Web容器一启动的时候,该类就应该加载了,在web.xml文件中配置onloadStart*/public class ActionServlet extends HttpServlet {//该对象封装了所有的XML信息ActionMappingManager actionMappingManager ;@Overridepublic void init() throws ServletException {//让ActionMappingManager对象只有一个!actionMappingManager = new ActionMappingManager();}protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {try {//得到用户的uriString uri = request.getRequestURI();//截取uri的关键部分-----截完应该是loginuri = uri.substring(uri.lastIndexOf("/") + 1, uri.lastIndexOf("."));//通过uri得到配置文件中的action信息ActionMapping actionMapping = actionMappingManager.getActionMapping(uri);//得到action的类名,方法名String className = actionMapping.getClassName();String method = actionMapping.getMethod();//通过反射创建出Action的对象,调用对应的方法Class t = Class.forName(className);Object o = t.newInstance();//注意:这里的参数是接口的class,不是单纯的request的class,单纯的class是实现类Method m = t.getMethod(method, HttpServletRequest.class, HttpServletResponse.class);//调用方法,得到标记String returnFlag = (String) m.invoke(o, request, response);//通过标记得到result的具体信息Results result = actionMapping.getResults().get(returnFlag);String type = result.getType();String page = result.getPage();//判断是重定向还是转发,为空就是转发,反则是重定向if (type == null) {response.sendRedirect(page);} else {request.getRequestDispatcher(request.getContextPath() + page).forward(request, response);}} catch (Exception e) {e.printStackTrace();}}protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {this.doPost(request, response);}
}

具体的Action的方法只要返回一个标识量即可,我们通过标识量来得到具体的跳转页面url和跳转的方法的。。。


效果:


自定义MyStruts总结:

由于传统web的Controller模块存在弊端:

  • 一些功能重复使用,代码过于重复了。
  • 跳转的页面写死了。改变需求的时候需要更改源代码

本博文主要模拟Struts的开发流程

  • 使用一个ActionServlet核心控制器来管理全部的Web请求,写XML配置文件,读取配置文件。
  • ActionMapping封装了Action的基本信息,在XML配置文件中就是读取Action的基本信息,封装到JavaBean上,最后使用ActionMapping类的集合统一管理起来。
  • 当用户访问的时候,我们根据url也就是Action的名称反射出对应的类,来对其进行操作
  • 根据XML文件的配置信息来确定跳转方法、跳转的url

我们现在学习的是Struts2,其实Struts1和Struts2在技术上是没有很大的关联的。 Struts2其实基于Web Work框架的,只不过它的推广没有Struts1好,因此就拿着Struts这个名气推出了Struts2框架。

因此,学习Struts2的时候,不了解Struts1是没有任何关系的。

在前面,已经说明了为什么要引入Struts框架,其实就是为了提高开发效率...

Struts2框架预先实现了一些功能:

  • 请求数据自动封装
  • 文件上传的功能
  • 对国际化功能的简化
  • 数据效验功能.......等等

Struts2开发步骤

我们就直接来讲解Struts2的开发步骤是什么吧....在了解它的细节之前,先要把配置环境搭好!

引入jar文件

完整的struts中的jar包有80多个,我们日常开发是不需要那么多个的。一般我们导入的jar包有8个:

  • commons-fileupload-1.2.2.jar 【文件上传相关包】
  • commons-io-2.0.1.jar【文件上传相关包】
  • struts2-core-2.3.4.1.jar 【struts2核心功能包】
  • xwork-core-2.3.4.1.jar 【Xwork核心包】
  • ognl-3.0.5.jar 【Ognl表达式功能支持表】
  • commons-lang3-3.1.jar 【struts对java.lang包的扩展】
  • freemarker-2.3.19.jar 【struts的标签模板库jar文件】
  • javassist-3.11.0.GA.jar 【struts对字节码的处理相关jar】


配置web.xml

在web.xml中配置的过滤器,其实就是在为struts进行初始化工作

值得注意的是:如果该web.xml配置了多个fileter,那么struts的filter需要在最后面!


<!-- 引入struts核心过滤器 --><filter><filter-name>struts2</filter-name><filter-class>org.apache.struts2.dispatcher.ng.filter.StrutsPrepareAndExecuteFilter</filter-class></filter><filter-mapping><filter-name>struts2</filter-name><url-pattern>/*</url-pattern></filter-mapping>

开发Action

开山篇我们已经说了,Servlet的业务代码,我们都使用Action来代替...Action类一般继承着ActionSupport

Action类也叫动作类,处理请求的类。


public class HelloAction extends ActionSupport {@Overridepublic String execute() throws Exception {System.out.println("helloworld");return "success";}
}

至于execute()方法是什么,我们先不要去管它,为啥要返回一个String,我们也不要去管它....只要记住开发步骤,并且,我们的Action类是要继承ActionSupport类的

配置struts.xml

至于配置struts.xml,我们可以在文件中找到相对应的模版代码的...最终修改成下面这个样子就行了:


<?xml version="1.0" encoding="UTF-8" ?><!DOCTYPE struts PUBLIC"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN""http://struts.apache.org/dtds/struts-2.0.dtd">
<struts>
<package name="hello" extends="struts-default"><action name="hello" class="action.HelloAction" method="execute"><result name="success">/index.jsp</result></action>
</package>
</struts>

看完上面的配置文件,是非常像我们开山篇写的struts框架的配置文件的....

效果:

在地址栏中直接输入hello,就跳转到index.jsp页面了。并且,execute()中的语句被执行了...


Struts2执行流程

我们来简单地了解一下Struts的执行流程,然后再慢慢对上面的开发步骤的部分进行讲解....

服务器启动

下边我说的都是struts流程的重点:

  • 加载web.xml文件
  • 找到我们配置的filter中的StrutsPrepareAndExecuteFilter
  • StrutsPrepareAndExecuteFilter在里边执行init()方法
  • 一直到Dispatcher dispatcher = init.initDispatcher(config);,初始化dispatcher
  • 在初始化dispatcher的时候加载struts-default.xml和我们配置的struts.xml

下面用GIF图来看看它的执行过程:

细心的朋友可能会发现,我们在struts.xml的package节点下,extends了struts-default....那struts-default究竟是什么东西呢?

我们找到它的源码:

我们发现了一大堆的Bean,interceptor,result-type,interceptor-stack...下边我来讲解一下它们是干嘛用的...

  • bean指定了struts在运行的时候需要创建的对象类型

    • 在运行struts的时候,可能需要创建一些对象,那么就通过Bean来指定
  • interceptor是struts定义的拦截器,一共有32个
    • 前边已经说了,Struts为我们实现了一些功能,就是通过拦截器来实现的
  • result-type是跳转结果的类型
    • Action业务方法中的返回值,我们发现几个实用的:redirect【重定向】、dispatcher【转发】、redirectAction【重定向到Action资源】、stream【文件下载的时候用】...跳转结果的类型也在这里定义了
  • interceptor-stack是拦截器的栈
    • 拦截器有32个,我们可能会使用很多的拦截器,不可能一个一个来调用,于是提供了拦截器栈...其实可以简单看成文件夹和文件之间的关系
  • default-interceptor-ref是默认执行的拦截器栈
  • default-class-ref class是默认的执行Action类

还要补充的就是:默认的拦截器栈有18个拦截器....


拦截器和过滤器

拦截器和过滤器都是拦截资源的

拦截器只拦截Action请求,是struts的概念...

过滤器拦截web的所有资源,是Servlet的概念...


小总结

服务器启动的时候,其实就是加载了web.xml文件,然后调用init()方法去加载struts.xml和struts-default.xml之类的文件.....

注意:此时的拦截器是还没有被调用的


访问阶段

服务器启动的阶段,仅仅是加载了各种的xml文件...那么当我们访问Action的时候,它的执行流程是怎么的呢?

  • 首先,它会创建我们在struts.xml中配置的Action对象
  • 接着,它会按照默认的顺序执行18个拦截器【也就是调用默认拦截器栈】
  • 最后,它会执行Action的业务方法【也就是execute(),我们在struts.xml文件中配置了什么,就执行什么业务方法】

值得注意的是:每访问Action一次,它就会创建一个对象...它并不是和Servlet一样只有一个对象...因此它是线程安全的.


深入讲解struts.xml

这是我们的struts.xml的内容,相信现在对它也不会太陌生了...


<struts>
<package name="hello" extends="struts-default"><action name="hello" class="action.HelloAction" method="execute"><result name="success">/index.jsp</result></action>
</package>
</struts>

package

package其实就是包,那包用来干什么?包就是用来管理Action

通常来说,我们都是一个业务模版对应一个package

name

name是包的名字,值得注意的是,包的名称是不能重复的。


extends

extends代表的是当前包继承着哪个包。在struts中,包一定要继承着struts-default


abstract

在package中还有abstract这个属性,使用该属性时:表明当前包被其他的包继承...并且,在package下不能有action,否则会出错!


namespace

在package中还有namespace这个属性---名称空间....它是作为路径的一部分的,默认是"/"


actoin

action:配置请求路径与Action类的映射关系


name

name是请求路径的名字


class

class是处理action类的全名


method

method是调用的方法名称


result

result代表的是Action中业务方法返回的值


name

name是action处理返回的值


type

type是跳转的类型


文本值

文本值是跳转的路径


细节

前边已经说了,一个package应该对应一个业务模块..目的就是把职能细分出来...

struts为了让我们更好地管理xml文件,它还可以这样做:在不同的模块中用不同的xml文件进行描述...

最后在struts.xml文件中将其引入即可..


<!--struts在运行的时候总会加载这个文件-->
<!--总配置文件总引入其他的文件-->
<struts><include file="privilegeaction/privilege.xml"/><include file="useraction/hello.xml"/>
</struts>

如果文章有错的地方欢迎指正,大家互相交流。习惯在微信看技术文章,想要获取更多的Java资源的同学,可以关注微信公众号:Java3y

更多的文章可往:文章的目录导航

Struts2入门这一篇就够了 1相关推荐

  1. Struts2入门这一篇就够了

    前言 这是Strtus的开山篇,主要是引入struts框架...为什么要引入struts,引入struts的好处是什么,以及对Struts2一个简单的入门.... 为什么要引入struts? 既然Se ...

  2. React入门看这篇就够了

    2019独角兽企业重金招聘Python工程师标准>>> 摘要: 很多值得了解的细节. 原文:React入门看这篇就够了 作者:Random Fundebug经授权转载,版权归原作者所 ...

  3. Python语言入门这一篇就够了-学习笔记(十二万字)

    Python语言入门这一篇就够了-学习笔记(十二万字) 友情提示:先关注收藏,再查看,12万字保姆级 Python语言从入门到精通教程. 文章目录 Python语言入门这一篇就够了-学习笔记(十二万字 ...

  4. groovy if 判断字符串_Groovy快速入门看这篇就够了

    原标题:Groovy快速入门看这篇就够了 来自:刘望舒(微信号:liuwangshuAndroid) 前言 在前面我们学习了和两篇文章,对Gradle也有了大概的了解,这篇文章我们接着来学习Groov ...

  5. 强化学习入门这一篇就够了!!!万字长文

    强化学习 强化学习入门这一篇就够了万字长文带你明明白白学习强化学习... 强化学习入门这一篇就够了 强化学习 前言 一.概率统计知识回顾 1.1 随机变量和观测值 1.2 概率密度函数 1.3 期望 ...

  6. .NET Core实战项目之CMS 第五章 入门篇-Dapper的快速入门看这篇就够了

    写在前面 上篇文章我们讲了如在在实际项目开发中使用Git来进行代码的版本控制,当然介绍的都是比较常用的功能.今天我再带着大家一起熟悉下一个ORM框架Dapper,实例代码的演示编写完成后我会通过Git ...

  7. 动态规划入门看这篇就够了,万字长文!

    今天是小浩算法 "365刷题计划" 动态规划 - 整合篇.大家应该期待已久了吧!奥利给! 01 PART 动态规划是啥 我们把要解决的一个大问题转换成若干个规模较小的同类型问题,当 ...

  8. Spring入门这一篇就够了

    前言 前面已经学习了Struts2和Hibernate框架了.接下来学习的是Spring框架-本博文主要是引入Spring框架- Spring介绍 Spring诞生: 侵入式概念 Spring是一种非 ...

  9. elasticsearch说了一些了,这次说说Solr【入门Solr这篇就够了】

    在之前的博客中,我提到过ElasticSearch学习,请先看这一篇(win_Elasticsearch) 并在空闲之余写了一篇关于反向索引和手写分词器,具体在搜索引擎--反向索引原理揭秘及手写ik分 ...

最新文章

  1. 四级计算机基础知识,全国计算机等级考试四级通关攻略
  2. Python 内嵌函数运用(探究模块)
  3. Unity教程:如何使用枚举来帮助简化游戏开发
  4. 深度学习(23)随机梯度下降一: 随机梯度下降简介
  5. 图解CentOS系统启动流程
  6. 跨专业留学学计算机硕士,跨专业申请中国香港计算机硕士需要注意哪些?
  7. OOB模式下Exit事件的处理
  8. 这是一个价值一个亿的项目思维导图
  9. 按键精灵手机版读取MYSQL_mysql,按键精灵,读取写入
  10. 转载ios开发如何使用Xcode的Targets来管理开发和生产版本的构建
  11. EDM营销解读[转载]
  12. Git教程之如何版本回退
  13. 怎么去除视频上的文字?一篇教你:视频上的文字水印怎么去除
  14. VL102+IT6563替代方案|TYPEC转HDMI带PD方案|AG9311MAQ设计方案
  15. 静态页面如何引入head.html和foot.html
  16. 计算机毕设(附源码)JAVA-SSM科技类产品众筹系统
  17. 纯js实现文件下载并重命名功能
  18. 为什么说深度学习和机器学习截然不同?
  19. Mission planner加载天地图(混合卫星地图含标注)
  20. 冰雹猜想不一样的算法

热门文章

  1. 企业运维经典面试题汇总(4)
  2. 华为s5700-SI交换机常用命令
  3. Excel 常用快捷键总结(Alt系列)
  4. 如何考虑程序的优化性
  5. 【Oracle】重置参数
  6. eurekaAutoServiceRegistration 异常
  7. springboot在启动jar由于配置hibernate的映射文件上classpath导致的!BOOT-INF/classes/!路径出现!号问题解决方法
  8. 解决Ubuntu不能全屏问题
  9. mysql中CONCAT值为空的问题解决办法
  10. 项目出现 The superclass “javax.servlet.http.HttpServlet“ was not found on the Java Build Path 解决方法