手写SpringMVC(基本框架)

服务器启动阶段处理

分析服务器启动阶段都都需要初始化什么?

1.初始化Spring容器

  1. 组件扫描包下的类纳入IOC容器管理
  2. 创建视图解析器对象
  3. 创建所有的拦截器对象
  4. 扫描这和包下的所有类org.myspringmvc.web.servlet.mvc.method.annotation,全部实例化,纳入IoC容器管理

Spring容器:ApplicationContext

SpringWeb容器:WebApplicationContext

组件扫描
springmvc.xml文件
java 复制代码
<?xml version="1.0" encoding="utf-8" ?>

<beans>
    <!--
        拦截器
        视图解析器
        组件扫描
    -->

    <component-scan base-package="org.springmvc.oa.controller"/>
<!--    视图解析器-->
    <bean class="org.springmvc.web.servlet.view.InternalResourceViewResolver">
<!--        前缀-->
        <property name="prefix">
            <value>/WEB-INF/jsp/</value>
        </property>
<!--        后缀-->
        <property name="suffix">
            <value>.jsp</value>
        </property>
    </bean>

<!--    拦截器-->
    <interceptors>
        <bean class="org.springmvc.oa.interceptors.Interceptor1"></bean>
        <bean class="org.springmvc.oa.interceptors.Interceptor2"></bean>
    </interceptors>
</beans>

添加解析XML文件的依赖

XML 复制代码
<!--dom4j-->
<dependency>
    <groupId>dom4j</groupId>
    <artifactId>dom4j</artifactId>
    <version>1.6.1</version>
</dependency>
<!--jaxen-->
<dependency>
    <groupId>jaxen</groupId>
    <artifactId>jaxen</artifactId>
    <version>1.1.6</version>
</dependency>
ApplicationContext
java 复制代码
public class ApplicationContext {
    private Map<String, Object> beanMap = new HashMap<>();

    public ApplicationContext(String xmlPath) throws Exception {
        // 组件扫描
        SAXReader saxReader = new SAXReader();
        Document document = saxReader.read(new File(xmlPath));
        Element componentScanElement = (Element)document.selectSingleNode("/beans/context:component-scan");
        String basePackage = componentScanElement.attributeValue("base-package");
        System.out.println("组件扫描:" + basePackage);
        componentScan(basePackage);
        System.out.println("Spring Web容器当下状态:" + beanMap);
    }

    private void componentScan(String basePackage) throws Exception{
        String dirPath = Thread.currentThread().getContextClassLoader().getResource(basePackage.replace(".", "/")).getPath();
        File file = new File(URLDecoder.decode(dirPath));
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for (File classFile : files){
                if(classFile.getName().endsWith(".class")){
                    String className = basePackage + "." + classFile.getName().substring(0, classFile.getName().lastIndexOf("."));
                    Class<?> clazz = Class.forName(className);
                    Constructor<?> defaultCon = clazz.getDeclaredConstructor();
                    Object bean = defaultCon.newInstance();
                    beanMap.put(firstCharLowerCase(clazz.getSimpleName()), bean);
                }
            }
        }
    }

    private String firstCharLowerCase(String simpleName) {
        return simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
    }

    public Object getBean(String beanName){
        return beanMap.get(beanName);
    }
}

组件扫描的beanMap结果

WebApplicationContext
java 复制代码
package org.springmvc.web.context;

import jakarta.servlet.ServletContext;
import org.dom4j.DocumentException;
import org.springmvc.context.ApplicationContext;

import java.lang.reflect.InvocationTargetException;

public class WebApplicationContext extends ApplicationContext {
    private ServletContext servletContext;
    private String springMvcConfigPath;


    public WebApplicationContext() {
    }

    public WebApplicationContext(ServletContext servletContext, String springMvcConfigPath) throws DocumentException, ClassNotFoundException, InvocationTargetException, NoSuchMethodException, InstantiationException, IllegalAccessException {
        super(springMvcConfigPath);
        this.servletContext = servletContext;
        this.springMvcConfigPath = springMvcConfigPath;
    }

    /**
     * 获取
     * @return servletContext
     */
    public ServletContext getServletContext() {
        return servletContext;
    }

    /**
     * 设置
     * @param servletContext
     */
    public void setServletContext(ServletContext servletContext) {
        this.servletContext = servletContext;
    }

    /**
     * 获取
     * @return springMvcConfigPath
     */
    public String getSpringMvcConfigPath() {
        return springMvcConfigPath;
    }

    /**
     * 设置
     * @param springMvcConfigPath
     */
    public void setSpringMvcConfigPath(String springMvcConfigPath) {
        this.springMvcConfigPath = springMvcConfigPath;
    }

    public String toString() {
        return "WebApplicationContext{servletContext = " + servletContext + ", springMvcConfigPath = " + springMvcConfigPath + "}";
    }
}
创建视图解析器对象
InternalResourceViewResolver

内部资源视图解析器

java 复制代码
package org.springmvc.web.servlet.view;

import org.springmvc.web.servlet.View;
import org.springmvc.web.servlet.ViewResolver;

import java.util.Locale;

/*
    内部资源解析器
 */
public class InternalResourceViewResolver implements ViewResolver {
    String prefix;
    String suffix;

    public InternalResourceViewResolver() {
    }

    public InternalResourceViewResolver(String prefix, String suffix) {
        this.prefix = prefix;
        this.suffix = suffix;
    }
    /*
    将逻辑试图名字 转换为物理视图名称 并以View对象返回
     */
    @Override
    public View resolveViewName(String viewName, Locale locale) throws Exception {


        return new InternalResourceView("text/html;charset-UTF-8", prefix+viewName+suffix);
    }

    /**
     * 获取
     * @return prefix
     */
    public String getPrefix() {
        return prefix;
    }

    /**
     * 设置
     * @param prefix
     */
    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    /**
     * 获取
     * @return suffix
     */
    public String getSuffix() {
        return suffix;
    }

    /**
     * 设置
     * @param suffix
     */
    public void setSuffix(String suffix) {
        this.suffix = suffix;
    }

    public String toString() {
        return "InternalResourceViewResolver{prefix = " + prefix + ", suffix = " + suffix + "}";
    }

    /**
     * 获取
     * @return suffix
     */


    /**
     * 获取
     * @return prefix
     */

}

创建视图解析器对象

java 复制代码
private void createViewResolver(Element viewResolverElement) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        String viewResolverClassName=viewResolverElement.attributeValue("class");
        System.out.println("视图解析器类名"+viewResolverClassName);
        Class clazz= Class.forName(viewResolverClassName);
        Object bean = clazz.newInstance();
        //bean节点下子节点property
        List<Element> propertyElements=viewResolverElement.elements(Constant.PROPERTY_TAG_NAME);
        for(Element propertyElement:propertyElements){
            //获取属性名
            String fieldName=propertyElement.attributeValue(Constant.PROPERTY_NAME);
            String value=propertyElement.element(Constant.PROPERTY_VALUE).getTextTrim();
            //将属性名转为set方法
            String setMethodName=fieldNameToSetMethod(fieldName);

            //通过反射机制调用set方法
            Method setMethod = clazz.getDeclaredMethod(setMethodName,String.class);
            setMethod.invoke(bean,value);
        }
        beanMap.put(Constant.VIEW_RESOLVER, bean);
    }
创建拦截器对象
java 复制代码
 private void createInteceptor(Element interceptorElement) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
        List<Element> interceptorElements=interceptorElement.elements("bean");
        List<HandlerInterceptor> interceptors=new ArrayList<>();
        for(Element interceptor:interceptorElements){

            Class<?> clazz=Class.forName(interceptor.attributeValue("class"));
            Object bean=clazz.newInstance();
            interceptors.add((HandlerInterceptor)bean);
        }
        beanMap.put(Constant.INTERCEPROTS,interceptors);
    }

2.初始化HandlerMapping和初始化HandlerAdapter

初始化annotation包下所有类的实例

java 复制代码
 private void createHandlerMapping(String defaultPackage,Map<RequestMappingInfo, HandlerMethod>map) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
        //System.out.println(defaultPackage);
        // 将这个包下所有的类实例化:org.myspringmvc.web.servlet.mvc.method.annotation

        //获取绝对路径
        String dirPath=Thread.currentThread().getContextClassLoader().getResource(defaultPackage.replace('.', '/')).getPath();
        File file=new File(URLDecoder.decode(dirPath));
        //获取里面所有的子文件
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(File classFile : files){
                if(classFile.getName().endsWith(Constant.SUFFER_CLASS)){
                    //全类名
                    String className=defaultPackage+"."+classFile.getName().substring(0,classFile.getName().lastIndexOf("."));
                    Class<?>clazz=Class.forName(className);
                    if(HandlerMapping.class.isAssignableFrom(clazz)){
                        Constructor<?>defaultCon= clazz.getDeclaredConstructor(Map.class);
                        Object bean=defaultCon.newInstance(map);
                        beanMap.put(Constant.HANDLER_MAPPING, bean);
                    }
                    if(HandlerAdapter.class.isAssignableFrom(clazz)){
                        Constructor<?>defaultCon= clazz.getDeclaredConstructor();
                        Object bean=defaultCon.newInstance();
                        beanMap.put(Constant.HANDLER_ADAPTER, bean);
                    }
                }
            }
        }
    }

2.初始化Servlet对象

DispatchServlet init()

java 复制代码
 public void init(){
        //找到springmvc.xml文件
        /*
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc.xml</param-value>
            </init-param>
         */
        //根据以上配置springmvc.xml文件
        //获取SpringConfig对象(Servlet配置信息对象 该对象由web容器自动创建 并且将其传递给init方法
        // )
        ServletConfig servletConfig = this.getServletConfig();
        String contextConfigLocation = servletConfig.getInitParameter(Constant.CONTEXT_CONFIG_LOCATION);
        String springMvcXmlPath=getSpringMvcXmlPath(contextConfigLocation);
        System.out.println("Spring MVC配置文件路径解析完成:" + springMvcXmlPath);



        //初始化Spring Web容器
        WebApplicationContext webApplicationContext = null;
        try {
            webApplicationContext = new WebApplicationContext(this.getServletContext(), springMvcXmlPath);
        } catch (DocumentException e) {
            throw new RuntimeException(e);
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        } catch (InvocationTargetException e) {
            throw new RuntimeException(e);
        } catch (NoSuchMethodException e) {
            throw new RuntimeException(e);
        } catch (InstantiationException e) {
            throw new RuntimeException(e);
        } catch (IllegalAccessException e) {
            throw new RuntimeException(e);
        }
        this.getServletContext().setAttribute(Constant.WEB_APPLICATION_CONTEXT, webApplicationContext);

        //初始化HandlerMapping
        this.handlerMapping=(HandlerMapping)webApplicationContext.getBean(Constant.HANDLER_MAPPING);
        //初始化HandlerAdapter
        this.handlerAdapter=(HandlerAdapter)webApplicationContext.getBean(Constant.HANDLER_ADAPTER);
        //初始化试图解析器
        this.viewResolver=(ViewResolver)webApplicationContext.getBean(Constant.VIEW_RESOLVER);


    }

    private String getSpringMvcXmlPath(String contextConfigLocation) {
        if(contextConfigLocation.startsWith(Constant.CLASSPATH)){
            String path=contextConfigLocation.substring(Constant.CLASSPATH.length()).trim();
            //类路径中找
            String springMvcXmlPath=Thread.currentThread().getContextClassLoader().getResource(path).getPath();
            // 对路径解码,防止路径中有 % 等字符。
            return URLDecoder.decode(springMvcXmlPath, Charset.defaultCharset());

        }
        return null;
    }

doDispath方法

java 复制代码
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            doDispatch(req, resp);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    /**
     * 处理请求的核心方法
     * @param request
     * @param response
     * @throws ServletException
     * @throws IOException
     */
    private void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
        //访问浏览器才能断点到这
        //请求对象获取对应的处理器执行连对象
        HandlerExecutionChain mappedHandler=handlerMapping.getHandler(request);
        //获取处理器适配器对象
        HandlerAdapter adapter = this.handlerAdapter;

        //3.执行拦截器中的preHandle方法
        if(!mappedHandler.applyPreHandle(request,response)){
            return;//false的话
        }
        //4.执行处理器方法 返回ModelAndView
        ModelAndView mv=adapter.handle(request,response,mappedHandler.getHandler());
        System.out.println("处理器执行连对象:"+mappedHandler);


        //5.执行拦截器中postHanlder
        mappedHandler.applyPostHandle(request,response,mv);

        //6.试图解析器 逻辑试图名称转为物理视图名称
        View view=viewResolver.resolveViewName(mv.getView().toString(),request.getLocale());
        view.render(mv.getModel(),request,response);

        //执行拦截器渲染之后的afterCompletetion方法

        mappedHandler.triggerAfterCompletion(request,response,null);
    }
根据请求获取处理器执行链

HandlerExecutionChain mappedHandler=handlerMapping.getHandler(request);

HandlerMapping
java 复制代码
public interface HandlerMapping {
    /**
     * ClassName: HandlerMapping
     * Description: 主要是通过请求获取对应的处理器执行链。
     * Datetime: 2024/4/2 8:50
     * Author: 老杜@动力节点
     * Version: 1.0
     */
    HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception;
}
HandlerMethod
java 复制代码
package org.springmvc.web.method;

import java.lang.reflect.Method;

/*
    处理器方法 controller
 */
public class HandlerMethod {
    /*
        处理器对象
     */
    private Object handler;
    /**
     * 要执行的方法
     */
    private Method method;

    public HandlerMethod() {
    }

    public HandlerMethod(Object handler, Method method) {
        this.handler = handler;
        this.method = method;
    }

    /**
     * 获取
     * @return handler
     */
    public Object getHandler() {
        return handler;
    }

    /**
     * 设置
     * @param handler
     */
    public void setHandler(Object handler) {
        this.handler = handler;
    }

    /**
     * 获取
     * @return method
     */
    public Method getMethod() {
        return method;
    }

    /**
     * 设置
     * @param method
     */
    public void setMethod(Method method) {
        this.method = method;
    }

    public String toString() {
        return "HandlerMethod{handler = " + handler + ", method = " + method + "}";
    }
}
RequestMappingInfo
java 复制代码
package org.springmvc.web.servlet;

import java.util.Objects;

public class RequestMappingInfo {
    private String requestURI;

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        RequestMappingInfo that = (RequestMappingInfo) o;
        return Objects.equals(requestURI, that.requestURI) && Objects.equals(method, that.method);
    }

    @Override
    public int hashCode() {
        return Objects.hash(requestURI, method);
    }

    //GET POST PUT DELETE
    private String method;

    public RequestMappingInfo() {
    }

    public RequestMappingInfo(String requestURI, String method) {
        this.requestURI = requestURI;
        this.method = method;
    }

    /**
     * 获取
     * @return requestURI
     */
    public String getRequestURI() {
        return requestURI;
    }

    /**
     * 设置
     * @param requestURI
     */
    public void setRequestURI(String requestURI) {
        this.requestURI = requestURI;
    }

    /**
     * 获取
     * @return method
     */
    public String getMethod() {
        return method;
    }

    /**
     * 设置
     * @param method
     */
    public void setMethod(String method) {
        this.method = method;
    }

    public String toString() {
        return "RequestMappingInfo{requestURI = " + requestURI + ", method = " + method + "}";
    }
}
RequestMappingHandlerMapping
java 复制代码
package org.springmvc.web.servlet.mvc.method.annotation;

import jakarta.servlet.http.HttpServletRequest;
import org.springmvc.web.constant.Constant;
import org.springmvc.web.context.WebApplicationContext;
import org.springmvc.web.method.HandlerMethod;
import org.springmvc.web.servlet.*;

import java.util.List;
import java.util.Map;

/*
    处理器映射器 专门为 @RequestMapping 注解的类服务的处理器映射器
    通过前端提交的请求 来底层映射要执行的HandlerMethod
 */
public class RequestMappingHandlerMapping implements HandlerMapping {

    private Map<RequestMappingInfo, HandlerMethod> map;

    /*
        创建HandlerMapping对象时 给map赋值
     */

    public RequestMappingHandlerMapping(Map<RequestMappingInfo, HandlerMethod> map) {
        this.map = map;
    }

    @Override
    public HandlerExecutionChain getHandler(HttpServletRequest request) throws Exception {

        //通过request对象 获取请求路径,请求方式 封装程RequestMappingInfo
        RequestMappingInfo requestMappingInfo=new RequestMappingInfo(request.getServletPath(),request.getMethod());
        //******************必须重写 如果补充写 key是对象 对于相同请求创建多个对象 可能认为是不同的
        HandlerExecutionChain handlerExecutionChain =new HandlerExecutionChain();
        //给执行连设置handlerMethod
        handlerExecutionChain.setHandler(map.get(requestMappingInfo));
        //给执行连设置拦截器

        //通过Spring容器
        WebApplicationContext webApplicationContext=(WebApplicationContext)request.getServletContext().getAttribute(Constant.WEB_APPLICATION_CONTEXT);
        List<HandlerInterceptor> interceptors=(List<HandlerInterceptor>)webApplicationContext.getBean(Constant.INTERCEPROTS);
        handlerExecutionChain.setInterceptors(interceptors);
        return handlerExecutionChain;
    }
}
处理器执行链对象
注意:

public RequestMappingHandlerMapping(Map<RequestMappingInfo, HandlerMethod> map) {

this.map = map;

}

包扫描的时候初始化RequestMappingHandlerMapping时 要传入Map对象 所以包扫描要改为下面的代码

java 复制代码
private Map<RequestMappingInfo, HandlerMethod> componentScan(String basePackage) throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {

        Map<RequestMappingInfo, HandlerMethod>handlerMethodMap=new HashMap<>();
        //获取绝对路径
        String dirPath=Thread.currentThread().getContextClassLoader().getResource(basePackage
                .replace(".","/")).getPath();
        File file=new File(URLDecoder.decode(dirPath));//封装为文件
        //获取里面所有的子文件
        if(file.isDirectory()){
            File[] files = file.listFiles();
            for(File classFile:files){
                if(classFile.getName().endsWith(Constant.SUFFER_CLASS)){
                    String className=basePackage+"."+classFile.getName().substring(0,classFile.getName().lastIndexOf("."));

                    Class<?> clazz = Class.forName(className);
                    Constructor<?>defaultCon= clazz.getDeclaredConstructor();
                    //System.out.println("简单类名"+clazz.getSimpleName());
                    Object bean=defaultCon.newInstance();
                    beanMap.put(firstCharLowerCase(clazz.getSimpleName()), bean);
                    //UserController 默认beanName 首字母小写

                    //获取类上面的注解 @Controller注解包围
                    if(clazz.isAnnotationPresent(Controller.class)){//被Controller注解包裹
                        //获取类中所有的方法
                        Method []methods=clazz.getDeclaredMethods();
                        for(Method method:methods){
                            //如果该注解被RequestMapping注解包裹
                            if(method.isAnnotationPresent(RequestMapping.class)){
                                RequestMapping requestMapping=method.getAnnotation(RequestMapping.class);
                                String requestURI=requestMapping.value()[0];
                                String requestMethod=requestMapping.method().toString();//  GET
                                RequestMappingInfo requestMappingInfo=new RequestMappingInfo(requestURI,requestMethod);
                                HandlerMethod handlerMethod=new HandlerMethod();//请求的方法
                                handlerMethod.setHandler(bean);
                                handlerMethod.setMethod(method);
                                handlerMethodMap.put(requestMappingInfo,handlerMethod);
                            }
                        }
                    }
                }
            }

        }
执行拦截器的中preHandler方法
java 复制代码
 public boolean applyPreHandle(HttpServletRequest request, HttpServletResponse response) throws Exception {
        for (int i = 0; i <interceptors.size() ; i++) {
            HandlerInterceptor handlerInterceptor = interceptors.get(i);
            boolean result=handlerInterceptor.preHandle(request,response,handler);

            if(!result){
                //执行拦截器的AfterCompletion
                triggerAfterCompletion(request,response,handler);
                return false;
            }
        }
        return true;
    }
.执行处理器方法 返回ModelAndView
java 复制代码
public interface HandlerAdapter {
    ModelAndView handle(HttpServletRequest request, HttpServletResponse response,Object handler) throws Exception;
}

public class RequestMappingHandlerAdapter implements HandlerAdapter {
    @Override
    public ModelAndView handle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //需要调用处理器方法
        HandlerMethod handlerMethod = (HandlerMethod)handler;

        //获取控制器
        Object controller=handlerMethod.getHandler();

        //获取调用的方法
        Method method=handlerMethod.getMethod();
        ModelMap modelMap=new ModelMap();
        //通过反射机制调用
        String viewName=(String)method.invoke(controller,modelMap);
        //自己写的目前需要 Controller指定ModelMap
        //还有一个特殊要求 要求返回逻辑试图名称

        ModelAndView modelAndView = new ModelAndView();
        modelAndView.setViewName(viewName);
        modelAndView.setModel(modelMap);
        return modelAndView;
    }
}


package org.springmvc.ui;

import java.util.LinkedHashMap;

public class ModelMap extends LinkedHashMap<String, Object> {
    public ModelMap() {
    }
    public ModelMap addAttribute(String name, String value) {
        this.put(name,value);
        return this;
    }
}

package org.springmvc.oa.controller;

import org.springmvc.stereotype.Controller;
import org.springmvc.ui.ModelMap;
import org.springmvc.web.bind.annotation.RequestMapping;
import org.springmvc.web.bind.annotation.RequestMethod;

@Controller
public class UserController {
    @RequestMapping(value ="/index", method = RequestMethod.GET)
    public String index(ModelMap modelMap){
        modelMap.put("name","liwu");
        return "index";
    }
}
执行拦截器中postHanlder
java 复制代码
public void applyPostHandle(HttpServletRequest request, HttpServletResponse response, ModelAndView mv) throws Exception {
        for(int i=interceptors.size()-1;i>=0;i--){
            HandlerInterceptor handlerInterceptor = interceptors.get(i);
            handlerInterceptor.postHandle(request,response,handler,mv);
        }
    }
视图解析器 逻辑试图名称转为物理视图名称
java 复制代码
public interface ViewResolver {
    /*
        解析逻辑视图名称,返回视图对象
     */
    View resolveViewName(String viewName, Locale locale) throws Exception;

}
 public View resolveViewName(String viewName, Locale locale) throws Exception {


        return new InternalResourceView("text/html;charset-UTF-8", prefix+viewName+suffix);
   }

 public void render(Map<String, ?> model, HttpServletRequest request, HttpServletResponse response) throws Exception {
        //设置响应类型
        response.setContentType(contentType);
        //将model数据存储到request域中
        //默认情况下跳转到视图以转发方式
        model.forEach((key,value)->{
            request.setAttribute(key,value);
        });
        request.getRequestDispatcher(path).forward(request,response);
    }
执行拦截器渲染之后的afterCompletetion方法
java 复制代码
 public void triggerAfterCompletion(HttpServletRequest request, HttpServletResponse response, Object o) throws Exception {
        for(int i=interceptors.size()-1;i>=0;i--){
            HandlerInterceptor handlerInterceptor = interceptors.get(i);
            handlerInterceptor.afterCompletion(request, response,handler, (Exception) o);
        }
    }

SSM整合

引入相关依赖

XML 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.springmvc</groupId>
    <artifactId>ssm-annotation</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <!--springmvc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>6.1.4</version>
        </dependency>

        <!--spring jdbc-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>6.1.4</version>
        </dependency>
        <!--mybatis-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.15</version>
        </dependency>
        <!--mybatis spring-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>3.0.3</version>
        </dependency>
        <!--mysql驱动-->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.3.0</version>
        </dependency>
        <!--德鲁伊连接池-->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.2.22</version>
        </dependency>
        <!--jackson-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.17.0</version>
        </dependency>
        <!--servlet api-->
        <dependency>
            <groupId>jakarta.servlet</groupId>
            <artifactId>jakarta.servlet-api</artifactId>
            <version>6.0.0</version>
            <scope>provided</scope>
        </dependency>
        <!--logback-->
        <dependency>
            <groupId>ch.qos.logback</groupId>
            <artifactId>logback-classic</artifactId>
            <version>1.5.3</version>
        </dependency>
        <!--thymeleaf和spring6的整合依赖-->
        <dependency>
            <groupId>org.thymeleaf</groupId>
            <artifactId>thymeleaf-spring6</artifactId>
            <version>3.1.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>RELEASE</version>
            <scope>compile</scope>
        </dependency>
    </dependencies>
    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>

</project>

创建包结构

创建webapp目录

Spring整合Mybatis

编写mybatis.properties

java 复制代码
mybatis.driverClassName=com.mysql.cj.jdbc.Driver
mybatis.url=jdbc:mysql://localhost:3306/db03?useSSL=false&allowPublicKeyRetrieval=true
mybatis.username=root
mybatis.password=123456
mybatis.mapperLocations=classpath*:com/gao/ssm/dao/*Mapper.xml

MybatisProperties

java 复制代码
package com.gao.ssm.dao;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

@Component
@PropertySource("classpath:mybatis.properties")
public class MybatisProperties {
    @Value("${mybatis.driverClassName}")
    private String driverClassName;
    @Value("${mybatis.url}")
    private String url;
    @Value("${mybatis.username}")
    private String username;
    @Value("${mybatis.password}")
    private String password;
    @Value("${mybatis.mapperLocations}")
    private String mapperLocations;

    public MybatisProperties() {
    }

    public MybatisProperties(String driverClassName, String url, String username, String password, String mapperLocations) {
        this.driverClassName = driverClassName;
        this.url = url;
        this.username = username;
        this.password = password;
        this.mapperLocations = mapperLocations;
    }

    /**
     * 获取
     * @return driverClassName
     */
    public String getDriverClassName() {
        return driverClassName;
    }

    /**
     * 设置
     * @param driverClassName
     */
    public void setDriverClassName(String driverClassName) {
        this.driverClassName = driverClassName;
    }

    /**
     * 获取
     * @return url
     */
    public String getUrl() {
        return url;
    }

    /**
     * 设置
     * @param url
     */
    public void setUrl(String url) {
        this.url = url;
    }

    /**
     * 获取
     * @return username
     */
    public String getUsername() {
        return username;
    }

    /**
     * 设置
     * @param username
     */
    public void setUsername(String username) {
        this.username = username;
    }

    /**
     * 获取
     * @return password
     */
    public String getPassword() {
        return password;
    }

    /**
     * 设置
     * @param password
     */
    public void setPassword(String password) {
        this.password = password;
    }

    /**
     * 获取
     * @return mapperLocations
     */
    public String getMapperLocations() {
        return mapperLocations;
    }

    /**
     * 设置
     * @param mapperLocations
     */
    public void setMapperLocations(String mapperLocations) {
        this.mapperLocations = mapperLocations;
    }

    public String toString() {
        return "MybatisProperties{driverClassName = " + driverClassName + ", url = " + url + ", username = " + username + ", password = " + password + ", mapperLocations = " + mapperLocations + "}";
    }
}

MybatisConfig

java 复制代码
package com.gao.ssm.config;

import com.alibaba.druid.pool.DruidDataSource;
import com.gao.ssm.dao.MybatisProperties;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.PlatformTransactionManager;

import javax.sql.DataSource;
import java.io.IOException;

@Configuration
@ComponentScan("com.gao.ssm.dao")
@MapperScan("com.gao.ssm.dao")
public class MybatisConfig {
    @Autowired
    private MybatisProperties mybatisProperties;

    @Bean
    public DataSource dataSource(){
        DruidDataSource dataSource=new DruidDataSource();
        dataSource.setUsername(mybatisProperties.getUsername());
        dataSource.setPassword(mybatisProperties.getPassword());
        dataSource.setUrl(mybatisProperties.getUrl());
        dataSource.setDriverClassName(mybatisProperties.getDriverClassName());
        return dataSource;
    }

    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) throws IOException {
        SqlSessionFactoryBean sqlSessionFactoryBean=new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource);

        ResourcePatternResolver resolver=new PathMatchingResourcePatternResolver();
        Resource[]resources=resolver.getResources(mybatisProperties.getMapperLocations());
        sqlSessionFactoryBean.setMapperLocations(resources);
        return sqlSessionFactoryBean;
    }
    @Bean
    public PlatformTransactionManager platformTransactionManager(DataSource dataSource){
        DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
        dataSourceTransactionManager.setDataSource(dataSource);
        return dataSourceTransactionManager;
    }
}

SpringConfig

java 复制代码
package com.gao.ssm.config;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.transaction.annotation.EnableTransactionManagement;

@Configuration
@ComponentScan("com.gao.ssm.service")
@EnableTransactionManagement
@Import({MybatisConfig.class})
public class SpringConfig {
}

Spring整合Spring MVC

java 复制代码
package com.gao.ssm.config;

import jakarta.servlet.Filter;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

@Configuration
public class WebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
    /*
    配置Spring的配置文件
     */
    @Override
    protected Class<?>[] getRootConfigClasses() {
        return new Class[0];
    }

    /*
        SpringMVC配置文件
     */
    @Override
    protected Class<?>[] getServletConfigClasses() {
        return new Class[]{SpringMvcConfig.class};
    }



    /*
            配置DispatcherServlet的映射规则,即url-pattern
         */
    @Override
    protected String[] getServletMappings() {
        return new String[]{"/"};
    }
    /**
     * Spring的配置
     * @return
     */

    @Override
    protected Filter[] getServletFilters() {
        CharacterEncodingFilter characterEncodingFilter = new CharacterEncodingFilter();
        characterEncodingFilter.setEncoding("UTF-8");
        characterEncodingFilter.setForceRequestEncoding(true);
        characterEncodingFilter.setForceResponseEncoding(true);
        //!--隐藏的HTTP请求方式过滤器 字符编码过滤器之后-->
        HiddenHttpMethodFilter hiddenHttpMethodFilter = new HiddenHttpMethodFilter();

        return new Filter[]{characterEncodingFilter, hiddenHttpMethodFilter};
    }

}

SpringMvcConfig

java 复制代码
package com.gao.ssm.config;

import com.gao.ssm.interceptor.MyInterceptor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.config.annotation.*;
import org.springframework.web.servlet.handler.SimpleMappingExceptionResolver;
import org.thymeleaf.spring6.SpringTemplateEngine;
import org.thymeleaf.spring6.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring6.view.ThymeleafViewResolver;
import org.thymeleaf.templatemode.TemplateMode;
import org.thymeleaf.templateresolver.ITemplateResolver;

import java.util.List;
import java.util.Properties;

@Configuration
@ComponentScan("com.gao.ssm.handler")
@Import({SpringConfig.class})
@EnableWebMvc
//开启注解驱动
public class SpringMvcConfig implements WebMvcConfigurer {
    @Override
    public void configureHandlerExceptionResolvers(List<HandlerExceptionResolver> resolvers) {
        SimpleMappingExceptionResolver simpleHandlerExceptionResolver=new SimpleMappingExceptionResolver();
        Properties prop = new Properties();
        prop.setProperty("java.lang.Exception", "tip");
        simpleHandlerExceptionResolver.setExceptionMappings(prop);
        simpleHandlerExceptionResolver.setExceptionAttribute("e");
        resolvers.add(simpleHandlerExceptionResolver);

    }

    @Bean
    public ThymeleafViewResolver getViewResolver(SpringTemplateEngine springTemplateEngine) {
        ThymeleafViewResolver resolver = new ThymeleafViewResolver();
        resolver.setTemplateEngine(springTemplateEngine);
        resolver.setCharacterEncoding("UTF-8");
        resolver.setOrder(1);
        return resolver;
    }

    @Bean
    public SpringTemplateEngine templateEngine(ITemplateResolver iTemplateResolver) {
        SpringTemplateEngine templateEngine = new SpringTemplateEngine();
        templateEngine.setTemplateResolver(iTemplateResolver);
        return templateEngine;
    }
    public void addInterceptors(InterceptorRegistry registry) {
        MyInterceptor myInterceptor = new MyInterceptor();
        registry.addInterceptor(myInterceptor).addPathPatterns("/**").excludePathPatterns("/test");
    }
    @Override
    public void addViewControllers(ViewControllerRegistry registry) {
        registry.addViewController("/index").setViewName("index");
    }

    @Bean
    public ITemplateResolver templateResolver(ApplicationContext applicationContext) {
        SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver();
        resolver.setApplicationContext(applicationContext);
        resolver.setPrefix("/WEB-INF/thymeleaf/");
        resolver.setSuffix(".html");
        resolver.setTemplateMode(TemplateMode.HTML);
        resolver.setCharacterEncoding("UTF-8");
        resolver.setCacheable(false);//开发时关闭缓存,改动即可生效
        return resolver;
    }

    @Override
    //开启静态资源处理
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

添加事务控制

java 复制代码
@Configuration
@ComponentScan("com.gao.ssm.service")
@EnableTransactionManagement
@Import({MybatisConfig.class})
public class SpringConfig {
}

@Transactional()
@Service
public class EmpServiceImpl implements EmpService{

pojo类编写

java 复制代码
package com.gao.ssm.bean;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDate;
import java.time.LocalDateTime;

/**
 * 员工实体类
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Emp {
    private Integer id; //ID
    private String username; //用户名
    private String password; //密码
    private String name; //姓名
    private Short gender; //性别 , 1 男, 2 女
    private String image; //图像url
    private Short job; //职位 , 1 班主任 , 2 讲师 , 3 学工主管 , 4 教研主管 , 5 咨询师
    private LocalDate entrydate; //入职日期
    private Integer deptId; //部门ID
    private LocalDateTime createTime; //创建时间
    private LocalDateTime updateTime; //修改时间
}

dao编写

java 复制代码
package com.gao.ssm.dao;

import com.gao.ssm.bean.Emp;
import org.apache.ibatis.annotations.Mapper;

import java.util.List;
public interface EmpDao {
    List<Emp> listAll();

    Emp listOne(Emp emp);

    void add(Emp emp);
}

service编写

java 复制代码
package com.gao.ssm.service;


import com.gao.ssm.bean.Emp;

import java.util.List;

public interface EmpService{
    public void register(Emp emp);

    public List<Emp> listAll();

}
package com.gao.ssm.service;

import com.gao.ssm.bean.Emp;
import com.gao.ssm.dao.EmpDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Transactional()
@Service
public class EmpServiceImpl implements EmpService{
    @Autowired
    private EmpDao empDao;

    @Override
    public void register(Emp emp) {
        empDao.add(emp);
    }

    @Override
    public List<Emp> listAll() {
        return empDao.listAll();
    }
}

handler编写

java 复制代码
package com.gao.ssm.handler;

import com.gao.ssm.bean.Emp;
import com.gao.ssm.service.EmpService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;

@RestController
@RequestMapping("/emp")
public class EmpController {
    @Autowired
    private EmpService empService;
    @RequestMapping("/list")
    public String list(){
        return empService.listAll().toString();
    }
}

EmpMapper.xml

java 复制代码
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//MyBatis//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="com.gao.ssm.dao.EmpDao">

    <!-- 查询所有员工 -->
    <select id="listAll" resultType="com.gao.ssm.bean.Emp">
        SELECT * FROM emp
    </select>

    <!-- 根据 ID 查询单个员工 -->
    <select id="listOne" resultType="com.gao.ssm.bean.Emp" parameterType="java.lang.Integer">
        SELECT * FROM emp WHERE id = #{id}
    </select>

    <!-- 添加员工,返回主键 ID -->
    <insert id="add" parameterType="com.gao.ssm.bean.Emp" useGeneratedKeys="true" keyProperty="id">
        INSERT INTO emp(username, name, gender, image, job, entrydate, dept_id, create_time, update_time)
        VALUES (#{username}, #{name}, #{gender}, #{image}, #{job}, #{entrydate}, #{deptId}, #{createTime}, #{updateTime})
    </insert>

</mapper>

测试

html 复制代码
<!DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <meta charset="UTF-8">
    <title>ssm整合</title>
    <!--引入vue-->
    <script th:src="@{/static/js/vue3.4.21.js}"></script>
    <!--引入axios-->
    <script th:src="@{/static/js/axios.min.js}"></script>
</head>
<body>
<div id="app">
    <button @click="getMessage">查询所有用户</button>
    <h1>{{message}}</h1>
</div>
<script th:inline="javascript">
    Vue.createApp({
        data(){
            return {
                message : '很多信息'
            }
        },
        methods : {
            async getMessage(){
                console.log("------------查询")
                let response = await axios.get("/ssm/emp/list")
                console.log(response)
                this.message = response.data
            }
            }
        }).mount("#app")
</script>
</body>
</html>
相关推荐
martian6654 分钟前
信创时代技术栈选择与前景分析:国产替代背景下的战略路径与实践指南
开发语言·科技·系统安全·创业创新
MacroZheng6 分钟前
换掉ES!SpringBoot + Meilisearch实现商品搜索,太方便了!
java·spring boot·后端
{⌐■_■}12 分钟前
【计网】认识跨域,及其在go中通过注册CORS中间件解决跨域方案,go-zero、gin
java·linux·开发语言·c++·中间件·golang·gin
ErizJ20 分钟前
Golang|外观模式和具体逻辑
开发语言·golang·外观模式
ErizJ22 分钟前
Golang | 集合求交
开发语言·后端·golang·集合·交集
猿java26 分钟前
什么是Nginx?它有哪些应用场景?
java·nginx·面试
代码程序猿RIP27 分钟前
【C语言干货】回调函数
c语言·开发语言·数据结构·c++·算法
DBWYX30 分钟前
redis
java·redis·mybatis
mask哥31 分钟前
实用的java技术架构组件汇总
java·spring·微服务·springboot·vo校验·常用java组件
不穿铠甲的穿山甲43 分钟前
gradle-tasks.register(‘classesJar‘, Jar)解析
android·java·gradle·groovy