今天头一次配置成功一个SSM框架,兴奋之余,还应该从使用的角度,将整个ssm配置的方法,配置的原因,认真的分析一下。在结束了对SSM框架简单的分析之后,就要开始回归基础,全面的认真复习。所以这个文档,要尽量的完整,目标就是,这个文档是是对SSM框架的学习,也是一个将来复习的资料。
这个版本中的代码貌似无法复制,我将可以也可复制版也发布了,在我的博客中,但是没有很好的样式。
1 resources 配置文件
1.1 配置文件简介
这个部分用来解读在resources文件夹下的配置信息。这一部分是整个ssm框架中,最难的一部分,也是最复杂的一部分。在配置这个部分的时候,尽管是不停的肤质粘贴,偶尔改一改路径。但是这样的一个 过程要比写业务逻辑代码麻烦的多。
1.1.1 jdbc.properties
这个文件是用来保存和数据库连击的相关的信息,比如JDBC驱动器、数据库户名、数据库密码等等,和数据库相关的静态信息,都会以KV的形式,保存在这个文件中。这样在需要对数据库的信息进行改变时,只需要打开这个文件,就可以对数据库的配置进行改变。与之前传统数据库连接方式相比,在修改时,不用改变多处的信息。
1.1.2 spring-mvc.xml
和名字显示的显示的一样,这是一个对MVC配置的一个文件。简单来讲有下面的几点功能:
1
设定用来存放controller的包的位置。
2
以文件前缀、后缀的方式,设定存放JSP文件的路径。
我对这个文件的理解为,从controller获取执行哪一个文件的信息,在将这个信息,通过前缀和后缀的包装,传递给下一级。
1.1.3 spring-mybatis.xml
这个配置也常常叫做:applicationContext.xml,但是其实是一个东西,都是用来配置mybatis。
1
将标注Spring注解的类自动转化成Bean,并且完成Bean的注入。
2
配置数据库相关的资源。
3
配置数据库的sessionFactory
4
配置需要被扫描的用来存放DAO接口的接口包。
5
配置事务管理器。
1.1.4 UserDAO.xml
mapper.xml稍微好理解一些。就是使用DAO接口的全限定地址,和DAO中的操作数据库的sql方法,封装起来,那么调用DAO接口的某个方法的时候,就可以使用mapper.xml中封装的sql语句操作数据库。
通常在这个文件也叫做mapper.xml文件.
1.1.5 web.xml
53
1
<?xml version="1.0" encoding="UTF-8"?>
2
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
5
version="3.1">
6
?
7
<display-name>SSM</display-name>
8
<description>SSM_test</description>
9
?
10
<!-- 编码过滤器 -->
11
<filter>
12
<filter-name>encodingFilter</filter-name>
13
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
14
<init-param>
15
<param-name>encoding</param-name>
16
<param-value>UTF-8</param-value>
17
</init-param>
18
</filter>
19
?
20
<filter-mapping>
21
<filter-name>encodingFilter</filter-name>
22
<url-pattern>/*</url-pattern>
23
</filter-mapping>
24
?
25
<!-- 配置前端控制器Spring MVC DispatcherServlet -->
26
<servlet>
27
<servlet-name>SpringMVC</servlet-name>
28
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
29
<!-- 配置两个文件 -->
30
<init-param>
31
<param-name>contextConfigLocation</param-name>
32
<param-value>classpath:spring-*.xml</param-value>
33
</init-param>
34
?
35
<load-on-startup>1</load-on-startup>
36
<async-supported>true</async-supported>
37
</servlet>
38
?
39
?
40
<!--将所有的请求使用上面配置的 DispatcherServlet 分发器分发-->
41
<servlet-mapping>
42
<servlet-name>SpringMVC</servlet-name>
43
<!-- 匹配所有请求,此处也可以配置成 *.do 形式 -->
44
<url-pattern>/</url-pattern>
45
</servlet-mapping>
46
?
47
?
48
<welcome-file-list>
49
<welcome-file>index.html</welcome-file>
50
</welcome-file-list>
51
?
52
</web-app>
53
?
1.1.6 其他
Logback是一个关于log日志的配置文件,并没有使用sql文件夹。还有就是pom.xml文件,看起来很多,其实也就是添加了几个依赖罢了。
175
1
<?xml version="1.0" encoding="UTF-8"?>
2
?
3
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
5
<modelVersion>4.0.0</modelVersion>
6
?
7
<groupId>com.xzj</groupId>
8
<artifactId>ssmSecondTry</artifactId>
9
<version>1.0-SNAPSHOT</version>
10
<packaging>war</packaging>
11
?
12
<name>ssmSecondTry Maven Webapp</name>
13
<!-- FIXME change it to the project‘s website -->
14
<url>http://www.example.com</url>
15
?
16
<properties>
17
<!-- 设置项目编码编码 -->
18
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
19
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
20
<!-- spring版本号 -->
21
<spring.version>4.3.5.RELEASE</spring.version>
22
<!-- mybatis版本号 -->
23
<mybatis.version>3.4.1</mybatis.version>
24
</properties>
25
?
26
<dependencies>
27
?
28
<!-- java ee -->
29
<dependency>
30
<groupId>javax</groupId>
31
<artifactId>javaee-api</artifactId>
32
<version>7.0</version>
33
</dependency>
34
?
35
<!-- 单元测试 -->
36
<dependency>
37
<groupId>junit</groupId>
38
<artifactId>junit</artifactId>
39
<version>4.12</version>
40
</dependency>
41
?
42
<!-- 实现slf4j接口并整合 -->
43
<dependency>
44
<groupId>ch.qos.logback</groupId>
45
<artifactId>logback-classic</artifactId>
46
<version>1.2.2</version>
47
</dependency>
48
?
49
<!-- JSON -->
50
<dependency>
51
<groupId>com.fasterxml.jackson.core</groupId>
52
<artifactId>jackson-databind</artifactId>
53
<version>2.8.7</version>
54
</dependency>
55
?
56
?
57
<!-- 数据库 -->
58
<dependency>
59
<groupId>mysql</groupId>
60
<artifactId>mysql-connector-java</artifactId>
61
<version>5.1.41</version>
62
<scope>runtime</scope>
63
</dependency>
64
?
65
<!-- 数据库连接池 -->
66
<dependency>
67
<groupId>com.mchange</groupId>
68
<artifactId>c3p0</artifactId>
69
<version>0.9.5.2</version>
70
</dependency>
71
?
72
<!-- MyBatis -->
73
<dependency>
74
<groupId>org.mybatis</groupId>
75
<artifactId>mybatis</artifactId>
76
<version>${mybatis.version}</version>
77
</dependency>
78
?
79
<!-- mybatis/spring整合包 -->
80
<dependency>
81
<groupId>org.mybatis</groupId>
82
<artifactId>mybatis-spring</artifactId>
83
<version>1.3.1</version>
84
</dependency>
85
?
86
<!-- Spring -->
87
<dependency>
88
<groupId>org.springframework</groupId>
89
<artifactId>spring-core</artifactId>
90
<version>${spring.version}</version>
91
</dependency>
92
<dependency>
93
<groupId>org.springframework</groupId>
94
<artifactId>spring-beans</artifactId>
95
<version>${spring.version}</version>
96
</dependency>
97
<dependency>
98
<groupId>org.springframework</groupId>
99
<artifactId>spring-context</artifactId>
100
<version>${spring.version}</version>
101
</dependency>
102
<dependency>
103
<groupId>org.springframework</groupId>
104
<artifactId>spring-jdbc</artifactId>
105
<version>${spring.version}</version>
106
</dependency>
107
<dependency>
108
<groupId>org.springframework</groupId>
109
<artifactId>spring-tx</artifactId>
110
<version>${spring.version}</version>
111
</dependency>
112
<dependency>
113
<groupId>org.springframework</groupId>
114
<artifactId>spring-web</artifactId>
115
<version>${spring.version}</version>
116
</dependency>
117
<dependency>
118
<groupId>org.springframework</groupId>
119
<artifactId>spring-webmvc</artifactId>
120
<version>${spring.version}</version>
121
</dependency>
122
<dependency>
123
<groupId>org.springframework</groupId>
124
<artifactId>spring-test</artifactId>
125
<version>${spring.version}</version>
126
</dependency>
127
?
128
</dependencies>
129
<build>
130
<finalName>ssmSecondTry</finalName>
131
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
132
<plugins>
133
<plugin>
134
<groupId>org.apache.maven.plugins</groupId>
135
<artifactId>maven-compiler-plugin</artifactId>
136
<configuration>
137
<!-- 设置JDK版本 -->
138
<source>1.8</source>
139
<target>1.8</target>
140
</configuration>
141
</plugin>
142
<plugin>
143
<artifactId>maven-clean-plugin</artifactId>
144
<version>3.1.0</version>
145
</plugin>
146
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
147
<plugin>
148
<artifactId>maven-resources-plugin</artifactId>
149
<version>3.0.2</version>
150
</plugin>
151
<plugin>
152
<artifactId>maven-compiler-plugin</artifactId>
153
<version>3.8.0</version>
154
</plugin>
155
<plugin>
156
<artifactId>maven-surefire-plugin</artifactId>
157
<version>2.22.1</version>
158
</plugin>
159
<plugin>
160
<artifactId>maven-war-plugin</artifactId>
161
<version>3.2.2</version>
162
</plugin>
163
<plugin>
164
<artifactId>maven-install-plugin</artifactId>
165
<version>2.5.2</version>
166
</plugin>
167
<plugin>
168
<artifactId>maven-deploy-plugin</artifactId>
169
<version>2.8.2</version>
170
</plugin>
171
</plugins>
172
</pluginManagement>
173
</build>
174
</project>
175
?
1.2 jdbc.properties
1.2.1 代码
11
1
driver = com.mysql.jdbc.Driver
2
url = jdbc:mysql://localhost:3306/mySSM
3
username = root
4
password = admin97
5
?
6
?
7
maxPoolSize = 30
8
minPoolSize = 10
9
autoCommitOnClose = false
10
checkoutTimeout = 10000
11
acquireRetryAttempts = 2
1.2.2 代码解析
前面四行是我们在操作JDBC的时候,常常操作的四项,分别是:驱动器、地址、用户名、密码。
第7行设定最大连接数为30。最大连接数是连接池能申请的最大连接数。如果数据连接请求超过此数,后面的数据连接请求将被加入到等待队列中,这会影响之后的数据库操作。
第8行设定最小连接数为10。最小连接数是连接池一直保持的数据连接。如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费掉。
第9行设定在关闭之后,不会自动执行commit。如果设置为true,那么在数据库的连接状态关闭的时候,会将没有commit的数据库操作,执行commit。
第10行设定连接超时时间。
第11行设定失败后尝试的次数。
这一个文件的设置十分明确。并且在它被读取的时候,也是以键值对的形式被读取的。
1.2 spring-mvc.xml
1.2.1 代码
28
1
<?xml version="1.0" encoding="UTF-8"?>
2
<beans xmlns="http://www.springframework.org/schema/beans"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xmlns:context="http://www.springframework.org/schema/context"
5
xmlns:mvc="http://www.springframework.org/schema/mvc"
6
xsi:schemaLocation="http://www.springframework.org/schema/beans
7
http://www.springframework.org/schema/beans/spring-beans.xsd
8
http://www.springframework.org/schema/context
9
http://www.springframework.org/schema/context/spring-context.xsd
10
http://www.springframework.org/schema/mvc
11
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
12
?
13
<!-- 扫描web相关的bean -->
14
<context:component-scan base-package="com.xzj.controller"/>
15
?
16
<!-- 开启SpringMVC注解模式 -->
17
<mvc:annotation-driven/>
18
?
19
<!-- 静态资源默认servlet配置 -->
20
<mvc:default-servlet-handler/>
21
?
22
<!-- 配置jsp 显示ViewResolver -->
23
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
24
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
25
<property name="prefix" value="/WEB-INF/views/"/>
26
<property name="suffix" value=".jsp"/>
27
</bean>
28
</beans>
1.2.2 代码解析
在这样的一个文件中,第1~11行都是环境。
第14行设置存放controller的目录,在这个目录下的类都会被扫描。
第17行将设定模式为注解模式,解析base-package目录下面带有@Controller注解的类。
第23~27行设定这个解析的前缀和后缀,前缀设定这个文件的目录,后缀设定这个文件的拓展名。
1.2.3 功能简析
SpringMVC是View层的一部分,来自于页面的信息由SpringMVC控制的Controller将请求发送到Model层中的Service中,然后再Model中处理业务逻辑。从Model返回的model,添加上前缀和后缀,传递到View层中。
所以,具体的细分的话,这是一个介于页面和Model层之间的模块:
1
将来自页面的请求,传递到合适的controller。
2
将从controller返回的一个jsp文件名的字符串,在这里包装之后,的到下一个jsp文件的全限定路径,然后显示出来。当然并不是所有的controller都有返回值,如果没有,那就是在当前的页面修改。
1.3 spring-mybatis.xml
1.3.1 代码
61
1
<?xml version="1.0" encoding="UTF-8"?>
2
<beans xmlns="http://www.springframework.org/schema/beans"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xmlns:context="http://www.springframework.org/schema/context"
5
xmlns:tx="http://www.springframework.org/schema/tx"
6
xsi:schemaLocation="http://www.springframework.org/schema/beans
7
http://www.springframework.org/schema/beans/spring-beans.xsd
8
http://www.springframework.org/schema/context
9
http://www.springframework.org/schema/context/spring-context.xsd
10
http://www.springframework.org/schema/tx
11
http://www.springframework.org/schema/tx/spring-tx.xsd">
12
?
13
<!-- 设定扫描路径 -->
14
<context:component-scan base-package="com.xzj.service"/>
15
?
16
<!-- 导入数据库配置文件 -->
17
<context:property-placeholder location="classpath:jdbc.properties"/>
18
?
19
<!-- 数据库连接池 -->
20
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
21
<property name="driverClass" value="${driver}"/>
22
<property name="jdbcUrl" value="${url}"/>
23
<property name="user" value="${username}"/>
24
<property name="password" value="${password}"/>
25
<property name="maxPoolSize" value="${maxPoolSize}"/>
26
<property name="minPoolSize" value="${minPoolSize}"/>
27
<property name="autoCommitOnClose" value="${autoCommitOnClose}"/>
28
<property name="checkoutTimeout" value="${checkoutTimeout}"/>
29
<property name="acquireRetryAttempts" value="${acquireRetryAttempts}"/>
30
</bean>
31
?
32
<!-- 配置SqlSessionFactory -->
33
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
34
<!-- 注入数据库连接池 -->
35
<property name="dataSource" ref="dataSource"/>
36
?
37
<!-- 扫描model包 使用别名 -->
38
<property name="typeAliasesPackage" value="com.xzj.model"/>
39
?
40
<!-- 扫描sql配置文件:mapper需要的xml文件 -->
41
<property name="mapperLocations" value="classpath:mapper/*.xml"/>
42
</bean>
43
?
44
<!-- 配置扫描Dao接口包,动态实现Dao接口,注入到spring容器中 -->
45
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
46
<!-- 注入sqlSessionFactory -->
47
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
48
?
49
<!-- 给出需要扫描Dao接口包 -->
50
<property name="basePackage" value="com.xzj.DAO"/>
51
</bean>
52
?
53
<!-- 配置事务管理器 -->
54
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
55
<!-- 注入数据库连接池 -->
56
<property name="dataSource" ref="dataSource"/>
57
</bean>
58
?
59
<!-- 配置基于注解的声明式事务 -->
60
<tx:annotation-driven transaction-manager="transactionManager"/>
61
</beans>
1.3.2 代码解析
第14行:加上这一行以后,将自动扫描路径下面的包,如果一个类带了@Service注解,将自动注册到Spring容器,不需要在spring-mybatis.xml文件定义bean了。类似的注解还有Component、Repository、Controller。
例如:有下面一个类位于com.xzj.service或其子目录中:
5
1
@Service("xxx")
2
@Scope("yyy")
3
public class test extends A implements B{
4
······
5
}
那么就相当于在spring-mybatis.xml添加了一段这样的。
3
1
<bean id="xxx" class="com.xzj.service.test" scope="yyy">
2
......
3
</bean>
简而言之,这是一个将带有注解的类添加到Spring容器的语句。
第20~30行,创建数据库连接池,读取jdbc.properties文件中的有关于数据库的设置,按照这样的设置,设定数据库连接池的属性。
1.3.3 功能简析
纵观整个项目,就是这一块是最复杂的,在上面的代码中:
第50行和第38行的bean将DAO接口和mapper连接起来。这样的配置,使得在使用DAO接口的时候,直接调用接口方法,就可以直接调用DAO.xml中的sql语句。
另外的一点就是注册了service的目录,将service的内容注册到了Spring的容器中。也就是说,这个这个文件配置了两个方面的内容:一个是以service代表的业务逻辑,一个是以DAO及其映射mapper代表的数据访问,这两者共同组成了Model层。
1.4 web.xml
1.4.1 web.xml的代码
50
1
<?xml version="1.0" encoding="UTF-8"?>
2
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
3
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
4
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
5
version="3.1">
6
?
7
<display-name>SSM</display-name>
8
<description>SSM_test</description>
9
?
10
?
11
<!-- 编码过滤器 -->
12
<filter>
13
<filter-name>encodingFilter</filter-name>
14
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
15
<init-param>
16
<param-name>encoding</param-name>
17
<param-value>UTF-8</param-value>
18
</init-param>
19
</filter>
20
<filter-mapping>
21
<filter-name>encodingFilter</filter-name>
22
<url-pattern>/*</url-pattern>
23
</filter-mapping>
24
?
25
?
26
<!-- 配置前端控制器Spring MVC DispatcherServlet -->
27
<servlet>
28
<servlet-name>SpringMVC</servlet-name>
29
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
30
<!-- 配置两个文件 -->
31
<init-param>
32
<param-name>contextConfigLocation</param-name>
33
<param-value>classpath:spring-*.xml</param-value>
34
</init-param>
35
?
36
<load-on-startup>1</load-on-startup>
37
<async-supported>true</async-supported>
38
</servlet>
39
<servlet-mapping>
40
<servlet-name>SpringMVC</servlet-name>
41
<!-- 匹配所有请求,此处也可以配置成 *.do 形式 -->
42
<url-pattern>/</url-pattern>
43
</servlet-mapping>
44
?
45
<welcome-file-list>
46
<welcome-file>index.html</welcome-file>
47
</welcome-file-list>
48
?
49
</web-app>
50
?
1.4.2 代码解析
配置编码过滤器。
配置分发器,并且这里的分发器中,注入了两个xml文件。
配置welcome页面。
2 MVC三层结构的简析
2.1 MVC的构成
2.1.1 M层
M层表示Model层,模型层。用来实现业务逻辑、对数据进行访问。
这一层大概分为三个部分,Model、Service、DAO这样的三块,以我创建的这个工程来讲三个部分分别是:
1
User类,这个类中有三个属性,分别有他们的get、set方法。
2
Service接口及其实现类。在spring-mybatis.xml中配置,从而被扫描。
3
UserDAO接口,在spring-mybatis.xml中配置,与UserDAO.xml配对,被动态实现。
即:这层的三个模块都是在spring-mybatis.xml中被配置的。
2.1.2 V层
V层表示View层,显示层。是用来和用户交互的可视化界面,如html和jsp文件在浏览器中的显示。同样,在这些显示页面中,可以设定不同的请求,根据不同的前端动作,提交给分发器。那么这样的到的响应也不同。
2.1.3 C层
C层表示Controller控制器。C层大致上有两种功能:
1
根据不同的请求,调用M层中不同业务逻辑处理。
2
得到了来自M层处理结果的返回,给予V层的显示结果也不相同。
这一块是在spring-mvc.xml配置的。
2.2 M层的具体构成
2.2.1 简述
M层是Model层,负责业务逻辑和数据访问。以这个项目为例子,除了controller 以外的Java类和接口都是属于Model层的的,除此之外还有UserDAO.xml也是属于Model层的。
2.2.2 User类
User的代码如下:
53
1
package com.xzj.model;
2
?
3
// @ author :zjxu time:2018/12/31
4
public class User {
5
private int userID;
6
private String userName;
7
private String userPassword;
8
private String comments;
9
?
10
public int getUserID() {
11
return userID;
12
}
13
?
14
public String getUserName() {
15
return userName;
16
}
17
?
18
public String getUserPassword() {
19
return userPassword;
20
}
21
?
22
public String getComments() {
23
return comments;
24
}
25
?
26
?
27
public void setUserID(int userID) {
28
this.userID = userID;
29
}
30
?
31
public void setUserName(String userName) {
32
this.userName = userName;
33
}
34
?
35
public void setUserPassword(String userPassword) {
36
this.userPassword = userPassword;
37
}
38
?
39
public void setComments(String comments) {
40
this.comments = comments;
41
}
42
?
43
@Override
44
public String toString() {
45
return "User{" +
46
"userID=" + userID +
47
", userName=‘" + userName + ‘\‘‘ +
48
", userPassword=‘" + userPassword + ‘\‘‘ +
49
", comments=‘" + comments + ‘\‘‘ +
50
‘}‘;
51
}
52
}
53
?
言简意赅,这一段代码就是定义一个User类,并且定义了几个域。id、name、password、comments。设定了它们的getter和setter。
2.2.3 UserService接口
8
1
package com.xzj.service;
2
import com.xzj.model.User;
3
?
4
// @ author :zjxu time:2018/12/31
5
?
6
public interface UserService {
7
public User selectUser(long userID);
8
}
申明一个接口,这个接口是Model层中的service的接口。它的实现类,必须实现在这个类中定义的方法。其实这个接口并不是必须的,在spring-mybatis.xml中,配置的com.xzj.service目录中直接写UserServiceImple类,即可将这个类配置进spring中。但是有这个过接口可以将Model层中的service服务的结构更加清晰。所以在这个工程中,就使用了UserService接口。
2.2.4 UserServiceImple 类
21
1
package com.xzj.service.imple;
2
?
3
// @ author :zjxu time:2018/12/31
4
?
5
import com.xzj.DAO.UserDAO;
6
import com.xzj.model.User;
7
import com.xzj.service.UserService;
8
import org.springframework.stereotype.Service;
9
import javax.annotation.Resource;
10
?
11
@Service("userService")
12
public class UserServiceImple implements UserService {
13
?
14
@Resource
15
private UserDAO userDAO;
16
17
public User selectUser(long UserID) {
18
return this.userDAO.selectUser(UserID);
19
}
20
}
21
?
UserService的实现类,其中两个很重要的是两个注解11和14行的两个注解。 ?第11行的注解@Service("userService”),表示是为userService服务的,这个userService是controller的userService类。
2.2.5 UserDAO 接口
9
1
package com.xzj.DAO;
2
?
3
// @ author :zjxu time:2018/12/31
4
?
5
import com.xzj.model.User;
6
?
7
public interface UserDAO {
8
User selectUser(long id);
9
}
2.2.5 UserDAO.xml
10
1
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
2
?
3
<!-- 设置为UserDao接口方法提供sql语句配置 -->
4
<mapper namespace="com.xzj.DAO.UserDAO">
5
?
6
<select id="selectUser" resultType="User" parameterType="long">
7
SELECT * FROM userList WHERE userID = #{id}
8
</select>
9
?
10
</mapper>
2.3 C层的具体构成
2.3.1 简述
C层就是 controller,位于Model层和View层之间的一层。
2.3.2 Controller 类
50
1
package com.xzj.controller;
2
?
3
// @ author :zjxu time:2018/12/31
4
?
5
import com.fasterxml.jackson.databind.ObjectMapper;
6
import com.xzj.model.User;
7
import com.xzj.service.UserService;
8
import org.springframework.stereotype.Controller;
9
import org.springframework.web.bind.annotation.RequestMapping;
10
import org.springframework.web.servlet.ModelAndView;
11
?
12
import javax.annotation.Resource;
13
import javax.servlet.http.HttpServletRequest;
14
import javax.servlet.http.HttpServletResponse;
15
import java.io.IOException;
16
?
17
@Controller
18
public class UserController {
19
?
20
@Resource
21
private UserService userService;
22
?
23
@RequestMapping("/showUser.do")
24
public void selectUser(HttpServletRequest request,HttpServletResponse response,ModelAndView mav) throws IOException {
25
26
request.setCharacterEncoding("UTF-8");
27
response.setCharacterEncoding("UTF-8");
28
?
29
long userId = Long.parseLong(request.getParameter("id"));
30
User user = this.userService.selectUser(userId);
31
System.out.println("\n\n\n\n\n\n\n\n\n\n\n\n" + userId + "\n\n\n\n\n\n\n\n\n\n\n\n");
32
?
33
ObjectMapper mapper = new ObjectMapper();
34
response.getWriter().write(mapper.writeValueAsString(user));
35
response.getWriter().close();
36
}
37
?
38
@RequestMapping("/test")
39
public ModelAndView test(HttpServletRequest request,HttpServletResponse response)throws IOException {
40
?
41
ModelAndView mav = new ModelAndView();
42
mav.addObject("name", "SpringMVC");
43
mav.setViewName("test");
44
return mav;
45
?
46
}
47
?
48
}
49
?
50
?
2.3.3 Controller层注解的详细分析
在controller中,重要的地方在于这几个注解。controller、Resource、RequestMapping这单个注解。
1
注解@Controller。
表明这是一个Controller类,spring-mvc.xml文件设定的扫描目录下被扫描。
2
注解@Resource。
这个注解放在UserService的申明的前一行。它声明一个名为userService的对象,并且这个对象的类型为UserService接口的实现类。
通过@Resource注解,可以在service目录下UserServiceImple类中找到带有一个@Service("userService”)的注解,找到的注解中的变量名和这个注解下一行声明的对象的变量名是一致的。
在service目录下找到的那个类前面的注解表明在controller中的申明的对象的具体类型的UserServiceImple,它实现了UserService这个接口。
也就是说,@Service(“userService”)注解的类,为@Resource下一行的名为userService的对象服务。而它们一个位于service目录下,一个位于controller目录下。这两个目录都被添加到了spring-*.xml中。这样的对应关系,是属于两个层的
3
注解@RequestMapping
参数类型为一个字符串。这个参数的意义是从controller来的除了项目以外的三级及以下的URL地址。比如:
1
1
http://localhost:8080/ssm/test
这样的一个地址,经过分发器,来到了controller,controller会根据除了localhost/ssm/之外的地址,调用不同的方法。在controller这端,就会将后面的这段url地址通过注解,标记在方法上,或者是类上面。以这个url来看,它就会调用UserController.test(request,response)方法。
注解的位置的区别在于:
<1> 类的上方没有注解的时候,这个类的下面所有的方法的注解都需要添加参数。当然,也不是所有的方法都需要有注解。
但是只要是存在的注解都需要有参数。否则这样写的controller方法,作出的效果将会是“localhost:8080/ssm”的结果,这样welcome页面将会失效。
<2> 类的上方存在注解,那么这个注解会成为这个一个上级字符串加在类中的方法上的字符串上。就如同java的package的目录一样。com.xzj.test这样子,只不过在这里是用’/’来连接。
2.4 V层的具体构成
V层是直接面对用户的页面。所以在V层中,写的代码都是面向用户的操作的,尽管有一部分和请求相关,但是绝大部分还是于页面的展示相关的。
2.4.1 index.html文件
27
1
<!DOCTYPE html>
2
<html>
3
<head>
4
<meta charset="UTF-8">
5
<title>test</title>
6
</head>
7
<script>
8
function selectUser() {
9
var xmlhttp = new XMLHttpRequest();
10
xmlhttp.onreadystatechange = function () {
11
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
12
document.getElementById("test").innerHTML = xmlhttp.responseText;
13
}
14
}
15
xmlhttp.open("POST", "showUser.do", true);
16
xmlhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
17
xmlhttp.send("id=100");
18
}
19
</script>
20
<body>
21
<p id="test">Hello World!</p>
22
<button type="button" onclick="selectUser()">onclick test</button>
23
<br/>
24
<a href="test">ThisIsALinkForTest</a>
25
<br/>
26
</body>
27
</html>
这段html代码,其中的重点在于javascript代码中的Ajax掌握。
这段短短的javascript,发起请求,获得响应,设定显示,完成这三个工作。
按钮-响应,这一块非常基础的前端应用,主要的问题来自于响应方法中的Http请求。关于javascript的代码逻辑,虽然一眼就能明了,但是,还是要仔细研究一下ajax如何在html文件中,更加精巧的应用。
对于这个连接,就是在点击之后,会跳转到显示/WEB-INF/views/test.jsp相当于一个请求controller,并通过controller的到一个“test”的字符串,将其包装成为了一个完整的路径,在View层显示。
2.4.2 jsp目录及其下的test.jsp文件
17
1
<%--
2
Created by IntelliJ IDEA.
3
User: thisxzj
4
Date: 2019/1/1
5
Time: 23:45
6
To change this template use File | Settings | File Templates.
7
--%>
8
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
9
<html>
10
<head>
11
<title>xxx</title>
12
</head>
13
<body>
14
<h>test</h>
15
<p>我新建的项目是${name}</p>
16
</body>
17
</html>
这个jsp的路径是/WEB-INF/views/test.jsp,在从controller中返回一个字符串,比如一个“test”串,或者是modelAndView.setViewName("test”),并将其返回。
就会通过spring-mvc.xml中的下两行,将其包装成/WEB-INF/views/test.jsp。并将其显示到页面中。
2
1
<property name="prefix" value="/WEB-INF/views/"/>
2
<property name="suffix" value=".jsp"/>
在这样一个jsp页面中,${name}这个变量,是通过controller类中以返回modealAndView的方式,在modelAndView对象:
1
1
modelAndView.addObject("name", "SpringMVC");
那么在jsp页面中,将会读取这个KV,将name显示为成“SpringMVC”。这样就完成了一个从后台向前台的传输的过程。
响应过程
1
用户在浏览器窗口输入一个URL地址,以localhost:8080/ssm/为例子,由于这个URL是直接localhost/ssm/后面并没有接更多的信息,就会直接显示在web.xml中设定的welcome页面index.html。?这个过程发生在View层。
2
于是在浏览器页面上就会显示index.html的内容。用户按下显示onclick test的按钮,通过javascript的ajax发送一个包含一个字符串“showUser.do”和一个KV对“id = 1”的一个请求到controller,controller再根据请求中的“showUser.do”调用对应的controller方法。?这个过程从View层到Controller层。
3
请求来到Cotroller,通过扫描在Spring-MVC.xml中配置的目录下的所有的Java类。找到这个目录下所有带有@Controller的类。在这些类中寻找“showUser.do”对应的方法。在Controller类的第24行找到了一个selectUser方法。那么就会执行这个方法,对来自View层的请求进行响应。这个过程发生在Controller层。
4
找到这个方法了,开始执行。这个方法的核心在于,使用了userService这个对象以及它的的方法。关于如何使用的,在2.3.3中有详细的解释。在在这个类中,使用这个对象以及它的seclectUser方法,就是工作从Controller层到Model层的转移。
5
来到Model层,映入眼帘的就是Service接口的实现类ServiceImple。我们不关注Service接口,经过我的测试,去掉这个接口对程序的运行也没有影响。但是这个接口的存在,可以使Service方法更加规范化。在大型工程中,有一个这样的接口可以更加规范。在这个ServiceImple类中,申明了一个UserDAO的对象,这个对象并没有实体。通过注解,将UserDAO.xml作为这个接口的实体,使用定义在UserDAO.xml中的sql语句操作数据库。
sql语句和UserDAO的对应,通过在spring-mybatis.xml中,将mapper的目录和DAO接口的目录配置进去。这样在mapper的xml中,声明是对哪一个DAO接口的实现,并且在每一个sql方法的标签内,设置id为对应的DAO接口中的方法名,就可以视作将DAO的方法在mapper的xml中实现了。
通过目录、接口名、方法名的一系列配置,在调用UserDAO接口的方法的时候,就可以直接使用对应的sql语句。以:
1
1
User user = this.userService.selectUser(userId);
然后在UserServiceImple中,调用UserDAO的方法:
1
1
this.userDAO.selectUser(UserID);
在通过DAO的配置,使用UserDAO.xml中的sql语句操作数据库。
这个部分的操作都是在Model层。但是将Model层分为两个模块:service和DAO的话,那么这个部分是在Model层中是从service到DAO的转移。
6
我们忽略mapper的xml文件中具体执行了什么。只考虑对数据库操作之后有没有数据的返回。
如果没有,那么一个从前到后的SSM请求到响应就已经结束了。
大多数时候是有返回的。通过mapper的xml文件中的result设定的返回值类型,将从数据库中获取的数值,包装成一个返回类型,返回给UserServiceImple的对象。
在这个例子中是从数据库中获取指定id处的,一个User对象的所有信息,将其包装成一个User对象,返回给UserServiceImple。
这个部分的操作都是在Model层,细分的话那么这个部分是在Model层中是从DAO到service的转移。
7
UserServiceImple对象将从DAO获得了一个User对象的返回值,返回给Controller层。
这一个操作是从Model层返回到Controller层。
8
这样的一个User对象从DAO、Service传递了Controller层中由“showUser.do”对应的那个方法中继续着下一步的操作。
通过下面两行语句,将获取的User对象转换成一个字符串,然后传送到javascript代码中请求所对应的响应码responseText中。
2
1
ObjectMapper mapper = new ObjectMapper();
2
response.getWriter().write(mapper.writeValueAsString(user);
然后通过DOM操作,将html中的某个字段修改成User对象的字符串形式。完成了获取内容在页面上的显示。这一步是从Controller层到View层的传递。
html { }
:root { }
html { font-size: 14px; background-color: var(--bg-color); color: var(--text-color); font-family: "Helvetica Neue", Helvetica, Arial, sans-serif }
body { margin: 0px; padding: 0px; height: auto; bottom: 0px; top: 0px; left: 0px; right: 0px; font-size: 1rem; line-height: 1.42857143; background-image: inherit; background-attachment: inherit; background-color: inherit; background-position: inherit inherit; background-repeat: inherit inherit }
iframe { margin: auto }
a.url { }
a:active,a:hover { outline: 0px }
.in-text-selection,::selection { background: var(--select-text-bg-color); color: var(--select-text-font-color) }
#write { margin: 0px auto; height: auto; width: inherit; position: relative; white-space: normal }
#write.first-line-indent p { text-indent: 2em }
#write.first-line-indent li p,#write.first-line-indent p * { text-indent: 0px }
#write.first-line-indent li { margin-left: 2em }
.for-image #write { padding-left: 8px; padding-right: 8px }
body.typora-export { padding-left: 30px; padding-right: 30px }
.typora-export .footnote-line,.typora-export p { white-space: pre-wrap }
#write li>figure:first-child { margin-top: -20px }
#write ol,#write ul { position: relative }
img { max-width: 100%; vertical-align: middle }
button,input,select,textarea { color: inherit; font-family: inherit; font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit }
input[type="checkbox"],input[type="radio"] { line-height: normal; padding: 0px }
*,::after,::before { }
#write h1,#write h2,#write h3,#write h4,#write h5,#write h6,#write p,#write pre { width: inherit }
#write h1,#write h2,#write h3,#write h4,#write h5,#write h6,#write p { position: relative }
h1,h2,h3,h4,h5,h6 { orphans: 2 }
p { orphans: 4 }
h1 { font-size: 2rem }
h2 { font-size: 1.8rem }
h3 { font-size: 1.6rem }
h4 { font-size: 1.4rem }
h5 { font-size: 1.2rem }
h6 { font-size: 1rem }
.md-math-block,.md-rawblock,h1,h2,h3,h4,h5,h6,p { margin-top: 1rem; margin-bottom: 1rem }
.hidden { display: none }
.md-blockmeta { color: rgb(204, 204, 204); font-weight: 700; font-style: italic }
a { cursor: pointer }
sup.md-footnote { padding: 2px 4px; background-color: rgba(238, 238, 238, 0.701961); color: rgb(85, 85, 85); cursor: pointer }
sup.md-footnote a,sup.md-footnote a:hover { color: inherit; text-transform: inherit; text-decoration: inherit }
#write input[type="checkbox"] { cursor: pointer; width: inherit; height: inherit }
figure { margin: 1.2em 0px; max-width: calc(100% + 16px); padding: 0px }
figure>table { margin: 0px !important }
tr { }
thead { display: table-header-group }
table { border-collapse: collapse; border-spacing: 0px; width: 100%; overflow: auto; text-align: left }
table.md-table td { min-width: 80px }
.CodeMirror-gutters { border-right-width: 0px; background-color: inherit }
.CodeMirror { text-align: left }
.CodeMirror-placeholder { opacity: 0.3 }
.CodeMirror pre { padding: 0px 4px }
.CodeMirror-lines { padding: 0px }
div.hr:focus { cursor: none }
#write pre { white-space: pre-wrap }
#write.fences-no-line-wrapping pre { white-space: pre }
#write pre.ty-contain-cm { white-space: normal }
.CodeMirror-gutters { margin-right: 4px }
.md-fences { font-size: 0.9rem; display: block; text-align: left; overflow: visible; white-space: pre; background-image: inherit; background-attachment: inherit; background-color: inherit; position: relative !important; background-position: inherit inherit; background-repeat: inherit inherit }
.md-diagram-panel { width: 100%; margin-top: 10px; text-align: center; padding-top: 0px; padding-bottom: 8px }
#write .md-fences.mock-cm { white-space: pre-wrap }
.md-fences.md-fences-with-lineno { padding-left: 0px }
#write.fences-no-line-wrapping .md-fences.mock-cm { white-space: pre }
.md-fences.mock-cm.md-fences-with-lineno { padding-left: 8px }
.CodeMirror-line,twitterwidget { }
.footnotes { opacity: 0.8; font-size: 0.9rem; margin-top: 1em; margin-bottom: 1em }
.footnotes+.footnotes { margin-top: 0px }
.md-reset { margin: 0px; padding: 0px; border: 0px; outline: 0px; vertical-align: top; text-decoration: none; float: none; position: static; width: auto; height: auto; white-space: nowrap; cursor: inherit; line-height: normal; font-weight: 400; text-align: left; direction: ltr; background-position: 0px 0px; background-repeat: initial initial }
li div { padding-top: 0px }
blockquote { margin: 1rem 0px }
li .mathjax-block,li p { margin: 0.5rem 0px }
li { margin: 0px; position: relative }
blockquote>:last-child { margin-bottom: 0px }
blockquote>:first-child,li>:first-child { margin-top: 0px }
.footnotes-area { color: rgb(136, 136, 136); margin-top: 0.714rem; padding-bottom: 0.143rem; white-space: normal }
#write .footnote-line { white-space: pre-wrap }
.footnote-line { margin-top: 0.714em; font-size: 0.7em }
a img,img a { cursor: pointer }
pre.md-meta-block { font-size: 0.8rem; min-height: 0.8rem; white-space: pre-wrap; background-color: rgb(204, 204, 204); display: block; background-position: initial initial; background-repeat: initial initial }
p>.md-image:only-child:not(.md-img-error) img,p>img:only-child { display: block; margin: auto }
p>.md-image:only-child { display: inline-block; width: 100% }
#write .MathJax_Display { margin: 0.8em 0px 0px }
.md-math-block { width: 100% }
.md-math-block:not(:empty)::after { display: none }
[contenteditable="true"]:active,[contenteditable="true"]:focus { outline: 0px }
.md-task-list-item { position: relative; list-style-type: none }
.task-list-item.md-task-list-item { padding-left: 0px }
.md-task-list-item>input { position: absolute; top: 0px; left: 0px; margin-left: -1.2em; margin-top: calc(1em - 10px) }
.math { font-size: 1rem }
.md-toc { min-height: 3.58rem; position: relative; font-size: 0.9rem }
.md-toc-content { position: relative; margin-left: 0px }
.md-toc-content::after,.md-toc::after { display: none }
.md-toc-item { display: block; color: rgb(65, 131, 196) }
.md-toc-item a { text-decoration: none }
.md-toc-inner:hover { text-decoration: underline }
.md-toc-inner { display: inline-block; cursor: pointer }
.md-toc-h1 .md-toc-inner { margin-left: 0px; font-weight: 700 }
.md-toc-h2 .md-toc-inner { margin-left: 2em }
.md-toc-h3 .md-toc-inner { margin-left: 4em }
.md-toc-h4 .md-toc-inner { margin-left: 6em }
.md-toc-h5 .md-toc-inner { margin-left: 8em }
.md-toc-h6 .md-toc-inner { margin-left: 10em }
a.md-toc-inner { font-size: inherit; font-style: inherit; font-weight: inherit; line-height: inherit }
.footnote-line a:not(.reversefootnote) { color: inherit }
.md-attr { display: none }
.md-fn-count::after { content: "." }
code,pre,samp,tt { font-family: var(--monospace) }
kbd { margin: 0px 0.1em; padding: 0.1em 0.6em; font-size: 0.8em; color: rgb(36, 39, 41); background-color: rgb(255, 255, 255); border: 1px solid rgb(173, 179, 185); white-space: nowrap; vertical-align: middle; background-position: initial initial; background-repeat: initial initial }
.md-comment { color: rgb(162, 127, 3); opacity: 0.8; font-family: var(--monospace) }
code { text-align: left }
a.md-print-anchor { white-space: pre !important; border: none !important; display: inline-block !important; position: absolute !important; width: 1px !important; right: 0px !important; outline: 0px !important; background-position: 0px 0px !important; background-repeat: initial initial !important }
.md-inline-math .MathJax_SVG .noError { display: none !important }
.md-math-block .MathJax_SVG_Display { text-align: center; margin: 0px; position: relative; text-indent: 0px; max-width: none; max-height: none; min-height: 0px; min-width: 100%; width: auto; display: block !important }
.MathJax_SVG_Display,.md-inline-math .MathJax_SVG_Display { width: auto; margin: inherit; display: inline-block !important }
.MathJax_SVG .MJX-monospace { font-family: var(--monospace) }
.MathJax_SVG .MJX-sans-serif { font-family: sans-serif }
.MathJax_SVG { display: inline; font-style: normal; font-weight: 400; line-height: normal; text-indent: 0px; text-align: left; text-transform: none; letter-spacing: normal; word-spacing: normal; white-space: nowrap; float: none; direction: ltr; max-width: none; max-height: none; min-width: 0px; min-height: 0px; border: 0px; padding: 0px; margin: 0px }
.MathJax_SVG * { }
.MathJax_SVG_Display svg { vertical-align: middle !important; margin-bottom: 0px !important }
.os-windows.monocolor-emoji .md-emoji { font-family: "Segoe UI Symbol", sans-serif }
.md-diagram-panel>svg { max-width: 100% }
[lang="mermaid"] svg,[lang="flow"] svg { max-width: 100% }
[lang="mermaid"] .node text { font-size: 1rem }
table tr th { border-bottom-width: 0px }
video { max-width: 100%; display: block; margin: 0px auto }
iframe { max-width: 100%; width: 100%; border: none }
.highlight td,.highlight tr { border: 0px }
.CodeMirror { height: auto }
.CodeMirror.cm-s-inner { background-image: inherit; background-attachment: inherit; background-color: inherit; background-position: inherit inherit; background-repeat: inherit inherit }
.CodeMirror-scroll { z-index: 3 }
.CodeMirror-gutter-filler,.CodeMirror-scrollbar-filler { background-color: rgb(255, 255, 255) }
.CodeMirror-gutters { border-right-width: 1px; border-right-style: solid; border-right-color: rgb(221, 221, 221); background-image: inherit; background-attachment: inherit; background-color: inherit; white-space: nowrap; background-position: inherit inherit; background-repeat: inherit inherit }
.CodeMirror-linenumber { padding: 0px 3px 0px 5px; text-align: right; color: rgb(153, 153, 153) }
.cm-s-inner .cm-keyword { color: rgb(119, 0, 136) }
.cm-s-inner .cm-atom,.cm-s-inner.cm-atom { color: rgb(34, 17, 153) }
.cm-s-inner .cm-number { color: rgb(17, 102, 68) }
.cm-s-inner .cm-def { color: rgb(0, 0, 255) }
.cm-s-inner .cm-variable { color: rgb(0, 0, 0) }
.cm-s-inner .cm-variable-2 { color: rgb(0, 85, 170) }
.cm-s-inner .cm-variable-3 { color: rgb(0, 136, 85) }
.cm-s-inner .cm-string { color: rgb(170, 17, 17) }
.cm-s-inner .cm-property { color: rgb(0, 0, 0) }
.cm-s-inner .cm-operator { color: rgb(152, 26, 26) }
.cm-s-inner .cm-comment,.cm-s-inner.cm-comment { color: rgb(170, 85, 0) }
.cm-s-inner .cm-string-2 { color: rgb(255, 85, 0) }
.cm-s-inner .cm-meta { color: rgb(85, 85, 85) }
.cm-s-inner .cm-qualifier { color: rgb(85, 85, 85) }
.cm-s-inner .cm-builtin { color: rgb(51, 0, 170) }
.cm-s-inner .cm-bracket { color: rgb(153, 153, 119) }
.cm-s-inner .cm-tag { color: rgb(17, 119, 0) }
.cm-s-inner .cm-attribute { color: rgb(0, 0, 204) }
.cm-s-inner .cm-header,.cm-s-inner.cm-header { color: rgb(0, 0, 255) }
.cm-s-inner .cm-quote,.cm-s-inner.cm-quote { color: rgb(0, 153, 0) }
.cm-s-inner .cm-hr,.cm-s-inner.cm-hr { color: rgb(153, 153, 153) }
.cm-s-inner .cm-link,.cm-s-inner.cm-link { color: rgb(0, 0, 204) }
.cm-negative { color: rgb(221, 68, 68) }
.cm-positive { color: rgb(34, 153, 34) }
.cm-header,.cm-strong { font-weight: 700 }
.cm-del { text-decoration: line-through }
.cm-em { font-style: italic }
.cm-link { text-decoration: underline }
.cm-error { color: red }
.cm-invalidchar { color: red }
.cm-constant { color: rgb(38, 139, 210) }
.cm-defined { color: rgb(181, 137, 0) }
div.CodeMirror span.CodeMirror-matchingbracket { color: rgb(0, 255, 0) }
div.CodeMirror span.CodeMirror-nonmatchingbracket { color: rgb(255, 34, 34) }
.cm-s-inner .CodeMirror-activeline-background { background-image: inherit; background-attachment: inherit; background-color: inherit; background-position: inherit inherit; background-repeat: inherit inherit }
.CodeMirror { position: relative; overflow: hidden }
.CodeMirror-scroll { height: 100%; outline: 0px; position: relative; background-image: inherit; background-attachment: inherit; background-color: inherit; background-position: inherit inherit; background-repeat: inherit inherit }
.CodeMirror-sizer { position: relative }
.CodeMirror-gutter-filler,.CodeMirror-hscrollbar,.CodeMirror-scrollbar-filler,.CodeMirror-vscrollbar { position: absolute; z-index: 6; display: none }
.CodeMirror-vscrollbar { right: 0px; top: 0px; overflow: hidden }
.CodeMirror-hscrollbar { bottom: 0px; left: 0px; overflow: hidden }
.CodeMirror-scrollbar-filler { right: 0px; bottom: 0px }
.CodeMirror-gutter-filler { left: 0px; bottom: 0px }
.CodeMirror-gutters { position: absolute; left: 0px; top: 0px; padding-bottom: 30px; z-index: 3 }
.CodeMirror-gutter { white-space: normal; height: 100%; padding-bottom: 30px; margin-bottom: -32px; display: inline-block }
.CodeMirror-gutter-wrapper { position: absolute; z-index: 4; border: none !important; background-position: 0px 0px !important; background-repeat: initial initial !important }
.CodeMirror-gutter-background { position: absolute; top: 0px; bottom: 0px; z-index: 4 }
.CodeMirror-gutter-elt { position: absolute; cursor: default; z-index: 4 }
.CodeMirror-lines { cursor: text }
.CodeMirror pre { border-width: 0px; font-family: inherit; font-size: inherit; margin: 0px; white-space: pre; color: inherit; z-index: 2; position: relative; overflow: visible; background-position: 0px 0px; background-repeat: initial initial }
.CodeMirror-wrap pre { white-space: pre-wrap }
.CodeMirror-code pre { border-right-width: 30px; border-right-style: solid; border-right-color: transparent; width: fit-content }
.CodeMirror-wrap .CodeMirror-code pre { border-right-style: none; width: auto }
.CodeMirror-linebackground { position: absolute; left: 0px; right: 0px; top: 0px; bottom: 0px; z-index: 0 }
.CodeMirror-linewidget { position: relative; z-index: 2; overflow: auto }
.CodeMirror-wrap .CodeMirror-scroll { }
.CodeMirror-measure { position: absolute; width: 100%; height: 0px; overflow: hidden; visibility: hidden }
.CodeMirror-measure pre { position: static }
.CodeMirror div.CodeMirror-cursor { position: absolute; visibility: hidden; border-right-style: none; width: 0px }
.CodeMirror div.CodeMirror-cursor { visibility: hidden }
.CodeMirror-focused div.CodeMirror-cursor { visibility: inherit }
.cm-searching { background-color: rgba(255, 255, 0, 0.4); background-position: initial initial; background-repeat: initial initial }
:root { }
.mac-seamless-mode #typora-sidebar { top: 20px; padding-top: 0; height: auto }
html,body,#write { background: var(--bg-color); font-family: "TeXGyreAdventor", "Century Gothic", "Yu Gothic", "Raleway", sans-serif; font-weight: 300 }
h1,h2,h3,h4,h5,h6 { color: #111; font-family: "TeXGyreAdventor", "Century Gothic", "Yu Gothic", "Ubuntu", sans-serif }
html { font-size: 16px }
#write { max-width: 914px; text-align: justify }
#write>h1:first-child { margin-top: 2.75rem }
#write>h2:first-child { margin-top: 1.75rem }
#write>h3:first-child { margin-top: 1rem }
#write>h4:first-child { margin-top: 0.5rem }
h1 { font-weight: normal; line-height: 4rem; margin: 0 0 1.75rem; padding: 20px 30px; text-align: center; text-transform: uppercase; margin-top: 4rem }
h2 { font-weight: normal; line-height: 3rem; margin: 0 0 1.9375rem; padding: 0 30px; text-align: center; text-transform: uppercase; margin-top: 3rem }
h3 { font-weight: normal }
h4 { font-weight: normal }
h5 { font-size: 1.125rem; font-weight: normal }
h6 { font-size: 1rem; font-weight: bold }
p { color: #111; font-size: 1rem; line-height: 1.75rem; margin: 0 0 1.25rem }
#write>h3.md-focus::before { left: -1.875rem; top: 0.5rem; padding: 2px }
#write>h4.md-focus::before { left: -1.875rem; top: 0.3125rem; padding: 2px }
#write>h5.md-focus::before { left: -1.875rem; top: 0.25rem; padding: 2px }
#write>h6.md-focus::before { left: -1.875rem; top: .125rem; padding: 2px }
a,.md-def-url { color: #990000; text-decoration: none }
a:hover { text-decoration: underline }
table { margin-bottom: 20px }
table th,table td { padding: 8px; line-height: 1.25rem; vertical-align: top; border-top: 1px solid #ddd }
table th { font-weight: bold }
table thead th { vertical-align: bottom }
table caption+thead tr:first-child th,table caption+thead tr:first-child td,table colgroup+thead tr:first-child th,table colgroup+thead tr:first-child td,table thead:first-child tr:first-child th,table thead:first-child tr:first-child td { border-top: 0 }
table tbody+tbody { border-top: 2px solid #ddd }
code,.md-fences { border: 1px solid #ccc; padding: .1em; font-size: 0.9em; margin-left: 0.2em; margin-right: 0.2em }
.md-fences { margin: 0 0 20px; font-size: 1em; padding: 0.3em 1em; padding-top: 0.4em }
.task-list { padding-left: 0 }
.task-list-item { padding-left: 2.125rem }
.task-list-item input { top: 3px }
.md-task-list-item input { margin-left: -1.5em }
.task-list-item input::before { content: ""; display: inline-block; width: 1rem; height: 1rem; vertical-align: middle; text-align: center; border: 1px solid gray; background-color: #fdfdfd; margin-left: 0; margin-top: -1rem }
.typora-node .task-list-item input::before { top: 0.3ex; position: absolute }
.task-list-item input:checked::before,.task-list-item input[checked]::before { content: "?"; font-size: 0.8125rem; line-height: 0.9375rem }
blockquote { margin: 0 0 1.11111rem; padding: 0.5rem 1.11111rem 0 1.05556rem; border-left: 1px solid gray }
blockquote,blockquote p { line-height: 1.6; color: #6f6f6f }
#write pre.md-meta-block { min-height: 30px; background: #f8f8f8; padding: 1.5em; font-weight: 300; font-size: 1em; padding-bottom: 1.5em; padding-top: 3em; margin-top: -1.5em; color: #999; width: 100vw; max-width: calc(100% + 60px); margin-left: -30px; border-left: 30px #f8f8f8 solid; border-right: 30px #f8f8f8 solid }
.MathJax_Display { font-size: 0.9em; margin-top: 0.5em; margin-bottom: 0 }
p.mathjax-block,.mathjax-block { padding-bottom: 0 }
.mathjax-block>.code-tooltip { bottom: 5px }
.md-image>.md-meta { padding-left: 0.5em; padding-right: 0.5em }
.md-image>img { margin-top: 2px }
.md-image>.md-meta:first-of-type::before { padding-left: 4px }
#typora-source { color: #555 }
#md-searchpanel { border-bottom: 1px solid #ccc }
#md-searchpanel .btn { border: 1px solid #ccc }
#md-notification::before { top: 14px }
#md-notification { background: #eee }
.megamenu-menu-panel .btn { border: 1px solid #ccc }
#typora-sidebar { }
.file-list-item,.show-folder-name .file-list-item { padding-top: 20px; padding-bottom: 20px; line-height: 20px }
.file-list-item-summary { height: 40px; line-height: 20px }
.ty-table-edit { background: #ededed }
.dropdown-menu .divider { border-color: #e5e5e5 }
.typora-export p,.typora-export .footnote-line { white-space: normal }
原文地址:https://www.cnblogs.com/zjxu97/p/10211203.html