目录
1、日期和字符串之间的转化
1.1 JDK 8 之前的 Date 日期的格式转换
Date 和 String 之间的转换 API:
// String -> java.util.Date
String dateStr1 = "2021-12-30 12:59:59";
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
try {
Date date1 = sdf.parse(dateStr1);
} catch (ParseException e) {
e.printStackTrace();
}
// Date -> String
Date date2 = new Date();
String dateStr2 = sdf.format(date2);
1.2 JDK 8之后 LocalDateTime 新日期的格式转换
只要你使用的是 MySQL 8.0 之后版本的数据库,里面的时间格式已经改成了 LocalDateTime,之前项目中涉及到的日期均需要改成 LocalDateTime 类型的。
参考文献:java8 — 新日期时间API篇,包括之前的所有相关的 LocalDateTime 内容都可以参考这篇文章。
LocalDateTime 和 String 之间的格式转换:
//java.time.LocalDateTime -> String
LocalDateTime date = LocalDateTime.now();
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
String dateStr = formatter.format(date);
System.out.println(dateStr);
System.out.println("-----------------------------------");
//String -> java.time.LocalDateTime
String datetime = "2020-01-13 21:27:30";
LocalDateTime ldt = LocalDateTime.parse(datetime, formatter);
System.out.println(ldt);
1.3 Thymeleaf 中将 Date 日期转换为字符串
thymeleaf中使用#dates这个公共的内置对象:
th:text="${#dates.format(topic.topicDate ,'yyyy-MM-dd HH:mm:ss')}"
1.4 Thymeleaf 中将 LocalDateTime 日期转换为字符串
但是,查找相关资料,将 LocalDateTime 日期转换为字符串均需要进行 maven 配置,所以这里暂时先不对日期格式进行转化。
th:text="${#temporals.format(localDateTime, 'dd-MM-yyyy HH:mm:ss')}"
参考文献:用thymeleaf将LocalDateTime类型的日期格式化为yyyy-MM-dd hh:mm:ss
2、系统启动时访问页面
- 系统启动时,我们访问的页面是:
http://localhost:8080/pro23/page.do?operate=page&page=login
- 为什么不是:
http://localhost:8080/pro23/login.html
- 答: 如果是后者,那么属于直接访问静态页面。那么页面上的 thymeleaf 表达式浏览器是不能识别的;我们访问前者的目的其实就是要执行
ViewBaseServlet
中的processTemplete()
,也就是说,我们要经过 PageController 去访问后端进行视图渲染的组件,而不能直接访问静态页面。
3、访问某 URL 执行的过程
http://localhost:8080/pro23/page.do?operate=page&page=login
访问这个URL,执行的过程是什么样的?
3.1 前置知识
-
ServerIP(服务器端的 IP 地址):怎么保证访问你的服务器,不是访问别人的服务器嘞,要把域名地址解析成一个 IP 地址,每一台服务器都在网络上有唯一的 IP 地址。
-
URL 地址各个部分对应的含义如下面表格所示:
URL 地址 | http:// | localhost | :8080 | /pro23 | /page.do | ?operate=page&page=login |
---|---|---|---|---|---|---|
各部分含义 | 协议 | ServerIP | port | context root(根目录) | request.getServletPath() | query string |
3.2 组件路径和查询字符串执行过程
- DispatcherServlet 组件的 URL 映射表 urlPattern 为
*.do
拦截/page.do
; - 中央控制器中的
request.getServletPath()
得到/page.do
这个字符串; - 中央控制器解析处理字符串,将
/page.do
转化成page
; - 拿到 page 这个字符串,然后去 IOC 容器(
BeanFactory
)中寻找id=page
的那个 bean 对象,然后我们就可以找到PageController.java
这个 class 类; - 获取
operate
的值,这里是page
方法,因此得知,应该执行PageController
中的page()
方法; - PageController 中的 page 方法定义如下:
public String page(String page){
return page ;
}
- 在 queryString:
?operate=page&page=login
中获取请求参数,参数名是page
,参数值是login
,因此 page 方法传入的形参 page 值会被赋上"login"
,然后return "login"
, return 给 谁?? - 因为 PageController 的 page 方法是 DispatcherServlet 通过反射调用的
method.invoke(....) ;
,因此,字符串 “login” 返回给中央控制器DispatcherServlet
; - DispatcherServlet 接收到返回值,然后处理视图:
目前处理视图的方式有两种: 1. 带前缀redirect: 2. 不带前缀
当前,返回 “login”,不带前缀
那么直接执行视图渲染操作:super.processTemplate("login",request,response);
- 此时 ViewBaseServlet 中的 processTemplate 方法会执行,会帮助我们设置前缀、设置后缀、封装成 templateEngine 引擎,然后这个引擎帮我们工作,执行 process 方法 ,效果是:
在 “login” 这个字符串前面拼接"/"
(其实就是配置文件中 view-prefixe 配置的值)
在"login"这个字符串后面拼接".html"
(其实就是配置文件中 view-suffix 配置的值) - 最后进行服务器转发,属于内部转发。
4、目前 javaweb 项目开发的“套路”
4.1 详细步骤
- 导入
myssm.jar
包,这里也可以选择直接复制粘贴整个 myssm 文件夹到下一个项目,后面会对 myssm 中封装好的 DispatcherServlet 进行修改; - 新建配置文件
applicationContext.xml
或者可以不叫这个名字,在 web.xml 中指定文件名 - 在
web.xml
文件中配置:
- 配置前缀和后缀,这样 thymeleaf 引擎就可以根据我们返回的字符串进行拼接,再跳转
<context-param>
<param-name>view-prefix</param-name>
<param-value>/</param-value>
</context-param>
<context-param>
<param-name>view-suffix</param-name>
<param-value>.html</param-value>
</context-param>
- 配置监听器要读取的参数,目的是加载 IOC 容器的配置文件(也就是 applicationContext.xml)
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
4. 开发具体的业务模块:
(1)一个具体的业务模块纵向上由几个部分组成
- html 页面
- POJO 类
- DAO 接口和实现类
- Service 接口和实现类
- Controller 控制器组件
(2)如果 html 页面有 thymeleaf 表达式,一定不能够直接访问,必须要经过 PageController
(3)在 applicationContext.xml 中配置 DAO、Service、Controller,以及三者之间的依赖关系
(4)DAO 实现类中 , 继承 BaseDAO,然后实现具体的接口, 需要注意, BaseDAO 后面的泛型不能写错。
例如:
public class UserDAOImpl extends BaseDAO<User> implements UserDAO{}
(5)Service 是业务控制类,这一层我们只需要记住一点:
- 业务逻辑我们都封装在 service 这一层,不要分散在 Controller 层,也不要出现在 DAO 层(我们需要保证 DAO 方法的单精度特性)
- 当某一个业务功能需要使用其他模块的业务功能时,尽量的调用别人的 service,而不是深入到其他模块的 DAO 细节
(6)Controller 类的编写规则
- 在 applicationContext.xml 中配置
Controller <bean id="user" class="com.atguigu.qqzone.controllers.UserController>
那么,用户在前端发请求时,对应的 servletpath 就是/user.do
,其中的 “user” 就是对应此处的 bean 的 id 值 - 在 Controller 中设计的方法名需要和 operate 的值一致
public String login(String loginId , String pwd , HttpSession session){
return "index";
}
因此,我们的登录验证的表单如下:
<form th:action="@{/user.do}" method="post">
<inut type="hidden" name="operate" value="login"/>
</form>
- 在表单中,组件的 name 属性和 Controller 中方法的参数名一致
<input type="text" name="loginId" />
public String login(String loginId , String pwd , HttpSession session){
- 另外,需要注意的是: Controller 中的方法中的参数不一定都是通过请求参数获取的,要除去浏览器发送请求自带的的参数,比如请求、响应、session 等
if("request".equals...) else if("response".equals....) else if("session".equals....){
直接赋值
}else{
此处才是从request的请求参数中获取
request.getParameter("loginId") .....
}
(7)DispatcherServlet中步骤大致分为:
- 初始化方法中:从 application 作用域获取 IOC 容器
- 解析 servletPath , 在 IOC 容器中寻找对应的 Controller 组件
- 准备 operate 指定的方法所要求的参数
- 调用 operate 指定的方法
- 接收到执行 operate 指定的方法的返回值,对返回值进行处理 - 视图处理
(8)为什么 DispatcherServlet 能够从 application 作用域获取到 IOC 容器?
ContextLoaderListener
在容器启动时会执行初始化任务,而它的操作就是:
- 解析 IOC 的配置文件,创建一个一个的组件,并完成组件之间依赖关系的注入;
- 将 IOC 容器保存到 application 作用域。
5、使用 druid 数据库连接池
修改 BaseDAO,让其支持 propertie s文件以及 druid 数据源连接池,这里老师讲了两种方式,也不太清晰,具体的可以看我之前的 JDBC 中有关讲解:(尚硅谷)JDBC总复习
- 新建一个 jdbc.properties 配置文件放置在 src 文件夹下:
配置文件的内容,这里的每一对配置中的 key 是固定格式的,不可以修改:
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/qqzonedb?useUnicode=true&characterEncoding=utf-8&useSSL=false
username=root
password=abc123
# 初始化时建立物理连接的个数
initialSize=5
# 最大连接池数量
maxActive=10
# 最大等待时间
maxWait=3000
- 将 ConnUtil 获取连接的文件修改为:
public class ConnUtil {
private static ThreadLocal<Connection> threadLocal = new ThreadLocal<>();
/**
* 使用Druid数据库连接池技术
*/
private static DataSource source;
static{
try {
Properties pros = new Properties();
InputStream is = ConnUtil.class.getClassLoader().getResourceAsStream("jdbc.properties");
pros.load(is);
source = DruidDataSourceFactory.createDataSource(pros);
} catch (Exception e) {
e.printStackTrace();
}
}
private static Connection createConn(){
try {
// //1.加载驱动
// Class.forName(DRIVER);
// //2.通过驱动管理器获取连接对象
// return DriverManager.getConnection(URL,USER,PWD);
return source.getConnection();
} catch (SQLException e) {
e.printStackTrace();
}
return null ;
}
public static Connection getConn(){
Connection conn = threadLocal.get();
if(conn==null){
conn =createConn();
threadLocal.set(conn);
}
return threadLocal.get() ;
}
public static void closeConn() throws SQLException {
Connection conn = threadLocal.get();
if(conn==null){
return ;
}
if(!conn.isClosed()){
conn.close();
//threadLocal.set(null);
threadLocal.remove();
}
}
}
注意:
- 这里的这个
private static DataSource source;
要放置在createConn()
方法外面,而不是像老师那样放置在方法内部,这样才能保证初始加载我们只创建了一个数据库连接池,而不是每次连接都创建一个数据库连接池(这样会更慢)。 - 这边需要将康师傅讲的 将
ClassLoader.getSystemClassLoader().getResourceAsStream()
变成ConnUtil.class.getClassLoader().getResourceAsStream();
,否则就会报 500 错误,读取不到流文件,具体原因呢还不清楚为什么获取系统类加载器不可以,之后看完反射回来再说。
6、Idea 打包 jar 包
具体打包方法看 尚硅谷丨2022版JavaWeb教程(全新技术栈,全程实战),P68 18.00开始讲解如何打包的。
注意:
- 但是这里不建议删除,不建议打包,因为后面还要对这个 myssm 包内的文件进行修改,毕竟手写的不如人家框架里面封装好的考虑的全面,直接复制粘贴到下一个项目就可以了。