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

②init()

   public void init() throws ServletException{
   }

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.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>

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

@WebServlet(urlPatterns = {"/demo01"} ,
     initParams = {
         @WebInitParam(name="hello",value="world"),
     })

补充:
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)获取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);
    }

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

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

在以前的实现过程中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>

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>

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

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

在这里插入图片描述

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

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

    需要实现的是:
(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();
        }
    }
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

皮皮皮皮皮皮皮卡乒

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值