目录

- M:model
- V:View
- C:controller
controller
包括了filter,dispatcher,本质是servlet
filter
请求先经过filter,这里用filter实现设置编码。
@WebFilter(urlPatterns = {"*.do"},initParams = {@WebInitParam(name = "encoding",value = "UTF-8")}) public class CharacterEncodingFilter implements Filter { //默认设置编码格式是UTF-8 private String encoding = "UTF-8"; @Override public void init(FilterConfig filterConfig) throws ServletException { //先获取配置的编码格式 String encodingStr = filterConfig.getInitParameter("encoding"); //如果配置的编码格式为空,就使用默认的编码格式 if(StringUtil.isNotEmpty(encodingStr)){ encoding = encodingStr ; } } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { //使用默认的编码格式 ((HttpServletRequest)servletRequest).setCharacterEncoding(encoding); //放行 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() { } }
dispatcher
1.拦截*.do结尾的servlet请求。
2.在init方法中获取到beanFactory,beanFactory有获取所有bean(controller,service,DAO)的方法返回一个Map集合。*的内容对应controller的全类名,也就是说获取到*就能获取到处理该请求的对应controller类。
3.截取*的内容,根据*的值从集合中获取controller类
4.获取请求中的operate参数,根据operate的值执行对应controller中的方法。
5.获取请求中的其他参数,给方法中的参数赋值,参数名和参数名相同
6.执行方法,根据方法的返回值判断下面的操作
7.如果是redirect:开头就执行重定向操作,要么就执行渲染操作。
渲染操作是渲染页面,一般是转移到页面时进行的操作。
//执行的操作要和方法名相同 //传入的参数的参数名要和方法的参数名相同 @WebServlet("*.do") public class DispatcherServlet extends ViewBaseServlet { //beanFactory中包含了所有的controller层,service层,DAO层的实现类 private BeanFactory beanFactory; public DispatcherServlet(){ } public void init() throws ServletException { super.init(); ServletContext application = getServletContext(); Object beanFactoryObj = application.getAttribute("beanFactory"); if(beanFactoryObj!=null){ beanFactory = (BeanFactory)beanFactoryObj; }else{ throw new RuntimeException("IOC容器获取失败!"); } } @Override protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { //拦截所以的servlet请求,并删去后缀,以便找到想要访问的具体controller,比如user.do->user String servletPath = request.getServletPath(); servletPath = servletPath.substring(1); int lastDotIndex = servletPath.lastIndexOf(".do") ; servletPath = servletPath.substring(0,lastDotIndex); //得到想问访问的controller的字符串形式,去找到具体要执行操作的controller的实例 //对应关系已经写到了配置文件中,并将配置文件中的对应关系保存到了map集合中 Object controllerBeanObj = beanFactory.getBean(servletPath); //找到了具体的controller类后,就要考虑执行这个类的什么方法,先获取页面发过来想要进行的操作 //注意操作名和方法名是相同的 String operate = request.getParameter("operate"); //如果请求中没有想要进行的操作,则默认进行index操作即返回主页。 if(StringUtil.isEmpty(operate)){ operate = "index" ; } //获取到方法名之后就要执行这个方法,但是执行方法时要传入参数,所以接下来要获取方法列表中的参数名 try { //先得到这个类的所以方法名 Method[] methods = controllerBeanObj.getClass().getDeclaredMethods(); for(Method method : methods){ //找到想要执行的方法 if(operate.equals(method.getName())){ //获取这个方法中所以参数的方法名 //getParameter方法返回的是方法名的别名 arg0,arg1.... //通过设置来使编译的class文件中带有具体的方法名 //所以设置完后要进行重新编译 Build Parameter[] parameters = method.getParameters(); //获取到参数名后,要给参数赋值 Object[] parameterValues = new Object[parameters.length]; for (int i = 0; i < parameters.length; i++) { //拿到一个参数(参数名) Parameter parameter = parameters[i]; //先处理特殊情况的赋值,有的方法需要传入HttpServletRequest类型的参数来保存作用域 //但这种参数请求中不会写在URL或表单中,所以要单独赋值 String parameterName = parameter.getName(); if("request".equals(parameterName)){ parameterValues[i] = request ; }else if("response".equals(parameterName)){ parameterValues[i] = response ; }else if("session".equals(parameterName)){ parameterValues[i] = request.getSession(); }else{ //一般情况的赋值,参数的名字和传进的参数名字要相同,如loginId //这里只考虑参数是单个值的情况,不考虑复选框 //通过页面的请求,从请求中获取参数值 String parameterValue = request.getParameter(parameterName); //获取到的参数都是字符串类型(URL中?后面的参数或表单中的参数),假如方法执行要使用int类型,就会发生参数类型不匹配的错误 //获取要执行的方法中参数的类型,目的是如果参数类型是Integer类型的,则要将页面获取的参数转为Integer类型 String typeName = parameter.getType().getName(); //先将从页面获取到的参数赋给Object类型的对象 Object parameterObj = parameterValue; if(parameterObj!=null) { //如果方法中的参数类型是Integer,则将页面获取到的参数转为int类型再赋值 if ("java.lang.Integer".equals(typeName)) { parameterObj = Integer.parseInt(parameterValue); } //按道理来说,从页面上获取到的参数都是String类型,所以如果要传入的参数中有double,Boolean等都要转换 } parameterValues[i] = parameterObj ; } } //知道了要具体执行什么方法(根据operate判断)和方法的参数,下面就开始执行方法 method.setAccessible(true); //执行方法controllerBeanObj这个类中的方法,并将参数数组传入 Object returnObj = method.invoke(controllerBeanObj,parameterValues); String methodReturnStr = (String)returnObj ; //执行方法后返回一个字符串,根据字符串进行跳转 if(methodReturnStr.startsWith("redirect:")){ //比如: redirect:fruit.do String redirectStr = methodReturnStr.substring("redirect:".length()); response.sendRedirect(redirectStr); }else{ super.processTemplate(methodReturnStr,request,response); // 比如: "edit" } } } } catch (Exception e) { e.printStackTrace(); throw new DispatcherServletException("DispatcherServlet出错了..."); } } }注意
获取方法参数名的时候需要进行一些设置。
一些区别
在filter中使用了config类的配置,config是针对于某个servlet的,代码中展示了在注解中设置,在web.xml中设置的方式如下
要设置在一个servlet中。
dispatcher中使用了context类的配置,也可以设置在配置文件web.xml文件中。
不需要设置在某个servlet中。
model
包括了controller,service, DAO,POJO
controller
一个controller包含几个service。
比如发来了关于user的请求。
先根据.do定位到具体的controller,再根据operate定位具体的方法。
比如定位到登录这个方法,登录时需要先校验,用到UserService,登录后再显示日志,再用到TopicService。
service
一个service包含几个DAO。来得到DAO层的数据。
比如用户登录这个业务操作,登录时需要校验用户密码,登录后需要显示好友列表,这两个操作对应两个DAO,但都和登录有关。
DAO
直接通过sql语句与数据库交互,来返回数据库结果。
如UserDAOImpl就主要负责查询用户表的数据。
TopicDAOImpl就主要负责查询日志表的数据。
当然UserDAOImpl也可能查询别的表中的数据,但这个表和User有关系,比如根据User的Id查询User的好友,就需要去friend表中查。
POJO
简单类,主要是实例化数据库的表,每一张表对应一个类。
整体的过程
想要controller完成登录功能,需要有登录验证和登陆后显示好友列表和日志列表。
登陆验证需要与数据库中取出的数据比对,好友和日志列表需要从数据库取数据
需要有userBasicService实现登录的校验和得到好友列表的数据。
topicService实现得到日志列表的数据。
UserBasicService通过UserDAO获取好友列表和校验数据
TopicService通过调用TopicDAO获取日志列表
总结
DAO里的内容主要包括
- 对该表的数据的增删改
- 获取该表的所有数据
- 根据ID获取该表一行数据
- 根据某一个与其他表关联的字段获取该表的一行数据
一个DAO对应一张表,DAO的通用性强
Service里的内容主要包括
dispatcher(中央控制器):拦截servlet请求,通过对URL修改和配置文件比对,找到具体的controller类。
调用controller类后,每个controller都会跳转,则返回字符串参数让中央控制器统一做跳转。
加载配置文件,创建配置文件对象,通过对象获取配置文件中的标签对象


package myssm.myspringmvc;
import myssm.ioc.BeanFactory;
import myssm.myspringmvc.DispatcherServletException;
import myssm.myspringmvc.ViewBaseServlet;
import myssm.util.StringUtil;
import javax.servlet.ServletContext;
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;
//根据路径找到controller,根据operate参数找到方法,再传入其他参数执行方法
//执行的操作要和方法名相同
//传入的参数的参数名要和方法的参数名相同
@WebServlet("*.do")
public class DispatcherServlet extends ViewBaseServlet {
private BeanFactory beanFactory ;
public DispatcherServlet(){
}
public void init() throws ServletException {
super.init();
ServletContext application = getServletContext();
Object beanFactoryObj = application.getAttribute("beanFactory");
if(beanFactoryObj!=null){
beanFactory = (BeanFactory)beanFactoryObj ;
}else{
throw new RuntimeException("IOC容器获取失败!");
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
//拦截所以的servlet请求,并删去后缀,以便找到想要访问的具体controller,比如user.do->user
String servletPath = request.getServletPath();
servletPath = servletPath.substring(1);
int lastDotIndex = servletPath.lastIndexOf(".do") ;
servletPath = servletPath.substring(0,lastDotIndex);
//得到想问访问的controller的字符串形式,去找到具体要执行操作的controller的实例
//对应关系已经写到了配置文件中,并将配置文件中的对应关系保存到了map集合中
Object controllerBeanObj = beanFactory.getBean(servletPath);
//找到了具体的controller类后,就要考虑执行这个类的什么方法,先获取页面发过来想要进行的操作
//注意操作名和方法名是相同的
String operate = request.getParameter("operate");
//如果请求中没有想要进行的操作,则默认进行index操作即返回主页。
if(StringUtil.isEmpty(operate)){
operate = "index" ;
}
//获取到方法名之后就要执行这个方法,但是执行方法时要传入参数,所以接下来要获取方法列表中的参数名
try {
//先得到这个类的所以方法名
Method[] methods = controllerBeanObj.getClass().getDeclaredMethods();
for(Method method : methods){
//找到想要执行的方法
if(operate.equals(method.getName())){
//获取这个方法中所以参数的方法名
//getParameter方法返回的是方法名的别名 arg0,arg1....
//通过设置来使编译的class文件中带有具体的方法名
//所以设置完后要进行重新编译 Build
Parameter[] parameters = method.getParameters();
//获取到参数名后,要给参数赋值
Object[] parameterValues = new Object[parameters.length];
for (int i = 0; i < parameters.length; i++) {
//拿到一个参数(参数名)
Parameter parameter = parameters[i];
//先处理特殊情况的赋值,有的方法需要传入HttpServletRequest类型的参数来保存作用域
//但这种参数请求中不会写在URL或表单中,所以要单独赋值
String parameterName = parameter.getName();
if("request".equals(parameterName)){
parameterValues[i] = request ;
}else if("response".equals(parameterName)){
parameterValues[i] = response ;
}else if("session".equals(parameterName)){
parameterValues[i] = request.getSession();
}else{
//一般情况的赋值,参数的名字和传进的参数名字要相同,如loginId
//这里只考虑参数是单个值的情况,不考虑复选框
//通过页面的请求,从请求中获取参数值
String parameterValue = request.getParameter(parameterName);
//获取到的参数都是字符串类型(URL中?后面的参数或表单中的参数),假如方法执行要使用int类型,就会发生参数类型不匹配的错误
//获取要执行的方法中参数的类型,目的是如果参数类型是Integer类型的,则要将页面获取的参数转为Integer类型
String typeName = parameter.getType().getName();
//先将从页面获取到的参数赋给Object类型的对象
Object parameterObj = parameterValue;
if(parameterObj!=null) {
//如果方法中的参数类型是Integer,则将页面获取到的参数转为int类型再赋值
if ("java.lang.Integer".equals(typeName)) {
parameterObj = Integer.parseInt(parameterValue);
}
//按道理来说,从页面上获取到的参数都是String类型,所以如果要传入的参数中有double,Boolean等都要转换
}
parameterValues[i] = parameterObj ;
}
}
//知道了要具体执行什么方法(根据operate判断)和方法的参数,下面就开始执行方法
method.setAccessible(true);
//执行方法controllerBeanObj这个类中的方法,并将参数数组传入
Object returnObj = method.invoke(controllerBeanObj,parameterValues);
String methodReturnStr = (String)returnObj ;
//执行方法后返回一个字符串,根据字符串进行跳转
if(methodReturnStr.startsWith("redirect:")){ //比如: redirect:fruit.do
String redirectStr = methodReturnStr.substring("redirect:".length());
response.sendRedirect(redirectStr);
}else{
super.processTemplate(methodReturnStr,request,response); // 比如: "edit"
}
}
}
} catch (Exception e) {
e.printStackTrace();
throw new DispatcherServletException("DispatcherServlet出错了...");
}
}
}
// 常见错误: IllegalArgumentException: argument type mismatch 需要将页面获取的参数与方法的参数进行匹配
package myssm.ioc;
import myssm.util.StringUtil;
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;
public class ClassPathXmlApplicationContext implements BeanFactory {
//保存每个bean的id和对应的实例对象
private Map<String,Object> beanMap = new HashMap<>();
private String path = "applicationContext.xml" ;
public ClassPathXmlApplicationContext(){
this("applicationContext.xml");
}
public ClassPathXmlApplicationContext(String path){
if(StringUtil.isEmpty(path)){
throw new RuntimeException("IOC容器的配置文件没有指定...");
}
try {
//1.加载xml配置文件,目的是获取xml文件中id和class属性的对应关系并保存到map集合中
InputStream inputStream = getClass().getClassLoader().getResourceAsStream(path);
DocumentBuilderFactory documentBuilderFactory = DocumentBuilderFactory.newInstance();
DocumentBuilder documentBuilder = documentBuilderFactory.newDocumentBuilder();
//2.获取到xml文件的对象
Document document = documentBuilder.parse(inputStream);
//3.获取xml文件中所有的bean标签对象,放到list集合中
NodeList beanNodeList = document.getElementsByTagName("bean");
//4.遍历每个bean标签对象,将其属性id和class保存到map中
for(int i = 0 ; i<beanNodeList.getLength() ; i++){
//4.1获取每一个bean标签对象
Node beanNode = beanNodeList.item(i);
//4.2这行代码先不管,是为了用Element类中的方法
if(beanNode.getNodeType() == Node.ELEMENT_NODE){
Element beanElement = (Element)beanNode;
//4.3获取bean标签的属性
String beanId = beanElement.getAttribute("id");
String className = beanElement.getAttribute("class");
//4.4class属性是字符串,转为对应的类
//4.4.1用全类名获取Class对象
Class beanClass = Class.forName(className);
//4.4.2创建bean实例
Object beanObj = beanClass.newInstance() ;
//4.5将bean实例对象保存到map容器中
beanMap.put(beanId , beanObj);
}
}
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);
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);
Object beanObj = beanMap.get(beanId);
Class beanClazz = beanObj.getClass();
Field propertyField = beanClazz.getDeclaredField(propertyName);
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();
}
}
@Override
public Object getBean(String id) {
return beanMap.get(id);
}
}
本文详细介绍了MVC设计模式的各个组成部分,包括Controller、Filter和Dispatcher的角色与工作原理。Controller不仅包含filter和dispatcher,还涉及到请求处理和重定向操作。Dispatcher通过URL匹配找到对应的Controller并执行相应的方法。Model层由Controller、Service、DAO和POJO组成,Service和DAO分别处理业务逻辑和数据库交互。总结了DAO和Service的主要职责,最后概述了整个MVC流程。



434

被折叠的 条评论
为什么被折叠?



