1 Spring

SpringBoot项目并不是替换Spring、SpringMVC,而是使他们用起来更加简单

1.1 Spring框架最核心的特性一依赖注入DI(控制反转IOC)

1.1.1 依赖注入的核心注解

@Component 最底层注解,依赖注入默认扫描的类型
@ResponseBody 返回数据json数据
@RequestBody 参数是json数据

下面接口都继承了@Component,在扫描解析@ComponentScan时会收集
@Repository dao层注解 (代码中是直接指定了包,没有使用注解也可以扫描接口)
@Service service层注解
@Controller controller层对外接口注解
@RestController @ResponseBody 与 @Controller 的组合体 ,对外接口返回json数据

@Autowired 最常用的注入注解 byName,byType
@Resource 注入注解 byName
@Qualifier 与@Resource配合使用,解决byType重复的问题

1.1.2 注解的使用

注解使用起来很简单,在需要注入的地方使用写上注解就可以,默认是单例。
image.png
image.png

1.2 Spring框架最核心的特性二 AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

1.2.1 AOP的应用场景如下

Authentication 权限
Caching 缓存
Context passing 内容传递
Error handling 错误处理
Lazy loading 懒加载
Debugging  调试
logging, tracing, profiling and monitoring 记录跟踪 优化 校准
Performance optimization 性能优化
Persistence  持久化
Resource pooling 资源池
Synchronization 同步
Transactions 事务

1.2.2 AOP的使用概念

代理 注入的类被动态代理 (jdk、cglib动态代理,jdk要实现接口,cglib不实现接口也可以代理)
切面 使用注解@Pointcut 可以理解为切西瓜的第一刀

切点
@Around 环绕通过,可以实现下面注解的所有功能
@Before
@After 最终通知
@AfterReturning 返回结果通知
@AfterThrowing 发生的异常通知

事务处理,现在spring5已经封装的很好了,要看底层原码才行,这里用日志打印来讲解AOP

示例1:

1.2.2.1 引入AOP包

  1. <dependency>
  2. <groupId>org.springframework.boot</groupId>
  3. <artifactId>spring-boot-starter-aop</artifactId>
  4. </dependency>

1.2.2.2 核心代码

  1. package com.alpaak.hellospringboot.utils.log;
  2. import com.alibaba.fastjson.JSON;
  3. import com.alibaba.fastjson.serializer.SerializerFeature;
  4. import com.alpaak.hellospringboot.utils.IPUtil;
  5. import org.aspectj.lang.ProceedingJoinPoint;
  6. import org.aspectj.lang.annotation.Around;
  7. import org.aspectj.lang.annotation.Aspect;
  8. import org.aspectj.lang.annotation.Pointcut;
  9. import org.aspectj.lang.reflect.MethodSignature;
  10. import org.slf4j.Logger;
  11. import org.slf4j.LoggerFactory;
  12. import org.springframework.stereotype.Component;
  13. import org.springframework.web.context.request.RequestContextHolder;
  14. import org.springframework.web.context.request.ServletRequestAttributes;
  15. import javax.servlet.http.HttpServletRequest;
  16. import javax.servlet.http.HttpServletResponse;
  17. import java.time.LocalDateTime;
  18. import java.util.Enumeration;
  19. import java.util.Objects;
  20. /**
  21. * @ClassName: ReqLogAspect
  22. * @Description: 请求日志
  23. * @author: Alpaak
  24. * @date: 2021/07/08 15:38
  25. */
  26. @Aspect
  27. @Component
  28. public class ReqLogAspect {
  29. private static final Logger LOGGER = LoggerFactory.getLogger(ReqLogAspect.class);
  30. private final static String REQ_TYPE_FILE = "file";
  31. private final static String REQ_TYPE_FILES = "files";
  32. @Pointcut("execution(* com.alpaak.hellospringboot.controller..*(..))")
  33. public void controller() {
  34. }
  35. @Around("controller()")
  36. public Object doAround(ProceedingJoinPoint pjp) throws Throwable {
  37. //记录请求开始执行时间:
  38. long beginTime = System.currentTimeMillis();
  39. RequestInfo request = resolve(pjp);
  40. //记录日志
  41. LOGGER.info("请求开始!\n{}", request);
  42. Object result = pjp.proceed();
  43. //请求操作成功
  44. String resultJson = "";
  45. if (result != null) {
  46. // if (result instanceof Result) {
  47. // resultJson = JSON.toJSONString(result, SerializerFeature.WriteDateUseDateFormat);
  48. // } else {
  49. // resultJson = String.valueOf(result);
  50. // }
  51. resultJson = JSON.toJSONString(result, SerializerFeature.WriteDateUseDateFormat);
  52. }
  53. //记录请求完成执行时间:
  54. long endTime = System.currentTimeMillis();
  55. long usedTime = endTime - beginTime;
  56. //记录日志
  57. LOGGER.info("{} \n请求成功! 耗时:[{}]ms.", resultJson, usedTime);
  58. // TODO Save to DB
  59. return result;
  60. }
  61. private RequestInfo resolve(ProceedingJoinPoint pjp) {
  62. //获取请求信息
  63. ServletRequestAttributes sra = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
  64. HttpServletRequest request = Objects.requireNonNull(sra).getRequest();
  65. Enumeration<String> headerNames = request.getHeaderNames();
  66. StringBuilder headers = new StringBuilder();
  67. while (headerNames.hasMoreElements()) {
  68. headers.append(headerNames.nextElement()).append(",");
  69. }
  70. LOGGER.debug("====> Headers: {}", headers.toString());
  71. //获取代理地址、请求地址、请求类名、方法名
  72. //获取请求参数
  73. MethodSignature ms = (MethodSignature) pjp.getSignature();
  74. //获取请求参数类型
  75. String[] parameterNames = ms.getParameterNames();
  76. //获取请求参数值
  77. Object[] parameterValues = pjp.getArgs();
  78. StringBuilder params = new StringBuilder();
  79. //组合请求参数,进行日志打印
  80. if (parameterNames != null && parameterNames.length > 0) {
  81. for (int i = 0; i < parameterNames.length; i++) {
  82. String name = parameterNames[i];
  83. if ("bindingResult".equals(name)) {
  84. break;
  85. }
  86. // 文件
  87. if (REQ_TYPE_FILE.equals(name.toLowerCase()) ||
  88. REQ_TYPE_FILES.equals(name.toLowerCase())) {
  89. continue;
  90. }
  91. Object value = parameterValues[i];
  92. if ((value instanceof HttpServletRequest) || (value instanceof HttpServletResponse)) {
  93. params.append("{").append(name).append(":").append(value).append("}");
  94. } else {
  95. params.append("{").append(name).append(":")
  96. .append(JSON.toJSONString(value, SerializerFeature.WriteDateUseDateFormat))
  97. .append("}");
  98. }
  99. }
  100. }
  101. final RequestInfo.RequestInfoBuilder builder = RequestInfo.builder()
  102. .date(LocalDateTime.now().toString())
  103. .address(IPUtil.getClientIp(request))
  104. .uri(request.getRequestURI())
  105. .method(request.getMethod())
  106. .clazz(pjp.getTarget().getClass().getSimpleName() + "->" + pjp.getSignature().getName())
  107. .params(params.toString());
  108. // Optional.ofNullable(UserUtils.getUser())
  109. // .ifPresent(user -> builder.user(user.getName()));
  110. return builder.build();
  111. }
  112. static class RequestInfo {
  113. String client;
  114. String user;
  115. String address;
  116. String uri;
  117. String clazz;
  118. String method;
  119. String params;
  120. String date;
  121. private RequestInfo(RequestInfoBuilder b) {
  122. this.client = b.client;
  123. this.user = b.user;
  124. this.address = b.address;
  125. this.uri = b.uri;
  126. this.clazz = b.clazz;
  127. this.method = b.method;
  128. this.params = b.params;
  129. this.date = b.date;
  130. }
  131. public static RequestInfoBuilder builder() {
  132. return new RequestInfoBuilder();
  133. }
  134. @Override
  135. public String toString() {
  136. return String.format(
  137. "%s %s\n==>|PARAMS:%s\n==>|IP: %s;\n==>|CLIENT: %s\n==>|USER: %s\n==>|CONTROLLER:%s\n==>|DATE: %s",
  138. method, uri, params, address, client, user, clazz, date
  139. );
  140. }
  141. public static class RequestInfoBuilder {
  142. String client;
  143. String user;
  144. String address;
  145. String uri;
  146. String clazz;
  147. String method;
  148. String params;
  149. String date;
  150. private RequestInfoBuilder() {
  151. }
  152. public RequestInfoBuilder client(String client) {
  153. this.client = client;
  154. return this;
  155. }
  156. public RequestInfoBuilder user(String user) {
  157. this.user = user;
  158. return this;
  159. }
  160. public RequestInfoBuilder address(String address) {
  161. this.address = address;
  162. return this;
  163. }
  164. public RequestInfoBuilder uri(String uri) {
  165. this.uri = uri;
  166. return this;
  167. }
  168. public RequestInfoBuilder clazz(String clazz) {
  169. this.clazz = clazz;
  170. return this;
  171. }
  172. public RequestInfoBuilder method(String method) {
  173. this.method = method;
  174. return this;
  175. }
  176. public RequestInfoBuilder params(String params) {
  177. this.params = params;
  178. return this;
  179. }
  180. public RequestInfoBuilder date(String date) {
  181. this.date = date;
  182. return this;
  183. }
  184. public RequestInfo build() {
  185. return new RequestInfo(this);
  186. }
  187. }
  188. }
  189. }
package com.alpaak.hellospringboot.utils.response;

/**
 * @ClassName: GlobalConstant
 * @Description: 全局常量定义
 */
public class GlobalConstant {

    // 返回状态
    public static final String RET_STATUS = "status";
    // 交互成功状态
    public static final int SUCCESS = 1;
    // 交互失败状态
    public static final int ERROR = 0;
    //未登陆code
    public static final int NO_LOGIN = 401;

}
package com.alpaak.hellospringboot.utils;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import java.net.InetAddress;
import java.net.NetworkInterface;
import java.net.SocketException;
import java.net.UnknownHostException;
import java.util.Enumeration;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * IPUtil
 *
 * @author alpaak  ip的通用工具类
 * @version 1.0
 * @date 2021/4/2 18:56
 */
public class IPUtil {

    private static final Logger log = LoggerFactory.getLogger(IPUtil.class);

    private final static String UNKNOWN = "unknown";


    /**
     * 检查IP是否合法
     *
     * @param ip -
     * @return boolean
     */
    public static boolean valid(String ip) {
        String regex0 = "(2[0-4]\\d)" + "|(25[0-5])";
        String regex1 = "1\\d{2}";
        String regex2 = "[1-9]\\d";
        String regex3 = "\\d";
        String regex = "(" + regex0 + ")|(" + regex1 + ")|(" + regex2 + ")|(" + regex3 + ")";
        regex = "(" + regex + ").(" + regex + ").(" + regex + ").(" + regex + ")";
        Pattern p = Pattern.compile(regex);
        Matcher m = p.matcher(ip);
        return m.matches();
    }

    /**
     * 获取本地ip 适合windows与linux
     *
     * @return String
     */
    public static String localIP() {
        try {
            Enumeration netInterfaces = NetworkInterface.getNetworkInterfaces();
            while (netInterfaces.hasMoreElements()) {
                NetworkInterface ni = (NetworkInterface) netInterfaces.nextElement();
                try {
                    InetAddress ip = ni.getInetAddresses().nextElement();
                    if (!ip.isLoopbackAddress() && ip.getHostAddress().indexOf(":") == -1) {
                        return ip.getHostAddress();
                    }
                } catch (Exception e) {
                    log.warn(e.getMessage());
                }
            }
        } catch (SocketException e) {
            log.warn(e.getMessage());
            try {
                return InetAddress.getLocalHost().getHostAddress();
            } catch (UnknownHostException e1) {
                log.warn(e1.getMessage());;
            }
        }

        return  "127.0.0.1";
    }

    /**
     * 获取客户机的ip地址
     *
     * @param request -
     * @return String
     */
    public static String getClientIp(HttpServletRequest request) {
        String ip = request.getHeader("x-forwarded-for");
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("WL-Proxy-Client-IP");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getRemoteAddr();
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("http_client_ip");
        }
        if (ip == null || ip.length() == 0 || UNKNOWN.equalsIgnoreCase(ip)) {
            ip = request.getHeader("HTTP_X_FORWARDED_FOR");
        }
        // 如果是多级代理,那么取第一个ip为客户ip
        if (ip != null && ",".indexOf(ip) != -1) {
            ip = ip.substring(ip.lastIndexOf(",") + 1).trim();
        }
        return ip;
    }


    /**
     * 把ip转化为整数
     *
     * @param ip -
     * @return long
     */
    public static long toInt(String ip) {
        String[] intArr = ip.split("\\.");
        int[] ipInt = new int[intArr.length];
        for (int i = 0; i < intArr.length; i++) {
            ipInt[i] = new Integer(intArr[i]);
        }
        return ipInt[0] * 256 * 256 * 256 + +ipInt[1] * 256 * 256 + ipInt[2] * 256 + ipInt[3];
    }

    public static void main(String[] args) {
        System.out.println(localIP());
    }

}
  1. Spring MVC提供了一种友好的方式来开发Web应用程序,可以轻松开发web

  2. Spring 和 Spring MVC最大的弊病在于重量级的配置,并且这些配置有很高的相似性。

  3. Spring Boot期望通过结合自动配置和启动器来解决模板化问题

  4. Spring Boot提供了丰富的第三方集成,简化开发体验

    2 SpringMVC

    SpringBoot项目并不是替换Spring、SpringMVC,而是使他们用起来更加简单
    springmvcv.png

    1 dao层,service层,controller层

如果去饭店吃饭,不可能找厨师点菜吧?小工负责食材存取,洗菜切肉;厨师负责烹饪;跑堂负责接待食客并与后厨沟通。这就是精典的分层体现。
如果一个厨师既负责跑堂,又负责烹饪。那这个饭店的管理一定非常混乱吧。小工就是DAO,从食材库里(数据源)取出食材(原始数据),进行简单处理(数据对象化)。厨师就是Service,找到小工(DAO),获取各种半成品(对象化数据),加工成顾客需要的菜肴(最终数据)。跑堂就是Controller,负责接单(提交数据)上菜(响应数据),是顾客与后厨间的媒介(提供用户与后台程序的接口)。各司其职(高内聚),轻松协作(低耦合),就是分层思想的目标。

1.1 dao层

与数据库打交道,提供原始数据,我们使用mybatis-plus + spring解决

1.2 service 层

承上启下,上接controller下接dao,spring解决

1.3 controller层

接收请求,反馈结果,springmvc解决

2 视图层,又可以称为前端,现在前后分离,后端提供数据,前端进行展示

前端可以是web页面、手机端、平板电脑端