Javaweb

该博客围绕Java Web开发展开,介绍了CS与BS架构,讲解了Tomcat、Servlet相关知识,包括编码问题、继承关系和生命周期等。还涉及HTTP协议解析、会话管理、Thymeleaf模板引擎,以及MVC、IOC等设计模式,同时提及过滤器、事务管理、监听器和数据库范式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

CS与BS

    CS:客户端服务器架构模式
        充分利用客户端机器资源,减轻服务器负荷
            一部分安全要求不高的计算任务放在客户端执行,不需要把计算和存储都在服务端执行,从而减轻服务端压力,也能够减轻网络负荷
        需要安装;升级维护成本高
    BS:浏览器服务器架构模式
        客户端不需要安装;维护成本低
        所有计算和存储都在服务端,服务器负荷较重;在服务端计算之后把结果给客户端,会进行频繁的数据通信,网络负荷较重

Tomcat

    server服务端:tomcat是一个webContainer
    Tomcat目录:
        bin 可执行文件
        conf 配置文件
        lib 依赖
        logs
        webapps项目部署目录
        work工作目录
        temp

Servlet

    Server服务端  +  let小应用程序
    作用:
        获取客户端发送的数据
        调用DAO
    步骤:
        继承HttpServlet重写doPost/doGet
        1. 用户发请求,action=add
        2. 项目中,web.xml中找到url-pattern = /add
        3. 找servlet-name = addFruit
        4. 找和servlet-mapping中servlet-name一致的servlet
        5. servlet-class -> com.example.controller.FruitController
        6. 用户发送的是post请求(method=post) , 因此 tomcat会执行AddServlet中的doPost方法
    web.xml

            <servlet>
                <servlet-name>addFruit</servlet-name>
                <servlet-class>com.example.controller.FruitController</servlet-class>
            </servlet>
            <servlet-mapping>
                <servlet-name>addFruit</servlet-name>
                <url-pattern>/add</url-pattern>
            </servlet-mapping>
    java.lang.ClassNotFoundException: org.apache.ibatis.io.Resources
        原因:没有在设置中将项目设置为输出目录。
    Artifact中:
       web Application:Exploded web程序部署包,解压后的war包
       web Application:Archive web程序的压缩包,在tomcat webapp目录下会自动解压
    如果网址是:http://localhost:8080/,那么默认访问index.html接着index.htm index.jsp,在tomcat web.xml文件的welcome-file-list中设置,如果这几个文件都没有404。也可以在自己的web.xml中设置欢迎页

编码问题

    Post方式下,设置编码,防止中文乱码
        req.setCharacterEncoding("utf-8");必须放在获取参数之前
    Get方式不需要设置编码(基于tomcat8)

servlet的继承关系

    1.继承关系
        Servlet接口
            GenericServlet抽象类
                HttpServlet抽象子类
    2.相关方法
        javax.servlet.Servlet接口
            init()
            service(ServletRequest,ServletResponse)
                当请求过来时,service方法自动响应(tomcat容器调用的)
            destroy()
        javax.servlet.GenericServlet
            service(ServletRequest,ServletResponse)任然是抽象的
        javax.servlet.HttpServlet
            service(ServletRequest,ServletResponse)
                1.req.getMethod() 获取请求的方式“GET/POST等”
                2.各种if判断,根据请求方式不同决定调用不同的do方法(doGet之类的)
                3.do方法默认是405的实现风格-要求子类实现对应do方法,不然会报405错误

Servlet生命周期

    生命周期:从出生到死亡的过程,对应servlet的三个方法:init() service() destroy()
    默认情况下:
        第一次接收请求时,servlet进行实例化(调用构造方法),初始化(init()),然后服务(调用service())
            提高了启动速度,但是第一次请求时间长(如果要提高响应速度,应该设置servlet初始化时机)
        从第二次请求开始,每一次都是服务
        容器关闭,servlet实例被销毁,调用destroy()
    servlet初始化请求:
        默认第一次接收请求时servlet实例化
        <load-on-startup>1</load-on-startup>设置servlet实例化时机,数字越小,启动越靠前
    servlet在容器中是 单例的、线程不安全的
        单例:所有请求都是同一个实例去响应
        线程不安全:一个线程要根据这个实例中某个成员变量的值去做逻辑判断。但在中间某个时机,另一个线程改变了该成员变量的值,改变了这个成员变量的执行路径
            启示:尽量不要在servlet中定义成员变量。如果不得不定义,尽量不要修改逻辑变量的值,不要根据成员变量的值做逻辑判断

HTTP

    超文本传输协议
    http是无状态的
    http请求响应:
        请求:
            请求行:包含 请求方式 请求url 请求的协议(一般是http1.1)
            请求头:对本次请求进行详细说明,客户端对服务器的自我介绍 cookie在里面
            请求体:
                get方式:没有请求体,但是有一个queryString,在地址后面
                post方式:有请求体,form data
                json方式:有请求体,request payload(json: raw 生的,未处理的数据)
        响应:
            响应行:包含 协议 状态码 响应状态
            响应头:包含了服务器的信息,服务器发送给浏览器的描述信息(内容的媒体类型、编码、内容长度等)
            响应体:响应的实际内容

Http协议解析 

http :Hyper Text Transfer Protocol 超文本传输协议,规定浏览器与服务器之间数据传输的规则 
     特点: Tcp 
        请求响应模型:一次请求一次响应 
        http协议是无状态的协议:对于事务没有记忆能力,每次请求响应独立 
     缺点:多次请求之间不能共享数据 
     优点:速度快 
http请求协议(请求数据的格式) 
        请求行:请求数据第一行(请求方式get 资源路径 协议http1.1) 
        请求头:第二行开始,格式key,value 
        请求体:Post请求有,存放请求参数 
                get请求:请求参数在请求行资源路径中,没有请求体,get请求大小是有限制的 
                post请求,请求参数在请求体中 
http响应格式(响应数据的格式) 
        响应行:第一行(协议HTTP/1.1 状态码200 描述OK) 
                1xx:响应中,临时状态码,表示请求已经接收,告诉客户端应该继续请求或者如果已经完成则忽略 
                2xx:成功 
                3xx:让客户端重定向 
                4xx:客户端错误:请求不存在的资源,客户端未被授权,禁止访问等 
                5xx:服务端错误:程序抛出异常等 
        响应头:格式key,value 
        响应体:响应数据 
Apache Tomcat web服务器(web容器,servlet容器) 
        软件程序,对http协议的操作进行封装,提供网上信息浏览服务 
Tomcat识别Servlet,Tomcat是Servlet容器 
前端控制器:DispatcherServlet,springMVC提供,实现Servlet接口 
        请求数据对象:HttpServletRequest 
        响应数据对象:HttpServletResponse 
http-DispatcherServlet(封装HttpServletRequest)-xxxController
html文件放于static中,public或resource


会话

   http是无状态的:每次请求服务器无法区分是否来源于同一个客户端
   通过会话跟踪技术解决无状态的问题
   常用API:
        request.getSession() 获取当前会话,没有创建一个新的会话
        request.getSession(false) 没有返回null,不会创建新的
        session.getMaxInactiveInterval() session非激活间隔时长 session的有效时间,默认30分钟
        session.invalidate() 强制会话失效
   session保存作用域:
        session.setAttribute(key,value)向当前session保存作用域保存一个数据
服务器内部转发与客户端重定向:
    服务器内部转发:request.getRequestDispatcher(...).forward(request,response)
        一次请求响应的过程
    客户端重定向: response.sendRedirect(...)
        多次请求响应

Thymeleaf

    视图模板技术
    步骤:
        添加jar包
        新建一个Servlet类ViewBaseServlet
        在web.xml配置
            配置前缀 view-prefix
            配置后缀 view-suffix
        使得自定义Servlet继承ViewBaseServlet
    将逻辑视图名称拼接成物理视图名称

    <!--配置Thymeleaf上下文参数-->
    <context-param>
        <param-name>view-prefix</param-name>
        <param-value>/</param-value>
        <!--  /代表webapp根目录      -->
    </context-param>
    <context-param>
        <param-name>view-suffix</param-name>
        <param-value>.html</param-value>
    </context-param>
Servlet从3.0开始支持注解方式的注册,不需要在web.xml注册
    @WebServlet()
    <!--表示不让@webServlet拦截静态资源-->
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.css</url-pattern>
    </servlet-mapping>
    <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.js</url-pattern>
    </servlet-mapping>

保存作用域

     原始情况下,四个保存作用域:page,request,session,application
     page:页面级别,几乎不用
     request:一次请求响应范围
        注意:客户端重定向与服务器端转发
     session:一次会话范围
     application:应用级别req.getServletContext()

     打印两遍是因为tomcat中我们勾选了after launch,勾选后这个操作相当于自动提前发了一个请求,你可验证一下,你打印两次sessionid,这两个sessionid不同,说明是两个会话
 路径问题:
    相对路径:
    绝对路径:eg:http://localhost:8080/context-root/index.html
    <base href="http://localhost:8080/context-root/* />作用:当前页面所有路径以其为基础
    thymeleaf中:th:href=@{}就相当于base标签


@WebServlet("/index")
public class FruitController extends ViewBaseServlet {
    private FruitService fruitService = new FruitServiceImpl();

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        System.out.println("doPost");
        fruitService.add(req, resp);
        super.processTemplate("index", req, resp);
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        System.out.println("doGet");

            List<Fruit> fruitList = fruitService.getFruitList();
            req.getSession().setAttribute("fruitList", fruitList);

        //此处的视图名称是 index
        //那么thymeleaf会将这个 逻辑视图名称 对应到 物理视图 名称上去
        //逻辑视图名称 :   index
        //物理视图名称 :   view-prefix + 逻辑视图名称 + view-suffix
        //所以真实的视图名称是:      /       index       .html
        super.processTemplate("index", req, resp);
    }
}
优化:

 

@Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
        String operator = req.getParameter("operate");
        if (operator == null || operator.equals("")) {
            operator = "index";
        }

        Method[] methods = this.getClass().getDeclaredMethods();
        for (int i = 0; i < methods.length; i++) {
            if(methods[i].getName().equals(operator)) {

                try {
                    methods[i].invoke(this, req, resp);
                    return;
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            }
          if(i==methods.length-1)throw new RuntimeException("operate非法");
        }
/*        switch (operator) {
            case "index":
                index(req, resp);
                break;
            case "edit":
                edit(req,resp);
                break;
            case "update":
                update(req,resp);
                break;
            case "add":
                add(req,resp);
                break;
            case "delete":
                delete(req,resp);
                break;
            default:
                throw new RuntimeException("operator非法");
        }
        */
    }

xml

    概念:
        html:超文本标记语言
        xml:可扩展标记语言
        html是xml的一个子类
    xml组成:
        xml声明
            <?xml version="1.0" encoding="UTF-8" ?>
        DTD文档类型定义
        xml正文
    节点分类:Node接口
        元素节点:Element子接口 标签<bean/> 单标签没有子节点
        文本节点:Text子接口   双标签之间的文本 空白 注释 等
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="fruit" class="com.example.controller.FruitController"/>
</beans>

-parameters  java虚拟机编译的时候附带方法参数名称

DispatcherServlet

   DispatcherServlet这个类的工作分为两大部分:
   1.根据url定位到能够处理这个请求的controller组件:
    1)从url中提取servletPath : /fruit.do -> fruit
    2)根据fruit找到对应的组件:FruitController , 这个对应的依据我们存储在applicationContext.xml中
      <bean id="fruit" class="com.atguigu.fruit.controllers.FruitController/>
      通过DOM技术我们去解析XML文件,在中央控制器中形成一个beanMap容器,用来存放所有的Controller组件
    3)根据获取到的operate的值定位到我们FruitController中需要调用的方法
   2.调用Controller组件中的方法:
    1) 获取参数
       获取即将要调用的方法的参数签名信息: Parameter[] parameters = method.getParameters();
       通过parameter.getName()获取参数的名称;
       准备了Object[] parameterValues 这个数组用来存放对应参数的参数值
       另外,我们需要考虑参数的类型问题,需要做类型转化的工作。通过parameter.getType()获取参数的类型
    2) 执行方法
       Object returnObj = method.invoke(controllerBean , parameterValues);
    3) 视图处理
       String returnStr = (String)returnObj;
       if(returnStr.startWith("redirect:")){
        ....
       }else if.....
package com.example.ssm.springmvc;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.HashMap;
import java.util.Map;

@WebServlet("/")
public class DispatcherServlet extends ViewBaseServlet {
    private Map<String, Object> beanMap = new HashMap<>();

    public DispatcherServlet() {
        try {
            //1.解析xml获取bean的id和class
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
            //1.1创建documentBuilderFactory
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            //1.2创建documentBuilder
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            //1.3创建document
            Document document = documentBuilder.parse(inputStream);
            //1.4获取所有的bean节点
            NodeList beanNodeList = document.getElementsByTagName("bean");
            for (int i = 0; i < beanNodeList.getLength(); i++) {
                Node beanNode = beanNodeList.item(i);
                if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element beanElement = (Element) beanNode;
                    //获取id和class属性
                    String beanId = beanElement.getAttribute("id");
                    String className = beanElement.getAttribute("class");
                    //得到他的实例
                    Object beanObject = Class.forName(className).newInstance();
                    beanMap.put(beanId, beanObject);
                }
            }


        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }


    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        req.setCharacterEncoding("utf-8");
       /*
         得到相对请求路径
         第一步:/fruit 切割为 fruit(servletPath)
         第二步:通过applicationContext.xml配置,将fruit与FruitController对应起来(放在map中)
         第三步:通过servletPath获取Map的value,value就是对应的Controller对象,然后根据operate通过反射调用不同方法
        */
        String servletPath = req.getServletPath();
        servletPath = servletPath.substring(1);
        Object controllerBeanObject = beanMap.get(servletPath);

        String operator = req.getParameter("operate");
        if (operator == null || operator.equals("")) {
            operator = "index";
        }

        try {
            Method[] methods = controllerBeanObject.getClass().getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().equals(operator)) {
                    //1.统一获取请求参数

                    //获取当前方法参数,返回参数数组
                    Parameter[] parameters = method.getParameters();
                    //parameterValues用来承载参数的值
                    Object[] parameterValues= new Object[parameters.length];
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];

                        String parameterName = parameter.getName();
                        Class<?> parameterType = parameter.getType();
                        if ("req".equals(parameterName)) {
                            parameterValues[i] = req;
                        } else if ("resp".equals(parameterName)) {
                            parameterValues[i] = resp;
                        } else if ("session".equals(parameterName)) {
                            parameterValues[i] = req.getSession();
                        } else if ("java.lang.Integer".equals(parameterType.getName())) {
                            String parameterString = req.getParameter(parameterName);
                            if (parameterString != null)
                                parameterValues[i] = Integer.parseInt(parameterString);
                        } else if ("java.lang.String".equals(parameterType.getName())) {
                            parameterValues[i] = req.getParameter(parameterName);
                        } else {
                            Object instance = parameterType.newInstance();
                            Field[] fields = instance.getClass().getDeclaredFields();
                            for(Field field:fields){
                                field.setAccessible(true);
                                String fieldValueString = req.getParameter(field.getName());
                                if(fieldValueString!=null){
                                    if(field.getType().getName().equals("java.lang.Integer")){
                                        field.set(instance,Integer.parseInt(fieldValueString));
                                    }else{
                                        field.set(instance,fieldValueString);
                                    }
                                }
                            }
                            parameterValues[i]=instance;
                        }
                    }

                    //2.controller组件中的方法调用
                    method.setAccessible(true);
                    Object methodReturnObject = method.invoke(controllerBeanObject,parameterValues);
                    //3.视图处理
                    if (methodReturnObject != null) {
                        String methodReturnValue = (String) methodReturnObject;
                        if (methodReturnValue.startsWith("redirect:")) {
                            resp.sendRedirect(methodReturnValue.substring("redirect:".length()));
                        } else {
                            super.processTemplate(methodReturnValue, req, resp);
                        }
                    }
                }
            }

        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

Servlet生命周期:实例化 初始化 服务 销毁

    Servlet初始化有两个重载方法:
        public void init(){
        }
        public void init(ServletConfig config){
            this.config=config;
            init();
        }
    如果我们需要在Servlet初始化时做一些准备工作,考虑重写init(),可以通过以下步骤获取初始话数据:
        1.配置xml文件或者使用注解
                <servlet>
                    <servlet-name></servlet-name>
                    <servlet-class></servlet-class>
                    <init-param>
                        <param-name></param-name>
                        <param-value></param-value>
                    </init-param>
                </servlet>
                <servlet-mapping>
                    <servlet-name></servlet-name>
                    <url-pattern></url-pattern>
                </servlet-mapping>
                注解方式配置:@WebServlet(
                    urlPatterns={},
                    initParams={
                        @WebInitParam(name=key,value=value)
                    }
                )
        2.获取config对象:ServletConfig config=getServletConfig();
        3.获取初始化参数值:config.getInitParameter(key)
    ServletContext Servlet上下文:
        获取ServletContext的方式:
            1.可以在初始化方法中获取
                ServletContext servletContext=getServletContext();
            2.可以在服务方法中获取
                request.getServletContext();
                session.getServletContext();
        获取Servlet上下文值:
            1.配置xml文件
                <context-param>
                    <param-name></param-name>
                    <param-value></param-value>
                </context-param>
            2.获取初始化值
                servletContext.getInitParameter();

MVC

    model:数据访问模型DAO;业务逻辑模型BO;值对象模型POJO/VO;数据传输对象DTO
    view:数据展示、用户交互的一个界面
    controller:  接收客户端的请求,业务功能借助于模型组件

IOC

    耦合/依赖
        高内聚低耦合:层内部的组成应该是高度耦合的,而层与层之间关系应该是低耦合的
    IOC控制反转/DI依赖注入
        控制反转:
            1) 之前在Servlet中,我们创建service对象 , FruitService fruitService = new FruitServiceImpl();
               这句话如果出现在servlet中的某个方法内部,那么这个fruitService的作用域(生命周期)应该就是这个方法级别;
               如果这句话出现在servlet的类中,也就是说fruitService是一个成员变量,那么这个fruitService的作用域(生命周期)应该就是这个servlet实例级别
            2) 之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产生fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory中
               因此,我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转

        依赖注入:
        1) 之前我们在控制层出现代码:FruitService fruitService = new FruitServiceImpl();
           那么,控制层和service层存在耦合。
        2) 之后,我们将代码修改成FruitService fruitService = null ;
           然后,在配置文件中配置:
           <bean id="fruit" class="FruitController">
                <property name="fruitService" ref="fruitService"/>
           </bean>

<?xml version="1.0" encoding="UTF-8" ?>
<beans>
    <bean id="fruitService" class="com.example.service.Impl.FruitServiceImpl"/>
    <!-- id表示将来servletPath中涉及的名字是fruit,那就用class值的类FruitController来处理-->
    <bean id="fruit" class="com.example.controller.FruitController">
        <!-- property标签用来表示FruitController中的属性,name表示属性名,ref表示引用其他bean的id  -->
        <property name="fruitService" ref="fruitService"/>
    </bean>
</beans>
public interface BeanFactory {
    Object getBean(String id);
}
package com.example.ssm.ioc;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;

public class ClassPathXmlApplicationContext implements BeanFactory {
    private Map<String, Object> beanMap = new HashMap<>();

    public ClassPathXmlApplicationContext() {
        try {
            //1.解析xml获取bean的id和class
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
            //1.1创建documentBuilderFactory
            DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
            //1.2创建documentBuilder
            DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
            //1.3创建document
            Document document = documentBuilder.parse(inputStream);
            //1.4获取所有的bean节点
            NodeList beanNodeList = document.getElementsByTagName("bean");
            for (int i = 0; i < beanNodeList.getLength(); i++) {
                Node beanNode = beanNodeList.item(i);
                if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
                    Element beanElement = (Element) beanNode;
                    //获取applicationContext.xml标签的id和class属性
                    String beanId = beanElement.getAttribute("id");
                    String className = beanElement.getAttribute("class");
                    //得到他的实例
                    Object beanClass = Class.forName(className).newInstance();
                    //将bean实例对象保存到map容器中
                    beanMap.put(beanId, beanClass);
                    //由fruit获取controller并进行映射在DispatchersServlet中完成,下一步将组装bean和bean之间的依赖关系
                }
            }

            //组装bean之间的依赖关系
            for (int i = 0; i < beanNodeList.getLength(); i++) {
                Node beanNode = beanNodeList.item(i);
                if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
                    //要得到元素节点bean
                    Element beanElement = (Element) beanNode;
                    String beanId = beanElement.getAttribute("id");
                    //得到的他子节点List
                    NodeList beanChildNodes = beanElement.getChildNodes();
                    for (int j = 0; j < beanChildNodes.getLength(); j++) {
                        //去除单个子节点
                        Node beanChildNode = beanChildNodes.item(j);
                        //得到元素节点并且名称是property
                        if(beanChildNode.getNodeType()==Node.ELEMENT_NODE&&"property".equals(beanChildNode.getNodeName())){
                            Element propertyElement = (Element) beanChildNode;
                            String propertyName = propertyElement.getAttribute("name");
                            String propertyRef = propertyElement.getAttribute("ref");
                            //下一步根据ref找到对象,给属性名为propertyName的赋值
                            //1.找到ref对应的实例 eg:根据fruitService找到new FruitServiceImpl
                            Object refObject = beanMap.get(propertyRef);
                            //2.将refObj设置到当前bean对应的实例property属性上去 eg:找到FruitController
                            Object beanObj = beanMap.get(beanId);
                            //3.根据propertyName获取同名属性
                            Field propertyField = beanObj.getClass().getDeclaredField(propertyName);
                            propertyField.setAccessible(true);
                            propertyField.set(beanObj,refObject);
                        }
                    }
                }
            }
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String id) {
        return beanMap.get(id);
    }
}
@WebListener
//监听上下文启动,在启动的时候创建IOC容器,然后将其保存到application作用域中,后面中央控制器从application作用域中获取IOC容器
public class ContextLoaderListener implements ServletContextListener {
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        BeanFactory beanFactory = new ClassPathXmlApplicationContext();
        ServletContext application = servletContextEvent.getServletContext();
        application.setAttribute("beanFactory",beanFactory);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}
package com.example.ssm.springmvc;

import com.example.ssm.ioc.BeanFactory;
import com.example.ssm.ioc.ClassPathXmlApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;

@WebServlet("/")
public class DispatcherServlet extends ViewBaseServlet {

    private BeanFactory beanFactory;

    @Override
    public void init() throws ServletException {
        super.init();
        //之前是在此处创建IOC容器,也就是IOC随着中央控制器初始化而创建,但是IOC应该在上下文被创建的时侯就准备好,所以优化在监听器中进行
        //beanFactory = new ClassPathXmlApplicationContext();
        ServletContext application = getServletContext();
        Object beanFactoryObject = application.getAttribute("beanFactory");
        if (beanFactoryObject != null) {
            beanFactory = (BeanFactory) beanFactoryObject;
        } else {
            throw new RuntimeException("IOC容器获取失败");
        }
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        req.setCharacterEncoding("utf-8");
       /*
         得到相对请求路径
         第一步:/fruit 切割为 fruit(servletPath)
         第二步:通过applicationContext.xml配置,将fruit与FruitController对应起来(放在map中)
         第三步:通过servletPath获取Map的value,value就是对应的Controller对象,然后根据operate通过反射调用不同方法
        */
        String servletPath = req.getServletPath();
        servletPath = servletPath.substring(1);
        Object controllerBeanObject = beanFactory.getBean(servletPath);

        String operator = req.getParameter("operate");
        if (operator == null || operator.equals("")) {
            operator = "index";
        }

        try {
            Method[] methods = controllerBeanObject.getClass().getDeclaredMethods();
            for (Method method : methods) {
                if (method.getName().equals(operator)) {
                    //1.统一获取请求参数

                    //获取当前方法参数,返回参数数组
                    Parameter[] parameters = method.getParameters();
                    //parameterValues用来承载参数的值
                    Object[] parameterValues = new Object[parameters.length];
                    for (int i = 0; i < parameters.length; i++) {
                        Parameter parameter = parameters[i];

                        String parameterName = parameter.getName();
                        Class<?> parameterType = parameter.getType();
                        if ("req".equals(parameterName)) {
                            parameterValues[i] = req;
                        } else if ("resp".equals(parameterName)) {
                            parameterValues[i] = resp;
                        } else if ("session".equals(parameterName)) {
                            parameterValues[i] = req.getSession();
                        } else if ("java.lang.Integer".equals(parameterType.getName())) {
                            String parameterString = req.getParameter(parameterName);
                            if (parameterString != null)
                                parameterValues[i] = Integer.parseInt(parameterString);
                        } else if ("java.lang.String".equals(parameterType.getName())) {
                            parameterValues[i] = req.getParameter(parameterName);
                        } else {
                            Object instance = parameterType.newInstance();
                            Field[] fields = instance.getClass().getDeclaredFields();
                            for (Field field : fields) {
                                field.setAccessible(true);
                                String fieldValueString = req.getParameter(field.getName());
                                if (fieldValueString != null) {
                                    if (field.getType().getName().equals("java.lang.Integer")) {
                                        field.set(instance, Integer.parseInt(fieldValueString));
                                    } else {
                                        field.set(instance, fieldValueString);
                                    }
                                }
                            }
                            parameterValues[i] = instance;
                        }
                    }

                    //2.controller组件中的方法调用
                    method.setAccessible(true);
                    Object methodReturnObject = method.invoke(controllerBeanObject, parameterValues);
                    //3.视图处理
                    if (methodReturnObject != null) {
                        String methodReturnValue = (String) methodReturnObject;
                        if (methodReturnValue.startsWith("redirect:")) {
                            resp.sendRedirect(methodReturnValue.substring("redirect:".length()));
                        } else {
                            super.processTemplate(methodReturnValue, req, resp);
                        }
                    }
                    return;
                }
            }

        } catch (InvocationTargetException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
    }

}

    1. 过滤器Filter
    2. 事务管理(TransactionManager、ThreadLocal、OpenSessionInViewFilter)
    3. 监听器(Listener , ContextLoaderListener)

过滤器Filter

    1) Filter也属于Servlet规范
    2) Filter开发步骤:新建类实现Filter接口,然后实现其中的三个方法:init、doFilter、destroy
       配置Filter,可以用注解@WebFilter,也可以使用xml文件 <filter> <filter-mapping>
    3) Filter在配置时,和servlet一样,也可以配置通配符,例如 @WebFilter("*.do")表示拦截所有以.do结尾的请求
    4) 过滤器链
       1)执行的顺序依次是: A B C demo03 C2 B2 A2
       2)如果采取的是注解的方式进行配置,那么过滤器链的拦截顺序是按照全类名的先后顺序排序的
       3)如果采取的是xml的方式进行配置,那么按照配置的先后顺序进行排序

事务管理

   1) 涉及到的组件:
     - OpenSessionInViewFilter
     - TransactionManager
     - ThreadLocal
     - ConnUtil
     - BaseDAO

   2) 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();
     }

监听器WebListener

    接口:
    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

数据库的范式

  1) 第一范式:列不可再分
  2) 第二范式:一张表只表达一层含义(只描述一件事情)
  3) 第三范式:表中的每一列和主键都是直接依赖关系,而不是间接依赖
数据库设计的范式和数据库的查询性能很多时候是相悖的,我们需要根据实际的业务情况做一个选择:
  - 查询频次不高的情况下,我们更倾向于提高数据库的设计范式,从而提高存储效率
  - 查询频次较高的情形,我们更倾向于牺牲数据库的规范度,降低数据库设计的范式,允许特定的冗余,从而提高查询的性能
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值