热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

SpringMVC学习笔记五

自定义超迷你简单版springmvc. 实现自定义注解@Autowired,@Controller,@RequestMapping,@Service. 实现springmvc基础功能:根据loc

自定义超迷你简单版springmvc.

  • 实现自定义注解@Autowired,@Controller,@RequestMapping,@Service.
  • 实现springmvc基础功能:根据localhost:8080/xialu/sayHello?msg=hello xialu,能够成功请求服务.

回顾Spring MVC学习笔记一,使用spingmvc的主要流程分为:

  1. web.xml中配置前端控制器DispatcherServlet
  2. 配置springmvc.xml,指定需要扫描的包路径.
  3. web.xml中配置springmvc.properties.
  4. 指定匹配规则.
  5. 使用@Controller,@RequestMapping等注解标记要使用的类和方法.

根据上面的步骤自定义sprngmvc的开发流程可以分为以下流程:

  1. 实现DispatcherServlet前端控制器.
  2. 加载指定的配置文件springmvc.properties.
  3. (1)扫描包获取要处理的类路径,
    (2)扫描注解,@Controller,@RequestMapping等完成Bean的初始化.
    (3)依赖注入.
  4. 实现HandlerMapping管理维护url和method之间的关系.
  5. 处理用户请求.
开始前的准备
1)创建一个webapp项目

因为springmvc学习记录一有写过,这里就不再重复记录:https://www.jianshu.com/writer#/notebooks/50462304/notes/89366803/preview

2)实现自定义注解

@Autowired

@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    String value() default "";
}

@Controller

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Controller {
    String value() default "";
}

@RequestMapping

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RequestMapping {
    String value() default "";
}

@Service

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Service {
    String value() default "";
}
3)pom文件
4.0.0xialu.studycustom-springmvc1.0-SNAPSHOTjarcustom-springmvc Maven Webapphttp://www.example.comUTF-888junitjunit4.12testjavax.servletjavax.servlet-api3.1.0provided

准备完成


第一步,自定义DispatcherServlet
public class XialuDispatcherServlet extends HttpServlet {

    private XialuConfig xialuConfig;
    private ApplicationContext context;
    private List handlerMappings;

    @Override
    public void init(ServletConfig config) {
        /**
         * 1.加载web.xml,解析springmvc.properties.
         */
        this.xialuCOnfig= XialuConfig.newInstance();
        xialuConfig.parseConfig(config);

        /**
         * 2.扫描包路径,初始化Bean.
         */
        this.cOntext= ApplicationContext.newInstance(xialuConfig.getProperties().getProperty("xialuPackage"));
        /**
         * 初始化处理映射器,并完成注册.
         */
        this.handlerMappings = HandlerMappingBuilder.newInstance(this.context).build();
    }


    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        doPost(req, resp);
    }


    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException { }

}
第二步,加载配置文件
public class XialuConfig {

    private Properties properties = new Properties();

    /**
     * 实例化.
     *
     * @return
     */
    public static XialuConfig newInstance() {
        return new XialuConfig();
    }

    /**
     * 解析配置文件.
     *
     * @param config
     */
    public void parseConfig(ServletConfig config) {

        try {
            /**
             *加载web.xml,解析springmvc.properties.
             */
            String cOntextConfigLocation= config.getInitParameter("contextConfigLocation");

            InputStream resourceAsStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);

            properties.load(resourceAsStream);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    public Properties getProperties() {
        return properties;
    }
}
第三步,扫描包和注解,初始化Bean,完成依赖注入
public class BeanDefinition {

    /**
     * 类全限定名.
     */
    private Class> aClass;

    /**
     * 实例化.
     *
     * @return
     */
    public static BeanDefinition newInstance() {
        return new BeanDefinition();
    }

    public Class> getaClass() {
        return aClass;
    }

    public void setaClass(Class> aClass) {
        this.aClass = aClass;
    }

}
public class ApplicationContext {

    private List beanDefinitiOns= new ArrayList();
    private Map singletOnObjects= new HashMap();

    public ApplicationContext(String packagePath) {
        /**
         * 扫描加载Beandifinition.
         */
        doScanner(packagePath);
        /**
         * 实例化.
         */
        doInstance();
        /**
         * 实现依赖注入.
         */
        doDI();
    }

    /**
     * 实例化.
     *
     * @param packagePath
     * @return
     */
    public static ApplicationContext newInstance(String packagePath) {
        return new ApplicationContext(packagePath);
    }

    /**
     * 扫描包路径,加载BeanDifinition.
     *
     * @param packagePath
     */
    private void doScanner(String packagePath) {
        URL url = getClass().getClassLoader().getResource(packagePath.replaceAll("\.", "/"));
        File dir = new File(Objects.requireNonNull(url).getFile());
        for (File file : Objects.requireNonNull(Objects.requireNonNull(dir).listFiles())) {
            if (file.isDirectory()) {
                doScanner(packagePath + "." + file.getName());
            }
            if (file.isFile()) {
                String className = packagePath + "." + file.getName().replace(".class", "");
                try {
                    Class> aClass = Class.forName(className);
                    BeanDefinition beanDefinition = BeanDefinition.newInstance();
                    beanDefinition.setaClass(aClass);
                    beanDefinitions.add(beanDefinition);
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    private void doInstance() {
        Optional.ofNullable(beanDefinitions).ifPresent(difinitions -> {
            difinitions.forEach(beanDefinition -> {
                try {
                    Class> aClass = beanDefinition.getaClass();
                    if (aClass.isAnnotationPresent(Controller.class)) {
                        /**
                         * 类名称。
                         */
                        String simpleName = aClass.getSimpleName();
                        /**
                         * 首字母小写的类。
                         */
                        String lowerName = getLowerName(simpleName);
                        /**
                         * 放入单例池。
                         */
                        Object obj = aClass.newInstance();
                        singletonObjects.put(lowerName, obj);

                    } else if (aClass.isAnnotationPresent(Service.class)) {

                        Service service = aClass.getAnnotation(Service.class);
                        /**
                         * 获取beanName.
                         */
                        String beanName = Optional.ofNullable(service.value()).filter(value -> !"".equals(value)).orElseGet(
                                () -> getLowerName(aClass.getSimpleName()));
                        /**
                         * 放入单例池.
                         */
                        singletonObjects.put(beanName, aClass.newInstance());
                        /**
                         * 如果实现了接口,需要以接口的全限定名为beanName放入单例池一份.
                         */
                        Class>[] interfaces = aClass.getInterfaces();
                        if (interfaces != null && interfaces.length != 0) {
                            for (Class> anInterface : interfaces) {
                                /**
                                 * 放入单例池.
                                 */
                                singletonObjects.put(anInterface.getName(), aClass.newInstance());
                            }
                        }
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            });
        });
    }

    /**
     * 实现依赖注入.
     */
    private void doDI() {
        Optional.ofNullable(singletonObjects).filter(map -> !map.isEmpty()).ifPresent(map -> {
            map.values().stream().distinct().collect(Collectors.toList()).forEach(value -> {
                Field[] declaredFields = value.getClass().getDeclaredFields();
                for (Field field : declaredFields) {
                    field.setAccessible(true);
                    if (field.isAnnotationPresent(Autowired.class)) {
                        Autowired autowired = field.getAnnotation(Autowired.class);
                        String beanName = Optional.ofNullable(autowired.value())
                                .filter(name -> !"".equals(name)).orElseGet(() -> field.getType().getName());
                        try {
                            field.set(value, singletonObjects.get(beanName));
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            });
        });
    }

    /**
     * 获取首字母小写的单词.
     *
     * @param name
     * @return
     */
    private String getLowerName(String name) {

        String lowerCase = String.valueOf(name.charAt(0)).toLowerCase();
        String lowerName = lowerCase + name.substring(1);

        return lowerName;
    }

    public  Map getSingletonObjects() {
        return singletonObjects;
    }
}
第四步,实例化并注册HandlerMapping
public class HandlerMapping {

    private Object controller;
    private Map paramIndexMap;
    private Method method;
    private Pattern pattern;

    public HandlerMapping(Object controller, Method method, Pattern pattern) {
        this.cOntroller= controller;
        this.method = method;
        this.pattern = pattern;
        this.paramIndexMap = new HashMap();
    }

    public Object getController() {
        return controller;
    }

    public Map getParamIndexMap() {
        return paramIndexMap;
    }


    public Method getMethod() {
        return method;
    }

    public Pattern getPattern() {
        return pattern;
    }

}
public class HandlerMappingBuilder {

    private List handlerMappings;
    private ApplicationContext context;
    private final String EMPTY = "";

    public HandlerMappingBuilder(ApplicationContext context) {
        this.handlerMappings = new ArrayList();
        this.cOntext= context;
    }

    public static HandlerMappingBuilder newInstance(ApplicationContext context) {
        return new HandlerMappingBuilder(context);
    }

    public List build() {
        /**
         * 获取单例池.
         */
        Map objectMap = context.getSingletonObjects();
        if (!objectMap.isEmpty()) {
            for (Object obj : objectMap.values()) {
                if (obj.getClass().isAnnotationPresent(Controller.class)) {
                    /**
                     * 获取请求url.
                     */
                    String baseUrl = Optional.ofNullable(obj.getClass().getAnnotation(RequestMapping.class))
                            .map(RequestMapping::value).orElse(EMPTY);
                    Optional.ofNullable(obj.getClass().getMethods()).ifPresent(methods -> {
                        for (Method method : methods) {
                            if (method.isAnnotationPresent(RequestMapping.class)) {

                                String methodUrl = Optional.ofNullable(method.getAnnotation(RequestMapping.class))
                                        .map(RequestMapping::value).orElse(EMPTY);

                                /**
                                 * /xialu/hello.
                                 */
                                String requestUrl = baseUrl + methodUrl;

                                HandlerMapping handlerMapping = new HandlerMapping(obj, method, Pattern.compile(requestUrl));

                                /**
                                 * 封装参数位置,方便后续填充参数。(这里只处理HttpServletRequest,HttpServletReponse,基本数据类型等入参)
                                 */
                                Parameter[] parameters = method.getParameters();
                                for (int i = 0; i 
第五步,处理用户请求

这一步需要在最开始实现的DispatchServlet的doPost方法中添加逻辑

public class HandlerUtil {


    private HandlerUtil() {
    }

    public static Optional getHandlerMappint(List maps, String url) {
        /**
         * 遍历查找是否有匹配的处理器映射器.
         */
        for (HandlerMapping map : maps) {
            if (map.getPattern().matcher(url).matches()) {
                return Optional.ofNullable(map);
            }
        }
        return Optional.empty();
    }
}
   @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        /**
         * 获取处理器映射器.
         */
        Optional mappingOptiOnal= HandlerUtil.getHandlerMappint(this.handlerMappings, req.getRequestURI());
        mappingOptional.ifPresent(handlerMapping -> {
            /**
             * 获取方法需要的参数类型数组.
             */
            Class>[] parameterTypes = handlerMapping.getMethod().getParameterTypes();
            /**
             * 创建参数数组.
             */
            Object[] argArr = new Object[parameterTypes.length];
            /**
             * 获取请求参数.
             */
            Map parameterMap = req.getParameterMap();
            /**
             * 封装请求参数.
             */
            for (Map.Entry entry : parameterMap.entrySet()) {
                /**
                 * 因为req获取到的值是数组形式,需要自己转换成合适的格式.
                 */
                String value = Arrays.stream(entry.getValue()).collect(Collectors.joining(",", "", ""));
                /**
                 * 获取参数位置索引,并完成参数填充.
                 */
                Optional.ofNullable(handlerMapping.getParamIndexMap().get(entry.getKey())).ifPresent(index -> argArr[index] = value);
                Integer reqIndex = handlerMapping.getParamIndexMap().get(req.getClass().getSimpleName());
                Optional.ofNullable(reqIndex).ifPresent(index -> argArr[index] = req);
                Integer respIndex = handlerMapping.getParamIndexMap().get(resp.getClass().getSimpleName());
                Optional.ofNullable(respIndex).ifPresent(index -> argArr[index] = resp);
            }
            try {
                /**
                 * 反射调用方法.
                 */
                handlerMapping.getMethod().invoke(handlerMapping.getController(), argArr);
            } catch (Exception e) {
                e.printStackTrace();
            }
        });
    }
第六步,功能测试
Spring MVC学习笔记五
打成jar包

Spring MVC学习笔记五
添加依赖

Spring MVC学习笔记五
配置web.xml

Spring MVC学习笔记五
配置springmvc.properties

Spring MVC学习笔记五
controller层

Spring MVC学习笔记五
service层

Spring MVC学习笔记五
service.impl

Spring MVC学习笔记五
发起请求

Spring MVC学习笔记五
测试通过

推荐阅读
  • ps:写的第一个,不足之处,欢迎拍砖---只是想用自己的方法一步步去实现一些框架看似高大上的小功能(比如说模型中的toArraytoJsonsetAtt ... [详细]
  • 在springmvc框架中,前台ajax调用方法,对图片批量下载,如何弹出提示保存位置选框?Controller方法 ... [详细]
  • Java实战之电影在线观看系统的实现
    本文介绍了Java实战之电影在线观看系统的实现过程。首先对项目进行了简述,然后展示了系统的效果图。接着介绍了系统的核心代码,包括后台用户管理控制器、电影管理控制器和前台电影控制器。最后对项目的环境配置和使用的技术进行了说明,包括JSP、Spring、SpringMVC、MyBatis、html、css、JavaScript、JQuery、Ajax、layui和maven等。 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • r2dbc配置多数据源
    R2dbc配置多数据源问题根据官网配置r2dbc连接mysql多数据源所遇到的问题pom配置可以参考官网,不过我这样配置会报错我并没有这样配置将以下内容添加到pom.xml文件d ... [详细]
  • MVC设计模式的介绍和演化过程
    本文介绍了MVC设计模式的基本概念和原理,以及在实际项目中的演化过程。通过分离视图、模型和控制器,实现了代码的解耦和重用,提高了项目的可维护性和可扩展性。详细讲解了分离视图、分离模型和分离控制器的具体步骤和规则,以及它们在项目中的应用。同时,还介绍了基础模型的封装和控制器的命名规则。该文章适合对MVC设计模式感兴趣的读者阅读和学习。 ... [详细]
  • 本文介绍了ASP.NET Core MVC的入门及基础使用教程,根据微软的文档学习,建议阅读英文文档以便更好理解,微软的工具化使用方便且开发速度快。通过vs2017新建项目,可以创建一个基础的ASP.NET网站,也可以实现动态网站开发。ASP.NET MVC框架及其工具简化了开发过程,包括建立业务的数据模型和控制器等步骤。 ... [详细]
  • SpringBoot简单日志配置
     在生产环境中,只打印error级别的错误,在测试环境中,可以调成debugapplication.properties文件##默认使用logbacklogging.level.r ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • flowable工作流 流程变量_信也科技工作流平台的技术实践
    1背景随着公司业务发展及内部业务流程诉求的增长,目前信息化系统不能够很好满足期望,主要体现如下:目前OA流程引擎无法满足企业特定业务流程需求,且移动端体 ... [详细]
  • 本文介绍了Windows操作系统的版本及其特点,包括Windows 7系统的6个版本:Starter、Home Basic、Home Premium、Professional、Enterprise、Ultimate。Windows操作系统是微软公司研发的一套操作系统,具有人机操作性优异、支持的应用软件较多、对硬件支持良好等优点。Windows 7 Starter是功能最少的版本,缺乏Aero特效功能,没有64位支持,最初设计不能同时运行三个以上应用程序。 ... [详细]
  • MySQL中的MVVC多版本并发控制机制的应用及实现
    本文介绍了MySQL中MVCC的应用及实现机制。MVCC是一种提高并发性能的技术,通过对事务内读取的内存进行处理,避免写操作堵塞读操作的并发问题。与其他数据库系统的MVCC实现机制不尽相同,MySQL的MVCC是在undolog中实现的。通过undolog可以找回数据的历史版本,提供给用户读取或在回滚时覆盖数据页上的数据。MySQL的大多数事务型存储引擎都实现了MVCC,但各自的实现机制有所不同。 ... [详细]
  • 本文介绍了MVP架构模式及其在国庆技术博客中的应用。MVP架构模式是一种演变自MVC架构的新模式,其中View和Model之间的通信通过Presenter进行。相比MVC架构,MVP架构将交互逻辑放在Presenter内部,而View直接从Model中读取数据而不是通过Controller。本文还探讨了MVP架构在国庆技术博客中的具体应用。 ... [详细]
  • centos安装Mysql的方法及步骤详解
    本文介绍了centos安装Mysql的两种方式:rpm方式和绿色方式安装,详细介绍了安装所需的软件包以及安装过程中的注意事项,包括检查是否安装成功的方法。通过本文,读者可以了解到在centos系统上如何正确安装Mysql。 ... [详细]
  • Todayatworksomeonetriedtoconvincemethat:今天在工作中有人试图说服我:{$obj->getTableInfo()}isfine ... [详细]
author-avatar
我没资格我不配
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有