Javaweb总结-目前开发Javaweb的套路梳理

前置知识

前置准备

知识准备

已掌握JavaSE/MySQL/JDBC+HTML/CSS/JavaScript基础

并已完成了Javaweb前置知识的学习

01-JavaWeb-HTML初识

02-JavaWeb-CSS初识

03-JavaWeb-JavaScript初识

04-JavaScript基础应用-鼠标悬浮/离开表格格式变化

05-JavaWeb-Tomcat8安装、Servlet初识

06-JavaWeb-Servlet方法/生命周期、HTTP/会话session

07-JavaWeb-视图模板技术Thymeleaf的使用

08-JavaWeb-Servlet保存作用域

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

16-JavaWeb阶段性项目2:QQZone项目梳理

资源准备

尚硅谷丨2022版JavaWeb教程视频

教学资源

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中

closeConn:关闭从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>

listeners

-ContextLoaderListener

当创建servletcontext时,ServletContextListener接口可以接收到相应通知

ContextLoaderListenerServletContextListener接口的实现类,其中的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,

所以它被称为中央控制器。

其更具体的作用如下:

①init()

从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文件中配置

  1. 配置前缀和后缀,这样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>

     

  2. 配置监听器要读取的参数,目的是加载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中步骤大致分为:

  1. 从application作用域获取IOC容器

    1. 解析servletPath , 在IOC容器中寻找对应的Controller组件

    2. 准备operate指定的方法所要求的参数

    3. 调用operate指定的方法

    4. 接收到执行operate指定的方法的返回值,对返回值进行处理 - 视图处理

8)为什么DispatcherServlet能够从application作用域获取到IOC容器? ContextLoaderListener在容器启动时会执行初始化任务,而它的操作就是:

  1. 解析IOC的配置文件,创建一个一个的组件,并完成组件之间依赖关系的注入

  2. 将IOC容器保存到application作用域

5.修改BaseDAO,让其支持properties文件以及druid数据源连接池

讲解了两种方式:

  1. 直接自己配置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
  2. 使用druid连接池技术,那么properties中的key是有要求的

生成myssm的jar包

此处爆红的,去课件里找名为《idea中导出jar包》的文档,下载插件,使用插件导出jar就没问题了,不要使用idea原生build就可以识别类了!!!!

posted @ 2022-08-19 23:59  Fancy[love]  阅读(96)  评论(0编辑  收藏  举报