阶段一模块三

手写springmvc Spring MVC 通过 @RequestMapping 注解将 URL 请求与业务方法进行进行映射。 在控制器的类定义处以及方法定义处都可添加 @RequestMapping,在类定义处添加 @RequestMapping 注解,相当于多了一层访问路径。 立即下载

作业要求:

手写 MVC 框架基础上增加如下功能:

  1. 定义注解 @Security(有 value 属性,接收 String 数组),该注解用于添加在 Controller 类或者 Handler 方法上,表明哪些用户拥有访问该 Handler 方法的权限(注解配置用户名)

  2. 访问 Handler 时,用户名直接以参数名 username 紧跟在请求的 url 后面即可,比如 http://localhost:8080/demo/handle01?username=zhangsan

  3. 程序要进行验证,有访问权限则放行,没有访问权限在页面上输出

注意:自己造几个用户以及 url,上交作业时,文档提供哪个用户有哪个 url 的访问权限

分析:

参照着 Spring MVC 框架,很多同学第一反应都应该是拦截器 Interceptor,同样,我的第一反应也是实现拦截器,然后实现权限控制。然而,当我在参照着课程将手写 MVC 框架实现后,我发现调用 handler 方法的时候本身就是通过反射调用的,而权限校验恰好可以直接添加在这之前,这无疑比实现拦截器要简单快捷很多。

作业要求分析之后,其实就是获取请求中的 username 属性,然后按照 Controller 以及 Handler 方法上的 @Security 注解中的 value 数组进行比较,如果当前用户在数组中,则可以调用 Handler 方法,否则直接输出没有权限访问。

权限:

  1. Controller

    • DemoController

      映射请求路径:"/demo"

      权限:{"first", "second", "third"}

  2. Method

    • query()

      映射请求路径:"/query"

      权限:无

    • queryArr()

      映射请求路径:"/queryArr"

      权限:无

    • querySecurity()

      映射请求路径:"/querySecurity"

      权限:{"first"}

请求:

  1. http://localhost:8080 404
  2. http://localhost:8080/demo/query 没有权限
  3. http://localhost:8080/demo/query?username=first&name=xxx 正常执行
  4. http://localhost:8080/demo/query?username=second&name=xxx 正常执行
  5. http://localhost:8080/demo/query?username=first&name=xxx&name=vvv 正常执行
  6. http://localhost:8080/demo/query?username=fourth&name=xxx 没有权限
  7. http://localhost:8080/demo/queryArr?username=second&name=xxx&name=vvv 正常执行
  8. http://localhost:8080/demo/querySecurity?username=second&name=xxx 没有权限
  9. http://localhost:8080/demo/querySecurity?username=first&name=xxx 正常执行

代码:

注解部分:
  1. Security

    package cn.worstone.framework.annotation;
    
    import java.lang.annotation.*;
    
    @Documented
    @Target({ElementType.TYPE, ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    public @interface Security {
        String[] value();
    }
    
    
代码修改部分:
  1. DispatcherServlet

    package cn.worstone.framework.servlet;
    
    import cn.worstone.framework.annotation.*;
    import cn.worstone.framework.bean.Handler;
    import org.apache.commons.lang3.StringUtils;
    
    import javax.servlet.ServletConfig;
    import javax.servlet.ServletException;
    import javax.servlet.http.HttpServlet;
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import javax.servlet.http.HttpSession;
    import java.io.File;
    import java.io.IOException;
    import java.io.InputStream;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.util.*;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    
    public class DispatcherServlet extends HttpServlet {
        private Properties properties = new Properties();
        private List<String> classNames = new ArrayList<>();
        private Map<String, Object> beansMap = new HashMap<>();
        private List<Handler> handlers = new ArrayList<>();
        private List<Class> commonsType = new ArrayList<>();
    
        {
            commonsType.add(HttpServletRequest.class);
            commonsType.add(HttpServletResponse.class);
            commonsType.add(HttpSession.class);
        }
    
        @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 {
            // 处理请求
            // 根据请求 Url, 获取对应的 Handler, 获取其中的方法, 然后进行调用
            Handler handler = getHandler(req);
    
            if (handler == null) {
                resp.getWriter().write("404 NOT FOUND !");
                return;
            }
    
            // 权限校验
            if (!checkSecurity(req, resp, handler)) return;
    
            // 参数绑定
            // 构建参数数组, 数组长度与调用方法参数类型数组的长度一致
            Method method = handler.getMethod();
            Parameter[] parameters = method.getParameters();
            Object[] args = new Object[parameters.length];
    
            // 构建方法参数, 需要保证参数的顺序
            Map<String, String[]> parameterMap = req.getParameterMap();
            for (int i = 0; i < parameters.length; i++) {
                Parameter parameter = parameters[i];
                // 处理公共参数
                if (commonsType.contains(parameter.getType())) {
                    if (parameter.getType() == HttpServletRequest.class) {
                        args[i] = req;
                        continue;
                    }
                    if (parameter.getType() == HttpServletResponse.class) {
                        args[i] = resp;
                        continue;
                    }
                    if (parameter.getType() == HttpSession.class) {
                        HttpSession session = req.getSession();
                        args[i] = session;
                        continue;
                    }
                }
    
                String[] values = parameterMap.get(parameter.getName());
                // 没有找到当前参数
                if (values == null) continue;
                // 这里没有进行参数类型转换
                if (parameter.getType().isArray()) {
                    // 如果参数类型为数组, 则直接赋值为 values
                    args[i] = values;
                } else {
                    // 如果参数不为数组
                    if (values.length == 1) {
                        // 数组中只有 1 个元素
                        args[i] = values[0];
                    } else {
                        // 数组中有多个元素
                        args[i] = StringUtils.join(values, ",");
                    }
                }
            }
    
            // 调用 handler 中的方法
            try {
                handler.getMethod().invoke(handler.getController(), args);
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        @Override
        public void init(ServletConfig config) throws ServletException {
            // 加载配置文件
            String contextConfigLocation = config.getInitParameter("contextConfigLocation");
            doLoadingConfig(contextConfigLocation);
    
            // 扫描注解
            doScan(properties.getProperty("scanPackage"));
    
            // 初始化 Bean 对象
            doInstance();
    
            // 实现依赖注入
            doAutowired();
    
            // 构造处理器映射器
            initHandlerMapping();
    
            System.out.println("MVC Framework init finished...");
        }
    
        private void doLoadingConfig(String contextConfigLocation) {
            InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(contextConfigLocation);
            try {
                properties.load(inputStream);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    
        private void doScan(String packagePath) {
            // 没有处理 classpath*:
            String path = Thread.currentThread().getContextClassLoader().getResource("").getPath() + packagePath.replaceAll("\\.", "/");
            File packageFolder = new File(path);
            File[] files = packageFolder.listFiles();
            for (File file : files) {
                if (file.isDirectory()) {
                    // 目录
                    doScan(packagePath + "." + file.getName());
                } else {
                    // 文件
                    if (file.getName().endsWith(".class")) {
                        String className = packagePath + "." + file.getName().replaceAll(".class", "");
                        classNames.add(className);
                    }
                }
            }
        }
    
        private void doInstance() {
            if (classNames.size() == 0) return;
            try {
                for (String className : classNames) {
                    // 获取 Class 对象
                    Class<?> aClass = Class.forName(className);
                    // 扫描 @Controller 注解以及 @Service 注解
                    if (aClass.isAnnotationPresent(Controller.class)) {
                        // 处理 @Controller
                        // 直接将类首字母小写作为 bean 名称
                        Object bean = aClass.getDeclaredConstructor().newInstance();
                        String name = lowerFirstCharacter(aClass.getSimpleName());
                        beansMap.put(name, bean);
                    } else if (aClass.isAnnotationPresent(Service.class)) {
                        // 处理 @Service
                        Object bean = aClass.getDeclaredConstructor().newInstance();
                        Service annotation = aClass.getAnnotation(Service.class);
                        // 获取注解 value 属性值
                        String name = annotation.value();
                        if ("".equals(name)) {
                            // value 属性值为 ""
                            // 如果没有指定 Service 名称, 则默认以其类名首字母小写作为名称
                            name = lowerFirstCharacter(aClass.getSimpleName());
                        }
                        beansMap.put(name, bean);
    
                        // Service 一般是面向接口开发, 为了后续的依赖注入, 这里根据接口类型存入一份
                        Class<?>[] interfaces = aClass.getInterfaces();
                        for (Class<?> anInterface : interfaces) {
                            beansMap.put(anInterface.getName(), bean);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    
        private String lowerFirstCharacter(String simpleName) {
            char[] chars = simpleName.toCharArray();
            // 判断其首字母是否大写
            if ('A' <= chars[0] && chars[0] <= 'Z') {
                chars[0] += 32;
            }
            return String.valueOf(chars);
        }
    
        private void doAutowired() {
            if (beansMap.isEmpty()) return;
    
            // 判断对象的字段是否存在 @Autowired 注解
            for (String key : beansMap.keySet()) {
                Object bean = beansMap.get(key);
                Field[] fields = bean.getClass().getDeclaredFields();
                for (Field field : fields) {
                    if (field.isAnnotationPresent(Autowired.class)) {
                        // 当前字段存在 @Autowired 注解
                        Autowired annotation = field.getAnnotation(Autowired.class);
                        String name = annotation.value();
                        // 在 Spring 中这里判断的是 @Qualifier 注解
                        if ("".equals(name.trim())) {
                            // 如果没有指定具体名称, 则直接使用类型名
                            name = field.getType().getName();
                        }
    
                        // 开启暴力访问
                        field.setAccessible(true);
                        try {
                            // 属性赋值
                            // 判断当前的 name 对应的 bean 对象是否存在
                            Object autowired = beansMap.get(name);
                            if (autowired == null) {
                                throw new RuntimeException("The bean is not found. name: " + name);
                            }
                            field.set(bean, autowired);
                        } catch (IllegalAccessException e) {
                            e.printStackTrace();
                        }
                    }
                }
            }
        }
    
        private void initHandlerMapping() {
            if (beansMap.isEmpty()) return;
            for (String key : beansMap.keySet()) {
                Object bean = beansMap.get(key);
                Class<?> aClass = bean.getClass();
    
                if (aClass.isAnnotationPresent(Controller.class)) {
                    String baseUrl = "";
                    if (aClass.isAnnotationPresent(RequestMapping.class)) {
                        RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
                        baseUrl = annotation.value();
                    }
    
                    // 解析 @Security 注解
                    String[] typeValues = new String[0];
                    if (aClass.isAnnotationPresent(Security.class)) {
                        Security annotation = aClass.getAnnotation(Security.class);
                        typeValues = annotation.value();
                    }
    
                    // 获取当前 Controller 下的全部方法
                    Method[] methods = aClass.getMethods();
                    for (Method method : methods) {
                        if (method.isAnnotationPresent(RequestMapping.class)) {
                            // 当前方法存在 @RequestMapping 注解
                            RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                            String specificUrl = annotation.value();
                            String url = baseUrl + specificUrl;
    
                            // 封装 Handler 对象
                            Handler handler = new Handler(bean, method, Pattern.compile(url));
    
                            // 存储 HandlerMapping 映射关系
                            handlers.add(handler);
    
                            // 解析 @Security 注解
                            String[] methodValues = new String[0];
                            if (method.isAnnotationPresent(Security.class)) {
                                Security anno = method.getAnnotation(Security.class);
                                methodValues = anno.value();
                            }
    
                            // 构建权限集合
                            List<String> security = new ArrayList<>();
                            if (typeValues.length == 0 || methodValues.length == 0) {
                                security.addAll(Arrays.asList(typeValues));
                                security.addAll(Arrays.asList(methodValues));
                            } else {
                                Set<String> set = new HashSet<>(Arrays.asList(typeValues));
                                for (String methodValue : methodValues) {
                                    if (!set.add(methodValue)) {
                                        security.add(methodValue);
                                    }
                                }
                            }
                            if (security.size() > 0) {
                                handler.setSecurityList(security);
                            }
                        }
                    }
                }
            }
        }
    
        private Handler getHandler(HttpServletRequest request) {
            if (handlers.isEmpty()) return null;
            String uri = request.getRequestURI();
            for (Handler handler : handlers) {
                Matcher matcher = handler.getPattern().matcher(uri);
                if (matcher.matches()) {
                    return handler;
                }
            }
            return null;
        }
    
        // 调用请求的时候判断权限
        /*
        private boolean checkSecurity(HttpServletRequest request, HttpServletResponse response, Handler handler) {
            String username = Optional.ofNullable(request.getParameter("username")).orElse("");
            // 校验 Controller 权限
            Object controller = handler.getController();
            boolean isSecurity = check(username, controller.getClass().getAnnotation(Security.class));
            if (!isSecurity) {
                // 没有权限
                securityHandle(request, response);
                return false;
            }
            // 校验 Method 权限
            Method method = handler.getMethod();
            isSecurity = check(username, method.getAnnotation(Security.class));
            if (!isSecurity) {
                // 没有权限
                securityHandle(request, response);
                return false;
            }
            return true;
        }
        */
    
        // 预处理方式判断权限
        private boolean checkSecurity(HttpServletRequest request, HttpServletResponse response, Handler handler) {
            String username = Optional.ofNullable(request.getParameter("username")).orElse("");
            List<String> securityList = handler.getSecurityList();
            if (securityList != null) {
                if (!securityList.contains(username)) {
                    securityHandle(request, response);
                    return false;
                }
            }
            return true;
        }
    
        private boolean check(String username, Security annotation) {
            // 权限校验
            if (annotation == null) return true;
            String[] values = annotation.value();
            for (int i = 0; i < values.length; i++) {
                if (username.equals(values[i])) {
                    return true;
                }
            }
            return false;
        }
    
        private void securityHandle(HttpServletRequest request, HttpServletResponse response) {
            // 没有权限的处理
            // 可以输出提示, 也可以跳转页面
            try {
                response.getWriter().write("The current user does not have permission.");
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
    
    

    在 DispatcherServlet 中做了一些修改,之前在课程里面老师接收的参数如果是数组则直接转换成字符串,每个数组元素以"," 分割,这个地方,我将其修改成支持 String 数组类型,如果调用方法中接收的是数组类型参数,则直接传入这个数组,如果是 String 类型,则与课程中的处理方式一致。同时,在不断修改代码的过程中我发现 paramIndexMapping 这个集合特别无用,所以这里将其去掉。我这里的处理方式是,每次获取方法的参数数组,然后循环这个参数数组,不断地按照顺序去构建方法参数数组,获取请求传入参数的时候,直接使用当前方法参数的名称直接获取,如果获取的数组不是 null,则证明当前传入参数不为空。

    关于权限验证这个地方,这里是在根据请求获取到 Handler 之后进行权限校验。如果当前用户没有权限,则直接输出 The current user does not have permission.。这里校验具体实现是,通过 Handler 获取当前请求的 Controller 对象以及 Method,然后分别对 @Security 注解进行判断,这里首先判断的是 Controller。这种方式有一些弊端,每次请求的时候,都会通过反射去判断 Controller 类上面以及对应方法上面是否存在 @Security 注解,效率很差,但是不是没有优点,节约空间成本,不需要去存储权限映射。进一步优化,在初始化的时候对 @Security 注解进行扫描,然后使用一个 Map 将权限信息进行存储,当请求来的时候,只需要直接判断即可。这种方式预处理了权限信息,所以当请求来的时候,处理权限效率非常高,但是会占用空间去存储权限信息。两种方式其实可以结合起来,就是请求来的时候判断去权限 Map 中去查找,如果没有则去解析当前 Handler 是否进行权限控制,处理完毕将其存储到权限 Map 中,这种方式相当于前面两种方式的综合。这次作业里面,实现了前面两种方式。

目前存在的缺点:(对比 Spring MVC)

  1. 方法参数处理相对比较简陋
  2. 视图解析方面没有实现
  3. 在解析框架的配置文件的路径的时候,没有处理 classpath*:

问题:

  1. Tomcat 10 会报找不到 Servlet 错误。 (因为 Tomcat 10 中的 Servlet 中修改)
  2. Maven 的编译后的变量名称依旧是没有变化,依旧是 arg。 (使用 Maven 将构建好的项目清理一下,重新构建即可)
  3. 配置了 @Service 注解依旧找不到对应的 Service,一直报 NullPointerException (getFields() 方法)

扩展:

Tomcat 10自带Jakarta® EE,包名不再是“javax.servlet.**”

修改:

直播的时候,作业讲解的时候,老师说遵循注解的“就近原则”,所以这里对代码进行修改。

说明一下修改思路:

分别获取 Controller 以及 Method 方法上面的 @Security 注解的 value 数组,初始值为 String[0]。创建一个集合,然后将 Method 解析的数组放入到集合中,如果 Method 解析的数组长度为 0,那么将 Controller 解析的数组放入到集合中。最后判断集合长度,如果为 0,则说明没有权限校验,如果不为 0,那么直接返回是否包含 username 即可。

// 调用请求的时候判断权限
private boolean checkSecurity(HttpServletRequest request, HttpServletResponse response, Handler handler) {
    String username = Optional.ofNullable(request.getParameter("username")).orElse("");
    List<String> securityList = new ArrayList<>();
    // 获取 Controller 权限
    Object controller = handler.getController();
    String[] typeValues = securityValues(controller.getClass().getAnnotation(Security.class));
    // 获取 Method 权限
    Method method = handler.getMethod();
    String[] methodValues = securityValues(method.getAnnotation(Security.class));
    securityList.addAll(Arrays.asList(methodValues));
    if (methodValues.length == 0) {
        securityList.addAll(Arrays.asList(typeValues));
    }
    if (securityList.size() == 0) return true;
    return securityList.contains(username);
}


private void initHandlerMapping() {
    if (beansMap.isEmpty()) return;
    for (String key : beansMap.keySet()) {
        Object bean = beansMap.get(key);
        Class<?> aClass = bean.getClass();

        if (aClass.isAnnotationPresent(Controller.class)) {
            String baseUrl = "";
            if (aClass.isAnnotationPresent(RequestMapping.class)) {
                RequestMapping annotation = aClass.getAnnotation(RequestMapping.class);
                baseUrl = annotation.value();
            }

            // 解析 @Security 注解
            String[] typeValues = new String[0];
            if (aClass.isAnnotationPresent(Security.class)) {
                Security annotation = aClass.getAnnotation(Security.class);
                typeValues = annotation.value();
            }

            // 获取当前 Controller 下的全部方法
            Method[] methods = aClass.getMethods();
            for (Method method : methods) {
                if (method.isAnnotationPresent(RequestMapping.class)) {
                    // 当前方法存在 @RequestMapping 注解
                    RequestMapping annotation = method.getAnnotation(RequestMapping.class);
                    String specificUrl = annotation.value();
                    String url = baseUrl + specificUrl;

                    // 封装 Handler 对象
                    Handler handler = new Handler(bean, method, Pattern.compile(url));

                    // 存储 HandlerMapping 映射关系
                    handlers.add(handler);

                    // 解析 @Security 注解
                    String[] methodValues = new String[0];
                    if (method.isAnnotationPresent(Security.class)) {
                        Security anno = method.getAnnotation(Security.class);
                        methodValues = anno.value();
                    }

                    // 构建权限集合
                    List<String> security = new ArrayList<>();
                    security.addAll(Arrays.asList(methodValues));
                    if (methodValues.length == 0) {
                        security.addAll(Arrays.asList(typeValues));
                    }
                    if (security.size() > 0) {
                        handler.setSecurityList(security);
                    }
                }
            }
        }
    }
}

权限:

  1. Controller

    • DemoController

      映射请求路径:"/demo"

      权限:{"first", "second", "third"}

  2. Method

    • query()

      映射请求路径:"/query"

      权限:无

    • queryArr()

      映射请求路径:"/queryArr"

      权限:无

    • querySecurity()

      映射请求路径:"/querySecurity"

      权限:{"first", "fourth"} 注意:这个地方对比之前修改过

请求:

  1. http://localhost:8080 404

  2. http://localhost:8080/demo/query 没有权限

  3. http://localhost:8080/demo/query?username=first&name=xxx 正常执行

  4. http://localhost:8080/demo/query?username=second&name=xxx 正常执行

  5. http://localhost:8080/demo/query?username=first&name=xxx&name=vvv 正常执行

  6. http://localhost:8080/demo/query?username=fourth&name=xxx 没有权限

  7. http://localhost:8080/demo/queryArr?username=second&name=xxx&name=vvv 正常执行

  8. http://localhost:8080/demo/querySecurity?username=second&name=xxx 没有权限

  9. http://localhost:8080/demo/querySecurity?username=first&name=xxx 正常执行

  10. http://localhost:8080/demo/querySecurity?username=fourth&name=xxx 正常执行

springmvc 拦截器 立即下载

相关推荐

基于注解的Spring MVC小demo

此demo是基于注解的SpringMVC,结构清晰,新手可以参考一下,所有用到的jar包我都打包进去了;

spring mvc手写权限框架实现

一、编程题 手写MVC框架基础上增加如下功能 1)定义注解@Security(有value属性,接收String数组),该注解用于添加在Controller类或者Handler方法上,表明哪些用户拥有访问该Handler方法的权限(注解配置用户名) 2)访问Handler时,用户名直接以参数名username紧跟在请求的url后面即可,比如http://localhost:8080/auth/admin?name=admin 3)程序要进行验证,有访问权限则放行,没有访问权限在页面上输出 注意:自己造几个用

weixin_43960071的博客 602

Spring_MVC_test.zip

用springMVC、spring和hibernate写的Java框架,spring不是由xml配置,而是使用Java配置

springmvc学习

一、实现安全管控 在手写mvc框架得基础上(只完成了请求分发和自定义spring框架),定义注解@Security(有value属性,接收String数组),该注解用于添加在Controller类或者Handler方法上,实现安全管控。 一、思路 定义注解@Security,使用到Controller类上,代表其中的所有Handler方法都要进行安全管控;使用到Handler方法上,代表该方法需要进行安全管控。考虑了四种实现方法: 1.依照自定义spring框架中实现transactional的方式,我们可

jiqiren1994的博客 540

Spring Data JPA文档.zip

详细的介绍spring和jpa结合,包含的jpa的注解的解析和配置,比较详细的介绍,适合初级和进阶学习

手写简单Spring MVC

手写简易SpringMVC 附测试demo

阶段一模块三总结

阶段一模块三总结

qq_23187859的博客 274

springMVC使用注解方式进行页面跳转

springMVC使用注解方式进行页面跳转

阶段三模块一 HTML

内容输出来源:拉钩教育Java就业训练营 1 HTML介绍 HTML 指的是超文本标记/标签语言 (Hyper Text Markup Language) 专门制作网页的计算机语言 普通的文本就是英文单词,英文字母一样的存在。 超文本的意思是有一些单词或字母,在网页浏览器的世界中被赋予了特殊的权利。 字母img只是普通的字母,没什么特殊的含义。而被加上尖括号后,在网页的世界中,就具备了 显示图片的作用。 <html> <head> <title>拉钩有我

wangxu123445678的博客 206

springMVC+springDataJPA

在网上不知道找了多少个,没有一个能用的,这个,是我搭建的,亲测可用,直接可以跑

双轴按键摇杆模块原理与嵌入式驱动实战

双轴按键摇杆是一种融合模拟量(X/Y轴)与数字量(Z轴按键)的复合型人机交互传感器,其本质基于电位器分压原理与机械开关通断特性。通过ADC采集连续电压信号并结合数字IO检测按键状态,可实现高鲁棒性的二维运动感知与触发控制。该方案技术门槛低、成本可控、兼容性强,广泛应用于智能小车、DIY游戏手柄、工业HMI及教育开发板等嵌入式场景。文中深入解析电位器线性度、KEY引脚开漏特性、ADC参考电压稳定性及软件消抖状态机等关键工程要素,为基于Arduino、STM32或ESP32平台的快速集成提供可复用的技术路径。

weixin_30995917的博客 84

阶段三模块一作业

简答题 1、谈谈你对工程化的初步认识,结合你之前遇到过的问题说出三个以上工程化能够解决问题或者带来的价值。 2、你认为脚手架除了为我们创建项目结构,还有什么更深的意义? 编程题 1、概述脚手架实现的过程,并使用 NodeJS 完成一个自定义的小型脚手架工具 2、尝试使用 Gulp 完成项目的自动化构建 项目基础代码下载地址: 百度网盘:https://pan.baidu.com/s/1AyGApMTFEfCeGfQBdykOGg 提取码: bw3r 说明: 本次作业中的编程题要求大家完成相应代码后(二选一)

weixin_45345105的博客 337

【js逆向学习】新手视角破解「广东省公共资源交易平台」

学习js逆向,破解广东省公共资源交易平台,详细记录个人探索、踩坑全过程。网站地址: aHR0cHM6Ly95Z3AuZ2R6d2Z3Lmdvdi5jbi8jLzQ0L2p5Z2c=仅供学习参考,如有疑问欢迎交流,一起学习进步。

DCSDA的博客 3321

阶段三模块一 CSS

内容输出来源:拉钩教育Java就业训练营 1 CSS CSS是指层叠样式表 cascading style sheets 通过CSS可以让我们定义HTML元素如何显示。 CSS可以让我们原本HTML不能描述的效果,通过CSS描述出来。 通过CSS描述我们的html页面,可以让我们的页面更加漂亮,可以提高工作效率。 2 CSS与HTML结合方式 2.1 内联/行内样式 就是在我们的HTML标签上通过style属性来引用CSS代码。 优点:简单方便 ; 缺点:只能对一个标签进行修饰。 2.2 内部样式表 我们通

wangxu123445678的博客 216

CondaHTTPError: HTTP 404 NOT FOUND for url

用conda创建新环境时出现问题了。

qq_40713201的博客 1270

BQ4050开发进阶(二):Protections参数优化策略与实战解析

本文深入解析了BQ4050电池管理芯片中Protections参数的优化策略与实战方法。针对电压、电流、温度等核心保护功能,详细阐述了如何根据具体电芯特性、负载场景和环境因素,精细调整CUV、COV、OCC/OCD等阈值与延时,以在确保电池绝对安全的前提下,实现极致的用户体验与系统可靠性。

rgv234567的博客 482

阶段三模块一 JavaScript

内容输出来源:拉钩教育Java就业训练营 1 JavaScript JavaScript 是一种网页编程技术,用来向 HTML 页面添加交互行为 JavaScript 是一种基于对象和事件驱动的解释性脚本语言,直接嵌入 HTML 页面,由浏览器解释执行代码,不进行预编译。 1.1 js的特点 可以使用任何文本编辑工具编写,只需要浏览器就可以执行程序 解释执行:事先不编译,逐行执行 基于对象:内置大量现成对象 适宜: 客户端数据计算 客户端表单合法性验证 浏览器事件的触发 网页特殊显示效果制作 1.2 js的

wangxu123445678的博客 488

G.6 PaddleHub实战篇{ERNIE实现文新闻本分类、ERNIE3.0 实现序列标注}【四】

百度飞桨:ERNIE 3.0 、通用信息抽取 UIE、paddleNLP的安装使用[一]_汀、的博客-优快云博客PaddleHub--飞桨预训练模型应用工具{风格迁移模型、词法分析情感分析、Fine-tune API微调}【一】_汀、的博客-优快云博客 PaddleHub--{超参优化AutoDL Finetuner}【二】_汀、的博客-优快云博客PaddleHub实战篇{词法分析模型LAC、情感分类ERNIE Tiny}训练、部署【三】_汀、的博客-优快云博客通过前面几篇文章大家都有一定了解,下面

丨汀、的博客 6186

阶段三模块一 直播

注意介绍vue集成ts

weixin_45345105的博客 301

作为一名爬虫人如何实现千万级数据爬取?

爬虫门槛不高,高就高在往后余生的每一次实操都随时会让你崩溃!Bro

吴秋霖的博客 1万+

第154章 点云大规模并行处理(GPU加速)

优化点云处理✨ 使用。

yumeiguo的博客 374

阶段一模块三 常用类

在Integer类的内部提供了自动装箱池技术,将-128到127之间的整数已经装箱完毕,当程序中使用该范围之间的整数时,无需装箱直接取用自动装箱池中的对象即可,从而提高效率。

wangxu123445678的博客 211

从零到精通:BLDC电机驱动电路详解与设计思路

这套电机驱动电路从核心控制器到反馈采集模块,涉及了BLDC电机驱动的方方面面。每一个模块的设计和选择都需要综合考虑系统需求和实际使用环境。在实际应用中,很多细微的设计改进都会显著影响电机的运行性能。

weixin_43199439的博客 3109

阶段一模块三 String类

1 String类 java.lang.String类用于描述字符串,Java程序中所有的字符串字面值都可以使用该类的对象加以描述。 该类由final关键字修饰,表示该类不能被继承。 2 常量池 由于String类型描述的字符串内容是常量不可改变,因此Java虚拟机将首次出现的字符串放入常量池中。 若后续代码中出现了相同字符串内容则直接使用池中已有的字符串对象而无需申请内存及创建对象,从而提高了性能。 3 常用的构造方法 方法声明 功能介绍 String() 使用无参方式构造对象得到空字符序

wangxu123445678的博客 130

ET2012新版免狗免安装绿色软件

ET2012新版免狗免安祖昂绿色软件。,新界面,新视觉,

怀旧版电脑GBC模拟器

再现原版GBC掌机的界面,电脑运行连游戏界面都一样大小,很怀旧,很真实,(英文版)强烈推荐,很小的文件,很难才找到的。

阶段一模块三 集合类库(1)

1 集合概述 当需要在Java程序中记录单个数据内容时,则声明一个变量。 当需要在Java程序中记录多个类型相同的数据内容时,声明一个一维数组。 当需要在Java程序中记录多个类型不同的数据内容时,则创建一个对象。 当需要在Java程序中记录多个类型相同的对象数据时,创建一个对象数组。 当需要在Java程序中记录多个类型不同的对象数据时,则准备一个集合。 框架 Java中集合框架顶层框架是:java.util.Collection集合 和 java.util.Map集合。 其中Collection集合中存取

wangxu123445678的博客 144

lwip-2.0.3

lwip是瑞典计算机科学院(SICS)的Adam Dunkels 开发的一个小型开源的TCP/IP协议栈。实现的重点是在保持TCP协议主要功能的基础上减少对RAM 的占用。

上一篇: 阶段一模块二
下一篇: 阶段一模块四
LiveForCode
博客等级 码龄5年 2粉丝 16原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值