浅谈对SpringMVC框架的理解及实现
本文是简单分析SpringMVC源码及运行原理后,搭建的简易版SpringMVC框架。可实现正常工作中常用到的基本功能。主要是用于加深对SpringMVC框架的理解。
SpringMVC的工作流程
- 用户发送请求至DispatcherServlet
- DispatcherServlet收到请求调用HandlerMapping处理器映射器
- DispatcherServlet通过HandlerAdapter处理器适配器调用处理器
- 执行处理器(Controller)
- Controller执行完成返回ModelAndView
- HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServle
Spring的两大核心思想就是IOC(控制反转),DI(依赖注入),AOP(面向切面编程)。
那么
为什么Controller和Service要加注解。
Spring是如何实现依赖注入。
SpringMVC是如何将不同类型、不同个数的参数进行传递的。
SpringMVC是如何通过请求地址找到相应的Controller和方法的。
SpringMVC的实现原理
从任意web项目的web.xml可以看到,项目启动时,首先加载的是 org.springframework.web.servlet.DispatcherServlet,从SpringMVC的工作流程可以看到。SpringMVC的最核心的控制器,就是DispatcherServlet,也就是一个Servlet。那么DispatcherServlet在启动时,做了哪些事。
以下代码,均为本人查看源码后,对于SpringMVC的理解,搭建的简易版框架,与源码有一定差距,主要是用于理解SpringMVC。如有不实之处,请予以指出。
源码下载地址:
https://download.csdn.net/download/geniuslesserpanda/10778632
此项目为Maven项目,引用jar包:servlet-api.jar.
一、注解的声明
通常我们用的注解有
@Controller
@Service
@Autowired
@RequestMapping
@RequestParam
我们需要实现将这几种注解声明出来,每种注解的Target,即作用域不同,可自行修改。例如:
@Target({java.lang.annotation.ElementType.TYPE}) //作用于类上
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
@Documented//表明该注解将被包含在javadoc中 @Inherited:说明子类可以继承父类中的该注解
public @interface Controller {
String value() default "";
}
二、DispatcherServlet 初始化工作和依赖注入的实现。
1.声明集合对象,用于后期使用。
public class DispatcherServlet extends HttpServlet{
private static final long serialVersionUID = 1L;
//存放所有.class名称
private List<String> classNames = new ArrayList<String>();
//存放所有带@Controller、@Service类的实体
private Map<String,Object> beans = new HashMap<String,Object>();
//请求地址和方法的映射关系
private Map<String,Object> handleMapping = new HashMap<String,Object>();
2.获取配置的 base-package 路径,加载该路径下的所有.class,将类名放至内存集合List中,下面简称classNames。
/**
* 加载该路径下的所有类
* 此参数通常通过spring配置文件的 base-package 定义
* @param basePackage
*/
private void scanPackage(String basePackage){
URL url = this.getClass().getClassLoader().getResource("/"+basePackage.replaceAll("\\.", "/"));
String path = url.getPath();
File baseFile = new File(path);
String[] fileArray = baseFile.list();
for(String fileName:fileArray){
File file = new File(path+"/"+fileName);
if(file.isDirectory()){
scanPackage(basePackage+"."+fileName);
}else{
classNames.add(basePackage+"."+fileName);
}
}
}
3.遍历.class集合对象(classNames),将带有@Controller,@Service注解的类实例化,放至内存集合Map中。下面简称 beans 。
@Controller 的 key: @RequestMapping 的值 value:Controller的实例
@Service 的 key:@Service的值 value:Service的实例
/**
* 将带有@Controller、@Service注解的类实体化
*/
private void instance(){
if(classNames.size()<=0){
System.out.println("----扫描basePackage失败----");
return;
}
for(String className:classNames){
try {
className = className.replace(".class", "");
Class<?> clzz = Class.forName(className);
if(clzz.isAnnotationPresent(Controller.class)){
RequestMapping mapping = clzz.getAnnotation(RequestMapping.class);
String key = mapping.value();
Object value = clzz.newInstance();
beans.put(key, value);
}else if(clzz.isAnnotationPresent(Service.class)){
Service service = clzz.getAnnotation(Service.class);
String key = service.value();
Object value = clzz.newInstance();
beans.put(key, value);
}else{
continue;
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
4.依赖注入。
遍历所有的beans,拿到带有@Controller 注解的类的实体。
拿到该 Controller 下的所有带@Resource @Autowired 等所有需要注入变量。
通过注解的 value,去内存集合Map(benas)中相应的实体。
打开变量的权限。将实体注入到 Controller的变量中。
/**
* 依赖注入
*/
private void ioc(){
if(beans.size()<=0){
System.out.println("----扫描Controller、Service失败----");
return;
}
for (Map.Entry<String, Object> entry : beans.entrySet()) {
try {
Object instance = entry.getValue();//获取bean实例
Class<?> clzz = instance.getClass();
if(clzz.isAnnotationPresent(Controller.class)){
Field[] fieldArray = clzz.getDeclaredFields();
for(Field f:fieldArray){
if(f.isAnnotationPresent(Autowired.class)){
Autowired auto = f.getAnnotation(Autowired.class);
String key = auto.value();
Object vaule = beans.get(key);
f.setAccessible(true);
f.set(instance, vaule);
}else{
continue;
}
}
}else{
continue;
}
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
5.建立请求地址和Method的映射关系。
遍历.class集合对象(classNames),拿到所有带@Controller注解的类。拿到Controller的请求路径,及@RequestMapping注解的值,下面简称 path。
拿到该 Controller 下所有带有@RequestMapping注解的方法,并获取到注解的值,下面简称 methodPath。
将 path+methodPath 作为key,Method 作为value,放至内存队列,下面简称 handleMapping.
/**
* 建立请求地址和Method的映射关系
*/
private void HandlerMapping(){
for(String className:classNames){
try {
className = className.replace(".class", "");
Class<?> clzz = Class.forName(className);
if(clzz.isAnnotationPresent(Controller.class)){
RequestMapping requestMapping = clzz.getAnnotation(RequestMapping.class);
String path = requestMapping.value();
Method[] methods = clzz.getMethods();
for(Method method:methods){
if(method.isAnnotationPresent(RequestMapping.class)){
RequestMapping mapping = method.getAnnotation(RequestMapping.class);
String methodPath = mapping.value();
handleMapping.put(path+methodPath, method);
}else{
continue;
}
}
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
6.用户请求达到DispatcherServlet后,即可通过请求地址找到对应的Method。然后通过策略模式,解析请求参数,并调用对应的Method。
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String uri = req.getRequestURI();
String context = req.getContextPath();
String url = uri.replace(context, "");
String controller = "/"+url.split("/")[1];
Method method = (Method) handleMapping.get(url);
HandlerAdapterImpl adapter = (HandlerAdapterImpl) beans.get(HANDELADAPTER);
Object[] param = adapter.hand(req, resp, method, beans);
Object instance = beans.get(controller);
try {
method.invoke(instance, param);
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
参数解析器主要代码:
@Service("requestParamArgumentResolver")
public class RequestParamArgumentResolver implements ArgumentResolver{
public boolean support(Class<?> clzz, int paramIndex, Method method) {
Annotation[][] an = method.getParameterAnnotations();
Annotation[] paramAns = an[paramIndex];
for (Annotation paramAn : paramAns) {
if (RequestParam.class.isAssignableFrom(paramAn.getClass())) {
return true;
}
}
return false;
}
public Object argumentResolver(HttpServletRequest request, HttpServletResponse resp, Class<?> clzz, int paramIndex,
Method method) {
Annotation[][] annots = method.getParameterAnnotations();
Annotation[] paramAns = annots[paramIndex];
for(Annotation ann:paramAns){
if(RequestParam.class.isAssignableFrom(ann.getClass())){
RequestParam param = (RequestParam) ann;
String key = param.value();
return request.getParameter(key);
}
}
return null;
}
}
处理器主要代码:
@Service("handlerAdapter")
public class HandlerAdapterImpl implements HandlerAdapterService{
public Object[] hand(HttpServletRequest request, HttpServletResponse resp, Method method,
Map<String, Object> beans) {
Class<?>[] paramType = method.getParameterTypes();
Object[] args = new Object[paramType.length];
Map<String,Object> argumentResolvers = getBeansOfType(ArgumentResolver.class, beans);
int paramIndex = 0;
int i = 0;
for(Class<?> clzz:paramType){
for(Map.Entry<String, Object> entry:argumentResolvers.entrySet()){
ArgumentResolver resolver = (ArgumentResolver) entry.getValue();
if(resolver.support(clzz, paramIndex, method)){
args[i++] = resolver.argumentResolver(request, resp, clzz, paramIndex, method);
}
}
paramIndex++;
}
return args;
}
private Map<String,Object> getBeansOfType(Class<?> clzz,Map<String,Object> beans){
Map<String,Object> argumentResolvers = new HashMap<String,Object>();
for(Map.Entry<String, Object> entry:beans.entrySet()){
Class<?>[] clzzArray = entry.getValue().getClass().getInterfaces();
for(Class<?> cl:clzzArray){
if(clzz.isAssignableFrom(cl)){
argumentResolvers.put(entry.getKey(), entry.getValue());
}
}
}
return argumentResolvers;
}
}