Java web 2022跟学尚硅谷五 后端基础
- 2.2MVC优化.依赖注入和控制反转
- 类图
- FruitController
- FruitServiceInterface
- FruitServiceImpl
- BeanFactory
- ClassPathXmlApplicationContext
- DispatcherServlet
- applicationContext.xml
- 总结
2.2MVC优化.依赖注入和控制反转
类图
FruitController
package com.atguigu.fruit.controllers;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.fruit.service.impl.FruitServiceImpl;
import com.atguigu.uils.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.List;
/**
* @ClassName: FruitServlet
* @Description:
* @Author: wty
* @Date: 2022/12/6
*/
public class FruitController {
/**
* 接口的多态
*/
private FruitServiceImpl fruitServiceimpl = null;
//private FruitServiceInterface fruitServiceInterface = new FruitServiceImpl();
private String index(String oper, String keyword, Integer pageNumber, HttpServletRequest request) {
List<Fruit> fruitList = null;
// 分页查询(增加参数pageNumber)
int pageCount = 0;
HttpSession session = request.getSession();
if (null == pageNumber) {
pageNumber = 1;
}
// 模糊查询
if (StringUtils.isNotEmpty(oper) && "search".equals(oper)) {
// 相当于是模糊查询
if (StringUtils.isEmpty(keyword)) {
keyword = "";
}
pageNumber = 1;
} else {
// 说明此处不是模糊查询,此时keyword应该从session作用域获取
// 分页查询(增加参数pageNumber)
Object keywordObj = session.getAttribute("keyword");
if (null != keywordObj) {
keyword = (String) keywordObj;
} else {
keyword = "";
}
}
// 将keyword从session作用域获取
session.setAttribute("keyword", keyword);
fruitList = fruitServiceimpl.getFruitList(pageNumber, keyword);
// 非分页,显示全部,更新当前页的值
session.setAttribute("pageNumber", pageNumber);
// 获取总条数
pageCount = fruitServiceimpl.getFruitCount(keyword);
// 获取总页数pageCount
session.setAttribute("pageCount", pageCount);
// 保存到session作用域
//HttpSession session = request.getSession();
session.setAttribute("fruitList", fruitList);
// 此处的视图名称是index
// thymeleaf会将这个逻辑视图名称对应到物理视图名称中去
// 物理视图名称 = view-prefix + 逻辑视图名称 + view-suffix
// 所以真实视图名称: /index.html
//super.processTemplate("index", request, response);
return "index";
}
private String add(String fname, Integer price, Integer fcount, String remark, HttpServletRequest request) throws IOException {
request.setCharacterEncoding("UTF-8");
// 判断是doGet还是doPost
//if (StringUtils.isEmpty(request.getParameter("fname"))
// || StringUtils.isEmpty(request.getParameter("price"))
// || StringUtils.isEmpty(request.getParameter("fcount"))) {
// //processTemplate("add", request, response);
// return "add";
//}
if (StringUtils.isEmpty(fname)
|| null == price
|| null == fcount) {
//processTemplate("add", request, response);
return "add";
}
fruitServiceimpl.addFruit(new Fruit(0, fname, price, fcount, remark));
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
private String del(Integer fid) {
if (null != fid) {
//fruitService
fruitServiceimpl.delFruitByFid(fid);
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
return "error";
}
private String edit(Integer fid, HttpServletRequest request) {
if (null != fid) {
Fruit fruit = fruitServiceimpl.getFruitByFid(fid);
request.setAttribute("fruit", fruit);
//processTemplate("edit", request, response);
return "edit";
}
return "error";
}
private String update(Integer fid, String fname, Integer price, Integer fcount, String remark) {
// 执行更新
fruitServiceimpl.updateFruitByFid(new Fruit(fid, fname, price, fcount, remark));
// 资源跳转
//response.sendRedirect("fruit.do");
return "redirect:fruit.do";
}
}
FruitServiceInterface
package com.atguigu.fruit.service;
import com.atguigu.fruit.pojo.Fruit;
import java.util.List;
/**
* @InterfaceName: FruitServiceInterface
* @Description:
* @Author: wty
* @Date: 2022/12/8
*/
public interface FruitServiceInterface {
// 获取指定页面的库存列表信息
public List<Fruit> getFruitList(Integer pageNumber, String keyword);
// 添加库存记录
public boolean addFruit(Fruit fruit);
//根据id查看指定库存记录
public Fruit getFruitByFid(Integer fid);
// 获取总页数
public Integer getFruitCount(String keyword);
// 通过fid删除
public boolean delFruitByFid(Integer fid);
// 修改库存记录
boolean updateFruitByFid(Fruit fruit);
}
FruitServiceImpl
package com.atguigu.fruit.service.impl;
import com.atguigu.fruit.dao.FruitDAO;
import com.atguigu.fruit.pojo.Fruit;
import com.atguigu.fruit.service.FruitServiceInterface;
import java.util.List;
/**
* @ClassName: FruitServiceImpl
* @Description:
* @Author: wty
* @Date: 2022/12/8
*/
public class FruitServiceImpl implements FruitServiceInterface {
//private FruitDAO fruitDAO = new FruitDAO();
private FruitDAO fruitDAO = null;
/**
* @param
* @return java.util.List<com.atguigu.fruit.pojo.Fruit>
* @description 支持模糊和分页查询
* @param: pageNumber
* @param: keyword
* @date 2022/12/8 0:24
* @author wty
**/
@Override
public List<Fruit> getFruitList(Integer pageNumber, String keyword) {
String sqlStr = "select * from t_fruit where fname like ? or remark like ? limit ?,5";
return fruitDAO.queryMulti(sqlStr, Fruit.class, "%" + keyword + "%", "%" + keyword + "%", (pageNumber - 1) * 5);
}
/**
* @param
* @return boolean
* @description 添加库存记录
* @param: fruit
* @date 2022/12/8 0:24
* @author wty
**/
@Override
public boolean addFruit(Fruit fruit) {
String sqlStr = "insert into t_fruit values(0,?,?,?,?)";
int affectedrows = fruitDAO.dml(sqlStr, fruit.getFname(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark());
return affectedrows > 0;
}
/**
* @param
* @return com.atguigu.fruit.pojo.Fruit
* @description 通过fid获取一行库存记录
* @param: fid
* @date 2022/12/8 0:23
* @author wty
**/
@Override
public Fruit getFruitByFid(Integer fid) {
String sqlStr = "select * from t_fruit where fid = ?";
return fruitDAO.querySingle(sqlStr, Fruit.class, fid);
}
/**
* @param
* @return java.lang.Integer
* @description 获取总页数pageCount
* @param: keyword
* @date 2022/12/8 0:23
* @author wty
**/
@Override
public Integer getFruitCount(String keyword) {
String sqlStr = "select count(*) from t_fruit where fname like ? or remark like ?";
Object o = fruitDAO.queryScalar(sqlStr, "%" + keyword + "%", "%" + keyword + "%");
Integer i = 0;
if (o instanceof Long) {
i = ((Long) o).intValue();
}
Integer pageCount = (i + 5 - 1) / 5;
return pageCount;
}
/**
* @param
* @return boolean
* @description 通过fid删除某行记录
* @param: fid
* @date 2022/12/8 0:32
* @author wty
**/
@Override
public boolean delFruitByFid(Integer fid) {
String sqlStr = "delete from t_fruit where fid = ?";
int affectedrows = fruitDAO.dml(sqlStr, fid);
return affectedrows > 0;
}
/**
* @param
* @return boolean
* @description 修改水果库存记录
* @param: fruit
* @date 2022/12/8 0:32
* @author wty
**/
@Override
public boolean updateFruitByFid(Fruit fruit) {
String sqlStr = "update t_fruit set fname = ?, price = ?, fcount = ?, remark = ? where fid = ? ";
int affectedrows = fruitDAO.dml(sqlStr, fruit.getFname(), fruit.getPrice(), fruit.getFcount(), fruit.getRemark(), fruit.getFid());
return affectedrows > 0;
}
}
BeanFactory
package com.atguigu.myssm.io;
/**
* @InterfaceName: BeanFactory
* @Description:
* @Author: wty
* @Date: 2022/12/8
*/
public interface BeanFactory {
public Object getBean(String id);
}
ClassPathXmlApplicationContext
package com.atguigu.myssm.io;
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;
/**
* @ClassName: ClassPathXmlApplicationContext
* @Description:
* @Author: wty
* @Date: 2022/12/8
*/
public class ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> beanMap = new HashMap<>();
@Override
public Object getBean(String id) {
return beanMap.get(id);
}
/**
* @param
* @return
* @description //构造方法
* @date 2022/12/8 10:49
* @author wty
**/
public ClassPathXmlApplicationContext() {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
// 1.创建DocumentBuilderFactory
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
// 2.创建DocumentBuilder
try {
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对象和bean实例
Class<?> beanClass = Class.forName(className);
// 类对象
Object beanObject = beanClass.newInstance();
// 加入servletContext
//Field servletContextField = beanObj.getDeclaredField("servletContext");
//servletContextField.setAccessible(true);
//servletContextField.set(o, this.getServletContext());
//Method setServletContext = beanObj.getDeclaredMethod("setServletContext", ServletContext.class);
//setServletContext.invoke(o, this.getServletContext());
// 将bean对象保存到beanMap中
beanMap.put(beanId, beanObject);
// 到目前位置需要注意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);
/**
*
* Node 结点
* Element 元素节点 sname
* Text 文本节点 jim
* <sname>jim</sname>
*
*/
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对应的实例propertyName
Object refObject = beanMap.get(propertyref);
// 2.将refObject设置到当前bean对应的实例的property属性上
Object beanObject = beanMap.get(beanId);
Class<?> beanClass = beanObject.getClass();
Field propertyField = beanClass.getDeclaredField(propertyName);
propertyField.setAccessible(true);
propertyField.set(beanObject, refObject);
}
}
}
}
} catch (ParserConfigurationException | InstantiationException | IllegalAccessException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (SAXException | ClassNotFoundException e) {
e.printStackTrace();
} catch (NoSuchFieldException e) {
e.printStackTrace();
}
}
}
DispatcherServlet
package com.atguigu.myssm.myspringmvc;
import com.atguigu.myssm.io.BeanFactory;
import com.atguigu.myssm.io.ClassPathXmlApplicationContext;
import com.atguigu.uils.StringUtils;
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.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
/**
* @ClassName: DispatcherServlet
* @Description:
* @Author: wty
* @Date: 2022/12/7
*/
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory = null;
public DispatcherServlet() {
}
/**
* @param
* @return void
* @description Servlet初始化阶段,并且初始化BeanFactory对象
* @date 2022/12/8 10:54
* @author wty
**/
@Override
public void init() throws ServletException {
super.init();
beanFactory = new ClassPathXmlApplicationContext();
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
// 设置编码
request.setCharacterEncoding("UTF-8");
// 假设URL是:http://localhost:8080/pro15/hello.do
// 那么servletPath 是hello.do
String servletPath = request.getServletPath();
/// hello.do
System.out.println(servletPath);
// 1.字符串截取 hello.do 得到 hello
servletPath = servletPath.substring(1, servletPath.length() - 3);
System.out.println(servletPath);
// 2. hello 和 helloController对应上
Object contorllerBeanObject = beanFactory.getBean(servletPath);
// 下面的逻辑来自于FruitController
String operate = request.getParameter("operate");
if (StringUtils.isEmpty(operate)) {
operate = "index";
}
try {
Method declaredMethods[] = contorllerBeanObject.getClass().getDeclaredMethods();
for (Method declaredMethod : declaredMethods) {
if (operate.equals(declaredMethod.getName())) {
// 1.统一获取请求参数
// 获取当前方法的所有参数,返回参数数组
Parameter[] parameters = declaredMethod.getParameters();
// parametersobjectsValue用来存放参数的值
Object[] parametersobjectsValue = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
Parameter parameter = parameters[i];
String parameterName = parameter.getName();
// 如果参数名是request、response、session那么单独处理
if ("request".equals(parameterName)) {
parametersobjectsValue[i] = request;
} else if ("response".equals(parameterName)) {
parametersobjectsValue[i] = response;
} else if ("session".equals(parameterName)) {
parametersobjectsValue[i] = request.getSession();
} else {
// 从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
//当前页面http://localhost:8080/pro16/fruit.do?pageNumber=2
// 最后的2在parameterValue中是"2"会报错argument type mismatch
String typeName = parameter.getType().getName();
if ("java.lang.String".equals(typeName)) {
parametersobjectsValue[i] = parameterValue;
} else if (null != parameterValue && "java.lang.Integer".equals(typeName)) {
parametersobjectsValue[i] = Integer.parseInt(parameterValue);
} else {
parametersobjectsValue[i] = parameterValue;
}
}
}
// 2.controller组件中的方法调用
declaredMethod.setAccessible(true);
Object o = declaredMethod.invoke(contorllerBeanObject, parametersobjectsValue);
// 3.视图处理:
String methodReturnStr = (String) o;
if (methodReturnStr.startsWith("redirect")) {
// 比如 redirect:fruit.do
// 截取后只想要 fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
} else {
// 比如 edit
super.processTemplate(methodReturnStr, request, response);
}
} else {
//throw new RuntimeException("operate值非法!");
}
}
} catch (IllegalAccessException | InvocationTargetException e) {
e.printStackTrace();
}
}
}
// 常间错误 argument type mismatch
// 当前页面http://localhost:8080/pro16/fruit.do?pageNumber=2
applicationContext.xml
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<bean id="fruitDAO" class="com.atguigu.fruit.dao.FruitDAO"></bean>
<bean id="fruitServiceimpl" class="com.atguigu.fruit.service.impl.FruitServiceImpl">
<property name="fruitDAO" ref="fruitDAO"></property>
</bean>
<!-- 这个bean标签的作用是,将来servletpath中涉及的名字对应的是fruit那么就需要去FruitController与之对应 -->
<bean id="fruit" class="com.atguigu.fruit.controllers.FruitController">
<property name="fruitServiceimpl" ref="fruitServiceimpl"></property>
</bean>
</beans>
<!--
1.概念
HTML 超文本标记语言
XML 可扩展的标记语言
HTML 是XML的一个自集
2.XML包含三个部分
(1)XML声明,而且声明这一行代码必须在XML文件的第一行
(2)DTD文档类型定义
(3)XML正文
-->
总结
今日内容:
- 再次学习Servlet的初始化方法
- Servlet生命周期:实例化、初始化、服务、销毁
- Servlet中的初始化方法有两个:init() , init(config)
其中带参数的方法代码如下:
public void init(ServletConfig config) throws ServletException {
this.config = config ;
init();
}
另外一个无参的init方法如下:
public void init() throws ServletException{
}
如果我们想要在Servlet初始化时做一些准备工作,那么我们可以重写init方法
我们可以通过如下步骤去获取初始化设置的数据
- 获取config对象:ServletConfig config = getServletConfig();
- 获取初始化参数值: config.getInitParameter(key);
- 在web.xml文件中配置Servlet
<servlet>
<servlet-name>Demo01Servlet</servlet-name>
<servlet-class>com.atguigu.servlet.Demo01Servlet</servlet-class>
<init-param>
<param-name>hello</param-name>
<param-value>world</param-value>
</init-param>
<init-param>
<param-name>uname</param-name>
<param-value>jim</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"),
@WebInitParam(name="uname",value="jim")
})
- 学习Servlet中的ServletContext和
- 获取ServletContext,有很多方法
在初始化方法中: ServletContxt servletContext = getServletContext();
在服务方法中也可以通过request对象获取,也可以通过session获取:
request.getServletContext(); session.getServletContext()
2) 获取初始化值:
servletContext.getInitParameter();
- 什么是业务层
- Model1和Model2
MVC : Model(模型)、View(视图)、Controller(控制器)
视图层:用于做数据展示以及和用户交互的一个界面
控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成
模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件,有数据访问层组件
- pojo/vo : 值对象
- DAO : 数据访问对象
- BO : 业务对象
- 区分业务对象和数据访问对象:
1) DAO中的方法都是单精度方法或者称之为细粒度方法。什么叫单精度?一个方法只考虑一个操作,比如添加,那就是insert操作、查询那就是select操作…
2) BO中的方法属于业务方法,也实际的业务是比较复杂的,因此业务方法的粒度是比较粗的
注册这个功能属于业务功能,也就是说注册这个方法属于业务方法。
那么这个业务方法中包含了多个DAO方法。也就是说注册这个业务功能需要通过多个DAO方法的组合调用,从而完成注册功能的开发。
注册:
1. 检查用户名是否已经被注册 - DAO中的select操作
2. 向用户表新增一条新用户记录 - DAO中的insert操作
3. 向用户积分表新增一条记录(新用户默认初始化积分100分) - DAO中的insert操作
4. 向系统消息表新增一条记录(某某某新用户注册了,需要根据通讯录信息向他的联系人推送消息) - DAO中的insert操作
5. 向系统日志表新增一条记录(某用户在某IP在某年某月某日某时某分某秒某毫秒注册) - DAO中的insert操作
6. … - 在库存系统中添加业务层组件
4.IOC
1) 耦合/依赖
依赖指的是某某某离不开某某某
在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。
我们系统架构或者是设计的一个原则是: 高内聚低耦合。
层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合(就是没有耦合)
2) IOC - 控制反转 / DI - 依赖注入
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>