使用过spring mvc的小伙伴都知道,mvc在使用的时候,我们只需要在controller上注解上@controller跟@requestMapping(“URL”),当我们访问对应的路径的时候,框架便会帮我们去映射到指定的controller里面的指定方法,那么这一切都是怎么做到的呢?还有我们所传递过去的参数,为什么通过request.getParam就能轻易地 拿到呢?大家都知道mvc的核心控制器DispacherServlet的基本运行流程,那么他的内部是怎么运行的呢,我们来做一下简单的实现,让我们能进一步的了解MVC。以助于我们今后的开发。

SpringMVC流程:

1、  用户发送请求至前端控制器DispatcherServlet。

2、  DispatcherServlet收到请求调用HandlerMapping处理器映射器。

3、  处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。

4、  DispatcherServlet调用HandlerAdapter处理器适配器。

5、  HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。

6、  Controller执行完成返回ModelAndView。

7、  HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

8、  DispatcherServlet将ModelAndView传给ViewReslover视图解析器。

9、  ViewReslover解析后返回具体View。

10、DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。

11、 DispatcherServlet响应用户。

  此次实现基于Servlet 3.0,SpringBoot 2.0.1,采用注解的方式实现.这里我先把我的demo的工程包结构先贴出来:

  pom文件:

  1. <dependency>
  2.   <groupId>javax.servlet</groupId>
  3.   <artifactId>javax.servlet-api</artifactId>
  4.   <version>3.1.</version>
  5. </dependency>

  首先,我们先从注解入手,为什么在类上面标注一下@controller以及@RequestMapping,他就能其效果呢? 我们先来定义出这两个注解 @Controller :

  1. @Target(ElementType.TYPE)//表示注解运行在哪里 这里表示只能注解再类上面
  2. @Retention(RetentionPolicy.RUNTIME)//表示注解的(生命周期)哪来出现
  3. public @interface WuzzController {
  4. }

  @RequestMapping :

  1. @Target({ElementType.TYPE, ElementType.METHOD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface WuzzRequestMapping {
  4. String value();
  5. }

  @Service

  1. @Target({ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface WuzzService {
  5. String value() default "";
  6. }

  @Autowired

  1. @Target({ElementType.FIELD})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. @Documented
  4. public @interface WuzzAutowired {
  5. String value() default "";
  6. }

  定义完注解后,那么我们创建一个TestController 来测试一下我们自己定义的注解

  1. @WuzzController
  2. @WuzzRequestMapping("/wuzz")
  3. public class TestController extends BaseController{
  4.  
  5. @WuzzAutowired
  6. private ServiceDemo serviceDemo;
  7.  
  8. @WuzzRequestMapping("/index.do")
  9. public void index() {
  10. try {
  11. response.getWriter().write("index"+serviceDemo.get("wuzz"));
  12. } catch (IOException e) {
  13. e.printStackTrace();
  14. }
  15. }
  16.  
  17. @WuzzRequestMapping("/index1.do")
  18. public void index1() {
  19. try {
  20. response.getWriter().write("index1");
  21. } catch (IOException e) {
  22. e.printStackTrace();
  23. }
  24. }
  25.  
  26. @WuzzRequestMapping("/index2.do")
  27. public void index2() {
  28. try {
  29. response.getWriter().write("index2");
  30. } catch (IOException e) {
  31. e.printStackTrace();
  32. }
  33. }
  34.  
  35. @WuzzRequestMapping("/index3.do")
  36. public void index3() {
  37. try {
  38. response.getWriter().write("index3");
  39. } catch (IOException e) {
  40. e.printStackTrace();
  41. }
  42. }
  43. }

  Service:

  1. public interface ServiceDemo {
  2.  
  3. String get(String name);
  4. }
  5.  
  6. @WuzzService
  7. public class ServiceDemoImpl implements ServiceDemo {
  8. @Override
  9. public String get(String name) {
  10. return "service demo" + name;
  11. }
  12. }

  很显然,现在我们虽然能在类,方法上注解上我们自己定义的注解,但是他们现在是起不到我们MVC框架中的效果的,在框架的内部肯定是需要有一系列操作,才能使得这些注解起效果,我们注意到,要使用MVC的时候我们通常需要配置一个注解扫描包。然后肯定是将有这些特定注解的类扫描出来,并创建出映射的路径,才能达到我们预期的效果。

  现在我们可以做一个小小的测试:所以我这里建了一个Test类来做简单的获取指定类(TestController)里面有没有我们的注解:

  1. public class Test {
  2. private static final Logger LOGGER = LogManager.getLogger(Test.class);
  3. public static void main(String[] args) {
  4. // Class
  5. Class clazz = TestController.class;
  6. //判断这个类是否存在 @WuzzController
  7. if (clazz.isAnnotationPresent(WuzzController.class)) {
  8. LOGGER.info(clazz.getName() + "被标记为controller");
  9. String path = "";
  10. //判断clazz是否存在注解@WuzzRequestMapping
  11. if (clazz.isAnnotationPresent(WuzzRequestMapping.class)) {
  12. //取出注解的值 放入path
  13. WuzzRequestMapping reqAnno =
                (WuzzRequestMapping)clazz.getAnnotation(WuzzRequestMapping.class);
  14. path = reqAnno.value().toString();
  15. }
  16.  
  17. Method[] ms = clazz.getMethods();//拿到控制类所有公开方法遍历
  18. for (Method method : ms) {
  19. //如果不存在该注解 就进入下一轮
  20. if (!method.isAnnotationPresent(WuzzRequestMapping.class)) {
  21. continue;
  22. }
  23. LOGGER.info("方法"+method.getName()+",映射的对外路径:" + path
                + method.getAnnotation(WuzzRequestMapping.class).value().toString());
  24. }
  25. }
  26. }
  27. }

  这里我们运行后的结果为:

  这样我们就可以拿到指定的类里面的指定的一些注解的值,还可以做一系列的操作。好,那么现在我们需要想到的就是核心控制器DispacherServlet了。既然是servlet,我们先来看一下servlet的生命周期。Servlet 生命周期可被定义为从创建直到毁灭的整个过程。以下是 Servlet 遵循的过程:

  • Servlet 通过调用 init () 方法进行初始化。
  • Servlet 调用 service() 方法来处理客户端的请求。
  • Servlet 通过调用 destroy() 方法终止(结束)。
  • 最后,Servlet 是由 JVM 的垃圾回收器进行垃圾回收的。

  既然知道了servlet的生命周期,那就好办了,我们可以通过servlet的初始化,将指定包下的类都扫描起来,然后再重写service()方法去处理这些请求,不久可以了么?接下去我们试一试。

  创建自己的DispacherServlet:我是再spring boot环境下去操作的。我们要配置好拦截路径,基准包并重写init(),service()方法

  1. @WebServlet(urlPatterns = {"*.do"},loadOnStartup = ,initParams = {@WebInitParam(name = "basePackage", value = "com.wuzz.demo")})
  2. public class WuzzDispacherServlet extends HttpServlet {
  3.  
  4. private static final long serialVersionUID = 1L;
  5. //保存url和Method的对应关系
  6. private Map<String, Method> handlerMapping = new HashMap<String, Method>();
  7.  
  8. //保存扫描的所有的类名
  9. private List<String> classNames = new ArrayList<String>();
  10.  
  11. //存放所扫描出来的类及其实例
  12. private Map<String, Object> ioc = new HashMap<String, Object>();
  13.  
  14. public WuzzDispacherServlet() {
  15. super();
  16. }
  17.  
  18. @Override
  19. protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  20. super.doGet(req, resp);
  21. }
  22.  
  23. @Override
  24. protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  25. super.doPost(req, resp);
  26. }
  27.  
  28. @Override
  29. protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
  30. //访问地址http://localhost:8081/wuzz/index.do
  31. //这里拿到uri : /wuzz/index.do
  32. String uri = req.getRequestURI();
  33. //从方法map里获取到映射到的方法实例 : public void com.example.demo.annotation.TestController.index()
  34. //处理成相对路径
  35. if (!this.handlerMapping.containsKey(uri)) {
  36. resp.getWriter().write("404 Not Found!!!");
  37. return;
  38. }
  39. Method method = this.handlerMapping.get(uri);
  40. //通过反射拿到method所在class,拿到class之后还是拿到class的名称
  41. //再调用toLowerFirstCase获得beanName
  42. String beanName = toLowerFirstCase(method.getDeclaringClass().getSimpleName());
  43. BaseController controller;
  44. try {
  45. //获取实例
  46. controller = (BaseController) ioc.get(beanName);
  47. //初始化该controller的请求与响应
  48. //也就是我们的请求中参数怎么通过requset.getParam方法拿到的原因
  49. System.out.println(req.getRequestURI());
  50. controller.init(req, resp);
  51. //然后调用该方法
  52. method.invoke(controller);
  53. } catch (IllegalAccessException e) {
  54. // TODO Auto-generated catch block
  55. e.printStackTrace();
  56. } catch (IllegalArgumentException e) {
  57. // TODO Auto-generated catch block
  58. e.printStackTrace();
  59. } catch (InvocationTargetException e) {
  60. // TODO Auto-generated catch block
  61. e.printStackTrace();
  62. }
  63. }
  64.  
  65. @Override
  66. public void init(ServletConfig config) throws ServletException {
  67. //获取基础扫描包: 这里设定为com.wuzz.demo
  68. String basePackage = config.getInitParameter("basePackage");
  69. //1 扫描包得到所有的class 并且注入ioc
  70. doScanner(basePackage);
  71. //2、初始化扫描到的类,并且将它们放入到ICO容器之中
  72. doInstance();
  73. //3.实际上这里中间可以扫描@Service @Autowired 注解实现自动的依赖注入
  74. //可参考DispacherServlet 的初始化流程
  75. //可参考DispacherServlet#initStrategies(ApplicationContext context)
  76. doAutowired();
  77. //4、初始化HandlerMapping
  78. initHandlerMapping();
  79. }
  80.  
  81. //扫描出相关的类
  82. private void doScanner(String scanPackage) {
  83. //scanPackage = com.gupaoedu.demo ,存储的是包路径
  84. //转换为文件路径,实际上就是把.替换为/就OK了
  85. //classpath
  86. URL url = this.getClass().getClassLoader().getResource("" + scanPackage.replaceAll("\\.", "/"));
  87. File classPath = new File(url.getFile());
  88. for (File file : classPath.listFiles()) {
  89. if (file.isDirectory()) {
  90. doScanner(scanPackage + "." + file.getName());
  91. } else {
  92. if (!file.getName().endsWith(".class")) {
  93. continue;
  94. }
  95. String className = (scanPackage + "." + file.getName().replace(".class", ""));
  96. classNames.add(className);
  97. }
  98. }
  99. }
  100.  
  101. private void doInstance() {
  102. //初始化,为DI做准备
  103. if (classNames.isEmpty()) {
  104. return;
  105. }
  106. try {
  107. for (String className : classNames) {
  108. Class<?> clazz = Class.forName(className);
  109. //什么样的类才需要初始化呢?
  110. //加了注解的类,才初始化,怎么判断?
  111. //为了简化代码逻辑,主要体会设计思想,只举例 @Controller和@Service,
  112. // @Componment...就一一举例了
  113. if (clazz.isAnnotationPresent(WuzzController.class)) {
  114. Object instance = clazz.newInstance();
  115. //Spring默认类名首字母小写
  116. String beanName = toLowerFirstCase(clazz.getSimpleName());
  117. ioc.put(beanName, instance);
  118. } else if (clazz.isAnnotationPresent(WuzzService.class)) {
  119. //1、自定义的beanName
  120. WuzzService service = clazz.getAnnotation(WuzzService.class);
  121. String beanName = service.value();
  122. //2、默认类名首字母小写
  123. if ("".equals(beanName.trim())) {
  124. beanName = toLowerFirstCase(clazz.getSimpleName());
  125. }
  126. Object instance = clazz.newInstance();
  127. ioc.put(beanName, instance);
  128. //3、根据类型自动赋值,投机取巧的方式
  129. for (Class<?> i : clazz.getInterfaces()) {
  130. if (ioc.containsKey(i.getName())) {//接口若有多个实现
  131. throw new Exception("The “" + i.getName() + "” is exists!!");
  132. }
  133. //把接口的类型直接当成key了
  134. ioc.put(i.getName(), instance);
  135. }
  136. } else {
  137. continue;
  138. }
  139. }
  140. } catch (Exception e) {
  141. e.printStackTrace();
  142. }
  143. }
  144. //如果类名本身是小写字母,确实会出问题
  145. //但是我要说明的是:这个方法是我自己用,private的
  146. //传值也是自己传,类也都遵循了驼峰命名法
  147. //默认传入的值,存在首字母小写的情况,也不可能出现非字母的情况
  148. //为了简化程序逻辑,就不做其他判断了,大家了解就OK
  149. //其实用写注释的时间都能够把逻辑写完了
  150. private String toLowerFirstCase(String simpleName) {
  151. char[] chars = simpleName.toCharArray();
  152. //之所以加,是因为大小写字母的ASCII码相差32,
  153. // 而且大写字母的ASCII码要小于小写字母的ASCII码
  154. //在Java中,对char做算学运算,实际上就是对ASCII码做算学运算
  155. chars[] += ;
  156. return String.valueOf(chars);
  157. }
  158. //自动依赖注入
  159. private void doAutowired() {
  160. if (ioc.isEmpty()) {
  161. return;
  162. }
  163. for (Map.Entry<String, Object> entry : ioc.entrySet()) {
  164. //Declared 所有的,特定的 字段,包括private/protected/default
  165. //正常来说,普通的OOP编程只能拿到public的属性
  166. Field[] fields = entry.getValue().getClass().getDeclaredFields();
  167. for (Field field : fields) {
  168. if (!field.isAnnotationPresent(WuzzAutowired.class)) {
  169. continue;
  170. }
  171. WuzzAutowired autowired = field.getAnnotation(WuzzAutowired.class);
  172. //如果用户没有自定义beanName,默认就根据类型注入
  173. //这个地方省去了对类名首字母小写的情况的判断,这个作为课后作业
  174. //小伙伴们自己去完善
  175. String beanName = autowired.value().trim();
  176. if ("".equals(beanName)) {
  177. //获得接口的类型,作为key待会拿这个key到ioc容器中去取值
  178. beanName = field.getType().getName();
  179. }
  180. //如果是public以外的修饰符,只要加了@Autowired注解,都要强制赋值
  181. //反射中叫做暴力访问, 强吻
  182. field.setAccessible(true);
  183. try {
  184. //用反射机制,动态给字段赋值
  185. field.set(entry.getValue(), ioc.get(beanName));
  186. } catch (IllegalAccessException e) {
  187. e.printStackTrace();
  188. }
  189. }
  190. }
  191. }
  192.  
  193. //初始化url和Method的一对一对应关系
  194. private void initHandlerMapping() {
  195. if (ioc.isEmpty()) {
  196. return;
  197. }
  198. for (Map.Entry<String, Object> entry : ioc.entrySet()) {
  199. Class<?> clazz = entry.getValue().getClass();
  200. if (!clazz.isAnnotationPresent(WuzzController.class)) {
  201. continue;
  202. }
  203. //保存写在类上面的@GPRequestMapping("/demo")
  204. String baseUrl = "";
  205. if (clazz.isAnnotationPresent(WuzzRequestMapping.class)) {
  206. WuzzRequestMapping requestMapping = clazz.getAnnotation(WuzzRequestMapping.class);
  207. baseUrl = requestMapping.value();
  208. }
  209. //默认获取所有的public方法
  210. for (Method method : clazz.getMethods()) {
  211. if (!method.isAnnotationPresent(WuzzRequestMapping.class)) {
  212. continue;
  213. }
  214. WuzzRequestMapping requestMapping = method.getAnnotation(WuzzRequestMapping.class);
  215. //优化
  216. // //demo///query
  217. String url = ("/" + baseUrl + "/" + requestMapping.value())
  218. .replaceAll("/+", "/");
  219. handlerMapping.put(url, method);
  220. System.out.println("Mapped :" + url + "," + method);
  221. }
  222. }
  223. }
  224. }

  最后要使这个@WebServlet 起效果,需要配置启动类:

  1. @SpringBootApplication
  2. @ServletComponentScan
  3. public class App {
  4. private final static Logger log = LoggerFactory.getLogger(App.class);
  5.  
  6. public static void main(String[] args) {
  7. SpringApplication.run(App.class, args);
  8. log.info("服务启动成功");
  9. }
  10. }

  经过以上的这些操作,我们自己定义的注解就能生效了,那么现在我们需要考虑的是,这个controller里面,我需要获取请求参数和响应要怎么做呢,其实,我们只要在初始化controller的时候将requset跟response给他塞进去不久好了嘛?我们可以创建一个controller的基类

  1. public class BaseController {
  2.  
  3. protected HttpServletRequest request;
  4. protected HttpServletResponse response;
  5.  
  6. public void init(HttpServletRequest request, HttpServletResponse response) {
  7. this.request = request;
  8. this.response = response;
  9. }
  10.  
  11. public HttpServletRequest getRequest() {
  12. return request;
  13. }
  14.  
  15. public void setRequest(HttpServletRequest request) {
  16. this.request = request;
  17. }
  18.  
  19. public HttpServletResponse getResponse() {
  20. return response;
  21. }
  22.  
  23. public void setResponse(HttpServletResponse response) {
  24. this.response = response;
  25. }
  26. }

  这样子,我们在service()方法内去获取该controller实例的时候controller.init(req, resp); 给他插进去这两个东西,就完事了。最后启动主类,你会发现它真的就调用了controller下的方法。

  如果启用XML的形式去做的话,大致大逻辑还是一样的,只不过需要如下修改:

  1.添加web.xml

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:javaee="http://java.sun.com/xml/ns/javaee"
  4. xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
  5. xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd"
  6. version="2.4">
  7. <display-name>Gupao Web Application</display-name>
  8. <servlet>
  9. <servlet-name>gpmvc</servlet-name>
  10. <servlet-class>com.gupaoedu.mvcframework.v2.servlet.GPDispatcherServlet</servlet-class>
  11. <init-param>
  12. <param-name>contextConfigLocation</param-name>
  13. <param-value>application.properties</param-value>
  14. </init-param>
  15. <load-on-startup></load-on-startup>
  16. </servlet>
  17. <servlet-mapping>
  18. <servlet-name>gpmvc</servlet-name>
  19. <url-pattern>/*</url-pattern>
  20. </servlet-mapping>
  21. </web-app>

  2.去掉 @WebServlet 注解,添加 application.properties 内容如下:

  1. basePackage = com.wuzz.demo

  3.既然添加了配置文件,那么我们需要扫描配置文件获取配置信息

  1. //保存application.properties配置文件中的内容
  2. private Properties contextConfig = new Properties();
  3. //加载配置文件
  4. private void doLoadConfig(String contextConfigLocation) {
  5.   //直接从类路径下找到Spring主配置文件所在的路径
  6.   //并且将其读取出来放到Properties对象中
  7.   //相对于scanPackage=com.gupaoedu.demo 从文件中保存到了内存中
  8.   InputStream fis = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
  9.   try {
  10.     contextConfig.load(fis);
  11.   } catch (IOException e) {
  12.     e.printStackTrace();
  13.   }finally {
  14.     if(null != fis){
  15.       try {
  16.         fis.close();
  17.       } catch (IOException e) {
  18.         e.printStackTrace();
  19.       }
  20.     }
  21.   }
  22. }
  1. 加载配置文件
    doLoadConfig(config.getInitParameter("contextConfigLocation"));
  2.  
  3. 扫描相关的类
    doScanner(contextConfig.getProperty("scanPackage"));

  4.修改pom文件为war包形式

  contextConfigLocation 是我们在web.xml中配置的。然后用Tomcat启动即可。

spring mvc底层(DispacherServlet)的简单实现的更多相关文章

  1. spring MVC mybatis dispacherServlet(源码解读)

    以下源码版本(4.2.0.RELEASE) dispacherServlet是servlet的实现类,是spring MVC的前端转发器,是spring MVC的核心. 那么它做了哪些事呢? 它主要做 ...

  2. Spring MVC(八)--控制器接受简单列表参数

    有些场景下需要向后台传递一个数组,比如批量删除传多个ID的情况,可以使用数组传递,数组中的ID元素为简单类型,即基本类型. 现在我的测试场景是:要从数据库中查询minId<id<maxId ...

  3. Spring MVC 底层原理

    参考博客:http://www.cnblogs.com/xiaoxi/p/6164383.html Spring MVC处理的流程: 具体执行步骤如下: 1 首先用户发送请求给前端控制器,前端控制器根 ...

  4. Spring MVC 学习 之 - 配置简单demo

    1.环境参数: Maven:3.1.1 JDK  :1.6 2.项目文件结构图: 3.各文件配置: 3.1. pom.xml <project xmlns="http://maven. ...

  5. Spring MVC 文件上传简单示例(form、ajax方式 )

    1.Form Upload SpringMVC 中,文件的上传是通过 MultipartResolver 实现的,所以要实现上传,只要注册相应的 MultipartResolver 即可. Multi ...

  6. spring mvc+mybatis+sql server简单配置

    context.xml配置文件 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns=&qu ...

  7. Spring MVC 关于分页的简单实现

    据本人了解,目前较常用的分页实现办法有两种: 1.每次翻页都修改SQL,向SQL传入相关参数去数据库实时查出该页的数据并显示. 2.查出数据库某张表的全部数据,再通过在业务逻辑里面进行处理去取得某些数 ...

  8. Spring mvc i18n国际化的简单demo

    在渲染视图的xml文件中,配置一个i18nBean 实现两个接口: SessionLocaleResolver --> 加载资源主题 ReloadableResourceBundleMessag ...

  9. 0000 - Spring MVC 原理以及helloworld

    1.概述 Spring MVC是目前最好的实现MVC设计模式的框架,是Spring框架的一个分支产品.以Spring IOC容器为基础,并利用容易的特性来简化它的配置.Spring MVC相当于Spr ...

随机推荐

  1. Vue.js 技术揭秘(学习) 深入响应式原理 nextTick外传

    microTask  mutationObserve. promise.then macroTask setImmediate. messageChannnel.setTimeout.postMess ...

  2. C#获取客户端IP地址

    客户端ip:Request.ServerVariables.Get("Remote_Addr").ToString();客户端主机名:Request.ServerVariables ...

  3. day 2 - 逻辑运算

    1. 初识编码 最早的'密码本' ascii 涵盖了英文字母大小写,特殊字符,数字. ascii 只能表示256种可能,太少,后来创办了万国码 unicode 16表示一个字符不行,32位表示一个字符 ...

  4. Python Django CBV下的通用视图函数

    ListView TemplateView DetailView 之前的代码实例基本上都是基于FBV的模式来撰写的,好处么,当然就是简单粗暴..正如: def index(request): retu ...

  5. Linux中的libc和glibc

    现在centos6.8-x64系统里的c标准库已经成了glibc,glibc取代了libc,c标准库的位置在/lib64/libc.so.6 以下为转载 一.libc库 Linux平台提供的C标准库包 ...

  6. A - Arcade Game Gym - 100814A (概率思维题)

    题目链接:https://cn.vjudge.net/contest/285964#problem/A 题目大意:每一次给你你一个数,然后对于每一次操作,可以将当前的数的每一位互换,如果互换后的数小于 ...

  7. Django学习手册 - 基于requests API验证(二)

    原理分析: API接口验证 1.一个认证的key server端 和 client端都必须有这么一个认证key. 2.认证登录的时间限定 3.保存已验证的信息,在以后的验证不能再次登录 client ...

  8. 数据库无法启动ORA-01034: ORACLE not available

    错误场景: 1.数据库未启动,查询v$instance报错 SQL> select status from v$instance; select status from v$instance * ...

  9. [转] Understanding-LSTMs 理解LSTM

    图文并茂,讲得极清晰. 原文:http://colah.github.io/posts/2015-08-Understanding-LSTMs/ colah's blog Blog About Con ...

  10. Spring @Bean注解 (基于java的容器注解)

    基于java的容器注解,意思就是使用Java代码以及一些注解,就可以取代spring 的 xml配置文件. 1-@Configuration & @Bean的配合 @Configuration ...