Javaweb总结-目前开发Javaweb的套路梳理
前置知识
前置准备
知识准备
已掌握JavaSE/MySQL/JDBC+HTML/CSS/JavaScript基础
并已完成了Javaweb前置知识的学习
04-JavaScript基础应用-鼠标悬浮/离开表格格式变化
05-JavaWeb-Tomcat8安装、Servlet初识
06-JavaWeb-Servlet方法/生命周期、HTTP/会话session
09-JavaWeb-阶段性项目1:最简单的后台库存管理系统
10-JavaWeb阶段性项目1:系统的servlet优化1
11-JavaWeb阶段性项目1:系统的servlet优化2
12-JavaWeb阶段性项目1:系统的servlet优化3
13-JavaWeb阶段性项目1:系统的servlet优化4
14-JavaWeb阶段性项目1:系统的servlet优化5
15-JavaWeb阶段性项目1:Servlet-api、mvc-service引入、IOC和DI
资源准备
教学资源
https://pan.baidu.com/s/1TS7QJ_a2vHHmXkggAs8RMQ
提取码:yyds
目前开发Javaweb的套路梳理
目前我们进行javaweb项目开发的“套路”是这样的:
1 拷贝 myssm包
basedao
-BaseDAO
DAO:data access object,数据访问对象。封装了操作数据库的JDBC代码,
BaseDAO是考虑了泛型、事务之后通用型的DAO。
在之前的改进中,我们把它获取连接需要的参数移到了ConnUtil中,各增删改查方法在获取数据库连接时不再是直接在BaseDAO中进行,而是调用ConnUtil
protected Connection getConn(){
return ConnUtil.getConn();
}
-ConnUtil
①定义了一个ThreadLocal<Connection>对象
ThreadLocal
-
get() , set(obj) ThreadLocal称之为本地线程 。 我们可以通过set方法在当前线程上存储数据、通过get方法在当前线程上获取数据
set方法源码分析:
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前的线程
ThreadLocalMap map = getMap(t); //每一个线程都维护各自的一个容器(ThreadLocalMap)
if (map != null)
map.set(this, value); //这里的key对应的是ThreadLocal,因为我们的组件中需要传输(共享)的对象可能会有多个(不止Connection)
else
createMap(t, value); //默认情况下map是没有初始化的,那么第一次往其中添加数据时,会去初始化
}
-get方法源码分析:
public T get() {
Thread t = Thread.currentThread(); //获取当前的线程
ThreadLocalMap map = getMap(t); //获取和这个线程(企业)相关的ThreadLocalMap(也就是工作纽带的集合)
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //this指的是ThreadLocal对象,通过它才能知道是哪一个工作纽带
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value; //entry.value就可以获取到工具箱了
return result;
}
}
return setInitialValue();
}
②获取连接需要的参数
public static final String DRIVER = "com.mysql.jdbc.Driver" ;
public static final String URL = "jdbc:mysql://localhost:3306/qqzonedb?useUnicode=true&characterEncoding=utf-8&useSSL=false";
public static final String USER = "root";
public static final String PWD = "123456" ;
③方法
createConn:通过我们配置的参数,返回一个数据库连接对象
getConn:考虑ThreadLocal,调用createConn,将连接对象放入ThreadLocal中
-DAOException
public class DAOException extends RuntimeException{
public DAOException(String msg){
super(msg);
}
}
自定义的运行时异常,在BaseDAO类里有使用
throw new DAOException("BaseDAO 构造方法出错了,可能的原因是没有指定<>中的类型");
filters
-CharacterEncodingFilter
通过过滤器设置编码规则
-OpenSessionInViewFilter
通过过滤器进行事务操作
IOC
-BeanFactory
Bean工厂接口,getBean方法
-ClassPathXmlApplicationContext
根据ApplicationContext.xml配置文件中配置的bean节点和property属性
将bean实例对象保存到map容器中,
我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转
各property属性对应的都按配置文件中的关系实例化了,并保存在beanMap中;
我们将代码修改成FruitService fruitService = null ; 然后,在配置文件中配置: <bean id="fruit" class="FruitController"> <property name="fruitService" ref="fruitService"/> </bean>
-ContextLoaderListener
当创建servletcontext时,ServletContextListener
接口可以接收到相应通知
ContextLoaderListener
是ServletContextListener
接口的实现类,其中的contextInitialized
方法通过监听上下文启动,在上下文启动的时候去创建IOC容器,然后将其保存到application作用域,后面中央控制器再从application作用域中去获取IOC
容器;之前是在DispatcherServlet
中获取。
具体过程如下:
①Tomcat在启动时,读取web.xml配置文件
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
其中有我们设置好的初始化参数contextConfigLocation对应applicationContext.xml
②contextInitialized(ServletContextEvent servletContextEvent)方法监听到ServletContext被加载,此时创建ServletContext对象,获取上下文的初始化参数,
ServletContext application = servletContextEvent.getServletContext();
String path = application.getInitParameter("contextConfigLocation");
也就得到了applicationContext.xml配置文件
③通过applicationContext.xml配置文件创建IOC容器(里面有各层直接的依赖关系)
BeanFactory beanFactory = new ClassPathXmlApplicationContext(path);
④将IOC容器放入application作用域(ServletContext对象)中
application.setAttribute("beanFactory",beanFactory);
还有以下监听器类:
监听器
1) ServletContextListener - 监听ServletContext对象的创建和销毁的过程
2) HttpSessionListener - 监听HttpSession对象的创建和销毁的过程
3) ServletRequestListener - 监听ServletRequest对象的创建和销毁的过程
4) ServletContextAttributeListener - 监听ServletContext的保存作用域的改动(add,remove,replace)
5) HttpSessionAttributeListener - 监听HttpSession的保存作用域的改动(add,remove,replace)
6) ServletRequestAttributeListener - 监听ServletRequest的保存作用域的改动(add,remove,replace)
7) HttpSessionBindingListener - 监听某个对象在Session域中的创建与移除
8) HttpSessionActivationListener - 监听某个对象在Session域中的序列化和反序列化
4. ServletContextListener的应用 - ContextLoaderListener
一个请求对应一个线程;一个线程对应一个threadlocalMap,对应多个threadlocal;一个threadlocal对应一个值.
反之如果等用到这些工具时候再做它的准备工作,响应时间就会变慢。spring会讲到
myspringmvc
-DispatcherServlet
在此系统中是唯一一个被定义为Servlet的类,它来处理web请求与相应。
通过注解@WebServlet("*.do")
, URL 映射表 urlPattern 为 *.do,所有以.do结尾的URL都会到达DispatcherServlet,
其更具体的作用如下:
从application作用域(ServletContext对象)中获取ContextLoaderListener已经创建好的BeanFactory对象
②service(HttpServletRequest request, HttpServletResponse response)
使前端发送过来的HTTP请求与后端自定义的各业务组件进行对接
后端业务组件处理完成后,DispatcherServlet会对以redirect:
开头的返回值进行截取如("redirect:topic.do?operate=topicDetail&id="+topicId),然后重定向以再次做出相应处理;
对不是redirect:
开头的返回值,直接执行视图渲染操作super.processTemplate("login",request,response);
此时 ViewBaseServlet 中的 processTemplate 方法会执行,会帮助我们设置前缀、设置后缀、封装成 templateEngine 引擎,然后这个引擎帮我们工作,执行 process 方法 ,效果是:
在 “login” 这个字符串前面拼接 "/" (其实就是配置文件中 view-prefixe 配置的值)
在"login"这个字符串后面拼接 ".html" (其实就是配置文件中 view-suffix 配置的值)
最后进行服务器转发,属于内部转发。
-DispatcherServletException
自定义的运行时异常
-PageController
public class PageController {
public String page(String page){
return page ; // frames/left
}
}
用于处理operate 的值为page的请求
使thymeleaf能够渲染登录后的login.html页面,而非是静态的login.html
-ViewBaseServlet
thymeleaf组件,
processTemplate()方法用于解析首页页面上的Thymeleaf元素。
trans
-TransactionManager
用于事务管理的类,三个静态方法,控制事务的开启-提交-回滚
在过滤器OpenSessionInViewFilter中被调用
util
-StringUtil
判断字符串是否为null或者""
2 新建配置文件applicationContext.xml
或者可以不叫这个名字,在web.xml中指定文件名
3.在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细节,比如以下Service
import com.atguigu.qqzone.dao.ReplyDAO; import com.atguigu.qqzone.pojo.HostReply; import com.atguigu.qqzone.pojo.Reply; import com.atguigu.qqzone.pojo.Topic; import com.atguigu.qqzone.pojo.UserBasic; import com.atguigu.qqzone.service.HostReplyService; import com.atguigu.qqzone.service.ReplyService; import com.atguigu.qqzone.service.UserBasicService; import java.util.List; public class ReplyServiceImpl implements ReplyService { private ReplyDAO replyDAO ; //此处引入的是其他POJO对应的Service接口,而不是DAO接口 //其他POJO对应的业务逻辑是封装在service层的,我需要调用别人的业务逻辑方法,而不要去深入考虑人家内部的细节 private HostReplyService hostReplyService ; private UserBasicService userBasicService ; @Override public List<Reply> getReplyListByTopicId(Integer topicId) { List<Reply> replyList = replyDAO.getReplyList(new Topic(topicId)); for (int i = 0; i < replyList.size(); i++) { Reply reply = replyList.get(i); //1.将关联的作者设置进去 UserBasic author = userBasicService.getUserBasicById(reply.getAuthor().getId()); reply.setAuthor(author); //2.将关联的HostReply设置进去 HostReply hostReply = hostReplyService.getHostReplyByReplyId(reply.getId()); reply.setHostReply(hostReply); } return replyList ; } @Override public void addReply(Reply reply) { replyDAO.addReply(reply); } @Override public void delReply(Integer id) { //1.根据id获取到reply Reply reply = replyDAO.getReply(id); if(reply!=null){ //2.如果reply有关联的hostReply,则先删除hostReply HostReply hostReply = hostReplyService.getHostReplyByReplyId(reply.getId()); if(hostReply!=null){ hostReplyService.delHostReply(hostReply.getId()); } //3.删除reply replyDAO.delReply(id); } } @Override public void delReplyList(Topic topic) { List<Reply> replyList = replyDAO.getReplyList(topic); if(replyList!=null){ for(Reply reply : replyList){ delReply(reply.getId()); } } } }
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="get"> <input type="hidden" name="operate" value="login"/> </form>
③ 在表单中,组件的name属性和Controller中方法的参数名一致 <input type="text" name="loginId" /> public String login(String loginId , String pwd , HttpSession session){ ④ 另外,需要注意的是: Controller中的方法中的参数不一定都是通过请求参数获取的 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.修改BaseDAO,让其支持properties文件以及druid数据源连接池
讲解了两种方式:
-
直接自己配置properties,然后读取,然后加载驱动.....
driverClassName=com.mysql.jdbc.Driver url=jdbc:mysql:jdbc:mysql://localhost:3306/qqzonedb?useUnicode=true&characterEncoding=utf-8&useSSL=false username=root password=123456 initialSize=10 maxActive=20 maxWait=1000 filters=wall
-
使用druid连接池技术,那么properties中的key是有要求的
生成myssm的jar包
此处爆红的,去课件里找名为《idea中导出jar包》的文档,下载插件,使用插件导出jar就没问题了,不要使用idea原生build就可以识别类了!!!!