• JavaWeb中篇Servlet-IOC笔记


    说明:

        本篇博客介绍了Servlet业务层(通过创建业务层,是的以前的Controller在调用的时候可以通过Service来间接调用DAO,目的是优化项目中的业务,使代码更简洁),以及IOC(控制反转和依赖注入)的详细介绍,其中3.3实现控制反转是一个重点也是一个难点,需要好好理解。

    1. Servlet的初始化设置以及相关配置

    1.1 Servlet的初始化设置方式

    Servlet生命周期:实例化、初始化、服务、销毁四个阶段

    Servlet的两种初始化方式:
    ①init(config)
    其中带参数的方法代码如下:

       public void init(ServletConfig config) throws ServletException {
         this.config = config ;
         init();
       }
    
    • 1
    • 2
    • 3
    • 4

    ②init()

       public void init() throws ServletException{
       }
    
    • 1
    • 2

    1.2 Servlet的初始化作用和获取初始化设置的数据

    如果我们想要在Servlet初始化时做一些准备工作,那么我们可以重写init方法

    ①获取config对象:ServletConfig config = getServletConfig();
    ②获取初始化参数值: config.getInitParameter(key);

    实现代码:

    @Override
        public void init()  throws ServletException{
            ServletConfig config = getServletConfig();
            String configInitParameter = config.getInitParameter("hello");//初始化servlet config在xml文件中
            System.out.println("configInitParameter = " + configInitParameter);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    1.3 Servlet的两种配置方式

    ①在web.xml文件中配置Servlet

     <servlet>
            <servlet-name>Demo01Servlet</servlet-name>
            <servlet-class>com.lei.servlet.Demo01Servlet</servlet-class>
            <init-param>
                <param-name>hello</param-name>
                <param-value>world</param-value>
            </init-param>
        </servlet>
        <servlet-mapping>
            <servlet-name>Demo01Servlet</servlet-name>
            <url-pattern>/demo01</url-pattern>
        </servlet-mapping>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ②通过注解的方式进行配置

    @WebServlet(urlPatterns = {"/demo01"} ,
         initParams = {
             @WebInitParam(name="hello",value="world"),
         })
    
    • 1
    • 2
    • 3
    • 4

    补充:
    Servlet中的ServletContext和<context -param>

    ServletContext作用:
    通过ServletContext获取配置的上下文参数
    <context -param>
    配置文件:

      <context-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath.applicationContext.xml</param-value>
        </context-param>
    
    • 1
    • 2
    • 3
    • 4

    (1)获取ServletContext

    ①在初始化方法中: ServletContext servletContext = getServletContext();
    ②在服务方法中也可以通过request对象获取request.getServletContext();
    ③可以通过session获取:session.getServletContext()

    (2)获取初始化值
    servletContext.getInitParameter();

    实现代码:

     @Override
        public void init()  throws ServletException{
            //获取ServletContext
            ServletContext servletContext = getServletContext();
            String contextConfigLocation = servletContext.getInitParameter("contextConfigLocation");
            System.out.println("contextConfigLocation = " + contextConfigLocation);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2. 业务层介绍

    业务层包含了系统所需要的所有功能上的算法和计算过程,并与数据访问层和控制层交互。(服务端返回 数据的处理以及相应页面的变化)

    2.1 MVC详细说明

    MVC : Model(模型)、View(视图)、Controller(控制器)

    视图层:用于做数据展示以及和用户交互的的一个界面
    控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成
    模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件
        ①pojo/vo : 值对象
        ②DAO : 数据访问对象
        ③ BO : 业务对象

    2.2 区分业务对象和数据访问对象

    ①DAO中的方法都是单精度方法或者称之为细粒度方法。什么叫单精度?一个方法只考虑一个操作,比如添加,那就是insert操作、查询那就是select操作…
    ②BO中的方法属于业务方法,也实际的业务是比较复杂,因此业务方法的粒度是比较粗
        注册这个功能属于业务功能,也就是说注册这个方法属于业务方法。
        那么这个业务方法中包含了多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能的开发。

    注册

    ①检查用户名是否已经被注册 - DAO中的select操作
    ②向用户表新增一条新用户记录 - DAO中的insert操作
    ③向用户积分表新增一条记录(新用户默认初始化积分100分) - DAO中的insert操作
    ④向系统消息表新增一条记录(某某某新用户注册了,需要根据通讯录信息向他的联系人推送消息) - DAO中的insert操作
    ⑤向系统日志表新增一条记录(某用户在某IP在某年某月某日某时某分某秒某毫秒注册) - DAO中的insert操作…

    2.3 业务层实现实例

    这里仍然使用的是水果库存管理系统

    Service层
    FruitServive接口:

    public interface FruitService {
        //获取指定页面的库存列表信息
        List<Fruit> getFruitList(String keyword,Integer pageNo);
    
        //添加库存列表信息
        void addFruit(Fruit fruit);
    
        //根据id查看指定库存记录
        Fruit getFruitByFid(Integer fid);
    
        //获取总页数
        Integer getPageCount(String keyword);
    
    
        //修改特定的库存记录
        void updateFruit(Fruit fruit);
    
        //根据id删除指定的库存记录
        void deleteFruit(Integer fid);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    FruitServive实现类:
    FruitServiveImpl:

    public class FruitServiceImpl implements FruitService {
    
        private FruitDAO fruitDAO = null;
        @Override
        public List<Fruit> getFruitList(String keyword, Integer pageNo) {
            return fruitDAO.getFruitList(keyword,pageNo);
        }
    
        @Override
        public void addFruit(Fruit fruit) {
            fruitDAO.addFruit(fruit);
        }
    
        @Override
        public Fruit getFruitByFid(Integer fid) {
            return fruitDAO.getFruitByFid(fid);
        }
    
        @Override
        public Integer getPageCount(String keyword) {
            int count = fruitDAO.getFruitCount(keyword);
            int pageCount = (count+5-1)/5;
            return pageCount;
        }
    
        @Override
        public void updateFruit(Fruit fruit) {
            fruitDAO.updateFruit(fruit);
        }
    
        @Override
        public void deleteFruit(Integer fid) {
            fruitDAO.deleteFruit(fid);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    在以前的实现过程中FruitController直接调用FruitDAO实现类的方法,通过业务层的设计转换为:
    Controller调用Service--------->Service调用FruitDAO实现类

    在这里插入图片描述
    在这里插入图片描述

    3. IOC介绍

         IOC全称Inversion of Control,翻译为“控制反转”,实现的是降低了代码的耦合度,所以在这里首先介绍耦合:

    3.1 耦合/依赖

    依赖指的是某某某离不开某某某,在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。
        在我们系统架构或者是设计的一个原则是: 高内聚低耦合
            层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合(就是没有耦合),也就是说在一个类的内部不牵扯其他的类,在项目初始化的时候将它们之间的依赖关系生成(也可理解是删除一个类对另外一个类没有影响)

    3.2 IOC - 控制反转和DI - 依赖注入

    控制反转

    ①之前在Servlet中,我们创建service对象 , FruitService fruitService = new FruitServiceImpl();     这句话如果出现在servlet中的某个方法内部,那么这个fruitService的作用域(生命周期)应该就是这个方法级别;     如果这句话出现在servlet的类中,也就是说fruitService是一个成员变量,那么这个fruitService的作用域(生命周期)应该就是这个servlet实例级别

    ②之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML产生fruitService实例,存放在beanMap中,这个beanMap在一个BeanFactory中

        因此,我们转移(改变)了之前的service实例、dao实例等等他们的生命周期。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转

    依赖注入

    ①之前我们在控制层出现代码:FruitService fruitService = new FruitServiceImpl();那么,控制层和service层存在耦合
    ②之后,我们将代码修改成FruitService fruitService = null ;
    然后,在配置文件中配置:

    <bean id="fruit" class="FruitController">
            <property name="fruitService" ref="fruitService"/>
       </bean>
    
    • 1
    • 2
    • 3

    3.3 实现控制反转

    实现控制反转的通俗点说明就是:在定义的时候修改为FruitService fruitService = null ,之后在applicationContext.xml文件中加入配置,然后在DispatcherServlet中创建BeanFactory的实例,最终在ClassPathXmlApplicationContext中组装bean之间的依赖关系

    实现步骤
    ①:修改定义的格式(见2.3的两个图)
    ②:applicationContext.xml文件中加入配置
    在bean节点内部加入一个标签property,name表示属性名(对应bean标签里面的class中对应的属性FruitDAO)ref:表示引用其他bean的id值(也是FruitDAO,之后通过)

    <beans>
        <bean id="fruitDAO" class="com.lei.fruit.dao.impl.FruitDAOImpl"/>
        <bean id="fruitService" class="com.lei.fruit.service.impl.FruitServiceImpl">
            <!--bean实现-->
            <!--property标签用来表示属性:name表示属性名(对应bean标签里面的class中对应的属性)ref:表示引用其他bean的id值-->
            <property name = "fruitDAO"  ref="fruitDAO"/>
        </bean>
        <!--bean的作用是:servletpath中设计的名字对应的是fruit,那么就是FruitController来处理-->
        <bean id="fruit" class="com.lei.fruit.controllers.FruitController">
            <property name="fruitService" ref="fruitService"/>
        </bean>
    </beans>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    ③创建BeanFactory以及在Dispatcher(中央处理器)中创建它的实例

    public interface BeanFactory {
        //根据id获取对应的实现类
        Object getBean(String id);
    }
    
    • 1
    • 2
    • 3
    • 4

    在这里插入图片描述

    ④最后一步在ClassPathXmlApplicationContext实现功能
    详细说明:
        首先获取bean节点,存放到beanMap集合中,这在Java基础篇中央处理器的使用中已经实现,接下来需要做的就是将bean节点内部的property配置进行实现以其中的一个为例。

     <bean id="fruit" class="com.lei.fruit.controllers.FruitController">
            <property name="fruitService" ref="fruitService"/>
    
    • 1
    • 2

        需要实现的是:
    (1)通过一个循环获取到当前的节点,使用beanNodeList.item(i),
    (2)判断是否是元素节点,是的话就进行强制转化变为Element类型,
    (3)通过beanElement.getChildNodes();获得当前节点的所有孩子节点(property即使bean节点的其中一个孩子节点),利用"property".equals(beanChildNode.getNodeName()判断当前的孩子节点是否就是我们要找的
    (4)获得ref和当前类【name是通过反射调用当前Class的getDeclaredField(propertyName);来获取到它的属性的】,得到的ref是fruitService
    (5)调用beanMap集合得到他的Vale值(也就是实现类)
    (6)通过id得到Fruit再利用beanMap的get得到目标类【在这里面完成控制反转】FruitController,
    (7)getClass得到Class对象调用getDeclaredField(propertyName);获得相应的Field(属性)
    (8)完成设置
    这几个步骤比较关键,具体的代码实现看下面,注释也很清楚足够理解控制反转的完整过程

     public ClassPathXmlApplicationContext(){
    
            try {
                InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
                //1.创建DocumentBuilderFactory
                DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
                //2.创建DocumentBuilder对象
                DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
                //3.创建Document对象
                Document document = documentBuilder.parse(inputStream);
    
                //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;
                        String beanId = beanElement.getAttribute("id");
                        String className = beanElement.getAttribute("class");
                        //有多个bean节点,所以对名称做了改动
                        Class beanClass = Class.forName(className);
                        //创建bean实例对象
                        Object beanObj = beanClass.newInstance();
                        //将bean实例对象保存到容器中
                        beanMap.put(beanId, beanObj);
    
                        //到目前为止,需要主义的是bean和bean之间的依赖冠希还没有加入
                    }
                }
    
                //5.组装bean之间的依赖关系
                for (int i = 0; i < beanNodeList.getLength(); i++) {
                    //获取到每一个bean节点
                    Node beanNode = beanNodeList.item(i);
                    //判断每一个节点是不是元素节点
                    if (beanNode.getNodeType() == Node.ELEMENT_NODE) {
                        //进行强转
                        Element beanElement = (Element) beanNode;
                        String beanId = beanElement.getAttribute("id");
                        //获取当前节点的所有孩子节点
                        NodeList beanChildNodeList = beanElement.getChildNodes();
                        //进行遍历
                        for (int j = 0; j < beanChildNodeList.getLength(); j++) {
                            Node beanChildNode = beanChildNodeList.item(j);
                            if (beanChildNode.getNodeType()==Node.ELEMENT_NODE && "property".equals(beanChildNode.getNodeName())){
                                Element propertyElement = (Element) beanChildNode;
                                //<bean id="fruit" class="com.lei.fruit.controllers.FruitController">
                                //<property name="fruitService" ref="fruitService"/>
                                String propertyName = propertyElement.getAttribute("name");//fruitService
                                String propertyRef = propertyElement.getAttribute("ref");
                                //找到propertyRef对应的实例
                                //beanMap存储的是FruitService----FruitServiceImpl
                                Object refObj = beanMap.get(propertyRef);//加入上面的例子获取到的是FruitServiceImpl
                                //将refObj设置到当前bean对应的实力的property属性上去
                                Object beanObj = beanMap.get(beanId);//beanId是Fruit所以beanMap获取的是:beanIbj:FruitController
                                //找到当前的类(先找到他的类,然后再找对应的属性)
                                Class<?> beanClazz = beanObj.getClass();
                                //找到propertyName对应的属性
                                Field propertyField = beanClazz.getDeclaredField(propertyName);//找到存在FruitController中对应的属性
                                propertyField.setAccessible(true);
                                propertyField.set(beanObj,refObj);
                            }
                        }
                    }
                }
            } catch (ParserConfigurationException e) {
                e.printStackTrace();
            } catch (SAXException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InstantiationException e) {
                e.printStackTrace();
            } catch (ClassNotFoundException e) {
                e.printStackTrace();
            } catch (NoSuchFieldException e) {
                e.printStackTrace();
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
  • 相关阅读:
    ROS 代码调试常用方式配置
    【校招VIP】前端专业课考点之可靠传输协议
    初识Thread类与创建多线程的方法
    协程Part1-boost.Coroutine.md
    Java 设计模式——抽象工厂模式
    使用docker-compose安装gitlab-ce 以及升级gitlab
    vue、全局前置守卫
    详细解释HiveSQL执行计划
    Java Runtime 类详解
    在react中封装一个简单的自定义hook
  • 原文地址:https://blog.csdn.net/weixin_44606952/article/details/125495934