在之前的一篇博客中已经简单的实现了spring的IOC和DI功能,本文将在之前的基础上实现mvc功能。
一 什么是MVC
MVC简单的说就是一种软件实现的设计模式,将整个系统进行分层,M(model 数据模型,业务逻辑层) 、V(view 视图层)、C(controller 控制器调度),实现应用程序的分层开发。实现原理如下图:
主要执行步骤:
1 用户在发起request请求给前端控制器;
2 控制器接收到请求后,经过一系统的过滤器,找到对应的请求处理映射;
3 根据请求映射获得请求处理适配器;
4 适配器将对请求进行处理并将处理结果(ModelAndView)返回给前端控制器;
5 前端处理器将处理结果交给视图解析器解析;
6 视图解析器将解析的结果返回给控制器;
7 控制器将结果返回给用户。
二 简单模拟实现
创建一个核心控制器(DispatcherServlet)继承HttpServlet,配置在web.xml中,并指定要初始化的参数
<!-- 核心servlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.wl.test.spring.mvc.DispatcherServlet</servlet-class>
<!-- 初始化参数 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:application.properties</param-value>
</init-param>
<!-- 启动时加载 -->
<load-on-startup>0</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
核心控制器,在web容器启动时执行init方法进行文件的初始化
public class DispatcherServlet extends HttpServlet { private List<HandlerMapping> handlerMappingList = new ArrayList<HandlerMapping>(); private Map<HandlerMapping, HandlerAdapter> adapterMap = new HashMap<>(); @Override
public void init(ServletConfig config) throws ServletException {
// web.xml 配置核心servlet 获取配置的信息
String configFile = config.getInitParameter("contextConfigLocation");
//定义一个当前上下文对象,实现基础包的扫描、IOC、DI
ApplicationContext context = new ApplicationContext(configFile.replace("classpath:", ""));
//获取扫描到的有Controller注解的类
List<Object> controllerList = context.getControllerList();
//初始化HandlerMapping
initHandlerMapping(controllerList);
//初始化HandlerAdapter
initHandlerAdapter();
} private void initHandlerAdapter() {
if (handlerMappingList.size() == 0) {
return;
} handlerMappingList.forEach(handlerMapping -> {
Method method = handlerMapping.getMethod();
//方法的参数 <参数索引,参数名字>
Map<Integer, String> paramMap = new HashMap<>(); //使用了注解参数
Annotation[][] annos = method.getParameterAnnotations();
if(annos.length > 0){
for(int i=0; i<annos.length; i++){
for(Annotation anno : annos[i]){
if(anno instanceof RequestParam){
RequestParam requestParam = (RequestParam) anno;
String paramName = requestParam.value(); paramMap.put(i, paramName);
}
}
}
}
//直接用的servlet参数,如HttpServletRequest
Class<?>[] paramTypes = method.getParameterTypes();
if(paramTypes.length > 0){
for(int i=0; i<paramTypes.length; i++){
Class<?> typeClass = paramTypes[i];
if (typeClass == HttpServletRequest.class || typeClass == HttpServletResponse.class) {
String paramName = typeClass.getName(); paramMap.put(i, paramName);
}
}
} HandlerAdapter handlerAdapter = new HandlerAdapter(paramMap);
adapterMap.put(handlerMapping, handlerAdapter);
});
} /**
* 完成请求方法与请求处理实例的映射关系
* @param controllerList
*/
private void initHandlerMapping(List<Object> controllerList) {
if(controllerList.size() == 0){
return;
} controllerList.forEach(controllerObj -> {
//类上的请求路径
String classRequestUrl = "";
if (controllerObj.getClass().isAnnotationPresent(RequestMapping.class)) {
RequestMapping classRequestMapping = controllerObj.getClass().getAnnotation(RequestMapping.class);
if(classRequestMapping != null){
classRequestUrl += urlHandler(classRequestMapping.value());
}
}
//方法上的请求路径
Method[] methods = controllerObj.getClass().getDeclaredMethods();
if(methods.length > 0){
for(int i=0; i<methods.length; i++){
String methodRequestUrl = "";
Method method = methods[i];
//必须是public修饰的方法
if(method.getModifiers() == Modifier.PUBLIC){
if (method.isAnnotationPresent(RequestMapping.class)) {
RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);
if(methodRequestMapping != null){
methodRequestUrl += urlHandler(methodRequestMapping.value());
} String requestUrl = classRequestUrl + methodRequestUrl; HandlerMapping handlerMapping = new HandlerMapping();
handlerMapping.setMethod(method);
handlerMapping.setUrl(requestUrl); handlerMapping.setControllerInstance(controllerObj);
handlerMappingList.add(handlerMapping);
}
} }
} }); } /**
* url处理
* @param url
* @return
*/
public String urlHandler( String url){
if(!url.startsWith("/")){
url = "/" + url;
}
if(url.endsWith("/")){
url = url.substring(0, url.length() - 1);
}
return url;
} @Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
} @Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doDispatcher(req, resp);
} /**
* 请求处理
* @param req
* @param resp
*/
private void doDispatcher(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
req.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=UTF-8"); String contextUrl = req.getContextPath();
String requestUrl = req.getRequestURI(); String url = requestUrl.replace(contextUrl, "");
HandlerMapping handlerMapping = null;
for(int i=0; i<handlerMappingList.size(); i++){
if(url.equals(handlerMappingList.get(i).getUrl())){
handlerMapping = handlerMappingList.get(i);
break;
}
}
if(handlerMapping == null){
resp.getWriter().write("404, 未知的请求!");
}else{
HandlerAdapter adapter = adapterMap.get(handlerMapping);
try {
Object result = adapter.handler(req, resp, handlerMapping); viewResolve(req, resp, result);
} catch (Exception e) {
e.printStackTrace();
resp.getWriter().write("500, 服务器发生异常!");
}
} } /**
* 视图解析 返回
* @param result
*/
private void viewResolve(HttpServletRequest request, HttpServletResponse response, Object result) throws Exception{
if (result.getClass() == ModelAndView.class) {
ModelAndView mv = (ModelAndView) result;
String view = mv.getViewName();
Map<String, Object> dataMap = mv.getData();
if(dataMap.size() > 0){
for(String key : dataMap.keySet()){
request.setAttribute(key, dataMap.get(key));
}
}
request.getRequestDispatcher(view).forward(request, response);
}
} }
ApplicationContext的具体实现如下,主要实现对执行资源文件的扫描,并完成IOC和DI
public class ApplicationContext { /**
* 配置文件
*/
private static String PROPERTIES_FILE = ""; /**
* 初始化一个集合,存放扫描到的class对象
*/
private List<Class<?>> classList = Collections.synchronizedList(new ArrayList<>()); /**
* 初始化map 存放别名与对象实例
*/
private Map<String, Object> aliasInstanceMap = new HashMap<>(); public ApplicationContext(String fileName) {
PROPERTIES_FILE = fileName;
try {
String basePackage = getBasePackage(PROPERTIES_FILE); buildAliasInstanceMap(basePackage); doAutowired(); } catch (Exception e) {
e.printStackTrace();
}
} /**
* 完成别名与实例的映射
*/
public void buildAliasInstanceMap(String basePackage) throws Exception { scanClasses(basePackage); if(classList.size() == 0){return;} for(Class<?> clazz : classList){
if (clazz.isAnnotationPresent(Controller.class) || clazz.isAnnotationPresent(Service.class)
|| clazz.isAnnotationPresent(Autowired.class)) {
String alias = getAlias(clazz);
Object obj = aliasInstanceMap.get(alias); //如果别名实例映射关系已经存在,则给出提示
if(obj != null){
throw new Exception("alias is exist!");
}else{
aliasInstanceMap.put(alias, clazz.newInstance());
}
}
} System.out.println(aliasInstanceMap);
} /**
* 属性对象的注入
*/
public void doAutowired(){
if (aliasInstanceMap.size() == 0) {
return;
} aliasInstanceMap.forEach((k, v)->{ Field[] fields = v.getClass().getDeclaredFields(); for(Field field : fields){
if (field.isAnnotationPresent(Autowired.class)) {
String alias = ""; Autowired autowired = field.getAnnotation(Autowired.class);
if(autowired != null){
//注入的对象是接口时,由于不知道接口有几个实现类,所以就必须在Autowired或者Qualifier上指定要注解的具体的实现类
if(!"".equals(autowired.value())){
alias = autowired.value();
}else{
Qualifier qualifier = field.getAnnotation(Qualifier.class);
if(qualifier != null){
alias = qualifier.value();
}
}
} if ("".equals(alias)) {
alias = getAlias(field.getType());
} Object instance = null;
if(!"".equals(alias)){
instance = aliasInstanceMap.get(alias);
} field.setAccessible(true); try {
field.set(v, instance);
} catch (IllegalAccessException e) {
e.printStackTrace();
}
} }
}); } /**
* 获取对象的别名,如果注解中配置了别名,别使用配置的别名,否则默认使用类名首字母小写
* @param clazz
* @return
*/
public String getAlias(Class<?> clazz){
String alias = "";
Controller controller = clazz.getAnnotation(Controller.class);
if(controller != null){
alias = controller.value();
}
Service service = clazz.getAnnotation(Service.class);
if (service != null) {
alias = service.value();
}
Autowired autowired = clazz.getAnnotation(Autowired.class);
if(autowired != null){
alias = autowired.value();
} //注解中没有配置别名
if("".equals(alias)){
String simpleName = clazz.getSimpleName();
alias = simpleName.substring(0, 1).toLowerCase() + simpleName.substring(1);
}
return alias;
} /**
* 跟据基础包名读取包及子包中的类对象
* @param basePackage
*/
public void scanClasses(String basePackage){
if(basePackage == null || "".equals(basePackage)){return;} doScan(basePackage);
System.out.println(classList);
} private void doScan(String basePackage) {
String path = basePackage.replaceAll("\\.","/");
URL url = this.getClass().getClassLoader().getResource(path);
File file = new File(url.getFile());
file.listFiles(new FileFilter() {
@Override
public boolean accept(File childFile) {
String fileName = childFile.getName();
if(childFile.isDirectory()){
//当前文件是目录,递归 扫描下级子目录下的class文件
doScan(basePackage + "." + fileName);
}else{
if(fileName.endsWith(".class")){
String className = basePackage + "." + fileName.replace(".class", "");
try {
Class<?> clazz = this.getClass().getClassLoader().loadClass(className);
classList.add(clazz);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
return false;
}
});
} /**
* 从配置的属性文件中读取要扫描的包
* @return
*/
public String getBasePackage(String fileName) throws IOException {
String basePackage;
Properties prop = new Properties();
InputStream in = this.getClass().getClassLoader().getResourceAsStream(fileName);
prop.load(in);
basePackage = prop.getProperty("basePackage");
return basePackage;
} /**
* 根据beanName 获取
* @param beanName
* @return
*/
public Object getBean(String beanName){
return aliasInstanceMap.get(beanName);
} /**
* 获取所有标注了controller的注解
* @return
*/
public List<Object> getControllerList(){
List<Object> controllerList = new ArrayList<>();
if(aliasInstanceMap.size() > 0) {
aliasInstanceMap.values().forEach(obj -> {
if(obj.getClass().isAnnotationPresent(Controller.class)){
controllerList.add(obj);
}
});
}
return controllerList;
} public static void main(String[] args) throws Exception {
String fileName = "application.properties";
ApplicationContext context = new ApplicationContext(fileName);
String basePackage = context.getBasePackage(PROPERTIES_FILE); context.buildAliasInstanceMap(basePackage); context.doAutowired();
//测试
UserController controller = (UserController) context.getBean("userController");
controller.save();
} }
请求映射HandlerMapping,用来存放请求url和要执行的方法和方法所在对象实例,构建请求与请求处理的映射关系
public class HandlerMapping { private String url;
private Method method;
private Object controllerInstance; //此处省去getter和setter
}
处理适配器HandlerAdapter,每一个请求映射都有一个请求处理适配器来完成请求的处理(handler)
public class HandlerAdapter { private Map<Integer, String> paramMap; public HandlerAdapter(Map<Integer, String> paramMap){
this.paramMap = paramMap;
} public Object handler(HttpServletRequest request, HttpServletResponse response, HandlerMapping handlerMapping) throws Exception {
Method method = handlerMapping.getMethod();
Object classInstance = handlerMapping.getControllerInstance(); int paramNum = method.getParameterCount();
Object[] paramObj = new Object[paramNum];
for(int i=0; i<paramNum; i++){
String paramName = paramMap.get(i);
if(paramName.equals(HttpServletRequest.class.getName())){
paramObj[i] = request;
}else if(paramName.equals(HttpServletResponse.class.getName())){
paramObj[i] = response;
} else {
paramObj[i] = request.getParameter(paramName);
}
}
Object result = method.invoke(classInstance, paramObj);
return result;
} public Map<Integer, String> getParamMap() {
return paramMap;
} public void setParamMap(Map<Integer, String> paramMap) {
this.paramMap = paramMap;
}
}
处理结果ModelAndView,用来存放当前请求要返回的视图和数据
public class ModelAndView { private String viewName;
private Map<String, Object> data = new HashMap<>(); public ModelAndView(String viewName) {
this.viewName = viewName;
} public void addAttribute(String name, Object value){
data.put(name, value);
} //此处省略getter和setter
}
视图展示返回的结果
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>hello mvc...</h3>
<hr/>
用 户 信 息:<%=request.getAttribute("user") %>
</body>
</html>
浏览器端显示信息
以上是模拟实现springmvc的主要代码,实现的源代码我已经上传在github,感兴趣的朋友可以访问
https://github.com/wlzq/spring