手写一个SpringMVC的框架
1.理解为什么要写这样一个框架
SpringMVC 实际上跟Servlet是一样,都是 Controller的一个解决方案,也就是说我们手写这个框架的目的就是为了替换原来的 Servlet
注意:
spring不是框架,springMVC才是框架,八大框架之一
controller层使用springMVC,用注解和配置就可以了
SpringMVC在项目启动时,会扫描web.xml的controller配置内容,首先会自动跳到controller层
Servlet的缺点:
- 一个请求就要写一个Sevlet,就导致了需要写大量的 Servlet ,增加了程序的复杂度
- 每个Servlet中获取参数都要getParameter,会导致代码产生大量的冗余
- 每个Servlet都需要实现配置,如果在web.xml中来编写的话太复杂了
- 如果需要解密和加密(站在后端程序员的角度,先解密再加密),会导致每个Servlet需要去写这个逻辑显得很复杂
- 异常处理显得很鸡肋,要重写很多的类
- 请求参数还不能自动封装成对象
- 前端如果传递JSON格式,那么后台每一次都要 getInputStream
- 返回参数如果是JSON格式每一次都需要 getWriter()
2.这个框架的整体实现逻辑是什么
使用的角度:浏览器发送请求给DispatchServlet(不是发送给controller层,整个项目里只有一个servlet ),servlet会根据不同的请求地址去分发给controller层(controller是一个类)
/back/user:父级uri,login.action:子级uri(.action特定的规范就可以去访问对应的controller层)
@controller:controller层
大类的@RequestMapping(“/back/user”):父级uri
方法的@RequestMapping(“login.action”):子级uri
路径携带的数据会解析出来传到方法上的参数上面
准备工作,左边是spring-mvc-framework的基本准备工作,右边是shop-web的准备工作
放大版本:
ApplicationListener全局监听器,右边是异常处理类
3.开始手写这样一个框架
3.1 建项目
大的聚合项目里面包裹web项目和springMVC项目
创建聚合项目shop-parent
创建web项目shop-web,webapp
在shop-web里建包,com.qf.shop.web,在建小包pojo实体包和控制器Controller包
在实体类里要写无参构造,有参构造,get、set、toString,太麻烦了,所以使用lombok依赖
Lombok能通过注解的方式,在编译时自动为属性生成无参构造方法、有参构造、get、set、equals、hashcode、toString方法
安装插件File -> Plugins -> Marketplace 中所有lombok -> Install
实体类user
@NoArgsConstructor //无参构造
@AllArgsConstructor //有参构造
@Data //get set toString
public class User {
private String username;
private String password;
}
在web项目的pom.xml添加依赖
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
建一个Controller包,存在前台用户和后台用户(back包)
里面都是存放Controller类文件,要加注解(加了才是Controller层),springmvc才知道这个是Controller
前台用户UserController
package com.qf.shop.web.controller;
import com.qf.shop.mvc.annotation.Controller;
@Controller
//前台用户
public class UserController {
public void login(String username,String password){
System.out.println("前台用户 - login:" + username + " -- " + password);
}
public void register(User user){
System.out.println("前台用户 - register:" + user);
}
}
后台用户UserController,放在Controller/back包里
package com.qf.shop.web.controller.back;
import com.qf.shop.mvc.annotation.Controller;
//后台用户
@Controller
public class UserController {
public void login(String username,String password){
System.out.println("后台用户 - login:" + username + " -- " + password);
}
public void register(User user){
System.out.println("后台用户 - register:" + user);
}
}
ProvinceController
注意:要考虑参数,返回值(JSON字符串,路径)
package com.qf.shop.web.controller;
import com.qf.shop.mvc.annotation.Controller;
@Controller
public class ProvinceController {
//获取所有的省份信息,返回json格式的字符串
public String getProvinces(){
System.out.println("ProvinceController -- getProvinces()");
return "";
}
//根据省份的code,查询出对应的城市信息,返回json格式的字符串
public String getCities(String code){
System.out.println("ProvinceController -- getCities():" + code);
return "";
}
}
A(测试类)
package com.qf.shop.web.controller;
public class A {
public void method(){
System.out.println("好好学习");
}
}
Controller包里,springMVC怎么知道这里面的是Controller,哪些不是Controller
建一个Java项目spring-mvc-framework
在MVC中的pom.xml添加jdk1.8依赖
<build>
<finalName>spring-mvc-framework</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
shop-web的pom.xml添加jdk1.8依赖
<build>
<finalName>shop-web</finalName>
<plugins>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
3.2 准备注解
web项目中
mvc项目中
建包创建Controller注解
package com.qf.shop.mvc.annotation;
/**
* 作用在Controller类上,说明该类是Controller类
*/
@Target(ElementType.TYPE) //作用在类上
@Retention(RetentionPolicy.RUNTIME) //运行时有效
public @interface Controller {
}
spring-mvc-framework项目要作用到shop-web项目中,需要在shop-web项目的pom.xml添加依赖
<dependency>
<groupId>com.qf.shop.mvc</groupId>
<artifactId>spring-mvc-framework</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
给上面的Controller包里面的类都添加上Controller注解,除了A类不添加 (@Controller),A类是测试类
3.2.1 web项目中指定Controller包的路径(哪个包)
在web项目中新建一个config包,建一个AppConfig类
这是一个联通springmvc的配置类,要写在@configuration的value
package com.qf.shop.web.config;
import com.qf.shop.mvc.annotation.Configuration;
/**
* Web项目的配置类
* 指定Controller层的扫描路径
* 指定前后置处理器类的路径(后面加)
* 指定捕获全局异常类的路径(后面加)
*/
@Configuration(value="com.qf.shop.web.controller")
public class AppConfig {
}
在mvc项目的annotation注解包中添加一个注解Configuration
package com.qf.shop.mvc.annotation;
/**
* 作用在web项目中AppConfig类上,标注该web项目需要扫描的Controller层的路径
*/
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Configuration {
String value();
}
要在mvc项目中想办法拿到web项目中的config.class(Appconfig类),再利用class对象找到对应的注解信息路径即:@Configuration(value=“com.qf.shop.web.controller”)
在web项目中的web.xml配置一下,配置类config的路径
<web-app>
<display-name>Archetype Created Web Application</display-name>
<!-- 将web项目的配置类config的全路径存放在ServletContext对象中 -->
<context-param>
<param-name>config</param-name>
<param-value>com.qf.shop.web.config.AppConfig</param-value>
</context-param>
</web-app>
接下来要在mvc项目中想办法拿到这个全路径,就可以找到class对象,就找到对应的注解信息
添加一个servlet依赖
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
先建一个监听器包,项目一启动就可以运行,ApplicationListener类
启动web项目,已经在web项目中引入了mvc的依赖,所以mvc项目的代码就可以生效,但是监听器要写注解@WebListener,但是不用这种方法,用注解
缺点:用注解,项目启动,ApplicationListener一定会运行
框架设计的目的是我想让它启动它就启动,不想它启动就不能启动,启不启动,web项目说了算
在web的web.xml配置监听器,mvc项目的监听器才有效果
<listener>
<listener-class>com.qf.shop.mvc.listener.ApplicationListener</listener-class>
</listener>
mvc中的ApplicationListener类
package com.qf.shop.mvc.listener;
import com.qf.shop.mvc.annotation.Configuration;
public class ApplicationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("项目启动了...");
//获取到web项目中配置文件类的全路径
//获取全局域对象
ServletContext servletContext = sce.getServletContext();
//com.qf.shop.web.config.AppConfig
String config = servletContext.getInitParameter("config");
try {
//获取web项目中配置文件类的class对象 AppConfig.class
Class<?> configClass = Class.forName(config);
//获取web项目中配置类(AppConfig)中的Configuration注解
Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);
//获取web项目中Controller层的扫描路径 com.qf.shop.web.controller
String value = configClassAnnotation.value();
//System.out.println("配置文件类路径:" + value);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
配置tomcat,修改一下,下面修改为/
3.3 异常处理
关于config的各种异常,mvc项目有自己的自定义的异常
放大版:
在mvc项目中建包com.qf.shop.mvc.constant,创建一个ResponseCodeInterface接口
异常信息接口
package com.qf.shop.mvc.constant;
/**
* 异常信息的接口
*/
public interface ResponseCodeInterface {
public int getCode();
public void setCode(int code);
public String getMessage();
public void setMessage(String message);
}
接口的实现类,使用enum更好,这是提供了一些异常信息类型,并不是异常处理
接口是定义规范的
package com.qf.shop.mvc.constant;
public enum ResponseCode implements ResponseCodeInterface{
CONFIG_EXCEPTION(100,"config的配置信息出错"),
CONFIGURATION_EXCEPTION(101,"需要配置Configuration这个注解"),
CLASS_FILE_EXCEPTION(102,"class文件转换异常"),
REQUEST_MAPPING_PATH_EXCEPTION(103,"RequestMapping地址设置有误"),
REQUEST_PATH_EXCEPTION(104,"uri映射错误"),
ADVISER_CONFIG_EXCEPTION(105,"未配置处理器的路径"),
EXCEPTION_CONFIG_EXCEPTION(106,"未配置全局异常的路径");
//枚举类中的属性
private int code;
private String message;
//无参构造,有参构造,get,set,tostring方法省略
}
新建一个异常exception包,建一个FrameworkException类,继承RuntimeException,让他报错,但是不用强制在方法上处理
不需要无参构造,让ApplicationListener抛出异常,必须在括号中写内容
package com.qf.shop.mvc.exception;
/**
* spring-MVC的异常类
*/
@AllArgsConstructor
@Data
public class FrameworkException extends RuntimeException{
private int code;
private String message;
}
新建一个工具包utils,建StringUtils类
package com.qf.shop.mvc.utils;
public class StringUtils {
/**
* 判断字符串是否为空
*/
public static boolean isEmpty(String value) {
return value == null || value.length() == 0;
}
}
包结构
更新一下ApplicationListener,处理异常 v2.0
pakage com.qf.shop.mvc.listener;
public class ApplicationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("项目启动了...");
//获取到web项目中配置文件类的全路径
ServletContext servletContext = sce.getServletContext();
//com.qf.shop.web.config.AppConfig
String config = servletContext.getInitParameter("config");
//有可能用户没有在web.xml配置config,所以需要判断
if (StringUtils.isEmpty(config)) {
throw new FrameworkException(ResponseCode.CONFIG_EXCEPTION.getCode(),ResponseCode.CONFIG_EXCEPTION.getMessage());
}
//获取web项目中配置文件类的class对象(下面封装了一个异常处理方法)
Class<?> configClass = getConfigClass(config);
//获取web项目中配置类(AppConfig)中的Configuration注解
String controllerPosition = getControllerPosition(configClass);
System.out.println(controllerPosition);//com.qf.shop.web.controller
}
//获取web项目中Controller层的扫描路径的方法
public String getControllerPosition(Class<?> configClass){
//获取web项目中配置类(AppConfig)中的Configuration注解
Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);
//判断web项目中配置类(AppConfig)中是否有Configuration注解
if(configClassAnnotation == null){
throw new FrameworkException(ResponseCode.CONFIGURATION_EXCEPTION.getCode(),ResponseCode.CONFIGURATION_EXCEPTION.getMessage());
}
String controllerPosition = configClassAnnotation.value();
return controllerPosition;
}
//获取web项目中配置文件类的class对象的方法
public Class<?> getConfigClass(String config){
try {
Class<?> configClass = Class.forName(config);
return configClass;
} catch (ClassNotFoundException e) {
throw new FrameworkException(ResponseCode.CLASS_FILE_EXCEPTION.getCode(),ResponseCode.CLASS_FILE_EXCEPTION.getMessage());
}
}
}
现在可以在mvc项目的ApplicationListener监听器类中定位到web项目的Controller路径
记录在一个map集合中,key是路径(浏览器/back/user/login.action,父级uri+子级uri),value是这个类描述对象,在项目启动时就存储,在监听器存储
在mvc中创建一个注解RequestMapping
package com.qf.shop.mvc.annotation;
/**
* 作用在Controller类上或方法上,标注父级uri和子级uri
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface RequestMapping {
String value();
}
更新一下web项目中的@RequestMapping(“back/user”)
package com.qf.shop.web.controller.back;
@Controller
@RequestMapping("/back/user")
public class UserController {
@RequestMapping("login.action")
public void login(String username,String password){
System.out.println("后台用户 - login:" + username + " -- " + password);
}
@RequestMapping("register.action")
public void register(User user){
System.out.println("后台用户 - register:" + user);
}
}
ProvinceController
package com.qf.shop.web.controller;
@Controller
@RequestMapping("/province")
public class ProvinceController {
//获取所有的省份信息,返回json格式的字符串
@RequestMapping("/getProvinces.action")
public String getProvinces(){
System.out.println("ProvinceController -- getProvinces()");
return "";
}
//根据省份的code,查询出对应的城市信息,返回json格式的字符串
@RequestMapping("/getCities.action")
public String getCities(String code){
System.out.println("ProvinceController -- getCities():" + code);
return "";
}
}
UserController
package com.qf.shop.web.controller;
@Controller
@RequestMapping("/user")
//前台用户
public class UserController {
@RequestMapping("/login.action")
public void login(String username,String password){
System.out.println("前台用户 - login:" + username + " -- " + password);
}
@RequestMapping("/register.action")
public void register(User user){
System.out.println("前台用户 - register:" + user);
}
}
3.4 类描述类
类描述类概念:类描述web项目中的Controller类
三种类型:类描述类,方法描述类,参数描述类
在mvc项目建一个model包,新建三个类:类描述类,方法描述类,参数描述类
ParameterDefinition,参数描述类
package com.qf.shop.mvc.model;
/**
* 参数描述类
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class ParameterDefinition {
private String name; // 参数名
private Class<?> clazz; // 参数类型
private int index; // 参数位置,参数下标
private Parameter parameter;//参数对象
}
MethodDefinition 方法描述类
package com.qf.shop.mvc.model;
/**
* 方法描述类
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class MethodDefinition {
private String requestMappingPath;//子级uri
private Method method;//方法对象 反射用
private String name;//方法名
private List<ParameterDefinition> parameterDefinitions;//参数描述类对象的集合(一个方法可能有多个参数)
private Class<?> returnType;//返回值类型 反射
}
BeanDefinition 类描述类
package com.qf.shop.mvc.model;
/**
* 类描述类
*/
@NoArgsConstructor
@AllArgsConstructor
@Data
public class BeanDefinition<T> {
private String requestMappingPath;//父级uri
private Class<?> clazz;//该Controller类的class对象
private T t;//该Controller类的对象,调用方法会用,方法名.invoke(该类对象,xxx)
private String name;//类名
private MethodDefinition methodDefinition;//方法描述类的对象(不是集合,就是一个方法,因为一个路径就只有一个action,就只有一个功能,找到一个方法)
}
方法描述类的对象(不是集合,就是一个方法,因为一个路径就只有一个action,就只有一个功能,找到一个方法),解释:
问题:下面有几个类描述类?两个
package com.qf.shop.web.controller.back;
@Controller
@RequestMapping("/back/user")
public class UserController {
@RequestMapping("login.action")
public void login(String username,String password){
System.out.println("后台用户 - login:" + username + " -- " + password);
}
@RequestMapping("register.action")
public void register(User user){
System.out.println("后台用户 - register:" + user);
}
}
3.5 拼接路径
修改发布路径
放在tomcat目录的webapp里,运行不起的话,就删除webapp里的文件
需要拿到下面的class文件(反射),操作的就是下面的一个一个的class文件
下面就是拼接工作
更新ApplicationListener v3.0
package com.qf.shop.mvc.listener;
public class ApplicationListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("项目启动了...");
//获取到web项目中配置文件类的全路径
ServletContext servletContext = sce.getServletContext();
//com.qf.shop.web.config.AppConfig
String config = servletContext.getInitParameter("config");
//有可能用户没有在web.xml配置config,所以需要判断
if (StringUtils.isEmpty(config)) {
throw new FrameworkException(ResponseCode.CONFIG_EXCEPTION.getCode(),ResponseCode.CONFIG_EXCEPTION.getMessage());
}
//获取web项目中配置文件类的class对象(下面封装了一个异常处理方法)
Class<?> configClass = getConfigClass(config);
//获取web项目中配置类(AppConfig)中的Configuration注解
//com.qf.shop.web.controller
String controllerPosition = getControllerPosition(configClass);
//获取Controller层的绝对路径 D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller
String controllerAbsolutePath = getControllerAbsolutePath(servletContext, controllerPosition);
//将该路径下的所有文件对象存入到集合中
List<File> fileList = new ArrayList<>();
findFileByPath(controllerAbsolutePath,fileList);//A类也在
//转换为 List<Class<?>> 没有A类
//[class com.qf.shop.web.controller.back.UserController, class
// com.qf.shop.web.controller.ProvinceController, class
// com.qf.shop.web.controller.UserController ]
List<Class<?>> classList = transformTo(servletContext, fileList);
//封装类描述类对象、方法描述类对象、参数描述类对象,存入Map中
handlerClassList(classList);
//扫描结束
System.out.println("扫描结束...");
}
//下面都是封装的方法
//封装类描述类对象、方法描述类对象、参数描述类对象,存入Map中
public void handlerClassList(List<Class<?>> classList){
for (Class<?> clazz : classList) {
//class com.qf.shop.web.controller.back.UserController
//class com.qf.shop.web.controller.ProvinceController
//class com.qf.shop.web.controller.UserController
// System.out.println(clazz);
//获取类描述类对象需要的信息 ----------------------------
Object t=null;
try {
//创建大类对象
t = clazz.newInstance();
} catch (InstantiationException e) {
throw new RuntimeException(e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
String className = clazz.getName();//大类的类名
RequestMapping classRequestMapping = clazz.getAnnotation(RequestMapping.class);//大类的父级RequestMapping
String fatherUri = classRequestMapping.value();//父级uri
//父级uri可能没有写
if (StringUtils.isEmpty(fatherUri)){
throw new FrameworkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());
}
Method[] methods = clazz.getDeclaredMethods();//大类的方法
for (Method method : methods) {
method.setAccessible(true);//设置权限
//获取方法描述类对象需要的信息 ----------------------------
String methodName = method.getName();//方法的方法名
Class<?> returnType = method.getReturnType();//方法的返回值类型
RequestMapping methodRequestMapping = method.getAnnotation(RequestMapping.class);//方法上的RequestMapping
//方法上的RequestMapping有可能没有,不需要报错
if (methodRequestMapping == null){
continue;
}
String sonUri = methodRequestMapping.value();//子级uri
//子级uri可能没有写
if (StringUtils.isEmpty(sonUri)){
throw new FrameworkException(ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getCode(),ResponseCode.REQUEST_MAPPING_PATH_EXCEPTION.getMessage());
}
List<ParameterDefinition> parameterDefinitions = null;//参数描述类对象的集合
Parameter[] parameters = method.getParameters();//参数对象数组
if (parameters.length!=0){
parameterDefinitions =new ArrayList<>();//参数描述类对象要放在集合里
for (int i = 0; i < parameters.length; i++) {//获取参数的下标
//获取参数描述类对象的信息 ----------------------------
Parameter parameter = parameters[i];//参数对象
String parameterName = parameter.getName();//参数名(注意:java在编译时,不会将参数名编译到class文件中,class文件里的参数名使用arg0、arg1...替代,如果想要将参数名编译到class文件中必须使用编译插件)
Class<?> parameterType = parameter.getType();//参数类型的class对象
//创建参数描述类对象
ParameterDefinition parameterDefinition = new ParameterDefinition(parameterName, parameterType, i,parameter);
//添加到参数描述类对象的集合
parameterDefinitions.add(parameterDefinition);
}
}
//创建方法描述类的对象
MethodDefinition methodDefinition = new MethodDefinition(sonUri, method, methodName, parameterDefinitions, returnType);
//创建类描述对象
BeanDefinition<Object> beanDefinition = new BeanDefinition<>(fatherUri, clazz, t, className, methodDefinition);
//拼接uri
String uri=fatherUri+sonUri;
//存入Map容器中
ConcurrentHashMap<String, BeanDefinition> maps = TypeContainer.getMaps();
//key-uri,value-类描述类对象
maps.put(uri,beanDefinition);
Set<Map.Entry<String, BeanDefinition>> entries = maps.entrySet();
//for (Map.Entry<String, BeanDefinition> entry : entries) {
//System.out.println(entry);
//打印entry如下: ///back/userregister.action=BeanDefinition(requestMappingPath=/back/user, clazz=class com.qf.shop.web.controller.back.UserController, t=com.qf.shop.web.controller.back.UserController@23571581, name=com.qf.shop.web.controller.back.UserController, //methodDefinition=MethodDefinition(requestMappingPath=register.action, method=public void com.qf.shop.web.controller.back.UserController.register(com.qf.shop.web.pojo.User), name=register,
//parameterDefinitions=[ParameterDefinition(name=user, clazz=class com.qf.shop.web.pojo.User, index=0)], returnType=void))
//}
}
}
}
//将List<File> 转换为 List<Class<?>>
public List<Class<?>> transformTo(ServletContext servletContext,List<File> fileList){
//发布路径 -- D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes
String realPath = servletContext.getRealPath("WEB-INF\\classes");
List<Class<?>> classList = new ArrayList<>();
for (File file : fileList) {
//D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller\A.class
String absolutePath = file.getAbsolutePath();
//\com\qf\shop\web\controller\A.class 去掉前面的部分
absolutePath = absolutePath.replace(realPath,"");
//com\qf\shop\web\controller\A.class
absolutePath = absolutePath.substring(1);
//com\qf\shop\web\controller\A
absolutePath = absolutePath.substring(0,absolutePath.lastIndexOf("."));
//com.qf.shop.web.controller.A
absolutePath = absolutePath.replace("\\",".");
try {
Class<?> clazz = Class.forName(absolutePath);
Controller controllerAnnotation = clazz.getAnnotation(Controller.class);
//判断是否有Controller注解,没有则跳过,有(就是一个Controller类)则添加到集合中
if(controllerAnnotation == null){
continue;
}
classList.add(clazz);
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
return classList;
}
//将该路径下的所有文件对象存入到集合中
public void findFileByPath(String controllerAbsolutePath, List<File> fileList){
File file = new File(controllerAbsolutePath);
File[] files = file.listFiles();
for (File f : files) {
if(f.isDirectory()){//文件夹
findFileByPath(f.getAbsolutePath(),fileList); //递归
}else if(f.isFile()){//文件
fileList.add(f);
}
}
}
//获取Controller层的绝对路径
public String getControllerAbsolutePath(ServletContext servletContext,String controllerPosition){
//发布路径 -- D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes
//getRealPath()获取的是发布路径
String realPath = servletContext.getRealPath("WEB-INF\\classes");
//com.qf.shop.web.controller --> com\qf\shop\web\controller 替换 一个是转义字符,两个\\才是\
controllerPosition = controllerPosition.replace(".","\\");
//拼接 -- D:\apache-tomcat-8.0.49\webapps\ROOT\WEB-INF\classes\com\qf\shop\web\controller
//File.separator是系统分隔符 ,windows是\,linux是/
String controllerAbsolutePath = realPath + File.separator + controllerPosition;
return controllerAbsolutePath;
}
//获取web项目中Controller层的扫描路径的方法
public String getControllerPosition(Class<?> configClass){
//获取web项目中配置类(AppConfig)中的Configuration注解
Configuration configClassAnnotation = configClass.getAnnotation(Configuration.class);
//判断web项目中配置类(AppConfig)中是否有Configuration注解
if(configClassAnnotation == null){
throw new FrameworkException(ResponseCode.CONFIGURATION_EXCEPTION.getCode(),ResponseCode.CONFIGURATION_EXCEPTION.getMessage());
}
String controllerPosition = configClassAnnotation.value();
return controllerPosition;
}
//获取web项目中配置文件类的class对象的方法
public Class<?> getConfigClass(String config){
try {
Class<?> configClass = Class.forName(config);
return configClass;
} catch (ClassNotFoundException e) {
throw new FrameworkException(ResponseCode.CLASS_FILE_EXCEPTION.getCode(),ResponseCode.CLASS_FILE_EXCEPTION.getMessage());
}
}
}
新建一个container包
容器类TypeContainer
package com.qf.shop.mvc.container;
import com.qf.shop.mvc.model.BeanDefinition;
import java.util.concurrent.ConcurrentHashMap;
public class TypeContainer {
private static ConcurrentHashMap<String, BeanDefinition> maps;
static {
maps = new ConcurrentHashMap<>();
}
//拿到map对象
public static ConcurrentHashMap<String, BeanDefinition> getMaps() {
return maps;
}
}
添加编译插件 :maven-compiler-plugin插件
含义:将方法中的参数名编译到class文件中
String parameterName = parameter.getName();//参数名(注意:java在编译时,不会将参数名编译到class文件中,class文件里的参数名使用arg0、arg1…替代,如果想要将参数名编译到class文件中必须使用编译插件)
<build>
<finalName>spring-mvc-framework</finalName>
<pluginManagement>
<plugins>
<!-- 这个插件就是java类生成class的编译插件 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- 编译参数 -->
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
web项目也要添加(如果还不行,就删除web项目中的target文件夹)
<build>
<finalName>shop-web</finalName>
<pluginManagement>
<plugins>
<!-- 这个插件就是java类生成class的编译插件 -->
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<!-- 编译参数 -->
<compilerArgument>-parameters</compilerArgument>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</pluginManagement>
</build>
web项目框架
mvc项目框架
!
总结
1.完善ApplicationListener,封装类描述类、方法描述类、参数描述类的对象