运行环境
JDK :17
IntelliJ IDEA : 2022.3
Tomcat:8.5.86
前期工作
- 先创建一个新的Maven项目,按照图示操作:
- 在这里我们选择
Maven Archetype
选项,写好项目名称:Handwriting-SpringMVC,以及路径并选好Archetype为webapp。点击creat创建。
- 编辑环境:按照图示选择对应的选项,最后点击OK即可!
注意:在开始本项目前你需要了解一些MVC常用注解及其流程。
项目结构
项目类继承关系
main结构
java
com.bruce.controller
:用户自定义的SpringMVC控制层
com.bruce.pojo
:用户自定义的对象(User)
com.bruce.service
:用户自定义的接口和实现类(UserService、UserServiceImpl)
com.springmvc.annotation
:SpringMVC的相关注解
com.springmvc.context
:Spring容器
com.springmvc.exception
:用户自定义异常
com.springmvc.handler
:负责处理HTTP请求的组件
com.springmvc.servlet
:SpringMVC的核心控制器
com.springmvc.xml
:解析springmvc.xml文件
resources
目前只有一个springmvc.xml
文件
webapp
WEB-INF
:Java的Web应用的安全目录
test结构
java
com.bruce.test
:测试XML解析工具
resources
NULL
两个重要的XML文件和一个JSP文件:
springmvc.xml
:SpringMVC的配置文件web.xml
:web应用的配置文件。user.jsp
:后面用来跳转的JSP文件index.jsp
:自动生成的,可以不用管 (非必须)
项目流程
- 在启动
Tomcat
后就会自动解析webapp中的WEB-INF中的web.xml
,所以我们先配置web.xml
- 创建
DispatcherServlet
(前端控制器)和WebApplicationContext
(spring容器) - 接下来会执行
DispatcherServlet
中的init()
方法,创建Spring容器,并从springmvc.xml
文件中读取base-package中的包路径,并生成对象,存放在iocMap
中 - 当我们的对象生成完毕之后,就会执行
initHandlerMappring()
方法,遍历spring容器中的iocMap
,遍历其中所有的Controller类型的对象的Class对象,然后判断每一个Class对象中的所有方法,将有@RequestMapping
注解的方法封装成一个MyHandler
对象,其中包含@RequestMapping
注解的名字(url),Controller对象(controller),该方法(method)。然后将这个MyHandler
对象放到handlerList
集合中。 - 当有get请求的时候,就会执行
DispatcherServlet
中的doGet()
方法。遍历handlerList
,寻找浏览器url请求路径和我们MyHandler
对象中储存的@RequestMapping
中相等的对象,然后定义一个参数的Object数组,经过一系列的关于@RequestParam
参数的判断,将浏览器请求路径中的参数放到对应位置的Object数组中,然后通过handler.getMethod().invoke()
执行这个方法就可以调用控制中的方法并获得返回值,然后做一个返回值类型的判断:String还是Json。最后做跳转JSP或返回JSON数据的代码逻辑。 - 最后运行的结果如下,成功跳转到我们的
user.jsp
文件中:
注意:整体的流程最好自己debug一遍,印象会更深刻!
pom.xml文件:
<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/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>Handwriting-SpringMVC</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>Handwriting-SpringMVC Maven Webapp</name>
<url>http://maven.apache.org</url>
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--解析XML文件-->
<dependency>
<groupId>dom4j</groupId>
<artifactId>dom4j</artifactId>
<version>1.6.1</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.5</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<!--JSON转换工具-->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.16.1</version>
</dependency>
</dependencies>
<build>
<finalName>Handwriting-SpringMVC</finalName>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<source>17</source>
<target>17</target>
<!--JDK8新特性,能够获取到方法中的参数-->
<!--[request, response, name]-->
<compilerArgs>
<arg>-parameters</arg>
</compilerArgs>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
main
com.bruce.controller包:
UserController类:
package com.bruce.controller;
import com.bruce.pojo.User;
import com.bruce.service.UserService;
import com.springmvc.annotation.Autowired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.RequestMapping;
import com.springmvc.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: 控制器
* @Version: 1.0
**/
@Controller
public class UserController {
// 持有业务逻辑层的对象
@Autowired
UserService userService;
@RequestMapping("/user/query")
public String findUsers(HttpServletRequest request, HttpServletResponse response, String name) {
// 处理响应的中文乱码问题
response.setContentType("text/html;charset=utf-8");
String message = userService.getMessage(name);
request.setAttribute("message",message);
//转发到user.jsp
return "forward:/user.jsp";
}
@RequestMapping("/user/queryjson")
@ResponseBody
public List<User> queryUsers(HttpServletRequest request, HttpServletResponse response, String name){
return userService.findUsers(name);
}
}
com.bruce.pojo包:
User类:
package com.bruce.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: TODO
* @Version: 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Integer id;
private String name;
private String pass;
}
com.bruce.service包:
UserService接口:
package com.bruce.service;
import com.bruce.pojo.User;
import java.util.List;
public interface UserService {
List<User> findUsers(String name);
String getMessage(String name);
}
com.bruce.service.impl包:
UserServiceImpl类:
package com.bruce.service.impl;
import com.bruce.pojo.User;
import com.bruce.service.UserService;
import com.springmvc.annotation.Service;
import java.util.ArrayList;
import java.util.List;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: UserService的实现类
* @Version: 1.0
**/
@Service // 默认就是类名首字母小写
public class UserServiceImpl implements UserService {
@Override
public List<User> findUsers(String name) {
// 模拟数据
List<User> users = new ArrayList<>();
users.add(new User(1,"老王","admin"));
users.add(new User(2,"小王","12345"));
return users;
}
@Override
public String getMessage(String name) {
return "我是getMessage方法," + name;
}
}
com.springmvc.annotation包:
Autowired注解:
package com.springmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.FIELD) // 元注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
String value() default "";
}
Controller注解:
package com.springmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE) // 元注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
String value() default "";
}
RequestMapping注解:
package com.springmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD) // 元注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
String value() default "";
}
RequestParm注解:
package com.springmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.PARAMETER) // 元注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestParm {
String value() default "";
}
ResponseBody注解:
package com.springmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.METHOD) // 元注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ResponseBody {
}
Service注解:
package com.springmvc.annotation;
import java.lang.annotation.*;
@Target(ElementType.TYPE) // 元注解
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
String value() default "";
}
com.springmvc.context包:
WebApplicationContext
类:Spring容器
package com.springmvc.context;
import com.springmvc.annotation.Autowired;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.Service;
import com.springmvc.exception.ContextException;
import com.springmvc.xml.XmlParser;
import java.io.File;
import java.lang.reflect.Field;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: Spring容器
* @Version: 1.0
**/
public class WebApplicationContext {
// classpath:springmvc.xml
String contextConfigLocation;
List<String> classNameList = new ArrayList<>();
// Spring的Ioc容器
public Map<String, Object> iocMap = new ConcurrentHashMap<>();
public WebApplicationContext(String contextConfigLocation) {
this.contextConfigLocation = contextConfigLocation;
}
/**
* 初始化Spring容器
*/
public void refresh() {
// 1.解析springmvc.xml文件
// split方法用于将字符串根据指定的正则表达式进行拆分,这里得到的是springmvc.xml
String basePackage = XmlParser.getbasePackage(contextConfigLocation.split(":")[1]);
// 这里通过逗号分隔成了两个字符串:com.bruce.service 和 com.bruce.controller
String[] basePackages = basePackage.split(",");
if (basePackages.length > 0) {
for (String pack : basePackages) {
// com.bruce.service
// com.bruce.controller
excuteScanPackage(pack);
}
}
System.out.println("扫描后的结果是:" + classNameList);
// 2.实例化Spring容器中的bean
excuteInstance();
// Ioc容器中的对象是:
System.out.println("spring的Ioc容器对象是:" + iocMap);
// 3.需要实现spring容器中对象的注入
excuteAutowired();
}
/**
* 扫描包
*/
public void excuteScanPackage(String pack) {
// /com/bruce/service
URL url = this.getClass().getClassLoader().getResource("/" + pack.replaceAll("\\.", "/"));
String path = url.getFile();
File dir = new File(path);
for (File f : dir.listFiles()) {
if (f.isDirectory()) {
// 当前是一个文件目录
excuteScanPackage(pack + "." + f.getName());
} else {
// 文件目录下文件 获取全路径
String className = pack + "." + f.getName().replaceAll(".class", "");
classNameList.add(className);
}
}
}
/**
* 实例化Spring容器中的bean对象
*/
public void excuteInstance() {
if (classNameList.size() == 0) {
// 没有扫描到需要实例化的类
throw new ContextException("没有要实例化的class!");
}
try {
for (String className : classNameList) {
Class<?> clazz = Class.forName(className);
if (clazz.isAnnotationPresent(Controller.class)) {
// 控制层的类 com.bruce.controller
// UserController -> userController 控制层对象的名字
String beanName = clazz.getSimpleName().substring(0, 1).toLowerCase() + clazz.getSimpleName().substring(1);
iocMap.put(beanName, clazz.newInstance());
} else if (clazz.isAnnotationPresent(Service.class)) {
// 业务逻辑层的类 com.bruce.service.impl
Service serviceAnnotation = clazz.getAnnotation(Service.class);
String beanName = serviceAnnotation.value();
if ("".equals(beanName)) {
Class<?>[] interfaces = clazz.getInterfaces();
for (Class<?> c1 : interfaces) {
String beanName1 = c1.getSimpleName().substring(0, 1).toLowerCase() + c1.getSimpleName().substring(1);
// 接口名字作为beanName
iocMap.put(beanName1, clazz.newInstance());
}
} else {
iocMap.put(beanName, clazz.newInstance());
}
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
}
/**
* 实现spring容器中的对象的依赖注入
*/
private void excuteAutowired() {
try {
if (iocMap.isEmpty()) {
throw new ContextException("没有找到初始化的bean对象");
}
// Map的一种迭代方法
for (Map.Entry<String, Object> entry : iocMap.entrySet()) {
// 获取到了一个一个对象名称
String key = entry.getKey();
// 获取到了一个一个对象实例
Object bean = entry.getValue();
Field[] declaredFields = bean.getClass().getDeclaredFields();
for (Field declaredField : declaredFields) {
if (declaredField.isAnnotationPresent(Autowired.class)) {
Autowired autowiredAnnotation = declaredField.getAnnotation(Autowired.class);
String beanName = autowiredAnnotation.value();
// 进一步判断是否设置了 value 属性,如果未设置,则根据字段的类型推断 bean 的名称。
if (beanName.equals("")) {
// 这个拿到的其实是属性类的全名称
Class<?> type = declaredField.getType();
// 做剪枝操作
beanName = type.getSimpleName().substring(0, 1).toLowerCase() + type.getSimpleName().substring(1);
}
// 暴力反射,因为Controller层的属性类有可能是私有的
declaredField.setAccessible(true);
// 属性注入 调用反射给属性赋值
declaredField.set(bean, iocMap.get(beanName));
}
}
}
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
com.springmvc.exception包:
ContextException类:自定义异常
package com.springmvc.exception;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: 自定义异常
* @Version: 1.0
**/
public class ContextException extends RuntimeException {
public ContextException(String message) {
super(message);
}
public ContextException(Throwable cause) {
super(cause);
}
@Override
public String getMessage() {
return super.getMessage();
}
}
com.springmvc.handler包:
MyHandler类:自定义的处理器对象
package com.springmvc.handler;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.lang.reflect.Method;
/**
* @Author: Juechen
* @Date: 2024/4/25
* @Description: 自定义的处理器对象
* @Version: 1.0
**/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MyHandler {
private String url;
private Object controller;
private Method method;
}
com.springmvc.servlet包:
DispatcherServlet
类:SpringMVC的核心控制器
package com.springmvc.servlet;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.springmvc.annotation.Controller;
import com.springmvc.annotation.RequestMapping;
import com.springmvc.annotation.RequestParm;
import com.springmvc.annotation.ResponseBody;
import com.springmvc.context.WebApplicationContext;
import com.springmvc.exception.ContextException;
import com.springmvc.handler.MyHandler;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: SpringMVC的核心控制器
* @Version: 1.0
**/
public class DispatcherServlet extends HttpServlet {
private WebApplicationContext webApplicationContext;
// 存储url和对象方法的映射
List<MyHandler> handlerList = new ArrayList<>();
@Override
public void init() throws ServletException {
// 1.Servlet初始化的时候,读取初始化的参数 classpath:springmvc.xml
String contextConfigLocation = this.getServletConfig().getInitParameter("contextConfigLocation");
// 2.创建Spring容器
webApplicationContext = new WebApplicationContext(contextConfigLocation);
// 3.初始化Spring容器
webApplicationContext.refresh();
// 4.初始化请求映射 /user/query ----> Controller ----> method ----->parameter
initHandlerMappring();
System.out.println(handlerList);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 进行请求分发处理
excuteDispatch(req, resp);
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
/**
* 初始化请求映射
*/
private void initHandlerMappring() {
//判断Iocmap中是否有bean对象
if (webApplicationContext.iocMap.isEmpty()) {
throw new ContextException("Spring容器为空");
}
for (Map.Entry<String, Object> entry : webApplicationContext.iocMap.entrySet()) {
Class<?> clazz = entry.getValue().getClass();
if (clazz.isAnnotationPresent(Controller.class)) {
Method[] declaredMethods = clazz.getMethods();
for (Method declaredMethod : declaredMethods) {
if (declaredMethod.isAnnotationPresent(RequestMapping.class)) {
RequestMapping requestMappingAnnotation = declaredMethod.getAnnotation(RequestMapping.class);
// ("/user/query")
String url = requestMappingAnnotation.value();
// 构造一个自定义的处理器对象
MyHandler handler = new MyHandler(url, entry.getValue(), declaredMethod);
handlerList.add(handler);
}
}
}
}
}
/**
* 请求的分发处理
*/
public void excuteDispatch(HttpServletRequest req, HttpServletResponse resp) {
MyHandler handler = getHandler(req);
try {
if (handler == null) {
resp.getWriter().print("<h1>404 NOT FOUND!</h1>");
} else {
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
//定义一个参数的数组
Object[] params = new Object[parameterTypes.length];
for (int i = 0; i < parameterTypes.length; i++) {
Class<?> parameterType = parameterTypes[i];
if ("HttpServletRequest".equals(parameterType.getSimpleName())) {
params[i] = req;
} else if ("HttpServletResponse".equals(parameterType.getSimpleName())) {
params[i] = resp;
}
}
// 获取请求中的参数集合
Map<String, String[]> parameterMap = req.getParameterMap();
for (Map.Entry<String, String[]> entry : parameterMap.entrySet()) {
String name = entry.getKey(); // "name"
String value = entry.getValue()[0]; // "bruce"
int index = hasRequestParam(handler.getMethod(), name);
if (index != -1) {
params[index] = value;
} else {
// 不加@RequestParam的情况
List<String> names = getParameterNames(handler.getMethod());
System.out.println(names);
for (int i = 0; i < names.size(); i++) {
if (name.equals(names.get(i))) {
params[i] = value;
break;
}
}
}
}
//调用控制中的方法
Object result = handler.getMethod().invoke(handler.getController(), params);
if (result instanceof String) {
// 跳转jsp
String viewName = (String) result;
if (viewName.contains(":")) {
// forward:/user.jsp
String viewType = viewName.split(":")[0];
String viewPage = viewName.split(":")[1];
if (viewType.equals("forward")) {
req.getRequestDispatcher(viewPage).forward(req, resp);
} else {
// redirect:/user.jsp
resp.sendRedirect(viewPage);
}
} else {
// 默认就是转发
req.getRequestDispatcher(viewName).forward(req, resp);
}
} else {
// 返回JSON数据
Method method = handler.getMethod();
if (method.isAnnotationPresent(ResponseBody.class)) {
ObjectMapper objectMapper = new ObjectMapper();
String json = objectMapper.writeValueAsString(result);
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.print(json);
out.flush();
out.close();
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 获取请求对应的handler
*
* @return
*/
public MyHandler getHandler(HttpServletRequest req) {
String requestURI = req.getRequestURI();
for (MyHandler myHandler : handlerList) {
if (myHandler.getUrl().equals(requestURI)) {
return myHandler;
}
}
return null;
}
/**
* 判断控制器方法的参数是否有RequestParm注解,且找到对应的value值
*
* @param method
* @param name
* @return
*/
public int hasRequestParam(Method method, String name) {
Parameter[] parameters = method.getParameters(); //method:public void com.bruce.controller.UserController.findUsers
for (int i = 0; i < parameters.length; i++) {
Parameter p = parameters[i];
boolean b = p.isAnnotationPresent(RequestParm.class);
if (b) {
RequestParm requestParm = p.getAnnotation(RequestParm.class);
String requestParmValue = requestParm.value();
if (name.equals(requestParmValue)) {
return i;
}
}
}
return -1;
}
/**
* 获取控制器方法的参数的名字
*
* @param method
* @return
*/
public List<String> getParameterNames(Method method) {
List<String> list = new ArrayList<>();
for (Parameter parameter : method.getParameters()) {
String parameterName = parameter.getName();
list.add(parameterName);
}
return list;
}
}
com.springmvc.xml包:
XmlParser
类:解析springmvc.xml文件
package com.springmvc.xml;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;
import java.io.InputStream;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: 解析springmvc.xml文件
* @Version: 1.0
**/
public class XmlParser {
public static String getbasePackage(String xml) {
try {
SAXReader saxReader = new SAXReader();
// 获取XML文件的输入流
InputStream inputStream = XmlParser.class.getClassLoader().getResourceAsStream(xml);
// 使用SAXReader解析XML,并将结果存储在Document对象中
Document document = saxReader.read(inputStream);
// 获取XML文档的根元素
Element rootElement = document.getRootElement();
// 在根元素中查找名为"component-scan"的子元素
Element element = rootElement.element("component-scan");
// 获取"component-scan"元素的名为"base-package"的属性
Attribute attribute = element.attribute("base-package");
// 返回"base-package"属性的文本值,即要扫描的基础包名
return attribute.getText();
} catch (DocumentException e) {
e.printStackTrace();
}
return "";
}
}
springmvc.xml
文件:
<?xml version="1.0" encoding="UTF-8" ?>
<beans>
<!--设置使用注解的类所在的jar包-->
<component-scan base-package="com.bruce.service,com.bruce.controller"></component-scan>
</beans>
web.xml
文件:
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!--SpringMVC的核心控制器-->
<servlet>
<servlet-name>DispatcherServlet</servlet-name>
<servlet-class>com.springmvc.servlet.DispatcherServlet</servlet-class>
<!--SpringMVC的配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!--Web服务器一旦启动,Servlet就会实例化创建对象,然后初始化-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>DispatcherServlet</servlet-name>
<!--/表示所有请求都能被DispatcherServlet访问-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
user.jsp文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>User.jsp</title>
</head>
<body>
<h1>${requestScope.message}</h1>
</body>
</html>
index.jsp文件(自动生成的):
<html>
<body>
<h2>Hello World!</h2>
</body>
</html>
test
com.bruce.test包:
TestSpringMvc类:测试解析XML
package com.bruce.test;
import com.springmvc.xml.XmlParser;
import org.junit.Test;
/**
* @Author: Juechen
* @Date: 2024/4/24
* @Description: TODO
* @Version: 1.0
**/
public class TestSpringMvc {
@Test
public void testreadXml() {
String basePackage = XmlParser.getbasePackage("springmvc.xml");
System.out.println(basePackage);
}
}