写在前面

本文使用JAVA类库完成一个迷你版的springmvc框架,旨在深入理解springmvc的内部实现原理。

目录结构

spring 自定义 Environment 自定义springmvc_ide

自定义注解

元注解:就是注解的注解

@Retention

@Retention(RetentionPolicy.SOURCE)   //注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS)     // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得,
@Retention(RetentionPolicy.RUNTIME)  // 注解会在class字节码文件中存在,在运行时可以通过反射获取到

@Target

@Target(ElementType.CONSTRUCTOR)	 //用于描述构造器
  @Target(ElementType.FIELD)		 //成员变量、对象、属性(包括enum实例)
  @Target(ElementType.LOCAL_VARIABLE)    //用于描述局部变量
  @Target(ElementType.METHOD)		 //用于描述方法
  @Target(ElementType.PACKAGE)		 //用于描述包
  @Target(ElementType.PARAMETER)	 //用于描述参数
  @Target(ElementType.TYPE)		 //用于描述类、接口(包括注解类型) 或enum声明

@Documented

标记注解表示是否将注解信息加入JAVA文档中

controller

@Documented //javadoc
@Target(ElementType.TYPE) //注解作用在类上
@Retention(RetentionPolicy.RUNTIME) //限制注解的生命周期
public @interface Controller {
	/**
	 * 作用于该类上的注解有一个value属性,其实就是controller
	 * @return
	 */
	public String value();
}

Qualifier

@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Qualifier {
	public String value();
}

Repository

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Repository {
	public String value();
}

RequestMapping

@Documented
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
	public String value();
}

Service

@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Service {
	public String value();
}

DispatcherServlet

dispacherServlet是springmvc的核心控制器,是一个HttpServlet的子类,下面我们来实现它;

@WebServlet(name="dispatcherServlet",urlPatterns="/*",loadOnStartup=1,
initParams={@WebInitParam(name="base-package",value="com.flashhold")})
public class DispatcherServlet extends HttpServlet{
	//扫描的基包
	private String basePackage="";
	//基包下面所有带包路径权限定类名
	private List<String> packageNames=new ArrayList<String>();
	//注解实例化 注解上的名称:实例化对象
	private Map<String,Object>instanceMap=new HashMap<>();
	//带包路径的权限定名称:注解的名称
	private Map<String,String>nameMap=new HashMap<>();
	//url地址和方法的映射关系
	private Map<String,Method>urlMethodMap=new HashMap<>();
	//method和权限定名映射关系
	private Map<Method,String>methodPackageMap=new HashMap<>();

@WebServlet 以前我们定义的servlet需要在Web.xml中配置,Servlet3.0以后出现了基于注解的servlet,在springmvc中DispacherServlet启动需要配置扫描包,比如:   本文为了方便直接在初始化时引入。

<context:component-scan base-package="com.flashhold.myspringmvc">
</context:component-scan>

初始化dispacherServlet

初始化完成的动作:

  • 扫描基包下的类拿到信息
  • 拿到@Controller、@Service、@Repository注解相应的名称,初始化修饰的类
  • 扫描类中的字段,如果有@Qualifier注解,我们需要完成注入
  • 扫描@RequestMapping注解,如果有完成到Controller的关系映射。
@Override
    public void init(ServletConfig config) throws ServletException {
        // TODO Auto-generated method stub
        basePackage=config.getInitParameter("base-package");
        try {
            //扫描基包得到全部的带包路径全限定名
            scanBasePackage(basePackage);
            //把所有带有注解的类实例化放入map中key为注解的名称
            instance(packageNames);
            //注入
            springIOC();
            //完成url地址和方法的映射关系
            HandlerUrlMethodMap();
        } catch (Exception e) {
            // TODO: handle exception
        }
        
    }

扫描包

private void scanBasePackage(String basePackage) {
		URL url=this.getClass().getClassLoader().getResource(basePackage.replaceAll("\\.", "/"));
		File basePackageFile=new File(url.getPath());
		System.out.println("scan"+basePackageFile);
		File[]cFiles=basePackageFile.listFiles();
		for (File file : cFiles) {
			if(file.isDirectory()){
				scanBasePackage(basePackage+"."+file.getName());
			}else if(file.isFile()){
				packageNames.add(basePackage+"."+file.getName().split("\\.")[0]);
			}
		}
		
	}

实例化

private void instance(List<String> packageNames) throws ClassNotFoundException, 
InstantiationException, IllegalAccessException {
		// TODO Auto-generated method stub
		if(packageNames.size()<1){
			return;
		}
		for (String string : packageNames) {
			Class c=Class.forName(string);
			if(c.isAnnotationPresent(Controller.class)){
				Controller controller=(Controller) c.getAnnotation(Controller.class);
				String controllerName=controller.value();
				instanceMap.put(controllerName, c.newInstance());
				nameMap.put(string, controllerName);
				System.out.println("Controller:"+string+"value:"+controller.value());
			}else if(c.isAnnotationPresent(Service.class)){
				Service service=(Service) c.getAnnotation(Service.class);
				String serviceName=service.value();
				instanceMap.put(serviceName, c.newInstance());
				nameMap.put(string, serviceName);
				System.out.println("Service:"+string+"value:"+service.value());
			}else if(c.isAnnotationPresent(Repository.class)){
				Repository repository=(Repository) c.getAnnotation(Repository.class);
				String repositoryName=repository.value();
				instanceMap.put(repositoryName, c.newInstance());
				nameMap.put(string, repositoryName);
				System.out.println("Controller:"+string+"value:"+repository.value());
			}
		}
	}

注入

private void springIOC() throws IllegalArgumentException, IllegalAccessException {
		// TODO Auto-generated method stub
		for (Map.Entry<String,Object>entry: instanceMap.entrySet()) {
			Field[] fields=entry.getValue().getClass().getDeclaredFields();
			for (Field field : fields) {
				if(field.isAnnotationPresent(Qualifier.class)){
					String name=field.getAnnotation(Qualifier.class).value();
					field.setAccessible(true);
					field.set(entry.getValue(), instanceMap.get(name));
				}
			}
		}
	}

URL映射

private void HandlerUrlMethodMap() throws ClassNotFoundException {
		// TODO Auto-generated method stub
		if(packageNames.size()<1){
			return;
		}
		for (String string : packageNames) {
			Class c=Class.forName(string);
			if(c.isAnnotationPresent(Controller.class)){
				Method[]methods=c.getMethods();
				StringBuffer baseUrl=new StringBuffer();
				if(c.isAnnotationPresent(RequestMapping.class)){
					RequestMapping requestMapping=(RequestMapping) c.getAnnotation(RequestMapping.class);
					baseUrl.append(requestMapping.value());
				}
				for (Method method : methods) {
					if(method.isAnnotationPresent(RequestMapping.class)){
						RequestMapping requestMapping=method.getAnnotation(RequestMapping.class);
						baseUrl.append(requestMapping.value());
						urlMethodMap.put(baseUrl.toString(), method);
						methodPackageMap.put(method, string);
					}
				}
			}
		}
		
	}

doGet和doPost

@Override
	protected void doGet(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		// TODO Auto-generated method stub
		 doPost(req, resp);
	}
	@Override
	protected void doPost(HttpServletRequest req, HttpServletResponse resp)
			throws ServletException, IOException {
		String uri=req.getRequestURI();
		String contextPath=req.getContextPath();
		String path=uri.replaceAll(contextPath, "");
		Method method=urlMethodMap.get(path);
		if(method!=null){
			String packageName=methodPackageMap.get(method);
			String controllerName=nameMap.get(packageName);
			UserController userController=(UserController) instanceMap.get(controllerName);
			try {
				method.setAccessible(true);
				method.invoke(userController);
			} catch (Exception e) {
				// TODO: handle exception
			}
					
		}
	}

Controller层

@Controller("userController")
@RequestMapping("/user")
public class UserController {
	@Qualifier("userServiceImpl")
	private UserService userService;
	@RequestMapping("/insert")
	public void insert(){
		userService.insert();
	}
}

Service接口和实现类

public interface UserService {
	public void insert();
 }

@Service("userServiceImpl")
public class UserServiceImpl implements UserService{
    @Qualifier("userDaoImpl")
    private UserDao userDao;
    @Override
    public void insert() {
        // TODO Auto-generated method stub
        userDao.insert();
    }

}

Dao层接口和实现类

public interface UserDao {
	public void insert();
}


@Repository("userDaoImpl")
public class UserDaoImpl implements UserDao{
    @Override
    public void insert() {
        // TODO Auto-generated method stub
        System.out.println("我是dao");
    }
    
}