关于spring:IDEA命令行缩短器助你解决此问题Command-line-is-too-long

生命太短暂,不要去做一些基本没有人想要的货色。本文已被 https://www.yourbatman.cn 收录,外面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以收费学习。关注公众号【BAT的乌托邦】一一击破,深刻把握,回绝浅尝辄止。 前言各位小伙伴大家好,我是A哥。最近遇到两个问题,都是对于IDEA的(话中有话和代码无关),很是让我“怄气”呀(要害是浪费时间)。在痛定思痛后,我决定写此专栏,来专门分享/记录应用IntelliJ IDEA过程中遇到的那些奇葩问题和解决方案,以帮忙你缩短日常排错工夫,这么一思考如同还功德无量呢????。 IntelliJ IDEA作为Java开发者中最为风行的开发工具(eclipse粉勿喷),熟练掌握它(包含排雷)对晋升编码效率能有显著晋升。但工具毕竟是工具,这么长时间应用IDEA以来,每个人或多或少的都遇到过对于IDEA七七八八、奇奇怪怪的问题,这些与代码舞棍,但它很容易偷走你的工夫,半天又更或者是一天之久。 阐明:千万不要漠视对IDEA的钻研,因为把它玩纯熟了它就相当于你的物理外挂本专栏内容并非 IDEA教程,而是着眼于分享IDEA应用过程中,那些我遇到(或者大家遇到)的但又不是可能很疾速解决,总之就是比拟辣手的问题的汇总,有一种错题本的意思有木有。总之就是心愿它可能帮忙到大家迅速定位or解决问题,防止不必要的工夫节约,毕竟咱们的主业还是敲代码嘛~ 版本约定本文内容若没做非凡阐明,均基于以下版本: IntelliJ IDEA:2020.1.2旗舰版注释应用IDEA这么久,尽管之前时不时地的跟IDEA问题“交过手”,但真正促使我决定写此专栏的起因还是源自于前两天应用IDEA启动Spring Boot程序时的这个报错: Error running 'Application': Command line is too long. Shorten command line for Application or also for Spring Boot default configuration.说实话这个谬误我前所未见,看起来还蛮有意思,因而决定钻研一番。这不,把钻研后果分享给大家,信息共享。 为了解释好这个问题,咱们得先来做些功课,通晓写概念。 控制台首行门路在IDEA里,你每次启动一个main函数时,控制台第一行输入的“日志”称作为:控制台首行门路。这里,我运行一个最最最简略的程序,看看它长啥样,程序如下: public class Application { public static void main(String[] args) { System.out.println("Hello world"); }}运行程序,控制台输入如下截图:置信小伙伴每天都能看见它但大概率不会留神到它,我也不例外。你想不到的是,凑巧这行“日志”就成为了本文明天的配角,会围绕它来展论述。 特地阐明:如果你是用外置tomcat驱动利用启动的话成果不是这样子的。因为它应用的是tomcat的脚本来启动,所以首行日志形如这样:D:\developer\apache-tomcat-9.0.34\bin\catalina.bat run首行门路内容晓得了什么叫首行门路,那么它的内容才是咱们要关怀的。如上截图中,仔细的你会发现最初是...省略号,因而内容绝不止你当初看到的那么简略。你能够鼠标点击一下,开展全部内容,截图如下:这一行切实太长了,无奈横向截图全副展现进去,因而我把它复制进去放在文本编辑器中查看:这个截图是一行哦(只是我在文本编辑器了主动折行了而已),仍旧不能看到全部内容,因为字数真的太多了,总字数统计如下:仅仅一行,字数超过26000个。咋舌吧:第一行控制台“日志”居然输入了超过2.6w个字符。从内容构造上来看,这是一个command命令:调用java.exe程序启动一个java过程的命令。 为何启动抛错Command line is too long99.99%的状况下,你能够在IDEA里失常启动你的利用,即便首行门路很长很长。然而直到当我启动我的这个Spring Boot利用时,弹出红色提醒:间接禁止了我的running运行。提醒内容中文释义为:运行“Application”时出错:命令行太长。缩短应用程序或Spring Boot默认配置的命令行。我置信如果你也是第一次见到此case,表情和我一样是这样的:main办法都启不动了,那还得了。遇到这种状况,我只能应用百度大法(谷歌大法)了:一看能搜出这么多后果,我也就不慌了,依照“教程”很容易的把问题解决了。另外呢,通过此次搜寻到的后果聊两句题外话: 尽管Result Count不少,然而我发现本质上内容简直一毛一样,真乃天下文章一大抄访问量并不代表文章品质高,只是它刚好命中了关键字而已,比方题目党我得出如此感悟,也是促使我写本文的起因之一。因为A哥的文章一贯如此,是有些B格的。接下来以点带面,把这部分内容帮大家开展开展,解决问题并非最终目标,而是为了:记得牢,能装x,所有为了加薪。 起因剖析呈现此问题的间接起因是:IDEA集成开发环境运行你的“源码”的时候(留神是源码根底上运行,并非打好的jar包哦),是通过命令(首行那个十分十分长的)来启动Java过程的。这个命令次要蕴含两大部分: vm/程序参数。也就是你看到的那些-XX -D等参数,这部分实践上能够有限长但实际上个别不会太长-classpath参数,它用于指定运行时jar包门路(因为jar包实践上是能够在任何中央的),这部分可能性就多了要害就在于-classpath参数,它能够十分长,你依赖的jar包越多此门路就越长;你的base基门路越长它就越长;假使你还要做简单的Junit单元测试,那退出的jar包就更多长度可能就越长喽。总的来说:此part是很有可能超长从而导致Command line is too long景象的。 ...

July 18, 2020 · 1 min · jiezi

关于spring:架构师最常使用的5种架构模式及其适用场景分析

好莱坞电影中有多少情节?一些电影评论家说只有五个。您能够采纳几种架构来实现应用程序?目前大多数程序都应用上面提到的五种架构之一。 在本文中,我将五种软件架构模式的优缺点以及适宜场景提炼进去作为疾速参考。你能够在单个零碎中应用多个架构模式,它们的组合既是计算机科学,也是一门艺术。 一、分层架构这种办法可能是最常见的办法,因为它通常围绕数据库构建,并且业务中的许多应用程序天然会偏向于将信息存储在RDBMS的表中。许多比拟大的软件框架(例如Java EE,Drupal和Express)都是在这种架构下实现的,因而应用它们构建的许多应用程序天然都来自分层体系结构。 Model-View-Controller(MVC)分层构造是大多数风行的Web框架提供的规范软件开发办法,显然是分层体系结构。数据长久层上方是服务层,它通常蕴含业务逻辑和无关数据库中数据类型的信息。视图层位于顶层,通常是CSS,JavaScript和带有动静嵌入式代码的HTML。在两头有一个管制层,该管制层具备用于转换在视图和模型之间挪动的数据的各种规定和办法。 分层架构的长处:每个层能够只集中于本人的性能实现。这使得应用程序: 容易保护容易单元测试易于调配独自的“角色”易于更新和扩大适当的分层体系结构将开发层面进行隔离,这些层不受其余层的更改的影响,从而使重构更加容易。划分工作并定义独自的层是架构师面临的挑战。当需要很好地适应了模式时,这些层将易于解耦或分层开发。 适宜: 须要疾速构建的新应用程序传统IT部门和流程的企业或业务应用程序具备尚不理解其余架构的经验不足的开发人员的团队须要严格的可维护性和可测试性规范的利用二、事件驱动架构事件驱动的体系架构依据数据生成一个“事件”,事件由“消息中间件”或“事件散发治理的地方单元”对立接管,并将事件调配特定类型的代码解决。 应用JavaScript编程网页波及编写对诸如鼠标单击或击键之类的事件做出反馈的小模块。浏览器自身会协调所有输出,并确保只有正确的代码能力失去正确的事件。浏览器中常见许多不同类型的事件,然而模块仅与相干的事件进行交互。这与分层体系结构十分不同,在分层体系结构中,所有数据通常都将穿过所有层。总体而言,事件驱动的体系结构: 容易适应简单,凌乱的业务环境当呈现新的事件类型时,很容易扩大注意事项: 如果模块之间能够相互影响,则[测试可能会很简单当模块产生故障时,地方单元(或消息中间件)必须有一个事件备份打算。消息传递开销可能会升高处理速度,消息中间件必须缓冲以突发模式达到的音讯时。当事件有十分不同的需要时,为事件开发数据结构可能会很简单。保护基于事务的一致性机制很艰难,因为接管事件的模块是解耦和独立的。适宜: 具备异步数据流的异步零碎各个数据块仅与多模块中的多数模块交互的应用程序用户界面三、微内核-多插件架构许多的应用程序都具备一组外围代码,这些代码在不同的模块下重复应用。例如,开发工具Eclipse将关上文件,批注,编辑文件并启动后盾处理器。用于显示文件和对其进行编辑的代码是微内核的一部分。其余的插件扩大了Eclipse,从而扩大了其性能。 具体到解决方案就是将一些根本的外围的工作代码推入微内核。而后,不同的业务部门能够依据不同类型的申明编写插件。 注意事项: 确定哪些代码是微内核中的内容通常是一门艺术。它应该保留常常被应用的代码。一旦许多插件依赖微内核,批改微内核可能十分艰难,甚至不可能。惟一的解决方案就是批改插件。为内核函数抉择正确的粒度很难当时实现,也简直不可能在前期进行更改。适宜: 工具类软件在外围代码与边缘代码之间有清晰辨别的应用程序具备一组固定的外围函数和一组动静规定的应用程序四、微服务架构小宝宝既可恶又乏味,然而一旦变大,就很难操纵并且难以保护。微服务架构旨在帮忙开发人员防止让本人的宝宝长大,蠢笨,生硬,烦人。它的指标不是创立一个大型程序,而是创立多个不同的小型程序。防止批改一个小bug,就须要重新部署整个大型利用的状况呈现。 这种办法相似于事件驱动和微内核办法,然而次要用于解耦不同模块及工作。在许多状况下,不同的工作可能须要不同的处理量,并且用处可能会有所不同。所以微服务的特点是便于批改、便于扩大。应用负载平衡及服务发现的机制,在用户应用高峰期部署更多的微服务,保障服务的高可用;在用户低频服务时段缩减微服务,从而节俭服务器资源。 注意事项: 并非所有应用程序都能够拆分为绝对独立的微服务单元。当工作扩散在不同的微服务之间时,通信老本会更大。单个申请的响应时长会减少。适宜: 疾速倒退新业务团队大型Web应用程序五、高速缓存架构许多网站都是围绕数据库构建的,只有数据库可能满足负载,它们就能够失常运行。然而当使用量达到高峰,并且数据库无奈跟上用户申请的速度时,整个网站就会瘫痪。将数据存储在内存中能够使许多工作更快,从而大幅度提高用户并发拜访的撑持能力。 注意事项: 对于内存数据库,事务的反对更加艰难。开发业余的高速缓存数据的程序,对程序员的技术水平往往要求更高一些(至多比只会写增删改查的程序员要高)适宜: 高频点击数据流和用户日志之类的大量数据处理低价值数据,有时可能会失落而不会造成重大结果(比方用户访问量数据)读多写少的数据。比方新闻数据,写完之后简直不改,然而有很多的人看。欢送关注我的博客,外面有很多精品合集本文转载注明出处(必须带连贯,不能只转文字):字母哥博客。感觉对您有帮忙的话,帮我点赞、分享!您的反对是我不竭的创作能源! 。另外,笔者最近一段时间输入了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》《Spring Security-JWT-OAuth2一本通》《实战前后端拆散RBAC权限管理系统》《实战SpringCloud微服务从青铜到王者》《VUE深入浅出系列》

July 18, 2020 · 1 min · jiezi

关于spring:Spring-方法注入

实现办法/手动模式Java 注解配置元信息 @Autowired@Resource@Inject(可选)@Bean@Autowired 只依照byType 注入,由Spring提供@Resource 默认按byName主动注入,也提供依照byType 注入,由JDK提供// @Inject 须要依赖<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version></dependency>实体类Holder.java package com.rumenz;public class Holder { private Rumenz rumenz; public Holder(Rumenz rumenz) { this.rumenz = rumenz; } public Rumenz getRumenz() { return rumenz; } public void setRumenz(Rumenz rumenz) { this.rumenz = rumenz; }}实体类Rumenz.java package com.rumenz;public class Rumenz { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}配置文件Beans.xml ...

July 17, 2020 · 1 min · jiezi

关于spring:Spring字段注入

实现办法/手动模式Java 注解配置元信息 @Autowired 只依照byType 注入,由Spring提供@Resource @Resource默认按byName主动注入,也提供依照byType 注入@Inject(可选)@Autowired 只依照byType 注入,由Spring提供@Resource 默认按byName主动注入,也提供依照byType 注入,由JDK提供// @Inject 须要依赖<dependency> <groupId>javax.inject</groupId> <artifactId>javax.inject</artifactId> <version>1</version></dependency>实体类Holder.java package com.rumenz;public class Holder { private Rumenz rumenz; public Holder(Rumenz rumenz) { this.rumenz = rumenz; } public Rumenz getRumenz() { return rumenz; } public void setRumenz(Rumenz rumenz) { this.rumenz = rumenz; }}实体类Rumenz.java package com.rumenz;public class Rumenz { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}配置文件Beans.xml ...

July 17, 2020 · 1 min · jiezi

关于spring:spring注解驱动开发9-Profile多配置文件容器的注册选择

有时候咱们有两个配置文件, 一个是dev环境的, 一个是线上product环境的, 在开发的时候, 启动加载dev配置文件, 上线后启动应用product的配置; 这里就能够用到@Profile的性能; 其原理就是在上下文启动的时候, 通过扭转一个环境参数(-Dspring.profiles.active=xxx)的值, 让spring去抉择加载和初始化对应的profile; 举例: 咱们有两套数据源配置: dev 和 product; 配置区别大体如下: 配置文件: src\main\resources\db.propertiesdev.mysql.url = jdbc:mysql://127.0.0.1/devdev.mysql.username = niewjdev.mysql.password = 123456dev.mysql.validationQuery = SELECT 1#-----------------product.mysql.url = jdbc:mysql://niewj.com/productproduct.mysql.username = rootproduct.mysql.password = 654321product.mysql.validationQuery = SELECT 1配置注解文件: ProfilesDatasourceConfig.javapackage com.niewj.config;import com.alibaba.druid.pool.DruidDataSource;import org.springframework.beans.factory.annotation.Value;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Profile;import org.springframework.context.annotation.PropertySource;@Configuration@PropertySource("classpath:/db.properties")public class ProfilesDatasourceConfig { /** * product 配置 */ @Value("${product.mysql.url}") private String urlProduct; @Value("${product.mysql.username}") private String usernameProduct; @Value("${product.mysql.password}") private String passwordProduct; /** * dev 配置 */ @Value("${dev.mysql.url}") private String urlDev; @Value("${dev.mysql.username}") private String usernameDev; @Value("${dev.mysql.password}") private String passwordDev; /** * 其余通用配置 */ @Value("${dev.mysql.validationQuery}") private String validationQuery = "SELECT 1"; @Profile("dev") @Bean public DruidDataSource dataSourceDev(){ DruidDataSource ds = new DruidDataSource(); ds.setUrl(urlDev); ds.setUsername(usernameDev); ds.setPassword(passwordDev); ds.setValidationQuery(validationQuery); System.out.println("初始化-dev-druidDataSource"); return ds; } @Profile("product") @Bean public DruidDataSource dataSourceProduct(){ DruidDataSource ds = new DruidDataSource(); ds.setUrl(urlProduct); ds.setUsername(usernameProduct); ds.setPassword(passwordProduct); ds.setValidationQuery(validationQuery); System.out.println("初始化-product-druidDataSource"); return ds; }}测试用例: src\test\java\com\niewj\ProfileTest.javapackage com.niewj;import com.alibaba.druid.pool.DruidDataSource;import com.niewj.config.ProfilesDatasourceConfig;import org.junit.Test;import org.springframework.context.annotation.AnnotationConfigApplicationContext;import java.util.stream.Stream;/** * spring profile测试 */public class ProfileTest { @Test public void testProfile() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(); ctx.register(ProfilesDatasourceConfig.class); ctx.getEnvironment().setActiveProfiles("product"); // 1 // ctx.getEnvironment().setActiveProfiles("dev"); // 2 ctx.refresh(); // 打印spring容器中的 BeanDefinition Stream.of(ctx.getBeanDefinitionNames()).forEach(e -> System.out.println(e)); System.out.println("============================="); DruidDataSource ds = null; if ((ds = ctx.getBean(DruidDataSource.class)) != null) { System.out.println(ds); } ctx.close(); }}这里有两种形式能够实现调用: ...

July 17, 2020 · 2 min · jiezi

想用Autowired注入static静态成员官方不推荐你却还偏要这么做

生命太短暂,不要去做一些基本没有人想要的货色。本文已被 https://www.yourbatman.cn 收录,外面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以收费学习。关注公众号【BAT的乌托邦】一一击破,深刻把握,回绝浅尝辄止。 前言各位小伙伴大家好,我是A哥。通过本专栏前两篇的学习,置信你对static关键字在Spring/Spring Boot里的利用有了全新的意识,可能解释工作中遇到的大多数问题/疑难了。本文持续来聊聊static关键字更为常见的一种case:应用@Autowired依赖注入动态成员(属性)。 在Java中,针对static动态成员,咱们有一些最根本的常识:动态变量(成员)它是属于类的,而非属于实例对象的属性;同样的静态方法也是属于类的,一般办法(实例办法)才属于对象。而Spring容器治理的都是实例对象,包含它的@Autowired依赖注入的均是容器内的对象实例,所以对于static成员是不能间接应用@Autowired注入的。 这很容易了解:类成员的初始化较早,并不需要依赖实例的创立,所以这个时候Spring容器可能都还没“出世”,谈何依赖注入呢?这个示例,你或者似曾相识: @Componentpublic class SonHolder { @Autowired private static Son son; public static Son getSon() { return son; }}而后“失常应用”这个组件: @Autowiredprivate SonHolder sonHolder;@Transactionpublic void method1(){ ... sonHolder.getSon().toString();}运行程序,后果抛错: Exception in thread "main" java.lang.NullPointerException ...很显著,getSon()失去的是一个null,所以给你扔了个NPE。 版本约定本文内容若没做非凡阐明,均基于以下版本: JDK:1.8Spring Framework:5.2.2.RELEASE注释说起@Autowired注解的作用,没有人不相熟,主动拆卸嘛。依据此注解的定义,它仿佛能应用在很多中央: @Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Autowired { boolean required() default true;}本文咱们重点关注它应用在FIELD成员属性上的case,标注在static动态属性上是本文探讨的核心。 阐明:尽管Spring官网当初并不举荐字段/属性注入的形式,但它的便捷性仍无可取代,因而在做业务开发时它仍旧是支流的应用形式场景形容如果有这样一个场景需要:创立一个教室(Room),须要传入一批学生和一个老师,此时我须要对这些用户依照规定(如名字中含有test字样的示为测试帐号)进行数据合法性校验和过滤,而后能力失常走创立逻辑。此case还有以下特点: 用户名字/详细信息,须要近程调用(如FeignClient形式)从UC核心获取 因而很须要做桥接,提供防腐层该过滤规定功能性很强,工程内很多中央都有用到 有点工具的意思有木有浏览完“题目”感觉还是蛮简略的,很normal的一个业务需要case嘛,上面我来模仿一下它的实现。 从UC用户核心获取用户数据(应用本地数据模仿近程拜访): /** * 模仿去远端用户核心,依据ids批量获取用户数据 * * @author yourbatman * @date 2020/6/5 7:16 */@Componentpublic class UCClient { /** * 模仿近程调用的后果返回(有失常的,也有测试数据) */ public List<User> getByIds(List<Long> userIds) { return userIds.stream().map(uId -> { User user = new User(); user.setId(uId); user.setName("YourBatman"); if (uId % 2 == 0) { user.setName(user.getName() + "_test"); } return user; }).collect(Collectors.toList()); }}阐明:理论状况这里可能只是一个@FeignClient接口而已,本例就应用它mock喽因为过滤测试用户的性能过于通用,并且规定也须要收口,须对它进行封装,因而有了咱们的外部帮忙类UserHelper: ...

July 17, 2020 · 3 min · jiezi

springRestTemplate设计模式

作用用于http申请,且封装了不同http框架, 1.jdk api //默认 2.httpclient //1.连接池2.超时 3.okhttp //异步 RestTemplate,自身不实现http申请,http申请的底层实现是其余框架,RestTemplate的外围有两个 1.更不便http申请 2.且能够切换不同http框架 不同http框架,提供的性能不齐全一样 应用层-应用步骤 1.获取模板 2.调用模板的办法 List<AuditResult> auditResultList = null; //响应数据try { auditResultList = HttpClientUtil.getTemplate().postForObject(url, audits //入参数据的类型个别是map/list, List.class //响应数据的数据类型);} catch (Throwable e) { log.error(msg + " 调用风控系统超时!" + e.getMessage()); return;}切换默认jdk api为httpclient默认是jdk api,为什么要切换? 因为有更多的配置项,比方 1.连接池 2.超时 如何切换? 须要简略封装一下, 1.用httpclient的客户端申请类CloseableHttpClient笼罩默认的 2.配置连接池 3.配置超时 package xxx.util;import java.util.ArrayList;import java.util.List;import org.apache.http.client.config.RequestConfig;import org.apache.http.impl.client.CloseableHttpClient;import org.apache.http.impl.client.HttpClients;import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;import org.springframework.http.converter.HttpMessageConverter;import org.springframework.web.client.RestTemplate;import com.alibaba.fastjson.serializer.SerializerFeature;import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;public class HttpClientUtil {//连接池配置项 private static int POOLSIZE = 150;// 连接池 private static PoolingHttpClientConnectionManager connMgr = null;//超时配置项 private final static int SOCKETTIMEOUT = 1000;// 读取响应超时工夫 private final static int CONNECTIONREQUESTTIMEOUT = 3000; // 从连接池获取连贯超时工夫 private final static int CONNECTTIMEOUT = 2000;// 连贯超时工夫 public static RestTemplate getTemplate() { CloseableHttpClient httpClient = getConnection(); RestTemplate template = new RestTemplate( new HttpComponentsClientHttpRequestFactory(httpClient)); //用httpclient的客户端申请类CloseableHttpClient笼罩默认的 List<HttpMessageConverter<?>> converters = new ArrayList<HttpMessageConverter<?>>(); // 采纳fastjson进行数据转换 FastJsonHttpMessageConverter fastjson = new FastJsonHttpMessageConverter(); fastjson.setFeatures(SerializerFeature.WriteClassName, SerializerFeature.BrowserCompatible, SerializerFeature.DisableCircularReferenceDetect); converters.add(fastjson); template.setMessageConverters(converters); return template; } private static CloseableHttpClient getConnection() { if (connMgr == null) { // 创立链接池。连接池能够并发申请100个连贯 connMgr = new PoolingHttpClientConnectionManager(); connMgr.setMaxTotal(POOLSIZE + 1); connMgr.setDefaultMaxPerRoute(POOLSIZE); } RequestConfig requestConfig = RequestConfig.custom() .setConnectionRequestTimeout(CONNECTIONREQUESTTIMEOUT) .setConnectTimeout(CONNECTTIMEOUT) .setSocketTimeout(SOCKETTIMEOUT).build(); //配置超时 CloseableHttpClient httpClient = HttpClients.custom() .setConnectionManager(connMgr) //配置连接池 .setDefaultRequestConfig(requestConfig).build(); return httpClient; }}源码流程是 1.获取客户端 2.申请服务器 ...

July 17, 2020 · 2 min · jiezi

小书MybatisPlus第5篇Active-Record模式精讲

本文为一个系列中的第五节,前四节拜访如下地址: 小书MybatisPlus第1篇-整合SpringBoot疾速开始增删改查小书MybatisPlus第2篇-条件结构器的利用及总结小书MybatisPlus第3篇-自定义SQL小书MybatisPlus第4篇-表格分页与下拉分页查问Active Record 适宜非常简单的畛域需要,尤其在畛域模型和数据库模型十分相似的状况下。如果遇到更加简单的畛域模型构造(例如用到继承、策略的畛域模型),往往须要应用拆散数据源的畛域模型,联合 Data Mapper (数据映射器)应用。 具体到应用层面,咱们之前章节讲过应用Data Mapper 做数据的长久层操作。 User user = new User();user.setName("字母哥");user.setAge(18);userMapper.insert(user); //Mybatis Mapper模式当初咱们应用ActiveRecord模式,用法如下,留神二者的区别 User user = new User();user.setName("zimug");user.setAge(18);user.insert(); //ActiveRecord模式一、使ActiveRecord模式失效首先:须要让数据库表对应的数据长久层实体类。集成自Model<User>,并实现序列化接口。 @Data //lombok注解@EqualsAndHashCode(callSuper = true)public class User extends Model<User> implements Serializable { private static final long serialVersionUID = 6401942840459021558L; private Long id; private String name; private Integer age; private String email;}其实定义一个Mapper继承自BaseMapper<T>,T代表数据长久层实体类。 public interface UserMapper extends BaseMapper<User> {}这样Mybatis Plus的ActiveRecord模式就失效了,默认的帮咱们实现了如下的一些数据长久层办法。 二、增删该查的实现减少:向长久层实体类User对应的数据库表user,插入一条数据。 @Testpublic void testInsert() { User user = new User(); user.setName("zimug"); user.setAge(18); user.setEmail("hadoopcn2@163.com"); boolean success = user.insert(); System.out.println("是否插入胜利:"+success);}查问:从数据库表user查问所有数据 ...

July 17, 2020 · 1 min · jiezi

Spring-Setter方法注入

Setter办法输出实体类Rumenz.java/Holder.java package com.rumenz;public class Rumenz { private Integer id; private String name; public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}package com.rumenz;public class Holder { private Rumenz rumenz; public Rumenz getRumenz() { return rumenz; } public void setRumenz(Rumenz rumenz) { this.rumenz = rumenz; }}手动输出形式XML 资源配置元信息配置Beans.xml文件 ...

July 16, 2020 · 2 min · jiezi

新鲜出炉这是全网讲的最详细的springboot整合消息服务了吧建议收藏

springboot整合activeMqActiveMq是Apache提供的开源音讯零碎采纳java实现, 很好地反对JMS(Java Message Service,即Java音讯服务) 标准 ActiveMq装置:http://activemq.apache.org/co... 在官网下载安装对应的版本 下载实现后解压就能够应用 ActiveMq默认的端口号是8161,用户名和明码都是admin 在本机能够应用http://localhost:8161 去拜访 springboot整合ActiveMq1、导入依赖<dependency>   <groupId>org.springframework.boot</groupId>   <artifactId>spring-boot-starter-activemq</artifactId></dependency>2、在properties文件中配置activeMqspring.activemq.broker-url=tcp://localhost:61616#如果是点对点(queue),那么此处默认应该是false,如果公布订阅,那么肯定设置为truespring.activemq.packages.trust-all=truespring.activemq.user=adminspring.activemq.password=admin3、编写queue(队列)@Componentpublic class QueueBean{    //创立一个队列实例    @Bean    Queue queue(){        //这里设置的音讯是队列的名称        return new ActiveMQQueue("hello.javaboy");   }}4、创立音讯的发送者以及消费者@Componentpublic class JmsComponent{    //springboot提供的音讯模板    @Autowired    JmsMessagingTemplate jmsMessagingTemplate;    //本人创立的队列实例    @Autowired    Queue queue;    /**     * 发送音讯     * @param message     */    public void send(Message message){        jmsMessagingTemplate.convertAndSend(this.queue,message);   }    /**     * 接管音讯     * @param message     */    //示意监听该队列名称发来的音讯    @JmsListener(destination = "hello.javaboy")    public void readMessage(Message message){        System.out.println(message);   }}5、上述Message实体类public class Message implements Serializable {    private String content;//音讯主体    private Date sendDate;//音讯发送的工夫    //省略get、set、tostring办法}6、进行音讯的发送以及生产在测试类中注入JmsComponent 调用send()办法进行音讯的转发 ...

July 16, 2020 · 5 min · jiezi

static关键字有何魔法竟让Spring-Boot搞出那么多静态内部类

生命太短暂,不要去做一些基本没有人想要的货色。本文已被 https://www.yourbatman.cn 收录,外面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以收费学习。关注公众号【BAT的乌托邦】一一击破,深刻把握,回绝浅尝辄止。 前言各位小伙伴大家好,我是A哥。上篇文章理解了static关键字 + @Bean办法的应用,通晓了它可能晋升Bean的优先级,在@Bean办法前标注static关键字,特定状况下能够防止一些烦人的“正告”日志的输入,排除隐患让工程变得更加平安。咱们晓得static关键字它不仅可应用在办法上,那么本文将持续开掘static在Spring环境下的用途。 依据所学的JavaSE根底,static关键字除了可能润饰办法外,还能应用在这两个中央: 润饰类。确切的说,应该叫润饰外部类,所以它叫动态外部类润饰成员变量其实static还能够润饰代码块、static动态导包等,但很显著,这些与本文无关接下来就以这为两条主线,别离钻研static在对应场景下的作用,本文将聚焦在动态外部类上。 版本约定本文内容若没做非凡阐明,均基于以下版本: JDK:1.8Spring Framework:5.2.2.RELEASE注释说到Java里的static关键字,这当属最根底的入门常识,是Java中罕用的关键字之一。你平时用它来润饰变量和办法了,然而对它的理解,即便放在JavaSE情景下晓得这些还是不够的,问题虽小但这往往反映了你对Java根底的理解水平。 当然喽,本文并不探讨它在JavaSE下应用,毕竟咱们还是有肯定逼格的专栏,须要进阶一把,玩玩它在Spring环境下到底可能迸出怎么样的火花呢?比方动态外部类~ Spring下的动态外部类static润饰类只有一种状况:那就是这个类属于外部类,这就是咱们津津有味的动态外部类,形如这样: public class Outer { private String name; private static Integer age; // 动态外部类 private static class Inner { private String innerName; private static Integer innerAge; public void fun1() { // 无法访问外部类的成员变量 //System.out.println(name); System.out.println(age); System.out.println(innerName); System.out.println(innerAge); } } public static void main(String[] args) { // 动态外部类的实例化并不需要依赖于外部类的实例 Inner inner = new Inner(); }}在理论开发中,动态外部类的应用场景是十分之多的。 意识动态/一般外部类因为一些小伙伴对一般外部类 vs 动态外部类傻傻分不清,为了不便后续解说,本处把要害因素做简要比照阐明: ...

July 16, 2020 · 4 min · jiezi

SpringCloud-第十篇-Ribbon

1:Ribbon是什么Ribbon是一个开源的客户端负载均衡器,由Netflix公布,是SpringCloud-Netflix中重要的一环,通过它将Netflix的中间层服务连贯在一起。Ribbon客户端组件提供一系列欠缺的配置项,如连贯超时、重试等。简略的说,就是在配置文件中列出Load Balancer前面所有的服务,Ribbon会主动的基于某种规定(如简略轮询,随机连贯等)去连贯这些服务,也很容易实现自定义的负载平衡算法。 2:Ribbon能干什么Ribbon是在客户端来实现负载平衡的拜访服务,次要的性能点:1:服务发现,发现依赖服务的列表2:服务抉择规定,在多个服务中如何抉择一个无效服务3:服务监听,检测生效的服务,高效剔除生效服务 3:架构图 4:集成退出依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId></dependency>先不要加Eureka的starter官网的HelloWorld具体示例参看:https://github.com/Netflix/ri... 5:参数配置5.1:概述对于Ribbon参数的key以及value类型的定义,能够通过com.netflix.client.config.CommonClientConfigKey查看如果你没有配置任何属性,则ribbon会应用com.netflix.client.config.DefaultClientConfigImpl里的值 5.2:配置形式Ribbon的参数配置通常有二种形式:全局配置以及指定客户端配置 全局配置的形式很简略只须要应用ribbon.<key>=<value>格局进行配置即可。其中,<key>代表了Ribbon客户端配置的参数名,<value>则代表了对应参数的值。比方,能够向上面这样配置Ribbon的超时工夫:ribbon.ConnectTimeout=250 全局配置能够作为默认值进行设置,当指定客户端配置了相应的key的值时,将笼罩全局配置的内容 指定客户端的配置形式<client>.ribbon.<key>=<value>的格局进行配置<client>示意服务名,比方没有服务治理框架的时候(如Eureka),须要指定实例清单,如:userService.ribbon.listOfServers=localhost:8080 6:外围组件6.1:Ribbon外围组件IRule:依据特定算法中从服务列表中选取一个要拜访的服务IPing:后盾运行线程,用来判断服务是否可用ServerList: 服务列表,能够是动态的或动静的。如果它是动静的,后盾线程将刷新并按肯定的工夫距离过滤列表还有: ServerListFilter:该接口容许过滤配置或动静获取的具备所需个性的服务器列表ServerListUpdater:被DynamicServerListLoadBalancer用于动静的更新服务列表IClientConfig:定义各种配置信息,用来初始化ribbon客户端和负载均衡器ILoadBalancer:定义软件负载平衡器操作的接口。动静更新一组服务列表及依据指定算法从现有服务器列表中抉择一个服务6.2:配置这些组件能够以编程形式设置,也能够是客户端配置属性的一部分,并通过反射创立。反对的属性如下所示,应以<clientName>.ribbon.为前缀: NFLoadBalancerClassName:要应用的ILoadBalancerNFLoadBalancerRuleClassName:要应用的IruleNFLoadBalancerPingClassName:要应用的IpingNIWSServerListClassName:要应用的ServerListNIWSServerListFilterClassName:要应用的ServerListFilter6.3:IRule,罕用实现有以下几种:都在com.netflix.loadbalancer包上面 1:RoundRobinRule:轮询规定2:RandomRule:随机获取一个服务3:AvailabilityFilteringRule 这个负载均衡器规定,会先过滤掉因为屡次拜访故障而处于断路器跳闸状态的服务,还有并发的连贯数量超过阈值的服务,而后对残余的服务列表依照轮询策略进行拜访4:WeightedResponseTimeRule  依据均匀响应工夫计算所有服务的权重,响应工夫越快,服务权重越大、被选中的概率越高。刚启动时,如果统计信息有余,则应用RoundRobinRule策略,等统计信息足够,会切换到WeightedResponseTimeRule。5:RetryRule 先依照RoundRobinRule的策略获取服务,如果获取服务失败,则在指定工夫内会进行重试,获取可用的服务6:BestAvailableRule 此负载均衡器会先过滤掉因为屡次拜访故障而处于断路器跳闸状态的服务,而后抉择一个并发量最小的服务7:ZoneAvoidanceRule:默认规定,复合判断server所在区域的性能和server的可用性抉择server6.4:IPing,罕用实现有以下几种:都在com.netflix.loadbalancer包上面1:DummyPing:默认实现,默认返回true,即认为服务永远活着2:NIWSDiscoveryPing  不执行真正的ping。如果Discovery Client认为是在线,则程序认为本次心跳胜利,服务活着3:PingUrl  此组件会应用HttpClient调用服务的一个URL,如果调用胜利,则认为本次心跳胜利,示意此服务活着。4:NoOpPing:永远返回true,即认为服务永远活着ServerList,罕用实现有以下几种:都在com.netflix.loadbalancer包上面1:ConfigurationBasedServerList,从配置文件中获取所有服务列表,也是动态获取服务列表,配置例子:sample-client.ribbon.listOfServers=2:DiscoveryEnabledNIWSServerList 从Eureka Client中获取服务列表。此值必须通过属性中的VipAddress来标识服务器集群。DynamicServerListLoadBalancer会调用此对象动静获取服务列表3:DomainExtractingServerList:代理类,依据ServerList的值实现具体的逻辑7: 联合Eureka在SpringCloud中应用的HelloWorld 先退出依赖:<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>能够不必退出Ribbon依赖,因为Eureka的starter外面蕴含了ribbon 写一个配置类提供RestTemplate,示例如下: @Bean@LoadBalancedpublic RestTemplate rt(){ return new RestTemplate();}写一个Hello2的Controller,在办法外面应用RestTemplate启动SpringBoot的启动类,而后就能够通过页面进行拜访测试了8: RestTemplateRestTemplate 是Spring提供的用于拜访Rest服务的客户端,RestTemplate提供了多种便捷拜访近程Http服务的办法,可能大大提高客户端的编写效率。@LoadBalanced示意restTemplate应用LoadBalancerClient执行申请,会主动应用负载平衡策略,它外部会在RestTemplate中退出LoadBalancerInterceptor这个拦截器,这个拦截器的作用就是应用负载平衡。这样在拜访近程服务的时候,能够间接应用Provider向Eureka Server注册的虚构名字,如:rt.getForObject("http://userService/userServiceProvider?name=cc", String.class); 当Eureka与Ribbon联合应用(即两者都在类门路上)时,ribbonServerList将被扩大为DiscoveryEnabledNIWSServerList,扩大名为Eureka的服务器列表。它还用NIWSDiscoveryPing替换IPing接口,代理到Eureka以确定服务器是否启动。能够通过代码来进行配置,比方配置IRule、IPing等设置属性ribbon.eureka.enabled = false将明确禁用在Ribbon中应用Eureka9:ServerListFilter罕用实现有以下几种:都在com.netflix.loadbalancer包上面 ZoneAffinityServerListFilter  过滤掉所有的不和客户端在雷同zone的服务,如果和客户端雷同的zone不存在,才不过滤不同zone有服务。启用此配置应用以下配置<clientName>.ribbon.EnableZoneAffinity=true ServerListSubsetFilter  ZoneAffinityServerListFilter的子类。此过滤器确保客户端仅看到由ServerList实现返回的整个服务器的固定子集。 它还能够定期用新服务器代替可用性差的子集中的服务器。要启用此过滤器,请指定以下属性:myClient.ribbon.NIWSServerListClassName=com.netflix.niws.loadbalancer.DiscoveryEnabledNIWSServerList # the server must register itself with Eureka server with VipAddress "myservice"myClient.ribbon.DeploymentContextBasedVipAddresses=myservicemyClient.ribbon.NIWSServerListFilterClassName=com.netflix.loadbalancer.ServerListSubsetFilter# only show client 5 servers. default is 20.myClient.ribbon.ServerListSubsetFilter.size=510:ServerListUpdaterServerListUpdater,罕用实现有以下几种:都在com.netflix.loadbalancer包上面 ...

July 16, 2020 · 1 min · jiezi

据说比Spring快44倍的web开发框架不妨试试

该框架称为:light-4j 。官方网站简介:A fast, lightweight and more productive microservices framework。很简略,翻译过去就是:一个疾速、轻量级和更高效的微服务框架. 为什么叫light-4j?全称应该是:Light for Java,意味着轻量级,并以闪电般的速度来进行 Java 编程. 用这个框架有什么益处?1. 降低成本为什么说它能降低成本,因为它速度十分快,占用内存也十分小. 重点来了,它比相似 Spring Boot 这种内嵌 Tomcat 式的支流微服务框架平台要快 44 倍,并且只须要用其 1/5 内存,听起来是不是很牛逼,的确是节约了不少内存空间. 这是一个基准测试报告,与Spring Boot和其余微服务平台进行了比拟: 具体报告:https://github.com/networknt/... 很弱小,性能与 Go 语言并肩,并且领有更低的均匀提早. 这也是与其余Web框架的比拟: 具体报告:https://www.techempower.com/b... 2.丰盛的性能具备启动/敞开挂钩和各种中间件的插件架构分布式OAuth2 JWT平安验证作为框架的一部分基于OpenAPI标准的申请和响应验证收集测量指标以及反对服务和管制台上显示的客户全局运行时异样解决,例如API异样和其余查看的异样在日志输入之前对敏感数据进行加密,例如信用卡,SIN号等。革除申请参数,申请标头和注释的跨站点攻打脚本审核重要信息或整个申请/响应申请主体反对各种类型的内容类型配置标准化的响应代码和响应音讯反对内部配置的Docker环境的所有模块来自其余域名的跨域解决反对限速解决内部提供的服务服务发现和注册反对间接连贯,领事和Zookeeper客户端发现和负载平衡,打消了代理层与Light-OAuth2严密集成并反对可追溯性首先在这里介绍,每个有趣味的人都能够去Github修补一下...Github地址:https://github.com/networknt/... 最初浏览完之后,您可能会问,目前有在宽泛的应用吗?这个我目前没有牢靠数据,但这个框架的性能体现和内存耗费真的十分惊人,以及它的各种性能个性都值得借鉴。 比 Spring Boot 框架要快 44 倍,然而我感觉这个大家也不必太纠结,Spring 倒退到明天,通过国外各种大神的打磨,能够说是十分精湛.Spring 日益宠大的同时,其外部依赖集成了太多货色,在性能这方面没其余框架强,的确可能了解,但 Spring 的生态圈是没有任何框架能够比较的。在谋求生态完整性的同时,它必定也会就义很多货色,所以,我感觉一个生态凋敝的技术平台比谋求性能更重要. 欢送关注我的博客,外面有很多精品合集本文转载注明出处(必须带连贯,不能只转文字):字母哥博客。感觉对您有帮忙的话,帮我点赞、分享!您的反对是我不竭的创作能源! 。另外,笔者最近一段时间输入了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》《Spring Security-JWT-OAuth2一本通》《实战前后端拆散RBAC权限管理系统》《实战SpringCloud微服务从青铜到王者》《VUE深入浅出系列》

July 16, 2020 · 1 min · jiezi

Spring-自动绑定Autowiring模式

依赖注入类型依赖注入类型配置元数据举例Setter 办法<proeprty name="user" ref="userBean"/>结构器<constructor-arg name="user" ref="userBean" />字段@Autowired User user;办法@Autowired public void user(User user) { ... }接口回调class MyBean implements BeanFactoryAware { ... }主动绑定(Autowiring)模式主动绑定次要在xml模式下应用模式阐明no默认值,未激活 Autowiring,须要手动指定依赖注入对象。byName依据被注入属性的名称作为 Bean 名称进行依赖查找,并将对象设置到该 属性。byType依据被注入属性的类型作为依赖类型进行查找,并将对象设置到该属性。constructor非凡 byType 类型,用于结构器参数。Setter 办法注入(主动模式)ByName 通过名称ByType 通过类型ByName 通过名称配置文件Beans.xml <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:util="http://www.springframework.org/schema/util" xsi:schemaLocation=" http://www.springframework.org/schema/beans https://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"> <bean id="rumenz" class="com.rumenz.Rumenz"> <property name="id" value="123"/> <property name="name" value="入门小站"/> </bean> <bean id="holder" class="com.rumenz.Holder" autowire="byType"> </bean></beans>调用 package com.rumenz;public class DemoApplication { public static void main(String[] args) { ClassPathXmlApplicationContext ca=new ClassPathXmlApplicationContext("Beans.xml"); ca.refresh(); Holder bean1 = ca.getBean(Holder.class); System.out.println(bean1.getRumenz().getName()); ca.close(); }}输入 入门小站ByType 通过类型配置文件Beans.xml ...

July 15, 2020 · 1 min · jiezi

SpringOrder注解

如果咱们在同一个办法自定义多个AOP,如何执行执行程序?有一种答案是指定order,order越小越先执行。这种答案不算错,然而有些全面。 更改Aspect的Order有三中形式,1:通过实现org.springframework.core.Ordered接口;2:通过注解@Order(1) ;3:通过配置文件配置 咱们试验采纳注解的形式,定义两个Aspect: @Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Log { String description() default "";} order=1001代码块java@Aspect@Component@Order(1001)public class LogAspect { private final Logger logger \= LoggerFactory.getLogger(this.getClass()); //日志注解 @Pointcut(value \= "@annotation(com.meituan.pay.mapi.aspect.Log)") public void logAspect() { } //申明前置告诉 @Before("logAspect()") public void doBefore(JoinPoint point) { logger.info("logAspect:doBefore"); return; } //申明后置告诉 @AfterReturning(pointcut \= "logAspect()", returning \= "returnValue") public void doAfterReturning(JoinPoint point, Object returnValue) { logger.info("logAspect:doAfterReturning"); } //申明例外告诉 @AfterThrowing(pointcut \= "logAspect()", throwing \= "e") public void doAfterThrowing(Exception e) { logger.info("logAspect:doAfterThrowing"); } //申明最终告诉 @After("logAspect()") public void doAfter() { logger.info("logAspect:doAfter"); } //申明盘绕告诉 @Around("logAspect()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { logger.info("logAspect:doAround-start"); Object obj \= pjp.proceed(); logger.info("logAspect:doAround-end"); return obj; }}**SignAspect:**代码块java@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.METHOD)public @interface Sign { String description() default "";}order=1002代码块java@Aspect@Component@Order(1002)public class SignAspect { private final Logger logger \= LoggerFactory.getLogger(this.getClass()); //日志注解 @Pointcut(value \= "@annotation(com.meituan.pay.mapi.aspect.Sign)") public void signAspect() { } //申明前置告诉 @Before("signAspect()") public void doBefore(JoinPoint point) { logger.info("signAspect:doBefore"); return; } //申明后置告诉 @AfterReturning(pointcut \= "signAspect()", returning \= "returnValue") public void doAfterReturning(JoinPoint point, Object returnValue) { logger.info("signAspect:doAfterReturning"); } //申明例外告诉 @AfterThrowing(pointcut \= "signAspect()", throwing \= "e") public void doAfterThrowing(Exception e) { logger.info("signAspect:doAfterThrowing"); } //申明最终告诉 @After("signAspect()") public void doAfter() { logger.info("signAspect:doAfter"); } //申明盘绕告诉 @Around("signAspect()") public Object doAround(ProceedingJoinPoint pjp) throws Throwable { logger.info("signAspect:doAround-start"); Object obj \= pjp.proceed(); logger.info("signAspect:doAround-end"); return obj; }}**测试:**代码块java@Servicepublic class AspectTestService { private static final Logger LOGGER \= LoggerFactory.getLogger(AspectTestService.class); @Sign @Log public void testAspect() { LOGGER.info("执行办法"); }}**最终输入:**代码块javaLogAspect (LogAspect.java:55) logAspect:doAround\-startLogAspect (LogAspect.java:30) logAspect:doBeforeSignAspect (SignAspect.java:54) signAspect:doAround\-startSignAspect (SignAspect.java:29) signAspect:doBeforeAspectTestService (AspectTestService.java:19) 执行办法SignAspect (SignAspect.java:56) signAspect:doAround\-endSignAspect (SignAspect.java:48) signAspect:doAfterSignAspect (SignAspect.java:36) signAspect:doAfterReturningLogAspect (LogAspect.java:57) logAspect:doAround\-endLogAspect (LogAspect.java:49) logAspect:doAfterLogAspect (LogAspect.java:37) logAspect:doAfterReturning**论断:**从下面的测试咱们看到,的确是order越小越是最先执行,但更重要的是最先执行的最初完结。AOP就是面向切面编程,什么是切面,画一个图来了解下: ...

July 15, 2020 · 2 min · jiezi

spring注解驱动开发8-自动装配-Autowired-Resource-Qualifier-Primary

@Autowiredspring的注解, 用来做主动拆卸; 如果用于属性上: 首先依照类型匹配, 类型匹配失败会用name匹配;如果匹配到多个: 再用@Qualifier("beanName")的beanName来筛选, 看拆卸哪个bean; 如果一个都匹配不到, 而且申明了属性 required=false , 不调用不会报错; 调用就会 NoSuchBeanDefinitionException; 不仅能够用在属性上, 也能够用到办法/结构器/办法参数上: (用在办法上和用在入参上意义是雷同的) 如果应用在set办法上和构造方法上, 都有入参; 不管有没有@Autowired注解在办法上和参数上, 都会从IOC容器中去拆卸, 不会报错;@Qualifier@Qualifier的作用如上; 用来在拆卸某个类型的bean时, 找到多个bean后用它的value作为name来辨别;@Primary 如果某个类型的bean在IOC容器中有多个能够匹配到; 在拆卸时又没有指定@Qualifier(value="beanName"); 此时, 就会首先筛选申明时标注了 @Primary的bean来拆卸!@ResourceJSR250的注解, 属于java标准; @Resource默认先依照name来匹配, 如果name匹配不到, 再依照类型去匹配; 也能够和 @Qualifier 和 @Primary 一起应用; @Resource和@Autowired比拟:不同点@Resource首先依照name匹配, 匹配不到采纳类型; @Autowired首先依照类型匹配, 匹配到多个才会用name筛选;@Resource是JSR250的注解, 是java标准定义的; @Autowired是spring定义的注解;@Resource没有@Autowired(required=false)里的 required属性, 没有可选bean, 即便不调用, 也会报: NoSuchBeanDefinitionException;@Resource不能应用到参数和构造方法上; @Autowired能够(ElementType.PARAMETER, ElementType.CONSTRUCTOR)!共同点:都有备选机制: 即name和类型双备选, 一个找不到, 用另一个;当匹配到多个时, 都能够配合应用@Qualifier 和 @Primary来辨别;当匹配到多个时, 匹配到多个, 也没有@Qualifier 和 @Primary注解辨别, 两个注解都会报异样:Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'com.niewj.service.ProductService' available: expected single matching bean but found 2: productService,productService2配置类: 这里有一个ProductService初始化: productService2 ...

July 15, 2020 · 2 min · jiezi

static关键字真能提高Bean的优先级吗答真能

生命太短暂,不要去做一些基本没有人想要的货色。本文已被 https://www.yourbatman.cn 收录,外面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以收费学习。关注公众号【BAT的乌托邦】一一击破,深刻把握,回绝浅尝辄止。 前言各位小伙伴大家好,我是A哥。对于Spring初始化Bean的程序问题,是个陈词滥调的话题了,论断可总结为一句话:全局无序,部分有序。Spring Bean整体上是无序的,而事实是大多数状况下咱们真的无需关怀,无序就无序呗,无所谓喽。然而(此处应该有然而哈),我有理由置信,对于有肯定从业教训的Javaer来说,或多或少都经验过Bean初始化程序带来的“困扰”,兴许是因为没有对你的性能造成影响,兴许可能是你全然“不知情”,所以最终就不了了之~ 隐患终归隐患,按照墨菲定律来讲,放心的事它总归是会产生的。A哥常常“唆使”程序员要面向工资编程,尽管这价值观有点扭曲,但不可否认很多小伙伴真是这么想的(命中你了没有????),稍加掩饰了而已。话粗理不粗哦,almost所有的Javaer都在用Spring,你凭什么工资比你身边共事的高呢?Spring对Bean的(生命周期)治理是它最为外围的能力,同时也是很简单、很难把握的一个知识点。当初就能够启动你的工程,有木有这句日志: "Bean 'xxx' of type [xxxx] is not eligible for getting processed by all BeanPostProcessors" + "(for example: not eligible for auto-proxying)"这是一个典型的Spring Bean过早初始化问题,搜搜看你日志里是否有此句喽。这句日志是由Spring的BeanPostProcessorChecker这个类负责输入,含意为:你的Bean xxx不能被所有的BeanPostProcessors解决到(有的生命周期触达不到),揭示你留神。此句日志在低些的版本里是warn正告级别,在本文约定的版本里官网把它改为了info级别。 绝大多数状况下,此句日志的输入不会对你的性能造成影响,因而无需搭理。这也是Spring官网为何把它从warn调低为info级别的起因我在CSDN上写过一篇“Spring Bean过早初始化导致的误伤”的文章,访问量达近4w:从这个数据(访问量)上来看,这件事“并不简略”,遇到此麻烦的小伙伴不在少数且的确难倒了一众人。对于Spring Bean的程序,全局是不可控的,然而部分上它提供了多种形式来不便使用者进步/升高优先级(比方后面的应用@AutoConfigureBefore调整配置程序竟没失效?这篇文章),本文就聊聊static关键字对于提供Bean的优先级的效用。 版本约定本文内容若没做非凡阐明,均基于以下版本: JDK:1.8Spring Framework:5.2.2.RELEASE注释本文采纳从 问题提出-后果剖析-解决方案-原理分析 这4个步骤,层层递进的去感触static关键字在Spring Bean上的魅力~ 正告一:来自BeanPostProcessorChecker这是最为常见的一种正告,特地当你的工程应用了shiro做鉴权框架的时候。在我记忆中这一年来有N多位小伙伴问过我此问题,可见一斑。 @Configurationclass AppConfig { AppConfig() { System.out.println("AppConfig init..."); } @Bean BeanPostProcessor postProcessor() { return new MyBeanPostProcessor(); }}class MyBeanPostProcessor implements BeanPostProcessor { MyBeanPostProcessor() { System.out.println("MyBeanPostProcessor init..."); }}运行程序,输入后果: AppConfig init...2020-05-31 07:40:50.979 INFO 15740 --- [ main] trationDelegate$BeanPostProcessorChecker : Bean 'appConfig' of type [com.yourbatman.config.AppConfig$$EnhancerBySpringCGLIB$$29b523c8] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying)MyBeanPostProcessor init......后果剖析(问题点/冲突点): ...

July 15, 2020 · 4 min · jiezi

SpringCloud-第九篇-Feign

1:Feign是什么Feign是一个申明式的Web服务客户端,使得编写Web服务客户端变得非常容易,只须要创立一个接口,而后在下面增加注解即可。官网:https://github.com/OpenFeign/... 2:Feign能干什么Feign旨在使编写Java Http客户端变得更容易。后面在应用Spring Cloud Ribbon+RestTemplate时,利用RestTemplate对http申请的封装解决,造成了一套模版化的调用办法。然而在理论开发中,因为对服务依赖的调用可能不止一处,往往一个接口会被多处调用,所以通常都会针对每个微服务自行封装一些客户端类来包装这些依赖服务的调用。Spring Cloud Feign在此基础上做了进一步封装,由他来帮忙咱们定义和实现依赖服务接口的定义。在Spring Cloud Feign的实现下,咱们只需创立一个接口并应用注解的形式来配置它,即可实现对服务提供方的接口绑定,简化了应用Spring cloud ribbon时,主动封装服务调用客户端的开发量。Spring Cloud Feign具备可插拔的注解反对,包含Feign注解和JAX-RS注解,同时还扩大了Spring MVC的注解反对。同时还集成Ribbon和Eureka,以在应用Feign时提供Http客户端的负载平衡;集成了Hystrix以提供熔断、降级等性能。另外,对于Feign本身的一些次要组件,比如说编码器和解码器等,都反对可插拔的形式,在有须要的时候能够不便地扩大和替换它们。3:Feingn个性可插拔的注解反对,包含Feign注解和JAX-RS注解反对可插拔的HTTP编码器和解码器反对Hystrix和它的Fallback反对Ribbon的负载平衡反对HTTP申请和响应的压缩4: Feign集成4.1:退出依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-eureka</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-feign</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>4.2:启动类须要增加开启feign的注解,示例如下: @SpringBootApplication@EnableDiscoveryClient@EnableFeignClients4.3:定义接口,示例如下:@FeignClient(value = "userService")public interface UserService { @RequestMapping(value = "/userServiceProvider", method = RequestMethod.GET) String sayHiFromClientOne(@RequestParam(value = "name") String name);}4.4:配置文件根本相似于Ribbon的配置, application.properties示例如下: eureka.client.serviceUrl.defaultZone=http://localhost:8761/eureka/server.port=8770spring.application.name=service-feign4.5:调用服务的Controller类Feign集成了Ribbon,仍然是利用了Ribbon保护了userService的服务列表信息,并且通过轮询实现了客户端的负载平衡。而与Ribbon不同的是,通过feign只须要定义服务绑定接口,以申明式的办法,优雅而简略的实现了服务调用Spring Cloud Feign只反对spring mvc 3.x系列的, springmvc4.0呈现的一系列注解比方@GetMapping,@PostMapping,@PutMapping等等是Feign是不反对的

July 15, 2020 · 1 min · jiezi

Spring-源码第六弹容器的始祖-DefaultListableBeanFactory

Spring 源码持续开整! 在 XML 文件解析流程一文中,松哥和大家分享了 Spring 中配置文件的加载形式,如果小伙伴们还没看过,肯定先看一下,这有助于更好的了解本文,传送门:Spring 源码第一篇开整!配置文件是怎么加载的?。 还记得该篇文章中的代码吗? XmlBeanFactory factory = new XmlBeanFactory(new ClassPathResource("beans.xml"));User user = factory.getBean(User.class);System.out.println("user = " + user);当 ClassPathResource 将文件以 IO 流的形式输入后,接下来就是结构 XmlBeanFactory ,XmlBeanFactory 性能无限,它的大部分性能都在它的父类 DefaultListableBeanFactory 中实现了,而 DefaultListableBeanFactory 也相当于是容器的始祖,为什么这么说呢?咱们明天就来说一说这个话题。 本文是 Spring 源码解读第七篇,浏览本系列后面文章有助于更好的了解本文: Spring 源码解读打算Spring 源码第一篇开整!配置文件是怎么加载的?Spring 源码第二弹!XML 文件解析流程Spring 源码第三弹!EntityResolver 是个什么鬼?Spring 源码第四弹!深刻了解 BeanDefinition手把手教你搭建 Spring 源码剖析环境1.DefaultListableBeanFactory要说 XmlBeanFactory 就不得不先说它的父类 DefaultListableBeanFactory,因为 XmlBeanFactory 中的大部分性能实际上在 DefaultListableBeanFactory 中就曾经提供好了,XmlBeanFactory 只是对 IO 流的读取做了一些定制而已。 DefaultListableBeanFactory 是一个残缺的、性能成熟的 IoC 容器,如果你的需要很简略,甚至能够间接应用 DefaultListableBeanFactory,如果你的需要比较复杂,那么通过扩大 DefaultListableBeanFactory 的性能也能够达到,能够说 DefaultListableBeanFactory 是整个 Spring IoC 容器的始祖。 ...

July 15, 2020 · 5 min · jiezi

小书MybatisPlus第4篇表格分页与下拉分页查询

本文为mybatis系列文档的第4篇,前三篇请拜访上面的网址。 小书MybatisPlus第1篇-整合SpringBoot疾速开始增删改查小书MybatisPlus第2篇-条件结构器的利用及总结小书MybatisPlus第3篇-自定义SQL一、让Spring Boot-MybatisPlus反对分页在曾经集成了Mybatis Plus的Spring我的项目中退出如下分页拦截器的配置,让MybatisPlus反对分页 @Configurationpublic class MybatisPlusConfiguration { //配置分页拦截器 @Bean public PaginationInterceptor paginationInterceptor() { return new PaginationInterceptor(); }}二、单表查问分页-表格分页@Testpublic void testSelect() { LambdaQueryWrapper<User> query = new LambdaQueryWrapper<>(); query.ge(User::getAge,10) //查问条件:年龄大于10 .orderByDesc(User::getAge); //依照年龄的倒序排序 Page<User> page = new Page<> (1,10); //查问第1页,每页10条数据 userMapper.selectPage(page,query); //page分页信息,query查问条件 System.out.println("总页数:"+ page.getPages()); System.out.println("总记录数:"+ page.getTotal()); // 分页返回的对象与传入的对象是同一个 List<User> list = page.getRecords(); list.forEach(System.out::println);}查问输入后果如下: 总页数:1总记录数:6User(id=3, name=Tom, age=28, email=test3@baomidou.com)User(id=5, name=Billie, age=24, email=test5@baomidou.com)User(id=4, name=Sandy, age=21, email=test4@baomidou.com)User(id=2, name=Jack, age=20, email=test2@baomidou.com)User(id=1, name=Jone, age=18, email=test1@baomidou.com)User(id=1280261858003038209, name=字母哥, age=18, email=null)在分页查问过程中,一共执行了两条SQL ...

July 15, 2020 · 1 min · jiezi

spring注解驱动开发7-属性注入

5.1 @Value能够应用字符串/SpEL/${}表达式 5.2 @PropertySource指定配置文件, 相当于之前的 <context:property-placeholder location="classpath:jdbc.properties"/>实例:bean @Value("张三"): 常量字符串;@Value("#{20+3}"): spEL表达式;@Value("#{${ds.age1}+${ds.age2}}"): spEL表达式;@Value("${ds.passwd}"): 读取配置文件;package com.niewj.bean;import lombok.Data;import org.springframework.beans.factory.annotation.Value;@Datapublic class TestDataSource { @Value("张三") private String name; @Value("#{20+3}") private int age; // 这种居然都能够!!! @Value("#{${ds.age1}+${ds.age2}}") private int ageSum; @Value("${ds.passwd}") private String passwd; @Value("${ds.url}") private String url; public TestDataSource() { } public TestDataSource(String name, String passwd, String url) { System.out.println("User-初始化!"); this.name = name; this.passwd = passwd; this.url = url; }}配置类: 留神 @PropertySource 是在配置类 PropertyReadConfig 这里标注的! ...

July 15, 2020 · 1 min · jiezi

spring注解驱动开发6-Spring的生命周期

(1). Bean的创立bean的创立实际上就是指 构造方法的调用; singleton(单例)bean -容器初始化时会事后调用, 除非标注了 @Lazy 注解指定懒加载(提早创立); prototype(原型)bean -当第一次调用 getBean办法时, 才会调用构造方法 (2). Bean的初始化和销毁-4种办法bean的初始化, 指的是在构造方法调用之后, 对象的一些初始化操作; bean的销毁, 指的是在spring容器敞开前, 对bean对象做的一些后续解决操作的调用; Bean的初始化和销毁有一下几种办法: (2.1) @Bean注解指定initMethod/destroyMethod;bean: 一般java类, 其中定义了 办法名: init close package com.niewj.bean;public class LifeTestBean1 { private String value; public LifeTestBean1(String value){ System.out.println("LifeTestBean1-初始化!"); this.value = value; } public void init(){ System.out.println("LifeTestBean1#init-调用!"); } public void close(){ System.out.println("LifeTestBean1#close-调用!"); }}Life1Config配置类中: 通过 @Bean 的属性 指定了初始化和销毁办法: initMethod="init"destroyMethod="close"package com.niewj.config;import com.niewj.bean.LifeTestBean1;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;@Configurationpublic class Life1Config { @Bean(value = "lifeBean1", initMethod = "init", destroyMethod = "close") public LifeTestBean1 lifeTestBean(){ return new LifeTestBean1("bean1"); }}测试用例: testLifecycle ...

July 14, 2020 · 4 min · jiezi

Dubbo基本概念

Dubbo: 三大外围能力:面向接口的RPC,智能容错和负载平衡,服务主动注册和发现。交互流程图: 容器,服务发布者,注册核心,服务消费者,监控核心。图中能够看到服务发布者基于容器启动,而后发布者向注册核心增加服务,而后消费者让注册核心订阅服务。(初始化实现)在收到注册核心返回的告诉后, 同步 调用近程服务。发布者和消费者同时被监视器监听。 Zookeeper: 是一种树形目录服务,反对变更推送,实用于Dubbo的注册核心。公布端应用@Service(protocol="")注解实现类,调用端应用@Reference注解调用接口。 服务发布者:包扫描的是实现接口目录。(因为被调用的是接口) <!--名称惟一--><dubbo:application name="dubbo_provider"/> <!-- 注册核心地址 --><dubbo:registry protocol="zookeeper" address="zookeeper://127.0.0.1:2181"/> <!-- 配置协定与端口 --><dubbo:protocol name="dubbo" port="20880"/> <!-- Service包扫描--><dubbo:annotation package="com.yangfan.dubbodemo.impl"/>服务消费者:包扫描的是控制器controller(因为被调用的是控制器) <!--Client包扫描--><dubbo:annotation package="com.yangfan.dubbodemo.controller"/><!--敞开启动时查看,可使启动程序变得随便。生产环境要设为true--><dubbo:consumer check:"false"/>dubbo反对的协定:dubbo、rmi、hessian、webservice、rest、redis等。dubbo协定应用场景:小数据大并发;消费者远多于提供者;不适宜大数据传输:如文件,音视频。dubbo反对的负载平衡:随机、轮询、起码沉闷数、一次性hash,默认为随机。配置:消费者或提供者任意一方配置即可。注解中的loadBalance属性。[random|] 解决加上Transactional注解时,dubbo无奈公布服务:起因:spring事务管制是基于动静代理,而jdk动静代理时创立的代理对象的包名为:com.sun.proxy.$Proxy,使得dubbo公布服务时包扫描无奈进行包匹配。解决办法:用cglib代替jdk proxy。即设置proxy-target-class为true,且指定Service增加interfaceClass指定所实现的接口。 <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true"/>近程服务调用 形式: RPC(基于socket),如:webservice、dubboHttp(基于tcp),springCloudHttp客户端: httpClientokHttpJDK原生URLConnectionSpring提供的RestTemplate封装了上述三种形式,默认应用jdk原生URLConnection。

July 14, 2020 · 1 min · jiezi

Spring配置类深度剖析总结篇手绘流程图可白嫖

生命太短暂,不要去做一些基本没有人想要的货色。本文已被 https://www.yourbatman.cn 收录,外面一并有Spring技术栈、MyBatis、JVM、中间件等小而美的专栏供以收费学习。关注公众号【BAT的乌托邦】一一击破,深刻把握,回绝浅尝辄止。 前言各位好,我是A哥。最近写了好几篇对于Spring @Configuration的文章,收录在Spring配置类专栏里,这是本公众号的第一个专栏(尽管CSDN里已有几百篇)。尽管写的过程很艰巨,但从评估反馈来看都是正向的,聊以刺激呗,比方这个小伙的“三宗最”让我听了很开心啊????:尽管每篇文章的浏览量堪忧,毕竟从第一篇文章我就对我本人的公众号定位了嘛:不求大量风行,只求小众共鸣。因为我晓得违心保持看上来系列文章(强依赖于上下文)的小伙伴还是比拟少的,但我置信一旦保持下来咱们的共同话题就多了,“臭味相投”嘛,是这样的吧~ 在这之前,CSDN里写过几百篇对于Spring的文章,然而总感觉体系性不够,东打一炮,西放一枪难免会有失连贯性。我始终认为大多时候技术的相关性、上下文上很必要的,仅靠一篇文章想把一个成型的知识点讲清楚简直没可能,所以那种容易成为快餐,过眼即忘。这不这次我抉择在公众号里做些成系列的专题,在CSDN的根底上,抽取精髓,去其糟粕,以本身能力尽量的做好每一个专栏,普惠于有须要的小伙伴。过程很干燥和很乏味,违心看上来的人也不会很多,能保持下来或者会被本人打动????。 作为第一个专栏的总结篇,个别的都是少文字多流程图,旨在起到一个总览的作用,也为不便疾速应酬面试~ 版本约定本文内容若没做非凡阐明,均基于以下版本: JDK:1.8Spring Framework:5.2.2.RELEASE注释本文以绘制流程图为主,特点是快,毛病是不详,辅以该专栏(关注公众号,进入该专栏。或点击顶部相干举荐中转)前几篇文章能够达到很好的成果。 相干类@Configuration:标注在类上,示意该类是个Full模式的配置类 自Spring 5.2.0版本后它加了个proxyBeanMethods属性来显示管制Full模式还是Lite模式,默认是true示意Full模式@Bean:标注在办法上,示意办法生成一个由Spring容器治理的BeanConfigurationClassPostProcessor:用于疏导解决@Configuration配置类的后置处理器。留神:它只是疏导解决,并不是理论解决ConfigurationClassUtils:外部工具类。用于判断组件是否是配置类,又或是Full模式/Lite模式,而后在bd元数据里打上标记 它还会解决一件小事:获取@Configuration配置类上标注的@Order排序值并放进bd里BeanMethod:外部应用的类。用于封装标注有@Bean注解的办法ConfigurationClass:外部应用的类。每一个@Configuration配置类都会被封装为它,外部会蕴含多个@Bean办法(BeanMethod)ConfigurationClassParser:解析@Configuration配置类,最终以ConfigurationClass对象的模式展现,并且填充它:因为一个配置类能够@Import导入另外一个(或者N多个)其它配置类,所以须要填充ConfigurationClassBeanDefinitionReader:外部应用的类。读取给定的曾经解析好的Set<ConfigurationClass>汇合,把外面的bd信息注册到BeanDefinitionRegistry里去(这里决定了bd的有序和无序相干问题)ConfigurationClassEnhancer:外部应用的类。配置类增强器,用于对@Configuration类(Full模式)应用CGLIB加强,生成一个代理子类字节码Class对象EnhancedConfiguration:被增强器加强过的配置类,都会主动的让实现此接口(理论是个BeanFactoryAware)接口SpringNamingPolicy:应用CGLIB生成字节码类名名称生成策略 -> 名称中会有BySpringCGLIB字样BeanFactoryAwareMethodInterceptor:CGLIB代理对象拦截器。作用:拦挡代理类的setBeanFactory()办法,给对应属性赋值BeanMethodInterceptor:CGLIB代理对象拦截器。作用:拦挡所有@Bean办法的执行,以反对能够通过间接调用@Bean办法来治理依赖关系(当然也反对FactoryBean模式)配置类解析流程图配置类的解析均是交由ConfigurationClassPostProcessor来疏导。在Spring Framework里(非Spring Boot)里,它是BeanDefinitionRegistryPostProcessor处理器的惟一实现类,用于疏导解决@Configuration配置类。解析入口是postProcessBeanDefinitionRegistry()办法,理论解决委托给了processConfigBeanDefinitions()办法。 配置类加强流程图如果一个配置类是Full模式,那么它就须要被CGLIB字节码晋升。加强动作委托给enhanceConfigurationClasses(beanFactory)去实现。 以上是疏导/调度的流程图,上面对字节码加强、理论拦挡实现流程进行细分形容。 生成增强子类字节码流程图针对于Full模式配置类的字节码生成,委托给ConfigurationClassEnhancer增强器去实现,最终失去一个CGLIB晋升过的子类Class字节码对象。字节码理论是由Enhancer生成,就不必再深刻了,那属于CGLIB(甚至ASM)的领域,很容易头晕,也并无必要。 拦截器执行流程图拦截器是实现加强理论逻辑的核心部件,因而它的执行流程须要引起器重。一共有两个“有用”的拦截器,别离画出。 BeanFactoryAwareMethodInterceptor拦挡流程图拦挡setBeanFactory()办法的执行 BeanMethodInterceptor拦挡流程图拦挡@Bean办法的执行 总结本文作为公众号首个专栏Spring配置类的总结篇,次要是对外围解决流程画图论述,适宜须要疾速了解的白嫖党,毕竟面试最喜爱问的就是让你说说执行流程之类的,因而实用性还是蛮高的,当前的专栏均会仿造此套路来玩。 对于Spring配置类这个专栏到这就全副完结了,在此也多谢各位在这期间给我的反馈,让我确定以及必定了这么坚持下去是有意义的,是被反对的,是可能帮忙到同仁们的。我公众号定位为专栏式学习,回绝浅尝抑止,诚邀你的关注,一起提高。 Tips:有小伙伴私信我说有没有入门级别的?答案是没有的。次要是感觉入门级文章网上太多了,趋同性很强,所以我这个别会篇进阶,有点工作教训/根底再看成果更佳关注A哥AuthorA哥(YourBatman)集体站点www.yourbatman.cnE-mailyourbatman@qq.com微 信fsx641385712沉闷平台 公众号BAT的乌托邦(ID:BAT-utopia)常识星球BAT的乌托邦每日文章举荐每日文章举荐

July 14, 2020 · 1 min · jiezi

SpringCloud-第八篇-Hystrix熔断机制五

1:雪崩效应概述多个微服务之间调用的时候,假如微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应工夫过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起零碎解体,所谓的“雪崩效应” 2:熔断机制概述熔断机制是应答雪崩效应的一种微服务链路爱护机制。当扇出链路的某个微服务不可用或者响应工夫太长时,会进行服务的降级,进而熔断该节点微服务的调用,疾速返回谬误的响应信息。当检测到该节点微服务调用响应失常后,复原调用链路。 在Spring Cloud框架里,熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的情况,当失败的调用到肯定阈值,缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是@HystrixCommand。 3: 熔断类型在Hystrix外面,熔断又分为三种状况:半熔断、熔断关上、熔断敞开 熔断关上:申请不再进行调用以后服务,外部设置时钟个别为MTTR(均匀故障解决工夫),当关上时长达到所设时钟则进入半熔断状态半熔断: 局部申请依据规定调用以后服务,如果申请胜利且合乎规定则认为以后服务恢复正常,敞开熔断熔断敞开: 熔断敞开不会对服务进行熔断4: 断路器图解 5:断路器在什么状况下开始起作用波及到断路器的三个重要参数:快照工夫窗、申请总数阀值、谬误百分比阀值。 快照工夫窗:断路器确定是否关上须要统计一些申请和谬误数据,而统计的工夫范畴就是快照工夫窗,默认为最近的10秒。申请总数阀值:在快照工夫窗内,必须满足申请总数阀值才有资格熔断。默认为20,意味着在10秒内,如果该hystrix命令的调用次数有余20次,即便所有的申请都超时或其余起因失败,断路器都不会关上。谬误百分比阀值:当申请总数在快照工夫窗内超过了阀值,比方产生了30次调用,如果在这30次调用中,有15次产生了超时异样,也就是超过50%的谬误百分比,在默认设定50%阀值状况下,这时候就会将断路器关上。6:断路器开启或者敞开的条件当满足肯定的阀值的时候(默认10秒内超过20个申请次数)当失败率达到肯定的时候(默认10秒内超过50%的申请失败)达到以上阀值,断路器将会开启当开启的时候,所有申请都不会进行转发一段时间之后(默认是5秒),这个时候断路器是半开状态,会让其中一个申请进行转发。如果胜利,断路器会敞开,若失败,持续开启。反复4和57:断路器关上之后再有申请调用的时候,将不会调用主逻辑,而是间接调用降级fallback。通过断路器,实现了主动地发现错误并将降级逻辑切换为主逻辑,缩小响应提早的成果。原来的主逻辑要如何复原呢?对于这一问题,hystrix也为咱们实现了主动复原性能。当断路器关上,对主逻辑进行熔断之后,hystrix会启动一个休眠工夫窗,在这个工夫窗内,降级逻辑是长期的成为主逻辑,当休眠工夫窗到期,断路器将进入半开状态,开释一次申请到原来的主逻辑上,如果此次申请失常返回,那么断路器将持续闭合,主逻辑复原,如果这次申请仍然有问题,断路器持续进入关上状态,休眠工夫窗从新计时。 8:线程隔离示意图 9:依赖隔离Hystrix提供了两种隔离策略:线程池隔离和信号量隔离,默认采纳线程池隔离。 线程池隔离Hystrix应用舱壁模式来实现线程池的隔离,它会为每一个Hystrix命令创立一个独立的线程池,不同服务通过应用不同线程池,彼此间将不受影响,这样就算某个在Hystrix命令包装下的依赖服务呈现提早过高的状况,也只是对该依赖服务的调用产生影响,而不会拖慢其余的服务这种形式须要为每个依赖的服务申请线程池,有肯定的资源耗费;通过线程池大小能够管制并发量,当线程池饱和时能够提前拒绝服务,避免依赖问题扩散。倡议线程池不要设置过大,否则大量梗塞线程有可能会拖慢服务器 线程池隔离的益处1:利用本身失去齐全的爱护,不会受不可控的依赖服务影响。2:能够无效的升高接入新服务的危险3:当依赖的服务从失效恢复失常后,它的线程池会被清理并且可能马上恢复健康的服务,相比之下容器级别的清理复原速度要慢得多。4:当依赖的服务呈现配置谬误的时候,线程池会疾速的反馈出此问题(通过失败次数、提早、超时、回绝等指标的减少状况)。同时,能够在不影响利用性能的状况下通过实时的动静属性刷新来解决它。5:当依赖的服务因实现机制调整等起因造成其性能呈现很大变动的时候,此时线程池的监控指标信息会反映出这样的变动。同时,能够通过实时动静刷新本身利用对依赖服务的阈值进行调整以适应依赖方的扭转。 10:信号量隔离线程隔离会带来线程开销,有些场景(比方无网络申请场景)可能会因为用开销换隔离得失相当,为此Hystrix提供了信号量隔离,当服务的并发数大于信号量阈值时将进入fallback。实现形式是应用一个原子计数器(或信号量)来记录以后有多少个线程在运行,申请过去了,先判断计数器的数值,若超过设置的最大线程个数则抛弃该类型的新申请,若不超过则执行计数操作申请来计数器+1。信号隔离与线程隔离最大不同在于执行依赖代码的线程仍然是申请线程;而线程池形式下业务申请线程和执行依赖的服务的线程不是同一个线程

July 14, 2020 · 1 min · jiezi

使用位运算值交换等方式反转java字符串共四种方法

在本文中,咱们将向您展现几种在Java中将String类型的字符串字母倒序的几种办法。 StringBuilder(str).reverse()char[]循环与值替换byte循环与值替换apache-commons-lang3如果是为了进行开发,请抉择StringBuilder(str).reverse()API。出于学习的目标,咱们能够钻研char[]和byte办法,其中波及到值调换和移位运算技术,这些技术对于理解StringBuilder(str).reverse()API黑匣子背地原理十分有帮忙。 1. StringBuilder(str).reverse()在Java中,咱们能够应用StringBuilder(str).reverse()使字符串字母倒序。 public class ReverseString1 { public static void main(String[] args) { String str = "Reverse a String in Java"; StringBuilder sb = new StringBuilder(str).reverse(); System.out.println(sb.toString()); }}输入后果 avaJ ni gnirtS a esreveR2.char[]首先,咱们将字符串转换为char数组,并一一循环char数组,并应用temp变量替换值。 public class ReverseString2 { public static void main(String[] args) { String str = "Hello World"; System.out.println(reverse(str)); // dlroW olleH } public static String reverse(String input) { if (input == null || input.length() < 0) throw new IllegalArgumentException("Please provide an input!"); char[] result = input.toCharArray(); int startIndex = 0; int endIndex = result.length - 1; char temp; for (; endIndex > startIndex; startIndex++, endIndex--) { temp = result[startIndex]; result[startIndex] = result[endIndex]; result[endIndex] = temp; } return new String(result); }}下面的算法须要5个循环(长度/ 2)来使字符串倒序“ Hello World”。 ...

July 14, 2020 · 4 min · jiezi

spring注解驱动开发4-Conditional注解

Conditional注解此注解用于配合 @Bean 注解, 限定Bean注解的条件: 如果满足条件, 响应的Bean才会放入容器; 否则不会! springboot大量应用此注解! Conditional注解能够在满足某条件时才初始化Bean, 条件就是实现了Condition接口的match办法的逻辑!本实例就是依据VM option运行时传入一个参数 -Dspring.profiles.active=xxx, 依据xxx是dev还是product来决定生成的对象User是 admin还是user01(1). VM options减少参数-Dspring.profiles.active=product(C:UsersweijuAppDataRoamingTyporatypora-user-imagesimage-20200713180310059.png) (2). 测试代码:@Testpublic void testConditinalBean() { ApplicationContext ctx = new AnnotationConfigApplicationContext(ConditionalConfig.class); Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println); Environment environment = ctx.getEnvironment(); String profile = environment.getProperty("spring.profiles.active"); System.out.println(profile); User user = ctx.getBean(User.class); System.out.println(user);}(3). 输入:User-初始化!conditionalConfiguProductproductUser{name='user01', passwd='realP@sswd', online=true}(4). ConditionalConfig.java:能够看到: ConditionalConfig中, 应用了注解: @Conditional({ConditionalDev.class}) @Conditional({ConditionalProduct.class}) 两个@Bean, 然而实际上初始化和输入的只有 product, 是-D参数的抉择!! package com.niewj.config;import com.niewj.bean.User;import com.niewj.condition.ConditionalDev;import com.niewj.condition.ConditionalProduct;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Conditional;import org.springframework.context.annotation.Configuration;@Configurationpublic class ConditionalConfig { @Conditional({ConditionalDev.class}) @Bean("uDev") public User userDev(){ return new User("admin", "admin", false); } @Conditional({ConditionalProduct.class}) @Bean("uProduct") public User userProduct(){ return new User("user01", "realP@sswd", true); }}(5). User.java实体类:package com.niewj.bean;import lombok.Data;@Datapublic class User { private String name; private String passwd; private boolean online ; public User(String name, String passwd, boolean online){ System.out.println("User-初始化!"); this.name = name; this.passwd = passwd; this.online = online; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", passwd='" + passwd + '\'' + ", online=" + online + '}'; }}(6). 外围: Condition接口实现ConditionalDev + ConditionalProduct ...

July 13, 2020 · 2 min · jiezi

spring注解驱动开发3-Bean名称自定义

获取bean:@Bean:ctx.getBean("person", Person.class);@Bean("personJson"):ctx.getBean("personJson", Person.class);括号里自定义bean名称: 如果没有定义, 默认是办法名(person) : @Bean("personJson") @Test public void testBeanName() { AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(ScopeConfig.class); Arrays.stream(ctx.getBeanDefinitionNames()).forEach(System.out::println); Person p = ctx.getBean("personJson", Person.class); System.out.println(p); }package com.niewj.config;import com.niewj.bean.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Lazy;import org.springframework.context.annotation.Scope;@Configurationpublic class ScopeConfig { /** * singleton单例默认是先初始化的; prototype 默认是提早初始化, 只有getBean才会初始化结构! * singleton单例的如果想提早初始化, 能够在@Bean同时加注解@Lazy * @return */ @Scope("singleton") @Lazy @Bean("personJson") public Person person() { return new Person("json", 22); }}

July 13, 2020 · 1 min · jiezi

spring注解驱动开发2-scope单例和prototype

package com.niewj.config;import com.niewj.bean.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.Lazy;import org.springframework.context.annotation.Scope;@Configurationpublic class ScopeConfig { /** * @return */ @Scope("singleton") @Lazy @Bean public Person person() { return new Person("json", 22); }}单例bean 默认是事后 初始化的;prototype 默认是提早初始化; 只有getBean才会初始化结构(调用构造方法)singleton单例的如果想提早初始化, 能够在@Bean同时加注解@Lazysingleton的不论获取几次, 只初始化一次;prototype的获取几次, 初始化几次! Bean的作用域: 残缺的是: singleton(默认)prototyperequest(Web)session(Web) 后两种用的少

July 13, 2020 · 1 min · jiezi

Spring注解驱动开发笔记ComponentScan

ComponentScan注解@ComponentScan 用法1:package com.niewj.config;import com.niewj.bean.Person;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.ComponentScan;import org.springframework.context.annotation.Configuration;import org.springframework.context.annotation.FilterType;import org.springframework.stereotype.Controller;@Configuration@ComponentScan("com.niewj")public class MainConfig { @Bean public Person person() { return new Person("張三", 22); }}(0). 惯例: @ComponentScan(value="com.niewj") (1). FilterType.ANNOTATION 只排除注解 value + excludeFilters @ComponentScan( // 只排除: value = "com.niewj", excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})(2).FilterType.ANNOTATION 只过滤注解: 须要加上 value + useDefaultFilters=false + includeFilters @ComponentScan( // 只蕴含: 必须要写 useDefaultFilters 否则不失效 value = "com.niewj", useDefaultFilters = false, includeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION, classes = Controller.class)})(3). FilterType.ASSIGNABLE_TYPE :制订具体的类型, includeFilters 和 excludeFilters 自身就是个数组! ...

July 13, 2020 · 2 min · jiezi

SpringCloud-第七篇-Hystrix工作流程四

1:工作流程图解 2:流程阐明创立一个 HystrixCommand 或 HystrixObservableCommand 实例来向其它组件收回操作申请,通过构造方法来创立实例。缓存判断查看缓存内是否有对应指令的后果,如果有的话,将缓存的后果间接以 Observable 对象的模式返回 断路器判断查看Circuit Breaker的状态。如果Circuit Breaker的状态为开启状态,Hystrix将不会执行对应指令,而是间接进入失败解决状态(图中8)。如果Circuit Breaker的状态为敞开状态,Hystrix会继续执行 线程池、工作队列、信号量的查看确认是否有足够的资源执行操作指令。当线程池和队列(或者是信号量,当不应用线程池隔离模式的时候)资源满的时候,Hystrix将不会执行对应指令并且会间接进入失败解决状态 HystrixObservableCommand.construct()和 HystrixCommand.run()如果资源短缺,Hystrix将会执行操作指令,调用最终都会到这两个办法:HystrixCommand.run()或HystrixObservableCommand.construct()。如果执行指令的工夫超时,执行线程会抛出 TimeoutException 异样。Hystrix会摈弃后果并间接进入失败解决状态。如果执行指令胜利,Hystrix会进行一系列的数据记录,而后返回执行的后果。 统计断路器的衰弱状况Hystrix会依据记录的数据来计算失败比率,一旦失败比率达到某一阈值将主动开启Circuit Breaker 降级如果在Command中实现了HystrixCommand.getFallback()办法(或HystrixObservableCommand. resumeWithFallback() 办法,Hystrix会返回对应办法的后果。如果没有实现这些办法的话,依然 Hystrix会返回一个空的 Observable 对象,并且能够通过 onError 来终止并处理错误。调用不同的办法返回不同的后果:execute(): 将会抛出异样queue(): 将会返回一个Future 对象,如果调用它的get()办法将会抛出异样observe()和 toObservable():都会返回上述的 Observable 对象 返回胜利如果Hystrix执行胜利,返回的响应取决于在步骤2中调用命令。execute():阻塞型办法,返回单个后果(或者抛出异样)queue():异步办法,返回一个 Future 对象,能够从中取出单个后果(或者抛出异样)observe():返回Observable 对象toObservable():返回Observable 对象

July 13, 2020 · 1 min · jiezi

Spring依赖查找中的常见异常

[TOC] BeansException的子接口异样类型触发条件(举例)场景举例NoSuchBeanDefinitionException当查找 Bean 不存在于 IoC 容器时BeanFactory#getBean ObjectFactory#getObjectNoUniqueBeanDefinitionException类型依赖查找时,IoC 容器存在多 个 Bean 实例BeanFactory#getBean(Class)BeanInstantiationException当 Bean 所对应的类型非具体类时BeanFactory#getBeanBeanCreationException当 Bean 初始化过程中Bean 初始化办法执行异样 时BeanDefinitionStoreException当 BeanDefinition 配置元信息非法 时XML 配置资源无奈关上时Rumenz.java实体类 package com.rumenz;import org.springframework.beans.factory.InitializingBean;public class Rumenz implements InitializingBean { private Integer id; private String name; @Override public void afterPropertiesSet() throws Exception { throw new Exception("初始化异样。。。。"); } @Override public String toString() { return "Rumenz{" + "id=" + id + ", name='" + name + '\'' + '}'; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; }}NoSuchBeanDefinitionException不存在要查找的Beanpackage com.rumenz;import java.util.Map;public class DemoApplication { public static void main(String[] args) { AnnotationConfigApplicationContext ac=new AnnotationConfigApplicationContext(); ac.register(DemoApplication.class); //不注册Rumenz ac.refresh(); //NoSuchBeanDefinitionException不存在要查找的Bean byBeanFactory(ac); ac.close(); } //NoSuchBeanDefinitionException不存在要查找的Bean private static void byBeanFactory(AnnotationConfigApplicationContext ac) { ac.getBean(Rumenz.class); }}输入 ...

July 12, 2020 · 2 min · jiezi

秒杀系统实战如何优雅的完成订单异步处理

前言我回来啦,前段时间忙得不可开交。这段时间终于能喘口气了,持续把之前挖的坑填起来。写完上一篇秒杀零碎(四):数据库与缓存双写一致性深入分析后,感觉文章深度一下子被我贬低了一些,当初构思新文章的时候,反而畏手畏脚,不敢轻易写了。对于将来文章内容的想法,我写在了本文的开端。 本文咱们来聊聊秒杀零碎中的订单异步解决。 本篇文章次要内容为何咱们须要对下订单采纳异步解决简略的订单异步解决实现非异步与异步下单接口的性能比照一个用户抢购体验更好的实现形式前文回顾零根底实现秒杀零碎(一):避免超卖零根底实现秒杀零碎(二):令牌桶限流 + 再谈超卖零根底实现秒杀零碎(三):抢购接口暗藏 + 单用户限度频率零根底实现秒杀零碎(四):数据库与缓存双写一致性深入分析零根底上手秒杀零碎(五):如何优雅的实现订单异步解决(本文)...欢送关注我的公众号关注最新的动静:后端技术漫谈我的项目源码再也不必放心看完文章不会代码实现啦: https://github.com/qqxx6661/m... 我发现该仓库的star数人不知;鬼不觉曾经超过100啦。 整个我的项目源码仓库应用了Maven + Springboot进行编写,并且上传了SQL文件,反对SpringBoot一键启动,不便大家调试。我致力将整个仓库的代码尽量做到整洁和可复用,在代码中我尽量做好每个办法的文档,并且尽量最小化办法的性能,比方上面这样: public interface StockService { /** * 查问库存:通过缓存查问库存 * 缓存命中:返回库存 * 缓存未命中:查询数据库写入缓存并返回 * @param id * @return */ Integer getStockCount(int id); /** * 获取残余库存:查数据库 * @param id * @return */ int getStockCountByDB(int id); /** * 获取残余库存: 查缓存 * @param id * @return */ Integer getStockCountByCache(int id); /** * 将库存插入缓存 * @param id * @return */ void setStockCountCache(int id, int count); /** * 删除库存缓存 * @param id */ void delStockCountCache(int id); /** * 依据库存 ID 查询数据库库存信息 * @param id * @return */ Stock getStockById(int id); /** * 依据库存 ID 查询数据库库存信息(乐观锁) * @param id * @return */ Stock getStockByIdForUpdate(int id); /** * 更新数据库库存信息 * @param stock * return */ int updateStockById(Stock stock); /** * 更新数据库库存信息(乐观锁) * @param stock * @return */ public int updateStockByOptimistic(Stock stock);}这样就像一个可拔插(plug-in)模块一样,尽量让小伙伴们能够复制粘贴,整合到本人的代码里,稍作批改适配便能够应用。 ...

July 12, 2020 · 3 min · jiezi

SpringCloud-第六篇-Hystrix参数配置三

1:概述 Hystrix应用Archaius作为配置属性的默认实现。官网配置文档: https://github.com/Netflix/Hy...每个属性有四个优先级,顺次增大: 1:代码的全局默认值2:动静全局默认属性能够应用全局属性文件来更改全局默认值。3:代码实例默认定义特定于实例的默认值,比方在HystrixCommand构造函数中设置的值4:动静实例属性能够动静设置实例特定的值,从而笼罩后面三个默认级别,格局是:hystrix.command.命令key.属性名称=值2:申请上下文1:requestCache.enabled 设置是否开启申请的缓存性能,默认true2:requestLog.enabled 设置是否开启申请的日志性能,默认true3:命令执行execution.isolation.strategy批示HystrixCommand.run()执行哪个隔离策略,选项:1:THREAD - 它在独自的线程上执行,并发申请受线程池中线程数的限度2:SEMAPHORE - 它在调用线程上执行,并发申请受信号计数的限度3:官网举荐应用线程隔离策略,默认也是依照线程隔离进行解决。4:信号量隔离的形式是限度了总的并发数,每一次申请过去,申请线程和调用依赖服务的线程是同一个线程,那么如果不波及近程RPC调用(没有网络开销)则应用信号量来隔离,更为轻量,开销更小。5:信号量的大小能够动静调整, 线程池大小不能够动静调整。6:配置示例: @HystrixCommand(fallbackMethod = "error", commandProperties = { @HystrixProperty(name="execution.isolation.strategy", value = "THREAD") })execution.isolation.thread.timeoutInMilliseconds示意申请线程总超时工夫,如果超过这个设置的工夫hystrix就会调用fallback办法。value的参数为毫秒,默认值为1000ms。 @HystrixCommand(fallbackMethod = "error", commandProperties = { @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "4000") })execution.timeout.enabled这个超时开关示意,当超时后是否触发fallback办法,默认为true。 execution.isolation.thread.interruptOnTimeout示意HystrixCommand.run()在产生超时时是否应该中断执行,默认true execution.isolation.thread.interruptOnCance示意HystrixCommand.run()在产生勾销时是否应该中断执行,默认false execution.isolation.semaphore.maxConcurrentRequests当隔离策略应用SEMAPHORE时,最大的并发申请量,如果申请超过这个最大值将回绝后续的申请,默认值为10 4:回退fallback.isolation.semaphore.maxConcurrentRequests设置HystrixCommand.getFallback()办法容许从调用线程进行申请的最大数量,默认10 如果达到最大并发限度,则随后的申请将被回绝,并抛出异样。fallback.enabled开启fallback性能,默认true 5:断路器circuitBreaker.enabled设置是否将应用断路器来跟踪健康状况,并且如果断路器跳闸则将其短路。默认true circuitBreaker.requestVolumeThreshold设置滚动窗口中将使电路跳闸的最小申请数量,默认20。熔断器在整个统计工夫内是否开启的阀值,每个熔断器默认保护10个bucket,每秒一个bucket,每个bucket记录胜利,失败,超时,回绝的状态,该阈值默认20次。也就是一个统计窗口工夫内(10秒钟)至多申请20次,熔断器才启动。 circuitBreaker.sleepWindowInMilliseconds熔断器默认工作工夫,默认值为5秒,熔断器中断请求5秒后会进入半关上状态,放局部流量过来重试,如果重试胜利则会恢复正常申请。 circuitBreaker.errorThresholdPercentage熔断器谬误阈值,默认为50%。当在一个工夫窗口内出错率超过50%后熔断器主动启动。熔断器启动后会主动转发到配置的fallbackMethod,进行降级解决。 circuitBreaker.forceOpen断路器强制开关,如果设置为true则示意强制关上熔断器,所有申请都会回绝,默认false circuitBreaker.forceClosed断路器强制开关,如果设置为true则示意强制敞开熔断器,所有申请都会容许,默认false 6: 度量指标metrics.rollingStats.timeInMilliseconds设置统计滚动窗口的持续时间,以毫秒为单位。默认10秒 metrics.rollingStats.numBuckets该属性设置滚动统计窗口分成的桶的数量。默认10 metrics.rollingPercentile.enabled示意执行提早是否应该跟踪和计算为百分比。如果被禁用,则所有汇总统计返回为-1。默认true metrics.rollingPercentile.timeInMilliseconds设置滚动窗口的持续时间,在该窗口中保留执行工夫以容许百分数计算(单位毫秒),默认1分钟。 metrics.rollingPercentile.numBuckets设置rollingPercentile窗口将被分成的桶的数量,默认6 metrics.rollingPercentile.bucketSize设置每个存储桶的最大执行次数,如果在执行屡次,将在桶的结尾重写。默认100例如,如果存储区大小设置为100,并示意10秒的存储区窗口,但在此期间产生500次执行,则只有最初100次执行将保留在该10秒存储区中 metrics.healthSnapshot.intervalInMilliseconds设置容许执行衰弱快照之间期待的工夫(以毫秒为单位)。默认500 7: ThreadPool配置coreSize线程池外围线程数,默认值为10。 MAXIMUMSIZE设置线程池大小,默认10 maxQueueSize配置线程池工作队列的大小,默认值为 -1。当应用 -1 时,SynchronousQueue将被应用,即意味着其实这个队列只是一个交换器,工作将被间接交给工作线程解决。如果工作线程有余,那工作将被回绝;如果应用任何正整数,LinkedBlockingQueue将被应用 queueSizeRejectionThreshold示意期待队列超过阈值后开始回绝线程申请,默认值为5,如果maxQueueSize为-1,则该属性生效。 keepAliveTimeMinutes设置流动放弃工夫,以分钟为单位,默认1分钟 allowMaximumSizeToDivergeFromCoreSize配置maximumSize失效。这个值能够等于或高于coreSize,默认false

July 12, 2020 · 1 min · jiezi

你还在用Autowired吗

聊看题目是不是吓一跳,用了好多年的@Autowired用错了吗?没那么夸大,本篇仅仅是讨论一下咱们Spring中最罕用的依赖注入形式,目前注入形式有三种,别离是:构造函数注入、办法注入、属性注入。咱们来看一小段代码 public class HelloController { @Autowired private BeanA a; private BeanB b; @Autowired public void setB(BeanB b) { this.b = b; } private final BeanC c; // @Autowired public HelloController(BeanC c) { this.c = c; }}是不是十分相熟,让我猜一下,大多数人用的是哪一种,我猜必定是第一种属性注入形式,因为我在学Java刚入门时也始终在用第一种。那为什么题目说你还在用@Autowired吗,是不须要了?还是有代替计划了,还是什么。如果还有不晓得@Autowired是作啥的同学请本人科普一下,其实在我写这篇文章之前,我也不晓得@Autowired的详情,我再提两个注解,@Required @Inject ,有没有同学用过,我自学Java时候刚刚接触Spring时还是用的Required,当年记得还探讨过 用@Autowired 还是@Required,也查问过材料,但那时没有记录的习惯,这也不是本篇探讨的重点。那重点是啥,当初如果不必@Autowired用什么,为什么不必了,有什么区别。读完本篇文章就能够学到。 学还是先看下面的那段代码,有没有发现第三种办法构造函数注入形式的@Autowired是被正文掉的,会不会有疑难,正文掉之后注入办法还能胜利吗?这个问题,或者说这类问题,我集体学习时都会有比拟明确的办法,就是《官网文档》,咱们一起看一下Spring Boot 2.0的 17. Spring Bean和依赖注入 您能够自在应用任何规范的Spring Framework技术来定义bean及其注入的依赖项。为简略起见,咱们常常发现应用 @ComponentScan(查找您的bean)和应用@Autowired(进行构造函数注入)成果很好。 如果依照下面的倡议结构代码(将应用程序类放在根包中),则能够增加@ComponentScan任何参数。您的所有应用程序组件(的@Component,@Service,@Repository,@Controller等)主动注册为春豆。 以下示例显示了一个@Service应用构造函数注入来获取所需RiskAssessorBean的Bean: package com.example.service;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;@Servicepublic class DatabaseAccountService implements AccountService { private final RiskAssessor riskAssessor; @Autowired public DatabaseAccountService(RiskAssessor riskAssessor) { this.riskAssessor = riskAssessor; } // ...}如果bean具备一个构造函数,则能够省略@Autowired,如以下示例所示: ...

July 11, 2020 · 1 min · jiezi

521我发誓读完本文再也不会担心Spring配置类问题了

当大潮退去,才晓得谁在裸泳。关注公众号【BAT的乌托邦】开启专栏式学习,回绝浅尝辄止。本文 https://www.yourbatman.cn 已收录,外面一并有Spring技术栈、MyBatis、中间件等小而美的专栏供以学习哦。 前言各位小伙伴大家好,我是A哥。本文对Spring @Configuration配置类持续进阶,尽管有点烧脑,但目标只有一个:为拿高薪备好弹药。如果说上篇文章曾经脑力有点“不适”了,那这里得先给你个下马威:本篇文章内容将更加的让你“感觉不适”。 读本文之前,为确保连贯性,倡议你移步先浏览上篇文章内容,中转电梯:你自我介绍说很懂Spring配置类,那你怎么解释这个景象?为什么有些时候我会倡议先浏览上篇文章,这的确是无奈之举。技术的内容个别都具备很强相关性,它是须要有Context上下文撑持的,所以花几分钟先理解相干内容成果更佳,磨刀不误砍柴工的情理大家都懂。同时呢,这也是写深度剖析类的技术文章的难堪之处:吃力反而不讨好,须要保持。 版本约定本文内容若没做非凡阐明,均基于以下版本: JDK:1.8Spring Framework:5.2.2.RELEASE注释上篇文章介绍了代理对象两个拦截器其中的前者,即BeanFactoryAwareMethodInterceptor,它会拦挡setBeanFactory()办法从而实现给代理类指定属性赋值。通过第一个拦截器的解说,你可能胜利“忽悠”很多面试官了,但仍旧不可能解释咱们最常应用中的这个纳闷:为何通过调用@Bean办法最终指向的仍旧是同一个Bean呢? 带着这个疑难,开始本文的陈诉。请系好安全带,筹备发车了... Spring配置类的应用误区依据不同的配置形式,展现不同状况。从Lite模式的应用产生误区,到应用Full模式解决问题,最初引出解释为何有此成果的起因剖析/源码解析。 Lite模式:谬误姿态配置类: public class AppConfig { @Bean public Son son() { Son son = new Son(); System.out.println("son created..." + son.hashCode()); return son; } @Bean public Parent parent() { Son son = son(); System.out.println("parent created...持有的Son是:" + son.hashCode()); return new Parent(son); }}运行程序: public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); AppConfig appConfig = context.getBean(AppConfig.class); System.out.println(appConfig); // bean状况 Son son = context.getBean(Son.class); Parent parent = context.getBean(Parent.class); System.out.println("容器内的Son实例:" + son.hashCode()); System.out.println("容器内Person持有的Son实例:" + parent.getSon().hashCode()); System.out.println(parent.getSon() == son);}运行后果: ...

July 10, 2020 · 5 min · jiezi

小书MybatisPlus第3篇自定义SQL

本文档为一个系列,后面章节: 小书MybatisPlus第1篇-整合SpringBoot疾速开始增删改查小书MybatisPlus第2篇-条件结构器的利用及总结书接上回,尽管Mybatis Plus帮咱们提供了大量的默认办法,但咱们为了实现多表关联查问,或者依据不同的查问条件传参,实现不同的动静SQL。在这种状况下咱们还是须要自定义SQL,不管怎样咱们须要首先通过配置指定Mapper.xml文件的存储地位。 mybatis-plus: mapper-locations: classpath*:/mapper/*Mapper.xml1、原始的自定义SQL办法笔者认为:将多表关联查问或动静SQL写在XML文件外面进行保护,大多数场景下依然是Mybatis最佳实际。单表的增删改查应用Mybatis Plus或者mybatis generator生成代码,是最佳实际。UserMapper 接口放在@MapperScan配置的扫描门路上面。这种办法是Mybatis 为咱们提供的,在Mybatis Plus外面依然能够持续应用,丝毫不耽搁!应用最原始的Mybatis SQL定义形式,在集成BaseMapper的根底上(mybatis plus),新定义一个接口办法findUser。public interface UserMapper extends BaseMapper<User> { List<User> findUser(@Param("name") String name, @Param("email") String email);}新定义一个UserMapper.xml,放在mybatis-plus.mapper-locations配置门路上面。上面的动静SQL示意: 当参数name不为null或空串的时候,AND name = #{name}条件失效当参数email不为null或空串的时候,AND email = #{email}条件失效<!--这个外面写动静SQL、多表关联查问都能够胜任--><select id="findUser" resultType="com.zimug.example.model.User"> SELECT id,name,age,email FROM user <trim prefix="WHERE" prefixOverrides="AND|OR" suffixOverrides="AND|OR"> <if test="name != null and name != '' " > AND name = #{name} </if> <if test="email != null and email != '' " > AND email= #{email} </if> </trim></select>应用测试 ...

July 10, 2020 · 1 min · jiezi

Spring-Data-JPA-使用探究

这里就滤过Spring Data JPA我的项目部署,不分明的能够看一下开源我的项目AutoAdmin。 Entity(表实体类)一个表对应一个实体类,这里以AutoAdmin的sys_user表为例: import cn.songhaiqing.autoadmin.base.BaseEntity;import org.hibernate.annotations.Where;import javax.persistence.Column;import javax.persistence.Entity;import javax.persistence.Table;@Entity@Table(name = "sys_user")@Where(clause = "deleted=0")public class SysUser extends BaseEntity { // 账号 @Column(name = "account") private String account; // 明码 @Column(name = "password") private String password; // 姓名 @Column(name = "name") private String name; // 明码加密盐 @Column(name = "salt") private String salt; public String getAccount() { return account; } public void setAccount(String account) { this.account = account; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getSalt() { return salt; } public void setSalt(String salt) { this.salt = salt; }}备注 ...

July 9, 2020 · 3 min · jiezi

配置类需要标注Configuration却不知原因那这次就不能给你涨薪喽

专一Java畛域分享、成长,回绝浅尝辄止。关注公众号【BAT的乌托邦】开启专栏式学习,回绝浅尝辄止。本文 https://www.yourbatman.cn 已收录,外面一并有Spring技术栈、MyBatis、中间件等小而美的专栏供以学习哦。 前言各位小伙伴大家好,我是A哥。这是继上篇文章:真懂Spring的@Configuration配置类?你可能自我感觉太良好 的原理/源码解释篇。依照本公众号的定位,原理个别跑不了,尽管很干燥,但还得做,毕竟做难事必有所得,真的把握了才有底气谈涨薪嘛。 Tips:鉴于常常有些同学无奈辨别某个性能/某项能力属于Spring Framework的还是Spring Boot,你能够参考文章里的【版本约定】目录,那里会阐明本文的版本依赖,也就是性能所属喽。比方本文内容它就属于Spring Framework,和Spring Boot木有关系。 版本约定本文内容若没做非凡阐明,均基于以下版本: JDK:1.8Spring Framework:5.2.2.RELEASE注释Spring的IoC就像个“大熔炉”,什么都当作Bean放在外面。然而,尽管它们都放在了一起,然而理论在性能上是有区别的,比方咱们相熟的BeanPostProcessor就属于后置处理器性能的Bean,还有本文要探讨的@Configuration配置Bean也属于一种非凡的组件。 判断一个Bean是否是Bean的后置处理器很不便,只需看它是否实现了BeanPostProcessor接口即可;那么如何去确定一个Bean是否是@Configuration配置Bean呢?若是,如何辨别是Full模式还是Lite模式呢?这便就是本文将要探讨的内容。 如何判断一个组件是否是@Configuration配置?首先须要明确:@Configuration配置前提必须是IoC治理的一个组件(也就是常说的Bean)。Spring应用BeanDefinitionRegistry注册核心治理着所有的Bean定义信息,那么对于这些Bean信息哪些属于@Configuration配置呢,这是须要甄选进去的。 判断一个Bean是否是@Configuration配置类这个逻辑对立交由ConfigurationClassUtils这个工具类去实现。 ConfigurationClassUtils工具类见名之意,它是和配置无关的一个工具类,提供几个动态工具办法供以应用。它是Spring 3.1新增,对于它的作用,官网给的解释是:用于标识@Configuration类的实用程序(Utilities)。它次要提供了一个办法:checkConfigurationClassCandidate()用于查看给定的Bean定义是否是配置类的候选对象(或者在配置/组件类中申明的嵌套组件类),并做相应的标记。 checkConfigurationClassCandidate()它是一个public static工具办法,用于判断某个Bean定义是否是@Configuration配置。 ConfigurationClassUtils: public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) { ... // 依据Bean定义信息,拿到器对应的注解元数据 AnnotationMetadata metadata = xxx; ... // 依据注解元数据判断该Bean定义是否是配置类。若是:那是Full模式还是Lite模式 Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName()); if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL); } else if (config != null || isConfigurationCandidate(metadata)) { beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE); } else { return false; } ... // 到这。它必定是一个残缺配置(Full or Lite) 这里进一步把@Order排序值放上去 Integer order = getOrder(metadata); if (order != null) { beanDef.setAttribute(ORDER_ATTRIBUTE, order); } return true; }步骤总结: ...

July 9, 2020 · 2 min · jiezi

结合SpEL使用Value基于配置文件或非配置的文件的值注入Spring-Boot

本文次要介绍Spring @Value 注解注入属性值的应用办法的剖析,文章通过示例代码十分具体地介绍,对于每个人的学习或工作都有肯定的参考学习价值 在应用spring框架的我的项目中,@Value是常常应用的注解之一。其性能是将与配置文件中的键对应的值调配给其带注解的属性。在日常应用中,咱们罕用的性能绝对简略。本文使您系统地理解@Value的用法。 @Value注入模式依据注入的内容起源,@ Value属性注入性能能够分为两种:通过配置文件进行属性注入和通过非配置文件进行属性注入。 非配置文件注入的类型如下: 注入一般字符串注入操作系统属性注射表白后果注入其余bean属性注入文件资源注入URL资源基于配置文件的注入首先,让咱们看一下配置文件中的数据注入,无论它是默认加载的application.properties还是自定义my.properties文档(须要@PropertySource额定加载)。例如:application.properties属性值以以下模式定义: user.name=adminmy.properties配置文件中定义的属性如下: user.password=pwd123而后,在bean中应用@Value,如下所示: @PropertySource("classpath:my.properties")@RestControllerpublic class ValueController { /** *Get in application.properties Properties configured in */ @Value("${user.name}") private String name; /** *Get in my.properties Configuration properties in */ @Value("${user.password}") private String password;}区别在于,在spring boot我的项目中,如果应用my.properties文件,则须要通过类中的@ PropertySource导入配置文件,而application.properties中的属性将主动加载。 同时,您不仅能够通过@Value注入单个属性,还能够采纳数组和列表的模式。例如,配置如下: tools=car,train,airplane能够通过以下形式注入它: /** *Injection array (automatically split according to ",") */@Value("${tools}")private String[] toolArray;/** *Injection list form (automatic segmentation based on "," and) */@Value("${tools}")private List<String> toolList;默认状况下,spring将以“,”宰割,并将其转换为相应的数组或列表。 基于非配置文件的注入在应用示例阐明基于非配置文件注入属性的实例之前,让咱们看一下SpEl。 ...

July 9, 2020 · 1 min · jiezi

太牛了这是我见过把微服务讲的最全最好的SpringCloud架构进阶

写在后面现如今微服务架构非常风行,而采纳微服务构建零碎也会带来更清晰的业务划分和可扩展性。同时,反对微服务的技术栈也是多种多样的,而Spring Cloud就是这些技术中的翘楚。作为新一代的服务框架,Spring Cloud提出的口号是开发“面向云环境的应用程序”,它为微服务架构提供了更加全面的技术支持。而且作为Spring的拳头我的项目,它也可能与Spring Framework、Spring Boot、Spring Data、Spring Batch等其余Spring我的项目完满交融,这些对于微服务而言是至关重要的。Spring Cloud的残缺技术组成:明天就给大家分享一份《Spring Cloud微服务架构进阶》电子版材料!转发+关注,而后私信回复关键字 “666” 即可取得电子书的收费支付形式!留神:技术类书籍是拿来获取常识的,不是拿来珍藏的,你失去了书籍不意味着你失去了常识,所以请不要失去书籍后就感觉沾沾自喜,要常常翻阅!常常翻阅!内容简介本书全面系统地介绍Spring Cloud及其在微服务架构中的利用。全书共13章:第1章介绍微服务架构相干的基本概念;2章介绍Spring Cloud中蕴含的组件以及Spring Cloud约定的上下文;第3章介绍Spring Cloud的根底: Spring Boot,包含如何构建一个Spring Boot服务、SpringBoot的配置文件和Actuator提供的监控治理端点;第4~13章具体解说Spring Cloud组件,包含Eureka (服务注册与发现)、OpenFeign (申明式RESTful客户端)、Hystrix (断路器)、Ribbon ( 客户端负载均衡器)、Gateway ( API网关)、Config ( 配置核心)、Stream (音讯驱动)、Bus (音讯总线)、Security (认证与受权)、Sleuth (服务链路追踪)。本书为读者揭开了Spring Cloud组件实现的技术底细,并提供了进阶利用的思路,可作为应用Spring Cloud进行微服务架构实际的参考书。第一章—微服务架构介绍微服务架构的呈现微服务架构的流派云原生与微服务近年来,微服务架构始终是互联网技术圈的热点之一,越来越多的互联网利用都采纳了微服务架构作为零碎构建的根底,很多新技术和理念如Docker、Kubernetes、 DevOps 、继续交付、ServiceMesh等也都在关注、反对和追随微服务架构的倒退。本章将会概要性地介绍微服务架构:包含微服务架构是如何演进的,微服务架构的次要流派,以后支流的云原生利用与微服务之间的关系等。第二章—Spring Cloud总览Spring Cloud架构Spring C1 oud个性本章将会对SpringCloud架构及相干组件进行初步介绍,而后介绍SpringCloud上下文和Spring Cloud的公共形象,这部分内容可能会在多个组件中呈现,所以此处的介绍并不依赖具体的组件。第三章—Spring Cloud的根底:SpringBootSpring Boot简介构建一个微服务Spring Boot配置文件本章第一大节对SpringBoot性能和特点进行概述,包含SpringBoot中的外围个性;第二大节将搭建一个Spring Boot我的项目来疾速理解根本开发流程;在第三大节中总结了Spring Boot的配置文件及利用。第四章—服务注册与发现:Eureka根底利用服务发现原理Fur eka Cli ent源码解析Fur eka Server源码解析进阶利用本章中,第一大节将对Eureka进行--个综合性的概述,同时搭建一-个Eureka利用的简略例子,以演示运行机制;第二大节将从宏观的角度对Eureka的整体架构进行概述,对Eureka各组件和组件间的行为进行介绍;第三大节将从源码的角度对EurekaClient的运行原理进行解析,剖析它与Eureka Server之间的交互行为;第四大节将对Euerka Server的源码进行解说,剖析其如何在微服务架构中施展服务注册核心的作用;在第五大节将对Eureka中的配置属性和高级个性进行解说。第五章—申明式RESTful客户端:Spring Cloud OpenFeign根底利用源码剖析进阶利用在本章中,第一大节次要解说了微服务交互的常见形式以及OpenFeign的根底利用;第二大节对OpenFeign的源码进行了剖析,别离讲述了动静注册Spring实例定义( BeanDefinition)、实例初始化和函数调用与网络申请三局部的代码实现逻辑;第三大节则介绍了OpenFeign相干的高级进阶用法。第六章—断路器:Hystrix根底利用Hystrix原理源码解析进阶利用本章中,第一大节将会搭建用于演示Hystrix服务断路作用的简略例子;第二大节将会对Hystrix中相干术语和设计原理进行介绍;第三大节将从源码的角度剖析Hystrix的实现机制和运行原理;第四大节将对Hystrix中的配置属性和高级个性进行介绍。私信回复关键字 “666” 即可取得《Spring Cloud微服务架构进阶》电子书完整版的收费支付形式!**留神:技术类书籍是拿来获取常识的,不是拿来珍藏的,你失去了书籍不意味着你失去了常识,所以请不要失去书籍后就感觉沾沾自喜,要常常翻阅!常常翻阅!第七章—客户端负载均衡器:Spring Cloud Netflix Ribbon负载平衡根底利用源码剖析进阶利用本章的第一大节次要解说了负载平衡的相干概念和实现;第二大节则展现了应用Ribbon的一些代码示例,解说了Ribbon 的根本应用办法;第三大节次要是Ribbon的实现原理和代码详解;第四大节解说了无关Ribbon的进阶利用。第八章—API网关:Spring Cloud GatewaySpring Cloud Gateway介绍根底利用源码解析利用进阶本章第一局部将会介绍Spring Cloud Gateway的相干个性;第二局部为根底利用,通过网关服务和用户服务示例,解说SpringCloudGateway的基本功能;第三局部将会联合源码解说Spring Cloud Gateway基本功能;最初是Spring Cloud Gateway的进阶学习,介绍如何应用限流机制和熔断降级等性能。第九章—配置核心:Spring Cloud Config根底利用源码解析利用进阶本章第一大节将会实现一个简略的配置核心,包含配置服务器、客户端和Git仓库,以根底利用的案例,引出微服务配置核心的次要性能;第二大节首先解说配置核心的工作,而后介绍配置核心的两个局部:配置客户端和配置服务器,联合源码介绍这两局部次要性能的实现;最初是配置核心的利用进阶,介绍配置核心的其余高级个性,如平安爱护、加密解密、多配置仓库等。第十章—音讯驱动:Spring Cloud Stream音讯队列根底利用源码剖析进阶利用本章的第--大节次要解说了音讯队列的概念;第二大节次要解说了Stream的根底利用,给出相干的代码示例;第三大节则次要解说了Stream框架的实现原理和源码剖析;第四大节是Stream的进阶利用教程。第十一章—音讯总线:SPring Cloud Bus根底利用源码解析利用进阶本章第一大节由配置核心的动静更新作为根底利用的案例,引入Spring Cloud Bus 的利用,示例如何革新第9章中的配置服务中心;第二大节将会对Spring Cloud Bus的次要性能:事件的订阅与公布、事件监听的具体实现,联合源码进行剖析;第三大节是利用进阶局部,定制-一个注册事件,并实现事件的监听和发动。第十二章— 认证与受权:Spring Cloud Security根底利用整体架构源码解析进阶利用在本章的第一大节将对SpringCloudSecurity中的前置常识0Auth2和JWT进行介绍,搭建一个基于Spring Cloud Security的认证和受权服务项目进行演示;第二大节将对SpringCloud Security中的整体设计构造进行概括;第三大节从源码角度别离对Spring Security、Spring Security OAuth2中的设计和实现进行解说;第四大节对Spring Security和Spring SecurityOAuth2中的配置属性进行解说。第十三章—— 服务链路追踪:Spring Cloud Sleuth链路监控组件简介根底利用本章第一大节将介绍微服务架构中链路监控相干的组件;第二大节介绍链路监控组件的根底利用,介绍Spring Cloud Sleuth的两种实际形式:独立应用和整合Zipkin。整合Zipkin时,又有两种不同的通信形式用于发送链路信息,将会联合示例场景解说具体的利用办法。私信回复关键字 “666” 即可取得《Spring Cloud微服务架构进阶》电子书完整版的收费支付形式!**留神:技术类书籍是拿来获取常识的,不是拿来珍藏的,你失去了书籍不意味着你失去了常识,所以请不要失去书籍后就感觉沾沾自喜,要常常翻阅!常常翻阅!目录关注一波,而后si信回复关键字 “666”或者“书籍” 即可取得《Spring Cloud微服务架构进阶》电子书完整版的收费支付形式!**珍藏 ...

July 8, 2020 · 1 min · jiezi

基于SpringBoot-AOP面向切面编程实现Redis分布式锁

基于SpringBoot AOP面向切面编程实现Redis分布式锁基于SpringBoot AOP面向切面编程实现Redis分布式锁基于SpringBoot AOP面向切面编程实现Redis分布式锁 锁定的目标是确保相互排斥其访问的资源。实际上,此资源通常是字符串。使用redis实现锁主要是将资源放入redis中并利用其原子性。当其他线程访问时,如果Redis中已经存在此资源,则不允许进行某些后续操作。 Spring Boot通过RedisTemplate使用Redis,在实际使用过程中,分布式锁可以在封装后在方法级别使用,这样使用起来就更方便了,无需到处获取和释放锁。 首先,定义一个注解:@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited public @interface RedisLock { //锁定的资源,redis的键    String value() default "default"; //锁定保持时间(以毫秒为单位)     long keepMills() default 30000; //失败时执行的操作    LockFailAction action() default LockFailAction.CONTINUE; //失败时执行的操作--枚举    public enum LockFailAction{         GIVEUP,         CONTINUE;     } //重试的间隔    long sleepMills() default 200; //重试次数    int retryTimes() default 5; }具有分布式锁的Bean@Configuration @AutoConfigureAfter(RedisAutoConfiguration.class)public class DistributedLockAutoConfiguration {    @Bean    @ConditionalOnBean(RedisTemplate.class)    public DistributedLock redisDistributedLock(RedisTemplate redisTemplate){         return new RedisDistributedLock(redisTemplate);    }}面向切面编程-定义切面@Aspect @Configuration @ConditionalOnClass(DistributedLock.class) @AutoConfigureAfter(DistributedLockAutoConfiguration.class) public class DistributedLockAspectConfiguration {    private final Logger logger = LoggerFactory.getLogger(DistributedLockAspectConfiguration.class);    @Autowired     private DistributedLock distributedLock;    @Pointcut("@annotation(com.itopener.lock.redis.spring.boot.autoconfigure.annotations.RedisLock)")     private void lockPoint(){    }    @Around("lockPoint()")     public Object around(ProceedingJoinPoint pjp) throws Throwable{         Method method = ((MethodSignature) pjp.getSignature()).getMethod();         RedisLock redisLock = method.getAnnotation(RedisLock.class);         String key = redisLock.value();         if(StringUtils.isEmpty(key)){             Object\[\] args = pjp.getArgs();             key = Arrays.toString(args);         }         int retryTimes = redisLock.action().equals(LockFailAction.CONTINUE) ? redisLock.retryTimes() : 0; //获取分布式锁         boolean lock = distributedLock.lock(key, redisLock.keepMills(), retryTimes, redisLock.sleepMills());         if(!lock) {             logger.debug("get lock failed : " + key);             return null;         } //执行方法之后,释放分布式锁        logger.debug("get lock success : " + key);         try {             return pjp.proceed(); //执行方法        } catch (Exception e) {             logger.error("execute locked method occured an exception", e);         } finally {             boolean releaseResult = distributedLock.releaseLock(key); //释放分布式锁            logger.debug("release lock :" + key + (releaseResult ?" success" : "failed"));         }         return null;     } }使用方法进入该方法时,占用分布式锁,方法执行完成时,释放分布式锁使用同一个资源,如your-custom-service-redis-key的多个函数,抢占同一个锁。谁抢到谁先执行。@RedisLock(value="your-custom-service-redis-key")public void serviceMethod(){ //正常写方法实现}欢迎关注我的博客,里面有很多精品合集本文转载注明出处(必须带连接,不能只转文字):字母哥博客。觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。 ...

July 8, 2020 · 2 min · jiezi

Spring-bean的作用域

在默认情况下,Spring应用上下文中所有bean都是作为以单列(singleton)的形式创建的。也就是说,不管给定的一个bean被注入到其它bean多少次,每次所注入的都是同一个实例。 在大多数情况下,单例bean是很理想的方案。初始化和垃圾回收对象实例所带来的成本只留给一些小规模任务,在这些任务中,让对象保持无状态并且在应用上下文中反复重用这些对象可能并不合理。 有时候,可能会发现,你所使用的类是易变的(mutable),它们会保持一些状态,因此重用是不安全的。在这种情况下,将class声明为单例的bean就不是什么好主意了,因为对象会被污染,稍后重用的时候会出现意想不到的问题。 Spring定义了多种作用域,可以基于这些作用域创建bean,包括: 单例(Singleton):在整个应用中,只创建bean的一个实例原型(Prototype):每次注入或者通过Spring应用

July 7, 2020 · 1 min · jiezi

小书MybatisPlus第2篇条件构造器的应用及总结

一、条件构造器WrapperMybatis Plus为我们提供了如下的一些条件构造器,我们可以利用它们实现查询条件、删除条件、更新条件的构造。 条件构造器用于给如下的Mapper方法传参,通常情况下: updateWrapper用于给update方法传条件参数queryWrapper用于给delete和select方法传参public interface BaseMapper<T> extends Mapper<T> { int delete(@Param("ew") Wrapper<T> wrapper); int update(@Param("et") T entity, @Param("ew") Wrapper<T> updateWrapper); T selectOne(@Param("ew") Wrapper<T> queryWrapper); Integer selectCount(@Param("ew") Wrapper<T> queryWrapper); List<T> selectList(@Param("ew") Wrapper<T> queryWrapper); List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper); List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper); IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper); IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);}举例一:like 条件构造String name = "字母"; //name不为空String email = ""; //email为空串QueryWrapper<User> query = new QueryWrapper<>();query.like(StringUtils.isNotEmpty(name), "name", name) //因为email为空串,该条件未生效 .like(StringUtils.isNotEmpty(email), "email", email); List<User> list = userMapper.selectList(query);list.forEach(System.out::println);QueryWrapper是查询条件构造器,like是一种条件构造函数,还有很多的条件构造函数。请参考:条件构造器 ...

July 7, 2020 · 1 min · jiezi

性能测试之jmeter逻辑控制种类详解一

逻辑控制器介绍Jmeter逻辑控制可以对元件的执行逻辑进行控制,除Once only Controller仅一次控制器以外,其他控制器都可以可以嵌套其他种类的控制器,下面是jmeter5.3支持的控制器种类,我们分别对其进行介绍 Simple ControllerSimple Controller用来指定一个执行单元,他不改变元件的执行顺序,主要作用是用来组织采样器和其他的逻辑控制器 简单控制器只有名称和注释. 比如说我们可以用它组织模块,在各个模块里在进行请求的发送,这样看起来各模块比较独立,好维护一些。 Include ControllerInclude Controller用来导入外部的测试片段,这个片段并不是完整的测试计划,导入的这个测试计划是不能有线程组的,只能包含简单的控制器以及控制器下的元件。相当于执行了封装了业务操作到的单元,相当于将一些通用的功能逻辑封装,在这里进行调用。 Runtime Controller 用来控制器子元件的执行时长,单位是秒 Switch ControllerSwitch Controller条件控制器,类似于编程语言里的switch语句 比如由上图的配置,Switch Controller下面有两个java request,jmeter会给下面的java请求进行编号,第一个为0,第二个为1以此类推 如果上面switch Value设置0,那么第一个java request就会执行。如果指定的编号超出了控制器下面的节点数,或者没有指定,则会默认运行第一个节点。 Switch Controller也可以指定取样器的名称,名称匹配时大小写以及空格都铭感,如果匹配不上则不运行取样器。 While ControllerWhile条件控制器,其节点下的元件将一直运行知道while条件为false Condition:接受表达式与变量,还可以输入常量,也就是jmeter内置的默认值 LAST:当循环前有取样器失败则不进入循环 BLANK:当循环中有取样器失败后停止 Otherwise:当判断条件为false停止循环 Interleave ControllerInterleave Controller交替控制器,其节点下的取样器会交替执行, 假设我们有如上的线程组和交替控制器的配置,线程组下面有一个请求3,交替控制器下面有请求1和请求2,线程组配置一个线程循环且循环三次,执行以下我们来看看请求的执行顺序。 循环三次,总共有六个请求发出,也就是说每一次循环会执行线程组下面的请求和交替控制器里的一个请求。 <img src="https://zyjblog.oss-cn-beijing.aliyuncs.com/1593947944.jpg" style="zoom: 25%;" /> 下篇文章我们将介绍剩余的逻辑控制器 欢迎大家去 我的博客 瞅瞅,里面有更多关于测试实战的内容哦!!

July 7, 2020 · 1 min · jiezi

Spring-bean的生命周期

在传统的Java应用中,bean的生命周期很简单。使用Java关键字new进行bean实例化,然后该bean就可以使用了。一旦该bean不再被使用,则由Java自动进行垃圾回收。 相比之下,Spring容器中的bean的生命周期就显得相对复杂多了。正确理解Spring bean的生命周期非常重要。因为你或许要利用Spring提供的扩展点来自定义bean的创建过程。下图展示了bean装载到Spring应用上下文的一个典型的生命周期过程。 如图所示,bean在Spring容器中从创建到销毁经历了若干阶段,每一阶段都可以针对Spring如何管理Bean进行个性化定制。在bean准备就绪之前,bean工厂执行了以下启动步骤: Spring对bean进行实例化;Spring将值和bean的引用注入到bean对应的属性中;如果bean实现了BeanNameAware接口,Spring将bean的ID传递给setBeanName()方法;如果bean实现了BeanFactoryAware接口,Spring将调用setBeanFactory()方法,将BeanFactory容器实列传入;如果bean实现了ApplicationContextAware接口,Spring将调用setApplicationContext()方法,将bean所在的应用上下文的引用传入进来;如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessBeforeInitialization()方法;如果bean实现了InitializingBean接口,Spring将调用它们的afterPropertiesSet()方法。类似地,如果bean使用init-method声明了初始化方法,该方法也会被调用;如果bean实现了BeanPostProcessor接口,Spring将调用它们的postProcessAfterInitialization()方法;此时,bean已经准备就绪,可以被应用程序使用了,它们将一直驻留在应用上下文中,直到该应用上下文被销毁;如果bean实现了DisposableBean接口,Spring将调用它的destroy()接口方法。同样,如果bean使用destroy-method声明了销毁方法,该方法也会被调用。参考:《Spring In Action》

July 6, 2020 · 1 min · jiezi

面试必杀技讲一讲Spring中的循环依赖

本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring中的对象跟Bean,你知道Spring怎么创建对象的吗? 这篇文章,我们来谈一谈Spring中的属性注入 Spring中AOP相关的API及源码解析,原来AOP是这样子的 你知道Spring是怎么将AOP应用到Bean的生命周期中的吗? 推荐阅读: Spring官网阅读 | 总结篇 Spring杂谈 本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础! ” 前言Spring中的循环依赖一直是Spring中一个很重要的话题,一方面是因为源码中为了解决循环依赖做了很多处理,另外一方面是因为面试的时候,如果问到Spring中比较高阶的问题,那么循环依赖必定逃不掉。如果你回答得好,那么这就是你的必杀技,反正,那就是面试官的必杀技,这也是取这个标题的原因,当然,本文的目的是为了让你在之后的所有面试中能多一个必杀技,专门用来绝杀面试官! 本文的核心思想就是, 当面试官问: “请讲一讲Spring中的循环依赖。”的时候, 我们到底该怎么回答? 主要分下面几点 什么是循环依赖?什么情况下循环依赖可以被处理?Spring是如何解决的循环依赖?同时本文希望纠正几个目前业界内经常出现的几个关于循环依赖的错误的说法 只有在setter方式注入的情况下,循环依赖才能解决(错)三级缓存的目的是为了提高效率(错)OK,铺垫已经做完了,接下来我们开始正文 什么是循环依赖?从字面上来理解就是A依赖B的同时B也依赖了A,就像下面这样 image-20200705175322521 体现到代码层次就是这个样子 @Componentpublic class A { // A中注入了B @Autowired private B b;}@Componentpublic class B { // B中也注入了A @Autowired private A a;}当然,这是最常见的一种循环依赖,比较特殊的还有 // 自己依赖自己@Componentpublic class A { // A中注入了A @Autowired private A a;}虽然体现形式不一样,但是实际上都是同一个问题----->循环依赖 什么情况下循环依赖可以被处理?在回答这个问题之前首先要明确一点,Spring解决循环依赖是有前置条件的 出现循环依赖的Bean必须要是单例依赖注入的方式不能全是构造器注入的方式(很多博客上说,只能解决setter方法的循环依赖,这是错误的)其中第一点应该很好理解,第二点:不能全是构造器注入是什么意思呢?我们还是用代码说话 @Componentpublic class A {// @Autowired// private B b; public A(B b) { }}@Componentpublic class B {// @Autowired// private A a; public B(A a){ }}在上面的例子中,A中注入B的方式是通过构造器,B中注入A的方式也是通过构造器,这个时候循环依赖是无法被解决,如果你的项目中有两个这样相互依赖的Bean,在启动时就会报出以下错误: ...

July 6, 2020 · 3 min · jiezi

SpringCloud-第四篇-Hystrix快速上手一

1:Hystrix简介1.1:分布式系统面临的问题复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。 对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。1.2:Hystrix是什么Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。 1.3:Hystrix能干什么Hystrix能做很多事情,主要有: 服务隔离、降级、熔断、限流、快速失败请求合并、请求缓存接近实时的监控1.4:Hystrix的设计原则防止任何单独的依赖使用所有的容器(如Tomcat)用户线程切断负载并快速失败,而不是排队尽可能提供回退以保护用户免受故障使用隔离技术(例如舱壁,泳道和断路器模式)来限制任何一个依赖的影响通过接近实时的指标,监控和警报,优化发现时间通过配置更改的低延迟传播,优化恢复时间防止各种客户端执行失败,而不仅仅是网络通信2: HelloWord加入依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId></dependency>官方示例https://github.com/Netflix/Hy... 3:基本使用3.1:HystrixCommand和HystrixObservableCommand两者主要区别是: 1:前者的命令逻辑写在run();后者的命令逻辑写在construct()2:前者的run()是由新创建的线程执行;后者的construct()是由调用程序线程执行3:前者一个实例只能向调用程序发送单条数据,比如上面例子中run()只能返回一个String结果;后者一个实例可以顺序发送多条数据,可以顺序调用多个onNext(),便实现了向调用程序发送多条数据3.2:命令执行方法execute()、queue()、observe()、toObservable()这4个方法用来触发执行run()/construct(),一个实例只能执行一次这4个方法,注意HystrixObservableCommand没有execute()和queue()。 1:execute():以同步堵塞方式执行run()。调用execute()后,Hystrix先创建一个新线程运行run(),接着调用程序要在execute()调用处一直堵塞着,直到run()运行完成2:queue():以异步非堵塞方式执行run()。一调用queue()就直接返回一个Future对象,同时hystrix创建一个新线程运行run(),调用程序通过Future.get()拿到run()的返回结果,而Future.get()是堵塞执行的3:observe():事件注册前执行run()/construct()。第一步是事件注册前,先调用observe()自动触发执行run()/construct()(如果继承的是HystrixCommand,hystrix将创建新线程非堵塞执行run();如果继承的是HystrixObservableCommand,将以调用程序线程堵塞执行construct())第二步是从observe()返回后调用程序调用subscribe()完成事件注册,如果run()/construct()执行成功则触发onNext()和onCompleted(),如果执行异常则触发onError() 4:toObservable():事件注册后执行run()/construct()。第一步是事件注册前,一调用toObservable()方法就直接返回一个Observable<String>对象第二步调用subscribe()完成事件注册后自动触发执行run()/construct()(如果继承的是HystrixCommand,hystrix将创建新线程非堵塞执行run(),调用程序不必等待run();如果继承的是HystrixObservableCommand,将以调用程序线程堵塞执行construct(),调用程序等待construct()执行完才能继续往下走),如果run()/construct()执行成功则触发onNext()和onCompleted(),如果执行异常则触发onError() 3.3:命令名称默认情况下,命令名是从类名派生的:getClass().getSimpleName();要明确地定义名字,通过HystrixCommand或HystrixObservableCommand构造函数传入,例如: super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey(" HelloWorld1")) .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")) .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));1:GroupKey 是 HystrixCommand 不可缺少的配置,其它配置均为可选,例如:super(HystrixCommandGroupKey.Factory.asKey("HelloWorld1"));HystrixCommandGroupKey 的作用主要有两个:一是起到分组监控、报警的作用;二是在不配置 HystrixThreadPoolKey 的情况下,起到分组线程池的作用。即默认使用 HystrixCommandGroupKey 去命名线程池。使用同一个 HystrixCommandGroupKey 且没有自定义 HystrixThreadPoolKey 的 HystrixCommand 将使用同一个线程池。 2:commandKey:命令的标识名称3:ThreadPoolKey:线程池的标识名称3.4:Command Thread-Pool设置虽然 HystrixCommandGroupKey 可以起到隔离线程池的作用,但是无法起到对线程池进行精细配置的作用。所以这里就需要线程池进行配置,例如: super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorld1"))  .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("MyThreadPool")) .andThreadPoolPropertiesDefaults( .HystrixThreadPoolProperties.Setter() .withCoreSize(20) .withKeepAliveTimeMinutes(1) .withMaxQueueSize(-1) ) .andCommandPropertiesDefaults( HystrixCommandProperties.Setter().withExecutionTimeoutInMilliseconds(100))1:andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("MyThreadPool")) 这是配置 ThreadPoolKey。如果需要在同一个 GroupKey 下面配置不同的 ThreadPool 就需要这个配置。2:andThreadPoolPropertiesDefaults 表示设置线程池默认的属性值,包括:(1)withCoreSize(20) 用来配置线程池大小,不配置的话使用的默认值是 10。(2)withKeepAliveTimeMinutes(1) 用来配置核心线程数空闲时 keep alive 的时长,默认为 1 mins,一般不需要修改。(3)withMaxQueueSize(-1) 配置线程池任务队列的大小,默认值为 -1。当使用 -1 时,SynchronousQueue将被使用,即意味着其实这个队列只是一个交换器,任务将被直接交给工作线程处理。如果工作线程不足,那任务将被拒绝;如果使用任何正整数,LinkedBlockingQueue将被使用 3.5:使用编写完 自己的Command 之后,使用的时候每次都需要 new 一个新对象,再调用 execute() 方法。注意,不要调用 run() 方法,否则熔断、隔离等功能是不生效的 3.6:错误传播run()里面抛出的HystrixBadRequestException只用做计数,方法抛出的所有其它异常都作为失败,触发getFallback()和断路器逻辑。你可以包装你想要抛出的异常,HystrixBadRequestException适用的情况,如举报非法参数或非系统故障,不会计入失败的指标,不会触发回退逻辑。 3.7:快速失败快速失败就是指没有重写getFallback,遇到异常后直接抛出,程序停止运行 ...

July 6, 2020 · 1 min · jiezi

什么是DevOps该如何正确的在企业内进行实践

传统IT技术团队中通常都有多个独立的组织-开发团队、测试团队和运维团队。开发团队进行软件开发、测试团队进行软件测试,运维团队致力于部署,负载平衡和发布管理。 他们之间的职能有时重叠、有时依赖、有时候会冲突。 因为衔接与沟通问题,有时会导致开发-测试-发布的周期太长了,特别是对于互联网公司,时间就是生命。从而需要建立一支由开发,质量保证和运营组成的混合专业知识的团队的需求,并引入了DevOps来弥合团队之间的鸿沟,帮助他们快速而可靠地操作和开发应用程序。 一、什么是DevOps?顾名思义,Devops是“开发”与“运维”的缩写。它定义了一组流程,这些流程通过在开发和运维团队之间进行协作带来组织文化上的转变。它包含四个关键组件:协作,实践,文化和工具。 二、为什么我们需要DevOps?DevOps带来更高水平的协作和速度,使组织提高生产力,降低运营成本以有效地为客户提供服务并保持市场竞争力。它还有助于更快地发布产品,管理计划外的工作,更快地捕获和解决关键问题。 三、DevOps实践 独立devops团队DevOps使用各种工具,例如docker、k8s、Jenkins,Terraform,Nagios,Grafana,Prometheus或Splunk等,组织应建立一个集中部门来创建和操作这些工具。这个集中管理部门负责开发团队中的敏捷实践设置,该团队将研究新的工具并坚持使用,并提供指导程序和培训以实施DevOps。该部门核心工作内容是追求测试、集成、部署的自动化,减少人员参与、降低沟通成本、弥合技术团队之间的鸿沟。 持续集成(CI) CI是一种软件开发实践,可改善团队之间的协作并有助于构建高质量的软件。开发团队会定期检入存储库中的代码更改,CI会执行自动构建和测试以验证代码质量。持续集成所采用的实践可以在早期阶段快速检测出软件开发生命周期(SDLC)的挑战,从而有助于开发团队在开发阶段本身解决问题。 持续交付/部署(CD) 部署过程具有不同的阶段,例如开发→构建→验证→构建版本控制→部署→后部署等。持续部署过程的思想是,在构建通过QA阶段的所有阶段后,将已开发的代码自动部署到生产环境中。 -beta,Integration,UAT等。有一些可用的工具,例如Spinnaker,Jenkins,Harness,Ansible,Chef,Puppet等,使DevOps团队可以建立自动化管道,以最少的人工干预就可以在多个环境中进行部署。 持续交付是DevOps的一种实践,其中新的代码库由QA团队在自动和手动QA周期的不同阶段进行测试。如果代码库通过了QA周期并得到QA团队的批准,则它将部署到生产环境中。通过这种方式,DevOps将团队划分为较短的周期,从而使团队能够更快,更频繁地构建,测试和发布代码库。这使组织能够提供更多版本,减少手动部署,并将生产中的故障风险降至最低。 配置管理(CM) 配置和变更管理是DevOps周期的重要组成部分。配置管理是对基础结构和系统的所有实体(例如服务器,应用程序,存储,网络和所有托管服务)进行配置,监视,管理和维护的自动化。也有许多的集中配置管理工具可以使用,比如:nacos、apollo等。 配置管理带来了许多优势,例如简化了新环境的设置,降低了生产配置的风险,并节省了软件开发的大量时间,而无需花费时间和精力来使用基础结构即代码实践从头开始新服务。 变更管理 变更管理是一个请求,计划,实施和评估满足新要求所需的变更的过程。在配置管理过程中,如果现有系统和基础架构中需要进行任何更改,那么此时就要考虑更改管理。运营团队需要提供他们的意见,更改的原因,以及可能在更大范围内产生的后果,包括可能受到新更改影响的其他系统。 测试自动化 每个代码库的自动测试有助于运行更多测试,提高测试频率,并节省手动QA上花费的时间。此过程可及早发现错误,修复错误,并提高整体软件质量。有几种工具可以与DevOps工具集成,例如Selenium,RobotFramework,Appium,XCUITest,JUnit等,以实现自动化的测试。 持续监控(CM) 持续监控建议使用几种工具,仪表板和警报来监视所有系统和基础架构,包括对影响软件的不同指标的实时洞察,例如系统性能,测试次数,成功和失败率,部署状态,错误日志以及所有信息以图形,表格和详细报告格式显示。为了完成这样的监视,DevOps团队可以设置一些工具,例如Prometheus,Grafana,Nagios,Appdynamics,NewRelic,Splunk,Logstash,以及市场上可用的更多工具。 欢迎关注我的博客,里面有很多精品合集本文转载注明出处(必须带连接,不能只转文字):字母哥博客。觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》《Spring Security-JWT-OAuth2一本通》《实战前后端分离RBAC权限管理系统》《实战SpringCloud微服务从青铜到王者》《VUE深入浅出系列》

July 6, 2020 · 1 min · jiezi

Sping-IOC常用注解

Sping IOC常用注解1.什么是注解注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值..)使用注解,注解作用在类上面,方法上面,属性上面使用注解目的:简化xml配置2.常用注解2.1用于创建对象的 他们的作用就和在XML配置文件中编写一个<bean>标签实现的功能是一样的 Component: 作用:用于把当前类对象存入spring容器中 属性: value:用于指定bean的id。当我们不写时,它的默认值是当前类名,且首字母改小写。 Controller:一般用在表现层Service:一般用在业务层Repository:一般用在持久层==以上三个注解他们的作用和属性与Component是一模一样。他们三个是spring框架为我们提供明确的三层使用的注解,使我们的三层对象更加清晰== 2.2用于注入数据的 他们的作用就和在xml配置文件中的bean标签中写一个<property>标签的作用是一样的 Autowired: 作用:自动按照类型注入。 只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功 如果ioc容器中没有任何bean的类型和要注入的变量类型匹配,则报错。 如果Ioc容器中有多个类型匹配时:使用要注入的对象变量作为bean的id,在spring容器中寻找 出现位置: 可以是变量上,也可以是方法上 细节: 在使用注解注入时,set方法就不是必须的了。 Qualifier: 作用: 在按照类中注入的基础之上再按照名称注入。它在给类成员注入时不能单独使用。但是在给方法参数注入时可以 属性: value:用于指定注入bean的id。 Resource: 作用:直接按照bean的id注入。它可以独立使用属性:name:用于指定bean的id。以上三个注入都只能注入==其他bean类型==的数据,而==基本类型和String类型==无法使用上述注解实现。 ==另外,集合类型的注入只能通过XML来实现。== Value 作用:用于注入基本类型和String类型的数据属性: value:用于指定数据的值。它可以使用spring中SpEL(也就是spring的el表达式) SpEL的写法:${表达式} 2.3用于改变作用范围的他们的作用就和在bean标签中使用scope属性实现的功能是一样的 Scope 作用:用于指定bean的作用范围属性:value:指定范围的取值。常用取值:singleton(单例) prototype(多例)2.4和生命周期相关他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的 PreDestroy:用于指定销毁方法PostConstruct :用于指定初始化方法3.Spring新注解使用一个配置类,它的作用和bean.xml是一样的 @ComponentScan("com.ming")@Import(JdbcConfig.class)@PropertySource("classpath:driuid.properties")public class SpringConfiguration {}Configuration 作用:指定当前类是一个配置类细节:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。ComponentScan 作用:用于通过注解指定spring在创建容器时要扫描的包属性: value:它和basePackages的作用是一样的,都是用于指定创建容器时要扫描的包。 我们使用此注解就等同于在xml中配置了: <context:component-scan base-package="com.ming"></context:component-scan>Bean 作用:用于把当前方法的返回值作为bean对象存入spring的ioc容器中属性: name:用于指定bean的id。当不写时,默认值是当前方法的名称 细节: 当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象。 查找的方式和Autowired注解的作用是一样的 Import 作用:用于导入其他的配置类属性: value:用于指定其他配置类的字节码。 当我们使用Import的注解之后,有Import注解的类就父配置类,而导入的都是子配置类 ...

July 6, 2020 · 1 min · jiezi

SpringCloud-第三篇-Ribbon快速上手

1:Ribbon是什么Ribbon是一个开源的客户端负载均衡器,由Netflix发布,是SpringCloud-Netflix中重要的一环,通过它将Netflix的中间层服务连接在一起。Ribbon客户端组件提供一系列完善的配置项,如连接超时、重试等。简单的说,就是在配置文件中列出Load Balancer后面所有的服务,Ribbon会自动的基于某种规则(如简单轮询,随机连接等)去连接这些服务,也很容易实现自定义的负载均衡算法。 2:Ribbon能干什么Ribbon是在客户端来实现负载均衡的访问服务,主要的功能点: 服务发现,发现依赖服务的列表服务选择规则,在多个服务中如何选择一个有效服务服务监听,检测失效的服务,高效剔除失效服务 3:代码实例加入依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-ribbon</artifactId></dependency>官方的HelloWorld具体示例参看:https://github.com/Netflix/ri... 4:参数配置4.1:概述对于Ribbon参数的key以及value类型的定义,可以通过com.netflix.client.config.CommonClientConfigKey查看如果你没有配置任何属性,则ribbon会使用com.netflix.client.config.DefaultClientConfigImpl里的值 4.2:配置方式Ribbon的参数配置通常有二种方式:全局配置以及指定客户端配置 全局配置的方式很简单只需要使用ribbon.<key>=<value>格式进行配置即可。其中,<key>代表了Ribbon客户端配置的参数名,<value>则代表了对应参数的值。比如,可以向下面这样配置Ribbon的超时时间:ibbon.ConnectTimeout=250 全局配置可以作为默认值进行设置,当指定客户端配置了相应的key的值时,将覆盖全局配置的内容 指定客户端的配置方式<client>.ribbon.<key>=<value>的格式进行配置<client>表示服务名,比如没有服务治理框架的时候(如Eureka),需要指定实例清单,如:userService.ribbon.listOfServers=localhost:8080 4.3:配置示例my.properties:# Max number of retries on the same server (excluding the first try)hello-client.ribbon.MaxAutoRetries=1# Max number of next servers to retry (excluding the first server)hello-client.ribbon.MaxAutoRetriesNextServer=1# Whether all operations can be retried for this clienthello-client.ribbon.OkToRetryOnAllOperations=true# Interval to refresh the server list from the sourcehello-client.ribbon.ServerListRefreshInterval=2000# Connect timeout used by Apache HttpClienthello-client.ribbon.ConnectTimeout=3000# Readtimeout used by Apache HttpClienthello-client.ribbon.ReadTimeout=3000# Initial list of servers, can be changed via Archaius dynamic property at runtime#ribbon.listOfServers=localhost:8765hello-client.ribbon.listOfServers=localhost:8765,localhost:8766hello-client.ribbon.NFLoadBalancerRuleClassName=com.netflix.loadbalancer.RoundRobinRuleConfigurationManager.loadPropertiesFromResources("my.properties"); // 1System.out.println( ConfigurationManager.getConfigInstance() .getProperty("hello-client.ribbon.listOfServers"));RestClient client = (RestClient) ClientFactory.getNamedClient("hello-client"); // 2HttpRequest request = HttpRequest.newBuilder().uri(new URI("/userServiceProvider?name=cc5")).build(); // 3for (int i = 0; i < 15; i++) { HttpResponse response = client.executeWithLoadBalancer(request); // 4 System.out.println("Status code for " + response.getRequestedURI() + " :" + response.getStatus());}4.4:启动查看日志: ...

July 5, 2020 · 1 min · jiezi

性能测试之Jmeter中场景设置与启动方式

Jmeter场景设置与启动方式性能测试场景是用来模拟模拟真实用户操作的工作单元,所以场景设计一定要切合用户的操作逻辑,jmeter主要是通过线程组配合其他组件来一起完成场景的设置。 线程组设置Jmeter线程组实际上是简历一个线程池,然后根据用户的设置完成线程池的初始化,在运行时做各种逻辑处理 上图是一个线程组的配置界面,下面我们对具体的参数进行说明 name和comments是名称和注释的配置,这里根据自己要测试的场景设置就好,最好是见名知意 Action to be taken after a sampler error在取样器错误后要执行的操作,也就是说在某一个请求出错后的异常处理方式,有5中方式 Continue, 继续,取样器模拟的请求出错后,继续运行 在大量用户并发请求时,服务器可能会出现错误响应,这属于正常现象,此时要把错误记录下来,继续后面的请求 start next Thread Loop 如果出错,则统一脚本中的剩下的请求将不在执行,直接重新开始,比如有个场景,需要先登录后发文,这个时候如果登录失败了,那么发帖就不会成功,所以没必要继续执行,直接开始下一轮测试就好,此时就可以设置这个错误处理方式。stop thread 停止线程,如果遇到取样器模拟的某个请求失败,则停止当前线程,不再执行,比如配置了40个线程,某个线程的请求失败,则停止当前线程,剩余的线程继续执行,如果失败的越多,则停止的越多,运行状态的线程就会越来越少,那服务器的负载就会不够,测试结果也不具备参考性了,所一般不设置这个。stop test 停止测试,如果某一个线程的一个请求失败了,则停止掉所有线程,也就是说测试要停止,但是每个线程任然会执行完当前场景,比如当前场景为登陆发文,有个线程登陆失败,其他线程也执行到了登陆,错误发生后,其他线程还是会继续执行发文操作。stop test Now 立即停止测试,如果有线程请求失败,则立即停止所有线程,不再继续执行Thread Properties 线程属性设置线程数 Number of Threads(users)运行的线程数设置,一个线程对应一个用户 Ramp-up period(seconds)线程启动开始运行的时间间隔,单位是秒,所有线程在多长时间内开始运行比如说设置了50个线程,Ramp-up period设置10秒,那么每秒就会启动5个线程,如果设置0秒,那么50线程就会立即启动。 Loop count 请求的重复次数,如果选择infinite,那么请求将会一直执行下去,除非停止或者崩溃,如果选择填入具体次数,那就可以控制循序的次数了。sam user on each iteration 每次迭代有使用相同的用户,这个配置是5.2版本以后新加的,之前的版本没有这个选项,所以会有一个问题,模拟的用户并不是和真实场景一样是相互独立的,他默认在每个线程中是复用用户,给大家推荐一篇文章https://www.cnblogs.com/nmb12...Delay Thread creation until neededJMeter 所有的线程是一开始就创建完成的,只是递增的时候会按照上面的规则递增。如果选择了这个选项,则不会在一开始创建所有线程,只有在需要时才会创建。 如果不选择,在启动场景时,JMeter 会用更多的 CPU 来创建线程,它会影响前面的一些请求的响应时间,因为压力机的 CPU 在做其他事情嘛。如果选择了的话,就会在使用时再创建,CPU 消耗会平均一些,但是这时会有另一个隐患,就是会稍微影响正在跑的线程 Specify Thread lifetime 线程运行时间,这个配置只有Loop count 请求的重复次数,配置了infinite,才会生效,否则还是Loop count 控制线程的执行 Duration(seconds)设置线程组运行多长时间,单位是秒StartUp delay(seconds)设置线程启动延时时间,单位是秒场景运行Jmeter的运行方式分为两种,一种是GUI可视化界面运行,另一种是命令行运行,这两种都支持本地化运行即单体运行和远程运行。这里我们主要介绍远程执行和jmeter命令行运行。 ...

July 5, 2020 · 1 min · jiezi

你知道Spring是怎么将AOP应用到Bean的生命周期中的吗

聊一聊Spring是怎么将AOP应用到Bean的生命周期中的? 本系列文章: 听说你还没学Spring就被源码编译劝退了?30+张图带你玩转Spring编译 读源码,我们可以从第一行读起 你知道Spring是怎么解析配置类的吗? 配置类为什么要添加@Configuration注解? 谈谈Spring中的对象跟Bean,你知道Spring怎么创建对象的吗? 这篇文章,我们来谈一谈Spring中的属性注入 Spring中AOP相关的API及源码解析,原来AOP是这样子的 推荐阅读: Spring官网阅读 | 总结篇 Spring杂谈 本系列文章将会带你一行行的将Spring的源码吃透,推荐阅读的文章是阅读源码的基础! ” 前言在上篇文章中(Spring中AOP相关的API及源码解析,原来AOP是这样子的)我们已经分析过了AOP的实现的源码,那么Spring是如何将AOP应用到Bean的生命周期的呢?这篇文章就带着大家来探究下这个问题。本文我们要分析的代码还是位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#doCreateBean这个方法中,在《我们来谈一谈Spring中的属性注入 》这篇文章中,我们已经分析过了populateBean这个方法, image-20200703202825887 所以本文我们接着来看看initializeBean这个方法,它主要干了这么几件事 执行Aware接口中的方法执行生命周期回调方法完成AOP代理对应源码如下: protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {  if (System.getSecurityManager() != null) {   AccessController.doPrivileged((PrivilegedAction<Object>) () -> {    invokeAwareMethods(beanName, bean);    return null;   }, getAccessControlContext());  }  else {                       // 执行Aware接口中的方法   invokeAwareMethods(beanName, bean);  }  Object wrappedBean = bean;  if (mbd == null || !mbd.isSynthetic()) {                        // 调用InitDestroyAnnotationBeanPostProcessor            // 的postProcessBeforeInitialization方法            // 处理@PostContructor注解标注的方法            // 另外有一部分aware方法也是在这里调用的   wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);  }  try {            // 如果实现了InitializingBean,会调用afterPropertiesSet方法            // 如果XML中配置了init-method属性,会调用对应的初始化方法   invokeInitMethods(beanName, wrappedBean, mbd);  }  catch (Throwable ex) {   throw new BeanCreationException(     (mbd != null ? mbd.getResourceDescription() : null),     beanName, "Invocation of init method failed", ex);  }  if (mbd == null || !mbd.isSynthetic()) {            // 在这里完成AOP   wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);  }  return wrappedBean; }因为在Spring官网阅读(九)Spring中Bean的生命周期(上)文章中我们已经对这个方法做过分析了,并且这个方法本身也比较简单,所以不再对这个方法做过多赘述,我们主要关注的就是Spring是如何将AOP应用到Bean的生命周期中的,对应的就是applyBeanPostProcessorsAfterInitialization这个方法,其源码如下: public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)    throws BeansException {    Object result = existingBean;    for (BeanPostProcessor processor : getBeanPostProcessors()) {        Object current = processor.postProcessAfterInitialization(result, beanName);        if (current == null) {            return result;        }        result = current;    }    return result;}实际上就是调用了所有后置处理器的postProcessAfterInitialization方法,在Spring中AOP相关的API及源码解析,原来AOP是这样子的一文中已经提到过了,@EnableAspectJAutoProxy注解实际上就是向容器中注册了一个AnnotationAwareAspectJAutoProxyCreator,这个类本身就是一个后置处理器,AOP代理就是由它在这一步完成的。 Bean生命周期中AOP的流程1、@EnableAspectJAutoProxy通过@EnableAspectJAutoProxy注解向容器中注册一个AnnotationAwareAspectJAutoProxyCreator的BeanDefinition,它本身也是一个BeanPostProcessor,这个BeanDefinition会在org.springframework.context.support.AbstractApplicationContext#registerBeanPostProcessors这个方法中完成创建,如下图所示 image-20200704112937846 2、postProcessBeforeInstantiation方法执行执行AnnotationAwareAspectJAutoProxyCreator的postProcessBeforeInstantiation方法,实际上就是父类AbstractAutoProxyCreator的postProcessBeforeInstantiation被执行 对应源码如下: //  这个方法的主要目的就是在不考虑通知的情况下,确认哪些Bean不需要被代理//  1.Advice,Advisor,Pointcut类型的Bean不需要被代理//  2.不是原始Bean被包装过的Bean不需要被代理,例如ScopedProxyFactoryBean//  实际上并不只是这些Bean不需要被代理,如果没有对应的通知需要被应用到这个Bean上的话//  这个Bean也是不需要被代理的,只不过不是在这个方法中处理的。public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) {    Object cacheKey = getCacheKey(beanClass, beanName);    // 如果beanName为空或者为这个bean提供了定制的targetSource    if (!StringUtils.hasLength(beanName) || !this.targetSourcedBeans.contains(beanName)) {        // advisedBeans是一个map,其中key是BeanName,value代表了这个Bean是否需要被代理        // 如果已经包含了这个key,不需要在进行判断了,直接返回即可        // 因为这个方法的目的就是在实例化前就确认哪些Bean是不需要进行AOP的        if (this.advisedBeans.containsKey(cacheKey)) {            return null;        }        // 说明还没有对这个Bean进行处理        // 在这里会对SpringAOP中的基础设施bean,例如Advice,Pointcut,Advisor做标记        // 标志它们不需要被代理,对应的就是将其放入到advisedBeans中,value设置为false        // 其次,如果这个Bean不是最原始的Bean,那么也不进行代理,也将其value设置为false        if (isInfrastructureClass(beanClass) || shouldSkip(beanClass, beanName)) {            this.advisedBeans.put(cacheKey, Boolean.FALSE);            return null;        }    }    // 是否为这个Bean提供了定制的TargetSource    // 如果提供了定制的TargetSource,那么直接在这一步创建一个代理对象并返回    // 一般不会提供    TargetSource targetSource = getCustomTargetSource(beanClass, beanName);    if (targetSource != null) {        if (StringUtils.hasLength(beanName)) {            this.targetSourcedBeans.add(beanName);        }        Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(beanClass, beanName, targetSource);        Object proxy = createProxy(beanClass, beanName, specificInterceptors, targetSource);        this.proxyTypes.put(cacheKey, proxy.getClass());        return proxy;    }    return null;}3、postProcessAfterInitialization方法执行实际上也是执行父类AbstractAutoProxyCreator中的方法,对应源码如下: public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {    if (bean != null) {        Object cacheKey = getCacheKey(bean.getClass(), beanName);        // 什么时候这个判断会成立呢?        // 如果不出现循环引用的话,remove方法必定返回null        // 所以这个remove(cacheKey) != bean肯定会成立        // 如果发生循环依赖的话,这个判断就不会成立        // 这个我们在介绍循环依赖的时候再详细分析,        // 目前你只需要知道wrapIfNecessary完成了AOP代理        if (this.earlyProxyReferences.remove(cacheKey) != bean) {            // 需要代理的话,在这里完成的代理            return wrapIfNecessary(bean, beanName, cacheKey);        }    }    return bean;}4、wrapIfNecessary方法执行protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {       // 在postProcessBeforeInstantiation方法中可能已经完成过代理了    // 如果已经完成代理了,那么直接返回这个代理的对象    if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {        return bean;    }        // 在postProcessBeforeInstantiation方法中可能已经将其标记为不需要代理了    // 这种情况下,也直接返回这个Bean    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {        return bean;    }        // 跟在postProcessBeforeInstantiation方法中的逻辑一样    // 如果不需要代理,直接返回,同时在advisedBeans中标记成false    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {        this.advisedBeans.put(cacheKey, Boolean.FALSE);        return bean;    }    // 获取可以应用到这个Bean上的通知    Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);    // 如果存在通知的话,说明需要被代理    if (specificInterceptors != DO_NOT_PROXY) {        this.advisedBeans.put(cacheKey, Boolean.TRUE);        // 到这里创建代理,实际上底层就是new了一个ProxyFactory来创建代理的        Object proxy = createProxy(            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));        this.proxyTypes.put(cacheKey, proxy.getClass());        return proxy;    } // 如果没有通知的话,也将这个Bean标记为不需要代理    this.advisedBeans.put(cacheKey, Boolean.FALSE);    return bean;}关于创建代理的具体源码分析,在Spring中AOP相关的API及源码解析,原来AOP是这样子的一文中已经做了详细介绍,所以本文不再赘述,现在我们的重点将放在Spring是如何解析出来通知的,对应方法就是getAdvicesAndAdvisorsForBean,其源码如下: 第一步:调用org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#getAdvicesAndAdvisorsForBean protected Object[] getAdvicesAndAdvisorsForBean(      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {     // 通过findEligibleAdvisors方法返回对应的通知   // 这个方法回返回所有能应用在指定的Bean上的通知   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);      if (advisors.isEmpty()) {      return DO_NOT_PROXY;   }   return advisors.toArray();}第二步:调用org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findEligibleAdvisors protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {    // 获取到所有的通知    List<Advisor> candidateAdvisors = findCandidateAdvisors();    // 从获取到的通知中筛选出能应用到这个Bean上的通知    List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);    extendAdvisors(eligibleAdvisors);    if (!eligibleAdvisors.isEmpty()) {        eligibleAdvisors = sortAdvisors(eligibleAdvisors);    }    return eligibleAdvisors;}第三步:调用org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator#findCandidateAdvisors获取到所有的通知 // 这个方法的目的就是为了获取到所有的通知protected List<Advisor> findCandidateAdvisors() {       // 先调用父类的方法,父类会去查找容器中所有属于Advisor类型的Bean    List<Advisor> advisors = super.findCandidateAdvisors();       // 这个类本身会通过一个aspectJAdvisorsBuilder来构建通知    // 构建的逻辑就是解析@Aspect注解所标注的类中的方法    if (this.aspectJAdvisorsBuilder != null) {        advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());    }        // 最后返回这些通知    return advisors;}第四步:org.springframework.aop.aspectj.annotation.BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors构建通知,这个方法比较长,我们就只分析其中的关键代码即可 public List<Advisor> buildAspectJAdvisors() {  List<String> aspectNames = this.aspectBeanNames;  if (aspectNames == null) {   synchronized (this) {    aspectNames = this.aspectBeanNames;    if (aspectNames == null) {     List<Advisor> advisors = new ArrayList<>();     aspectNames = new ArrayList<>();     // 会获取到容器中的所有BeanName     String[] beanNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(       this.beanFactory, Object.class, true, false);     for (String beanName : beanNames) {      // 如果对beanName配置了正则匹配的话,那么要按照正则表达式的匹配规则进行过滤      // 默认是没有的,可以认为isEligibleBean始终返回true      if (!isEligibleBean(beanName)) {       continue;      }      // We must be careful not to instantiate beans eagerly as in this case they      // would be cached by the Spring container but would not have been weaved.      Class<?> beanType = this.beanFactory.getType(beanName);      if (beanType == null) {       continue;      }      // 判断类上是否添加了@Aspect注解      if (this.advisorFactory.isAspect(beanType)) {       aspectNames.add(beanName);       AspectMetadata amd = new AspectMetadata(beanType, beanName);       // 默认就是SINGLETON,代理切面对象是单例的       if (amd.getAjType().getPerClause().getKind() == PerClauseKind.SINGLETON) {                            // 最后从这个切面实例中解析出所有的通知                            // 关于通知解析的具体代码就不再分析了         MetadataAwareAspectInstanceFactory factory =          new BeanFactoryAspectInstanceFactory(this.beanFactory, beanName);        List<Advisor> classAdvisors = this.advisorFactory.getAdvisors(factory);        if (this.beanFactory.isSingleton(beanName)) {         this.advisorsCache.put(beanName, classAdvisors);        }        else {         this.aspectFactoryCache.put(beanName, factory);        }        advisors.addAll(classAdvisors);       }  // 省略部分代码  return advisors; }第五步:org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator#findAdvisorsThatCanApply protected List<Advisor> findAdvisorsThatCanApply(    List<Advisor> candidateAdvisors, Class<?> beanClass, String beanName) {    ProxyCreationContext.setCurrentProxiedBeanName(beanName);    try {        return AopUtils.findAdvisorsThatCanApply(candidateAdvisors, beanClass);    }    finally {        ProxyCreationContext.setCurrentProxiedBeanName(null);    }}这个方法其实没啥好分析的,就是根据前面找出来的Advisor集合进行遍历,然后根据每个Advisor对应的切点来进行匹配,如何合适就返回,对应源码也比较简单,当然前提是你看过我之前那篇AOP源码分析的文章了. 总结这篇文章比较短,因为没有做很细节的源码分析,比较详细的源码分析已经放到上篇文章中了。最后我这里画个流程图总结一下AOP是怎么被应用到Bean的生命周期中的 image-20200705152704917 Spring源码的最后一点补充protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args)    throws BeanCreationException {    // 1.实例化    ---> createBeanInstance    // 2.属性注入  ---> populateBean    // 3.初始化    ---> 完成初始化及AOP    // exposedObject 就是完成初始化后的Bean      // 省略部分代码,省略代码的作用已经在上面标明了        // 下面的代码实际上主要目的在于处理循环依赖    if (earlySingletonExposure) {        Object earlySingletonReference = getSingleton(beanName, false);        if (earlySingletonReference != null) {            if (exposedObject == bean) {                exposedObject = earlySingletonReference;            }            // 我们之前早期暴露出去的Bean跟现在最后要放到容器中的Bean不是同一个            // allowRawInjectionDespiteWrapping为false            // 并且当前Bean被当成依赖注入到了别的Bean中            else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) {                // 获取到当前Bean所从属的Bean                String[] dependentBeans = getDependentBeans(beanName);                // 要得到真实的从属的Bean                Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length);                for (String dependentBean : dependentBeans) {                    // 移除那些仅仅为了类型检查而创建出来                    if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) {                        actualDependentBeans.add(dependentBean);                    }                }                if (!actualDependentBeans.isEmpty()) {     // 抛出异常                    // 出现了循环依赖,并且实际存在容器中的Bean跟被当作依赖注入到别的Bean中的                    // 不是同一个对象,这个时候也报错                }            }        }    }    // 注册bean的销毁回调    try {        registerDisposableBeanIfNecessary(beanName, bean, mbd);    }    catch (BeanDefinitionValidationException ex) {        throw new BeanCreationException(            mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);    }    return exposedObject;}实际这段代码还是跟循环依赖相关,循环依赖是Spring中一个比较重要的话题,不管是为了面试还是更好的了解清楚Spring的流程都很有必要去弄懂它 关于Spring的循环依赖,我将在下篇文章专门分析! 如果本文对你有帮助的话,记得点个赞吧!也欢迎关注我的公众号,微信搜索:程序员DMZ,或者扫描下方二维码,跟着我一起认认真真学Java,踏踏实实做一个coder。 公众号 我叫DMZ,一个在学习路上匍匐前行的小菜鸟!

July 5, 2020 · 1 min · jiezi

SpringCloud-第二篇-Eureka快速上手

1 Eureka是什么Eureka是Spring Cloud Netflix的一个子模块,也是核心模块之一。Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移。 服务注册与发现对于微服务架构来说是非常重要的,有了服务发现与注册,只需要使用服务的标识符,就可以访问到服务,而不需要修改服务调用的配置文件了。功能类似于dubbo的注册中心,比如Zookeeper。 2 基本概念Register:服务注册服务提供者向Eureka Serve注册,注册发生在第一次心跳,它提供关于自己的元数据(诸如主机和端口,健康指标URL等)Eureka Server。 Renew:服务续约Eureka客户会每隔30秒发送一次心跳来续约。 通过续约来告知Eureka Server该Eureka客户仍然存在,没有出现问题。 正常情况下,如果Eureka Server在90秒没有收到Eureka客户的续约,它会将实例从其注册表中删除。 建议不要更改续约间隔。注册信息和续订被复制到集群中的Eureka Serve所有节点。 来自任何区域的Eureka Client都可以查找注册表信息(每30秒发生一次)。根据这些注册表信息,Application Client可以远程调用Applicaton Service来消费服务 Fetch Registries:获取注册列表信息Eureka客户端从服务器获取注册表信息,并将其缓存在本地。客户端会使用该信息查找其他服务,从而进行远程调用。该注册列表信息定期(每30秒钟)更新一次。每次返回注册列表信息可能与Eureka客户端的缓存信息不同, Eureka客户端自动处理。如果由于某种原因导致注册列表信息不能及时匹配,Eureka客户端则会重新获取整个注册表信息。 Eureka服务器缓存注册列表信息,默认的情况下Eureka客户端使用压缩JSON格式来获取注册列表的信息。 Cancel:服务下线Eureka客户端在程序关闭时向Eureka服务器发送取消请求。 发送请求后,该客户端实例信息将从服务器的实例注册表中删除。  Eviction 服务剔除Eviction用来定期(默认为每60秒)在Eureka Server检测失效的服务,检测标准就是超过一定时间没有Renew的服务,默认失效时间为90秒,也就是如果有服务超过90秒没有向Eureka Server发起Renew请求的话,就会被当做失效服务剔除掉, Eureka服务器会将该服务实例从服务注册列表删除。 3 自我保护模式概述保护模式主要用于一组客户端和Eureka Server之间存在网络分区场景下的保护。一旦进入保护模式,Eureka Server将会尝试保护其服务注册表中的信息,不再删除服务注册表中的数据,也就是不会注销任何微服务。如果在Eureka Server的首页看到以下这段提示,则说明Eureka进入了保护模式: EMERGENCY! EUREKA MAY BE INCORRECTLY CLAIMING INSTANCES ARE UP WHEN THEY'RE NOT. RENEWALS ARE LESSER THAN THRESHOLD AND HENCE THE INSTANCES ARE NOT BEING EXPIRED JUST TO BE SAFE 何时进入自我保护模式当一个新的Eureka Server出现时,它尝试从相邻节点获取所有实例注册表信息。如果从Peer节点获取信息时出现问题,Eureka Serve会尝试其他的Peer节点。如果服务器能够成功获取所有实例,则根据该信息设置应该接收的更新阈值。如果有任何时间,Eureka Serve接收到的续约低于为该值配置的百分比(默认为15分钟内低于85%),则服务器开启自我保护模式,即不再剔除注册列表的信息。这样做的好处就是,如果是Eureka Server自身的网络问题,导致Eureka Client的续约不上,Eureka Client的注册列表信息不再被删除,也就是Eureka Client还可以被其他服务消费。 ...

July 5, 2020 · 2 min · jiezi

SpringCloud-第一篇-初见

1 简介SpringCloud是基于SpringBoot的一整套实现微服务的框架。他提供了微服务开发所需的配置管理、服务发现、断路器、智能路由、微代理、控制总线、全局锁、决策竞选、分布式会话和集群状态管理等组件。 具体介绍请见:注意其中的版本信息官方: http://projects.spring.io/spr...Dalston版本的文档:http://cloud.spring.io/spring...中文: https://springcloud.cc/ 2 Spring Cloud Netflix简介该项目是Spring Cloud的核心子项目,是对Netflix公司一系列开源产品的封装。它为Spring Boot应用提供了自配置的整合,只需要通过一些简单的注解,就可以快速地在Spring Cloud的应用中使用起来。 主要提供的模块包括:服务发现注册(Eureka)、客户端负载均衡(Ribbon)、断路器(Hystrix)、智能路由(Zuul)、声明性REST客户端(Feign)、外部配置(Archaius)等

July 5, 2020 · 1 min · jiezi

使用IDEA实现SSM整合MavenSpringMybatisSpringMvc

我是阿福,公众号「阿福聊编程」作者,一个在后端技术路上摸盘滚打的程序员,在进阶的路上,共勉!文章已收录在 JavaSharing 中,包含Java技术文章,面试指南,资源分享。 本文是基于Maven工程实现SSM整合,主要是通过XML实现三大框架的整合,可能有的小伙伴会说XML早就过时了,现在人们都使用注解开发,但是不是这么认为,我个人觉得你XML 整合会了,注解能不会吗? 开干!!!! SSM整合总体思路 Spring和MyBatis整合整合JAR包<dependencies> <!-- Spring依赖 --> <!-- https://mvnrepository.com/artifact/org.springframework/spring-orm --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>4.3.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.2.RELEASE</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.2</version> </dependency> <!-- https://mvnrepository.com/artifact/cglib/cglib --> <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>2.2</version> </dependency> <!-- MySQL驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-Java</artifactId> <version>5.1.3</version> </dependency> <!-- 数据源 --> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.0.31</version> </dependency> <!-- MyBatis --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.2.8</version> </dependency> <!-- MyBatis与Spring整合 --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.2.2</version> </dependency> <!-- 日志 --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>1.7.7</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.7</version> </dependency> <!-- Spring进行JSON数据转换依赖 --> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>1.9.2</version> </dependency> <!-- JSTL标签库 --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- junit测试 --> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!-- 引入Servlet容器中相关依赖 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <!-- JSP页面使用的依赖 --> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.1.3-b06</version> <scope>provided</scope> </dependency> </dependencies>创建Spring的配置文件spring-persist-mybatis.xml ---数据源相关的配置spring-persist-tx.xml ---事务相关的配置spring-web-mvc.xml ---SpringMvc相关的配资创建jdbc.propertiesjdbc.driver=com.mysql.jdbc.Driverjdbc.url=jdbc:mysql://localhost:3307/crowdfunding?useUnicode=true&characterEncoding=utf8jdbc.username=rootjdbc.password=123456创建log4j.propertieslog4j.rootLogger=DEBUG,myConsolelog4j.appender.myConsole=org.apache.log4j.ConsoleAppenderlog4j.appender.myConsole.Target=System.outlog4j.appender.myConsole.layout=org.apache.log4j.PatternLayoutlog4j.appender.myConsole.layout.ConversionPattern=[%-5p] %d(%r) --> [%t] %l: %m %x %n配置数据源在配置文件spring-persist-mybatis.xml,分为两步:加载外部的属性文件,配置数据源 ...

July 5, 2020 · 3 min · jiezi

第一个Spring程序代码篇

前言上一篇介绍了一些理解和概念,本篇结合代码谈谈,本篇适合小白代码在 github 仓库 [toc] IoC - 第一个 Spring 程序先来个 Demo 感受一下,代码是基于 Maven 构建的,如果不熟悉 maven 可以查看公众号 JavaPub 目录学习。创建项目在 Idea 新建 Maven 项目,目录结构如图 导入依赖 pom.xml<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>javapub.rodert</groupId> <artifactId>firstSpringProject</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.13</version> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.7.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>5.2.7.RELEASE</version> </dependency> </dependencies></project>在项目的 src 目录下创建一个名为 javapub.rodert 的包,然后在该包中创建一个名为 PersonDao 的接口,并在接口中添加一个 add() 方法package javapub.rodert;/** * @author wangshiyu rodert * @date 2020/7/2 20:13 * @description */public interface PersonDao { public void add();}创建接口实现类 PersonDaoImpl在 javapub.rodert 包下创建 PersonDao 的实现类 PersonDaoImpl ...

July 4, 2020 · 2 min · jiezi

Springboot快速上手-第七篇-单元测试

1 概述SpringBoot对测试提供了一些简化支持,只需要添加起步依赖即可使用: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency>2 以前的测试方式SpringJUnit支持,由此引入Spring-Test框架支持,通过这个注解让SpringJUnit4ClassRunner这个类提供Spring测试上下文@RunWith(SpringJUnit4ClassRunner.class)指定SpringBoot工程的Application启动类,通过这个注解加载和配置Spring应用上下文@SpringApplicationConfiguration(classes = App.class)由于是Web项目,Junit需要模拟ServletContext,因此需要给测试类加上@WebAppConfiguration@WebAppConfiguration 3 常见的第一种方式 @RunWith(SpringRunner.class)@SpringBootTest(classes = App.class)@AutoConfigureMockMvc这种方式下:直接@Autowiredprivate MockMvc mockMvc;然后就可以使用mockMvc1:@RunWith里面,不再是SpringJUnit4ClassRunner.class,而是springboot专门做的一个启动类SpringRunner.class,当然,也可以使用@RunWith(SpringJUnit4ClassRunner.class)2:也不使用@SpringApplicationConfiguration了,使用@SpringBootTest来指定启动类,启动类上面就有配置的注解3:还可以指定随机的端口@SpringBootTest(classes = App.class,webEnvironment = WebEnvironment.RANDOM_PORT)4:可以引入自定义的配置类 @Import(MyTestsConfiguration.class) 4 常见的第二种方式1:如果没有 @AutoConfigureMockMvc, 那么就需要 @Autowiredprivate WebApplicationContext wac;private MockMvc mockMvc;2:然后加上: @Beforepublic void setup() { this.mockMvc = MockMvcBuilders.webAppContextSetup(this.wac).build();}然后就可以使用mockMvc 5 常见的第三种方式:使用TestRestTemplate@RunWith(SpringRunner.class)@SpringBootTest(classes = App.class,webEnvironment = WebEnvironment.RANDOM_PORT)直接注入:@Autowired private TestRestTemplate rest; 然后就可以直接使用TestRestTemplate了 6 其它Springboot还有一些专项的检查,比如:@DataJpaTest、@JdbcTest、@DataMongoTest、@RestClientTest、@JsonTest等等

July 4, 2020 · 1 min · jiezi

Springboot快速上手-第八篇-Actuator

1 概述Spring Boot Actuator的关键特性是在应用程序里提供众多Web端点,通过它们了解应用程序运行时的内部状况,比如: Spring应用程序上下文里配置的BeanBean在Spring应用程序上下文里是如何组装在一 起的Spring Boot的自动配置做的决策应用程序取到的环境变量、系统属性、配置属性和命令行参数应用程序里线程的当前状态应用程序最近处理过的HTTP请求的追踪情况各种和内存用量、垃圾回收、Web请求以及数据源用量相关的指标…… Spring Boot Actuator提供的端点,可以查看官方文档:https://docs.spring.io/spring... 2 启用Actuator 要启用Actuator的端点,只需在项目中引入Actuator的起步依赖即可 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>同时在properties里面设置management.security.enabled=false 3 autoconfig该端点用来获取应用的自动化配置报告,其中包括所有自动化配置的候选项。同时还列出了每个候选项自动化配置应用或未应用的原因。该报告内容将自动化配置内容分为两部分: positiveMatches中返回的是条件匹配成功的自动化配置negativeMatches中返回的是条件匹配不成功的自动化配置4 beans该端点用来获取应用上下文中创建的所有Bean 5 configprops该端点用来获取应用中配置的属性信息报告 6 env用来获取应用所有可用的环境属性报告。包括:环境变量、JVM属性、应用的配置配置、命令行中的参数。 7 health用来获取应用的各类健康指标信息。springboot自带了一些常用资源的健康指标检测器,都通过HealthIndicator接口实现,并且会实现自动化装配,可以查看官方目前提供的HealthIndicators。 8 自定义health检测器1:写一个类实现HealthIndicator接口,并注册为bean2:在实现方法中使用Health类 9 info用来返回一些应用自定义的信息。默认返回一个空的json串。可以在配置文件中通过info前缀来设置一些属性,如:info.app.author=cc 10 metrics 用来返回当前应用的各类重要度量指标,比如: 1:系统信息:包括处理器数量processors、运行时间uptime和instance.uptime、系统平均负载systemload.average2:mem.*:内存概要信息,包括分配给应用的总内存数量以及当前空闲的内存数量3:heap.*:堆内存使用情况4:nonheap.*:非堆内存使用情况5:threads.*:线程使用情况,包括线程数、守护线程数(daemon)、线程峰值(peak)等6:classes.*:应用加载和卸载的类统计7:gc.*:垃圾收集器的详细信息,包括垃圾回收次数gc.ps_scavenge.count、垃圾回收消耗时间gc.ps_scavenge.time、标记-清除算法的次数gc.ps_marksweep.count、标记-清除算法的消耗时间gc.ps_marksweep.time8:httpsessions.*:Web容器的会话使用情况。包括最大会话数httpsessions.max和活跃会话数httpsessions.active。该度量指标信息仅在引入了嵌入式Tomcat作为应用容器的时候才会提供。9:gauge.*:表示一个绝对数值的指标10:counter.*:主要作为计数器来使用,记录了增加量和减少量 11 自定义Counter1:需要注入计数服务: @Autowired private CounterService counterService; @Autowired private GaugeService gaugeService; 2:直接使用计数服务来计数 counterService.increment("abc.call.count"); gaugeService.submit("cc.redis.hit", 79); 12 mappings用来返回所有Spring MVC的控制器映射关系 13 dump用来暴露程序运行中的线程信息 14 trace用来返回基本的HTTP跟踪信息。默认情况下,跟踪信息的存储采用org.springframework.boot.actuate.trace.InMemoryTraceRepository实现的内存方式,始终保留最近的100条请求记录 15 shutdown用来提供远程关闭应用的功能,比较危险,建议不要打开。 16 常见配置 1:修改端点名称 直接配置endpoints.endpoint-id.id ,例如:endpoints.beans.id=bs 这么设置过后,访问就不是: http://localhost:8080/beans,而是:http://localhost:8080/bs2:启用和禁用端点 设置endpoints.endpoint-id.enabled3:设置管理端口 management.port ,-1表示关闭Http访问端口4:设置管理地址 management.address=127.0.0.15:设置指定的根路径 management.context-path=/admin ...

July 4, 2020 · 1 min · jiezi

Springboot快速上手-第九篇-Web应用开发

1 应用开发基础1.1 静态文件1: Spring Boot默认为我们提供了静态资源处理,使用 WebMvcAutoConfiguration 来配置各种属性,建议使用默认配置方式,提供的静态资源映射,按照优先级顺序如下: classpath:/META-INF/resources classpath:/resources classpath:/static classpath:/public 2:可以通过修改spring.mvc.static-path-pattern来修改默认的映射路径3:注意:如果应用将被打包成jar,就不要使用src/main/webapp文件夹。尽管该文件夹是一个共同的标准,但它仅在打包成war的情况下起作用4:SpringMVC使用ResourceHttpRequestHandler 来进行资源映射,所以可以通过添加自己的WebMvcConfigurerAdapter并覆写addResourceHandlers方法,来改变这个行为,也就是自定义加载静态文件 1.2 自定义加载静态文件示例@Configurationpublic class MyWebMvcConfig extends WebMvcConfigurerAdapter { public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/static2/**") .addResourceLocations("classpath:/static2/"); super.addResourceHandlers(registry); }}1:也可以指定外部的路径,直接addResourceLocations指定即可,示例如下: 把.addResourceLocations("classpath:/static/")变换成 .addResourceLocations("file:D:/my/") 1.3 添加拦截器配置1:先按照SpringMVC的自定义拦截器的写法,写好拦截器类2:然后在重写WebMvcConfigurerAdapter中的addInterceptors方法,示例如下: public void addInterceptors(InterceptorRegistry registry) {    registry.addInterceptor(new MyInterceptor()).addPathPatterns("/**").excludePathPatterns("/toLogin","/login");    super.addInterceptors(registry);}1.4 国际化1:定义国际化资源文件,放到resource下面,默认名字是messages.properties2:在前面的MyWebMvcConfig中添加读取消息文件的bean @Bean  public MessageSource messageSource() {     ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();     messageSource.setBasename("messages");     messageSource.setDefaultEncoding("UTF-8");     return messageSource;  }3:程序里面就可以直接注入MessageSource并使用 @Autowiredprivate MessageSource messageSource;4:一样可以向消息文件中传入参数 1.5 支持的模板引擎Spring Boot支持多种模版引擎包括:FreeMarker、Groovy、Thymeleaf(官方推荐) 1:JSP技术,Spring Boot官方是不推荐的,原因可能有:(1)Tomcat只支持war的打包方式,不支持可执行的jar(2)Jetty 嵌套的容器不支持jsp2:默认的模板配置路径为:src/main/resources/templates 2 Thymeleaf2.1 概述Thymeleaf是一款用于渲染XML/XHTML/HTML5内容的模板引擎。类似JSP,Velocity,FreeMaker等,它也可以轻易的与Spring MVC等Web框架进行集成作为Web应用的模板引擎。 与其它模板引擎相比,Thymeleaf最大的特点是能够直接在浏览器中打开并正确显示模板页面,而不需要启动整个Web应用。它的功能特性如下: 1:Spring MVC中@Controller中的方法可以直接返回模板名称,接下来Thymeleaf模板引擎会自动进行渲染2:模板中的表达式支持Spring表达式语言(Spring EL)3:表单支持,并兼容Spring MVC的数据绑定与验证机制4:国际化支持 2.2 基本语法和使用具体的请参见官网:http://www.thymeleaf.org/升级到使用thymeleaf 3.x: <thymeleaf.version>3.0.8.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.2</thymeleaf-layout-dialect.version> 3 Springboot集成Jsp ...

July 4, 2020 · 1 min · jiezi

小书MybatisPlus第1篇整合SpringBoot快速开始增删改查

Mybatis Plus官方文档已经很完善了,为什么还要写一个这样的文档?官方文档注重知识结构的整理,没有注重学习者的学习顺序官方文档中的案例注重API描述,比较适合学会mybatis plus之后,遇到问题再去翻阅。对于第一次使用Mybatis的初学者进行学习不是非常友好。官方文档考虑的是全面完整的介绍Mybatis Plus, 我考虑的角度是:“最佳实践”。世界上很多东西都符合2/8原则,本文档的目的是将:最重要最常用的那20%帮你提炼出来、快速上手应用!。另外的那80%都是不常用的,有空自己再去官方文档里面学吧!官网文档地址:https://mybatis.plus/guide/ 我会将此文档写成一个系列的内容,记得关注我!zimug.com我会将此文档写成一个系列的内容,记得关注我!zimug.com我会将此文档写成一个系列的内容,记得关注我!zimug.com 一、Spring Boot整合Mybatis Plus通过maven坐标引入依赖 <!-- mybatis --><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.1.2</version></dependency><!-- mysql --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency><!-- lombok --><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency>application配置数据源及日志输出级别 # 配置数据源spring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/mp?useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: test password: <填上数据库访问密码># 配置日志logging: level: root: warn com.zimug.boot.launch.mapper: trace第三步:配置Mybatis的Mapper类文件的包扫描路径 @SpringBootApplication@MapperScan(basePackages = {"com.zimug.boot.launch.mapper"})public class BootLaunchApplication { public static void main(String[] args) { SpringApplication.run(BootLaunchApplication.class, args); }}二、编码构建实体和Mapper编写实体类User.java,对应的数据库表创建SQL在文末。 @Data //lombok注解public class User { private Long id; private String name; private Integer age; private String email;}编写Mapper类UserMapper.java ...

July 4, 2020 · 2 min · jiezi

听说你还没学Spring就被源码编译劝退了30张图带你玩转Spring编译

源码学习第一步,Spring源码编译 之所以写这么一篇文章是因为群里的小伙伴在编译源码时碰到了问题,再加上笔者自身正准备做一个源码的注释版本,恰好也需要重新编译一份代码,至于为什么要将源码编译到本地就不用多说了吧?比如,你可以任意的添加注释,一边读源码一边记笔记,甚至你可以修改源码,更好的调试程序等等。但是如果你坚持通过导入依赖或者引入jar包的方式来学习源码,我觉得也可以,没有最好的,只有最适合自己的! 本文的主要目的是帮助那些在源码学习之初就被源码编译劝退的同学重拾信心! 话不多说,我们开始正题 参考官方文档: https://github.com/spring-pro... https://github.com/spring-pro... 前期准备确保本机已经安装好了gitjdk对应版本为1.8 Gradle,目前不需要安装,在编译的时候根据源码提示按照对应版本的Gradle即可IDEA,我使用的版本如下: 1、获取Spring源码这里我推荐使用clone的方式将源码拉取到本地,最大的好处在于可以利用IDEA直接比较版本间的差异,例如 在上图中我本地编译的5.0版本的代码,所以我对比的是5.0跟5.1版本populateBean方法实现的差异。 接下来我们开始拉取Spring源码,大家可以按照以下几步进行 在任意磁盘路径下新建一个文件夹,名称随便取,建议为SpringFramWork进入SpringFramWork文件夹中,打开git命令行,输入以下命令git clone https://github.com/spring-projects/spring-framework.git接着等待仓库克隆完毕,这个过程可能会耗费比较长的时间,如果实在不行的话,大家可以直接将源码的压缩包down下来。 如果你是跟我一样直接拉取的代码,记得切换到5.2.x版本,在命令行中执行命令: git checkout origin/5.2.x2、添加阿里云镜像在编译过程中,Spring会去自动下载一些依赖的包,默认使用的是官方的镜像,下载比较慢,所以我们提前添加好国内镜像,将下面这行代码粘贴到build.gradle文件中的repositories节点下即可 , //添加阿里云镜像maven { url "http://maven.aliyun.com/nexus/content/groups/public" }如下图所示 3、预先编译spring-oxm模块打开命令行窗口,并切换到源码所在文件夹,执行以下命令 gradlew :spring-oxm:compileTestJava出现BUILD SUCCESS字样时说明构建成功,如下图所示 4、根据编译后的源码下载并安装对应的版本的Gradle在完成对spring-oxm模块的编译后,会在当前目录生成一个.gradle文件夹,打开后可以查看对应的Gradle版本。 双击打开.gradle文件夹就能看到对应所需要的gradle的版本号 大家直接在这个网站上下载对应所需要的版本即可:https://gradle.org/releases/,选择 binary-only 安装好后记得配置Gradle的环境变量 新增 GRADLE_HOME 环境变量,指向Gradle解压目录配置Path环境变量:新增 %GRADLE_HOME%\bin之后测试是否安装成,在命令行中输入以下命令:gradle -v,查看是否正确输出了对应版本。 5、为安装好的Gradle配置国内镜像进入Gradle安装目录,在init.d目录下新建一个init.gradle文件,并添加以下内容: allprojects{ repositories { def REPOSITORY_URL = 'http://maven.aliyun.com/nexus/content/groups/public/' all { ArtifactRepository repo -> def url = repo.url.toString() if ((repo instanceof MavenArtifactRepository) && (url.startsWith('https://repo1.maven.org/maven2') || url.startsWith('https://jcenter.bintray.com'))) { project.logger.lifecycle 'Repository ${repo.url} replaced by $REPOSITORY_URL .' remove repo } } maven { url REPOSITORY_URL } }}如下图所示: ...

July 3, 2020 · 1 min · jiezi

Springboot快速上手-第六篇-开发者工具

1 概述SpringBoot提供了强大易用的Devtools来辅助开发,包括如下功能。 1:自动重启:当Classpath里的文件发生变化时,自动重启运行中的应用程序2: LiveReload支持:对资源的修改自动触发浏览器刷新3: 远程开发:远程部署时支持自动重启和LiveReload4: 默认的开发时属性:为一些属性提供有意义的默认开发时属性值 2 环境构建在pom里面加入: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <optional>true</optional> <scope>provided</scope></dependency>3 自动重启在激活了开发者工具后,Classpath里对文件做任何修改都会触发应用程序重启。为了让重启速度够快,不会修改的类(比如第三方JAR文件里的类)都加载到了基础类加载器里,而应用程序的代码则会加载到一个单独的重启类加载器里。检测到变更时,只有类加载器重启。 1:有些Classpath里的资源变更后不需要重启应用程序。像Thymeleaf这样的视图模板可以直接编辑,不用重启应用程序;在/static或/public里的静态资源也不用重启应用程序,所以Spring Boot开发者工具会在重启时排除掉如下目录:/META-INF/resources、/resources、/static、/public和 /templates2:可以设置spring.devtools.restart.exclude属性来覆盖默认的重启排除目录,例如:spring.devtools.restart.exclude=/static/,/templates/ 3:如果想彻底关闭自动重启,可以将spring.devtools.restart.enabled设置为false4:还可以设置一个触发文件,必须修改这个文件才能触发重启。例如,在修改名为my.trigger的文件前你都不希望执行重启,那么你只需像这样设置:spring.devtools.restart.trigger-file=my.trigger4 LiveReloadLiveReload支持不点刷新就直接看到修改结果,激活开发者工具后,Spring Boot会启动一个内嵌的LiveReload服务器,在资源文件变化时会触发 浏览器刷新。你要做的就是在浏览器里安装LiveReload插件。如 果 想 要 禁 用 内 嵌 的 LiveReload 服 务 器 , 可 以如下设置: spring.devtools.livereload. Enabled=false

July 3, 2020 · 1 min · jiezi

Springboot快速上手-第五篇-部署应用

1 部署可执行jarJava –jar 启动 2 部署war注意:就算我们在构建的是WAR文件,这个文件仍旧可以脱离应用服务器直接运行 部署到外部tomcat 1:pom里面修改jar为war,然后添加依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency>2:让启动类App继承SpringBootServletInitializer并重写configure方法,例如: public class App extends SpringBootServletInitializer{ @Override protected SpringApplicationBuilder configure(SpringApplicationBuilder application) { return application.sources(App.class); }}3:然后就可以打包部署到外部tomcat了 3 部署到Docker一:第一种方法1:直接把制作好的jar/war包上传到有docker的服务器上 2:然后在上面写Dockerfile,进行镜像制作 3:然后使用这个镜像 二:应用docker-maven-plugin插件工具来直接编译,生成docker镜像1:在开发机器上安装docker-toolbox,地址:https://www.docker.com/products/docker-toolbox,提示:不要去想一些其它捷径,比如什么开启Docker远程访问之类的方法(就是修改docker配置文件/usr/lib/systemd/system/docker.service,在ExecStart这行后面添加上: -H tcp://0.0.0.0:1234 -H unix:///var/run/docker.sock),这些基本上都是不可行的 2:安装好了之后,使用docker-machine的命令来操作,比如: 创建虚拟机(可以使用docker-machine来创建,也可以直接运行toolbox生成): docker-machine create –driver virtualbox default 启动虚拟机:docker-machine start default 停止虚拟机:docker-machine stop default使用ssh登录到default,登上后,就可以使用docker命令了: docker-machine ssh default 查看环境变量:docker-machine env 查看帮助:docker-machine help3:在工程的src/main下面新建一个docker文件夹,然后在里面放置Dockerfile,示例如下: FROM frolvlad/alpine-oraclejdk8:slimCOPY springbootstudy.jar springbootstudy.jarEXPOSE 8080ENTRYPOINT [ "java", "-Djava.security.egd=file:/dev/./urandom”, “-jar”, “/springbootstudy.jar" ]4:在pom里面添加plugin ...

July 3, 2020 · 1 min · jiezi

ssm学习相关笔记

1 2请求参数绑定实体类型 name的值需要与实体类相同 3超链接地址接变量 参数传递 前端如果是 href=“test/paramTest?username=hehe” 那么后端的方法里面参数test(String username)就会自动赋值 4如何拿到请求体的内容 比如一个表单 @requestBody 把数据使用json方式封装起来给参数 5Restful风格 请求地址都一样 但是请求方式不一样 这个10 就是代表id的值 {}代表占位符 findbyid(id)这个参数id使用@pathVariable可以拿到 6 Name=”_method”是固定写法 7 我们表单只提交了老王和age=35 birthday是数据库的数据提供的 这就是这个注解的作用 而且有这个注解的方法会先执行 所以把uname弄出来后查询数据库 把birthday查出来封装 在封装其他的属性 8 9 10如果返回值不是string类型的 那么默认去找路径.jsp/html 解决方法 重定向一下 需要写路径 或者 11modleAndView 写法和上面modle类似 跳转时候根据视图解析器返回success 前端就显示了对象名字和密码了 使用el表达式 12 请求转发的博客https://blog.csdn.net/weixin_40001125/article/details/88663468 ...

July 3, 2020 · 1 min · jiezi

恕我直言你可能真的不会java第12篇如何使用Stream-API对Map类型元素排序

在这篇文章中,您将学习如何使用Java对Map进行排序。前几日有位朋友面试遇到了这个问题,看似很简单的问题,但是如果不仔细研究一下也是很容易让人懵圈的面试题。所以我决定写这样一篇文章。在Java中,有多种方法可以对Map进行排序,但是我们将重点介绍Java 8 Stream,这是实现目标的一种非常优雅的方法。 一、什么是Java 8 Stream使用Java 8 Streams,我们可以按键和按值对映射进行排序。下面是它的工作原理: 将Map或List等集合类对象转换为Stream对象使用Streams的sorted()方法对其进行排序最终将其返回为LinkedHashMap(可以保留排序顺序)sorted()方法以aComparator作为参数,从而可以按任何类型的值对Map进行排序。如果对Comparator不熟悉,可以看本号前几天的文章,有一篇文章专门介绍了使用Comparator对List进行排序。 二、学习一下HashMap的merge()函数在学习Map排序之前,有必要讲一下HashMap的merge()函数,该函数应用场景就是当Key重复的时候,如何处理Map的元素值。这个函数有三个参数: 参数一:向map里面put的键参数二:向map里面put的值参数三:如果键发生重复,如何处理值。可以是一个函数,也可以写成lambda表达式。 String k = "key"; HashMap<String, Integer> map = new HashMap<String, Integer>() {{ put(k, 1); }}; map.merge(k, 2, (oldVal, newVal) -> oldVal + newVal);看上面一段代码,我们首先创建了一个HashMap,并往里面放入了一个键值为k:1的元素。当我们调用merge函数,往map里面放入k:2键值对的时候,k键发生重复,就执行后面的lambda表达式。表达式的含义是:返回旧值oldVal加上新值newVal(1+2),现在map里面只有一项元素那就是k:3。 其实lambda表达式很简单:表示匿名函数,箭头左侧是参数,箭头右侧是函数体。函数的参数类型和返回值,由代码上下文来确定。三、按Map的键排序下面一个例子使用Java 8 Stream按Map的键进行排序: // 创建一个Map,并填入数据Map<String, Integer> codes = new HashMap<>();codes.put("United States", 1);codes.put("Germany", 49);codes.put("France", 33);codes.put("China", 86);codes.put("Pakistan", 92);// 按照Map的键进行排序Map<String, Integer> sortedMap = codes.entrySet().stream() .sorted(Map.Entry.comparingByKey()) .collect( Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (oldVal, newVal) -> oldVal, LinkedHashMap::new ) );// 将排序后的Map打印sortedMap.entrySet().forEach(System.out::println);看上文中第二段代码: ...

July 3, 2020 · 1 min · jiezi

恕我直言你可能真的不会java第11篇Stream-API终端操作

一、Java Stream管道数据处理操作在本号之前写过的文章中,曾经给大家介绍过 Java Stream管道流是用于简化集合类元素处理的java API。在使用的过程中分为三个阶段。在开始本文之前,我觉得仍然需要给一些新朋友介绍一下这三个阶段,如图: 第一阶段(图中蓝色):将集合、数组、或行文本文件转换为java Stream管道流第二阶段(图中虚线部分):管道流式数据处理操作,处理管道中的每一个元素。上一个管道中的输出元素作为下一个管道的输入元素。第三阶段(图中绿色):管道流结果处理操作,也就是本文的将介绍的核心内容。在开始学习之前,仍然有必要回顾一下我们之前给大家讲过的一个例子: List<String> nameStrs = Arrays.asList("Monkey", "Lion", "Giraffe","Lemur");List<String> list = nameStrs.stream() .filter(s -> s.startsWith("L")) .map(String::toUpperCase) .sorted() .collect(toList());System.out.println(list);首先使用stream()方法将字符串List转换为管道流Stream然后进行管道数据处理操作,先用fliter函数过滤所有大写L开头的字符串,然后将管道中的字符串转换为大写字母toUpperCase,然后调用sorted方法排序。这些API的用法在本号之前的文章有介绍过。其中还使用到了lambda表达式和函数引用。最后使用collect函数进行结果处理,将java Stream管道流转换为List。最终list的输出结果是:[LEMUR, LION]如果你不使用java Stream管道流的话,想一想你需要多少行代码完成上面的功能呢?回到正题,这篇文章就是要给大家介绍第三阶段:对管道流处理结果都可以做哪些操作呢?下面开始吧! 二、ForEach和ForEachOrdered如果我们只是希望将Stream管道流的处理结果打印出来,而不是进行类型转换,我们就可以使用forEach()方法或forEachOrdered()方法。 Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .parallel() .forEach(System.out::println);Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .parallel() .forEachOrdered(System.out::println);parallel()函数表示对管道中的元素进行并行处理,而不是串行处理,这样处理速度更快。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证forEachOrdered从名字上看就可以理解,虽然在数据处理顺序上可能无法保障,但是forEachOrdered方法可以在元素输出的顺序上保证与元素进入管道流的顺序一致。也就是下面的样子(forEach方法则无法保证这个顺序):MonkeyLionGiraffeLemurLion三、元素的收集collectjava Stream 最常见的用法就是:一将集合类转换成管道流,二对管道流数据处理,三将管道流处理结果在转换成集合类。那么collect()方法就为我们提供了这样的功能:将管道流处理结果在转换成集合类。 3.1.收集为Set通过Collectors.toSet()方法收集Stream的处理结果,将所有元素收集到Set集合中。 Set<String> collectToSet = Stream.of( "Monkey", "Lion", "Giraffe", "Lemur", "Lion") .collect(Collectors.toSet());//最终collectToSet 中的元素是:[Monkey, Lion, Giraffe, Lemur],注意Set会去重。3.2.收集到List同样,可以将元素收集到List使用toList()收集器中。 List<String> collectToList = Stream.of( "Monkey", "Lion", "Giraffe", "Lemur", "Lion").collect(Collectors.toList());// 最终collectToList中的元素是: [Monkey, Lion, Giraffe, Lemur, Lion]3.3.通用的收集方式上面为大家介绍的元素收集方式,都是专用的。比如使用Collectors.toSet()收集为Set类型集合;使用Collectors.toList()收集为List类型集合。那么,有没有一种比较通用的数据元素收集方式,将数据收集为任意的Collection接口子类型。所以,这里就像大家介绍一种通用的元素收集方式,你可以将数据元素收集到任意的Collection类型:即向所需Collection类型提供构造函数的方式。 ...

July 2, 2020 · 1 min · jiezi

一篇告诉你什么是Spring

阅读全文大概需要7分钟 前言大多数文章讲"什么到Spring?"上来就是控制反转(IoC)或依赖注入(DI)和面向切面编程(AOP),拿着官网文档直译 copy。对小白来说并不友好,看完可能还是一头雾水。下面是我利用业余时间整理的一些资料、书籍和自己的理解,致力于更容易理解方式讲 Spring。[toc] 什么是Spring ?Spring 是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由 Rod Johnson 在其著作 Expert One-On-One J2EE Development and Design 中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring 使用基本的 JavaBean 来完成以前只可能由 EJB 完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring 是一个分层的 JavaSE/EE full-stack(一站式) 轻量级开源框架。 简单来说,它是一个容器框架,用来装 javabean(java对象),中间层框架(万能胶)可以起一个连接作用,比如说把 Struts 和 hibernate 粘合在一起运用。简单来说,Spring 是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。 如果你现在还有点疑惑,通读全文,你一定有所收获Spring 模块以下内容都是基于 Spring 4 及以上的Spring 官网图片 Spring Core:Spring核心模块,主要提供 ioC 依赖注入、Spring Context:向Spring框架提供上下文信息、Spring AOP:面向切面编程,为基于 Spring 的应用程序中的对象提供了事务管理服务、Spring JDBC:Java数据库连接、Spring JMS:Java消息服务、Spring ORM:用于支持 MyBatis、Hibernate 等 ORM 工具、Spring Web:为创建Web应用程序提供支持、Spring Test:提供了对 JUnit 和 TestNG 测试的支持、Spring Aspects:该模块为与AspectJ的集成提供支持。Spring Web:Spring框架支持与Struts集成,为基于web的应用程序提供了上下文。Spring的优点方便解耦,简化开发 (高内聚低耦合)Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护,交给Spring管理,spring工厂是用于生成bean ...

July 1, 2020 · 2 min · jiezi

Spring源码1容器的基本实现

背景使用了很久Spring,但对Spring的内在实现并不了解,正好最近有些时间,研究下Spring的源码 首先写一个测试方法 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml")); User userTest = (User) bf.getBean("testBean"); System.out.println(userTest.getEmail());和对应的配置文件beans.xml <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:myName="http://www.wjs.com/schema/user" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.wjs.com/schema/user http://www.wjs.com/schema/user.xsd"> <bean id = "testBean" class="com.zero.pojo.User"> <property name="userName" value="zhang"></property> <property name="email" value = "18"></property> </bean></beans>以上代码的功能使用过Spring的都能猜出来 读取配置文件beans.xml根据beans.xml中的配置找到对应的类,并进行实例化调用实例化后的类现在开始调试代码 1.1初始化ClassPathResourcenew ClassPathResource("beans.xml")进入代码时首先执行了new ClassPathResource("beans.xml"),在这里加载了配置文件路径到ClassPathResource的path属性中。 public ClassPathResource(String path) { this(path, (ClassLoader)null); } public ClassPathResource(String path, @Nullable ClassLoader classLoader) { Assert.notNull(path, "Path must not be null"); String pathToUse = StringUtils.cleanPath(path); if (pathToUse.startsWith("/")) { pathToUse = pathToUse.substring(1); } this.path = pathToUse; this.classLoader = classLoader != null ? classLoader : ClassUtils.getDefaultClassLoader(); }1.2初始化XmlBeanFactorynew XmlBeanFactory(new ClassPathResource("beans.xml"))初始化ClassPathResource后,开始初始化XmlBeanFactory,在构造函数内部再调用构造函数,this.reader.loadBeanDefinitions(resource);这个才是资源加载的真正实现 ...

July 1, 2020 · 8 min · jiezi

Spring源码3自定义标签的解析

背景上一篇我们介绍了默认标签的解析,本篇我们介绍默自定义标签的解析 1.修改原有工程1.1首先创建一个POJO,用来接收配置文件参数User.classpublic class User { private String id; private String userName; private String email; get/set方法省略}1.2定义一个XSD文件描述组件的内容user.xsd<?xml version="1.0" encoding="UTF-8"?><xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns="http://www.wjs.com/schema/user" targetNamespace="http://www.wjs.com/schema/user" elementFormDefault="qualified"> <xsd:element name="user"> <xsd:complexType> <xsd:attribute name="id" type="xsd:string" /> <xsd:attribute name="userName" type="xsd:string" /> <xsd:attribute name="email" type="xsd:string" /> </xsd:complexType> </xsd:element></xsd:schema>1.3创建java类实现AbstractSingleBeanDefinitionParser接口,用来解析XSD文件中的定义和组件UserBeanDefinitionParser.classpublic class UserBeanDefinitionParser extends AbstractSingleBeanDefinitionParser { @Override protected Class getBeanClass(Element element){ return User.class; } @Override protected void doParse(Element element, BeanDefinitionBuilder bean){ String userName = element.getAttribute("userName"); String email = element.getAttribute("email"); if(StringUtils.hasText(userName)){ bean.addPropertyValue("userName",userName); } if(StringUtils.hasText(email)){ bean.addPropertyValue("email",email); } }}1.4创建Handler文件,将组件注册到Spring容器MyNamespaceHandler.classpublic class MyNamespaceHandler extends NamespaceHandlerSupport { public void init() { registerBeanDefinitionParser("user",new UserBeanDefinitionParser()); }}1.5创建配置文件1.5.1handler配置文件spring.handlershttp\://www.wjs.com/schema/user=com.zero.test.MyNamespaceHandler1.5.2XSD配置文件spring.schemashttp\://www.wjs.com/schema/user.xsd=META-INF/user.xsd1.6修改配置文件,在配置文件中引入命名空间和XSDbeans.xml命名空间 ...

July 1, 2020 · 6 min · jiezi

Spring源码2默认标签的解析

背景上一篇我们介绍了容器的基本实现,结束时讲到这里进行XML的读取,本篇我们介绍默认标签(主要是bean)的解析 parseBeanDefinitions(root, this.delegate); /** * Parse the elements at the root level in the document: * "import", "alias", "bean". * @param root the DOM root element of the document */ protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; //默认标签 if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { //自定义标签 delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }1.对各类型标签进行处理parseDefaultElement(ele, delegate)标签的详细解析是在parseDefaultElement中进行的,分别处理import,alias,bean和beans四种不同的标签 ...

July 1, 2020 · 16 min · jiezi

Spring源码4bean的加载

背景上一篇我们介绍了自定义标签的解析,本篇我们介绍bean的加载 依旧是之前的测试方法 BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beans.xml")); User userTest = (User) bf.getBean("testBean"); System.out.println(userTest.getEmail());对于bean的加载,在Spring中的调用为 User userTest = (User) bf.getBean("testBean");1.加载流程1.转换对应的beanName传入的name可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,包括去除FactoryBean的修饰符,也就是说如果name="&aa",那么会首先去除&而使name="aa" 若指定alias所表示的最终beanName,例如别名A指向名称为B的bean,则返回B,若别名A指向别名B,别名B指向别名C,最终返回C this.transformedBeanName(name); 2.尝试从缓存中加载单例单例在Spring的同一个容器中只会被创建一次,后续再获取bean,就直接从单例缓存中获取了,当然这里也只是尝试加载,首先尝试从缓存加载,如果加载不成功则再次尝试从singletonFactories中加载,因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为例避免循环依赖,在Spring中创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建的时候需要依赖上一个bean则直接使用ObjectFactory this.getSingleton(beanName); 3.bean的实例化如果从缓存中得到了bean的原始状态,则需要对bean进行实例化,这里必须强调,缓存中记录的只是最原始的bean状态,并不一定是我们要的最终bean,举个例子,加入我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjecctForBeanInstance就是完成这个工作的。 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); 4.原型模式的依赖检查只有在单例模式下才会尝试解决循环依赖如果存在A中有B属性,B中有A属性,那么当依赖注入时,就会产生当A还未创建完成的时候,因为对于B的创建再次返回创建A,造成循环依赖,也就是说idpRrototypeCurrentlyInCreation(beanName)判断true 5.检查parentBeanFactory从代码上看,如果缓存没有数据的话,直接转到父类工厂上去加载了,判断条件parentBeanFaactory!=null&&!containsBeanDefinition(beanName)parentBeanFactory如果为空,则一切结束!containsBeanDefinition(beanName)能检测到当前加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试,然后再递归调用getBean方法 6.将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition因为从XML配置文件中读取到的bean信息是存储在GenericBeanDefinition中的,但是所有的bean后续处理都是针对RootBeanDefinition的,所以在这里需要一个转换,转换的同时,如果有父类的bean不为空的话,则会一并合并父类属性 7.寻找依赖因为bean初始化的过程很可能会用到某些属性,而某些属性很可能是动态配置的,且配置成依赖于其他其他的bean,那么在这个时候就必要先加载依赖的bean,所以在Spring的加载顺序中,初始化某一个bean的时候会先初始化这个bean对应的依赖 8.针对不同的scope进行bean创建在Spring中存在不同的Scope,期中默认是singletion,但是还有些其他配置诸如prototype,request之类,在这个步骤 中,Spring会根据不同的配置进行不同的初始化策略 9.类型转换程序到了这里返回bean后已经基本结束了,通常对该方法的调用参数requiredType是为空的,但是可能存在这样的情况,返回的bean实际是个String,但是requestType确传入Integer类型,那么这时候本步骤就会其作用了,它的功能是将返回的bean转换为requesType所指定的类型,Spring提供各种类型转换器 public Object getBean(String name) throws BeansException { return doGetBean(name, null, null, false); }2.执行doGetBean过程 doGetBean package org.springframework.beans.factory.support; public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory { private final ThreadLocal<Object> prototypesCurrentlyInCreation = new NamedThreadLocal<>("Prototype beans currently in creation"); /** * Return an instance, which may be shared or independent, of the specified bean. * @param name the name of the bean to retrieve * @param requiredType the required type of the bean to retrieve * @param args arguments to use when creating a bean instance using explicit arguments * (only applied when creating a new instance as opposed to retrieving an existing one) * @param typeCheckOnly whether the instance is obtained for a type check, * not for actual use * @return an instance of the bean * @throws BeansException if the bean could not be created * 返回一个实例,该实例可以是指定bean的共享或独立的。 * @param name要检索的bean的名称 * @param requiredType要检索的bean的必需类型 * @param args参数在使用显式参数创建bean实例时使用 *(仅适用于创建新实例而不是检索现有实例) * @param typeCheckOnly是否获取实例用于类型检查, * 不用于实际用途 * @返回bean的实例 * @throws BeansException如果无法创建bean */ @SuppressWarnings("unchecked") protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType, @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException { //提取对应的beanName final String beanName = transformedBeanName(name); Object bean; //检查缓存中或者实例工厂中是否有对应的实例 //在创建单例bean的时候存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则是不等bean创建完成的ObjectFactory提早曝光 //也就是将Objectactory加入到缓存中,一旦下一个bean创建的时候需要依赖上个bean则直接使用ObjectFactory //直接尝试从缓存获取,或者singletonFactories中的ObjectFactory中取 // Eagerly check singleton cache for manually registered singletons. Object sharedInstance = getSingleton(beanName); //缓存中或者实例工厂中存在对应的实例 if (sharedInstance != null && args == null) { if (logger.isDebugEnabled()) { if (isSingletonCurrentlyInCreation(beanName)) { logger.debug("Returning eagerly cached instance of singleton bean '" + beanName + "' that is not fully initialized yet - a consequence of a circular reference"); } else { logger.debug("Returning cached instance of singleton bean '" + beanName + "'"); } } //返回对应的实例,有时候存在诸如BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回的实例 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null); } //缓存中或者实例工厂中不存在对应的实例 else { //只有在单例情况下才会尝试解决循环依赖,原型模式的情况下如果存在A中有B属性,B中有A属性,那么当依赖注入的时候,就会产生当A还未创建完成的时候因为对于B的创建再次返回A造成循环依赖,也就是如下情况 // Fail if we're already creating this bean instance: // We're assumably within a circular reference. if (isPrototypeCurrentlyInCreation(beanName)) { //如果是Prototype(多例模式)并且bean正在创建中,如果是,说明存在循环依赖 throw new BeanCurrentlyInCreationException(beanName); } // Check if bean definition exists in this factory. BeanFactory parentBeanFactory = getParentBeanFactory(); //如果beanDefinitionMap中也就是在所有已加载的类中不包括beanName则尝试从parentBeanFactory中检测 if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { //递归到BeanFactory中寻找 // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } } //如果不仅仅做类型检查则是创建bean,这里要进行记录 if (!typeCheckOnly) { markBeanAsCreated(beanName); } try { //将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定BeanName是子Bean的话同时会合并父类的相关属性 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName); checkMergedBeanDefinition(mbd, beanName, args); // Guarantee initialization of beans that the current bean depends on. String[] dependsOn = mbd.getDependsOn(); //若存在依赖则需要递归实例化依赖的bean if (dependsOn != null) { for (String dep : dependsOn) { if (isDependent(beanName, dep)) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Circular depends-on relationship between '" + beanName + "' and '" + dep + "'"); } //缓存依赖调用 registerDependentBean(dep, beanName); try { getBean(dep);//在这里重新调用doGetBean方法,解析依赖的beanName即dep } catch (NoSuchBeanDefinitionException ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "'" + beanName + "' depends on missing bean '" + dep + "'", ex); } } } //依赖为空 //实例化依赖的bean后便可实例化mbd本身了 //singletion单例模式的创建 // Create bean instance. if (mbd.isSingleton()) { //缓存中或者实例工厂中不存在对应的实例,需要创建bean sharedInstance = getSingleton(beanName, () -> { try { //开始创建bean return createBean(beanName, mbd, args); } catch (BeansException ex) { // Explicitly remove instance from singleton cache: It might have been put there // eagerly by the creation process, to allow for circular reference resolution. // Also remove any beans that received a temporary reference to the bean. //销毁单例 destroySingleton(beanName); throw ex; } }); //bean的实例化 bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd); } //多例模式 else if (mbd.isPrototype()) { // It's a prototype -> create a new instance. //prototype模式的创建(new) Object prototypeInstance = null; try { //创建前回调,标记bean正在创建中(set进ThreadLocal) beforePrototypeCreation(beanName); //创建bean prototypeInstance = createBean(beanName, mbd, args); } finally { //创建bean后的回调,标记bean不在创建中(从ThreadLocal中移除) afterPrototypeCreation(beanName); } //bean的实例化 bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd); } else { //指定的scope上实例化bean String scopeName = mbd.getScope(); final Scope scope = this.scopes.get(scopeName); if (scope == null) { throw new IllegalStateException("No Scope registered for scope name '" + scopeName + "'"); } try { Object scopedInstance = scope.get(beanName, () -> { //创建前回调,标记bean正在创建中(set进ThreadLocal) beforePrototypeCreation(beanName); try { //创建bean return createBean(beanName, mbd, args); } finally { //创建bean后的回调,标记bean不在创建中(从ThreadLocal中移除) afterPrototypeCreation(beanName); } }); //bean的实例化 bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd); } catch (IllegalStateException ex) { throw new BeanCreationException(beanName, "Scope '" + scopeName + "' is not active for the current thread; consider " + "defining a scoped proxy for this bean if you intend to refer to it from a singleton", ex); } } } catch (BeansException ex) { cleanupAfterBeanCreationFailure(beanName); throw ex; } } // Check if required type matches the type of the actual bean instance. //检查需要的类型是否符合bean的实际类型,将返回的bean转换为requesType所指定的类型 if (requiredType != null && !requiredType.isInstance(bean)) { try { T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType); if (convertedBean == null) { throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } return convertedBean; } catch (TypeMismatchException ex) { if (logger.isDebugEnabled()) { logger.debug("Failed to convert bean '" + name + "' to required type '" + ClassUtils.getQualifiedName(requiredType) + "'", ex); } throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass()); } } return (T) bean; } 2.1转换对应的beanName transformedBeanNameString beanName = this.transformedBeanName(name);传入的name可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,包括去除FactoryBean的修饰符,也就是说如果name="&aa",那么会首先去除&而使name="aa" ...

July 1, 2020 · 42 min · jiezi

Spring-bean的循环依赖以及解决方式

什么是循环依赖循环依赖其实就是循环引用,也就是两个或则两个以上的bean互相持有对方,最终形成闭环。比如A依赖于B,B依赖于C,C又依赖于A,由于对象之间的依赖关系造成了死循环。 Spring中循环依赖场景有: 构造器的循环依赖field属性的循环依赖Spring怎么解决循环依赖Spring循环依赖主要基于Java引用传递,当获取到对象时,对象的field或者属性可以延后设置 创建Bean /** * Actually create the specified bean. Pre-creation processing has already happened * at this point, e.g. checking {@code postProcessBeforeInstantiation} callbacks. * <p>Differentiates between default bean instantiation, use of a * factory method, and autowiring a constructor. * @param beanName the name of the bean * @param mbd the merged bean definition for the bean * @param args explicit arguments to use for constructor or factory method invocation * @return a new instance of the bean * @throws BeanCreationException if the bean could not be created * @see #instantiateBean * @see #instantiateUsingFactoryMethod * @see #autowireConstructor * 实际创建指定的bean。 预创建处理已经发生 * 在这一点上,例如 检查{@code postProcessBeforeInstantiation}回调。 * <p>区分默认的bean实例化,使用 * 工厂方法,并自动装配构造函数。 * @param beanName bean的名称 * @param mbd该bean的合并bean定义 * @param args用于构造函数或工厂方法调用的显式参数 * @返回bean的新实例 * @throws BeanCreationException如果无法创建bean * @请参阅#instantiateBean * @see #instantiateUsingFactoryMethod * @请参阅#autowireConstructor */ protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final @Nullable Object[] args) throws BeanCreationException { // Instantiate the bean. BeanWrapper instanceWrapper = null; if (mbd.isSingleton()) { //从缓存中移除bean,因为要创建新的bean了 instanceWrapper = this.factoryBeanInstanceCache.remove(beanName); } if (instanceWrapper == null) { //根据指定bean的使用策略创建新的实例,如:工厂方法,构造函数自动注入,简单初始化 instanceWrapper = createBeanInstance(beanName, mbd, args); } final Object bean = instanceWrapper.getWrappedInstance(); Class<?> beanType = instanceWrapper.getWrappedClass(); if (beanType != NullBean.class) { mbd.resolvedTargetType = beanType; } // Allow post-processors to modify the merged bean definition. synchronized (mbd.postProcessingLock) { if (!mbd.postProcessed) { try { applyMergedBeanDefinitionPostProcessors(mbd, beanType, beanName); } catch (Throwable ex) { throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Post-processing of merged bean definition failed", ex); } mbd.postProcessed = true; } } // Eagerly cache singletons to be able to resolve circular references // even when triggered by lifecycle interfaces like BeanFactoryAware. //是否需要提早曝光:单例&允许循环依赖&当前bean正在创建中,检测循环依赖 boolean earlySingletonExposure = (mbd.isSingleton() && this.allowCircularReferences && isSingletonCurrentlyInCreation(beanName)); if (earlySingletonExposure) { if (logger.isDebugEnabled()) { logger.debug("Eagerly caching bean '" + beanName + "' to allow for resolving potential circular references"); } //为避免后期循环依赖,可以在bean初始化完成前将创建实例的ObjectFactory加入工厂,其他bean引用此bean时直接从ObjectFactory获取bean //getEarlyBeanReference对bean再一次依赖引用,主要应用SmartInstantiationAware BeanPost Processor其中我们熟悉的AOP就是在这里将advance动态织入bean中的,如果没有就直接返回bean,不做任何处理 addSingletonFactory(beanName, () -> getEarlyBeanReference(beanName, mbd, bean)); } // Initialize the bean instance. Object exposedObject = bean; try { //对bean进行填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归给初始依赖bean populateBean(beanName, mbd, instanceWrapper); //调用初始化方法,如init-method exposedObject = initializeBean(beanName, exposedObject, mbd); } catch (Throwable ex) { if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) { throw (BeanCreationException) ex; } else { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex); } } if (earlySingletonExposure) { Object earlySingletonReference = getSingleton(beanName, false); //earlySingletonReference只有在检测到有依赖循环的情况下才会不为空 if (earlySingletonReference != null) { //如果exposedObject没有在初始化方法中被改变,也就是没有被增强 if (exposedObject == bean) { exposedObject = earlySingletonReference; } else if (!this.allowRawInjectionDespiteWrapping && hasDependentBean(beanName)) { String[] dependentBeans = getDependentBeans(beanName); Set<String> actualDependentBeans = new LinkedHashSet<>(dependentBeans.length); for (String dependentBean : dependentBeans) { //检测依赖 if (!removeSingletonIfCreatedForTypeCheckOnly(dependentBean)) { actualDependentBeans.add(dependentBean); } } //因为bean创建后其所依赖的bean一定是已经创建的,actualDependentBeans不为空则表示当前bean创建后其依赖的bean却没有创建完,也就是存在循环依赖 if (!actualDependentBeans.isEmpty()) { throw new BeanCurrentlyInCreationException(beanName, "Bean with name '" + beanName + "' has been injected into other beans [" + StringUtils.collectionToCommaDelimitedString(actualDependentBeans) + "] in its raw version as part of a circular reference, but has eventually been " + "wrapped. This means that said other beans do not use the final version of the " + "bean. This is often the result of over-eager type matching - consider using " + "'getBeanNamesOfType' with the 'allowEagerInit' flag turned off, for example."); } } } } // Register bean as disposable. try { //根据scopse注册bean registerDisposableBeanIfNecessary(beanName, bean, mbd); } catch (BeanDefinitionValidationException ex) { throw new BeanCreationException( mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex); } return exposedObject; }以上是Spring创建bean的方法,代码很多但是核心思路就是实例化,填充属性,初始化,而循环依赖主要发生在实例化和填充属性这两部分 ...

July 1, 2020 · 4 min · jiezi

恕我直言你可能真的不会java第10篇集合元素归约

Stream API为我们提供了Stream.reduce用来实现集合元素的归约。reduce函数有三个参数: Identity标识:一个元素,它是归约操作的初始值,如果流为空,则为默认结果。Accumulator累加器:具有两个参数的函数:归约运算的部分结果和流的下一个元素。Combiner合并器(可选):当归约并行化时,或当累加器参数的类型与累加器实现的类型不匹配时,用于合并归约操作的部分结果的函数。注意观察上面的图,我们先来理解累加器: 阶段累加结果作为累加器的第一个参数集合遍历元素作为累加器的第二个参数Integer类型归约reduce初始值为0,累加器可以是lambda表达式,也可以是方法引用。 List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5, 6);int result = numbers .stream() .reduce(0, (subtotal, element) -> subtotal + element);System.out.println(result); //21int result = numbers .stream() .reduce(0, Integer::sum);System.out.println(result); //21String类型归约不仅可以归约Integer类型,只要累加器参数类型能够匹配,可以对任何类型的集合进行归约计算。 List<String> letters = Arrays.asList("a", "b", "c", "d", "e");String result = letters .stream() .reduce("", (partialString, element) -> partialString + element);System.out.println(result); //abcdeString result = letters .stream() .reduce("", String::concat);System.out.println(result); //ancde复杂对象归约计算所有的员工的年龄总和。 Employee e1 = new Employee(1,23,"M","Rick","Beethovan");Employee e2 = new Employee(2,13,"F","Martina","Hengis");Employee e3 = new Employee(3,43,"M","Ricky","Martin");Employee e4 = new Employee(4,26,"M","Jon","Lowman");Employee e5 = new Employee(5,19,"F","Cristine","Maria");Employee e6 = new Employee(6,15,"M","David","Feezor");Employee e7 = new Employee(7,68,"F","Melissa","Roy");Employee e8 = new Employee(8,79,"M","Alex","Gussin");Employee e9 = new Employee(9,15,"F","Neetu","Singh");Employee e10 = new Employee(10,45,"M","Naveen","Jain");List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10);Integer total = employees.stream().map(Employee::getAge).reduce(0,Integer::sum);System.out.println(total); //346先用map将Stream流中的元素由Employee类型处理为Integer类型(age)。然后对Stream流中的Integer类型进行归约Combiner合并器的使用除了使用map函数实现类型转换后的集合归约,我们还可以用Combiner合并器来实现,这里第一次使用到了Combiner合并器。因为Stream流中的元素是Employee,累加器的返回值是Integer,所以二者的类型不匹配。这种情况下可以使用Combiner合并器对累加器的结果进行二次归约,相当于做了类型转换。 ...

July 1, 2020 · 1 min · jiezi

HandlerMethodArgumentResolver

HandlerMethodArgumentResolverSpring的参数解析器顶层接口,将 HttpServletRequest(header + body 中的内容)解析为对应参数类型的对象,其中定义了2个方法 public interface HandlerMethodArgumentResolver { //是否支持解析该参数 boolean supportsParameter(MethodParameter parameter); //解析参数 Object resolveArgument(MethodParameter parameter, ModelAndViewContainer mavContainer, NativeWebRequest webRequest, WebDataBinderFactory binderFactory) throws Exception;}Spring启动时默认加载的HandlerMethodArgumentResolver实现类RequestAttributeMethodArgumentResolverSessionAttributeMethodArgumentResolverServletRequestMethodArgumentResolverServletResponseMethodArgumentResolverRedirectAttributesMethodArgumentResolverModelMethodProcessorHandlerMethodArgumentResolver 的设计模式策略模式:顶层 HandlerMethodArgumentResolver定义解析参数的方法,根据不同得策略实现对应的子类责任链模式:HandlerMethodArgumentResolverComposite.argumentResolvers循环遍历解析器,能解析则直接解析,不能则向下传递 模版模式:AbstractMessageConverterMethodArgumentResolver中定义解析参数的主逻辑, 子类 HttpEntityMethodProcessor | RequestResponseBodyMethodProcessor实现具体的逻辑

June 30, 2020 · 1 min · jiezi

恕我直言你可能真的不会java第9篇Stream元素的匹配与查找

在我们对数组或者集合类进行操作的时候,经常会遇到这样的需求,比如: 是否包含某一个“匹配规则”的元素是否所有的元素都符合某一个“匹配规则”是否所有元素都不符合某一个“匹配规则”查找第一个符合“匹配规则”的元素查找任意一个符合“匹配规则”的元素这些需求如果用for循环去写的话,还是比较麻烦的,需要使用到for循环和break!本节就介绍一个如何用Stream API来实现“查找与匹配”。 一、对比一下有多简单employees是10个员工对象组成的List,在前面的章节中我们已经用过多次,这里不再列出代码。如果我们不用Stream API实现,查找员工列表中是否包含年龄大于70的员工?代码如下: boolean isExistAgeThan70 = false;for(Employee employee:employees){ if(employee.getAge() > 70){ isExistAgeThan70 = true; break; }}如果我们使用Stream API就是下面的一行代码,其中使用到了我们之前学过的"谓词逻辑"。 boolean isExistAgeThan70 = employees.stream().anyMatch(Employee.ageGreaterThan70);将谓词逻辑换成lambda表达式也可以,代码如下: boolean isExistAgeThan72 = employees.stream().anyMatch(e -> e.getAge() > 72);所以,我们介绍了第一个匹配规则函数:anyMatch,判断Stream流中是否包含某一个“匹配规则”的元素。这个匹配规则可以是lambda表达式或者谓词。 二、其他匹配规则函数介绍是否所有员工的年龄都大于10岁?allMatch匹配规则函数:判断是够Stream流中的所有元素都符合某一个"匹配规则"。boolean isExistAgeThan10 = employees.stream().allMatch(e -> e.getAge() > 10);是否不存在小于18岁的员工?noneMatch匹配规则函数:判断是否Stream流中的所有元素都不符合某一个"匹配规则"。boolean isExistAgeLess18 = employees.stream().noneMatch(e -> e.getAge() < 18);三、元素查找与Optional从列表中按照顺序查找第一个年龄大于40的员工。 Optional<Employee> employeeOptional = employees.stream().filter(e -> e.getAge() > 40).findFirst();System.out.println(employeeOptional.get());打印结果 Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)Optional类代表一个值存在或者不存在。在java8中引入,这样就不用返回null了。 isPresent() 将在 Optional 包含值的时候返回 true , 否则返回 false 。ifPresent(Consumer<T> block) 会在值存在的时候执行给定的代码块。我们在第3章介绍了 Consumer 函数式接口;它让你传递一个接收 T 类型参数,并返回 void 的Lambda表达式。 ...

June 30, 2020 · 1 min · jiezi

SpringCloud第五篇熔断监控Hystrix-Dashboard和Turbine

Hystrix-dashboard是一款针对Hystrix进行实时监控的工具,通过Hystrix Dashboard我们可以在直观地看到各Hystrix Command的请求响应时间, 请求成功率等数据。但是只使用Hystrix Dashboard的话, 你只能看到单个应用内的服务信息, 这明显不够。我们需要一个工具能让我们汇总系统内多个服务的数据并显示到Hystrix Dashboard上, 这个工具就是Turbine。 1. Hystrix Dashboard创建一个新的项目hystrix-dashboard,延用上一篇文章提到的eureka和producer两个项目。 1. hystrix-dashboard pom.xml 依赖包管理<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springcloud</groupId> <artifactId>hystrix-dashboard</artifactId> <version>0.0.1-SNAPSHOT</version> <name>hystrix-dashboard</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>前面介绍过的包我这里不再多说,讲几个前面没有见过的包: ...

June 29, 2020 · 2 min · jiezi

恕我直言你可能真的不会java第8篇函数式接口

一、函数式接口是什么?所谓的函数式接口,实际上就是接口里面只能有一个抽象方法的接口。我们上一节用到的Comparator接口就是一个典型的函数式接口,它只有一个抽象方法compare。 只有一个抽象方法?那上图中的equals方法不是也没有函数体么?不急,和我一起往下看! 二、函数式接口的特点接口有且仅有一个抽象方法,如上图的抽象方法compare允许定义静态非抽象方法允许定义默认defalut非抽象方法(default方法也是java8才有的,见下文)允许java.lang.Object中的public方法,如上图的方法equals。FunctionInterface注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错甚至可以说:函数式接口是专门为lambda表达式准备的,lambda表达式是只实现接口中唯一的抽象方法的匿名实现类。 三、default关键字顺便讲一下default关键字,在java8之前 接口是不能有方法的实现,所有方法全都是抽象方法实现接口就必须实现接口里面的所有方法这就导致一个问题:当一个接口有很多的实现类的时候,修改这个接口就变成了一个非常麻烦的事,需要修改这个接口的所有实现类。 这个问题困扰了java工程师许久,不过在java8中这个问题得到了解决,没错就是default方法 default方法可以有自己的默认实现,即有方法体。接口实现类可以不去实现default方法,并且可以使用default方法。四、JDK中的函数式接口举例java.lang.Runnable, java.util.Comparator, java.util.concurrent.Callable java.util.function包下的接口,如Consumer、Predicate、Supplier等 五、自定义Comparator排序我们自定义一个排序器,实现compare函数(函数式接口Comparator唯一的抽象方法)。返回0表示元素相等,-1表示前一个元素小于后一个元素,1表示前一个元素大于后一个元素。这个规则和java 8之前没什么区别。 下面代码用自定义接口实现类的的方式实现:按照年龄的倒序排序! employees.sort(new Comparator<Employee>() { @Override public int compare(Employee em1, Employee em2) { if(em1.getAge() == em2.getAge()){ return 0; } return em1.getAge() - em2.getAge() > 0 ? -1:1; }});employees.forEach(System.out::println);最终的打印结果如下,按照年龄的自定义规则进行排序。 Employee(id=8, age=79, gender=M, firstName=Alex, lastName=Gussin)Employee(id=7, age=68, gender=F, firstName=Melissa, lastName=Roy)Employee(id=10, age=45, gender=M, firstName=Naveen, lastName=Jain)Employee(id=3, age=43, gender=M, firstName=Ricky, lastName=Martin)Employee(id=4, age=26, gender=M, firstName=Jon, lastName=Lowman)Employee(id=1, age=23, gender=M, firstName=Rick, lastName=Beethovan)Employee(id=5, age=19, gender=F, firstName=Cristine, lastName=Maria)Employee(id=9, age=15, gender=F, firstName=Neetu, lastName=Singh)Employee(id=6, age=15, gender=M, firstName=David, lastName=Feezor)Employee(id=2, age=13, gender=F, firstName=Martina, lastName=Hengis)这段代码如果以lambda表达式简写。箭头左侧是参数,右侧是函数体,参数类型和返回值根据上下文自动判断。如下: ...

June 29, 2020 · 1 min · jiezi

转置

【摘要】    SQL 提供的静态转置功能 pivot 和 unpivot 适用范围很受限,要用 SQL 实现一些比较复杂的转置功能常常会遇到语句过于复杂的问题,而且也缺少一个标准的解决思路。而集算器的 SPL 语言,则因其语法的灵活和函数库的丰富,恰好可以弥补 SQL 这方面的不足。若想了解更多,请前往乾学院一探究竟:转置 一. 行转列 数据库的 pivot SPL 的 pivot 二. 列转行 SQL 的 unpivot SPL 的列转行 三. 双向转置 四. 动态行列转置 pivot 函数自动生成列 动态行列转置 复杂静态行列转置 复杂动态行列转置 五. 转置同时列间计算 六. 表间关联列转行 子表动态插入主表 多表关联列转行 七. 分栏 ...

June 28, 2020 · 1 min · jiezi

FactoryBean

FactoryBean<T>是一个接口,实现此接口的Bean是交由FactoryBean管理的Bean。Spring中共有二种bean,一种是BeanFactory管理的bean,另一种是实现FactoryBean接口的Bean。 FactoryBean管理的Bean实际上也是由Spring进行管理、实例化,配置,因此由FactoryBean管理的Bean不能再次配置到Spring配置文件中,否则会报异常。 从spring的applicationContext或BeanFactory获取bean时,获取到是FactoryBean创建的实例,而不是FactoryBean自己;如果想通过Spring拿到FactoryBean,需要在名称前面加&符号。 FactoryBean在IOC的应用,在获取Bean的时候,都会经过getObjectForBeanInstance方法,这个方法会进行如下判断: 1.如果Bean是被FactoryBean引用,但Bean对象不是Factory相关(以&为前缀),则抛出异常(BeanIsNotAFactoryException);也就是说通过FactoryBean获取Bean,这个Bean不能配置到Spring配置文件或者注解实例化; 2.如果Bean实例不是FactoryBean,或者指定名称是FactoryBean引用,也就是普通的bean调用,则直接返回; 3.以上情况都不是,则调用FactoryBean获取Bean

June 28, 2020 · 1 min · jiezi

SpringCloud-第三篇服务的提供与Feign调用

上一篇,我们介绍了注册中心的搭建,包括集群环境吓注册中心的搭建,这篇文章介绍一下如何使用注册中心,创建一个服务的提供者,使用一个简单的客户端去调用服务端提供的服务。 本篇文章中需要三个角色,分别是服务的提供者,服务的消费者,还有一个是上一篇文章的主角——注册中心Eureka(使用单机版本即可,本篇的示例也会使用单机版本的Eureka)。 整体流程为: 先启动注册中心Eureka启动服务的提供者将提供服务,并将服务注册到注册中心Eureka上启动服务的消费者,在注册中心中找到服务并完成消费1. 服务提供者1. pom.xml<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.springcloud</groupId> <artifactId>producer</artifactId> <version>0.0.1-SNAPSHOT</version> <name>producer</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> <spring-cloud.version>Greenwich.SR1</spring-cloud.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-dependencies</artifactId> <version>${spring-cloud.version}</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>2. 配置文件application.ymlserver: port: 8080spring: application: name: spring-cloud-producereureka: client: service-url: defaultZone: http://localhost:8761/eureka/3. 启动类ProducerApplication.java增加@EnableEurekaClient,如果是其他注册中心可以使用注解@EnableDiscoveryClient来进行服务的注册 ...

June 28, 2020 · 2 min · jiezi

SpringCloud第四篇熔断器Hystrix

1. 熔断器服务雪崩在正常的微服务架构体系下,一个业务很少有只需要调用一个服务就可以返回数据的情况,这种比较常见的是出现在demo中,一般都是存在调用链的,比如A->B->C->D,如果D在某一个瞬间出现问题,比如网络波动,io偏高,导致卡顿,随着时间的流逝,后续的流量继续请求,会造成D的压力上升,有可能引起宕机。(了解源码可+求求: 1791743380) 你以为这就是结束么,图样图森破,这才是噩梦的开始,在同一个调用链上的ABC三个服务都会随着D的宕机而引发宕机,这还不是结束,一个服务不可能只有一个接口,当它开始卡顿宕机时,会影响到其他调用链的正常调用,最终导致所有的服务瘫痪。 如下图所示: (https://springcloud-oss.oss-c... 熔断器相信大家都知道家用电闸,原来老式的电闸是使用保险丝的(现在很多都是空气开关了),当家里用电量过大的时候,保险丝经常烧断,这么做是保护家里的用电器,防止过载。 熔断器的作用和这个很像,它可以实现快速失败,如果在一段时间内服务调用失败或者异常,会强制要求当前调用失败,不在走远程调用,走服务降级操作(返回固定数据或者其他一些降级操作)。从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以自动诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。 熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误。 Hystrix会有一个熔断时间窗口,具体转换逻辑如下: (https://springcloud-oss.oss-c... 熔断器就是保护服务高可用的最后一道防线。 2. Hystrix1. 断路器机制断路器很好理解, 当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务。断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN)。Hystrix的断路器就像我们家庭电路中的保险丝, 一旦后端服务不可用, 断路器会直接切断请求链, 避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力。 2. FallbackFallback相当于是降级操作。对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值。fallback方法的返回值一般是设置的默认值或者来自缓存。 3. 资源隔离在Hystrix中, 主要通过线程池来实现资源隔离。通常在使用的时候我们会根据调用的远程服务划分出多个线程池。例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池。这样做的主要优点是运行环境被隔离开了。这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响。但是带来的代价就是维护多个线程池会对系统带来额外的性能开销。如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源。 3. Feign Hystrix上一篇我们使用了producer和consumers,熔断器是只作用在服务调用端,因此上一篇使用到的consumers我们可以直接拿来使用。因为,Feign中已经依赖了Hystrix所以在maven配置上不用做任何改动。 1. 配置文件application.yml新增server: port: 8081spring: application: name: spring-cloud-consumerseureka: client: service-url: defaultZone: http://localhost:8761/eureka/feign: hystrix: enabled: true其中新增了feign.hystrix.enabled = true 2. 创建fallback类,继承与HelloRemote实现回调的方法package com.springcloud.consumers.fallback;import com.springcloud.consumers.remote.HelloRemote;import org.springframework.stereotype.Component;import org.springframework.web.bind.annotation.RequestParam;/** * Created with IntelliJ IDEA. * * @User: weishiyao * @Date: 2019/7/2 * @Time: 23:14 * @email: inwsy@hotmail.com * Description: */@Componentpublic class HelloRemoteFallBack implements HelloRemote { @Override public String hello(@RequestParam(value = "name") String name) { return "hello " + name + ", i am fallback massage"; }}3. 添加fallback属性在HelloRemote类添加指定fallback类,在服务熔断的时候返回fallback类中的内容。 ...

June 28, 2020 · 1 min · jiezi

恕我直言你可能真的不会java第7篇像使用SQL一样排序集合

在开始之前,我先卖个关子提一个问题:我们现在有一个Employee员工类。 @Data@AllArgsConstructorpublic class Employee { private Integer id; private Integer age; //年龄 private String gender; //性别 private String firstName; private String lastName;}你知道怎么对一个Employee对象组成的List集合,先按照性别字段倒序排序,再按照年龄的倒序进行排序么?如果您不知道4行代码以内的解决方案(其实是1行代码就可以实现,但笔者格式化为4行),我觉得您有必要一步步的看下去。 一、字符串List排序cities是一个字符串数组。注意london的首字母是小写的。 List<String> cities = Arrays.asList( "Milan", "london", "San Francisco", "Tokyo", "New Delhi");System.out.println(cities);//[Milan, london, San Francisco, Tokyo, New Delhi]cities.sort(String.CASE_INSENSITIVE_ORDER);System.out.println(cities);//[london, Milan, New Delhi, San Francisco, Tokyo]cities.sort(Comparator.naturalOrder());System.out.println(cities);//[Milan, New Delhi, San Francisco, Tokyo, london]当使用sort方法,按照String.CASE_INSENSITIVE_ORDER(字母大小写不敏感)的规则排序,结果是:[london, Milan, New Delhi, San Francisco, Tokyo]如果使用Comparator.naturalOrder()字母自然顺序排序,结果是:[Milan, New Delhi, San Francisco, Tokyo, london]同样我们可以把排序器Comparator用在Stream管道流中。 cities.stream().sorted(Comparator.naturalOrder()).forEach(System.out::println);//Milan//New Delhi//San Francisco//Tokyo//london在java 7我们是使用Collections.sort()接受一个数组参数,对数组进行排序。在java 8之后可以直接调用集合类的sort()方法进行排序。sort()方法的参数是一个比较器Comparator接口的实现类,Comparator接口的我们下一节再给大家介绍一下。 ...

June 28, 2020 · 2 min · jiezi

Spring中构建BeanDefinition的两种方法

BeanDefinitionBuilderGenericBeanDefinitionRumenzA.class package com.rumenz;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Component;import javax.annotation.PostConstruct;public class RumenzA { private String id; private String name; public String getId() { return id; } public void setId(String id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public RumenzA() { System.out.println("RumenzA 无参构造方法"); }}BeanDefinitionBuilder //1.通过BeanDefinitionBuilder BeanDefinitionBuilder bdb=BeanDefinitionBuilder.genericBeanDefinition(RumenzA.class); //设置属性 bdb.addPropertyValue("id",1).addPropertyValue("name","入门小站"); //获取BeanDefinition BeanDefinition bd=bdb.getBeanDefinition(); //后期的BeanDefinition还是可以修改的 System.out.println(bd); GenericBeanDefinition //2.通过GenericBeanDefinition GenericBeanDefinition gb= new GenericBeanDefinition(); //设置bean gb.setBeanClass(RumenzA.class); //设置属性 MutablePropertyValues mp=new MutablePropertyValues(); mp.add("id",1).add("name","入门小站"); gb.setPropertyValues(mp); System.out.println(gb); ...

June 27, 2020 · 1 min · jiezi

修炼内功springframework-7-Spring-Framework中的注解是如何运作的

本文已收录【修炼内功】跃迁之路 微信搜索 林中小舍,林小二带你聊技术截止本篇,已经介绍了Spring中的Resource、BeanDefinitionReader、BeanFactory、ApplicationContext、AOP等,本篇重点介绍Spring Framework中基于注解的Bean装配原理注解的使用大大简化了配置的过程,也更能表现代码与配置之间的直接关系,但同时也失去了部分配置统一管理的能力,对代码也有一定的侵入性 这似乎并不影响开发者对注解使用的高涨热情,使用注解还是XML进行配置开发并没有统一的定论,以何种方式进行开发还是需要看具体的项目适合什么 Are annotations better than XML for configuring Spring?The introduction of annotation-based configuration raised the question of whether this approach is “better” than XML. The short answer is “it depends.” The long answer is that each approach has its pros and cons, and, usually, it is up to the developer to decide which strategy suits them better. Due to the way they are defined, annotations provide a lot of context in their declaration, leading to shorter and more concise configuration. However, XML excels at wiring up components without touching their source code or recompiling them. Some developers prefer having the wiring close to the source while others argue that annotated classes are no longer POJOs and, furthermore, that the configuration becomes decentralized and harder to control. ...

June 26, 2020 · 6 min · jiezi

近两年流行面试题Spring循环依赖问题

作者:Vt 原文:https://juejin.im/post/5e927e... 前言Spring 如何解决的循环依赖,是近两年流行起来的一道Java 面试题。 其实笔者本人对这类框架源码题还是持一定的怀疑态度的。 如果笔者作为面试官,可能会问一些诸如 “如果注入的属性为 null,你会从哪几个方向去排查” 这些场景题。 那么既然写了这篇文章,闲话少说,发车看看 Spring 是如何解决的循环依赖,以及带大家看清循环依赖的本质是什么。 正文通常来说,如果问 Spring 内部如何解决循环依赖,一定是单默认的单例 Bean 中,属性互相引用的场景。 比如几个 Bean 之间的互相引用: 甚至自己 “循环” 依赖自己: 先说明前提:原型 (Prototype) 的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常。 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName);}原因很好理解,创建新的 A 时,发现要注入原型字段 B,又创建新的 B 发现要注入原型字段 A... 这就套娃了, 你猜是先 StackOverflow 还是 OutOfMemory? Spring 怕你不好猜,就先抛出了 BeanCurrentlyInCreationException 基于构造器的循环依赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。 那么默认单例的属性注入场景,Spring 是如何支持循环依赖的? Spring 解决循环依赖首先,Spring 内部维护了三个 Map,也就是我们通常说的三级缓存。 笔者翻阅 Spring 文档倒是没有找到三级缓存的概念,可能也是本土为了方便理解的词汇。 在 Spring 的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个 Map: singletonObjects 它是我们最熟悉的朋友,俗称 “单例池”“容器”,缓存创建完成单例 Bean 的地方。 ...

June 26, 2020 · 2 min · jiezi

Spring中的BeanDefinition

BeanDefinition 是 Spring Framework 中定义 Bean 的配置元信息接口.BeanDefinition描述一个bean. 包括bean的属性,构造函数参数列表,依赖bean,是否是单例,bean的类名等等** 打印BeanDefinition (BeanDefinition 并非 Bean 终态,可以自定义修改) Generic bean: class [com.rumenz.RumenzA] scope= abstract=false lazyInit=null autowireMode=0 dependencyCheck=0 autowireCandidate=true primary=false factoryBeanName=null factoryMethodName=null initMethodName=null destroyMethodName=null ** BeanDefinition元信息 源码 /* * Copyright 2002-2019 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * https://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */package org.springframework.beans.factory.config;import org.springframework.beans.BeanMetadataElement;import org.springframework.beans.MutablePropertyValues;import org.springframework.core.AttributeAccessor;import org.springframework.core.ResolvableType;import org.springframework.lang.Nullable;/** * A BeanDefinition describes a bean instance, which has property values, * constructor argument values, and further information supplied by * concrete implementations. * * <p>This is just a minimal interface: The main intention is to allow a * {@link BeanFactoryPostProcessor} to introspect and modify property values * and other bean metadata. * * @author Juergen Hoeller * @author Rob Harrop * @since 19.03.2004 * @see ConfigurableListableBeanFactory#getBeanDefinition * @see org.springframework.beans.factory.support.RootBeanDefinition * @see org.springframework.beans.factory.support.ChildBeanDefinition */public interface BeanDefinition extends AttributeAccessor, BeanMetadataElement { /** * Scope identifier for the standard singleton scope: {@value}. * <p>Note that extended bean factories might support further scopes. * @see #setScope * @see ConfigurableBeanFactory#SCOPE_SINGLETON */ String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON; /** * Scope identifier for the standard prototype scope: {@value}. * <p>Note that extended bean factories might support further scopes. * @see #setScope * @see ConfigurableBeanFactory#SCOPE_PROTOTYPE */ String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE; /** * Role hint indicating that a {@code BeanDefinition} is a major part * of the application. Typically corresponds to a user-defined bean. */ int ROLE_APPLICATION = 0; /** * Role hint indicating that a {@code BeanDefinition} is a supporting * part of some larger configuration, typically an outer * {@link org.springframework.beans.factory.parsing.ComponentDefinition}. * {@code SUPPORT} beans are considered important enough to be aware * of when looking more closely at a particular * {@link org.springframework.beans.factory.parsing.ComponentDefinition}, * but not when looking at the overall configuration of an application. */ int ROLE_SUPPORT = 1; /** * Role hint indicating that a {@code BeanDefinition} is providing an * entirely background role and has no relevance to the end-user. This hint is * used when registering beans that are completely part of the internal workings * of a {@link org.springframework.beans.factory.parsing.ComponentDefinition}. */ int ROLE_INFRASTRUCTURE = 2; // Modifiable attributes /** * Set the name of the parent definition of this bean definition, if any. */ void setParentName(@Nullable String parentName); /** * Return the name of the parent definition of this bean definition, if any. */ @Nullable String getParentName(); /** * Specify the bean class name of this bean definition. * <p>The class name can be modified during bean factory post-processing, * typically replacing the original class name with a parsed variant of it. * @see #setParentName * @see #setFactoryBeanName * @see #setFactoryMethodName */ void setBeanClassName(@Nullable String beanClassName); /** * Return the current bean class name of this bean definition. * <p>Note that this does not have to be the actual class name used at runtime, in * case of a child definition overriding/inheriting the class name from its parent. * Also, this may just be the class that a factory method is called on, or it may * even be empty in case of a factory bean reference that a method is called on. * Hence, do <i>not</i> consider this to be the definitive bean type at runtime but * rather only use it for parsing purposes at the individual bean definition level. * @see #getParentName() * @see #getFactoryBeanName() * @see #getFactoryMethodName() */ @Nullable String getBeanClassName(); /** * Override the target scope of this bean, specifying a new scope name. * @see #SCOPE_SINGLETON * @see #SCOPE_PROTOTYPE */ void setScope(@Nullable String scope); /** * Return the name of the current target scope for this bean, * or {@code null} if not known yet. */ @Nullable String getScope(); /** * Set whether this bean should be lazily initialized. * <p>If {@code false}, the bean will get instantiated on startup by bean * factories that perform eager initialization of singletons. */ void setLazyInit(boolean lazyInit); /** * Return whether this bean should be lazily initialized, i.e. not * eagerly instantiated on startup. Only applicable to a singleton bean. */ boolean isLazyInit(); /** * Set the names of the beans that this bean depends on being initialized. * The bean factory will guarantee that these beans get initialized first. */ void setDependsOn(@Nullable String... dependsOn); /** * Return the bean names that this bean depends on. */ @Nullable String[] getDependsOn(); /** * Set whether this bean is a candidate for getting autowired into some other bean. * <p>Note that this flag is designed to only affect type-based autowiring. * It does not affect explicit references by name, which will get resolved even * if the specified bean is not marked as an autowire candidate. As a consequence, * autowiring by name will nevertheless inject a bean if the name matches. */ void setAutowireCandidate(boolean autowireCandidate); /** * Return whether this bean is a candidate for getting autowired into some other bean. */ boolean isAutowireCandidate(); /** * Set whether this bean is a primary autowire candidate. * <p>If this value is {@code true} for exactly one bean among multiple * matching candidates, it will serve as a tie-breaker. */ void setPrimary(boolean primary); /** * Return whether this bean is a primary autowire candidate. */ boolean isPrimary(); /** * Specify the factory bean to use, if any. * This the name of the bean to call the specified factory method on. * @see #setFactoryMethodName */ void setFactoryBeanName(@Nullable String factoryBeanName); /** * Return the factory bean name, if any. */ @Nullable String getFactoryBeanName(); /** * Specify a factory method, if any. This method will be invoked with * constructor arguments, or with no arguments if none are specified. * The method will be invoked on the specified factory bean, if any, * or otherwise as a static method on the local bean class. * @see #setFactoryBeanName * @see #setBeanClassName */ void setFactoryMethodName(@Nullable String factoryMethodName); /** * Return a factory method, if any. */ @Nullable String getFactoryMethodName(); /** * Return the constructor argument values for this bean. * <p>The returned instance can be modified during bean factory post-processing. * @return the ConstructorArgumentValues object (never {@code null}) */ ConstructorArgumentValues getConstructorArgumentValues(); /** * Return if there are constructor argument values defined for this bean. * @since 5.0.2 */ default boolean hasConstructorArgumentValues() { return !getConstructorArgumentValues().isEmpty(); } /** * Return the property values to be applied to a new instance of the bean. * <p>The returned instance can be modified during bean factory post-processing. * @return the MutablePropertyValues object (never {@code null}) */ MutablePropertyValues getPropertyValues(); /** * Return if there are property values values defined for this bean. * @since 5.0.2 */ default boolean hasPropertyValues() { return !getPropertyValues().isEmpty(); } /** * Set the name of the initializer method. * @since 5.1 */ void setInitMethodName(@Nullable String initMethodName); /** * Return the name of the initializer method. * @since 5.1 */ @Nullable String getInitMethodName(); /** * Set the name of the destroy method. * @since 5.1 */ void setDestroyMethodName(@Nullable String destroyMethodName); /** * Return the name of the destroy method. * @since 5.1 */ @Nullable String getDestroyMethodName(); /** * Set the role hint for this {@code BeanDefinition}. The role hint * provides the frameworks as well as tools with an indication of * the role and importance of a particular {@code BeanDefinition}. * @since 5.1 * @see #ROLE_APPLICATION * @see #ROLE_SUPPORT * @see #ROLE_INFRASTRUCTURE */ void setRole(int role); /** * Get the role hint for this {@code BeanDefinition}. The role hint * provides the frameworks as well as tools with an indication of * the role and importance of a particular {@code BeanDefinition}. * @see #ROLE_APPLICATION * @see #ROLE_SUPPORT * @see #ROLE_INFRASTRUCTURE */ int getRole(); /** * Set a human-readable description of this bean definition. * @since 5.1 */ void setDescription(@Nullable String description); /** * Return a human-readable description of this bean definition. */ @Nullable String getDescription(); // Read-only attributes /** * Return a resolvable type for this bean definition, * based on the bean class or other specific metadata. * <p>This is typically fully resolved on a runtime-merged bean definition * but not necessarily on a configuration-time definition instance. * @return the resolvable type (potentially {@link ResolvableType#NONE}) * @since 5.2 * @see ConfigurableBeanFactory#getMergedBeanDefinition */ ResolvableType getResolvableType(); /** * Return whether this a <b>Singleton</b>, with a single, shared instance * returned on all calls. * @see #SCOPE_SINGLETON */ boolean isSingleton(); /** * Return whether this a <b>Prototype</b>, with an independent instance * returned for each call. * @since 3.0 * @see #SCOPE_PROTOTYPE */ boolean isPrototype(); /** * Return whether this bean is "abstract", that is, not meant to be instantiated. */ boolean isAbstract(); /** * Return a description of the resource that this bean definition * came from (for the purpose of showing context in case of errors). */ @Nullable String getResourceDescription(); /** * Return the originating BeanDefinition, or {@code null} if none. * Allows for retrieving the decorated bean definition, if any. * <p>Note that this method returns the immediate originator. Iterate through the * originator chain to find the original BeanDefinition as defined by the user. */ @Nullable BeanDefinition getOriginatingBeanDefinition();}方法解释String: getBeanClassName: 返回当前 bean definition 定义的类名ConstructorArgumentValues: getConstructorArgumentValues: 返回 bean 的构造函数参数String[]: getDependsOn: 返回当前 bean 所依赖的其他 bean 的名称String: getFactoryBeanName: 返回 factory bean 的名称String: getFactoryMethodName: 返回工厂方法的名称BeanDefinition: getOriginatingBeanDefinition: 返回原始的 BeanDefinition, 如果不存在返回nullString: getParentName: 返回当前 bean definition 的父 definition 的名字MutablePropertyValues: getPropertyValues: 返回一个用于新的 bean 实例上的属性值String: getScope: 返回当前 bean 的目标范围boolean: isAbstract: 当前 bean 是否是 abstract, 意味着不能被实例化boolean: isLazyInit: bean 是否是延迟初始化boolean: isPrimary: bean 是否为自动装配的主要候选 beanboolean: isPrototype: bean 是否是多实例boolean: isSingleton: bean 是否是单例void: setAutowiredCandidate(boolean): 设置 bean 是否对其他 bean 是自动装配的候选 beanvoid: setBeanClassName(String): 指定 bean definition 的类名void: setDependsOn(String ...): 设置当前 bean 初始化所依赖的 beans 的名称void: setFactoryBeanName(String): 如果 factory bean 的名称void: setFactoryMethodName(String): 设置工厂的方法名void: setLazyInit(boolean lazyInit): 设置是否延迟初始化void: setParentName(String): 设置父 definition 的名称void: setPrimary(boolean): 设置是否主要的候选 beanvoid: setScope(String): 设置 bean 的范围, 如: 单例, 多实例 ...

June 26, 2020 · 8 min · jiezi

SpringMVC-常用注解

@Controller在SpringMVC 中,控制器Controller 负责处理由DispatcherServlet 分发的请求,它把用户请求的数据经过业务处理层处理之后封装成一个Model ,然后再把该Model 返回给对应的View 进行展示。在SpringMVC 中提供了一个非常简便的定义Controller 的方法,无需继承特定的类或实现特定的接口,只需使用@Controller 标记一个类是Controller ,然后使用@RequestMapping 和@RequestParam 等一些注解用以定义URL 请求和Controller 方法之间的映射,这样的Controller 就能被外界访问到。此外Controller 不会直接依赖于HttpServletRequest 和HttpServletResponse 等HttpServlet 对象,它们可以通过Controller 的方法参数灵活的获取到。 @Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。单单使用@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。那么要如何做Spring 才能认识它呢?这个时候就需要把这个控制器类交给Spring 来管理。有两种方式: 在SpringMVC 的配置文件中定义MyController 的bean 对象。在SpringMVC 的配置文件中告诉Spring 该到哪里去找标记为@Controller 的Controller 控制器。<bean class="com.host.app.web.controller.MyController"/> < context:component-scan base-package = "com.host.app.web" /> @RestController相当于@Controller+@ResponseBody两个注解的结合,返回json数据不需要在方法前面加@ResponseBody注解了,使用@RestController这个注解,就不能返回jsp,html页面,视图解析器无法解析jsp,html页面 @RequestMappingRequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。 RequestMapping注解有六个属性,下面分成三类进行说明 1、 value, method; value:指定请求的实际地址,指定的地址可以是URI Template 模式; method:指定请求的method类型, GET、POST、PUT、DELETE等; 2、consumes,produces consumes:指定处理请求的提交内容类型(Content-Type),如application/json, text/html; produces:指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回; 3、params,headers params: 指定request中必须包含某些参数值,才让该方法处理。 headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。 @Resource和@Autowired@Resource和@Autowired都是注入时使用,@Resource并不是Spring的注解,需要导入javax.annotation.Resource,Spring支持该注解的注入。 @Autowired@Autowired为Spring提供的注解,需要导入org.springframework.beans.factory.annotation.Autowired;只按照byType注入。@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。 如@Autowired @Qualifier("userDao") private UserDao userDao; @Resource@Resource默认按照ByName自动注入,由J2EE提供,需要导入javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。 @Resource(name="userDao") private UserDao userDao; @Resource装配顺序: 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。 如果指定了type,则从上下文中找到匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。 @Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。 @ModelAttribute和 @SessionAttributes代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。 @SessionAttributes即将值放到session作用域中,写在class上面。 @PathVariable用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。如:@RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET)public String login(@PathVariable("userId") String userId, @PathVariable("roleId") String roleId){} @requestParam@requestParam主要用于在SpringMVC后台控制层获取参数,类似一种是request.getParameter("name"),它有三个常用参数:defaultValue = "0", required = false, value = "isApp";defaultValue 表示设置默认值,required 铜过boolean设置是否是必须要传入的参数,value 值表示接受的传入的参数类型。 ...

June 24, 2020 · 2 min · jiezi

恕我直言你可能真的不会java第6篇Stream性能差不要人云亦云

一、粉丝的反馈 问:stream比for循环慢5倍,用这个是为了啥?答:互联网是一个新闻泛滥的时代,三人成虎,以假乱真的事情时候发生。作为一个技术开发者,要自己去动手去做,不要人云亦云。 的确,这位粉丝说的这篇文章我也看过,我就不贴地址了,也没必要给他带流量。怎么说呢?就是一个不懂得测试的、不入流开发工程师做的性能测试,给出了一个危言耸听的结论。 二、所有性能测试结论都是片面的性能测试是必要的,但针对性能测试的结果,永远要持怀疑态度。为什么这么说? 性能测试脱离业务场景就是片面的性能测试。你能覆盖所有的业务场景么?性能测试脱离硬件环境就是片面的性能测试。你能覆盖所有的硬件环境么?性能测试脱离开发人员的知识面就是片面的性能测试。你能覆盖各种开发人员奇奇怪怪的代码么?所以,我从来不相信网上的任何性能测试的文章。凡是我自己的从事的业务场景,我都要在接近生产环境的机器上自己测试一遍。 所有性能测试结论都是片面的,只有你生产环境下的运行结果才是真的。 三、动手测试Stream的性能3.1.环境windows10 、16G内存、i7-7700HQ 2.8HZ 、64位操作系统、JDK 1.8.0_171 3.2.测试用例与测试结论我们在上一节,已经讲过: 针对不同的数据结构,Stream流的执行效率是不一样的针对不同的数据源,Stream流的执行效率也是不一样的所以记住笔者的话:所有性能测试结论都是片面的,你要自己动手做,相信你自己的代码和你的环境下的测试!我的测试结果仅仅代表我自己的测试用例和测试数据结构! 3.2.1.测试用例一测试用例:5亿个int随机数,求最小值测试结论(测试代码见后文): 使用普通for循环,执行效率是Stream串行流的2倍。也就是说普通for循环性能更好。Stream并行流计算是普通for循环执行效率的4-5倍。Stream并行流计算 > 普通for循环 > Stream串行流计算 3.2.测试用例二测试用例:长度为10的1000000随机字符串,求最小值测试结论(测试代码见后文): 普通for循环执行效率与Stream串行流不相上下Stream并行流的执行效率远高于普通for循环Stream并行流计算 > 普通for循环 = Stream串行流计算 3.3.测试用例三测试用例:10个用户,每人200个订单。按用户统计订单的总价。测试结论(测试代码见后文): Stream并行流的执行效率远高于普通for循环Stream串行流的执行效率大于等于普通for循环Stream并行流计算 > Stream串行流计算 >= 普通for循环 四、最终测试结论对于简单的数字(list-Int)遍历,普通for循环效率的确比Stream串行流执行效率高(1.5-2.5倍)。但是Stream流可以利用并行执行的方式发挥CPU的多核优势,因此并行流计算执行效率高于for循环。对于list-Object类型的数据遍历,普通for循环和Stream串行流比也没有任何优势可言,更不用提Stream并行流计算。虽然在不同的场景、不同的数据结构、不同的硬件环境下。Stream流与for循环性能测试结果差异较大,甚至发生逆转。但是总体上而言: Stream并行流计算 >> 普通for循环 ~= Stream串行流计算 (之所以用两个大于号,你细品)数据容量越大,Stream流的执行效率越高。Stream并行流计算通常能够比较好的利用CPU的多核优势。CPU核心越多,Stream并行流计算效率越高。stream比for循环慢5倍?也许吧,单核CPU、串行Stream的int类型数据遍历?我没试过这种场景,但是我知道这不是应用系统的核心场景。看了十几篇测试博文,和我的测试结果。我的结论是: 在大多数的核心业务场景下及常用数据结构下,Stream的执行效率比for循环更高。 毕竟我们的业务中通常是实实在在的实体对象,没事谁总对List<Int>类型进行遍历?谁的生产服务器是单核?。 五、测试代码<dependency> <groupId>com.github.houbb</groupId> <artifactId>junitperf</artifactId> <version>2.0.0</version></dependency>测试用例一: import com.github.houbb.junitperf.core.annotation.JunitPerfConfig;import com.github.houbb.junitperf.core.report.impl.HtmlReporter;import org.junit.jupiter.api.BeforeAll;import java.util.Arrays;import java.util.Random;public class StreamIntTest { public static int[] arr; @BeforeAll public static void init() { arr = new int[500000000]; //5亿个随机Int randomInt(arr); } @JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class}) public void testIntFor() { minIntFor(arr); } @JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class}) public void testIntParallelStream() { minIntParallelStream(arr); } @JunitPerfConfig( warmUp = 1000, reporter = {HtmlReporter.class}) public void testIntStream() { minIntStream(arr); } private int minIntStream(int[] arr) { return Arrays.stream(arr).min().getAsInt(); } private int minIntParallelStream(int[] arr) { return Arrays.stream(arr).parallel().min().getAsInt(); } private int minIntFor(int[] arr) { int min = Integer.MAX_VALUE; for (int anArr : arr) { if (anArr < min) { min = anArr; } } return min; } private static void randomInt(int[] arr) { Random r = new Random(); for (int i = 0; i < arr.length; i++) { arr[i] = r.nextInt(); } }}测试用例二: ...

June 24, 2020 · 3 min · jiezi

Spring-源码解读

《Spring 源码解读》傻瓜源码-内容简介傻瓜源码-内容简介????【职场经验】(持续更新)精编短文:如何成为值钱的Java开发-指南如何日常学习、如何书写简历、引导面试官、系统准备面试、选择offer、提高绩效、晋升TeamLeader.....????【源码解读】(持续更新) <br/>1. 源码选材:Java架构师必须掌握的所有框架和类库源码<br/>2. 内容大纲:按照“企业应用Demo”讲解执行源码:总纲“阅读指南”、第一章“源码基础”、第二章“相关Java基础”、第三章“白话讲源码”、第四章“代码解读”、第五章“设计模式”、第六章“附录-面试习题、相关JDK方法、中文注释可运行源码项目”3. 读后问题:粉丝群答疑解惑已收录:HashMap、ReentrantLock、ThreadPoolExecutor、《Spring源码解读》、《Dubbo源码解读》.....????【面试题集】(持续更新)<br/>1. 面试题选材:Java面试常问的所有面试题和必会知识点<br/>2. 内容大纲:第一部分”注意事项“、第二部分“面试题解读”(包括:”面试题“、”答案“、”答案详解“、“实际开发解说”)3. 深度/广度:面试题集中的答案和答案详解,都是对齐一般面试要求的深度和广度4. 读后问题:粉丝群答疑解惑已收录:Java基础面试题集、Java并发面试题集、JVM面试题集、数据库(Mysql)面试题集、缓存(Redis)面试题集 .....????【粉丝群】(持续更新) <br/>收录:阿里、字节跳动、京东、小米、美团、哔哩哔哩等大厂内推???? 作者介绍:Spring系源码贡献者、世界五百强互联网公司、TeamLeader、Github开源产品作者???? 作者微信:wowangle03 (企业内推联系我) 加入我的粉丝社群,阅读更多内容。从学习到面试,从面试到工作,从 coder 到 TeamLeader,每天给你答疑解惑,还能有第二份收入! 第 1 章 阅读指南本书基于 Spring 5.0.x(5.0.16.BUILD-SNAPSHOT)版本。本书根据”企业应用 Demo “解读源码。本书建议分为两个学习阶段,掌握了第一阶段,再进行第二阶段; 第一阶段,理解章节“源码解读”前的所有内容。即掌握 IT 技能:熟悉 Spring 原理。第二阶段,理解章节“源码解读”(包括源码解读)之后的内容。即掌握 IT 技能:精读 Spring 源码。建议按照本书内容顺序阅读(内容前后顺序存在依赖关系)。阅读过程中,如果遇到问题,记下来,后面不远的地方肯定有解答。阅读章节“源码解读”时,建议获得中文注释源码项目配合本书,Debug 进行阅读学习。源码项目中的注释含义; ”企业应用 Demo “在源码中,会标注“ // Spring Demo ”。在源码中的不易定位到的主线源码,会标注 “ // tofix 主线 ”。以下注释的源码,暂时不深入讲解: 在执行“企业应用 Demo ”过程中,没有执行到的源码(由于遍历空集合、 if 判断),会标注“ /* Demo不涉及 / ”。在执行”企业应用 Demo “过程中,有用变量的数据转换方法,输入值和输出值相同(由于遍历空集合、 if 判断没有处理数据),会标注“ /* 无效果 / ”。从头到尾都是空的变量(包括不包含元素的集合),会标注“ /* 空变量 / ”。有被赋值的变量,但“企业应用 Demo ”运行过程中没有使用到该变量,会标注” /* 无用逻辑 / “。不是核心逻辑,并且不影响源码理解,会标注” /* 非主要逻辑 / “。锁、异常处理逻辑、非空校验、日志打印没有标注注释 。第 2 章 Spring 实战2.1 源码本地构建下载作者详细中文注释后的 Spring 源码;保证本地已经安装 jdk(最好 1.8)、gradle(最好 4.4.1 版本);进入项目根目录,打开 cmd 或者 git 命令行,输入 ./gradlew :spring-oxm:compileTestJava 进行编译,如果中途编译失败,就重复编译几次;然后使用 Idea 导入项目,使用 gradle 进行 import;全局搜索“ // Spring Demo ”,运行“企业应用 Demo”。2.2 基础入门 Demo代码示例 1 ...

June 24, 2020 · 7 min · jiezi

源码剖析bean的生命周期

1.IOC容器从xml文件、java配置或注解配置中读取BeanDefinition 2.BeanDefinition分为普通BeanDefinition和特殊BeanDefinition(实现spring指定的某些接口),IOC容器会先实例化特殊BeanDefinition,这些特殊对象调用特定接口(例如BeanFactoryPostProcessor接口、BeanPostProcessor等),其中BeanFactoryPostProcessor可以动态的修改BeanFactory中的普通BeanDefinition 3.根据普通BeanDefinition实例化bean,即Instantiation 4.对bean进行属性赋值,即Populate 5.bean进行初始化,即Initialization 6.bean销毁,即Destruction 第3-5步的流程图 InstantiationAwareBeanPostProcessor是一种特殊的BeanPostProcessor,spring aop替换对象的时候是在 postProcessAfterInitialization处理

June 23, 2020 · 1 min · jiezi

图解Spring解决循环依赖

小Hub领读:Spring框架是所有java程序员都必须要懂的框架,都说ioc是核心,你知道如何解决循环依赖的问题么?看看这篇文章! 作者:Vt原文:https://juejin.im/post/5e927e... 前言Spring 如何解决的循环依赖,是近两年流行起来的一道 Java 面试题。 其实笔者本人对这类框架源码题还是持一定的怀疑态度的。 如果笔者作为面试官,可能会问一些诸如 “如果注入的属性为 null,你会从哪几个方向去排查” 这些场景题。 那么既然写了这篇文章,闲话少说,发车看看 Spring 是如何解决的循环依赖,以及带大家看清循环依赖的本质是什么。 正文通常来说,如果问 Spring 内部如何解决循环依赖,一定是单默认的单例 Bean 中,属性互相引用的场景。 比如几个 Bean 之间的互相引用: 甚至自己 “循环” 依赖自己: 先说明前提:原型 (Prototype) 的场景是不支持循环依赖的,通常会走到AbstractBeanFactory类中下面的判断,抛出异常。 if (isPrototypeCurrentlyInCreation(beanName)) {  throw new BeanCurrentlyInCreationException(beanName);}原因很好理解,创建新的 A 时,发现要注入原型字段 B,又创建新的 B 发现要注入原型字段 A... 这就套娃了, 你猜是先 StackOverflow 还是 OutOfMemory? Spring 怕你不好猜,就先抛出了 BeanCurrentlyInCreationException 基于构造器的循环依赖,就更不用说了,官方文档都摊牌了,你想让构造器注入支持循环依赖,是不存在的,不如把代码改了。 那么默认单例的属性注入场景,Spring 是如何支持循环依赖的? Spring 解决循环依赖首先,Spring 内部维护了三个 Map,也就是我们通常说的三级缓存。 笔者翻阅 Spring 文档倒是没有找到三级缓存的概念,可能也是本土为了方便理解的词汇。 在 Spring 的DefaultSingletonBeanRegistry类中,你会赫然发现类上方挂着这三个 Map: singletonObjects 它是我们最熟悉的朋友,俗称 “单例池”“容器”,缓存创建完成单例 Bean 的地方。singletonFactories 映射创建 Bean 的原始工厂earlySingletonObjects 映射 Bean 的早期引用,也就是说在这个 Map 里的 Bean 不是完整的,甚至还不能称之为 “Bean”,只是一个 Instance.后两个 Map 其实是 “垫脚石” 级别的,只是创建 Bean 的时候,用来借助了一下,创建完成就清掉了。 ...

June 23, 2020 · 2 min · jiezi

恕我直言你可能真的不会java第5篇Stream的状态与并行操作

一、回顾Stream管道流操作通过前面章节的学习,我们应该明白了Stream管道流的基本操作。我们来回顾一下: 源操作:可以将数组、集合类、行文本文件转换成管道流Stream进行数据处理中间操作:对Stream流中的数据进行处理,比如:过滤、数据转换等等终端操作:作用就是将Stream管道流转换为其他的数据类型。这部分我们还没有讲,我们后面章节再介绍。看下面的脑图,可以有更清晰的理解: 二、中间操作:有状态与无状态其实在程序员编程中,经常会接触到“有状态”,“无状态”,绝大部分的人都比较蒙。而且在不同的场景下,“状态”这个词的含义似乎有所不同。但是“万变不离其宗”,理解“状态”这个词在编程领域的含义,笔者教给大家几个关键点: 状态通常代表公用数据,有状态就是有“公用数据”因为有公用的数据,状态通常需要额外的存储。状态通常被多人、多用户、多线程、多次操作,这就涉及到状态的管理及变更操作。是不是更蒙了?举个例子,你就明白了 web开发session就是一种状态,访问者的多次请求关联同一个session,这个session需要存储到内存或者redis。多次请求使用同一个公用的session,这个session就是状态数据。vue的vuex的store就是一种状态,首先它是多组件公用的,其次是不同的组件都可以修改它,最后它需要独立于组件单独存储。所以store就是一种状态。回到我们的Stream管道流 filter与map操作,不需要管道流的前面后面元素相关,所以不需要额外的记录元素之间的关系。输入一个元素,获得一个结果。sorted是排序操作、distinct是去重操作。像这种操作都是和别的元素相关的操作,我自己无法完成整体操作。就像班级点名就是无状态的,喊到你你就答到就可以了。如果是班级同学按大小个排序,那就不是你自己的事了,你得和周围的同学比一下身高并记住,你记住的这个身高比较结果就是一种“状态”。所以这种操作就是有状态操作。三、Limit与Skip管道数据截取List<String> limitN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur") .limit(2) .collect(Collectors.toList());List<String> skipN = Stream.of("Monkey", "Lion", "Giraffe", "Lemur") .skip(2) .collect(Collectors.toList());limt方法传入一个整数n,用于截取管道中的前n个元素。经过管道处理之后的数据是:[Monkey, Lion]。skip方法与limit方法的使用相反,用于跳过前n个元素,截取从n到末尾的元素。经过管道处理之后的数据是: [Giraffe, Lemur]四、Distinct元素去重我们还可以使用distinct方法对管道中的元素去重,涉及到去重就一定涉及到元素之间的比较,distinct方法时调用Object的equals方法进行对象的比较的,如果你有自己的比较规则,可以重写equals方法。 List<String> uniqueAnimals = Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .distinct() .collect(Collectors.toList());上面代码去重之后的结果是: ["Monkey", "Lion", "Giraffe", "Lemur"] 五、Sorted排序默认的情况下,sorted是按照字母的自然顺序进行排序。如下代码的排序结果是:[Giraffe, Lemur, Lion, Monkey],字数按顺序G在L前面,L在M前面。第一位无法区分顺序,就比较第二位字母。 List<String> alphabeticOrder = Stream.of("Monkey", "Lion", "Giraffe", "Lemur") .sorted() .collect(Collectors.toList());排序我们后面还会给大家详细的讲一讲,所以这里暂时只做一个了解。 六、串行、并行与顺序通常情况下,有状态和无状态操作不需要我们去关心。除非?:你使用了并行操作。 还是用班级按身高排队为例:班级有一个人负责排序,这个排序结果最后就会是正确的。那如果有2个、3个人负责按大小个排队呢?最后可能就乱套了。一个人只能保证自己排序的人的顺序,他无法保证其他人的排队顺序。 串行的好处是可以保证顺序,但是通常情况下处理速度慢一些并行的好处是对于元素的处理速度快一些(通常情况下),但是顺序无法保证。这可能会导致进行一些有状态操作的时候,最后得到的不是你想要的结果。 Stream.of("Monkey", "Lion", "Giraffe", "Lemur", "Lion") .parallel() .forEach(System.out::println);parallel()函数表示对管道中的元素进行并行处理,而不是串行处理。但是这样就有可能导致管道流中后面的元素先处理,前面的元素后处理,也就是元素的顺序无法保证。如果数据量比较小的情况下,不太能观察到,数据量大的话,就能观察到数据顺序是无法保证的。MonkeyLionLemurGiraffeLion通常情况下,parallel()能够很好的利用CPU的多核处理器,达到更好的执行效率和性能,建议使用。但是有些特殊的情况下,parallel并不适合:深入了解请看这篇文章:https://blog.oio.de/2016/01/22/parallel-stream-processing-in-java-8-performance-of-sequential-vs-parallel-stream-processing/该文章中几个观点,说明并行操作的适用场景: 数据源易拆分:从处理性能的角度,parallel()更适合处理ArrayList,而不是LinkedList。因为ArrayList从数据结构上讲是基于数组的,可以根据索引很容易的拆分为多个。 适用于无状态操作:每个元素的计算都不得依赖或影响任何其他元素的计算,的运算场景。基础数据源无变化:从文本文件里面边读边处理的场景,不适合parallel()并行处理。parallel()一开始就容量固定的集合,这样能够平均的拆分、同步处理。欢迎关注我的博客,里面有很多精品合集本文转载注明出处(必须带连接,不能只转文字):字母哥博客。觉得对您有帮助的话,帮我点赞、分享!您的支持是我不竭的创作动力! 。另外,笔者最近一段时间输出了如下的精品内容,期待您的关注。 ...

June 23, 2020 · 1 min · jiezi

Spring事务管理实现方式注解Xml

事务隔离级别和传播行为Isolation :隔离级别 隔离级别是指若干个并发的事务之间的隔离程度,与我们开发时候主要相关的场景包括:脏读取、重复读、幻读。 具体的设置方式(注解):例如@Transactional(isolation = Isolation.DEFAULT ) 隔离级别含义DEFAULT使用数据库默认的事务隔离级别READ_UNCOMMITTED允许读取尚未提交的修改,可能导致脏读、幻读和不可重复读READ_COMMITTED允许从已经提交的事务读取,可防止脏读、但幻读,不可重复读仍然有可能发生REPEATABLE_READ对相同字段的多次读取的结果是一致的,除非数据被当前事务自生修改。可防止脏读和不可重复读,但幻读仍有可能发生SERIALIZABLE完全服从acid隔离原则,确保不发生脏读、不可重复读、和幻读,但执行效率最低。Propagation:传播行为 所谓事务的传播行为是指,如果在开始当前事务之前,一个事务上下文已经存在,此时有若干选项可以指定一个事务性方法的执行行为。 具体的设置方式(注解):@Transactional(propagation = Propagation.REQUIRED) 传播行为含义REQUIRED表示当前方法必须在一个具有事务的上下文中运行,如有客户端有事务在进行,那么被调用端将在该事务中运行,否则的话重新开启一个事务。(如果被调用端发生异常,那么调用端和被调用端事务都将回滚)MANDATORY表示当前方法必须在一个事务中运行,如果没有事务,将抛出异常NEVER表示当方法务不应该在一个事务中运行,如果存在一个事务,则抛出异常NOT_SUPPORTED表示该方法不应该在一个事务中运行。如果有一个事务正在运行,他将在运行期被挂起,直到这个事务提交或者回滚才恢复执行SUPPORTS表示当前方法不必需要具有一个事务上下文,但是如果有一个事务的话,它也可以在这个事务中运行NESTED表示如果当前方法正有一个事务在运行中,则该方法应该运行在一个嵌套事务中,被嵌套的事务可以独立于被封装的事务中进行提交或者回滚。如果封装事务存在,并且外层事务抛出异常回滚,那么内层事务必须回滚,反之,内层事务并不影响外层事务。如果封装事务不存在,则同propagation_required的一样REQUIRES_NEW表示当前方法必须运行在它自己的事务中。一个新的事务将启动,而且如果有一个现有的事务在运行的话,则这个方法将在运行期被挂起,直到新的事务提交或者回滚才恢复执行。基于Aspectj AOP配置事务几点说明: 导入外部资源文件 <context:property-placeholder location="classpath:db.properties"></context:property-placeholder注册组件包扫描,把类上标注了@Controller @Service @Repository @Component 都会自动加入到Spring容器中 <context:component-scan base-package="zfcoding.tx.aspectaop"></context:component-scan><<tx:advice>>配置一个事物通知,即执行的方法隔离级别和传播行为, <<aop:config>>配置事务通知在类上执行操作(切入点)。 <tx:advice id="txAdvice" transaction-manager="transactionManager"> <tx:attributes> <!-- 为连接点指定事务属性 --> <tx:method name="insert*" isolation="DEFAULT" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="point" expression="execution (* zfcoding.tx.aspectaop..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="point"></aop:advisor> </aop:config>实现步骤: 1、定义数据库的配置文件(db.properties),定义业务类UserDao,UserService。 2、定义Spring的配置文件(spring-aspect.xml) 导入通过<<context:property-placeholder>>导入数据库配置文件,然后通过包扫描的方式<<context:component-scan>>把UserDao,UserService注册到Spring的容器当中,配置数据额源,JDBC 的模板,最后事务管理器,配置事务通知,切入点。 配置文件(db.properties) db.username=rootdb.password=rootdb.url=jdbc:mysql://127.0.0.1:3306/springboot?serverTimezone=UTC&useSSL=false&autoReconnect=true&tinyInt1isBit=false&useUnicode=true&characterEncoding=utf8db.driverClass=com.mysql.jdbc.Driver 业务类(UserDao,UserService) @Repositorypublic class UserDao { @Autowired private JdbcTemplate jdbcTemplate; public void insertUser() { String sql = "INSERT INTO t_user (username,`password`) VALUES(?,?);"; String username = UUID.randomUUID().toString().substring(0, 3); jdbcTemplate.update(sql, username, 12); System.out.println("插入成功"); int i=10/0; }}@Servicepublic class UserService { @Autowired private UserDao userDao; public void insertUser() { userDao.insertUser(); }}Spring配置文件(spring-aspect.xml) ...

June 22, 2020 · 3 min · jiezi

MongoDB设计方法及技巧

MongoDB是一种流行的数据库,可以在不受任何表格schema模式的约束下工作。数据以类似JSON的格式存储,并且可以包含不同类型的数据结构。例如,在同一集合collection 中,我们可以拥有以下两个文档document: { id: '4', name: 'Mark', age: '21', addresses : [ { street: '123 Church St', city: 'Miami', cc: 'USA' }, { street: '123 Mary Av', city: 'Los Angeles', cc: 'USA' } ]}{ id: '15', name: 'Robin', department: 'New Business', example: 'robin@example.com'}为了能够充分利用MongoDB的优势,您必须了解并遵循一些基本的数据库设计原则。在讲解设计方法之前,我们必须首先了解MongoDB存储数据的结构。 一、 数据如何存储在MongoDB中与传统的RDBMS关系型数据库不同,MongoDB并没有表Table,行row和列column的概念。它将数据存储在集合collections,文档documents和字段fields中。下图说明了与RDBMS类比的结构之间的关系: 二、数据库设计技巧和窍门2.1.规范化存储与非规范化存储因为MongoDB使用文档来存储数据,所以理解“规范化存储“”和“非规范化存储”的概念非常重要。 规范化存储:-规范化意味着将数据存储到多个集合collections中,并在它们之间设计关联关系。数据保存之后,更新数据比较容易。但是在读取数据的时候,规范化存储的缺点就显现出来。如果要从多个集合collections查找数据,则必须执行多个查询,从而使读取数据的速度变慢。 (比如:将网页标题、作者、内容分别存储到不同的collections中) 非规范化存储:-这种方式将若干对象数据,以嵌套的方式存储到单个文档中。它在读取数据的时候表现更好,但在写入时会变慢。这种存储数据的方式还将占用更多空间。 (比如:将网页标题、作者、内容分别存储到同一个collection中) 所以在两种存储数据方式之间进行选择之前,先评估一下你的应用数据库的使用方式。 如果您有一个不需要频繁更新的数据,更新的即时一致性不是很重要,但是在读取时需要良好的性能,那么非规范化可能是明智的选择。(比如:我们博客的博文,作者一旦保存之后,几乎就不在进行频繁的修改,但是面临着读者频繁的读取阅读操作)如果数据库中的文档数据需要不断的更新,并且您希望在写入时具有良好的性能,那么您可能需要考虑规范化存储。(比如:需要频繁修改数据的业务类系统)2.2. 一对多关系与RDBMS相比,在MongoDB中对“一对多”关系建模需要进行更细粒度的设计。许多初学者陷入将文档数组嵌入父文档中的陷阱。正如我们在上文中介绍的,知道何时进行规范化存储或非规范化存储是非常重要的。因此设计者需要考虑关系的基数是“一个对少数几个”还是“一个对多个”?每种关系将具有不同的建模方法。  例如:下面“一个对少数几个”的建模示例。最好的建模方法是在父文档(persopn)中嵌入几个(address): > db.person.findOne(){  name: 'Mark Kornfield',  ssn: '1223-234-75554',  addresses : [     { street: '123 Church St', city: 'Miami', cc: 'USA' },     { street: '123 Mary Av', city: 'Los Angeles', cc: 'USA' } ]}在“一个对多个”示例中,我们将考虑设计两个集合,即产品products集合和零件parts集合。每个零件都有一个“ ObjectID”,该“ ObjectID”将出现在产品集合的引用中。这样的设计可以让读写性能更高效。 ...

June 22, 2020 · 1 min · jiezi

SpringBoot从入门到放弃之配置SpringDataJPA自动建表

pom文件配置引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>配置文件进行jpa配置这里有两个配置需要说明一下; show_sql: true 在控制台显示jpa生成的sqlformat_sql: true 控制台显示生成的sql的时候进行格式化ddl-auto: update 这种配置方式意思是没有表的时候新建表,有表的话就不会删除再新建,字段有更新的时候会自动更新表结构实体创建 服务启动可以看到咋子控台已经打印出建表的sql语句了 总结虽然这种自动建表的方式很方便,但是不建议在项目中使用这种方式还是要慎重,最好自己手写sql去建表。 欢迎大家去 我的博客 瞅瞅,里面有更多关于测试实战的内容哦!!

June 20, 2020 · 1 min · jiezi

SpringBoot-WEB系列WebFlux静态资源配置与访问

【SpringBoot WEB系列】WebFlux静态资源配置与访问上一篇博文介绍SpringMVC的静态资源访问,那么在WebFlux中,静态资源的访问姿势是否一致呢 <!-- more --> I. 默认配置与SpringBoot的默认配置一样,WebFlux同样是classpath:/META-INF/resources/,classpath:/resources/,classpath:/static/,classpath:/public/ 即,将静态文件放在这四个目录下,可以直接访问 1. 项目演示创建一个SpringBoot项目,添加依赖(本文使用的版本为: 2.2.1-RELEASE) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-webflux</artifactId></dependency>在资源路径下添加目录 static,目录下添加两个html文件,如下图 实现启动类,不添加额外逻辑,既可以直接通过完整url方式访问静态资源 @SpringBootApplicationpublic class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); }} 主要观察上面三个请求,放在index.html是无法直接访问到的,因为它所在的目录并不在默认的四个静态资源路径中 2. Url映射上面是直接通过静态资源文件名的方式进行访问,那么WebFlux是否可以实现SpringMVC那种,根据视图名返回View的方式呢? @Controllerpublic class ViewAction { @GetMapping(path = "a") public String a() { return "a.html"; }}直接访问,结果发现500,找不到名为a.html的视图 这种方式不行的话,改用WebFlux的路由写法 @Beanpublic RouterFunction<ServerResponse> indexRouter() { return RouterFunctions.route(RequestPredicates.GET("/b"), request -> ServerResponse.ok().contentType(MediaType.TEXT_HTML).bodyValue("b.html");} II. 自定义配置路径如果我们希望指定一个自定义的路径,是否可以如SpringMvc那样,修改配置or代码设置映射完成呢? 在资源目录下,新加两个文件夹,分别是 o1, o2 1. 配置修改如SpringMVC,修改静态资源配置 ...

June 18, 2020 · 1 min · jiezi

恕我直言你可能真的不会java第3篇Stream的Filter与谓词逻辑

一、基础代码准备建立一个实体类,该实体类有五个属性。下面的代码使用了lombok的注解Data、AllArgsConstructor,这样我们就不用写get、set方法和全参构造函数了。lombok会帮助我们在编译期生成这些模式化的代码。 @Data@AllArgsConstructorpublic class Employee { private Integer id; private Integer age; //年龄 private String gender; //性别 private String firstName; private String lastName;}写一个测试类,这个测试类的内容也很简单,新建十个Employee 对象 public class StreamFilterPredicate { public static void main(String[] args){ Employee e1 = new Employee(1,23,"M","Rick","Beethovan"); Employee e2 = new Employee(2,13,"F","Martina","Hengis"); Employee e3 = new Employee(3,43,"M","Ricky","Martin"); Employee e4 = new Employee(4,26,"M","Jon","Lowman"); Employee e5 = new Employee(5,19,"F","Cristine","Maria"); Employee e6 = new Employee(6,15,"M","David","Feezor"); Employee e7 = new Employee(7,68,"F","Melissa","Roy"); Employee e8 = new Employee(8,79,"M","Alex","Gussin"); Employee e9 = new Employee(9,15,"F","Neetu","Singh"); Employee e10 = new Employee(10,45,"M","Naveen","Jain"); List<Employee> employees = Arrays.asList(e1, e2, e3, e4, e5, e6, e7, e8, e9, e10); List<Employee> filtered = employees.stream() .filter(e -> e.getAge() > 70 && e.getGender().equals("M")) .collect(Collectors.toList()); System.out.println(filtered); }}需要注意的是上面的filter传入了lambda表达式(之前的章节我们已经讲过了),表达过滤年龄大于70并且男性的Employee员工。输出如下: ...

June 18, 2020 · 2 min · jiezi

SpringBoot-WEB系列静态资源配置与读取

【WEB系列】静态资源配置与读取SpringWeb项目除了我们常见的返回json串之外,还可以直接返回静态资源(当然在现如今前后端分离比较普遍的情况下,不太常见了),一些简单的web项目中,前后端可能就一个人包圆了,前端页面,js/css文件也都直接放在Spring项目中,那么你知道这些静态资源文件放哪里么 <!-- more --> I. 默认配置1. 配置静态资源路径,SpringBoot默认从属性spring.resources.static-locations中获取 默认值可以从org.springframework.boot.autoconfigure.web.ResourceProperties#CLASSPATH_RESOURCE_LOCATIONS获取 private static final String[] CLASSPATH_RESOURCE_LOCATIONS = { "classpath:/META-INF/resources/", "classpath:/resources/", "classpath:/static/", "classpath:/public/" };/** * Locations of static resources. Defaults to classpath:[/META-INF/resources/, * /resources/, /static/, /public/]. */private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;注意上面的默认值,默认有四个,优先级从高到低 /META-INF/resources//resources//static//public/2. 实例演示默认静态资源路径有四个,所以我们设计case需要依次访问这四个路径中的静态资源,看是否正常访问到;其次就是需要判定优先级的问题,是否和上面说的一致首先创建一个SpringBoot web项目,工程创建流程不额外多说,pom中主要确保有下面依赖即可(本文使用版本为: 2.2.1.RELEASE) <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>在资源文件夹resources下,新建四个目录,并添加html文件,用于测试是否可以访问到对应的资源文件(主要关注下图中标红的几个文件) a. META-INF/resources静态文件 m.html <html><title>META-INF/resource/m.html</title><body>jar包内,META-INF/resources目录下 m.html</body></html>完成对应的Rest接口 @GetMapping(path = "m")public String m() { return "m.html";} b. resources静态文件 r.html <html><title>resources/r.html</title><body>jar包内,resouces目录下 r.html</body></html>对应的Rest接口 @GetMapping(path = "r")public String r() { return "r.html";} ...

June 18, 2020 · 2 min · jiezi

spring5aop简单记录

一、What?AOP,面向切面编程 OOP面向对象编程的模式,OOP的主要功能是数据封装、继承和多态。OOP把系统看作多个对象的交互AOP是一种新的编程方式,AOP把系统分解为不同的关注点,或者称之为切面相关概念: Aspect:切面,即一个横跨多个核心逻辑的功能,或者称之为系统关注点Joinpoint:连接点,即定义在应用程序流程的何处插入切面的执行Pointcut:切入点,即一组连接点的集合Weaving:织入,指将切面整合到程序的执行流程中Target Object:目标对象,即真正执行业务的核心逻辑对象拦截器有以下类型: @Before:这种拦截器先执行拦截代码,再执行目标代码。如果拦截器抛异常,那么目标代码就不执行了;@After:这种拦截器先执行目标代码,再执行拦截器代码。无论目标代码是否抛异常,拦截器代码都会执行;@AfterReturning:和@After不同的是,只有当目标代码正常返回时,才执行拦截器代码;@AfterThrowing:和@After不同的是,只有当目标代码抛出了异常时,才执行拦截器代码;@Around:能完全控制目标代码是否执行,并可以在执行前后、抛异常后执行任意拦截代码,可以说是包含了上面所有功能。二、How?生产中使用注解装配AOP,不要使用AspectJ的类似execution( xxx.Xyz.(..))这样的复杂注解 三、Why?对于AOP的织入,有3种方式: 编译期:在编译时,由编译器把切面调用编译进字节码,这种方式需要定义新的关键字并扩展编译器,AspectJ就扩展了Java编译器,使用关键字aspect来实现织入;类加载器:在目标类被装载到JVM时,通过一个特殊的类加载器,对目标类的字节码重新“增强”;运行期:目标对象和切面都是普通Java类,通过JVM的动态代理功能或者第三方库实现运行期动态织入。Spring的AOP实现就是基于动态代理: Spring对接口类型使用JDK动态代理,对普通类使用CGLIB创建子类。如果一个Bean的class是final,Spring将无法为其创建子类。自动加super()的功能是Java编译器实现的,它发现你没加,就自动给加上,发现你加错了,就报编译错误。但实际上,如果直接构造字节码,一个类的构造方法中,不一定非要调用super()。Spring使用CGLIB构造的Proxy类,是直接生成字节码,并没有源码-编译-字节码这个步骤,所以: Spring通过CGLIB创建的代理类,不会初始化代理类自身继承的任何成员变量,包括final类型的成员变量!

June 13, 2020 · 1 min · jiezi