写在前面
本文使用JAVA类库完成一个迷你版的springmvc框架,旨在深入理解springmvc的内部实现原理。
源码地址https://github.com/Jacwo/myspringmvc.git 喜欢就fork me
目录结构
自定义注解
元注解:就是注解的注解
@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"); } }