这几天在做一个功能,具体的情况是这样的:

  项目中原有的几个功能模块中有数据上报的功能,现在需要在这几个功能模块的上报之后生成一条消息记录,然后入库,在写个接口供前台来拉取消息记录。

  看到这个需求,首先想到的是使用AOP来实现了,然后,我去看了下现有功能模块中的代码,发现了问题,这些模块中的业务逻辑并没有放在service层来处理,直接在controller中处理了,controller中包含了两个甚至多个service处理,这样是不能保证事务安全的,既然这样,那么我们如何实现能保证事务安全呢。我想直接在controller中定义切入点,然后before中手动开启事务,在afterReturn之后根据需要来提交或者回滚事务。

  然后趁着这个机会就查了下spring boot中的事务这块,就从最基础的说起。

  1.spring boot中声明式事务的使用

  想要在spring boot中使用声明式事务,有两种方式,一种是在各个service层中添加注解,还有一种是使用AOP配置全局的声明式事务管理

  先来说第一种,需要用到两个注解就,一个是@EnableTransactionManagement用来开启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />,另一个是@Transactional

  具体代码如下:

  1. package com.example.demo;
  2.  
  3. import org.springframework.boot.SpringApplication;
  4. import org.springframework.boot.autoconfigure.SpringBootApplication;
  5. import org.springframework.transaction.annotation.EnableTransactionManagement;
  6.  
  7. // @SpringBootApplication是Sprnig Boot项目的核心注解,主要目的是开启自动配置
  8. @SpringBootApplication
  9. @EnableTransactionManagement // 启注解事务管理,等同于xml配置方式的 <tx:annotation-driven />
  10. public class DemoApplication {
  11. public static void main(String[] args) {
  12. SpringApplication.run(DemoApplication.class, args);
  13. }
  14.  
  15. }

  然后,注解@Transactional直接加在service层就可以了,放两个service用来验证事务是否按预期回滚

  1. package com.example.demo.service;
  2.  
  3. import com.example.demo.bean.ResUser;
  4. import org.springframework.transaction.annotation.Transactional;
  5. import java.util.List;
  6.  
  7. /**
  8. * 注解加在接口上表名接口的所有方法都支持事务;
  9. * 如果加在方法上,则只有该方法支持事务
  10. * 可以根据需要在CUD操作上加注解
  11. **/
  12. @Transactional
  13. public interface IUserService {
  14.  
  15. int insertUser(ResUser resUser);
  16.  
  17. int updateUser(ResUser resUser);
  18.  
  19. List<ResUser> getResUserList();
  20.  
  21. }
  1. package com.example.demo.service;
  2.  
  3. import com.example.demo.bean.ResPartner;
  4. import org.springframework.transaction.annotation.Transactional;
  5.  
  6. import java.util.List;
  7. import java.util.Map;
  8.  
  9. @Transactional
  10. public interface IPartnerService {
  11.  
  12. int add(ResPartner resPartner);
  13.  
  14. int deleteByIds(String ids);
  15.  
  16. int update(ResPartner resPartner);
  17.  
  18. ResPartner queryById(int id);
  19.  
  20. List<ResPartner> queryList(Map<String, Object> params);
  21.  
  22. }

  实现类

  1. package com.example.demo.service.impl;
  2.  
  3. import com.example.demo.bean.ResPartner;
  4. import com.example.demo.dao.PartnerMapperXml;
  5. import com.example.demo.service.IPartnerService;
  6. import org.slf4j.Logger;
  7. import org.slf4j.LoggerFactory;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10.  
  11. import java.util.List;
  12. import java.util.Map;
  13.  
  14. @Component("partnerService")
  15. public class PartnerServiceImpl implements IPartnerService {
  16.  
  17. private Logger logger = LoggerFactory.getLogger(this.getClass());
  18. @Autowired
  19. private PartnerMapperXml partnerMapperXml;
  20.  
  21. @Override
  22. public int add(ResPartner resPartner) {
  23. StringBuilder sbStr = new StringBuilder();
  24. sbStr.append("id = ").append(resPartner.getId())
  25. .append(", name = ").append(resPartner.getName())
  26. .append(", city = ").append(resPartner.getCity())
  27. .append(", displayName = ").append(resPartner.getDisplayName());
  28. this.logger.info(sbStr.toString());
  29. return this.partnerMapperXml.add(resPartner);
  30. }
  31. }
  1. package com.example.demo.service.impl;
  2.  
  3. import com.example.demo.bean.ResPartner;
  4. import com.example.demo.bean.ResUser;
  5. import com.example.demo.dao.PartnerMapperXml;
  6. import com.example.demo.dao.ResUserMapper;
  7. import com.example.demo.service.IUserService;
  8. import org.springframework.beans.factory.annotation.Autowired;
  9. import org.springframework.stereotype.Component;
  10.  
  11. import java.util.List;
  12.  
  13. @Component("userService")
  14. public class UserServiceImpl implements IUserService {
  15.  
  16. @Autowired
  17. private ResUserMapper resUserMapper;
  18. @Autowired
  19. private PartnerMapperXml partnerMapperXml;
  20.  
  21. @Override
  22. public int insertUser(ResUser resUser) {
  23.  
  24. int i = resUserMapper.insert(resUser);
  25. // ResPartner partner = new ResPartner();
  26. // partner.setId(resUser.getId());
  27. // partner.setName(resUser.getName());
  28. // partner.setDisplayName(resUser.getLogin());
  29. //
  30. // if (true) // 用来验证异常,使事务回滚
  31. // throw new RuntimeException("xxxxxxxxxxxxxxx");
  32. // int a = 1/0;
  33. // partnerMapperXml.add(partner);
  34.  
  35. return i;
  36. }
  37.  
  38. }

  controller代码,JSONMsg是一个自定义类,就三个属性code,msg,data用来给前台返回数据。

  1. package com.example.demo.controllers;
  2.  
  3. import com.alibaba.fastjson.JSONObject;
  4. import com.example.demo.bean.JSONMsg;
  5. import com.example.demo.bean.ResPartner;
  6. import com.example.demo.bean.ResUser;
  7. import com.example.demo.service.IPartnerService;
  8. import com.example.demo.service.IUserService;
  9. import org.springframework.beans.factory.annotation.Autowired;
  10. import org.springframework.jdbc.datasource.DataSourceTransactionManager;
  11. import org.springframework.transaction.PlatformTransactionManager;
  12. import org.springframework.transaction.TransactionDefinition;
  13. import org.springframework.transaction.TransactionStatus;
  14. import org.springframework.web.bind.annotation.*;
  15.  
  16. import java.util.List;
  17.  
  18. @RestController
  19. @RequestMapping("/users")
  20. public class UserController {
  21.  
  22. @Autowired
  23. private IUserService userService;
  24. @Autowired
  25. private IPartnerService partnerService;
  26. @Autowired
  27. private PlatformTransactionManager platformTransactionManager;
  28. @Autowired
  29. private TransactionDefinition transactionDefinition;
  30.  
  31. @RequestMapping(value = "/insert", method = RequestMethod.POST)
  32. @ResponseBody
  33. public JSONMsg insertUser(@RequestBody String data){
  34.  
  35. JSONMsg jsonMsg = new JSONMsg();
  36. jsonMsg.setCode(400);
  37. jsonMsg.setMsg("error");
  38. System.out.println(data);
  39. JSONObject jsonObject = JSONObject.parseObject(data);
  40. if (!jsonObject.containsKey("data")){
  41. return jsonMsg;
  42. }
  43.  
  44. ResUser user = JSONObject.parseObject(jsonObject.get("data").toString(), ResUser.class);
  45. int i = userService.insertUser(user);
  46.  
  47. System.out.println(i);
  48. if (i!=0){
  49. jsonMsg.setCode(200);
  50. jsonMsg.setMsg("成功");
  51. jsonMsg.setData(user);
  52. }
  53.  
  54. return jsonMsg;
  55. }
  56.  
  57. // 该方法中的代码用来验证手动控制事务时使用
  58. // @RequestMapping(value = "/insert", method = RequestMethod.POST)
  59. // @ResponseBody
  60. // public JSONMsg insertUser(@RequestBody String data){
  61. // TransactionStatus transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
  62. //
  63. // System.out.println(transactionStatus.isCompleted());
  64. // System.out.println(transactionStatus.isRollbackOnly());
  65. //
  66. //
  67. // JSONMsg jsonMsg = new JSONMsg();
  68. // jsonMsg.setCode(400);
  69. // jsonMsg.setMsg("error");
  70. // System.out.println(data);
  71. // try{
  72. // JSONObject jsonObject = JSONObject.parseObject(data);
  73. // if (!jsonObject.containsKey("data")){
  74. // return jsonMsg;
  75. // }
  76. //
  77. // ResUser user = JSONObject.parseObject(jsonObject.get("data").toString(), ResUser.class);
  78. // int i = userService.insertUser(user);
  79. //
  80. // i= 1/0;
  81. //
  82. // ResPartner partner = new ResPartner();
  83. // partner.setId(user.getId());
  84. // partner.setName(user.getName());
  85. // partner.setDisplayName(user.getLogin());
  86. // partnerService.add(partner);
  87. //
  88. // if (i!=0){
  89. // jsonMsg.setCode(200);
  90. // jsonMsg.setMsg("成功");
  91. // jsonMsg.setData(user);
  92. // }
  93. //
  94. // platformTransactionManager.commit(transactionStatus);
  95. // System.out.println("提交事务");
  96. // }catch (Exception e){
  97. // e.printStackTrace();
  98. // platformTransactionManager.rollback(transactionStatus);
  99. // System.out.println("回滚事务");
  100. // }finally {
  101. //
  102. // }
  103. // return jsonMsg;
  104. // }
  105. }

  接下来说下spring boot中配置全局的声明式事务,定义一个configure类,具体代码如下

  1. package com.example.demo.configs;
  2.  
  3. import org.aspectj.lang.annotation.Aspect;
  4. import org.springframework.aop.Advisor;
  5. import org.springframework.aop.aspectj.AspectJExpressionPointcut;
  6. import org.springframework.aop.support.DefaultPointcutAdvisor;
  7. import org.springframework.beans.factory.annotation.Autowired;
  8. import org.springframework.context.annotation.Bean;
  9. import org.springframework.context.annotation.Configuration;
  10. import org.springframework.transaction.PlatformTransactionManager;
  11. import org.springframework.transaction.TransactionDefinition;
  12. import org.springframework.transaction.interceptor.DefaultTransactionAttribute;
  13. import org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource;
  14. import org.springframework.transaction.interceptor.TransactionInterceptor;
  15.  
  16. /**
  17. * @ClassName: GlobalTransactionAdviceConfig
  18. * @Description: AOP全局事务管理配置
  19. *
  20. * 声明式事务说明:
  21. * 1.如果将业务逻辑放到service层面来处理,则能够保证事务安全,即便使用了AOP来切入service方法也能保证事务安全;
  22. * 2.如果多个service在controller层做业务逻辑(本身就是错误的),则不能保证事务安全。
  23. * 对于2中的情况,应该尽量避免,因为本身就是错误的;
  24. * 这种情况在面向切面编程中也有可能碰到,如,因为必要切入点为controller(应尽量避免,原则应切service),切面程序跟controller业务逻辑不同,
  25. * service不同,会导致事务混乱;
  26. *
  27. * 如果出现上述情况,则可以使用编程式事务管理(也就是手动控制事务)
  28. * 在controller逻辑开始之前手动开启/获取事务,然后在controller逻辑结束后再根据需要提交或者回滚事务;
  29. * 在AOP中也是如此,在before中手动开启/获取事务(这一步是必须的),在after中处理切面逻辑,然后根据需要提交或者回滚事务,如果由于异常需要回滚事务,记得修改返回信息
  30. *
  31. * @Author:
  32. * @Date: 2019-08-01
  33. * @Version: V2.0
  34. **/
  35.  
  36. @Aspect
  37. @Configuration
  38. public class GlobalTransactionAdviceConfig {
  39. private static final String AOP_POINTCUT_EXPRESSION = "execution (* com.example.demo.service..*.*(..))";
  40.  
  41. @Autowired
  42. private PlatformTransactionManager transactionManager;
  43.  
  44. @Bean
  45. public TransactionInterceptor txAdvice() {
  46. DefaultTransactionAttribute txAttr_REQUIRED = new DefaultTransactionAttribute();
  47. txAttr_REQUIRED.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  48.  
  49. DefaultTransactionAttribute txAttr_REQUIRED_READONLY = new DefaultTransactionAttribute();
  50. txAttr_REQUIRED_READONLY.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
  51. txAttr_REQUIRED_READONLY.setReadOnly(true);
  52.  
  53. NameMatchTransactionAttributeSource source = new NameMatchTransactionAttributeSource();
  54. source.addTransactionalMethod("add*", txAttr_REQUIRED);
  55. source.addTransactionalMethod("insert*", txAttr_REQUIRED);
  56. source.addTransactionalMethod("save*", txAttr_REQUIRED);
  57. source.addTransactionalMethod("create*", txAttr_REQUIRED);
  58. source.addTransactionalMethod("delete*", txAttr_REQUIRED);
  59. source.addTransactionalMethod("update*", txAttr_REQUIRED);
  60. source.addTransactionalMethod("exec*", txAttr_REQUIRED);
  61. source.addTransactionalMethod("set*", txAttr_REQUIRED);
  62. source.addTransactionalMethod("get*", txAttr_REQUIRED_READONLY);
  63. source.addTransactionalMethod("query*", txAttr_REQUIRED_READONLY);
  64. source.addTransactionalMethod("find*", txAttr_REQUIRED_READONLY);
  65. source.addTransactionalMethod("list*", txAttr_REQUIRED_READONLY);
  66. source.addTransactionalMethod("count*", txAttr_REQUIRED_READONLY);
  67. source.addTransactionalMethod("is*", txAttr_REQUIRED_READONLY);
  68. source.addTransactionalMethod("select*", txAttr_REQUIRED_READONLY);
  69. return new TransactionInterceptor(transactionManager, source);
  70. }
  71.  
  72. @Bean
  73. public Advisor txAdviceAdvisor() {
  74. AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
  75. pointcut.setExpression(AOP_POINTCUT_EXPRESSION);
  76. return new DefaultPointcutAdvisor(pointcut, txAdvice());
  77. }
  78. }

  添加这个类,根据知己需要修改切入点,然后放到能被spring boot扫描到的包下即可,如果出现事务失败的情况,请查看下addTransactionalMethod是否配置正确,我当初就是用的insert*,而没有添加导致失败。

  2.切入点为controller时,如何使用编程式事务管理控制事务

  

  1. package com.example.demo.configs;
  2.  
  3. import com.example.demo.bean.JSONMsg;
  4. import com.example.demo.bean.ResPartner;
  5. import com.example.demo.bean.ResUser;
  6. import com.example.demo.service.IPartnerService;
  7. import org.aspectj.lang.JoinPoint;
  8. import org.aspectj.lang.annotation.AfterReturning;
  9. import org.aspectj.lang.annotation.Aspect;
  10. import org.aspectj.lang.annotation.Before;
  11. import org.aspectj.lang.annotation.Pointcut;
  12. import org.springframework.beans.factory.annotation.Autowired;
  13. import org.springframework.stereotype.Component;
  14. import org.springframework.transaction.PlatformTransactionManager;
  15. import org.springframework.transaction.TransactionDefinition;
  16. import org.springframework.transaction.TransactionStatus;
  17. import org.springframework.transaction.annotation.Transactional;
  18.  
  19. @Component
  20. @Aspect
  21. public class ResUserAspect {
  22.  
  23. @Autowired
  24. private IPartnerService partnerService;
  25. @Autowired
  26. private PlatformTransactionManager platformTransactionManager;
  27. @Autowired
  28. private TransactionDefinition transactionDefinition;
  29.  
  30. private TransactionStatus transactionStatus;
  31.  
  32. @Pointcut("execution(public * com.example.demo.controllers.UserController.insertUser(..))")
  33. // @Pointcut("execution(public * com.example.demo.service.IUserService.insertUser(..))") // 验证切入点为service时,AOP编程中的事务问题
  34. private void insertUser(){}
  35.  
  36. @Before(value = "insertUser()")
  37. public void before(){
  38. //在切入点程序执行之前手动开启事务 - 必须的操作
  39. transactionStatus = platformTransactionManager.getTransaction(transactionDefinition);
  40. }
  41.     // 验证切入点为service时,AOP编程中的事务问题
  42. // @AfterReturning(pointcut = "insertUser()", returning = "result")
  43. // public void afterReturning(JoinPoint joinPoint, Object result){
  44. //
  45. // Object[] args = joinPoint.getArgs();
  46. // System.out.println(args[0]);
  47. // if (((Integer)result) != 0){
  48. // ResPartner partner = new ResPartner();
  49. // ResUser user = (ResUser) args[0];
  50. // partner.setId(user.getId());
  51. // partner.setName(user.getName());
  52. // partner.setDisplayName(user.getLogin());
  53. //
  54. // int a = 1/0;
  55. // int i = partnerService.add(partner);
  56. //
  57. // System.out.println(i);
  58. // }
  59. // }
  60.    //切入点为controller时的事务验证
  61. @Transactional
  62. @AfterReturning(pointcut = "insertUser()", returning = "result")
  63. public void afterReturning(JoinPoint joinPoint, Object result){
  64.  
  65. if (!(result instanceof JSONMsg)){
  66. System.out.println(result.getClass());
  67. return;
  68. }
  69. JSONMsg jsonMsg = (JSONMsg) result;
  70. try{
  71.  
  72. if (jsonMsg.getCode() == 200){
  73. ResPartner partner = new ResPartner();
  74. ResUser user = (ResUser) jsonMsg.getData();
  75. partner.setId(user.getId());
  76. partner.setName(user.getName());
  77. partner.setDisplayName(user.getLogin());
  78.  
  79. int a = 1/0;
  80. int i = partnerService.add(partner);
  81.  
  82. System.out.println(i);
  83. }
  84.  
  85. platformTransactionManager.commit(transactionStatus); // 手动提交事务
  86. System.out.println("提交事务");
  87. }catch (Exception e){
  88. platformTransactionManager.rollback(transactionStatus); // 出现异常,回滚事务
  89. System.out.println("回滚事务");
  90. System.out.println(e.getMessage());
  91.  
  92. //修改返回数据
  93. jsonMsg.setCode(400);
  94. jsonMsg.setMsg(e.getMessage());
  95. }
  96.  
  97. }
  98. }

  用到的实体bean

  1. // ResUser.java中的属性
  2. private Integer id;
  3. private String login;
  4. private String name;
  5. private Integer age;
  6.  
  7. // ResPartner.java中的属性
  8. private int id;
  9. private String name;
  10. private String city;
  11. private String displayName;

  最后总结我写在了GlobalTransactionAdviceConfig 类中,也就是如下

  1. * 声明式事务说明:
    * 1.如果将业务逻辑放到service层面来处理,则能够保证事务安全,即便使用了AOP来切入service方法也能保证事务安全;
    * 2.如果多个servicecontroller层做业务逻辑(本身就是错误的),则不能保证事务安全。
    * 对于2中的情况,应该尽量避免;
    * 这种情况在面向切面编程中也有可能碰到,如,因为必要切入点为controller(应尽量避免,原则应切service),切面程序跟controller业务逻辑不同,
    * service不同,会导致事务混乱;
    *
    * 如果出现上述情况,则可以使用编程式事务管理(也就是手动控制事务)
    * controller逻辑开始之前手动开启/获取事务,然后在controller逻辑结束后再根据需要提交或者回滚事务;
    * AOP中也是如此,在before中手动开启/获取事务(这一步是必须的),在after中处理切面逻辑,然后根据需要提交或者回滚事务,如果由于异常需要回滚事务,记得修改返回信息

  

  注:

    有时候项目中使用了分布式框架,比如dubbo,则可能存在service层跟controller层分布式部署的问题,这会导致这种方式在controller中获取不到transactionManager,后续有时间在来看下分布式中的事务处理问题。

参考链接:

  Spring Boot 之 事务(声明式、编程式、自定义事务管理器、@EnableAspectJAutoProxy 同类方法调用)

  

  

  

  

spring boot中的声明式事务管理及编程式事务管理的更多相关文章

  1. Spring学习8-Spring事务管理(编程式事务管理)

    一.Spring事务的相关知识   1.事务是指一系列独立的操作,但在概念上具有原子性. 比如转账:A账号-100, B账号+100,完成.这两个操作独立是没问题的. 但在逻辑上,要么全部完成,要么一 ...

  2. 事务之三:编程式事务、声明式事务(XML配置事务、注解实现事务)

    Spring2.0框架的事务处理有两大类: JdbcTemplate操作采用的是JDBC默认的AutoCommit模式,也就是说我们还无法保证数据操作的原子性(要么全部生效,要么全部无效),如: Jd ...

  3. Spring事务管理之编程式事务管理

    © 版权声明:本文为博主原创文章,转载请注明出处 案例:利用Spring的编程式事务管理模拟转账过程 数据库准备 -- 创建表 CREATE TABLE `account`( `id` INT NOT ...

  4. 阶段3 2.Spring_10.Spring中事务控制_10spring编程式事务控制2-了解

    在业务层声明 transactionTemplate 并且声称一个set方法等着spring来注入 在需要事物控制的地方执行 execute.但是这个execute需要一个参数 需要的参数是Trans ...

  5. Spring事务管理的实现方式:编程式事务与声明式事务

    1.上篇文章讲解了Spring事务的传播级别与隔离级别,以及分布式事务的简单配置,点击回看上篇文章 2.编程式事务:编码方式实现事务管理(代码演示为JDBC事务管理) Spring实现编程式事务,依赖 ...

  6. Spring事务管理的实现方式之编程式事务与声明式事务详解

    原创说明:本博文为原创作品,绝非他处转载,转载请联系博主 1.上篇文章讲解了Spring事务的传播级别与隔离级别,以及分布式事务的简单配置,点击回看上篇文章 2.编程式事务:编码方式实现事务管理(代码 ...

  7. Spring事务:一种编程式事务,三种声明式事务

    事务隔离级别 隔离级别是指若干个并发的事务之间的隔离程度.TransactionDefinition 接口中定义了五个表示隔离级别的常量: TransactionDefinition.ISOLATIO ...

  8. 八、Spring之深入理解声明式事务

    Spring之深入理解声明式事务 何为事务? 事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用. 事务的四个属性: 1.原子性(atomicity) 事务是原子性操 ...

  9. spring的声明式事务和编程式事务

    事务管理对于企业应用来说是至关重要的,当出现异常情况时,它可以保证数据的一致性. Spring事务管理的两种方式 1.编程式事务 使用Transaction Ttempleate或者直接使用底层的Pl ...

随机推荐

  1. Java NIO 学习笔记(一)----概述,Channel/Buffer

    目录: Java NIO 学习笔记(一)----概述,Channel/Buffer Java NIO 学习笔记(二)----聚集和分散,通道到通道 Java NIO 学习笔记(三)----Select ...

  2. appcan中evaluateScript、evaluatePopoverScript的使用

    1. 如果要在某个主窗体中执行JS,使用 appcan.window.evaluateScript(name,scriptContent) eg: appcan.window.evaluateScri ...

  3. 3D echarts 点位报表展示

    一,准备工作1)获取Echarts 以下为官网推荐的几种获取 ECharts方式: 从官网下载界面选择你需要的版本下载,根据开发者功能和体积上的需求,我们提供了不同打包的下载,如果你在体积上没有要求, ...

  4. docker 获取镜像

    之前提到过,Docker Hub 上有大量的高质量的镜像可以用,这里我们就说一下怎么获取这些镜像. 从 Docker 镜像仓库获取镜像的命令是 docker pull.其命令格式为: docker p ...

  5. zabbix报警信息设置

    报警信息 默认标题: 服务器监控报警 告警主机:{HOST.NAME} 主机IP: {HOST.IP} 告警级别:{TRIGGER.SEVERITY} 告警信息:{TRIGGER.NAME} 问题详情 ...

  6. cur.execute(sql,args)和cur.execute(sql)的区别

    轉:https://blog.csdn.net/mjjyszazc/article/details/88932664 方式一: userid = “123”sql = “select id,name ...

  7. 【无线安全实践入门】网络扫描和ARP欺骗

    文中可能存在错误操作或错误理解,望大家不吝指正. 同时也希望可以帮助到想要学习接触此方面.或兴趣使然的你,让你有个大概的印象. !阅前须知! 本文是基于我几年前的一本笔记本,上面记录了我学习网络基础时 ...

  8. SpringMVC——MVC

    一.了解MVC mvc这种设计模式,分为三个基本部分:模型(Model).视图(View)和控制器(Controller),不光运用于Web领域,而且也能用于非Web领域:可以特指一种表现层设计模式, ...

  9. Android 开发你需要了解的那些事

    本文微信公众号「AndroidTraveler」首发. 背景 最近部门有新入职员工,作为规划技术路线的导师,这边给新员工安排了学习路线. 除了基本的学习路线之外,每次沟通,我都留了一个小问题,让小伙伴 ...

  10. Golang 高效实践之并发实践

    前言 在我前面一篇文章Golang受欢迎的原因中已经提到,Golang是在语言层面(runtime)就支持了并发模型.那么作为编程人员,我们在实践Golang的并发编程时,又有什么需要注意的点呢?下面 ...