JavaWeb Servlet的getInitParameter、业务层、控制反转IOC和依赖注入DI
目录
- 1. Servlet的getInitParameter
- 2. 业务层
- 3. 控制反转IOC和依赖注入DI
- 3.1 背景
- 3.2 实现如下
- 3.3 原理
1. Servlet的getInitParameter
Servlet有两个getInitParameter
- 一个是servletContext.getInitParameter,获取context-param的全局参数
- 一个是servletConfig.getInitParameter,取init-param的servlet参数
示例如下:
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<context-param>
<param-name>global-name</param-name>
<param-value>global-value</param-value>
</context-param>
<servlet>
<servlet-name>Demo01Servlet</servlet-name>
<servlet-class>com.hh.javaWebTest.demo.Demo01Servlet</servlet-class>
<init-param>
<param-name>servlet-name1</param-name>
<param-value>servlet-value1</param-value>
</init-param>
<init-param>
<param-name>servlet-name2</param-name>
<param-value>servlet-value2</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>Demo01Servlet</servlet-name>
<url-pattern>/demo01</url-pattern>
</servlet-mapping>
</web-app>
Demo01.java
package com.hh.javaWebTest.demo;
import jakarta.servlet.ServletConfig;
import jakarta.servlet.ServletContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServlet;
/*
// web.xml和注解选择一种
@WebServlet(
urlPatterns = {"/demo01"} ,
initParams = {
@WebInitParam(name="servlet-name1",value="servlet-value1"),
@WebInitParam(name="servlet-name2",value="servlet-value2")
}
)
*/
public class Demo01Servlet extends HttpServlet {
@Override
public void init() throws ServletException {
// 获取context-param的全局参数
// request.getServletContext();
// request.getSession().getServletContext();
ServletContext servletContext = getServletContext();
String globalValue = servletContext.getInitParameter("global-name");
System.out.println("globalValue = " + globalValue); // globalValue = global-value
// 获取init-param的servlet参数
ServletConfig servletConfig = getServletConfig();
String servletValue1 = servletConfig.getInitParameter("servlet-name1");
System.out.println("servletValue1 = " + servletValue1); // servletValue1 = servlet-value1
}
}
2. 业务层
Model1介绍:典型的就是JSP,用HTML(CSS、JS) + Java代码(将数据提供给页面的代码,加上和数据库通信的代码)。这样的Java代码显得很乱
Model2,即MVC: Model(模型) + View(视图) + Controller(控制器)
- 视图层:用于做数据展示以及和用户交互的一个界面
- 控制层:能够接受客户端的请求,具体的业务功能还是需要借助于模型组件来完成
- 模型层:模型分为很多种:有比较简单的pojo/vo(value object),有业务模型组件(BO业务对象),有数据访问层组件(DAO数据访问对象)、有Service传输给Controller的组件(DTO数据传输对象,一般用于前后端分离)
区分业务对象和数据访问对象:
- DAO中的方法都是细粒度方法。一个方法只考虑一个操作,比如insert添加
- BO中的方法属于业务方法,粒度是比较粗的,对应复杂的业务逻辑处理,如注册新用户,需要调用很多DAO,和做很多逻辑操作
3. 控制反转IOC和依赖注入DI
3.1 背景
在软件系统中,层与层之间是存在依赖的。我们也称之为耦合。但我们系统架构设计的一个原则是: 高内聚低耦合。即层内部的组成应该是高度聚合的,而层与层之间的关系应该是低耦合的,最理想的情况0耦合。我们可以通过控制反转IOC和依赖注入DI来实现高内聚低耦合
3.2 实现如下
FruitService.java
package com.hh.javaWebTest.service;
public interface FruitService {
}
FruitServiceImpl.java
package com.hh.javaWebTest.service.impl;
import com.hh.javaWebTest.service.FruitService;
public class FruitServiceImpl implements FruitService {
}
FruitController.java。里面有一个FruitService类型的属性
package com.hh.javaWebTest.controller;
import com.hh.javaWebTest.service.FruitService;
......省略部分......
public class FruitController {
private FruitService fruitService = null;
......省略部分......
}
applicationContext.xml。定义了两个bean,同时定义了fruitService是FruitController的属性
<?xml version="1.0" encoding="utf-8"?>
<beans>
<bean id="fruitService" class="com.hh.javaWebTest.service.impl.FruitServiceImpl"/>
<!--
Node节点:
Element元素节点
Text文本节点
-->
<!-- 子节点总共有5个。空白Text、注释Text、空白Text、property元素节点、空白Text-->
<bean id="fruit" class="com.hh.javaWebTest.controller.FruitController">
<!-- property标签用来表示属性;name表示属性名;ref表示引用其他bean的id值 -->
<property name="fruitService" ref="fruitService"/>
</bean>
</beans>
BeanFactory.java
package com.hh.javaWebTest.ioc;
public interface BeanFactory {
Object getBean(String id);
}
ClassPathXmlApplicationContext.java。解析applicationContext.xml,将bean放到beanMap中,然后给各个bean设置property属性
package com.hh.javaWebTest.ioc;
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 ClassPathXmlApplicationContext implements BeanFactory {
private Map<String, Object> beanMap = new HashMap<>();
public ClassPathXmlApplicationContext() {
try {
InputStream inputStream = getClass().getClassLoader().getResourceAsStream("applicationContext.xml");
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
Document document = documentBuilder.parse(inputStream);
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 controllerBeanClass = Class.forName(className);
Object beanObj = controllerBeanClass.getDeclaredConstructor().newInstance();
beanMap.put(beanId, beanObj);
}
}
// 组装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();
for (int j = 0; j < beanChildNodeList.getLength(); j++) {
Node beanChildNode = beanChildNodeList.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");
// 获取属性的值
Object refObj = beanMap.get(propertyRef);
// 获取到主节点的bean
Object beanObj = beanMap.get(beanId);
Class beanClazz = beanObj.getClass();
// 获取到主节点的bean的属性
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.java。不在DispatcherServlet进行applicationContext.xml的解析,而是直接从BeanFactory获取bean
package com.hh.javaWebTest.servlet;
import com.hh.javaWebTest.ioc.BeanFactory;
import com.hh.javaWebTest.ioc.ClassPathXmlApplicationContext;
import jakarta.servlet.ServletException;
import jakarta.servlet.annotation.WebServlet;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import java.io.IOException;
......省略部分......
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory;
@Override
public void init() throws ServletException {
// 手动进行ViewBaseServlet的初始化
super.init();
beanFactory = new ClassPathXmlApplicationContext();
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
......省略部分......
// 获取fruit对应的class
Object controllerBeanObj = beanFactory.getBean(servletPath);
......省略部分......
}
}
3.3 原理
控制反转:
- 之前在FruitController中,我们创建Service属性, FruitService fruitService = new FruitServiceImpl()。fruitService的作用域(生命周期)是FruitController实例级别
- 之后我们在applicationContext.xml中定义了这个fruitService。然后通过解析XML,产生fruitService实例。所有bean都存放在beanMap中,这个beanMap在一个BeanFactory中
- 因此,我们改变了之前的service实例等他们的作用域(生命周期)。控制权从程序员转移到BeanFactory。这个现象我们称之为控制反转
依赖注入:
- 之前在FruitController中,我们创建Service属性, FruitService fruitService = new FruitServiceImpl()。那么,FruitController和FruitService存在耦合
- 之后,我们将代码修改成FruitService fruitService = null;
- 然后,在配置文件中给FruitController这个bean定义了属性fruitService的值,解析配置文件,就可以将fruitService变量注入到FruitController的属性中,实现了解耦