mini版自定义SpringMVC框架及实现
自定义springmvc 框架的实现其实就是自定义DispatcherServle类
有以下几个步骤:
1、加载配置文件 springmvc.properties
2、扫描相关类 扫描注解
3、初始化bean对象 实现ioc容器 基于注解
4、实现依赖注入
5、构造HandlerMapping 处理器映射器 将配置好的url和method建立映射关系
6、配置web.xm
7、请求
LwlDispatcherServlet
public class LwlDispatcherServlet extends HttpServlet {
private Properties properties = new Properties();
//缓存扫描到的类的权限定类名
private List<String> classNames = new ArrayList<>();
private Map<String,Object> ioc = new HashMap<>();
//建立url与method之间的映射关系
private List<Handler> handlerMapping = new ArrayList<>();
//访问权限控制
private Map<Handler,List<String>> securityMap = new HashMap<>();
@Override
public void init(ServletConfig config) throws ServletException {
//加载配置文件 springmvc.properties
String contextConfigLocation = config.getInitParameter("ContextConfigLocation");
doLoadConfig(contextConfigLocation);
//扫描相关类 扫描注解
doScan(properties.getProperty("scanPackage"));
//初始化bean对象 实现ioc容器 基于注解
doInstance();
//实现依赖注入
doAutowired();
//构造HandlerMapping 处理器映射器 将配置好的url和method建立映射关系
initHandlerMapping();
System.out.println(" mvc 初始化完成....");
//等待请求
}
}
1、加载配置文件 springmvc.properties
加载配置文件
/**
* 加载配置文件
* @param contextConfigLocation
*/
private void doLoadConfig(String contextConfigLocation) {
InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
try {
properties.load(resourceAsStream);
} catch (IOException e) {
e.printStackTrace();
}
}
2、扫描相关类 扫描注解
/**
* 扫描相关类 扫描注解
*
* 找到传入的包路径的真实的磁盘路径
*
* @param scanPackage
*/
private void doScan(String scanPackage) {
// 找到传入的包路径的真实的磁盘路径
String scanPackagePath = Thread.currentThread().getContextClassLoader().getResource("").getPath() + scanPackage.replaceAll("\\.", "/");
File pack = new File(scanPackagePath);
File[] files = pack.listFiles();
for (File file : files) {
if (file.isDirectory()){
//递归
doScan(scanPackage+"."+file.getName());
}else if (file.getName().endsWith(".class")){
String className = scanPackage + "." + file.getName().replaceAll(".class", "");
classNames.add(className);
}
}
}
3、初始化bean对象 实现ioc容器 基于注解
/**
* 初始化bean对象 实现ioc容器 基于注解
* 基于classNames缓存的类全限定类名 以及反射技术完成对象的创建和管理
*/
private void doInstance() {
if (classNames.size() == 0){
return;
}else {
try {
for (int i = 0; i < classNames.size(); i++) {
String className = classNames.get(i);
Class<?> aClass = Class.forName(className);
if (aClass.isAnnotationPresent(LwlController.class)){
//controller 的注解时不做过多的处理,不用获取value 就用类的首字母小写作为id 存在 ioc中
String simpleName = aClass.getSimpleName();
String lowerFistSimpleName = lowerFirst(simpleName);
Object o = aClass.newInstance();
ioc.put(lowerFistSimpleName,o);
}else if (aClass.isAnnotationPresent(LwlService.class)){
//获取value
LwlService annotation = aClass.getAnnotation(LwlService.class);
String beanName = annotation.value();
if (!"".equals(beanName)){
//如果指定了value的值 就以此为ID
ioc.put(beanName,aClass.newInstance());
}else {
//如果没有指定就有类名首字母小写
beanName = lowerFirst(aClass.getSimpleName());
ioc.put(beanName,aClass.getNestHost());
}
//service 层有接口的话 面向接口开发,此时的接口名为ID 放一份对象到IOC中 便于后期根据接口类型注入
Class<?>[] interfaces = aClass.getInterfaces();
for (int j = 0; j < interfaces.length; j++) {
Class<?> anInterface = interfaces[j];
//以接口的全限定类名 作为ID
ioc.put(anInterface.getName(),aClass.newInstance());
}
}else {
continue;
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
首字母小写方法
// 首字母小写方法
public String lowerFirst(String str) {
char[] chars = str.toCharArray();
if('A' <= chars[0] && chars[0] <= 'Z') {
chars[0] += 32;
}
return String.valueOf(chars);
}
4、实现依赖注入
/**
* 实现依赖注入
*/
private void doAutowired() {
if (ioc.isEmpty()){
return;
}else {
//不为空时 进行依赖处理
//遍历对象中的字段,查看对象中的字段是否有 @LwlAutowired 注解 如果有就要维护依赖关系
for (Map.Entry<String,Object> entry:ioc.entrySet()){
//获取bean对象的字段
Field[] declaredField = entry.getValue().getClass().getDeclaredFields();
for (int i = 0; i < declaredField.length; i++) {
Field field = declaredField[i];
if (!field.isAnnotationPresent(LwlAutowired.class)){
continue;
}else {
LwlAutowired annotation = field.getAnnotation(LwlAutowired.class);
String beanName = annotation.value(); //需要注入的bean的id
if ("".equals(beanName)){
//没有配置具体的id
beanName = field.getType().getName();
}
//开启赋值
field.setAccessible(true);
try {
field.set(entry.getValue(),ioc.get(beanName));
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
}
}
}
}
5、构造HandlerMapping 处理器映射器 将配置好的url和method建立映射关系
/**
* 构造HandlerMapping 处理器映射器 将配置好的url和method建立映射关系
*
* 最关键的一个环节
* 将url 与method 建立关联
*
* 只处理 HttpServletResponse HttpServletRequest string
*
*/
private void initHandlerMapping() {
if (ioc.isEmpty()){
return;
}
for (Map.Entry<String,Object> entry:ioc.entrySet()){
//获取当前IOC的对象class类型
Class<?> aClass = entry.getValue().getClass();
if (!aClass.isAnnotationPresent(LwlController.class)){
continue;
}
String baseUrl ="";
if (aClass.isAnnotationPresent(LwlController.class)){
LwlRequestMapping annotation = aClass.getAnnotation(LwlRequestMapping.class);
baseUrl = annotation.value(); //类上面的地址前缀
}
//获取方法上的地址
Method[] methods = aClass.getMethods();
for (int i = 0; i < methods.length; i++) {
Method method = methods[i];
if (!method.isAnnotationPresent(LwlRequestMapping.class)){
continue;
}
if (method.isAnnotationPresent(LwlRequestMapping.class)){
LwlRequestMapping annotation = method.getAnnotation(LwlRequestMapping.class);
String methodUrl = annotation.value();
String url = baseUrl + methodUrl;
//将method的所有信息及url封装在handler
Handler handler = new Handler(entry.getValue(),method,Pattern.compile(url));
Parameter[] parameters = method.getParameters();
for (int i1 = 0; i1 < parameters.length; i1++) {
Parameter parameter = parameters[i1];
if (parameter.getType() == HttpServletResponse.class || parameter.getType() == HttpServletRequest.class){
//如果是 HttpServletResponse 和HttpServletRequest对象 那么参数名称写HttpServletResponse HttpServletRequest
handler.getParamIndexMapping().put(parameter.getType().getSimpleName(),i1);
}else {
handler.getParamIndexMapping().put(parameter.getName(),i1);
}
}
//判断可以是否有权限进入该方法
if(aClass.isAnnotationPresent(Security.class) && method.isAnnotationPresent(Security.class)) {
//判断是否存在Security类
Security controllerSecurity = aClass.getAnnotation(Security.class);
String[] controllerUserNames = controllerSecurity.value();
List<String> controllerUserNamesList = Arrays.asList(controllerUserNames);
Security handlerSecurity = method.getAnnotation(Security.class);
String[] handlerUserNames = handlerSecurity.value();
List<String> handlerUserNameList = Arrays.asList(handlerUserNames);
controllerUserNamesList = new ArrayList<>(controllerUserNamesList);
handlerUserNameList = new ArrayList<>(handlerUserNameList);
controllerUserNamesList.addAll(handlerUserNameList);
securityMap.put( handler, controllerUserNamesList);
}else if(aClass.isAnnotationPresent(Security.class)) {
Security controllerSecurity = aClass.getAnnotation(Security.class);
String[] controllerUserNames = controllerSecurity.value();
List<String> controllerUserNamesList = Arrays.asList(controllerUserNames);
securityMap.put( handler, controllerUserNamesList);
}else if(method.isAnnotationPresent(Security.class)) {
Security handlerSecurity = method.getAnnotation(Security.class);
String[] handlerUserNames = handlerSecurity.value();
List<String> handlerUserNameList = Arrays.asList(handlerUserNames);
securityMap.put( handler, handlerUserNameList);
}
//建立url与method之间的映射关系
handlerMapping.add(handler);
}
}
}
}
6、配置web.xm
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>lwlmvc</servlet-name>
<servlet-class>com.lwl.mvcframework.servlet.LwlDispatcherServlet</servlet-class>
<init-param>
<param-name>ContextConfigLocation</param-name>
<param-value>springmvc.properties</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>lwlmvc</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
7、请求
com.lwl.mvcframework.servlet.LwlDispatcherServlet#doGet
//接受处理请求
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
this.doPost(req, resp);
}
com.lwl.mvcframework.servlet.LwlDispatcherServlet#doPost
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// //处理请求 根据url找到对应的method方法进行调用
// String requestURI = req.getRequestURI();
// //获取到反射的方法
// Method method = handlerMapping.get(requestURI);
// //反射调用 传入对象
// method.invoke()
//根据uri 获取到能够处理当前请求的handler
Handler handler = getHandler(req);
if (handler == null){
resp.getWriter().write("404 not found");
return;
}
// 安全认证
List<String> usernameList = securityMap.get(handler);
// 不为空说明需要认证,那么不包含的话就return
String username = req.getParameter("username");
if(usernameList != null && !usernameList.contains(username)) {
System.out.println("username:"+username+"无权限访问");
resp.getWriter().write(username+"sorry No permission");
return;
}
System.out.println("username:"+username+"有权限访问,欢迎您");
//参数绑定
//获取所有类型的参数类型数组 这个数组的长度 就是我们在调用方法时 用的参数长度
Class<?>[] parameterTypes = handler.getMethod().getParameterTypes();
//根据上述数组长度创建一个新的数组
Object[] paraValues = new Object[parameterTypes.length];
//以下就是为了想参数数组中赋值 而且还得保证参数的顺序和方法中的形参顺序一致
Map<String, String[]> parameterMap = req.getParameterMap();
//遍历request所有参数 填充除request 和response 以外的参数
for (Map.Entry<String,String[]> param : parameterMap.entrySet()){
String value = StringUtils.join(param.getValue(), ",");
//如果参数和方法中的参数匹配上了,数据填充
if (!handler.getParamIndexMapping().containsKey(param.getKey())){
continue;
}
//方法形参确实有该参数 找到它的索引位置 把对应的参数值放入paramValues
Integer index = handler.getParamIndexMapping().get(param.getKey());
paraValues[index] = value;//把前台传递的参数值 填充到对应位置
}
Integer requestIndex = handler.getParamIndexMapping().get(HttpServletRequest.class.getSimpleName());
paraValues[requestIndex] = req;
Integer responseIndex = handler.getParamIndexMapping().get(HttpServletResponse.class.getSimpleName());
paraValues[responseIndex] = resp;
// 调用handler 和method属性
try {
handler.getMethod().invoke(handler.getController(),paraValues);
} catch (Exception e) {
e.printStackTrace();
}
}
com.lwl.mvcframework.servlet.LwlDispatcherServlet#getHandler
private Handler getHandler(HttpServletRequest request){
if (handlerMapping.isEmpty()){
return null;
}
//处理请求 根据url找到对应的method方法进行调用
String requestURI = request.getRequestURI();
for (Handler handler : handlerMapping) {
Matcher matcher = handler.getPattern().matcher(requestURI);
if (!matcher.matches()){
continue;
}
return handler;
}
return null;
}