Scala 入门

1. 基本语法

1. 变量 var

1
2
3
var name:string="hello";
var name="hello";
var num=3.0;

2. 常量 val

1
val name="string";

3. 这里是没有 ++ 操作和 – 操作的

4. 函数调用

不用使用对象来调用,导入包以后直接调用即可

1
2
3
4
import scala.math._
sqrt(2);
pow(1,2);
min(1,2);

5.apply()

默认调用了object的apply函数

1
"hello"<6>;

6.if-else

数字图像处理

1.基本概念

1. 图像的定义

  1. 图像:一幅图像是一个东西的另一种表示,是其所表示物体信息的一个浓缩和高度概括。

2. 图像分类

1. 模拟图像:连续变化的函数

2. 数字图像:离散的矩阵表示

数字图像的定义:

数字图像是指由被称为像素的小区域组成的二维矩阵。

数字图像类型:
  • 二值图像:只有0、1 (黑、白)

  • 灰度图像:像素取值是 0-255 ,有中间过度。

    Read More

Docker 入门

1. Docker 简介

直接运行于操作系统内核上的虚拟化解决方案,他是一个操作系统级别的虚拟化也就是说容器只能运行在相同或者相似的内和操作系统之上的。所以我们只能在 docker 中运行 Linux 系统而不能运行 Windows 系统。他是依赖于 Linux 的内核特性:Namespace 和 Cgroup (Control Group)。

Read More

SpringMVC 踩坑记录

1. 处理静态资源

静态资源直接放在 webapp/web 下,而我们的模板一般是在 WEB-INF 下,但是 WEN-INF 下的东西一般不让访问的,模板之所以能访问到是因为有模板引擎的映射,但是我们的警惕资源应该是直接能访问的东西,直接放在 web 下类似于 jsp 直接访问,而不能放在 WEB-INF 下,并且我们要开启静态资源访问 <mvc:default-servlet-handler/>

2. Thymeleaf 乱码问题

一开始除了乱码想着直接在 web.xml 中配置编码过滤器,接着发现根本没用还是一样的乱码,并且返回的是 ISO-8859-1的西欧字符集。后来想着如果是不是 SpringMVC 的问题,那么可能就是因为视图是被 Thymeleaf 渲染的导致的问题,然后找到 Thymeleaf 在 SpringMVC 中的配置,然后更改编码。重新启动项目才行。

SpringMVC 整合

今天一开始直接用了 Idea 来创建一个 SpringMVC+Spring+Mybatis+Thymeleaf 的项目,一开始还是挺顺利的,除了在 Thymeleaf 那个地方卡了一下,后面项目还是顺利跑起来了。

接着想用 Maven 搭建,因为一开始用 Idea 生成的项目使用的手动导入 jar 这样非常费力,为了一劳永逸和简单就采用了 maven 来构建项目。最后发现自己陷入了一个大坑,好久没有跳上来。

接着我就把用 Maven 搭建 SpringMVC + Spring + Mybatis + Thymeleaf 项目过程写下来,免得日后再采坑,以后项目直接拷贝就可以了不用再配置了!

1. 创建 Maven 项目

lo8Gl.md.png

2.添加 web 模块

loSLJ.png

在这里面勾选上 web 模块,接着我们会看到我们的项目中多了一个 web 目录也就是最重要的 WEB-INF 等等。

3. web 模块移动

CMJk0U.png

我们把 web 模块移动到这里,方便观看。以及在 maven 项目之内,接着我们需要改一下项目配置,因为我们移动了这个模块。如果移动的时候更新了依赖就无需这步操作。

CMJZtJ.md.png

4. 配置文件

CMJeh9.png

创建配置文件如上。

  1. spring 的配置文件 applicationContext.xml
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!--配置包扫描-->
<context:component-scan base-package="com.lwen"/>
<!--配置静态资源访问-->
<mvc:default-servlet-handler/>
<!--配置注解-->
<mvc:annotation-driven/>
<!--配置 thymeleaf 视图解析器 类似于 jsp 解析器-->
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver">
<property name="prefix" value="/WEB-INF/templates/" />
<property name="suffix" value=".html" />
<property name="templateMode" value="HTML5" />
</bean>
<bean id="templateEngine" class="org.thymeleaf.spring4.SpringTemplateEngine">
<property name="templateResolver" ref="templateResolver" />
</bean>
<bean class="org.thymeleaf.spring4.view.ThymeleafViewResolver">
<property name="templateEngine" ref="templateEngine" />
</bean>

<!--配置外部配置文件-->
<context:property-placeholder location="classpath:properties.properties"/>
<!--配置druid数据库连接池-->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<!-- 数据库基本信息配置 -->
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
<property name="driverClassName" value="${driverClassName}" />
<!-- 最大并发连接数 -->
<property name="maxActive" value="${maxActive}" />
<!-- 初始化连接数量 -->
<property name="initialSize" value="${initialSize}" />
</bean>
<!--配置 spring 事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--配置注解事务-->
<tx:annotation-driven/>
<!--配置手动路由-->
<mvc:view-controller path="/" view-name="index"/>


</beans>
  1. springMVC 配置文件 dispatcher-servlet.xml
1
2
3
4
5
6
7
8
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">



</beans>
  1. 数据库配置文件 properties.properties

    1
    2
    3
    4
    5
    6
    url=jdbc:mysql://localhost:3306/dbspring
    username=root
    password=12345678
    driverClassName=com.mysql.jdbc.Driver
    maxActive=500
    initialSize=5

5. pom.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>com.lwen</groupId>
<artifactId>springmvc</artifactId>
<version>1.0-SNAPSHOT</version>

<profiles>
<profile>
<id>jdk‐1.8</id>
<activation>
<activeByDefault>true</activeByDefault>
<jdk>1.8</jdk>
</activation>
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.compilerVersion>1.8</maven.compiler.compilerVersion>
</properties>
</profile>
</profiles>

<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.1</version>
</dependency>

<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>

<dependency>
<groupId>org.thymeleaf</groupId>
<artifactId>thymeleaf-spring4</artifactId>
<version>3.0.9.RELEASE</version>
</dependency>

<dependency>
<groupId>aopalliance</groupId>
<artifactId>aopalliance</artifactId>
<version>1.0</version>
</dependency>

<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.9</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.3.14.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-expression</artifactId>
<version>4.3.13.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-instrument-tomcat</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jms</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-messaging</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc-portlet</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
<version>4.3.16.RELEASE</version>
</dependency>


</dependencies>


</project>

6. 配置 jar 依赖

CMJ10O.md.png

这步很重要,否则会一直报错,说找不到 class。

iTerm2 配置及使用技巧

1. 技巧:

1. 选中

双击选中,三击选中整行,选中即复制。即任何选中状态的字符串都被放到了系统剪切板中。

2. command

  • 可以拖拽选中的字符串;
  • 点击 url:调用默认浏览器访问该网址;
  • 点击文件:调用默认程序打开文件

3. 快捷键

  • 切换 tab:⌘+←, ⌘+→
  • 切换分屏:⌘+[/]
  • 新建 tab:⌘+t;
  • 切分屏幕:⌘+d 水平切分,⌘+Shift+d 垂直切分;
  • 智能查找,支持正则查找:⌘+f。
命令 说明
command + t 新建标签
command + w 关闭标签
command + 数字 command + 左右方向键 切换标签
command + enter 切换全屏
command + f 查找
command + d 垂直分屏
command + shift + d 水平分屏
command + option + 方向键 command + [ 或 command + ] 切换屏幕
command + ; 查看历史命令
command + shift + h 查看剪贴板历史
ctrl + u 清除当前行
ctrl + l 清屏
ctrl + a 到行首
ctrl + e 到行尾
ctrl + f/b 前进后退
ctrl + p 上一条命令
ctrl + r 搜索命令历史

4. 自动完成

  • cmd+; 自动完成
  • cmd+shift+h 历史记录

5. autojump

  • 使用 j 目录名,无需完整的即可。
  • 输入 d 显示之前跳过的目录

6. suggestions

1
git clone https://github.com/zsh-users/zsh-autosuggestions ~/.oh-my-zsh/custom/plugins/zsh-autosuggestions.git

在 .zshrc 中添加 zsh-autosuggestions 接着执行如下命令

1
2
➜  ~ source .zshrc
➜ ~ source .oh-my-zsh/custom/plugins/zsh-autosuggestions/zsh-autosuggestions.zsh

即可使用了!

SpringMVC 基础

1.SpringMVC 基础原理

  1. C 前端控制器 ——> DispatcherServlet
  2. M 数据对象
  3. V 视图处理器ViewResvor

<!—more–>

处理步骤:

  1. 发起请求到前端控制器 DispatcherServlet
  2. 然后这个控制器会调用 HandlerMapping 查找对应的 Controller或者说 Handler
  3. 找到了对应的 Controller 就让 HandlerAdaptor 去执行 handler
  4. 执行了 handler 以后返回的就是 ModelAndView 对象。
  5. 对象返回给前端控制器,然后前端控制器会丢给 ViewResovr 去解析
  6. 然后继续返回给前端控制器并返回给用户。

2.基础程序

1. 配置基础的 web.xml 加载 SpringMVC

在 web.xml 中我们需要配一个 Servlet 和一个 Listener ,这个 Servlet 其实就是我们的路由调度器,然后 Listener 则是上下文监听器。还有一个初始化参数就是指定 spring 的配置文件的位置。

其实这些配置基本都是固定的,在使用 idea 建立 SpringMVC 项目的时候他会自动的帮我们配置好,但是我们还是需要在进行一些配置。主要的配置如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--配置 spring 的配置文件-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<!--1. / 所有的都进行解析,但是不管 jsp
2. *.from
3. /* 不能用 所有的都拦截
-->
<url-pattern>/</url-pattern>
</servlet-mapping>


<filter>
<filter-name>HttpHiddenMethods</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpHiddenMethods</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

</web-app>

2. 配置基础的 spring 配置文件

接着就是配置 spring 配置文件,可以看到在上面的 web.xml 中我们在初始化参数中指定了 spring 的配置文件就是 applicationContext.xml 放在了 WEB-INF 路径下面。

然后需要配置一些核心的 bean 让 spring 进行自动加载,以需要配置扫描的包。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!--&lt;!&ndash;处理器映射器&ndash;&gt;-->
<!--<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>-->
<!--&lt;!&ndash;处理器适配器&ndash;&gt;-->
<!--<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>-->

<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB_INF/templates/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置需要扫描的包-->
<context:component-scan base-package="com.mvc"/>
<!--必须要的,配置了 springMVC 的注解生效,不然的话我们的 url 映射还是通过配置文件的方式生效的
不然一直处于 404 状态,这也就是下面的 dispatcher-servlet.xml 的作用配置 url 映射
-->
<mvc:annotation-driven />
</beans>

3. 控制器

1
2
3
4
5
6
7
8
@Controller
public class Test {

@RequestMapping("/helloTest")
public String hello(){
return "hello";
}
}

这样我们的程序就能够跑起来了。

3. RESTful 风格的请求

一般的我们无法直接使用 RESTful 风格的请求,但是在 SpringMVC 中有一个过滤器可以帮我们把一个 post 请求转化成为 PUT 或者 DELETE 请求。具体的做法如下:

1. 配置过滤器(HiddenHttpMethodFilter)

1
2
3
4
5
6
7
8
<filter>
<filter-name>HttpHiddenMethods</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>HttpHiddenMethods</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

2. 在 post 的表单域中封装一个 hidden 的 input 标签,用来说明提交方式

1
2
3
4
5
6
<form method="post">
<input type="hidden" name="_method" value="PUT">
</form>
<form method="post">
<input type="hidden" name="_method" value="DELETE">
</form>

4. @RequestMapping 及参数传递

1. @RequestMapping 注解

这个注解主要就是用来做方法和类的 url 映射的,也就是相当于一个路由映射文件。这个注解可以使用在类上面也可以使用在方法上面,在类上面的话就是我们访问每一个方法的时候都需要加上类的 url 前缀。

他有几个比较重要的属性,用来管理 url 的:

  1. value: 这个是默认的,是 url 地址

  2. method:这个是用来指定请求方式的 RequestMethod.GET/POST/DELETE/PUT ….

  3. params:这个是用来规定我们的 url 中携带的参数的他是一个数组,可以放多个值

    1
    params = {"username","age!=10"}  params 是包含 username age!=10   headers 也是如此只是规定了请求头而已

2. @PathVariable 注解

这个注解的作用是用来传递参数的,我们不仅仅可使用 来传递参数,还可以使用更优雅的 /paramName/value 的方式来传递参数,并且能够直接在方法中绑定这些参数。

1
2
3
4
5
@RequestMapping(value = "/helloworld/{id}") 
public String hello(@PathVariable("id") String id){
System.out.println("hello controller");
return "hello";
}

这里的 id 必须和 url 中的保持一致,但是无需和方法的参数保持一致。

3. @RequestParam 注解

但是有一个问题,当我们传递过来的参数是通过 的形式传递过来的,那么我们又该怎么去获取他呢?是的这里我们可以使用 @RequestParam 方法来获取这些值,当我们有些参数不是必须传递的我们就可以使用 require=false 来规定不用必传,这个时候如果我们没有传值,这个参数刚好是一个引用类型的就会是 null 但是如果是一个基本数据类型,就会报错。我们必须手动的设置一个默认值。

1
@RequestParam(value = "age",required = false,defaultValue = "0") Integer age

上面的参数就可以获取到 http://localhost/hello?age=10 这种的 url 的参数了、

4. @RequestHeader 注解

用法同上!

1
@RequestHeader("Content-Type") String content

5. @CookieValue 注解

这个使用同上,获取 Cookie 的值。

6. POJO 参数(Java 类)

我们的表单提交过来的数据会自动的被封装到 POJO 对象中,我们只需要配置好表单的 name 值和 Bean 的属性值一致即可,如果说里面有级联的属性,我们就使用 proA.proB 来封装。例如:

1
2
3
4
5
6
<form method="post">
<input type="text" name="name"/>
<input type="text" name="age"/>
<input type="text" name="address.code">
<input type="text" name="address.name">
</form>

这个表单就会被封装成一个 POJO 对象,这个对象里面有另外一个类的引用就导致了级联属性的出现,我们是就是使用了点的方式完成的封装。

7.原生的 Servlet API 参数

它支持比较多的原生的 Servlet 的 API ,其实是在他内部调用了 request 对象的一些方法获取到的。

  1. HttpServletRequest
  2. HTTPServletResponse
  3. HttpSession
  4. Locale
  5. InputStream
  6. OutputStream
  7. Reader
  8. Writer
  9. Principal

5. 控制器与视图间数据交互

一般我们需要在控制器里面绑定一些数据到视图中,然后我们可以在视图里面采用标签来获取 Controller 里面的数据从而展示这些数据,在 SpringMVC 中有几种方法可以达到这个目的。

1. ModelAndView

这个东西其实就像他的名字一样,他是 Model 数据和模型的结合体,我们可以往里面添加数据(Model),也可以把要转发的页面放在里面让视图解析器去渲染。所以说这个对象里面有一个 Model 属性,这个属性就是用来存放数据的,其实就是一个 Map 。Map 里面的这些数据都会被遍历然后放到 request 域对象之中,我们只需要在请求域中获取就好。

1
2
3
4
5
6
7
8
9
10
/**
* ModelAndView 来用作 Controller 与 View 之间的数据交互的介质
* 也就是 Model 的载体
* @return
*/
public ModelAndView modelAndViewTest(){
ModelAndView modelAndView = new ModelAndView("hello");
modelAndView.addObject("time", new Date());
return modelAndView;
}

2. Model/Map/ModelMap

其实三个东西类型都是 Map 类型的,然后SpringMVC 在真正的传入的对象显然就是他们的实现类,这里我们不过分纠结,基层肯定是一个 Map 。Map 里面的这些数据都会被遍历然后放到 request 域对象之中,我们只需要在请求域中获取就好。

这个用起来也比较简单,就是在方法的入参里面传入这个一个东西就行了,而不是采用的返回值。Map 的具体的泛型就是 string 和 object。看下面的例子。

1
2
3
4
public String modelMap(Map<String,Object> map){
map.put("time", new Date());
return "hello";
}

3. @SessionAttributes 注解

这个注解只能放在类上面,然后我们使用 value 属性或者 types 属性来规定哪些属性需要被放在 Session 域中,这个两个属性其实都是一个数组,所以我们可以方多个值。

value 这个属性,就是当我们在放入 map 中的一个键名的时候我们就可以把它放到 session 域中。而 types 属性则是当我们放一个 class 的时候他会自动抓取处于 map 中的同类型的数据。

1
@SessionAttributes(value = {"time","username"},types = {String.class,Integer.class})

4. @ModelAttribute 注解

这个注解是标识在方法上的,这个注解标识的方法会在所有的方法调用前被调用。在这个被标识的方法里面我们需要从数据库中获取对应的对象,然后把这个对象放到 map 里面,但是注意 map 中的键必须要是我们的 Model 类对应的小写的一个字符串才能起作用。当我们使用其他的方法来进行某个 model 的修改动作的时候我们某个字段不传的话这时候在 map 中的那个对象的对应字段挥起一个补充作用,把对应字段填上。

执行流程:

  1. 首先是执行了被这个注解标识的方法,将数据库中获取到的值放到了 map 里面,然后这个 map 是被放到了一个implicitModel 里面
  2. 然后在我们提交一个表单的时候,我们对应的方法的参数会到 implicitModel 里面查找对应的对象,查找的 key 就是先看看我们的这个方法的参数是否被 ``@ModelAttribute(value=”…”)` 修饰。如果是的话我们采用的是直接使用它的 value 属性作为 key 去查找。
  3. 如果没有这个注解修饰参数,则采用这个 POJO 的类名第一个字母小写作为 key 查找。
  4. 如果没有则看看是否这个类被 @SessionAttributes 注解 注释,如果是则是去 session 中查找,如果没有找到抛异常。
  5. 如果上面的情况都没找到,则是使用 POJO 反射创建一个新的对象把表单数据封装进去,而如果上面有找到的话我们就使用那个 Model 然后设置对应的属性值。
  6. 接着把这个修改后的 model 放到 implicitModel 进而放到 request 域中。

6.视图

视图的解析步骤:

  1. 首先我们是访问了我们的控制器。
  2. 然后我们的控制器会返回 string 或者 VIewAndModel 对象。
  3. 他们都会被视图解析器(我们在 spring 中配置的 bean)包装成一个 ModelAndView 对象。
  4. 最后渲染视图,并转发到对应的视图。

1. 手动路由配置

可以手动配置路由,不经过 controller 就可以访问到对应的视图。在 spring 配置文件里写上如下内容:

1
2
3
<mvc:annotation-driven />
<!--手动配置路由 直接路由到视图无需经过 controller-->
<mvc:view-controller path="/success" view-name="hello"/>

那么我们访问 http://localhost:8080/success 就被转发到 WEB_INF/templates/hello.jsp 具体的目录取决于我们配置的视图解析器的前缀和后缀。

2. 自定义视图解析器

我们一般采用的就是 InternalResourceViewResolver 这个视图解析器,我们也可以自定义视图。自定义视图则需要一个特殊的视图解析器完成解析视图的工作,就是 BeanNameViewResolver 就是通过视图的 bean 的 name 来获取视图的。由于我们配置了多个视图解析器则需要定义一个优先级,哪个视图解析器先工作,使用 order 属性。

1
2
3
4
<!--自定义的 Bean视图解析器   直接通过 bean 的 name 获取视图-->
<bean class="org.springframework.web.servlet.view.BeanNameViewResolver">
<property name="order" value="100"/>
</bean>

下面是我们使用 bean 定义的一个视图。

1
2
3
4
5
6
7
8
9
10
11
12
@Component
public class MyViewRevsor implements View {
@Override
public String getContentType() {
return "text/html";
}

@Override
public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
response.getWriter().write("hello");
}
}

3. 转发和重定向

我们一般在 controller 中写的东西默认都会转发的,我们需要重定向的话我们只需要在人绘制前面加上 redirect 就可以。

1
2
3
4
@RequestMapping("/beanView")
public String beanView(){
return "redirect:/myViewRevsor";
}

4. 静态资源映射

对于静态资源我们需要直接获取而不需要进行映射,所以说我们在获取静态资源的时候回出现 404 ,我们就配置一个

<mvc:default-servlet-handler/> 这个就会自动的处理没有映射的 url 。

5. 表单数据到 controller 的映射

表单数据在提交以后实际上是依赖于 SpringMVC 里面一个 WebDataBinder 类进行的数据绑定,这个 WebDataBinder 里面有很多其他的对象的引用其中就有数据格式化、数据校验、数据转换的对象,也就是说在这个数据转换的过程我们是可以添加一些对象来手动的控制数据的绑定的。

  1. converter 这个是用来数据转换的,具体的参照文档,就比如我们把前端的一个字符串转成方法入参的一个 bean 对象,就经过这个 converter 来完成。
  2. @initBinder 被这个注解表示的方法,会在数据绑定之前进行运行,其功能就是对 binder 进行一些设置比如忽略一些字段。修改字段,拒绝字段等等。
1
2
3
4
5
@InitBinder
public void initBinder(WebDataBinder binder) {
// 拒绝 name 字段
binder.setDisallowedFields("name");
}
  1. 数据的格式化,采用注解注解在对应的 bean 的字段上。常用的有时间还有数子。
  2. JSR303 校验规范,这只是 JavaEE 的规范,真正的实现类是 Hibernate ValidData ,然后我们进行数据校验也是使用注解的方式,具体的注解在规范里面都有,都比较简单。并且我们需要在方法的入参的 bean 上加上 @Valid 注解。
  3. 错误消息的回显,显然如果我们的校验生效并且有错误的话我们需要回显到表单。我们就需要在方法的参数里面加上一个 BindResult 对象,然后错误的数据都会被放到这个东西里面,同时 BindResult 是一个 Error 类型的对象,所以我们亦可以放这个对象。最后放到 map 里面在前端回显即可。

6. 返回 JSON 数据

只需要在方法上加上 @ResponseBody 就可以,返回值是一个 List 或数组。

7. 拦截器(Interceptor)

1
2
3
4
5
6
7
8
9
10
11
<!--配置拦截器-->
<mvc:interceptors>
<!--自定义的拦截器组件-->
<bean class="com.mvc.MyInterceptor"/>
<!--更详细的配置可以针对 url-->
<mvc:interceptor>
<mvc:mapping path="/hello"/>
<mvc:exclude-mapping path="/hah"/>
<bean class="com.mvc.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
return false;
}

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

}

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

}
}

8. 统一异常处理

统一异常处理就采用对应的 handler 来处理异常,使用 @ExceptionHandler 注解,然后标注要处理的异常类型,但是如果说我们需要把异常带到错误页面我们不能使用 map 而只能使用 ModelAndView 不然那就会报错。

1
2
3
4
5
6
@ExceptionHandler({ArithmeticException.class})
public ModelAndView error(Exception ex){
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("ex", ex);
return modelAndView;
}

如果他在当前的 controller 中找不到对应的 ExceptionHandler 就去查找对应的 @ControllerAdvise 注解表示的类,中的ExceptionHandler 注解方法。也就是默认的处理器。

NioEventLoop 源码分析

NioEventLoop 源码分析

1. SingleThreadEventExecutor 的 execute 方法

NioEventLoop 的核心就在于它的 run() 他是在第一次添加任务的时候开始执行。那我们先看看第一次添加任务的地方,其实第一次添加任务的地方是在父类中的 execute() 方法。所以先去分析一下 SingleThreadEventExecutorexecute() 方法。我把代码精简了贴出来,只看核心的部分。

1
2
3
4
5
6
7
8
9
10
11
12
public void execute(Runnable task) {
boolean inEventLoop = inEventLoop();
// 线程已经启动 直接加入任务
if (inEventLoop) {
addTask(task);
// 否则先启动任务 再添加任务
} else {
startThread();
addTask(task);
}

}

   很明显,也就是我们往线程池中添加任务的时候,首先要看看我们的线程是不是已经启动了,没有的话首先我们需要启动一下线程。接下来要看看 startThread() 方法干了什么事。里面做了一些检查也就是线程只能被 start 一次,然后直接调用了 NioEventLoop 封装的 threadstart() 很简单!

   但是,等等这个线程是从哪来的?我们并没有显示的传入,来到 SingleThreadEventExecutor 构造方法,我们会发现他在构造器中进行了初始化,但是不是直接 new Thread 而是使用了我们传的线程工厂,然后在工程里面 new 了这个线程需要执行的任务。

   他的任务就是先执行一下 run 方法,然而他的 run 方法是抽象的,自然就调用到子类去了,这也就解释了为什么说是第一次添加任务的时候调用了 NioEventLooprun 方法。

   贴一下对 thread 初始化的代码(精简过后的):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// new 了一个新的线程
thread = threadFactory.newThread(new Runnable() {
@Override
public void run() {
boolean success = false;
updateLastExecutionTime();
try {
// 调用了 run 方法,这个 run 方法在这个类中是抽象的,显然在子类中实现了
SingleThreadEventExecutor.this.run();
success = true;
} catch (Throwable t) {
logger.warn("Unexpected exception from an event executor: ", t);
} finally {
// 让线程关闭
}
});

2. 再回到 NioEventLoop 的 run 方法

   那好,我们在上面已经看到了我们在创建一个 NioEventLoop 的时候会创建一个线程,这个线程的任务就是去调用子类的 run 方法。当我们执行 execute( task ) 方法,添加一个新任务去运行的时候,就会判断当前线程是不是启动了,否则启动我们一开始创建的那个线程。用一张图说明一下!!!
kUQnB.md.png

   好的现在正式的看一下 run 方法,还是贴一下核心代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
protected void run() {
for (;;) {

// 有任务在线程创建之后直接开始 select
if (hasTasks()) {
selectNow(); //直接调用了 select 的 selectNow 然后再次唤醒同下面的代码
// 没有任务
} else {
// 自旋进行等待可进行 select 操作
select(oldWakenUp);
// 再次唤醒,解决并发问题
if (wakenUp.get()) {
selector.wakeup();
}
}


// 都是处理 selected 的通道的数据,并执行所有的任务,只是在 runAllTasks 传的参数不同
if (ioRatio == 100) {
processSelectedKeys();
runAllTasks();
} else {
final long ioStartTime = System.nanoTime();
processSelectedKeys();
final long ioTime = System.nanoTime() - ioStartTime;
runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
}


}
}

   可以看到在代码里面的死循环中值做了三件事:select、processSelectedKeys、 runAllTasks .借一张图来看:

kUjcp.md.png

1. 首先轮询注册到reactor线程对用的selector上的所有的channel的IO事件

2. 处理产生网络IO事件的channel

3. 处理任务队列

具体做的事情放到下面一一道来!

1. select()

   如果有任务的话直接去 selectNow() 也就是不进行等待的 select() ,而没有任务的时候就进行自旋等待的 select() 。下面是 select() 的核心代码,可以看待里面调用了 selectNow() 所以说这个就是一个自旋的 selectNow() 。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
/**
* 这个方法主要干的事情:
* 1、如果不需要等待就直接 select
* 2、需要等待则等一个超时时间再去 select
* 这个过程是不停进行的也就是死循环直达有任务可进行 select 时 select 完毕退出循环
* @param oldWakenUp
* @throws IOException
*/
private void select(boolean oldWakenUp) throws IOException {
for (;;) {
// 不用等待进行一次 select 操作
long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
if (timeoutMillis <= 0) {
if (selectCnt == 0) {
selector.selectNow();
selectCnt = 1;
}
break;
}
// 等一个超时再去选择
int selectedKeys = selector.select(timeoutMillis);
selectCnt ++;

if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks() || hasScheduledTasks()) {
// - Selected something,
// - waken up by user, or
// - the task queue has a pending task.
// - a scheduled task is ready for processing
break;
}


if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 &&
selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
// 解决死循环问题,重建 selector
rebuildSelector();
selector = this.selector;
// 直接是 selectNow()
selector.selectNow();

}
}

wakenUp 表示是否应该唤醒正在阻塞的 select 操作,netty在进行一次新的loop之前,都会将 wakeUp 被设置成false,标志新的一轮loop的开始。

2. processSelectedKeys()

3. runAllTasks()

SpringBoot 笔记 ( 四 ):web 开发

SpringBoot 笔记 (四): web 开发

1、SpringBoot对静态资源的映射规则

1
2
3
4
5
6
@ConfigurationProperties(prefix = "spring.resources", ignoreUnknownFields = false)
public class ResourceProperties implements ResourceLoaderAware {
//静态资源的路径
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/", "classpath:/resources/",
"classpath:/static/", "classpath:/public/" };

Read More

SpringBoot 笔记 ( 三 ):日志系统

SpringBoot 笔记 ( 三 ):日志系统

1、日志框架

日志框架就是防止我们再去像以前那样,一直进行System.out.println(“”)将关键数据打印在控制台。框架来记录系统的一些运行时信息,但是随着日志框架的增长,和接口的不一致,导致了使用上的差别很大,这里采用了一个类似于数据库驱动的模式,数据库驱动是 Java 提供的一个 API,然后真正的实现是需要各个数据库厂商去完成的,而 log 也开始采用这种面向接口编程的方法采用日志抽象层。

Read More