8.Java Web之Servlet API及IOC

1.再次学习Servlet的初始化方法

1)Servlet声明周期:实例化、初始化、服务、销毁
2)Servlet中的初始化方法有两个:init()、init(config)

//其中带参数的init方法代码如下:
public void init(ServletConfig config) throws ServletException {
    this.config = config;
    this.init();
}

//无参的init方法
public void init() throws ServletException {
}

如果要在Servlet初始化时做一些准备工作,那么我们可以重写init方法
通过如下步骤去获取初始化的数据
1.获取config对象:ServletConfig config = getServletConfig();
2.获取初始化参数值:config.getInitParameter(key);

3)在Web.xml文件中配置Servlet

    <servlet>
        <servlet-name>Demo01Servlet</servlet-name>
        <servlet-class>com.example.servlet.Demo01Servlet</servlet-class>
        <init-param>
            <param-name>hello</param-name>
            <param-value>world</param-value>
        </init-param>
        <init-param>
            <param-name>hello1</param-name>
            <param-value>world1</param-value>
        </init-param>
    </servlet>
    <servlet-mapping>
        <servlet-name>Demo01Servlet</servlet-name>
        <url-pattern>/demo01</url-pattern>
    </servlet-mapping>

4)也可以通过注解的方式进行配置

@WebServlet(urlPatterns = {"/demo01"},
    initParams = {
        @WebInitParam(name="hello2",value="world2"),
        @WebInitParam(name="uname",value="jim")
    })

1.1 实验

配置文件方式
① 在web.xml中添加初始化参数key是hello,value是world

    <servlet>
        <servlet-name>Demo01Servlet</servlet-name>
        <servlet-class>com.example.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>

②编写Demo01Servlet获取初始化参数

package com.example.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;

public class Demo01Servlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        String initValue = config.getInitParameter("hello");
        System.out.println("initValue = " + initValue);
    }
}

③ 获取到结果:initValue = world

使用注解的方式
注解和web.xml冲突,注释掉一个即可

package com.example.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;

@WebServlet(urlPatterns = {"/demo01"},
        initParams = {
            @WebInitParam(name="hello2",value="world2"),
            @WebInitParam(name="uname",value="jim")
        })
public class Demo01Servlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        String initValue = config.getInitParameter("hello2");
        System.out.println("initValue = " + initValue);
    }
}

2.学习Servlet中的ServletContext和

1)获取ServletContext,有很多方法
在初始化方法中:ServletContext servletContext = getServletContext();
在服务方法中也可以通过request对象获取,也可以通过session获取
request.getServletContext();
request.getSession().getServletContext();

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

2.1 实验

① 编辑web.xml

    <context-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>classpath:com</param-value>
    </context-param>

② 在Demo01Servlet中获取

package com.example.servlet;

import javax.servlet.ServletConfig;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

//@WebServlet(urlPatterns = {"/demo01"},
//        initParams = {
//            @WebInitParam(name="hello2",value="world2"),
//            @WebInitParam(name="uname",value="jim")
//        })
public class Demo01Servlet extends HttpServlet {
    @Override
    public void init() throws ServletException {
        ServletConfig config = getServletConfig();
        String initValue = config.getInitParameter("hello2");
        System.out.println("initValue = " + initValue);
        ServletContext servletContext = getServletContext();
        String contextValue = servletContext.getInitParameter("contextConfigLocation");
        System.out.println("contextValue = " + contextValue);
    }

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//        req.getServletContext();
        req.getSession().getServletContext();
    }
}

③ 启动容器,获取到:contextValue = classpath:com

3.业务层

1)Model1(jsp)和Model2(MVC)
MVC:Model(模型)、View(视图)、Controller(控制器)
视图层:用于做数据展示以及和用户交互的一个界面
控制层:能够接受客户端的请求,具体的业务功能还是需要借助模型组件来完成
模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件

① pojo/vo :值对象
② DAO :数据访问对象
③ BO :业务对象

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

  • DAO中的方法都是单精度方法或者成为细粒度方法。单精度就是一个方法只考虑一个操作
  • BO中的方法属于业务方法,实际的业务是比较复杂的,因此业务方法的粒度是比较粗的

注册这个功能中包含多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能的开发。
注册:
1.检查用户名是否已经被注册 —— DAO中的select操作
2.向用户表新增一条新用户记录 —— DAO中的insert操作
3.向用户积分表新增一条记录(新用户默认初始化积分100分) —— DAO中的insert操作
4.向系统信息表新增一条记录(某某某新用户注册了,需要根据通讯录信息向他的联系人推送消息) —— DAO中的insert操作
5.向系统日志表新增一条记录(某用户在某IP在某年某月某日某时某分某秒注册) —— DAO中的insert操作

3.1 添加业务层

导入项目(链接:https://pan.baidu.com/s/1G14B1mzPNgBClOq5GTl4gA 提取码:22zz)

① 在fruit.service包下创建FruitService接口

package com.example.fruit.service;

import com.example.fruit.pojo.Fruit;

import java.util.List;


public interface FruitService {
    //获取指定页面的库存列表信息
    List<Fruit> getFruitList(String keyword, Integer pageNo);

    //增加库存记录信息
    void addFruit(Fruit fruit);

    //根据id查看指定库存记录
    Fruit getFruitByFid(Integer fid);

    //删除指定库存记录
    void delFruit(Integer fid);

    //获取总页数
    Integer getPageCount(String keyword);

    //修改特定库存记录
    void updateFruit(Fruit fruit);
}

② 在fruit.service.impl包下创建实现类FruitServiceImpl继承FruitService接口(实际工作中会很复杂)

package com.example.fruit.service.impl;

import com.example.fruit.service.FruitService;
import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.fruit.pojo.Fruit;

import java.util.List;

public class FruitServiceImpl implements FruitService {
    private FruitDAO fruitDAO= new FruitDAOImpl();

    @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 void delFruit(Integer fid) {
        fruitDAO.delFruit(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);
    }
}

③ 修改FruitController实现(FruitController -> FruitService -> FruitDAO)

package com.example.fruit.controllers;

import com.example.fruit.service.FruitService;
import com.example.fruit.service.impl.FruitServiceImpl;
import com.example.fruit.dao.FruitDAO;
import com.example.fruit.dao.impl.FruitDAOImpl;
import com.example.fruit.pojo.Fruit;
import com.example.myssm.util.StringUtil;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.util.List;

public class FruitController{
    private FruitService fruitService = new FruitServiceImpl();

    //UpdateServlet的doPost
    protected String update(Integer fid,String fname ,Integer price,Integer fcount,String remark){
        //3 执行更新
        fruitService.updateFruit(new Fruit(fid,fname,price,fcount,remark));
        //4 资源跳转
//        resp.sendRedirect("fruit.do");
        return "redirect:fruit.do";
    }

    //EditServlet得到doGet
    protected String edit(Integer fid,HttpServletRequest req){
        if(fid != null){
            Fruit fruit = fruitService.getFruitByFid(fid);
            req.setAttribute("fruit",fruit);
//            super.processTemplate("edit",req,resp);
            return "edit";
        }
        return "error";
    }

    //delServlet的doGet
    protected String del(Integer fid) {
        if(fid != null){
            fruitService.delFruit(fid);
//            resp.sendRedirect("fruit.do");
            return "redirect:fruit.do";
        }
        return "error";
    }

    //addServlet的doPost
    protected String add(Integer fid,String fname ,Integer price,Integer fcount,String remark){
        fruitService.addFruit(new Fruit(0,fname,price,fcount,remark));
//        resp.sendRedirect("fruit.do");
        return "redirect:fruit.do";
    }

    //indexServlet的doGet
    protected String index(String oper,String keyword,Integer pageNo,HttpServletRequest req) {

        HttpSession session = req.getSession();
        if(pageNo == null){
            pageNo = 1;
        }
        if(StringUtil.isNotEmpty(oper) && "search".equals(oper)){
            //说明是点击表单查询发送过来的请求
            //此时,pageNo应该还原为1,keyword应该从请求参数中获取
            pageNo = 1;
            if(StringUtil.isEmpty(keyword)){
                keyword = "";
            }
            session.setAttribute("keyword",keyword);
        }else{
            Object keywordObj = session.getAttribute("keyword");
            if(keywordObj!=null){
                keyword = (String) keywordObj;
            }else{
                keyword = "";
            }
        }

        session.setAttribute("pageNo",pageNo);
        FruitDAO fruitDAO = new FruitDAOImpl();
        List<Fruit> fruitList = fruitService.getFruitList(keyword,pageNo);
        //保存到session作用域
        session.setAttribute("fruitList",fruitList);

        //总记录条数
        int pageCount = fruitService.getPageCount(keyword);

        session.setAttribute("pageCount",pageCount);

//        super.processTemplate("index",req,resp);
        return "index";
    }
}

4.IOC

1)耦合/依赖
在软件系统中,层与层之间是纯在依赖的。我们也称之为耦合
我们系统架构或者是设计的一个原则是:高内聚低耦合
层内部的组成应该是高度聚合的,而层与层之间的关系是低耦合的,最理想的情况是0耦合(就是没有耦合)

2)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="fruitService" class="com.example.fruit.service.impl.FruitServiceImpl">
        <!--property标签用来表示属性;name表示属性名;ref表示引用其他bean的id值-->
        <property name="fruitDAO" ref="fruitDAO" />
</bean>

4.1 ioc实验

降低层之间的耦合
①修改applicationContext.xml(显示依赖关系)

<?xml version="1.0" encoding="utf-8" ?>
<beans>
    <bean id="fruitDAO" class="com.example.fruit.dao.impl.FruitDAOImpl"/>
    <bean id="fruitService" class="com.example.fruit.service.impl.FruitServiceImpl">
        <!--property标签用来表示属性;name表示属性名;ref表示引用其他bean的id值-->
        <property name="fruitDAO" ref="fruitDAO" />
    </bean>
    <bean id="fruit" class="com.example.fruit.controllers.FruitController" >
        <property name="fruitService" ref="fruitService" />
    </bean>
</beans>

② 在myssm.io包下创建BeanFactory接口

package com.example.myssm.io;

public interface BeanFactory {
    Object getBean(String id);
}

③ 在myssm.io包下创建BeanFactory接口的实现类ClassPathXmiApplicationContext(根据applicationContext.xml文件实例化bean以及添加bean之间的依赖关系)

package com.example.myssm.io;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

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

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

    public ClassPathXmiApplicationContext(){
        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");
                    Class beanClass = Class.forName(className);
                    //创建bean实例,
                    Object beanObj =  beanClass.newInstance();
                    //将bean实例对象保存到map容器中
                    beanMap.put(beanId,beanObj);
                    //到目前为止,此处需要注意的是bean和bean之间的依赖关系没有设置

                }
            }
            //5.组装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");
                    NodeList beanChildNodeList = beanElement.getChildNodes();
//                    System.out.println(beanChildNodeList.getLength());
                    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;
                            String propertyName = propertyElement.getAttribute("name");
                            String propertyRef = propertyElement.getAttribute("ref");
                            //1)找到propertyRef对应的实例
                            Object refObj = beanMap.get(propertyRef);
                            //2)将refObj设置到当前bean对应的实例的property属性上去
                            Object beanObj = beanMap.get(beanId);
                            Class beanClazz = beanObj.getClass();
                            Field propertyField = beanClazz.getDeclaredField(propertyName);
                            propertyField.setAccessible(true);
                            propertyField.set(beanObj,refObj);
                        }
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public Object getBean(String id) {
        return beanMap.get(id);
    }
}

④ 修改DispatcherServlet(调用ClassPathXmiApplicationContext)

package com.example.myssm.myspringmvc;

import com.example.myssm.io.BeanFactory;
import com.example.myssm.io.ClassPathXmiApplicationContext;
import com.example.myssm.util.StringUtil;


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.Method;
import java.lang.reflect.Parameter;

@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
    private BeanFactory beanFactory;

    public DispatcherServlet(){
    }

    public void init() throws ServletException {
        super.init();
        beanFactory = new ClassPathXmiApplicationContext();
    }
    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置编码
        req.setCharacterEncoding("utf-8");
        //假设url是 http://localhost:8080/hello.do
        //那么servletPath是:/hello.do
        //思路是:
        //1./hello.do -> hello
        //2.hello -> HelloController
        String servletPath = req.getServletPath();
//        System.out.println("servletPath = " + servletPath); //servletPath = /hello.do
        int lastDotIndex = servletPath.lastIndexOf(".do");
        servletPath = servletPath.substring(1,lastDotIndex);

//        System.out.println(servletPath); //hello

        Object controllerBeanObj = beanFactory.getBean(servletPath);

        String operate = req.getParameter("operate");
        if(StringUtil.isEmpty(operate)){
            operate = "index";
        }

        try {
            Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
            for (Method method : methods) {
                if(operate.equals(method.getName())){
                    //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();
                        if("req".equals(parameterName)){
                            //如果参数名是req,resp,session 那么久不是通过请求中获取参数的方式了
                            parameterValues[i] = req;
                        }else if("resp".equals(parameterName)) {
                            parameterValues[i] = resp;
                        }else if("session".equals(parameterName)) {
                            parameterValues[i] = req.getSession();
                        }else{
                            //从请求中获取参数值
                            String parameterValue = req.getParameter(parameterName);

                            //类型转换 避免错误: IllegalArgumentException: argument type mismatch
                            String typeName = parameter.getType().getName();
                            Object parameterObj = parameterValue;
                            if(parameterObj != null){
                                if("java.lang.Integer".equals(typeName)){
                                    parameterObj = Integer.parseInt(parameterValue);
                                }
                            }
                            parameterValues[i] = parameterObj;
                        }
                    }

                    //2.Controller组件中的方法调用
                    method.setAccessible(true);
                    Object returnObj = method.invoke(controllerBeanObj,parameterValues);

                    //3.视图处理
                    String methodReturnStr = (String) returnObj;
                    if(methodReturnStr.startsWith("redirect:")){//如:redirect:fruit.do
                        String redirectStr = methodReturnStr.substring("redirect:".length());
                        resp.sendRedirect(redirectStr);
                    }else{
                        super.processTemplate(methodReturnStr,req,resp); //如:"edit"
                    }
                }
            }

//            }else{
//                throw new RuntimeException("operate值非法");
//            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

⑤ 降低层之间的依赖关系
修改FruitServiceImpl
private FruitDAO fruitDAO= null;
修改FruitController
private FruitService fruitService = null;

⑥启动容器,访问http://localhost:8080/fruit.do

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值