就简单的写一写,面试可能会问,我一个中级java开发,不会太多东西,大佬绕道!
1 建立一个Servlet工程,分三层写好
…
2.建立web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>手写一个springMVC</display-name>
<!-- 手写一个servlet -->
<servlet>
<servlet-name>zbmvc</servlet-name>
<servlet-class>com.zb.mvcframework.servlet.ZbDispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>application.properties</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>zbmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
- 写注解:
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 0:30.
*/
// 作用域范围
@Target(ElementType.TYPE)
// 生命周期运行阶段可以运行
@Retention(RetentionPolicy.RUNTIME)
// 可见的
@Documented
public @interface MvcController {
String value() default "";
}
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 0:30.
*/
// 作用域范围
@Target(ElementType.TYPE)
// 生命周期运行阶段可以运行
@Retention(RetentionPolicy.RUNTIME)
// 可见的
@Documented
public @interface MvcService {
String value() default "";
}
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 0:37.
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MvcResponseBody {
String value() default "";
}
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 0:30.
*/
// 作用域范围
@Target(ElementType.PARAMETER)
// 生命周期运行阶段可以运行
@Retention(RetentionPolicy.RUNTIME)
// 可见的
@Documented
public @interface MvcRequestParam {
String value() default "";
}
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 0:37.
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MvcRequestMapping {
String value() default "";
}
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 0:30.
*/
// 作用域范围
@Target(ElementType.FIELD)
// 生命周期运行阶段可以运行
@Retention(RetentionPolicy.RUNTIME)
// 可见的
@Documented
public @interface MvcAutowried {
String value() default "";
}
package com.zb.mvcframework.anno;
import java.lang.annotation.*;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.anno
* this class created by ZB
* this data on 15:28.
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Mapper {
String value() default "";
}
- 手写一个Servlet:
package com.zb.mvcframework.servlet;
import com.zb.mvcframework.anno.MvcAutowried;
import com.zb.mvcframework.anno.MvcController;
import com.zb.mvcframework.anno.MvcRequestMapping;
import com.zb.mvcframework.anno.MvcService;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.util.*;
/**
* class descriptions:(核心入口类)
* the package's name is com.zb.mvcframework
* this class created by ZB
* this data on 0:03.
*/
public class ZbDispatcherServlet extends HttpServlet {
// properties
private Properties contextConfig = new Properties();
// 所有的类
private List<String> classNames = new ArrayList<String>();
// IOC
private Map<String,Object> ioc = new HashMap<String,Object>();
// Haddler
private Map<String,Method> handlerMapping = new HashMap<String,Method>();
@Override
public void init(ServletConfig config) throws ServletException {
// 加载配置文件 config.getInitParameter("contextConfigLocation) 是init-param参数name
doLoadConfig(config.getInitParameter("contextConfigLocation"));
// 解析配置文件,扫描相关的包.类, 这里用到Properties对像.getProperty("scanPackage")拿到对应的包名
doScanner(contextConfig.getProperty("scanPackage"));
// 初始化所有相关类,并且保存到IOC容器中
doInstance();
// 完成DI
doAutowried();
// 创建HandlerMapping 和 URL+Method建立关系
initHandlerMapping();
}
/**
*
*/
private void initHandlerMapping() {
if(ioc.isEmpty()){return;}
for(Map.Entry<String,Object> entry : ioc.entrySet()){
Class<?> aClass = entry.getValue().getClass();
// 如果是一个Controller用HandlerMapping
if(!aClass.isAnnotationPresent(MvcController.class)){
continue;
}
String baseUrl = "";
if(!aClass.isAnnotationPresent(MvcRequestMapping.class)){
baseUrl = aClass.getAnnotation(MvcRequestMapping.class).value();
// 获取方法
Method[] methods = aClass.getMethods();
for (Method method : methods){
if(!method.isAnnotationPresent(MvcRequestMapping.class)){continue;}
String url = "/"+baseUrl+"/"+method.getAnnotation(MvcRequestMapping.class).value().replaceAll("/+","/");
handlerMapping.put(url,method);
}
}
}
}
/**
* 依赖注入
*/
private void doAutowried() {
if(ioc.isEmpty()){
return;
}
for(Map.Entry<String,Object> entry : ioc.entrySet()){
Field[] declaredFields = entry.getValue().getClass().getDeclaredFields();
for(Field f : declaredFields){
// 不存在继续
if(!f.isAnnotationPresent(MvcAutowried.class)){continue;}
// 存在
// 获取注解
String beanName = f.getAnnotation(MvcAutowried.class).value();
if("".equals(beanName)){
beanName = f.getType().getName();
}
// 可见,setter方法
f.setAccessible(true); // 强制授权
try {
f.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
/**
* 将解析后的文件放入IOC中并实例化,IOC就是一个Map<String,Object>, 最后用List包起来
*/
private void doInstance() {
if(classNames.isEmpty()){return;}
try{
// List不为空的情况下,遍历出List,用反射机制或者类名/注解.value()值
for(String className : classNames){
Class<?> aClass = Class.forName(className);
// 带注解的类扫描
if (aClass.isAnnotationPresent(MvcController.class)) {
String beanName = lowerFirstCase(aClass.getSimpleName());// 类名
ioc.put(beanName,aClass.newInstance());
}else if(aClass.isAnnotationPresent(MvcService.class)){
// 类名首字母小写
// 自定义
String beanName = aClass.getAnnotation(MvcService.class).value(); // 获取value值,如果value没有值默认类名
if("".equals(beanName)){
beanName = lowerFirstCase(aClass.getSimpleName());
}
Object o = aClass.newInstance(); // 实例化
ioc.put(beanName, o);
// 接口全称作为key , 接口实例作为值
Class<?>[] interfaces = aClass.getInterfaces();
for(Class<?> i : interfaces){
if(ioc.containsKey(i.getName())){
throw new Exception("接口已经存在!");
}
ioc.put(i.getName(),o);
}
}else{
continue;
}
}
}catch (Exception e){
}
}
// 首字母小写
private String lowerFirstCase(String simpleName) {
char[] c = simpleName.toCharArray();
c[0] += 32;
return String.valueOf(c);
}
/**
* 解析xml/properties中的配置
* @param scanPackage
*/
private void doScanner(String scanPackage) {
// 将所有包名全部转为com/zb/xx/xx/ 文件存储
URL url = this.getClass().getClassLoader().getResource("/" + scanPackage.replaceAll("\\.", "/"));
File classDir = new File(url.getFile());
// 迭代
for(File file : classDir.listFiles()){
// 递归,有无这个目录,没有:包名.类名, 因为没有进行解析
if(file.isDirectory()){
doScanner(scanPackage + "." + file.getName());
}else{
if(!file.getName().contains(".class")){continue;}
// 包名.类名去掉.class文件后缀
String className = (scanPackage + "." + file.getName().replaceAll(".class","")).trim();
classNames.add(className);
}
}
}
/**
* 初始化加载配置init-param
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
// 类路径下取得properties
InputStream is = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
// 通过load方法直接调用,load方法参数InputStream
contextConfig.load(is);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != is){
try {
is.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//super.doGet(req, resp);
this.doPost(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
try {
doDispatch(req, resp);
} catch (IOException e) {
resp.getWriter().write("500");
}
}
private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws IOException {
if(this.handlerMapping.isEmpty())
{
return;
}
String url = req.getRequestURI();
String contextPath = req.getContextPath();
url = url.replace(contextPath,"").replaceAll("/+","/");
if(!this.handlerMapping.containsKey(url)){
resp.getWriter().write("404");return;
}
Method method = this.handlerMapping.get(url);
Map<String,String[]> params = req.getParameterMap();
String simpleName = lowerFirstCase(method.getDeclaringClass().getSimpleName());
String[] params1 = {""};
if(!"".equals(params.get("xx")) && null != params.get("xx")){
params1 = params.get("xx");
}
try {
method.invoke(ioc.get(simpleName),new Object[]{req,resp,params1});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
- Controller调用:
package com.zb.mvcframework.demo.action;
import com.zb.mvcframework.anno.*;
import com.zb.mvcframework.demo.service.MyService;
import java.util.List;
/**
* class descriptions:()
* the package's name is com.zb.mvcframework.demo.action
* this class created by ZB
* this data on 0:24.
*/
@MvcController
@MvcRequestMapping("/api")
public class MyAction {
@MvcAutowried
private MyService myService;
@MvcRequestMapping("/select")
@MvcResponseBody
public List select(@MvcRequestParam("xx") String xx){
List select = myService.select(xx);
return select;
}
}
就是一个大概,写的跟垃圾一样,看看就行了,这个东西百度一堆,淘宝也一堆,就没事合计发一个,顺便体验一下论坛流程