All Downloads are FREE. Search and download functionalities are using the official Maven repository.

cn.yangjunda.servlet.DispatcherServlet Maven / Gradle / Ivy

package cn.yangjunda.servlet;

import cn.yangjunda.annotation.*;
import cn.yangjunda.multipart.MultipartFile;
import cn.yangjunda.multipart.commons.CommonsMultipartFile;
import cn.yangjunda.util.*;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

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.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.math.BigDecimal;
import java.util.*;
import java.util.Map.Entry;


/**请求几种处理类
 * @author juanda
 *
 */
public class DispatcherServlet extends HttpServlet {
	private static final long serialVersionUID = 1378531571714153483L;
	
	/** 要扫描的包,只有在这个包下并且加了注解的才会呗扫描到 */
	private static String PACKAGE = null;
	
	private static final String CONTROLLER_KEY = "controller";
	
	private static final String METHOD_KEY = "method";

	private static final String ENTRY_KEY = "entry";
	
	/** 存放Controller中url和方法的对应关系,格式:{url:{controller:实例化后的对象,method:实例化的方法}} */
	private static Map> urlMethodMapping = new HashMap>();

	private Map handlerMapping = new HashMap<>();

	private Set> classes;

	private Configuration configuration;


	// 上传文件存储目录
	private static final String UPLOAD_DIRECTORY = "upload";

	// 上传配置
	private static final int MEMORY_THRESHOLD   = 1024 * 1024 * 3;  // 3MB
	private static final int MAX_FILE_SIZE      = 1024 * 1024 * 40; // 40MB
	private static final int MAX_REQUEST_SIZE   = 1024 * 1024 * 50; // 50MB


	public DispatcherServlet() {  
        super();  
    } 
	
	/**
	 * 初始化方法,用于实例化扫描到的对象,并做注入和url映射(注:该方法逻辑上已经判断了,只执行一次)
	 */
	@Override
	public void init(ServletConfig config) throws ServletException {

		// 只处理一次
		if (urlMethodMapping.size() > 0) {
			return;
		}
		long startTime=System.currentTimeMillis();
		System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  init");
		System.err.println("INFO: juanda-mvc 初始化开始");
		XMLReader xmlReader = new XMLReader();
		xmlReader.readXML();
		configuration = xmlReader.getConfiguration();
		System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  init");
		System.err.println("INFO: juanda-mvc 读取XML配置文件");
		PACKAGE = configuration.getPackagePath();
		if(PACKAGE==null){
			System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  init");
			System.err.println("INFO: juanda-mvc 读取properties配置文件");
			PACKAGE = PropertiesUtil.getProperty("packagePath");
		}
		if(StringUtils.isEmpty(PACKAGE)){
			throw new RuntimeException("配置文件中不存在packagePath");
		}
		// 开始扫描包下全部class文件
		System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  init");
		System.err.println("INFO: juanda-mvc 开始扫描包");
		classes = ClassTools.getClasses(PACKAGE);
		
		// 存放Controller和Service的Map,格式:{beanName:实例化后的对象} 
		Map instanceNameMap = new HashMap();
		// 存放Service接口类型与接口实例对象的Map,格式:{Service.instance.class:实现类实例化后的对象} 
		Map, Object> instanceTypeMap = new HashMap, Object>();
		
		// 组装instanceMap
		buildInstanceMap(classes, instanceNameMap, instanceTypeMap);
		
		// 开始注入
		doIoc(instanceNameMap, instanceTypeMap);
		
		// 注入完之后开始映射url和method
		buildUrlMethodMapping(instanceNameMap, urlMethodMapping);
		long endTime=System.currentTimeMillis();
		System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  init");
		System.err.println("INFO: juanda-mvc 初始化成功 持续时间:"+(endTime-startTime)+"ms");
    }
	
	@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 {  
    	// 完整路径
    	String url = req.getRequestURI();
		// 跟路径
    	String path = req.getContextPath();
    	// 计算出method上配置的路径,如果用户写了多个"///",只保留一个
    	String finallyUrl = url.replace(path, "").replaceAll("/+", "/");
    	
    	// 取出这个url对应的Controller和method
    	Map map = urlMethodMapping.get(finallyUrl);
    	if (map != null) {
			Method method = (Method) map.get(METHOD_KEY);
			Entry entry = (Entry) map.get(ENTRY_KEY);
			Class clazz = entry.getValue().getClass();
			if(!handleHttpRequestMethod(req,resp,method)){
				return;
			}
			Object object;
			try {
				String[] paramNames = ASMGetRealNameUtil.getMethodParameterNamesByAsm4(clazz,method);
				// 封装需要注入的参数
				List paramValue = buildParamObject(req, resp, method,paramNames);

				// 没有参数的场合
				if (paramValue.size() == 0) {
					object = method.invoke(map.get(CONTROLLER_KEY));
				}else {
					// 有参数的场合
					object = method.invoke(map.get(CONTROLLER_KEY), paramValue.toArray());
				}
				if(method.isAnnotationPresent(JuandaResponseBody.class)){
					ResponseJsonUtil.json(resp,object);
				}
			} catch (Exception e) {
				e.printStackTrace();
				throw new RuntimeException("juanda-mvc 执行url对应的method失败!");
			}


		}else {
			if(Objects.equals(url, "/") ||path.contains("html")||path.contains("xhtml")||path.contains("jsp")){
				return;
			}else {
//				throw new RuntimeException("请求地址不存在!");
			}
		}

    }

    /**
     * 封装需要注入的参数
     * @param req
     * @param resp
     * @param method
     * @return
     */
	private List buildParamObject(HttpServletRequest req, HttpServletResponse resp, Method method, String[] paramNames) {
		
		// 封装需要注入的参数,目前只支持request和response以及加了@RequestParam标签的基本数据类型的参数注入
		Parameter[] parameters = method.getParameters();
		List paramValue = new ArrayList();
		int i = 0;
		for (Parameter parameter : parameters) {
			// 当前参数有别名注解并且别名不为空
			if(parameter.isAnnotationPresent(RequestParam.class) && !parameter.getAnnotation(RequestParam.class).value().isEmpty()){
				// 我们获取
				String value = req.getParameter(parameter.getAnnotation(RequestParam.class).value());

				if(value==null&&(!"\n\t\t\n\t\t\n\ue000\ue001\ue002\n\t\t\t\t\n".equals(parameter.getAnnotation(RequestParam.class).defaultValue()))){
					value = parameter.getAnnotation(RequestParam.class).defaultValue();
				}
				if(parameter.getAnnotation(RequestParam.class).required()){
					if(value==null){
						throw new NullPointerException("字段:"+parameter.getAnnotation(RequestParam.class).value()+"为必须的,值不能为空");
					}
				}
				Object o = null;
				try {
					 o = adapter(parameter.getType(),value.trim(),req);
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}catch (NullPointerException e){
					o = null;
				}

				paramValue.add(o);
			}else if(parameter.isAnnotationPresent(RequestObject.class) && !parameter.getAnnotation(RequestObject.class).value().isEmpty()){
				// 我们获取
				try {
					String classname = null;
					String name = null;
					String className = null;
					String paramsName = null;
					for (Class clasz : classes) {
						name = clasz.getName();
						className = name.substring((clasz.getName().lastIndexOf(".") + 1), clasz.getName().length()).trim();
						paramsName= parameter.getAnnotation(RequestObject.class).value().trim().substring(0, 1).toUpperCase() + parameter.getAnnotation(RequestObject.class).value().substring(1);
						if(Objects.equals(className, paramsName)){
							classname = clasz.getName();
						}
					}
					paramValue.add(AutoPackObjectUtil.getObject(req,Class.forName(classname)));
				} catch (Exception e) {
					throw new RuntimeException("开始注入对象时出现了异常");
				}
			}else if (parameter.getParameterizedType().getTypeName().contains("HttpServletRequest")) {
				paramValue.add(req);
			}else if (parameter.getParameterizedType().getTypeName().contains("HttpServletResponse")) {
				paramValue.add(resp);
			}else if (parameter.getParameterizedType().getTypeName().contains("HttpSession")) {
				paramValue.add(req.getSession());
			}else if (parameter.getParameterizedType().getTypeName().contains("MultipartFile")) {
				// 配置上传参数
				DiskFileItemFactory factory = new DiskFileItemFactory();
				// 设置内存临界值 - 超过后将产生临时文件并存储于临时目录中
				factory.setSizeThreshold(MEMORY_THRESHOLD);
				// 设置临时存储目录
				factory.setRepository(new File(System.getProperty("java.io.tmpdir")));

				ServletFileUpload upload = new ServletFileUpload(factory);

				// 设置最大文件上传值
				upload.setFileSizeMax(MAX_FILE_SIZE);

				// 设置最大请求值 (包含文件和表单数据)
				upload.setSizeMax(MAX_REQUEST_SIZE);

				MultipartFile multipartFile = null;
				try {
					// 解析请求的内容提取文件数据
					List formItems = upload.parseRequest(req);
					if (formItems != null && formItems.size() > 0) {
						FileItem item = formItems.get(0);
						// 迭代表单数据
						// 处理不在表单中的字段
						if (!item.isFormField()) {
							multipartFile = new CommonsMultipartFile(item);
						}
					}
				} catch (Exception ex) {
					ex.printStackTrace();
				}
				paramValue.add(multipartFile);
			}else{
				String value = req.getParameter(paramNames[i]);
				Object o = null;
				try {
					o = adapter(parameter.getType(),value.trim(),req);
				} catch (InvocationTargetException e) {
					e.printStackTrace();
				} catch (IllegalAccessException e) {
					e.printStackTrace();
				}catch (NullPointerException e){
					o = null;
				}
				paramValue.add(o);
			}
			i++;
		}

		return paramValue;
	}  

	/**
	 * 注入完之后开始映射url和method
	 * @param instanceMap
	 * @param urlMethodMapping
	 */
	private void buildUrlMethodMapping(Map instanceMap,
			Map> urlMethodMapping) {
		// 注入完之后开始映射url和method
		// 组装urlMethodMapping
		for (Entry entry : instanceMap.entrySet()) {
			
			// 迭代出所有的url
			String parenturl = "";
			
			// 判断Controller上是否加了requestMapping
			if (entry.getValue().getClass().isAnnotationPresent(JuandaRequestMapping.class)) {
				parenturl = entry.getValue().getClass().getAnnotation(JuandaRequestMapping.class).value();
			}
			// 取出全部的method
			Method[] methods = entry.getValue().getClass().getMethods();
			
			// 迭代全部的方法,检查哪些方法上加了requestMaping注解
			for (Method method : methods) {
				if (method.isAnnotationPresent(JuandaRequestMapping.class)) {
					StringBuffer sb = new StringBuffer();
					for(int i = 0; i < method.getParameters().length; i++){
						if(i!=method.getParameters().length-1){
							sb.append(method.getParameters()[i]).append(",");
						}else {
							sb.append(method.getParameters()[i]);
						}
					}
					// 得到一个完整的url请求
					String url = parenturl + "/" + method.getAnnotation(JuandaRequestMapping.class).value();
					url = url.replaceAll("/+", "/");
					System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  buildUrlMethodMapping");
					System.err.println("INFO: Mapped \"{["+url+"],methods=["+method.getAnnotation(JuandaRequestMapping.class).method().toString()+"]}\" onto public "+method.getName()+" ("+sb.toString()+")");
					Map value = new HashMap<>();
					value.put(CONTROLLER_KEY, entry.getValue());
					value.put(METHOD_KEY, method);
					value.put(ENTRY_KEY,entry);
					urlMethodMapping.put(url, value );
				}
			}
		}
	}

	/**
	 * 根据实例Map开始注入
	 * @param instanceMap
	 */
	private void doIoc(Map instanceMap, Map, Object> instanceTypeMap) {
		// 开始注入,我们只对加了@Controller和@Service标签中的,属性加了@autowired的进行注入操作
		for (Entry entry : instanceMap.entrySet()) {

			// 取出全部的属性
			Field[] fields = entry.getValue().getClass().getDeclaredFields();

			// 循环属性校验哪些是加了@autowired注解的
			for (Field field : fields) {
				field.setAccessible(true);// 可访问私有属性

				// 有注解的时候
				if (field.isAnnotationPresent(JuandaAutowired.class)) {

					// 没有配别名注入的时候
					if (field.getAnnotation(JuandaAutowired.class).value().isEmpty()) {
						// 直接获取
						try {
							// 根据类型来获取他的实现类
							Object object = instanceTypeMap.get(field.getType());
							field.set(entry.getValue(), object);
						} catch (IllegalArgumentException | IllegalAccessException e) {
							throw new RuntimeException("开始注入时出现了异常");
						}
					} else {
						try {
							// 将被注入的对象
							Object object = instanceMap.get(field.getAnnotation(JuandaAutowired.class).value());
							field.set(entry.getValue(), object);
						} catch (Exception e) {
							throw new RuntimeException("开始注入时出现了异常");
						}
					}
				}
			}
		}
	}

	/**
	 * 组装instanceMap
	 * @param classes
	 * @param instanceMap
	 */
	private void buildInstanceMap(Set> classes, Map instanceMap, Map, Object> instanceTypeMap) {
		// 开始循环全部class
		for (Class clasz : classes) {
			
			// 组装instanceMap
			// 判断是否是是加了Controller注解的java对象
			if (clasz.isAnnotationPresent(JuandaController.class)) {
				try {
					// 实例化对象
					Object obj = clasz.newInstance();
					JuandaController controller = clasz.getAnnotation(JuandaController.class);
					
					// 如果没有设置别名,那么用类名首字母小写做key
					if (controller.value().isEmpty()) {
						instanceMap.put(firstLowerName(clasz.getSimpleName()), obj);
					}else{
						// 如果设置了别名那么用别名做key
						instanceMap.put(controller.value(), obj);
					}
				} catch (Exception e) {
					throw new RuntimeException("初始化instanceMap时在处理Controller注解时出现了异常");
				}				
			}else if(clasz.isAnnotationPresent(JuandaService.class)) {
				// 实例化对象
				Object obj = null;
				try {
					// 实例化对象
					obj = clasz.newInstance();
					JuandaService service = clasz.getAnnotation(JuandaService.class);
					
					// 如果没有设置别名,那么用类名首字母小写做key
					if (service.value().isEmpty()) {
						instanceMap.put(firstLowerName(clasz.getSimpleName()), obj);
					}else{
						// 如果设置了别名那么用别名做key
						instanceMap.put(service.value(), obj);
					}
				} catch (Exception e) {
					throw new RuntimeException("初始化instanceMap时在处理Service注解时出现了异常");
				}
				// 实现的接口数组
				Class[] interfaces = clasz.getInterfaces();
				for (Class class1 : interfaces) {
					if (instanceTypeMap.get(class1) != null) {
						throw new RuntimeException(class1.getName() + "接口不能被多个类实现!");
					}
					instanceTypeMap.put(class1, obj);
				}
			}else {
				if(configuration.getAutowire()){
					Object obj = null;
					try {
						obj = clasz.newInstance();
						Class[] interfaces = clasz.getInterfaces();
						for (Class class1 : interfaces) {
							System.err.println(new Date()+" cn.yangjunda.servlet.DispatcherServlet  bean封装");
							System.err.println("INFO: juanda-mvc "+configuration.getBeans().get(class1.getName())+"->"+class1.getName());
							if(configuration.getBeans().get(class1.getName())!=null){
								if (instanceTypeMap.get(class1) != null) {
									throw new RuntimeException(class1.getName() + "接口不能被多个类实现!");
								}
								instanceTypeMap.put(class1, obj);
							}
						}
					} catch (InstantiationException e) {
//					e.printStackTrace();
					} catch (IllegalAccessException e) {
//					e.printStackTrace();
					}
				}
			}
		}
	}
    
    /**
     * 首字母小写
     * @param name
     * @return
     */
    private String firstLowerName(String name) {
        name = name.substring(0, 1).toLowerCase() + name.substring(1);
       return  name;
    }

	/**
	 * url请求拦截器
	 * @param req
	 * @param resp
	 * @param method
	 * @return
	 */
	private boolean handleHttpRequestMethod(HttpServletRequest req, HttpServletResponse resp ,Method method){
		if(method.getAnnotation(JuandaRequestMapping.class).method().length>0){
			RequestMethod[] requestMethods = method.getAnnotation(JuandaRequestMapping.class).method();
			boolean flag = false;
			for (int i = 0;i paramType, String value,HttpServletRequest req) throws InvocationTargetException, IllegalAccessException {
		Object object = null;
		if (paramType == String.class) {
			object = value;
		} else if (paramType == Integer.class) {
			if(StringUtils.isBlank(value)){
				object=null;
			}else {
				object = Integer.valueOf(value);
			}
		} else if(paramType == int.class){
			object = Integer.parseInt(value);
		} else if (paramType == Long.class || paramType == long.class) {
			object = Long.parseLong(value);
		} else if (paramType == Boolean.class || paramType == boolean.class) {
			object = Boolean.parseBoolean(value);
		} else if (paramType == Short.class || paramType == short.class) {
			object = Short.parseShort(value);
		} else if (paramType == Float.class || paramType == float.class) {
			object = Float.parseFloat(value);
		} else if (paramType == Double.class || paramType == double.class) {
			object = Double.parseDouble(value);
		} else if (paramType == BigDecimal.class) {
			object = new BigDecimal(value);
		} else if (paramType == Character.class || paramType == char.class) {
			char[] cs = value.toCharArray();
			if (cs.length > 1) {
				throw new IllegalArgumentException("参数长度太大");
			}
			object = value.toCharArray()[0];
		} else if(classes.contains(paramType)){
			object = AutoPackObjectUtil.getObject(req,paramType);
		}
    	return object;
	}
}