关于spring:BeanDefinition的加载过程

BeanDefiniton的介绍Spring容器的启动过程大抵能够分为两步: BeanDefinition 的加载。容器的初始化。咱们晓得应用Spring之前咱们须要先定义Bean, 个别有两种形式定义,别离是xml和注解,两种形式的大同小异,都须要先将Bean的定义转化为BeanDefinition, BeanDefinition 蕴含了初始化该Bean所须要的信息,能够认为BeanDefinition 是Bean的一种形容,一种定义。 BeanDefinition 是Spring中重要的一个构造,后续的容器中实例的初始化依赖于BeanDefinition。 具体的加载过程本文以xml为例来阐明BeanDefinition的加载过程。咱们首先来看BeanDefinition的继承体系。以上是BeanDefinition的继承体系,其中AbstractBeanDefinitin, GenericBeanDefiniton, RootBeanDefinition, ChildBeanDefiniton是BeanDefiniton加载过程中会用到的,而且其中AbstractBeanDefinitin 是GenericBeanDefiniton, RootBeanDefinition, ChildBeanDefiniton的公共父类。上面从上到下来看一下这些接口和类。 AttributeAccessor: 该接口申明了获取或者设置任意对象的元数据(metadata)的办法。AttributeAccessorSupport: 该抽象类实现了AttributeAccessor,定义了一个HashMap的属性 attributes 用于存储元数据。BeanMetadataElement: 该接口只申明了一个默认办法getSource, 用于获取配置源,配置源指的是元数据对应的源文件。BeanMetadataAttributeAccessor:该类继承了AttributeAccessorSupport 同时实现了BeanMetadataElement,应用一个名为source的变量保留配置源,实现了getSource,同时定义setSource用于设置配置源。BeanDefinition:该接口同时继承了BeanMetadataElement 和 AttributeAccessor 接口,定义了与BeanDefinition相干的常量和操作。这里对定义的常量进行简要的介绍: /** * 示意Bean的作用域为单例, 容器中只会存在一个实例 */String SCOPE_SINGLETON = ConfigurableBeanFactory.SCOPE_SINGLETON;/** * 示意Bean的作用域为原型,容器中存在多个实例 */String SCOPE_PROTOTYPE = ConfigurableBeanFactory.SCOPE_PROTOTYPE;/** * ROLE_APPLICATION 通常示意用户自定义的Bean */int ROLE_APPLICATION = 0;/** * ROLE_SUPPORT 示意这个 Bean 是某些简单配置的撑持局部 */int ROLE_SUPPORT = 1;/** * ROLE_INFRASTRUCTURE 示意这个Bean是Spring基础设施的一部分 */int ROLE_INFRASTRUCTURE = 2;BeanDefinition中定义的操作包含对parentName,beanClassName,scope, lazyInit, dependsOn, autowireCandite, primary, factoryBeanName, factoryMethodName, propertyValues, initMethodName, destroyMethodName, role, description, resourceDescription等属性的设置与获取。 ...

September 5, 2021 · 9 min · jiezi

关于spring:Spring系列之事物是如何管理的

前言咱们都晓得Spring给咱们提供了很多形象,比方咱们在操作数据库的过程中,它为咱们提供了事物方面的形象,让咱们能够十分不便的以事物形式操作数据库。不论你用JDBC、Mybatis、Hibernate等任何一种形式操作数据库,也不论你应用DataSource还是JTA的事物,Spring事物形象治理都能很好的把他对立在一起。接下来看一下事物的形象外围接口 Spring事务形象PlatformTransactionManager是事物管理器接口 //事务管理器接口有以下几个接口,获取事物信息,提交和回滚public interface PlatformTransactionManager { TransactionStatus getTransaction(@Nullable TransactionDefinition var1) throws TransactionException; void commit(TransactionStatus var1) throws TransactionException; void rollback(TransactionStatus var1) throws TransactionException;}常见的事物管理器有以下几种: DataSourceTransactionManagerHibernateTransactionManagerJtaTransactionManager 这些管理器都实现了PlatformTransactionManager中的三个接口,实现逻辑略有差异,然而对用户来讲区别不大定义事物的一些参数: 一些事物的参数在TransactionDefinition.java中,详情如下: public interface TransactionDefinition { int PROPAGATION_REQUIRED = 0; int PROPAGATION_SUPPORTS = 1; int PROPAGATION_MANDATORY = 2; int PROPAGATION_REQUIRES_NEW = 3; int PROPAGATION_NOT_SUPPORTED = 4; int PROPAGATION_NEVER = 5; int PROPAGATION_NESTED = 6; //默认隔离级别,和数据库的隔离级别统一 int ISOLATION_DEFAULT = -1; int ISOLATION_READ_UNCOMMITTED = 1; int ISOLATION_READ_COMMITTED = 2; int ISOLATION_REPEATABLE_READ = 4; int ISOLATION_SERIALIZABLE = 8; //默认不超时 int TIMEOUT_DEFAULT = -1;}上面两张图对这些参数进行了阐明: 7种事务流传个性: 四种事务隔离级别: 在看事务隔离级别前须要先理解下什么是脏读、不可反复读、幻读 脏读: 脏读就是一个事物未提交的数据,被另外一个事物读到了,显然这种状况不可承受 不可反复读: 不可反复读是指在一个事务内,屡次读同一数据,前后读取的后果不统一。 幻读: 事务A对表中的一个数据进行了批改,这种批改波及到表中的全副数据行。同时事务B也批改了这个表中的数据,这种批改是向表中插入一行新数据。那么就会产生操作事务A的用户发现表中还存在没有批改的数据行,就如同产生了幻觉一样 晓得了以上几个概念,咱们来看看隔离级别: ...

September 2, 2021 · 2 min · jiezi

关于spring:MySQL为什么不支持中文排序

前言或者都晓得,MySQL不反对中文排序,这样的说法能够说对也能够说也不对。接下来咱们剖析一下: 首先执行命令,查看编码集:SHOW VARIABLES LIKE 'character_set%';能够看到字符集是utf8mb4,这个字符集是市面上比拟应用十分多的字符集 咱们再看下,这个字符集对应的排序规定有哪些?show collation like 'utf8mb4%';图中能够看到,utf8mb4有很多的排序规定,比如说通用的uft8mb4_general_ci,瑞典文比拟规定utf8mb4_swedish_ci等等,然而就是没有中文的比拟规定,这也是为什么utf8mb4字符集不反对中文排序的起因。 事例: select * from area_code order by province_name;从图中能够看出,排序是不失效的 如果要依照中文排序。须要对字段编码进行转换,如下操作: select * from area_code order by CONVERT(province_name USING gbk); 能够看到,转成gbk形式就能够排序了,起因是在gbk字符集下,排序规定反对中文排序,如下图: 因而说,MySQL不反对中文排序,能够说对,也能够说不对。

September 1, 2021 · 1 min · jiezi

关于spring:Spring认证Spring-GraphQL

我很快乐地发表Spring GraphQL我的项目的创立以及面向 1.0 版本的初始里程碑的可用性。该我的项目集成了GraphQL Java和 Spring,并由两个团队合作开发。 明天是 GraphQL Java 的 6 岁生日!我从一开始就做出的一个根本决定是将任何 HTTP 和 IO 方面作为独自的关注点。GraphQL Java 始终“只是”一个执行 GraphQL 申请的引擎。这个决定曾经失去了回报,但显著的毛病是须要为事实世界的应用创立本人的 HTTP 适配器。 这导致多年来为 Spring 创立了大量 GraphQL 集成,包含来自 GraphQL Java 团队的GraphQL Java Spring我的项目。 但坦率地说,我始终渴望一流的 Spring 集成。 大概一年前到当初,GraphQL Java 和 Spring 团队举办了第一次会议,探讨如何实现这一指标。 在过来的 12 个月中,只管时区差别具备挑战性,但咱们在宽泛的主题上进行了单干和探讨。咱们当初筹备通过里程碑版本将这项工作带给更宽泛的受众。 特别感谢Rossen和Brian的鼎力单干,以及Rob和Mark以及越来越多的 Spring 工程师退出这项工作。 我真的置信这个我的项目对于 GraphQL Java 和更宽泛的 GraphQL 生态系统来说是一个微小的提高:由 Spring 工程师保护和倒退的 Spring 集成是 GraphQL 胜利的关键因素。 Spring GraphQL是GraphQL Java Spring的继承者。目标是让 Spring GraphQL 成为所有 Spring GraphQL 应用程序的根底,进而构建在 GraphQL Java 上。 ...

September 1, 2021 · 1 min · jiezi

关于spring:Spring认证Spring注入集合

您曾经理解了如何应用值属性配置原始数据类型,并应用Bean 配置文件中的 <property> 标记的ref属性配置对象援用。这两种状况都波及将奇怪值传递给 bean。 当初,如果您想传递多个值,例如 Java 汇合类型,例如 List、Set、Map 和 Properties,该怎么办。为了解决这种状况,Spring 提供了四种类型的汇合配置元素,如下所示 - 没有 元素和形容1 <列表> 这有助于接线,即注入值列表,容许反复。 2 <设置> 这有助于连贯一组值但没有任何反复。 3 <地图> 这可用于注入名称-值对的汇合,其中名称和值能够是任何类型。 4 <道具> 这可用于注入名称和值都是字符串的名称-值对汇合。 您能够应用 <list> 或 <set> 来连贯 java.util.Collection 或数组的任何实现。 您将遇到两种状况 (a) 传递汇合的间接值和 (b) 传递 bean 的援用作为汇合元素之一。 例子让咱们有一个工作的 Eclipse IDE 并采取以下步骤来创立一个 Spring 应用程序 - 脚步 形容1 创立一个名为SpringExample的我的项目,并在创立的我的项目的src文件夹下创立一个包com.tutorialspoint。2 应用增加内部 JAR选项增加所需的 Spring 库,如Spring Hello World 示例章节中所述。3 创立Java类JavaCollection和MainApp下com.tutorialspoint包。4 在src文件夹下创立 Beans 配置文件Beans.xml。5 最初一步是创立所有 Java 文件和 Bean 配置文件的内容并运行应用程序,如下所述。这是JavaCollection.java文件的内容- package com.tutorialspoint;import java.util.*; public class JavaCollection { List addressList; Set addressSet; Map addressMap; Properties addressProp; ...

August 31, 2021 · 3 min · jiezi

关于spring:Spring系列之多个数据源配置

前言在上篇文章讲到了如何配置单数据源,然而在理论场景中,会有须要配置多个数据源的场景,比如说,咱们在领取零碎中,单笔操作(蕴含查问、插入、新增)中须要操作主库,在批量查问或者对账单查问等对实时性要求不高的场景,须要应用读库来操作,顺次来加重数据库的压力。那么咱们如何配置多数据源? 这里还是基于springboot利用的状况下,咱们看一下怎么配置。 因为SpringBoot会实现主动配置,然而SpringBoot并不知道咱们的业务场景别离要应用哪一个数据源,因而咱们须要把相干的主动配置敞开。 首先,生成我的项目骨架,引入相应的依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency>而后,在Application排除主动拆卸类 @SpringBootApplication(exclude = { DataSourceAutoConfiguration.class,DataSourceTransactionManagerAutoConfiguration.class,JdbcTemplateAutoConfiguration.class})@Slf4jpublic class MultiDataSourceDemoApplication {}下面代码中,咱们排除了DataSourceAutoConfiguration、DataSourceTransactionManagerAutoConfiguration、JdbcTemplateAutoConfiguration三个类,而后就能够本人定义DataSource了。 配置数据源 //第一个数据源 @Bean @ConfigurationProperties("first.datasource") public DataSource firstDataSource() { return DataSourceBuilder.create().build(); } @Bean public JdbcTemplate firstJdbcTemplate() { return new JdbcTemplate(firstDataSource()); } @Bean @Resource public PlatformTransactionManager firstTxManager(DataSource firstDataSource) { return new DataSourceTransactionManager(firstDataSource); } //第二个数据源 @Bean @ConfigurationProperties("second.datasource") public DataSource secondDataSource() { return DataSourceBuilder.create().build(); } @Bean public JdbcTemplate secondJdbcTemplate() { return new JdbcTemplate(secondDataSource()); } @Bean @Resource public PlatformTransactionManager secondTxManager(DataSource secondDataSource) { return new DataSourceTransactionManager(secondDataSource); }application.properties的配置项信息 ...

August 31, 2021 · 1 min · jiezi

关于spring:Spring系列之HikariCP连接池

上两篇文章,咱们讲到了Spring中如何配置单数据源和多数据源,配置数据源的时候,连接池有很多抉择,在SpringBoot 1.0中应用的是Tomcat的DataSource,在SpringBoot 2.0中,咱们应用默认连接池是HikariCP,本文讲一下HikariCP。 为什么SpringBoot 2.0要抉择HikariCP来作为默认的连接池呢?咱们先看一下官网的一张比照图。 一个连贯周期定义为单个DataSource.getConnection()/ Connection.close()。 一个语句周期定义为单个Connection.prepareStatement(), Statement.execute(), Statement.close() 从上图看出,HikariCP和常见的连接池相比,劣势非常明显。 为什么HikariCP那么快呢?依据官网概要总结了以下几点字节码精简 :字节码级别优化(很多⽅法通过 JavaAssist ⽣成),直到编译后的字节码起码,这样,CPU缓存能够加载更多的程序代码;优化代理和拦截器:缩小代码,例如HikariCP的Statement proxy只有100行代码,只有BoneCP的十分之一;自定义数组类型:(FastStatementList)代替ArrayList:防止每次get()调用都要进行range check,防止调用remove()时的从头到尾的扫描;自定义汇合类型(ConcurrentBag:进步并发读写的效率;代理类的优化(⽐如,⽤ invokestatic 代替了 invokevirtual)其余针对BoneCP缺点的优化,比方对于耗时超过一个CPU工夫片的办法调用的钻研(但没说具体怎么优化)。既然HikariCP那么快,接下来就看一下在Spring中怎么应用HikariCP?在Spring Boot 2.x中 • 默认使⽤ HikariCP • 配置 spring.datasource.hikari.* 配置 在Spring Boot 1.x中 • 默认使⽤ Tomcat 连接池,须要移除 tomcat-jdbc 依赖 • 在application.properties文件中加上spring.datasource.type=com.zaxxer.hikari.HikariDataSource 咱们来看一下SpringBoot2.0怎么应用配置HikariDataSource的上面是org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration中的相干代码 /*** 上面的三个注解意思是当classpath中有HikariDataSource.class,并且Spring上下文中没有配置DataSource的bean* 并且spring.datasource.type的值是com.zaxxer.hikari.HikariDataSource的时候,SpringBoot主动帮咱们抉择默认的连接池是HikariDataSource*/@ConditionalOnClass({HikariDataSource.class})@ConditionalOnMissingBean({DataSource.class})@ConditionalOnProperty(name = {"spring.datasource.type"},havingValue = "com.zaxxer.hikari.HikariDataSource",matchIfMissing = true)static class Hikari { Hikari() {}@Bean@ConfigurationProperties(prefix = "spring.datasource.hikari")HikariDataSource dataSource(DataSourceProperties properties) { HikariDataSource dataSource = (HikariDataSource)DataSourceConfiguration.createDataSource(properties, HikariDataSource.class); if (StringUtils.hasText(properties.getName())) { dataSource.setPoolName(properties.getName()); } return dataSource; }}最初看看,HikariCp配置的参数有哪些? ...

August 31, 2021 · 1 min · jiezi

关于spring:Spring系列之集成Druid连接池及监控配置

前言前一篇文章咱们相熟了HikariCP连接池,也理解到它的性能很高,明天咱们讲一下另一款比拟受欢迎的连接池:Druid,这是阿里开源的一款数据库连接池,它官网上宣称:为监控而生!他能够实现页面监控,看到SQL的执行次数、工夫和慢SQL信息,也能够对数据库明码信息进行加密,也能够对监控后果进行日志的记录,以及能够实现对敏感操作实现开关,杜绝SQL注入,上面咱们具体讲一下它如何与Spring集成,并且顺便理解一下它的监控的配置。 文章要点: Spring集成Druid监控Filters配置(stat、wall、config、log)HiKariCP和Druid该如何抉择如何集成Druid1、减少相干依赖<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>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid-spring-boot-starter</artifactId> <version>1.2.6</version> </dependency>2、配置DataSource@Configurationpublic class DataSourceConfiguration { @ConfigurationProperties(prefix = "spring.datasource.druid") @Bean public DataSource dataSource(){ return new DruidDataSource(); }}3、配置项参数application.properties# 或spring.datasource.urlspring.datasource.druid.url=jdbc:mysql://localhost:3306/chenrui# 或spring.datasource.usernamespring.datasource.druid.username=root# 或spring.datasource.passwordspring.datasource.druid.password=root#初始化时建设物理连贯的个数。初始化产生在显示调用init办法,或者第一次getConnection时spring.datasource.druid.initial-size=5#最大连接池数量spring.datasource.druid.max-active=20#最小连接池数量spring.datasource.druid.min-idle=5#获取连贯时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用偏心锁,并发效率会有所降落,如果须要能够通过配置useUnfairLock属性为true应用非偏心锁spring.datasource.druid.max-wait=500#是否缓存preparedStatement,也就是PSCache。PSCache对反对游标的数据库性能晋升微小,比如说oracle。在mysql下倡议敞开。spring.datasource.druid.pool-prepared-statements=false#要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements主动触发批改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,能够把这个数值配置大一些,比如说100spring.datasource.druid.max-pool-prepared-statement-per-connection-size=-1#用来检测连贯是否无效的sql,要求是一个查问语句,罕用select 'x'。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会起作用。spring.datasource.druid.validation-query=select 'x'#单位:秒,检测连贯是否无效的超时工夫。底层调用jdbc Statement对象的void setQueryTimeout(int seconds)办法spring.datasource.druid.validation-query-timeout=1#申请连贯时执行validationQuery检测连贯是否无效,做了这个配置会升高性能。spring.datasource.druid.test-on-borrow=true#偿还连贯时执行validationQuery检测连贯是否无效,做了这个配置会升高性能。spring.datasource.druid.test-on-return=true#倡议配置为true,不影响性能,并且保障安全性。申请连贯的时候检测,如果闲暇工夫大于timeBetweenEvictionRunsMillis,执行validationQuery检测连贯是否无效spring.datasource.druid.test-while-idle=true#有两个含意:默认1分钟#1) Destroy线程会检测连贯的间隔时间,如果连贯闲暇工夫大于等于minEvictableIdleTimeMillis则敞开物理连贯。#2) testWhileIdle的判断根据,具体看testWhileIdle属性的阐明spring.datasource.druid.time-between-eviction-runs-millis=60000# 连贯放弃闲暇而不被驱赶的最小工夫spring.datasource.druid.min-evictable-idle-time-millis=600000# 连贯放弃闲暇而不被驱赶的最大工夫spring.datasource.druid.max-evictable-idle-time-millis=900000#配置多个英文逗号分隔spring.datasource.druid.filters=stat,wall# WebStatFilter配置# 是否启用StatFilter默认值falsespring.datasource.druid.web-stat-filter.enabled=true# 匹配的urlspring.datasource.druid.web-stat-filter.url-pattern=/*# 排除一些不必要的url,比方.js,/jslib/等等spring.datasource.druid.web-stat-filter.exclusions=*.js,*.gif,*.jpg,*.bmp,*.png,*.css,*.ico,/druid/*# 你能够敞开session统计性能spring.datasource.druid.web-stat-filter.session-stat-enable=true# 默认sessionStatMaxCount是1000个,你也能够按须要进行配置spring.datasource.druid.web-stat-filter.session-stat-max-count=1000# 使得druid可能晓得以后的session的用户是谁spring.datasource.druid.web-stat-filter.principal-session-name=cross# 如果你的user信息保留在cookie中,你能够配置principalCookieName,使得druid晓得以后的user是谁spring.datasource.druid.web-stat-filter.principal-cookie-name=aniu# 配置profileEnable可能监控单个url调用的sql列表spring.datasource.druid.web-stat-filter.profile-enable=# 配置_StatViewServlet配置,用于展现Druid的统计信息#是否启用StatViewServlet(监控页面)默认值为false(思考到平安问题默认并未启动,如需启用倡议设置明码或白名单以保障平安)spring.datasource.druid.stat-view-servlet.enabled=truespring.datasource.druid.stat-view-servlet.url-pattern=/druid/*#容许清空统计数据spring.datasource.druid.stat-view-servlet.reset-enable=true#监控页面登陆的用户名spring.datasource.druid.stat-view-servlet.login-username=root# 登陆监控页面所需的明码spring.datasource.druid.stat-view-servlet.login-password=1234# deny优先于allow,如果在deny列表中,就算在allow列表中,也会被回绝。# 如果allow没有配置或者为空,则容许所有拜访#容许的IP# spring.datasource.druid.stat-view-servlet.allow=#回绝的IP#spring.datasource.druid.stat-view-servlet.deny=127.0.0.1#指定xml文件所在的地位mybatis.mapper-locations=classpath:mapper/*Mapper.xml#开启数据库字段和类属性的映射反对驼峰mybatis.configuration.map-underscore-to-camel-case=true4、代码相干数据库脚本create table user_info( id bigint unsigned auto_increment primary key, user_id int not null comment '用户id', user_name varchar(64) not null comment '实在姓名', email varchar(30) not null comment '用户邮箱', nick_name varchar(45) null comment '昵称', status tinyint not null comment '用户状态,1-失常,2-登记,3-解冻', address varchar(128) null) comment '用户根本信息';初始化数据INSERT INTO chenrui.user_info (id, user_id, user_name, email, nick_name, status, address) VALUES (1, 80001, '张三丰', 'xxx@126.com', '三哥', 1, '武当山');INSERT INTO chenrui.user_info (id, user_id, user_name, email, nick_name, status, address) VALUES (2, 80002, '张无忌', 'yyy@126.com', '', 1, null);mapper.xml文件编写<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"><mapper namespace="com.example.springdataSourcedruid.dao.UserInfoDAO"> <select id="findAllUser" resultType="com.example.springdataSourcedruid.entity.UserInfo"> select * from user_info </select> <select id="getUserById" resultType="com.example.springdataSourcedruid.entity.UserInfo"> select * from user_info where id = #{id} </select> <select id="getUserByIdEqualOne" resultType="com.example.springdataSourcedruid.entity.UserInfo"> select * from user_info where id =1 </select> <select id="getUserByIdEqualTwo" resultType="com.example.springdataSourcedruid.entity.UserInfo"> select * from user_info where id =2 </select></mapper>编写DAO接口public interface UserInfoDAO { List<UserInfo> findAllUser(); UserInfo getUserById(@Param("id") int id); UserInfo getUserByIdEqualOne(); UserInfo getUserByIdEqualTwo();}测试controller@RestController@Slf4jpublic class UserInfoController { @Resource private UserInfoDAO userInfoDAO; @GetMapping(path = "/all") public List<UserInfo> getAllUser(){ return userInfoDAO.findAllUser(); } @GetMapping(path = "/getUser/{id}") public UserInfo getById(@PathVariable int id){ return userInfoDAO.getUserById(id); } @GetMapping(path = "/getUser/one") public UserInfo getById1(){ return userInfoDAO.getUserByIdEqualOne(); } @GetMapping(path = "/getUser/two") public UserInfo getById2(){ return userInfoDAO.getUserByIdEqualTwo(); }}启动类@SpringBootApplication@MapperScan(basePackages = "com.example.springdataSourcedruid.dao")public class SpringDataSourceDruidApplication { public static void main(String[] args) { SpringApplication.run(SpringDataSourceDruidApplication.class, args); }}5、启动验证拜访:http://127.0.0.1:8080/druid/ ,弹出登陆界面,用户和明码对应咱们的配置文件中设置的用户名和明码 ...

August 31, 2021 · 2 min · jiezi

关于spring:Spring认证注入内部-Bean

如您所知,Java 外部类是在其余类的范畴内定义的,相似地,外部 bean是在另一个 bean 的范畴内定义的 bean。因而,<property/> 或 <constructor-arg/> 元素内的 <bean/> 元素称为外部 bean,如下所示。 <?xml version = "1.0" encoding = "UTF-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://www.springframework.org/schema/beanshttp://www.springframework.or...d"> <bean id = "outerBean" class = "..."> <property name = "target"> <bean id = "innerBean" class = "..."/> </property></bean> </beans>例子让咱们应用 Eclipse IDE 并依照以下步骤创立一个 Spring 应用程序 - 脚步 形容1 创立一个名为SpringExample的我的项目,并在创立的我的项目的src文件夹下创立一个包com.tutorialspoint。2 应用增加内部 JAR选项增加所需的 Spring 库,如Spring Hello World 示例章节中所述。3 创立Java类文本编辑,拼写检查和MainApp下com.tutorialspoint包。4 在src文件夹下创立 Beans 配置文件Beans.xml。5 最初一步是创立所有 Java 文件和 Bean 配置文件的内容并运行应用程序,如下所述。这是TextEditor.java文件的内容- ...

August 30, 2021 · 2 min · jiezi

关于spring:Spring认证Spring-依赖注入

每个基于 Java 的应用程序都有一些对象,它们协同工作以出现最终用户所看到的工作应用程序。在编写简单的 Java 应用程序时,应用程序类应尽可能独立于其余 Java 类,以减少重用这些类的可能性,并在单元测试时独立于其余类进行测试。依赖注入(或有时称为连贯)有助于将这些类粘合在一起,同时放弃它们的独立性。 假如您有一个具备文本编辑器组件的应用程序,并且您想要提供拼写查看。你的规范代码看起来像这样 - public class TextEditor { private SpellChecker spellChecker; public TextEditor() { spellChecker = new SpellChecker();}}咱们在这里所做的是,在 TextEditor 和 SpellChecker 之间创立一个依赖项。在管制反转的状况下,咱们会做这样的事件 - public class TextEditor { private SpellChecker spellChecker; public TextEditor(SpellChecker spellChecker) { this.spellChecker = spellChecker;}}在这里,TextEditor 不应该放心 SpellChecker 的实现。SpellChecker 将独立实现,并在 TextEditor 实例化时提供给 TextEditor。整个过程由 Spring 框架管制。 在这里,咱们从 TextEditor 中删除了齐全控制权并将其保留在其余中央(即 XML 配置文件),并且依赖项(即类 SpellChecker)通过Class Constructor注入到类 TextEditor 中。因而,控制流已被依赖注入(DI)“反转”,因为您曾经无效地将依赖委托给了某个内部零碎。 注入依赖项的第二种办法是通过TextEditor 类的Setter 办法,咱们将在其中创立 SpellChecker 实例。此实例将用于调用 setter 办法来初始化 TextEditor 的属性。 因而,DI 存在于两个次要变体中,以下两个子章节将通过示例涵盖它们 - ...

August 26, 2021 · 1 min · jiezi

关于spring:Spring认证Spring-Bean-后处理器

该BeanPostProcessor的接口定义回调办法,你能够实现提供本人的实例化逻辑,依赖解析逻辑等,还能够实现后的Spring容器实现实例化,配置,并通过在一个或多个梗塞初始化豆一些自定义逻辑BeanPostProcessor 实现。 您能够配置多个 BeanPostProcessor 接口,并且您能够通过设置order属性来管制这些 BeanPostProcessor 接口的执行程序,前提是 BeanPostProcessor 实现了Ordered接口。 BeanPostProcessor 操作 bean(或对象)实例,这意味着 Spring IoC 容器实例化一个 bean 实例,而后 BeanPostProcessor 接口实现它们的工作。 一个ApplicationContext的自动检测与该执行中定义的任何豆的BeanPostProcessor接口,并注册这些豆类如后处理器,被而后通过在容器创立bean的适当调用。 示例以下示例展现了如何在 ApplicationContext 的上下文中编写、注册和应用 BeanPostProcessors。 让咱们有一个工作的 Eclipse IDE 并采取以下步骤来创立一个 Spring 应用程序 - 这是HelloWorld.java文件的内容- package com.tutorialspoint; public class HelloWorld { private String message; public void setMessage(String message){ this.message = message;} public void getMessage(){ System.out.println("Your Message : " + message);} public void init(){ System.out.println("Bean is going through init.");} public void destroy(){ ...

August 24, 2021 · 2 min · jiezi

关于spring:Spring认证Spring-Bean-生命周期教程

Spring bean 的生命周期很容易了解。实例化 bean 时,可能须要执行一些初始化以使其进入可用状态。相似地,当不再须要 bean 并从容器中移除 bean 时,可能须要进行一些清理。 只管有在 bean 实例化和销毁之间产生的幕后活动的列表,本章将只探讨两个重要的 bean 生命周期回调办法,它们在 bean 初始化和销毁时须要。 要为 bean 定义设置和装配,咱们只需应用initmethod和/或destroy-method参数申明 <bean> 。init-method 属性指定了在实例化后立刻在 bean 上调用的办法。相似地,destroymethod 指定了在从容器中删除 bean 之前调用的办法。 初始化回调org.springframework.beans.factory.InitializingBean 接口指定了一个办法 - void afterPropertiesSet() throws Exception;因而,您能够简略地实现上述接口,初始化工作能够在 afterPropertiesSet() 办法中实现,如下所示 - public class ExampleBean implements InitializingBean { public void afterPropertiesSet() { // do some initialization work}}对于基于 XML 的配置元数据,您能够应用init-method属性来指定具备 void 无参数签名的办法的名称。例如 - <bean id = "exampleBean" class = "examples.ExampleBean" init-method = "init"/>以下是类定义 - public class ExampleBean { public void init() { ...

August 23, 2021 · 2 min · jiezi

关于spring:Spring-不常见注解

Spring官网:https://spring.io @ModelAttribute能够用在controller中,在每一次申请之前执行 作用:将数据增加到模型对象中,用于视图页面展现时应用,也可用于做一些拜访接口之前必要的操作

August 22, 2021 · 1 min · jiezi

关于spring:Spring-SAS-02-上手教程

正式上市 || SAS 0.2.0 上手教程 背景Spring Authorization Server (以下简称 SAS)是 Spring 团队最新开发适配 OAuth 协定的受权服务器我的项目,旨在代替原有的 Spring Security OAuth Server。通过半年的开发和孵化,目前曾经公布了 0.2.0 版本,已反对受权码、客户端、刷新、登记等 OAuth 协定。目前 SAS 我的项目曾经迁徙至官网正式仓库保护,成为官网的正式子项目。笔者年初 《新年开箱 | Spring Authorization Server 全新的受权服务器上手》文章曾经不适配以后版本,所以特写整合上手文章。本文环境基于 Spring Boot 2.5.3 && SAS 0.2.0开始上手1. 外围依赖这里须要 SAS 、Security, 留神看正文<!-- 留神groupId 正式仓库没有 experimental ,特地留神不然下载不到jar--><dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-oauth2-authorization-server</artifactId> <version>0.2.0</version></dependency><!--提供 form 认证--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency>2. 配置 security 平安认证定义用户起源及其 form 认证的信息@EnableWebSecuritypublic class DefaultSecurityConfig { @Bean UserDetailsService users() { UserDetails user = User.builder() .username("lengleng") .password("{noop}123456") .roles("USER") .build(); return new InMemoryUserDetailsManager(user); } @Bean SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http) throws Exception { http .authorizeRequests(authorizeRequests -> authorizeRequests.anyRequest().authenticated() ) .formLogin(withDefaults()); return http.build(); }}3. 配置 SAS 服务器@Configuration@EnableWebSecuritypublic class AuthServerConfiguration { // security 挂载 SAS 【最重要的一步】 @Bean @Order(Ordered.HIGHEST_PRECEDENCE) public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); return http.formLogin(Customizer.withDefaults()).build(); } // 客户端起源 @Bean public RegisteredClientRepository registeredClientRepository() { RegisteredClient client = RegisteredClient.withId("pig") .clientId("pig") .clientSecret("{noop}pig") .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) .authorizationGrantTypes(authorizationGrantTypes -> { authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE); authorizationGrantTypes.add(AuthorizationGrantType.REFRESH_TOKEN); }) .redirectUri("https://pig4cloud.com") .build(); return new InMemoryRegisteredClientRepository(client); } // 以下两个bean 定义 生成jwt 的配置,能够间接参考文末源码介绍,这里就不在截图 @Bean @SneakyThrows public JWKSource<SecurityContext> jwkSource() { .... } @Bean public static JwtDecoder jwtDecoder(JWKSource<SecurityContext> jwkSource) { ... }}测试运行通过以上配置即可搭建实现 SAS 服务端,咱们以受权码模式测试 ...

August 21, 2021 · 2 min · jiezi

关于spring:Spring认证-Bean-范围教程

定义 <bean> 时,您能够抉择申明该 bean 的作用域。例如,要强制 Spring 在每次须要时生成一个新的 bean 实例,您应该将 bean 的 scope 属性申明为prototype。相似地,如果您心愿 Spring 在每次须要时返回雷同的 bean 实例,您应该将 bean 的 scope 属性申明为singleton。 Spring Framework 反对以下五个范畴,其中三个仅在您应用 web-aware ApplicationContext 时可用。 在本章中,咱们将探讨前两个范畴,其余三个范畴将在探讨 Web 感知 Spring ApplicationContext 时探讨。 单例范畴如果范畴设置为单例,则 Spring IoC 容器将创立该 bean 定义定义的对象的一个实例。该单个实例存储在此类单例 bean 的缓存中,并且对该命名 bean 的所有后续申请和援用都返回缓存对象。 默认范畴始终是单例。然而,当您只须要一个 bean 实例时,您能够在 bean 配置文件中将scope属性设置为singleton,如上面的代码片段所示 - <!-- A bean definition with singleton scope --><bean id = "..." class = "..." scope = "singleton"> <!-- collaborators and configuration for this bean go here --></bean>示例让咱们有一个工作的 Eclipse IDE 并采取以下步骤来创立一个 Spring 应用程序 - ...

August 20, 2021 · 2 min · jiezi

关于spring:Spring-开发环境工具-Spring-Tools-4111-现已发布

8 月 18 日,Spring 官网发表 Spring Tools 4.11.1 现已公布。 Spring Tools 4 基本上是从零开始构建,交融了古代技术和开发者工具架构,它是一个 Spring 开发的 IDE 工具,为开发基于 Spring 的企业应用程序提供了全面反对。 同时,全新版本的 Spring Tools 与 IDE 无关,可在各种编码环境中应用,反对 Eclipse、Visual Studio Code 与 Theia。 (VS Code, Spring Boot) 修复:[codespaces] spring boot 扩大在 Codespaces 设置中屡次启动(VS Code, Spring Boot) 修复:[codespaces] spring boot 扩大在连贯到代码空间时找不到正确的 JDK(VS Code, Spring Boot) 修复:JAVA_Home 是正确的,但依然显示 “留神 Java 8 依然能够在你本人的我的项目中应用”(Eclipse) 改良:进度视图显示许多 AsyncLiveExpression 刷新音讯并且不再响应(Eclipse) 改良:最新的 CI 版本不再显示正在运行的应用程序的端口(Concourse) 改良:semver 资源无奈辨认属性 “commit_message”(Concourse) 改良:GitResource 架构,增加短少的属性(Concourse) 改良:未知属性 ‘fetch_tags’ 类型 ‘GitSource’(YamlSchemaProblem)最初,Spring 官网示意: ...

August 20, 2021 · 1 min · jiezi

关于spring:Spring认证Spring-Bean-定义教学

形成应用程序骨干并由 Spring IoC 容器治理的对象称为beans。bean 是由 Spring IoC 容器实例化、组装和治理的对象。这些 bean 是应用您提供给容器的配置元数据创立的。例如,您在后面的章节中曾经看到的 XML <bean/> 定义的模式。 Bean 定义蕴含称为配置元数据的信息,容器须要理解以下信息 - 如何创立一个beanBean 的生命周期细节Bean 的依赖所有上述配置元数据转换为一组以下属性,这些属性形成每个 bean 定义。 Spring 配置元数据Spring IoC 容器与理论写入此配置元数据的格局齐全拆散。以下是为 Spring Container 提供配置元数据的三种重要办法 - 基于 XML 的配置文件。基于注解的配置基于Java的配置您曾经看到了如何向容器提供基于 XML 的配置元数据,但让咱们看看另一个基于 XML 的配置文件示例,其中蕴含不同的 bean 定义,包含提早初始化、初始化办法和销毁办法 - <?xml version = "1.0" encoding = "UTF-8"?> <beans xmlns = "http://www.springframework.org/schema/beans" xmlns:xsi = "http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation = "http://www.springframework.org/schema/beanshttp://www.springframework.or...d"> <!-- A simple bean definition --> <bean id = "..." class = "..."> <!-- collaborators and configuration for this bean go here --></bean> ...

August 19, 2021 · 1 min · jiezi

关于spring:Spring-Authorization-Server-正式迁移至-springprojects

8 月 17 日,Spring 官网发表,Spring Authorization Server 曾经退出试验状态,并正式进入 Spring 我的项目的产品组合。 Spring Authorization Server 起初是一个社区驱动的我的项目,在 Spring 的试验我的项目中启动,由 Spring Security 团队领导,其目标次要是为 Spring 社区提供 OAuth 2.0 受权服务器反对,并最终取代 Spring Security OAuth 。该我的项目应用 ZenHub 来确定性能路线图的优先秩序,并帮忙组织我的项目打算。 自 2020 年 4 月 Spring Authorization Server 颁布以来以来,曾经实现了 OAuth 2.1 受权协定的绝大部分,并为 OpenID Connect 1.0 提供适度反对。随着该我的项目进入下一个开发阶段,其重点将转向推动对 OpenID Connect 1.0 的反对。 Spring 官网示意: 感激在这么短的工夫内为该我的项目做出奉献并帮忙其倒退的所有人。咱们对以后构建的我的项目基石充满信心,并对 Spring Authorization Server 进入下一个生命周期充斥了激情。咱们期待独特持续这项工作,并最终使 Spring Authorization Server 成为 Java 平台上反对 OAuth 2 Authorization Server 的施行规范。此外,预计将在本周公布 Spring Authorization Server 0.2.0 版本,这也是其第一个由 Spring 的新政策反对的正式生产版本。 ...

August 19, 2021 · 1 min · jiezi

关于spring:SpringMVC

一、SpringMVC简介1、Servlet的毛病 1)应用Servlet进行开发,通常状况下,一个Servlet类解决一个申请,如果一个我的项目有成千盈百个申请须要解决,就须要创立成千盈百个Servlet来解决这些申请,Servlet类的个数暴增。2)在Servlet2.5及2.5之前的版本中,每一个Servlet须要在web.xml文件中至多8行以上的配置,在团队开发时,配置信息太多,也容易呈现问题3)Servlet中通过request获取申请参数,无论传过来的是什么值,接管时一律依照字符串进行接管,如果这个值是数值或者日期类型的值,前期还须要强制转换。 request.getParameter(String paramName) String request.getParameterValues(String paramName) String[]4)Servlet须要依赖于服务器运行,不能进行单元测试。。。。2、SpringMVC介绍 SpringMVC是Spring框架中的一个模块,是针对体现层提出来相应的解决方案SpringMVC是基于MVC设计模式的体现层框架。它没有齐全替换Servlet,底层也是通过Servlet来实现。 DispatcherServlet:前端控制器,用于接管散发申请给各个组件,由各个组件对申请进行解决,最初将解决的后果响应给浏览器。 DispatcherServlet须要在web.xml文件中进行配置。3、SpringMVC执行过程 参考讲义=============================================== 二、SpringMVC的入门案例√ 1、创立工程、导入jar包、创立配置文件 1)CGB-SPRINGMVC-01(Maven的Web工程)2)导入springmvc的jar包(包含spring的jar包) 以及Servlet、jsp的jar包3)提供src/main/resources/springmvc-config.xml2、到web.xml文件中配置前端控制器(DispatcherServlet) 1)配置前端控制器servlet-class、url-pattern 配置前端控制器拦挡除JSP以外的所有申请。2)配置springmvc外围配置文件的地位 <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-config.xml</param-value> </init-param>3、创立 /WEB-INF/pages/home.jsp文件 /WEB-INF/pages/test.jsp文件4、创立一个Controller(HelloController) com.tedu.controller.HelloController5、配置springmvc的外围配置文件6、编写HelloController类,实现需要,并运行测试! ---------------------------------------@Controller@RequestMapping("/HelloController")public class HelloController { @RequestMapping("/hello") public String testHello() { return "xxx"; }}localhost:8080/CGB-SPRINGMVC-01/hw/hello---------------------------------------@Controllerpublic class HelloController { @RequestMapping("/hello") public String testHello() { return "xxx"; }}localhost:8080/CGB-SPRINGMVC-01/hello---------------------------------------三、SpringMVC参数绑定1、简略类型参数绑定---------------------------------1.1.需要: 浏览器申请:localhost:8080/CGB-SPRINGMVC-01/testParam1?name=张飞&age=20&addr=北京获取申请中的name、age、addr参数的值/* 1.简略类型参数绑定 * 浏览器申请: /testParam1?name=张飞&age=20&addr=北京 * 获取申请中携带的name、age、addr参数的值 */@RequestMapping("/testParam1")public String testParam1( String name,Integer age,String addr ) { System.out.println( "name="+name ); System.out.println( "age="+age ); System.out.println( "addr="+addr ); return "home";}总结:这种接管申请参数的形式要求:(1)办法上形参的类型要和申请参数值的类型保持一致;(2)办法上形参的名字要和申请参数名保持一致;2、包装类型参数绑定--------------------------------- ...

August 17, 2021 · 2 min · jiezi

关于spring:聊聊基于jdk实现的spi如何与spring整合实现依赖注入

前置常识什么是SPI之前有写过一篇文章-->java之spi机制简介不理解spi的敌人,能够先查阅这篇文章理解下,再浏览下文 前言假如大家曾经对SPI有肯定的理解,有应用过JDK提供的SPI的敌人,应该会发现JDK的SPI是无奈实现按需加载。那如何解决这个短板问题? 这边提供2种思路,一种是本人实现一套SPI,另外一种在实现组件很罕用的伎俩,就是以后组件无奈满足时,能够借助其余组件或者再加代理层。本文实现的思路,就是利用spring的IOC,spring的ioc实质上就是一个键值对map,将jdk spi生成的对象注入到spring ioc容器中,间接也领有了key-->value的映射性能 实现思路我的项目启动时,利用spi加载类并生成对象将生成的对象注入到spring容器在业务我的项目中,应用 @Autowired +@Qualifier注解,按需援用SPI生成的bean对象外围代码片段1、spi加载实现 public Map<String,T> getSpiMap(Class<T> clz){ listServicesAndPutMapIfNecessary(clz,true); return spiMap; } private List<T> listServicesAndPutMapIfNecessary(Class<T> clz,boolean isNeedPutMap){ List<T> serviceList = new ArrayList(); ServiceLoader<T> services = ServiceLoader.load(clz); Iterator<T> iterator = services.iterator(); while(iterator.hasNext()){ T service = iterator.next(); serviceList.add(service); setSevices2Map(isNeedPutMap, service); } return serviceList; } @SneakyThrows private void setSevices2Map(boolean isNeedPutMap, T service) { if(isNeedPutMap){ String serviceName = StringUtils.uncapitalize(service.getClass().getSimpleName()); service = getProxyIfNecessary(service); spiMap.put(serviceName,service); } }2、注入spring容器public class SpiRegister implements ImportBeanDefinitionRegistrar,BeanFactoryAware { private DefaultListableBeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { registerSingleton(importingClassMetadata); } private void registerSingleton(AnnotationMetadata importingClassMetadata) { Class<?> spiInterface = getSpiInterface(importingClassMetadata); if(spiInterface != null){ Map<String,?> spiMap = new SpiFactory().getSpiMap(spiInterface); if(MapUtil.isNotEmpty(spiMap)){ spiMap.forEach((beanName,bean) -> { registerSpiInterfaceSingleton(spiInterface, bean); beanFactory.registerSingleton(beanName,bean); }); } } } private void registerSpiInterfaceSingleton(Class<?> spiInterface, Object bean) { Spi spi = spiInterface.getAnnotation(Spi.class); String defalutSpiImplClassName = spi.defalutSpiImplClassName(); if(StringUtils.isBlank(defalutSpiImplClassName)){ defalutSpiImplClassName = spi.value(); } String beanName = bean.getClass().getName(); if(bean.toString().startsWith(SpiProxy.class.getName())){ SpiProxy spiProxy = (SpiProxy) Proxy.getInvocationHandler(bean); beanName = spiProxy.getTarget().getClass().getName(); } if(beanName.equals(defalutSpiImplClassName)){ String spiInterfaceBeanName = StringUtils.uncapitalize(spiInterface.getSimpleName()); beanFactory.registerSingleton(spiInterfaceBeanName,bean); } } private Class<?> getSpiInterface(AnnotationMetadata importingClassMetadata) { List<String> basePackages = getBasePackages(importingClassMetadata); for (String basePackage : basePackages) { Reflections reflections = new Reflections(basePackage); Set<Class<?>> spiClasses = reflections.getTypesAnnotatedWith(Spi.class); if(!CollectionUtils.isEmpty(spiClasses)){ for (Class<?> spiClass : spiClasses) { if(spiClass.isInterface()){ return spiClass; } } } } return null; } private List<String> getBasePackages(AnnotationMetadata importingClassMetadata) { Map<String, Object> enableSpi = importingClassMetadata.getAnnotationAttributes(EnableSpi.class.getName()); String[] spiBackagepackages = (String[]) enableSpi.get("basePackages"); List<String> basePackages = Arrays.asList(spiBackagepackages); if(CollectionUtils.isEmpty(basePackages)){ basePackages = new ArrayList<>(); basePackages.add(ClassUtils.getPackageName(importingClassMetadata.getClassName())); } return basePackages; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = (DefaultListableBeanFactory)beanFactory; }}业务我的项目如何应用示例1、定义spi服务接口@Spipublic interface HelloService { String sayHello(String username);}注: @Spi用来指定哪些spi服务接口须要注入到spring 容器中,同时@Spi还有一个defalutSpiImplClassName属性,用来指定默认注入spi实现类 ...

August 17, 2021 · 2 min · jiezi

关于spring:Spring

1. Spring的BeanFactory和FactoryBean的区别Spring的BeanFactory是顶层接口,定义了Spring容器的基本功能,ApplicationContenxt是BeanFactory的子接口,增强了BeanFactory,减少了读取配置文件和国际化等性能 BeanFactory -> ApplicationContenxt 子级接口 -> ClassPathXmlApplicationContenxt 基于XML的配置 -> FileSystemXmlApplicationContenxt -> AnnotationConfigApplicationContenxt 注解配置FactotyBean是一种结构简单Bean的形式的接口,能够自定义Bean的细节并退出Spring治理2. Bean的生命周期初始化Bean注入属性值如果实现BeanNameAware接口,调用BeanNameAware的setBeanName传入以后Bean的ID如果实现BeanFactoryAware接口,调用BeanFactoryAware的setBeanFactory传入以后工厂实例的援用如果实现ApplicationContextAware接口,调用ApplicationContextAware的setApplicationContext传入以后ApplicationContext实例的援用如果有Bean的前置初始化处理器,调用postProcessBeforeInitialization执行解决如果实现InitializingBean接口,调用InitializingBean的afterPropertiesSet办法调用init办法进行初始化如果有Bean的后置初始化处理器,调用postProcessAfterInitialization执行解决如果作用范畴是单例,则退出Ioc缓存池,否则完结当ApplicationContext容器敞开时,调用设定的preDestory和destory执行销毁/** * 注解将Bean退出IOC */@Configuration@ComponentScan({"com.pal.entity"})public class SpringConfig { @Bean(value = "PoDemo",initMethod = "init",destroyMethod = "destroy") public PoDemo getInstance(){ PoDemo poDemo = new PoDemo(); poDemo.setName("poDemo"); return poDemo; }}/** * 除process拦截器外的生命周期办法 */public class PoDemo implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean { private String name; private Integer age; @Override public void setBeanName(String name) { System.out.println("3.注册我成为Bean时定义的ID:"+name); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("4.治理此Bean的BeanFactory为:"+beanFactory); } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { System.out.println("5.治理此Bean的ApplicationContext为:"+applicationContext); } @Override public void afterPropertiesSet() throws Exception { System.out.println("7.属性设置之后执行 afterPropertiesSet()"); } public void init() { System.out.println("8.Bean的Init办法被执行"); } @PreDestroy public void preDestroy() { System.out.println("10.销毁Bean之前执行preDestroy"); } public void destroy() { System.out.println("11.调用destroy办法销毁Bean"); } public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; }}/** * 拦挡实例化、属性注入之后的对象 */@Componentpublic class BeanPostProcessorDemo implements BeanPostProcessor { /** * Init办法执行前的操作 */ @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { if ("PoDemo".equalsIgnoreCase(beanName)) { System.out.println("6.Init办法前的解决!"); } return null; } /** * Init办法执行后的操作 */ @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { if ("PoDemo".equalsIgnoreCase(beanName)) { System.out.println("9.Init办法后的解决!"); } return null; }}/** * 启动办法 */@Testvoid BeanLifeTest() { // 在config类中将Bean退出治理用 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfig.class); PoDemo poDemo = (PoDemo) applicationContext.getBean("PoDemo"); System.out.println("加载Bean -> "+poDemo.getName()); applicationContext.close();}3. Bean加载形式2.1 XML形式// 通常引入xml形式ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");BeanClass bean = applicationContext.getBean("beanId");<!-- Spring ioc 实例化Bean的三种形式 --><!-- 一:应用无参结构器(举荐) --><bean id = "beanId" class = "com.lagou.xxx."></bean><!-- 另外两种是为了将new的对象退出spring治理 --><!-- 二:静态方法 --><bean id = "beanId" class = "com.lagou.xxx.xxxFactory" factory-method = "getInstance"></bean>public class Factory{ public static XXBean getinstance(){ return new XXBean(); }}<!-- 三:实例化办法 --><bean id = "factoryBeanId" class = "com.lagou.xxx.xxxFactory"></bean><bean id = "beanId" factory-bean = "factoryBeanId" factory-method = "getInstance"></bean>public class Factory{ public XXBean getinstance(){ return new XXBean(); }}3. Bean的作用范畴与生命周期3.1 作用范畴Singleton -> 单例 bean对象生命周期与容器雷同。Prototype -> 多例 bean对象,spring框架只负责创立,不负责销毁(JVM回收)。Request,session<bean id = "beanId" class = "com.lagou.xxx." scope = "xxx"></bean>

August 16, 2021 · 2 min · jiezi

关于spring:Feign系列-绕过SSL验证的方案

Feign系列 - 绕过SSL验证的计划 背景做一个我的项目的时候,须要调用https的接口,然而对方的ssl证书曾经过期,而Feign默认会进行SSL认证,导致接口调用有点问题。 解决方案这里记录下。 Maven依赖Spring Boot:2.2.8.RELEASESpring Cloud:Hoxton.SR8<!--more--> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId></dependency><dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-ribbon</artifactId></dependency><dependency> <groupId>io.github.openfeign</groupId> <artifactId>feign-httpclient</artifactId></dependency>代码创立Feign的配置类import feign.Client;import feign.codec.Encoder;import feign.form.FormEncoder;import org.springframework.beans.factory.ObjectFactory;import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;import org.springframework.boot.autoconfigure.http.HttpMessageConverters;import org.springframework.cloud.netflix.ribbon.SpringClientFactory;import org.springframework.cloud.openfeign.ribbon.CachingSpringLoadBalancerFactory;import org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient;import org.springframework.cloud.openfeign.support.SpringEncoder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import javax.net.ssl.SSLContext;import javax.net.ssl.TrustManager;import javax.net.ssl.X509TrustManager;import java.security.KeyManagementException;import java.security.NoSuchAlgorithmException;import java.security.cert.X509Certificate;@Configurationpublic class FeignConfiguration { @Bean public CachingSpringLoadBalancerFactory cachingFactory(SpringClientFactory clientFactory) { return new CachingSpringLoadBalancerFactory(clientFactory); } @Bean @ConditionalOnMissingBean public Client feignClient(CachingSpringLoadBalancerFactory cachingFactory, SpringClientFactory clientFactory) throws NoSuchAlgorithmException, KeyManagementException { SSLContext ctx = SSLContext.getInstance("SSL"); X509TrustManager tm = new X509TrustManager() { @Override public void checkClientTrusted(X509Certificate[] chain, String authType) { } @Override public void checkServerTrusted(X509Certificate[] chain, String authType) { } @Override public X509Certificate[] getAcceptedIssuers() { return null; } }; ctx.init(null, new TrustManager[]{tm}, null); return new LoadBalancerFeignClient(new Client.Default(ctx.getSocketFactory(), (hostname, session) -> true), cachingFactory, clientFactory); }}Feign接口import org.springframework.cloud.openfeign.FeignClient;import org.springframework.http.MediaType;import org.springframework.web.bind.annotation.PostMapping;import java.util.Map;@FeignClient(name = "ignoreSSLFeign", url="https://127.0.0.1:8080", configuration = FeignConfiguration.class)public interface IgnoreSSLFeign { @PostMapping(value = "/ignore/ssl") Object test(TestParam param);}增加EnableFeignClients使FeignClient注解失效import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.cloud.openfeign.EnableFeignClients;@EnableFeignClients@SpringBootApplicationpublic class AppRun { public static void main(String[] args) { SpringApplication.run(AppRun.class, args); }}

August 16, 2021 · 1 min · jiezi

关于spring:Spring-IOC过程

spring ioc流程BeanFactory对象的创立和加强,实现bean配置信息的加载和解析创立Beanfactory容器通过xml,注解等形式定义bean信息,由BeanDefinitionReader接口解析生成BeanDefinition,存于BeanDefinitionMap中,这是属于BeanFactory的一部分通过BeanFactoryPostProcessor后置处理器接口对BeanFactory加强解决,其中罕用的实现类包含ConfigurationClassPostProcessor(注解),PlaceholderConfigurerSupport(占位符)等Bean的实例化,初始化过程注册BeanPostProcessor后置处理器(registerBeanPostProcessors(beanFactory))初始化信息源,国际化解决(initMessageSource())初始化多播器(initApplicationEventMulticaster())注册监听器(registerListeners())实例化并初始化bean对象 这一步才是真正开始创立bean,其中包含通过反射实例化对象,填充属性populateBean(),执行Aware接口办法(invokeAwareMethods()获取容器内置属性),执行实例化前置办法(processor.postProcessBeforeInitialization),执行自定义init办法(invokeInitMethods()),执行实例化后置办法(processor.postProcessAfterInitialization)等过程

August 15, 2021 · 1 min · jiezi

关于spring:个人博客系统开发系列一

Preview这个寒假原本好好地实习来着,没成想疫情忽然在南京呈现了,进一步的学习了下相干的常识,在小破站跟着大神学习,费了一番挫折,做出了一个个别般的demo,不过可能胜利的部署到阿里云这件事件真的很让我开心,嘴角疯狂上扬,还发了一个链接给敌人,难得的喜悦分享.上面绝对这些学的只知其一;不知其二的常识做一下略微零碎的梳理,也算是为本人当前再做相干的额我的项目打一个根底. 技术工具采纳SpringBootThymeleafJPASemantic UIhtml \ css \ jsjquery三方插件(css成果,md编辑显示)阿里云ECSmavenlog4jfinal shell宝塔面板Springbootspringboot 是目前支流的java技术框架,有很弱小的生态圈.借助它官网的话来讲 Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can "just run".的确如此,在很大水平上开发一个demo不是本人技术有多牛逼到哪儿去,而是站在了伟人的肩膀上,要是让本人把编译好字节码再分门别类错落有致的放到服务器对应的文件,工作量的确很大.比起jsp那种前后端耦合水平稍高,接口提供麻烦的形式,还是springboot这个货色比拟好,不便了开发者去做本人的demo,然而同样也加剧了行业的竞争和学生的内卷水平,哪天是个头. Springboot 小点这边看过一些剖析,然而感觉各有差异,而且版本更新很快,很多货色都在做变动,我是看着2.5.3的版本,下载源码看了一下作者的注解,有再加上其余相干的材料有了上面的一些理解.其中有一些很重要的类,上面列举几个. SpringApplication类 This Class that can be used to bootstrap and launch a Spring application from a Java main method. By default class will perform the following steps to bootstrap your application通过它咱们能够来启动一个程序,调用它的动态run办法(其实是外部new出一个实例再应用)是一种形式,还有一些其余的形式也可能让咱们的demo跑起来. ApplicationContextInitializer Springboot启动流程它的启动流程很简单,可能干这么多事儿,一是有了@SpringApplication这个注解,另外就是下面提到的SpringApplication类, 1.依据类门路创立一个ApplicationContext实例它的,结构器还带有一个primarySources的参数,这个实例里包含监听器、初始化器,我的项目利用类型,启动类汇合,类加载器 Create an appropriate ApplicationContext instance (depending on your classpath) ...

August 14, 2021 · 2 min · jiezi

关于spring:Spring认证Spring-框架概述

Spring 是最风行的企业 Java 利用程序开发框架。寰球数以百万计的开发人员应用 Spring Framework 来创立高性能、易于测试和可重用的代码。 Spring 框架是一个开源的 Java 平台。它最后由 Rod Johnson 编写,并于 2003 年 6 月在 Apache 2.0 许可下首次公布。 在尺寸和透明度方面,Spring 是轻量级的。Spring 框架的根本版本大概为 2MB。 Spring Framework 的外围性能可用于开发任何 Java 应用程序,但有一些扩大可用于在 Java EE 平台之上构建 Web 应用程序。Spring 框架旨在通过启用基于 POJO 的编程模型使 J2EE 开发更易于应用并促成良好的编程实际。 应用 Spring 框架的益处以下是应用 Spring Framework 的几个微小益处的列表 - Spring 使开发人员可能应用 POJO 开发企业级应用程序。仅应用 POJO 的益处是您不须要 EJB 容器产品(如应用程序服务器),但您能够抉择仅应用强壮的 servlet 容器(如 Tomcat 或某些商业产品)。Spring 以模块化形式组织。只管包和类的数量很多,但您只须要放心您须要的包和类,而疏忽其余的。Spring 并没有从新创造轮子,而是真正利用了一些现有技术,如几个 ORM 框架、日志框架、JEE、Quartz 和 JDK 计时器以及其余视图技术。测试用 Spring 编写的应用程序很简略,因为依赖于环境的代码被移到了这个框架中。此外,通过应用 JavaBeanstyle POJO,应用依赖注入来注入测试数据变得更加容易。Spring 的 web 框架是一个精心设计的 web MVC 框架,它提供了一个很好的代替 web 框架,如 Struts 或其余适度设计或不太风行的 web 框架。Spring 提供了一个不便的 API 来将特定于技术的异样(例如由 JDBC、Hibernate 或 JDO 抛出)转换为统一的、未经查看的异样。轻量级 IoC 容器往往是轻量级的,尤其是与 EJB 容器相比时。这有利于在内存和 CPU 资源无限的计算机上开发和部署应用程序。Spring 提供了一个统一的事务管理接口,能够放大到本地事务(例如应用单个数据库)和扩大到全局事务(例如应用 JTA)。依赖注入 (DI)Spring 最认同的技术是管制反转的依赖注入 (DI)格调。该管制反转(IOC)是一个抽象的概念,它能够在许多不同的形式来表白。依赖注入只是管制反转的一个具体例子。 ...

August 12, 2021 · 1 min · jiezi

关于spring:Spring事务传播机制及内部调用问题

1. 次要的三种流传机制https://www.cnblogs.com/xzwbl...REQUIRED、REQUIRES_NEW、NESTED 留神:以下所有的testA()办法与testB()办法是在两个类之中且都讲bean交给spring治理,如非如此会造成事务生效问题请看第二局部 1.1 REQUIRED(默认)以后没有事务,则新建一个事务,如存在事务,则退出此事务 // 例子// A.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)public void testA() { insertDataToTableA(dataList); testB();}// B.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)public void testB(){ insertDataToTableB(dataList); int i = 1/0; insertDataToTableC(dataList);}// 后果 testB()退出testA()的事务,testB()产生异样,两个办法均回滚// 如果 testA()不加事务,只有testB()加,则testA()胜利,testB()回滚1.2 REQUIRES_NEW创立一个新事务,如果存在以后事务,则挂起该事务。即无论是否已存在事务都新建事务 // 例子// A.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)public void testA() { insertDataToTableA(dataList); testB(); int i = 1/0;}// B.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRES_NEW)public void testB(){ insertDataToTableB(dataList); insertDataToTableC(dataList);}// 后果 testA() 回滚 testB() 胜利,阐明不是一个事物里 1.3 NESTED如果以后事务存在,则在嵌套事务中执行(子事务),否则REQUIRED的操作一样(开启一个事务) 这里须要留神两点: 和REQUIRES_NEW的区别REQUIRES_NEW是新建一个事务并且新开启的这个事务与原有事务无关,而NESTED则是以后存在事务时(咱们把以后事务称之为父事务)会开启一个嵌套事务(称之为一个子事务)。在NESTED状况下父事务回滚时,子事务也会回滚,而在REQUIRES_NEW状况下,原有事务回滚,不会影响新开启的事务。和REQUIRED的区别REQUIRED状况下,调用方存在事务时,则被调用方和调用方应用同一事务,那么被调用方出现异常时,因为共用一个事务,所以无论调用方是否catch其异样,事务都会回滚而在NESTED状况下,被调用方产生异样时,调用方能够catch其异样,这样只有子事务回滚,父事务不受影响// 例子1// A.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)public void testA() { insertDataToTableA(dataList); testB(); int i = 1/0;}// B.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)public void testB(){ insertDataToTableB(dataList); insertDataToTableC(dataList);}// 后果 二者皆回滚,因为是嵌套的父子级事务,所以有一个有问题就都回滚// 例子2// A.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED)public void testA() { insertDataToTableA(dataList); try{ testB(); }catch(Exception e){ log.error(e) } }// B.class@Transactional(rollbackFor = Exception.class,propagation = Propagation.NESTED)public void testB(){ insertDataToTableB(dataList); int i = 1/0; insertDataToTableC(dataList);}// 后果 testA胜利、testB回滚,阐明绝对 REQUIRED 异样能够被捕捉解决2. 事务生效2.1 外部调用问题1. A带事务调B不带事务// 例0 无异样 -> 两个都能顺利插入表// 例1 B办法异样// 后果 事务失效(A,B专用一个事务),两个办法皆回滚@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)public Integer insertUserInfo() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(5, 'Jack5')"); // 外部调用问题 insertUserInfo2(); return null;}//@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)public Integer insertUserInfo2() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(9, 'tom9')"); int i = 1 / 0; return null;}// 例2 A办法调用完B后再异样 -> 后果雷同事务失效2. A带事务调B带事务(REQUIRES_NEW)// 例1 B异样 且 B的流传机制为 REQUIRES_NEW(新建事务)// 后果 B事务失败(追加到A事务里),A,B皆回滚,阐明A,B还是在一个事务里,A办法里异样时同样状况@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)public Integer insertUserInfo() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(5, 'Jack5')"); // 外部调用问题 insertUserInfo2(); return null;}@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRES_NEW)public Integer insertUserInfo2() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(9, 'tom9')"); int i = 1 / 0; return null;}3. A带事务调B带事务(NESTED)// 例1 B异样 且 B的流传机制为 NESTED,在A中捕捉异样// 后果 A事务胜利,B事务失败,异样后仍能插入数据@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)public Integer insertUserInfo() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(5, 'Jack5')"); // 外部调用问题 try { insertUserInfo2(); }catch (Exception e){ e.printStackTrace(); } return null;}@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.NESTED)public Integer insertUserInfo2() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(9, 'tom9')"); int i = 1 / 0; return null;}// 例2 B异样 且 B的流传机制为 NESTED,在A中不捕捉异样// 后果 A,B皆回滚// 例3 A异样 且 B的流传机制为 NESTED,在A中捕捉异样与否都一样// 后果 A,B皆回滚4. A不带事务调B带事务// 例0 无异样 -> 两个都能顺利插入表// 例1 B办法异样 -> 事务生效,A,B都不会回滚,都会插入数据// 第一局部中的三种流传机制都会是这种状况//@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)public Integer insertUserInfo() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(5, 'Jack5')"); // 外部调用问题 insertUserInfo2(); return null;}@Transactional(rollbackFor = RuntimeException.class,propagation = Propagation.REQUIRED)public Integer insertUserInfo2() { jdbcTemplate.execute("INSERT INTO user (id, username) VALUES\n" + "(9, 'tom9')"); int i = 1 / 0; return null;}// 例2 A办法调用完B后异样 -> 与例1雷同,事务生效,A,B都不会回滚,都会插入数据5. 综述尽量避免外部调用,最多用A带事务调B不带事务,尤其是不能用A不带事务调B带 ...

August 11, 2021 · 2 min · jiezi

关于spring:Spring认证Spring核心基础教程详解

Spring 框架是一个开源 Java 平台,它为非常容易和十分疾速地开发强壮的 Java 应用程序提供了全面的基础设施反对。Spring 框架最后由 Rod Johnson 编写,并于 2003 年 6 月在 Apache 2.0 许可下首次公布。本教程是基于 2015 年 3 月公布的 Spring Framework 4.1.6 版编写的。 为什么要学习Spring?Spring 是最风行的企业 Java 利用程序开发框架。寰球数以百万计的开发人员应用 Spring Framework 来创立高性能、易于测试和可重用的代码。 Spring 框架是一个开源的 Java 平台。它最后由 Rod Johnson 编写,并于 2003 年 6 月在 Apache 2.0 许可下首次公布。 在尺寸和透明度方面,Spring 是轻量级的。Spring 框架的根本版本大概为 2MB。 Spring Framework 的外围性能可用于开发任何 Java 应用程序,但有一些扩大可用于在 Java EE 平台之上构建 Web 应用程序。Spring 框架旨在通过启用基于 POJO 的编程模型使 J2EE 开发更易于应用并促成良好的编程实际。 Spring的利用以下是应用 Spring Framework 的几个微小益处的列表 - ...

August 11, 2021 · 1 min · jiezi

关于spring:Spring认证什么是Spring-GraphQL

工具与资源核心帮忙开发者更加高效的工作,提供围绕开发者全生命周期的工具与资源https://developer.aliyun.com/... Spring认证_什么是Spring GraphQL?第一课:https://developer.aliyun.com/... 第二课:https://developer.aliyun.com/... 启动器这个我的项目建设在 Boot 2.x 上,但它应该与最新的 Boot2.4.x5 相干。 我的项目设置要创立我的项目,请转到start.spring.io并为要应用的GraphQL传输抉择启动器: 启动机 运输 执行 spring-boot-starter-web HTTP 秋季MVC spring-boot-starter-websocket 网络后果 用于 Servlet 应用程序的 WebSocket spring-boot-starter-webflux HTTP、WebSocket 弹簧 WebFlux 在生成的我的项目中,graphql-spring-boot-starter手动增加: 依赖{ // Spring GraphQL 启动 实现 'org.springframework.experimental:graphql-spring-boot-starter:1.0.0-SNAPSHOT' // ...} 存储库{ MavenCentral() maven { url 'https://repo.spring.io/milestone' } // 秋季里程碑 maven { url 'https://repo.spring.io/snapshot' } // Spring 快照} 架构默认状况下,GraphQL 架构文件将在src/main/resources/graphql与扩展名“.graphqls”,“.graphql”,“.gql”,或“.gqls”。您能够自定义要查看的目录地位,如下所示: spring.graphql.schema.locations=classpath:graphql/ 所述模式模式能够在“/GraphQL/模式”被graphQL通过HTTP。这不是默认容许的: spring.graphql.schema.printer.enabled=false DataFetcher注销你能够申明RuntimeWiringConfigurer在 Spring 的配置与 GraphQL 引擎豆类和应用这些注销的数据获取程序,类型旋转变压器,和更多: @成分 公共类 PersonDataWiring 实现 RuntimeWiringConfigurer { ...

August 11, 2021 · 2 min · jiezi

关于spring:SpringBoot实战基于异常日志的邮件报警

SpringBoot实战基于异样日志的邮件报警置信所有奋斗在一线的小伙伴,会很关怀本人的零碎的运行状况,一般来说,基础设施齐全一点的公司都会有欠缺的报警计划,那么如果咱们是一个小公司呢,不能因为基础设施没有,就失去对象的感知能力吧;如果咱们的零碎大量异样却不能实时的触达给咱们,那么也就只会有一个后果--杀个程序猿祭天 本文简略的介绍一种实现思路,基于error日志来实现邮件的报警计划 <!-- more --> I. 我的项目环境1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 开一个web服务用于测试 <dependencies> <!-- 邮件发送的外围依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency></dependencies>2. 配置邮件相干配置如下,留神应用本人的用户名 + 受权码填充上面缺失的配置 spring: #邮箱配置 mail: host: smtp.163.com from: xhhuiblog@163.com # 应用本人的发送方用户名 + 受权码填充 username: password: default-encoding: UTF-8 properties: mail: smtp: auth: true starttls: enable: true required: trueII. 异样日志的邮件预警1. 设计思路接下来这个计划的次要出发点在于,当程序呈现大量的异样,表明利用多半呈现了问题,须要立马发送给我的项目owner 要实现这个计划,关键点就在于异样呈现的感知与上报 异样的捕捉,并输入日志(这个感觉属于标配了吧,别通知我当初还有利用不输入日志文件的...) 对于这个感知,借助logback的扩大机制,能够实现,前面介绍异样上报:邮件发送对于email的应用姿态,举荐参考博文 SpringBoot 系列之邮件发送姿态介绍 2. 自定义appender定义一个用于谬误发送的Appender,如下 public class MailUtil extends AppenderBase<ILoggingEvent> { public static void sendMail(String title, String context) { SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); //邮件发送人 simpleMailMessage.setFrom(ContextUtil.getEnvironment().getProperty("spring.mail.from", "bangzewu@126.com")); //邮件接管人,能够是多个 simpleMailMessage.setTo("bangzewu@126.com"); //邮件主题 simpleMailMessage.setSubject(title); //邮件内容 simpleMailMessage.setText(context); JavaMailSender javaMailSender = ContextUtil.getApplicationContext().getBean(JavaMailSender.class); javaMailSender.send(simpleMailMessage); } private static final long INTERVAL = 10 * 1000 * 60; private long lastAlarmTime = 0; @Override protected void append(ILoggingEvent iLoggingEvent) { if (canAlarm()) { sendMail(iLoggingEvent.getLoggerName(), iLoggingEvent.getFormattedMessage()); } } private boolean canAlarm() { // 做一个简略的频率过滤 long now = System.currentTimeMillis(); if (now - lastAlarmTime >= INTERVAL) { lastAlarmTime = now; return true; } else { return false; } }}3. Spring容器下面的邮件发送中,须要应用JavaMailSender,写一个简略的SpringContext工具类,用于获取Bean/Propertiy ...

August 9, 2021 · 2 min · jiezi

关于spring:SpringBoot-系列之邮件发送姿势介绍

SpringBoot系列之邮件发送姿态介绍邮件发送,在理论的我的项目开发中,可能用的不是特地多,如果没有特定的需要,置信也没有多少小伙伴会特意的去关注,那么如果当初咱们心愿针对我的项目做一个异样的报警零碎,当出现异常的时候,能够向指定的小伙伴发送邮件揭示,那么让咱们来实现这个性能,能够怎么办呢? 这里介绍一下如何应用SpringBoot封装好的MailSender来实现邮件发送 <!-- more --> I. 我的项目环境1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 开一个web服务用于测试 <dependencies> <!-- 邮件发送的外围依赖 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-mail</artifactId> </dependency> <!-- 实用于html模板模式的邮件发送,借助freemarker来实现html模板渲染 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId> </dependency></dependencies>2. 配置在开始之前,咱们须要先筹备一个用于发送邮件的账号,比方我这里应用163的邮箱来发送邮件,须要先到邮箱提供商哪里获取受权码,具体如何获取这个货色,不同的邮箱姿态有些不同,各位小伙伴依据本人的理论状况,搜寻一下,置信很快就能get到 这里简略介绍下网易邮箱的获取形式 接下来设置发送邮件相干的配置信息,配置文件application.yml spring: #邮箱配置 mail: host: smtp.163.com from: xhhuiblog@163.com # 应用本人的发送方用户名 + 受权码填充 username: password: default-encoding: UTF-8 properties: mail: smtp: auth: true starttls: enable: true required: trueII. 邮件发送接下来进入正题,咱们将从简略根底的文本邮件发送开始,逐步介绍如何增加附件,应用丑陋的html模板等 1. 简略文本邮件发送咱们这里间接应用JavaMailSender来发送一个根底的文本邮件 @Servicepublic class MailDemo { @Autowired private JavaMailSender javaMailSender; @Value("${spring.mail.from:xhhuiblog@163.com}") private String from; private void basicSend() { SimpleMailMessage simpleMailMessage = new SimpleMailMessage(); //邮件发送人 simpleMailMessage.setFrom(from); //邮件接管人,能够是多个,参数为可变参数 simpleMailMessage.setTo("bangzewu@126.com"); //邮件主题,也就是题目 simpleMailMessage.setSubject("SpringBoot测试邮件发送"); //邮件内容 simpleMailMessage.setText("简略的邮件注释"); javaMailSender.send(simpleMailMessage); }}JavaMailSender: 间接作为一个Spring 的bean对象应用SimpleMailMessage:简略的邮件对象,外面有一些邮件发送时,关联的根底信息 ...

August 9, 2021 · 2 min · jiezi

关于spring:SpringBoot-Mybatis系列之插件机制-Interceptor

【SpringBoot + Mybatis系列】插件机制 Interceptor在 Mybatis 中,插件机制提供了十分弱小的扩大能力,在 sql 最终执行之前,提供了四个拦挡点,反对不同场景的性能扩大 Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)ParameterHandler (getParameterObject, setParameters)ResultSetHandler (handleResultSets, handleOutputParameters)StatementHandler (prepare, parameterize, batch, update, query)本文将次要介绍一下自定义 Interceptor 的应用姿态,并给出一个通过自定义插件来输入执行 sql,与耗时的 case <!-- more --> I. 环境筹备1. 数据库筹备应用 mysql 作为本文的实例数据库,新增一张表 CREATE TABLE `money` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名', `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱', `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫', `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫', PRIMARY KEY (`id`), KEY `name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;2. 我的项目环境本文借助 SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 ...

August 9, 2021 · 3 min · jiezi

关于spring:升级到Spring-53x之后GC次数急剧增加我TM人傻了

最近咱们我的项目降级到了 Spring Boot 2.4.6 + Spring Cloud 2020.0.x,通过我的另一系列即可看出:Spring Cloud 降级之路。然而降级后,咱们发现 YoungGC 显著增高,调配对象速率显著增高,然而降职的对象并没有增多,证实都是新创建的对象并且没过多久就能够被回收。咱们来看其中一个过程的监控,这时候的 http 申请速率大略在 100 左右: 这就很奇怪了,申请速率并没有那么大,然而通过监控能够看出每秒钟调配了将近两个 G 的内存。在降级之前,这个调配速率大略在 100~200 MB 左右,在等同申请速率下。那么这多进去的内存到底是哪里耗费的呢? 咱们须要看一下内存中各种对象的统计数据,即应用 jmap 命令。同时不能只查看存活对象的统计,因为从监控中看进去并不是老年代对象过多,因为降职的对象并没有增多,相同的,咱们如果咱们能排除当初还存活的对象就更好了。同时,因为 GC 相当频繁,1s 左右就会有一次。所以根本不能冀望一次就能抓到咱们想要的 jmap。同时 jmap 会导致所有线程进入 safepoint 从而 STW,对线上有肯定影响,所以不能太频繁 jmap。所以,咱们采取如下策略: 扩容一个实例,之后将一个实例,通过注册核心以及限流器将某个实例的流量切走一半;针对这个实例,间断执行 jmap -histo(统计所有对象) 以及 jmap -histo:live(仅统计存活对象);反复第二步 5 次,每次距离 100ms,300ms,500ms,700ms;去掉限流这个实例的限流,将新扩容的实例敞开。通过这几次的 jmap 比照,咱们发现 jmap 统计中排在后面的对象类型有一个 spring 框架的: num #instances #bytes class name (module)------------------------------------------------------- 1: 7993252 601860528 [B (java.base@11.0.8) 2: 360025 296261160 [C (java.base@11.0.8) 3: 10338806 246557984 [Ljava.lang.Object; (java.base@11.0.8) 4: 6314471 151547304 java.lang.String (java.base@11.0.8) 5: 48170 135607088 [J (java.base@11.0.8) 6: 314420 126487344 [I (java.base@11.0.8) 7: 4591109 110100264 [Ljava.lang.Class; (java.base@11.0.8) 8: 245542 55001408 org.springframework.core.ResolvableType 9: 205234 29042280 [Ljava.util.HashMap$Node; (java.base@11.0.8) 10: 386252 24720128 [org.springframework.core.ResolvableType; 11: 699929 22397728 java.sql.Timestamp (java.sql@11.0.8) 12: 89150 21281256 [Ljava.beans.PropertyDescriptor; (java.desktop@11.0.8) 13: 519029 16608928 java.util.HashMap$Node (java.base@11.0.8) 14: 598728 14369472 java.util.ArrayList (java.base@11.0.8)这个对象是怎么创立进去的呢?如何定位一个曾经不再存活的频繁创建对象,并且这个对象类型是框架外部的? ...

August 6, 2021 · 2 min · jiezi

关于spring:关于Spring注解开发教程打包全送你

摘要:spring是咱们web开发中必不可少的一个框架,基于传统的xml形式配置bean总感觉太过繁琐,从spring2.5之后注解的呈现能够大大简化咱们的配置。本文分享自华为云社区《如何高效晋升Java开发效率—Spring注解开发全套教程!》,作者: 灰小猿。 一、应用注解标识组件为了不再在IOC中一个个的申明类对象,首先依据每一个类的性能的不同,Spring中先规定了基于组件的注解,大抵能够分为以下四种: ①一般组件:@Component 标识一个受Spring IOC容器治理的组件,咱们也能够了解为是除了数据库层、业务逻辑层、管制层组件以外的其余组件应用的注解。 ②长久化层组件:@Respository 标识一个受Spring IOC容器治理的长久化层组件,个别就是用来标注数据库层 ③业务逻辑层组件:@Service 标识一个受Spring IOC容器治理的业务逻辑层组件, ④表述层控制器组件:@Controller 标识一个受Spring IOC容器治理的表述层控制器组件。 同时这几个注解前面也能够增加一些参数,比方比拟罕用的一个是注解的括号中加value属性,来示意这个组件在容器中的ID,如果不设置value值时,默认的ID是类名的全称(第一个字母小写)。 如下实例,咱们为一个Dao层组件增加一个@Respository注解 /** * 数据库层注解 * */@Repositorypublic class BookDao { public void saveBook() { System.out.println("bookDao中的图书已保留..."); }}通过这四个注解咱们首先就能够将所有的组件逐个分类。置于为什么要应用注解进行分类,说到底其实就是为了不便不便疾速的晓得哪一个类是做了什么类型的性能,同时不便之后通过这四个注解将组件退出到IOC容器中。 所以你也能够把这四个注解了解为是Spring分发给不同性能组件的一个“身份证”,只有领有这四种“身份证”,那么这个组件就能够被退出到IOC容器中。 在这里有一点须要留神:事实上Spring并没有能力辨认一个组件到底是不是它所标记的类型,即便将@Respository注解用在一个表述层控制器组件下面也不会产生任何谬误,所以@Respository、@Service、@Controller这几个注解仅仅是为了让开发人员本人明确以后的组件表演的角色。不便将组件退出到容器中去。 二、组件扫描1、一般扫描当初倒是对所有的组件进行了具体的分类,然而这样就等于将所有的组件曾经退出到IOC容器中了嘛?如果真的是这样的话,那么咱们就真正的实现了低代码时代了... 所以当初咱们就是应该如何将领有注解标识的组件退出到IOC容器中呢?在这里Spring在IOC中提供了一个包扫描的性能,通过这个名字咱们就能够晓得,Spring能够主动的扫描整个我的项目中被标记了这四个注解的组件,并且将其退出到IOC容器中。 进行包扫描的具体操作是这样的: 进行包扫描依赖于Context名称空间,所以须要在IOC中退出该名称空间,退出名称空间的办法有两种,一种是在IOC的头文件中退出如下代码: xmlns:context="http://www.springframework.org/schema/context"然而因为这种不好记所以不举荐, 还有一种就是间接在界面点击下方的Namespaces,在其中找到并勾选Context, 在容器中进行包扫描的代码是: <context:component-scan base-package="com.spring"></context:component-scan>其中base-package属性指定一个须要扫描的基类包,Spring容器将会扫描这个基类包及其子包中的所有类。当须要扫描多个包时能够应用逗号分隔。如下面的代码就是扫描com.spring包上面的所有类。 2、蕴含与排除特定组件然而这样进行扫描的范畴有时候未免还是有一些大,那么能不能再放大进行包扫描的范畴呢?当然是能够的。 如果仅心愿扫描特定的类而非基包下的所有类,可应用resource-pattern属性过滤特定的类,如: <context:component-scan base-package="com.atguigu.component" resource-pattern="autowire/*.class"/>(1)扫描蕴含特定组件如果咱们仅仅是想要扫描蕴含特定特色的组件,那么咱们能够如下办法: <context:include-filter>子节点示意要蕴含的指标类 然而须要留神的是:因为context:component-scan默认是将所有的类全副都增加进去,所以在此基础上再增加是没有用的,须要在context:component-scan中退出属性参数use-default-filters,use-default-filters="true" 示意默认将所有的类都增加进去,false示意将所有的类都不增加进去, 如下代码示意仅仅扫描蕴含include-filter中所指特色的组件,其中的type用来示意应用何种类型的扫描表达式,expression前面跟表达式。 <context:component-scan base-package="com.spring" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Repository"/></context:component-scan>(2)扫描排除特定组件尽然有扫描蕴含特定组件,那么就有扫描排除特定组件, <context:exclude-filter>子节点示意要排除在外的指标类。 以下代码示意扫描除以下特色外的其余组件。 <context:component-scan base-package="com.spring"> <context:exclude-filter type="assignable" expression="com.spring.service.BookService"/> </context:component-scan> 同时component-scan下能够领有若干个include-filter和exclude-filter子节点,来示意能够蕴含多个哪种特色的组件或排除具备哪种特色的组件。 对于下面说到的type中填写的过滤表达式类型及作用如下表: 最罕用的下面两个,以下三个简直不必: type="aspectj" aspectj表达式 type="custom" 定义一个TypeFile,本人写一个类定义应用哪一个 type="regex" 利用正则表达式 ...

August 4, 2021 · 1 min · jiezi

关于spring:SpringCloud升级之路20200x版4maven依赖回顾以及项目框架结构

本系列代码地址:https://github.com/HashZhang/... 咱们先来回顾下 maven 依赖中一个重要准则:最短门路准则。这在之后咱们的应用中会常常用到。 举一个例子,假如咱们以 spring-boot-parent 作为 parent: <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.9</version></parent>咱们想用想用 elasticsearch 作为搜索引擎,在我的项目中增加了依赖 <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>7.10.2</version></dependency>写好代码,一跑,报类不存在异样: java.lang.NoClassDefFoundError: org/elasticsearch/common/xcontent/DeprecationHandler at com.lv.springboot.datasource.ClientUTis.main(ClientUTis.java:13)Caused by: java.lang.ClassNotFoundException: org.elasticsearch.common.xcontent.DeprecationHandler at java.net.URLClassLoader.findClass(URLClassLoader.java:381) at java.lang.ClassLoader.loadClass(ClassLoader.java:424) at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:331) at java.lang.ClassLoader.loadClass(ClassLoader.java:357) ... 1 more看下依赖mvn dependency:tree,发现依赖的elasticsearch版本是: org.elasticsearch.client:elasticsearch-rest-high-level-client:7.0.1|--org.elasticsearch:elasticsearch:5.6.16|--org.elasticsearch.client:elasticsearch-rest-client:7.0.1|--org.elasticsearch.plugin:parent-join-client:7.0.1|--org.elasticsearch.plugin:aggs-matrix-stats-client:7.0.1|--org.elasticsearch.plugin:rank-eval-client:7.0.1|--org.elasticsearch.plugin:lang-mustache-client:7.0.1可能读者会感觉很奇怪,明明指定了elasticsearch的依赖了啊,而且是我的项目的根 pom,依赖不是最短门路准则么?不应该以这个依赖为准么? 仔细分析,原来 SpringBoot的DependencyManagement 中,org.elasticsearch:elasticsearch曾经被蕴含了(以下为节选): <groupId>org.springframework.boot</groupId><artifactId>spring-boot-dependencies</artifactId><version>2.0.9.RELEASE</version><properties><elasticsearch.version>5.6.16</elasticsearch.version></properties><dependencyManagement> <dependencies> <dependency> <groupId>org.elasticsearch</groupId> <artifactId>elasticsearch</artifactId> <version>${elasticsearch.version}</version> </dependency> </dependencies></dependencyManagement>spring-boot 其实曾经思考到用户可能要换版本了,所以将版本放入了 <properties/>,properties 也具备最短门路准则,所以能够通过在你的我的项目根 pom 中的 properties 减少雷同 key 批改版本: <properties> <elasticsearch.version>7.10.2</elasticsearch.version></properties>所有能够这么替换的属性, spring-boot 官网文档曾经列出了,参考官网文档附录:Version Properties 也能够通过 dependencyManagement 的最短门路准则,通过在你的我的项目根 pom 中的减少想批改依赖的 dependencyManagement 即可: ...

August 4, 2021 · 2 min · jiezi

关于spring:SpringCloud升级之路20200x版3Eureka-Server-与-API-网关要考虑的问题

本系列为之前系列的整顿重启版,随着我的项目的倒退以及我的项目中的应用,之前系列外面很多货色产生了变动,并且还有一些货色之前系列并没有提到,所以重启这个系列重新整理下,欢送各位留言交换,谢谢!~ 之前咱们提到了,不同的集群,应用的是同一套 Eureka 集群。 咱们会动静在线公布每个微服务,在 K8s 的环境下,咱们个别应用 ReplicaSet 将咱们的微服务部署成无状态的微服务实例(参考:ReplicaSet);在这种状况下,新的微服务实例地址(ip)和原来的地址个别是不一样的。在这种状况下,咱们想实现实例的疾速高低线,即疾速感知实例状态。 这须要两方面的配置,一是 Eureka 客户端与实例配置,另一个就是这里要探讨的 Eureka 服务器的配置。在失常公布的过程中,咱们会先启动一个新的实例,而后优雅敞开一个老实例,而后再启动一个新的,再敞开一个老的,以此类推,滚动更新。优雅敞开的时候,个别会从注册核心 Eureka 登记本人。然而有异常情况的时候,例如 JVM Stop-the-world,或者死锁等,优雅敞开可能失败。还有就是这些异样也可能导致心跳无奈失常发送到 Eureka。 所以为了实现实例状态疾速被其余实例感知,咱们须要启动Eureka 被动实例过期查看,同时,倡议敞开掉自我爱护机制。次要因为:自我爱护次要针对集群中网络呈现问题,或者 Eureka 呈现问题导致 Stop-the-world 并且无奈复原,或者压力过大,导致有很多实例无奈发送心跳导致很多实例状态异样,然而理论实例还在失常工作的状况,不要让这些实例不参加负载平衡。启用自我爱护的状况下,就会进行对于实例的过期。然而,如果呈现这种状况,其实也代表很多实例无奈读取注册核心了。并且还有一种状况就是,Eureka 重启,尽管不常见,然而对于镜像中其余的组件更新(例如 JDK 等)咱们还是很频繁的。在咱们的集群中, Eureka 集群压力不大(服务几百个实例),并且 Eureka 比较稳定,其实只须要思考 Eureka 网络出问题的状况。我偏向于从客户端对于实例缓存机制来解决这个问题,如果返回实例列表为空,则应用上次的实例列表进行负载平衡,这样既能解决 Eureka 重启的状况,又能解决一些 Eureka 网络隔离的状况。 对于 API 网关,咱们应用 Spring Cloud Gatway。Spring Cloud Gateway 是基于 WebFlux(底层即 Project Reactor,基于 Netty 实现)的 API 网关。咱们在应用的过程中,遇到并解决了以下一些问题: Spring Cloud Gateway 是纯异步响应式的代码实现,API 网关波及接口 Body 加密:咱们须要对发过来的申请进行解密再发往微服务,之后对微服务返回的响应进行加密再返回给客户端,这就波及到了 Body 读取与批改。在 Spring Cloud Gateway 中如何实现 Body 的读取与批改呢?这块要好好思考实现,并且保障不能有内存透露。API 网关须要鉴权,然而鉴权个别是独自有另一个微服务负责,API 网关须要调用这个微服务,如何在异步的环境下调用呢?发往微服务的每个申请,都是异步响应非阻塞的,所以能够不像微服务调用微服务那样做线程隔离,限流也能够不应用客户端限流,而是每个微服务本人限流。发往微服务的每个申请,是须要有重试机制的。发往微服务的每个申请,也须要做实例和门路级别的断路机制。 ...

August 3, 2021 · 1 min · jiezi

关于spring:SpringCloudDockerJenkinsGitLabMaven-实现自动化构建与部署实战

前言与初衷本文章会波及Docker常见命令根底知识点联合不同场景实操一起应用。本文章会波及联合工作过程中部署不同环境服务器的我的项目案例场景为初心进行理论细讲。 本文章次要讲述 Docker、Jenkins、GitLab、Git、JDK、SpringBoot、Maven等技术联合实现自动化运维部署(DevOps)利用工程,适宜SpringCloud部署。 初衷想法:在学习过程中遇到比拟乏味的问题、然而花了点心血和工夫去整顿,然而进行梳理进去一份文章比拟残缺有常识体系的DevOps自动化构建与部署工程文章,技术常识内容比拟多,而且文章内容较长,然而分了几个章程来讲述 什么是DevOps?DevOps(Development和Operations的组合词)是一组过程、办法与零碎的统称,用于促成开发(应用程序/软件工程)、技术经营和品质保障(QA)部门之间的沟通、合作与整合,它是一种器重“软件开发人员(Dev)”和“IT运维技术人员(Ops)”之间沟通单干的文化、静止或常规。透过自动化“软件交付”和“架构变更”的流程,来使得构建、测试、公布软件可能更加地快捷、频繁和牢靠。更多对于DevOps的介绍可参阅:超赞!阿里云外部超全K8s/ECS/RDS/OSS/DevOps/DTS实战手册,提供下载! 它的呈现是因为软件行业日益清晰地意识到:为了按时交付软件产品和服务,开发和经营工作必须严密单干。 波及软件环境搭建内容Centos7装置JDK1.8-u121Centos7装置Maven3.6.1Centos7装置GitCentOS7与Git配置免明码登陆Docker装置GitLabDocker创立NetWork网络Docker装置Registry私服Docker装置Jenkins特地阐明 1、如何应用Maven联合Docker把SpringBoot利用编译成可用的镜像进行部署。 2、其中JDK和Maven是传统形式进行装置,因为自己Centos操作系统是有其他软件依赖它们,有时候传统形式装置软件会更好,这里不过多的论述。有些软件在Docker装置过程与应用过程并没传统形式的简略,比方:Jenkins。 须要筹备的工作有哪些工程构造关上IDEA或Eclipse新建一个SpringBoot的利用. 环境配置特地阐明注意事项:其中Gitlab、Registry、Jenkins都装置在node1机器下面,也就是node1作为主机(master),node2作为slave(从机或副机),机器名起有意义或能辨别即可,举荐起master和slave,这里就不作过多的论述,为了防止看文章有疑难,请看清单列表.另外,关注民工哥技术之路公众号,回复“Java全栈常识体系”,送你一份40000字超强总结,Java 开发全栈技术常识体系.PDF手册! SpringBoot配置和代码详解工程的pom.xml配置SpringBoot和Docker依赖的jar配置 <dependencies><!-- Springboot依赖的Jar包 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!-- Springboot热部署jar--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional></dependency><!--yml配置文件提醒插件--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional></dependency><!-- spring-boot测试jar --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope></dependency> </dependencies> <build> <finalName>springboot</finalName> <!-- 肯定要申明如下配置 打包xml 到Jar包 --> <!-- <resources> <resource> <directory>src/main/java</directory> 是否替换资源中的属性 <filtering>false</filtering> </resource> </resources> <sourceDirectory>${project.basedir}/src/main/java</sourceDirectory> --> <plugins> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <!-- 默认反对jdk1.8编译 --> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin><!--docke rmaven编译插件--> <plugin> <groupId>com.spotify</groupId> <artifactId>docker-maven-plugin</artifactId> <version>0.4.12</version> <configuration> <dockerDirectory>${project.basedir}</dockerDirectory> <resources> <resource> <targetPath>/</targetPath> <directory>${project.build.directory}</directory> <include>${project.build.finalName}.jar</include> </resource> </resources> </configuration> </plugin> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-jar-plugin</artifactId> <configuration> <archive> <manifest> <mainClass>com.flong.SpringbootApplication</mainClass> </manifest> </archive> </configuration> </plugin> </plugins></build>no main manifest attribute谬误解决配置工程主入口 ...

July 29, 2021 · 3 min · jiezi

关于spring:spring泛型注入

泛型依赖注入Spring 4.0版本中更新了很多新性能,其中比拟重要的一个就是对带泛型的Bean进行依赖注入的反对。泛型依赖注入容许咱们在应用spring进行依赖注入的同时,利用泛型的长处对代码进行精简,同时在不减少代码的状况下减少代码的复用性。Spring默认依照字段的类进行依赖注入,而Spring4的新个性就是把泛型的具体类型也作为类的一种分类办法(Qualifier)。 背景假如有两个实体类Student和Teacher @Datapublic class Student implements IEntity{ private long id;} @Datapublic class Teacher implements IEntity{ private long id;}实体的存储,是通过仓储操作的,个别所有的实体仓储办法都是统一的,只有具体的实体类型不一样,定义仓储接口 public interface IRepository<TEntity extends IEntity>{ void add(TEntity entity);List<TEntity> findAll();...}定义仓储实现的基类,在本例中,应用List存储 public abstract class BaseRepository <TEntity extends IEntity> implements IRepository<TEntity>{ List<TEntity> datasource = new ArrayList<>();@Overridepublic void add(TEntity entity){ this.datasource.add(entity);}@Overridepublic List<TEntity> findAll(){ return datasource;}}泛型依赖注入的BeanBaseRepository 是一个抽象类,不合适注入到spring中,定义一个能够注入的bean @Repository()@Scope("prototype")public class DefaultRepository<TEntity extends IEntity> extends BaseRepository<TEntity>{}留神@Scope("prototype")注解,示意DefaultRepository的bean的作用是瞬态的,每次获取bean时都会创立一个新的bean,如果不增加作用域,默认spring的bean是单例的,这样注入的仓储实例会是同一个实例。在test中依赖注入@Autowired IRepository<Student> studentRepository; 和@Autowired IRepository<Teacher> teacherRepository;,验证这两个仓储的类型都是DefaultRepository类型,同时验证操作student不会影响到teacher。 @ExtendWith(SpringExtension.class)@ContextConfiguration( classes = {DemoTests.DemoTestsConfiguration.class})public class DemoTests { ...

July 28, 2021 · 1 min · jiezi

关于spring:SpringAOP构造注入实现技巧分享

AOP_面向切面编程初步理解 让咱们先设想一个场景,你正在编写一个我的项目,在开发过程中的多个模块都有某段反复的代码,于是你抉择将其形象成一个办法,而后在须要的中央调用这个办法,当须要批改这段代码时只须要批改这个办法就行。有一天,你的Boss给了新的需要,须要再形象出一个办法,而后再在各个须要这个办法的模块调用这个办法,这可能就让你头疼了,须要批改大量的代码,于是会想,能不能不批改源代码为零碎业务增加某种性能呢?侥幸的是,AOP能够很好的解决这个问题。 简略介绍 AOP:保障开发者不批改源代码的前提下,去为零碎中的业务组件增加某种通用性能,实质是由AOP框架批改业务组件的多个办法的源代码,咱们将其分为两类: 动态AOPAOP 框架在编译阶段对程序源代码进行批改,生成了动态的 AOP 代理类(生成的*.class文件曾经被改掉了,须要应用特定的编译器),比方 AspectJ。动静AOP:AOP 框架在运行阶段对动静生成代理对象(在内存中以 JDK 动静代理,或 CGlib 动静地生成 AOP 代理类),如 SpringAOP。 具体阐明Spring 的告诉类型 名称--标签-- 阐明前置告诉--@Before--用于配置前置告诉。指定加强的办法在切入点办法之前执行后置告诉--@AfterReturning--用于配置后置告诉。指定加强的办法在切入点办法之后执行盘绕告诉--@Around--用于配置盘绕告诉。指定加强的办法在切入点办法之前和之后都执行异样告诉--@AfterThrowing--用于配置异样抛出告诉。指定加强的办法在出现异常时执行最终告诉--@After--用于配置最终告诉。无论加强形式执行是否有异样都会执行切面类注解--@Aspect--标注该以后类是一个切面类断点注解--@Pointcut--应用一个返回值为 void 、办法体为空的办法来命名切入点 实战演练导入依赖包 <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.5</version> </dependency> <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.5</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.6</version> </dependency> <!-- https://mvnrepository.com/artifact/aopalliance/aopalliance --> <dependency> <groupId>aopalliance</groupId> <artifactId>aopalliance</artifactId> <version>1.0</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency></dependencies>创立一个加强类以及其接口 加强类接口:public interface VisitService { //用于实现前置告诉,后置告诉,异样告诉,最终告诉void visit(String str) throws Exception;//用于实现盘绕告诉void around();} ...

July 27, 2021 · 1 min · jiezi

关于spring:SpringBoot-Mybatis系列MapperMapper接口与Sql绑定几种姿势

【DB系列】SpringBoot系列Mybatis之Mapper接口与Sql绑定几种姿态通常咱们在应用Mybatis进行开发时,会抉择xml文件来写对应的sql,而后将Mapper接口与sql的xml文件建设绑定关系,而后在我的项目中调用mapper接口就能够执行对应的sql 那么如何将Mapper接口与sql进行绑定呢?本文将介绍四种常见的姿态 默认策略SpringBoot配置参数mybatis.mapper-locations<mapper>指定SqlSessionFactory指定<!-- more --> I. 环境筹备1. 数据库筹备应用mysql作为本文的实例数据库,新增一张表 CREATE TABLE `money` ( `id` int(11) unsigned NOT NULL AUTO_INCREMENT, `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名', `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱', `is_deleted` tinyint(1) NOT NULL DEFAULT '0', `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创立工夫', `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新工夫', PRIMARY KEY (`id`), KEY `name` (`name`)) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;2. 我的项目环境本文借助 SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 ...

July 26, 2021 · 2 min · jiezi

关于spring:SpringMVC上传文件报错Error-bean-with-name-multipartResolver

问题: SpringMVC上传文件报错: Error creating bean with name 'multipartResolver': Lookup method resolution failed; nested exception解决 导入commons-fileupload 依赖 <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>

July 22, 2021 · 1 min · jiezi

关于spring:详解Spring中Bean的作用域与生命周期

摘要:在利用Spring进行IOC配置时,对于bean的配置和应用始终都是比拟重要的一部分,同时如何正当的应用和创立bean对象,也是小伙伴们在学习和应用Spring时须要留神的局部,所以这一篇文章我就来和大家讲一下无关Spring中bean的作用域和其生命周期。本文分享自华为云社区《详解Spring中Bean的作用域与生命周期》,原文作者:灰小猿。 在利用Spring进行IOC配置时,对于bean的配置和应用始终都是比拟重要的一部分,同时如何正当的应用和创立bean对象,也是小伙伴们在学习和应用Spring时须要留神的局部,所以这一篇文章我就来和大家讲一下无关Spring中bean的作用域和其生命周期。 一、Bean的作用域首先咱们来讲一下有对于bean的作用域, 个别状况下,咱们书写在IOC容器中的配置信息,会在咱们的IOC容器运行时被创立,这就导致咱们通过IOC容器获取到bean对象的时候,往往都是获取到了单实例的Bean对象, 这样就意味着无论咱们应用多少个getBean()办法,获取到的同一个JavaBean都是同一个对象,这就是单实例Bean,整个我的项目都会共享这一个bean对象。 在Spring中,能够在<bean>元素的scope属性里设置bean的作用域,以决定这个bean是单实例的还是多实例的。Scope属性有四个参数,具体的应用能够看下图: 1、单实例Bean申明默认状况下,Spring只为每个在IOC容器里申明的bean创立惟一一个实例,整个IOC容器范畴内都能共享该实例:所有后续的getBean()调用和bean援用都将返回这个惟一的bean实例。该作用域被称为singleton,它是所有bean的默认作用域。也就是单实例。 为了验证这一说法,咱们在IOC中创立一个单实例的bean,并且获取该bean对象进行比照: <!-- singleton单实例bean 1、在容器创立时被创立 2、只有一个实例 --><bean id="book02" class="com.spring.beans.Book" scope="singleton"></bean>测试获取到的单实例bean是否是同一个: @Testpublic void test09() { // 单实例创立时创立的两个bean相等 Book book03 = (Book)iocContext3.getBean("book02"); Book book04 = (Book)iocContext3.getBean("book02"); System.out.println(book03==book04);}失去的后果是true; 2、多实例Bean申明而既然存在单实例,那么就肯定存在多实例。咱们能够为bean对象的scope属性设置prototype参数,以示意该实例是多实例的,同时获取IOC容器中的多实例bean,再将获取到的多实例bean进行比照, <!-- prototype多实例bean1、在容器创立时不会被创立,2、只有在被调用的时候才会被创立3、能够存在多个实例 --><bean id="book01" class="com.spring.beans.Book" scope="prototype"></bean>测试获取到的多实例bean是否是同一个: @Testpublic void test09() { // 多实例创立时,创立的两个bean对象不相等 Book book01 = (Book)iocContext3.getBean("book01"); Book book02 = (Book)iocContext3.getBean("book01"); System.out.println(book01==book02);}失去的后果是false 这就阐明了,通过多实例创立的bean对象是各不相同的。 在这里须要留神: 同时对于单实例和多实例bean的创立也有不同,当bean的作用域为单例时,Spring会在IOC容器对象创立时就创立bean的对象实例。而当bean的作用域为prototype时,IOC容器在获取bean的实例时创立bean的实例对象。 二、Bean的生命周期1、bean的初始和销毁其实咱们在IOC中创立的每一个bean对象都是有其特定的生命周期的,在Spring的IOC容器中能够治理bean的生命周期,Spring容许在bean生命周期内特定的工夫点执行指定的工作。如在bean初始化时执行的办法和bean被销毁时执行的办法。 Spring IOC容器对bean的生命周期进行治理的过程能够分为六步: 通过结构器或工厂办法创立bean实例为bean的属性设置值和对其余bean的援用调用bean的初始化办法bean能够失常应用当容器敞开时,调用bean的销毁办法那么对于bean的初始和销毁时执行的办法又该如何申明呢? 首先咱们应该在bean类外部增加初始和销毁时执行的办法。如上面这个javabean: package com.spring.beans;public class Book { private String bookName; private String author; /** * 初始化办法 * */ public void myInit() { System.out.println("book bean被创立"); } /** * 销毁时办法 * */ public void myDestory() { System.out.println("book bean被销毁"); } public String getBookName() { return bookName; } public void setBookName(String bookName) { this.bookName = bookName; } public String getAuthor() { return author; } public void setAuthor(String author) { this.author = author; } @Override public String toString() { return "Book [bookName=" + bookName + ", author=" + author + "]"; }}这时咱们在配置bean时,能够通过init-method和destroy-method 属性为bean指定初始化和销毁办法, ...

July 19, 2021 · 2 min · jiezi

关于spring:一文读懂-Spring-Bean-的生命周期

欢送大家关注我的微信公众号【老周聊架构】,Java后端支流技术栈的原理、源码剖析、架构以及各种互联网高并发、高性能、高可用的解决方案。一、前言明天咱们来说一说 Spring Bean 的生命周期,小伙伴们应该在面试中常常遇到,这是失常景象。因为 Spring Bean 的生命周期是除了 IoC、AOP 几个外围概念之外最重要概念,大家务必拿下。可 Spring 源代码又比较复杂,跟着跟着就不晓得跟到哪里去了,不太好拿下呀。这倒是真的,而且网上一上来就各种贴流程源码,对初学者来说是真的一脸懵逼,就像字都看的懂,但连在一块就不晓得意思了,太绕了。 本文老周试着讲的通俗易懂些,让更多的小伙伴们轻松的读懂 Spring Bean 的生命周期,并有对它有持续钻研学习的想法,那我写此文的目标也就达到了。 咱们讲 Spring Bean 的生命周期之前先来理解两个概念: 1.1 什么是 Bean 咱们来看下 Spring Framework 的官网文档: In Spring, the objects that form the backbone of your application and that are managed by the Spring IoC container are called beans. A bean is an object that is instantiated, assembled, and otherwise managed by a Spring IoC container. Otherwise, a bean is simply one of many objects in your application. Beans, and the dependencies among them, are reflected in the configuration metadata used by a container.简而言之,bean 是由 Spring IoC 容器实例化、组装和治理的对象。 ...

July 19, 2021 · 5 min · jiezi

关于spring:BeforeAround和After执行顺序

用过spring框架进行开发的人,多多少少会应用过它的AOP性能,都晓得有@Before、@Around和@After等advice。最近,为了实现我的项目中的输入日志和权限管制这两个需要,我也应用到了AOP性能。我应用到了@Before、@Around这两个advice。但在,应用过程中,却对它们的执行程序并不分明。为了弄清楚在不同状况下,这些advice到底是以怎么样的一个程序进行执行的,我作了个测试,在此将其记录下来,以供当前查看。 前提对于AOP相干类(aspect、pointcut等)的概念,本文不作阐明。对于如何让spring框架扫描到AOP,本文也不作阐明。状况一: 一个办法只被一个Aspect类拦挡当一个办法只被一个Aspect拦挡时,这个Aspect中的不同advice是依照怎么的程序进行执行的呢?请看: 增加 PointCut类该pointcut用来拦挡test包下的所有类中的所有办法。 package test; import org.aspectj.lang.annotation.Pointcut; public class PointCuts { @Pointcut(value = "within(test.*)")public void aopDemo() {}} 增加Aspect类该类中的advice将会用到下面的pointcut,应用办法请看各个advice的value属性。 package test; import org.aspectj.lang.JoinPoint;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.*;import org.springframework.stereotype.Component; @Component@Aspectpublic class Aspect1 { @Before(value = "test.PointCuts.aopDemo()")public void before(JoinPoint joinPoint) { System.out.println("[Aspect1] before advise");}@Around(value = "test.PointCuts.aopDemo()")public void around(ProceedingJoinPoint pjp) throws Throwable{ System.out.println("[Aspect1] around advise 1"); pjp.proceed(); System.out.println("[Aspect1] around advise2");}@AfterReturning(value = "test.PointCuts.aopDemo()")public void afterReturning(JoinPoint joinPoint) { System.out.println("[Aspect1] afterReturning advise");}@AfterThrowing(value = "test.PointCuts.aopDemo()")public void afterThrowing(JoinPoint joinPoint) { System.out.println("[Aspect1] afterThrowing advise");}@After(value = "test.PointCuts.aopDemo()")public void after(JoinPoint joinPoint) { System.out.println("[Aspect1] after advise");}} ...

July 18, 2021 · 4 min · jiezi

关于spring:Spring-的循环依赖问题

什么是循环依赖什么是循环依赖呢?能够把它拆分成循环和依赖两个局部来看,循环是指计算机领域中的循环,执行流程造成闭合回路;依赖就是实现这个动作的前提筹备条件,和咱们平时说的依赖大体上含意统一。放到 Spring 中来看就一个或多个 Bean 实例之间存在间接或间接的依赖关系,形成循环调用,循环依赖能够分为间接循环依赖和间接循环依赖,间接循环依赖的简略依赖场景:Bean A 依赖于 Bean B,而后 Bean B 又反过来依赖于 Bean A(Bean A -> Bean B -> Bean A),间接循环依赖的一个依赖场景:Bean A 依赖于 Bean B,Bean B 依赖于 Bean C,Bean C 依赖于 Bean A,两头多了一层,然而最终还是造成循环(Bean A -> Bean B -> Bean C -> Bean A)。 循环依赖的类型第一种是自依赖,本人依赖本人从而造成循环依赖,个别状况下不会产生这种循环依赖,因为它很容易被咱们发现。 第二种是间接依赖,产生在两个对象之间,比方:Bean A 依赖于 Bean B,而后 Bean B 又反过来依赖于 Bean A,如果比拟仔细的话肉眼也不难发现。 第三种是间接依赖,这种依赖类型产生在 3 个或者以上的对象依赖的场景,间接依赖最简略的场景:Bean A 依赖于 Bean B,Bean B 依赖于 Bean C,Bean C 依赖于 Bean A,能够设想当两头依赖的对象很多时,是很难发现这种循环依赖的,个别都是借助一些工具排查。 ...

July 17, 2021 · 3 min · jiezi

关于spring:首发撸了谷歌写的Spring源码笔记后感觉之前读的都是渣渣

Spring让咱们能够更快,更轻松,更平安地进行Java编程。Spring对速度,简略性和生产率的关注使其成为世界上最受欢迎的Java框架。 像阿里巴巴,亚马逊,谷歌,微软等在内的所有科技巨头对Spring都有很大的奉献,因而Spring常常在大厂面试的时候被问到,上面我选了几道对于pring源码的面试题,看大家能不能答复进去: IOC源码次要流程bean相互依赖注入问题为什么要应用springSpring事务在controller层不起作用的起因如何用基于 Java 配置的形式配置 SpringSpring的几种注入bean的形式如果你平时只会一些CRUD,或者你都没有读过源码,面试大厂必定是要熄火的!!!上面,我为大家分享这份谷歌大神撸的Spring源码笔记,图文联合,条理清晰,我认为所有Java开发人员都能够且有必要学习,全新版本,独家首发,须要这份Spring源码笔记的老铁能够在文末获取!!不多bb,看目录因为篇幅起因,为了不影响浏览在这就展现了整个目录和内容截图,须要这份Spring源码笔记的老铁能够在文末获取!首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣一、概述对Spring高级框架做肯定理解,做好后期的预习,后续的学习更容易了解和上手。https://p6-tt.byteimg.com/ori...首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣二、核心思想了解了核心思想,围绕这个思维去学习Spring源码就变得事倍功半了。其实,IOC和AOP不是spring提出的,然而spring在技术档次把这两个思维做了十分好的实现。首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣三、手写实现 IoC 和 AOP通过用【银行转账】的案例,剖析该案例在代码档次中的问题,用已有常识解决这些问题,整个过程带你一步步剖析并手写实现IoC 和 AOP,本人剖析过的才是本人的常识!首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣四、Spring IOC 利用根底到高级,思路清晰,容易了解,上手超快首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣五、Spring IOC源码深度分析学习源码后期很干燥,但对于每一个开发人员来说,这无疑不是进步造就代码思维,深刻了解框架最好、最重要的办法。首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣六、Spring AOP 利用搞懂这些,对于工作效率有显著的晋升,更深刻的了解源码首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣七、Spring AOP源码深度分析层层分析,深刻递进,最初吃透源码首发!撸了谷歌大神写的Spring源码笔记后,感觉之前读的都是渣渣如何支付这份学习笔记呢? 转发本文,关注点击这里可收费获取!

July 17, 2021 · 1 min · jiezi

关于spring:设计模式学习16Java实现命令模式

写在后面 记录学习设计模式的笔记进步对设计模式的灵活运用学习地址 https://www.bilibili.com/vide... https://www.bilibili.com/vide... 参考文章 http://c.biancheng.net/view/1... 我的项目源码https://gitee.com/zhuang-kang/DesignPattern 18,命令模式18.1 命令模式的定义和特点命令(Command)模式的定义如下:将一个申请封装为一个对象,使发出请求的责任和执行申请的责任宰割开。这样两者之间通过命令对象进行沟通,这样不便将命令对象进行贮存、传递、调用、减少与治理。 命令模式的次要长处如下。 通过引入中间件(形象接口)升高零碎的耦合度。扩展性良好,减少或删除命令十分不便。采纳命令模式减少与删除命令不会影响其余类,且满足“开闭准则”。能够实现宏命令。命令模式能够与组合模式联合,将多个命令装配成一个组合命令,即宏命令。不便实现 Undo 和 Redo 操作。命令模式能够与前面介绍的备忘录模式联合,实现命令的撤销与复原。能够在现有命令的根底上,减少额定性能。比方日志记录,联合装璜器模式会更加灵便。其毛病是: 可能产生大量具体的命令类。因为每一个具体操作都须要设计一个具体命令类,这会减少零碎的复杂性。命令模式的后果其实就是接管方的执行后果,然而为了以命令的模式进行架构、解耦申请与实现,引入了额定类型构造(引入了申请方与形象命令接口),减少了了解上的艰难。不过这也是设计模式的通病,形象必然会额定减少类的数量,代码抽离必定比代码聚合更加难了解。18.2 命令模式的构造与实现18.2.1 命令模式的构造形象命令类(Command)角色:申明执行命令的接口,领有执行命令的形象办法 execute()。具体命令类(Concrete Command)角色:是形象命令类的具体实现类,它领有接收者对象,并通过调用接收者的性能来实现命令要执行的操作。实现者/接收者(Receiver)角色:执行命令性能的相干操作,是具体命令对象业务的真正实现者。调用者/请求者(Invoker)角色:是申请的发送者,它通常领有很多的命令对象,并通过拜访命令对象来执行相干申请,它不间接拜访接收者。18.2.2 代码实现关系类图 Command package com.zhuang.command;/** * @Classname Command * @Description 形象命令类 * @Date 2021/3/27 10:25 * @Created by dell */public interface Command { void execute(); // 只须要定义一个对立的执行办法}OrderCommand package com.zhuang.command;/** * @Classname OrderCommand * @Description 具体命令类 * @Date 2021/3/27 10:25 * @Created by dell */public class OrderCommand implements Command{ //持有接受者对象 private SeniorChef receiver; private Order order; public OrderCommand(SeniorChef receiver, Order order) { this.receiver = receiver; this.order = order; } @Override public void execute() { System.out.println(order.getDiningTable()+"桌的订单:"); for (String key : order.getFoodDic().keySet()) { receiver.makefood(order.getFoodDic().get(key),key); } try { Thread.sleep(1000); //模仿做饭 睡眠1秒 } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(order.getDiningTable()+"桌的饭弄好了"); }}Order ...

July 17, 2021 · 3 min · jiezi

关于spring:WebFlux-和-Spring-Security-会碰出哪些火花

WebFlux 系列松哥曾经连着写了十几篇了,Spring Security 系列之前更是发了 68 篇(公号后盾回复 ss 获取系列教程),不过之前这两个教程都是离开讲的,还没有把这两个交融到一起讲过。 随着 WebFlux 的倒退,咱们有必要来理解下在响应式编程中如何应用 Spring Security。明天松哥就通过一个简略的案例来和大家分享下如何在 WebFlux 中应用 Spring Security。 1.基于内存的应用先来看一个简略的,就是把用户信息保留在内存中。 首先咱们来创立一个新的我的项目,引入 WebFlux 和 Spring Security 依赖,如下: 我的项目创立胜利后,咱们增加一个接口,用来获取登录用户信息,如下: @RestControllerpublic class UserController { @GetMapping("/user") public Mono<Principal> getCurrentUser(Mono<Principal> principal) { return principal; }}留神咱们的返回值是 Mono<Principal>,接口的参数也是反对 Mono<Principal> 的。 这就能够了,接下来咱们启动我的项目,在启动的过程中,控制台就会打印出默认的用户明码,拿着默认的用户明码以及默认用户名 user 去登录,登录实现后就能够拜访 /user 接口了,这个过程和一般的 Spring Security 用法并没有什么差别,所以我就不多说了,如果大家对一般的 Spring Security 用法还不相熟,也能够看看松哥的新书《深入浅出 Spring Security》。 如果咱们想配置基于内存的用户信息,该怎么配置呢?增加如下配置类即可: @Configurationpublic class SecurityConfig { @Bean MapReactiveUserDetailsService mapReactiveUserDetailsService() { UserDetails ud1 = User.withUsername("admin") .password("{noop}123") .roles("admin") .build(); UserDetails ud2 = User.withUsername("zhangsan") .password("{noop}123") .roles("user") .build(); return new MapReactiveUserDetailsService(ud1, ud2); }}只须要提供一个 MapReactiveUserDetailsService 实例即可。 ...

July 16, 2021 · 2 min · jiezi

关于spring:JPA注解

@Entity 申明类为实体或表。@Table 申明表名。@Basic 指定非束缚明确的各个字段。@Embedded 指定类或它的值是一个可嵌入的类的实例的实体的属性。@Id 指定的类的属性,用于辨认(一个表中的主键)。@GeneratedValue 指定如何标识属性能够被初始化,例如主动、手动、或从序列表中取得的值。@Transient 指定的属性,它是不长久的,即:该值永远不会存储在数据库中。@Column 指定长久属性栏属性。@SequenceGenerator 指定在@GeneratedValue注解中指定的属性的值。它创立了一个序列。@TableGenerator 指定在@GeneratedValue批注指定属性的值发生器。它发明了的值生成的表。@AccessType 这种类型的正文用于设置拜访类型。如果设置@AccessType(FIELD),则能够间接拜访变量并且不须要getter和setter,但必须为public。如果设置 @AccessType(PROPERTY),通过getter和setter办法拜访Entity的变量。@JoinColumn 指定一个实体组织或实体的汇合。这是用在多对一和一对多关联。@UniqueConstraint 指定的字段和用于次要或辅助表的惟一束缚。@ColumnResult 参考应用select子句的SQL查问中的列名。@ManyToMany 定义了连贯表之间的多对多一对多的关系。@ManyToOne 定义了连贯表之间的多对一的关系。@OneToMany 定义了连贯表之间存在一个一对多的关系。@OneToOne 定义了连贯表之间有一个一对一的关系。@NamedQueries 指定命名查问的列表。@NamedQuery 指定应用动态名称的查问参考网站https://blog.csdn.net/wujiaqi...

July 8, 2021 · 1 min · jiezi

关于spring:Mapper和Repository的区别

    1、应用@mapper后,不须要在spring配置中设置扫描地址,通过mapper.xml外面的namespace属性对应相干的mapper类,spring将动静的生成Bean后注入到ServiceImpl中。     2、@repository则须要在Spring中配置扫描包地址,而后生成dao层的bean,之后被注入到ServiceImpl中@Repository注解润饰哪个类,则表明这个类具备对对象进行CRUD(增删改查)的性能,而且@Repository是@Component注解的一个派生品,所以被@Repository注解的类能够主动的被@ComponentScan 通过门路扫描给找到。(这也在肯定水平上解释了,为什么被@Repository注解的类也能@Autowired)

July 8, 2021 · 1 min · jiezi

关于spring:译Hello-Spring-GraphQL

前言我很快乐地发表咱们新增了 Spring GraphQL 我的项目并且 1.0 公布的初始里程碑可用。该我的项目集成了 GraphQL Java 和 Spring,并在两个团队之间合作开发。 明天是 GraphQL Java 的 6 岁生日!我从一开始所做的一个根本决定是将任何 HTTP 和 IO 方面作为独自的思考。GraphQL Java 始终“只是”一个执行 GraphQL 申请的引擎。这个决定失去了回报,但显著的毛病是须要为理论应用创立本人的 HTTP 适配器。 这导致多年来为 Spring 创立了大量的 GraphQL 集成,包含来自 GraphQL Java 团队的 GraphQL Java Spring 我的项目。但坦率地说,我始终渴望一流的 Spring 集成。 一、介绍如果您想开始应用,请转到咱们的 参考文档 并浏览 “Boot Starter” 局部, 或运行 samples 。 如果您对 GraphQL 不太理解,这里有很多很好的资源。 你能够从 graphql.org/learn 开始。 基于 InfoQ 2020 年 架构趋势 ,GraphQL 被 宽泛采纳 并处于“Early Majority”。它提供了一种更专一于数据的 REST API 的代替计划,并提供了一种模式和一种供客户端应用的查询语言。 在这份 JavaScript 现状报告 中,从客户端的角度来看,这种吸引力是不言而喻的。 你能够浏览 GitHub 的应用案例 ,理解它为什么应用 GraphQL。 ...

July 7, 2021 · 2 min · jiezi

关于spring:锁屏面试题百日百刷Spring篇四

锁屏面试题百日百刷,每个工作日保持更新面试题。锁屏面试题app、小程序现已上线,官网地址:https://www.demosoftware.cc/#...。已收录了每日更新的面试题的所有内容,还蕴含特色的解锁屏幕温习面试题、每日编程题目邮件推送等性能。让你在面试中后人一步,吊打面试官!接下来的是今日的面试题: 1、在 Spring 中如何注入一个 java 汇合?Spring 提供以下几种汇合的配置元素:1.<list>类型用于注入一列值,容许有雷同的值。<!-- 给数组注入值 --> <property name="empName"> <list> <value>小米</value> <value>小明</value> <value>小四</value> </list> </property> <!-- 给list注入值 能够有雷同的多个对象 --> <property name="empList"> <list> <ref bean="emp1" /> <ref bean="emp2"/> </list> </property>2.<set> 类型用于注入一组值,不容许有雷同的值。<!-- 给set注入值 不能有雷同的对象 --> <property name="empSets"> <set> <ref bean="emp1" /> <ref bean="emp2"/> </set> </property>3.<map> 类型用于注入一组键值对,键和值都能够为任意类型。 <!-- 给map注入值 只有map中的key值不一样就能够拆卸value --> <property name="empMap"> <map> <entry key="1" value-ref="emp1" /> <entry key="2" value-ref="emp2" /> </map> </property>4.<props>类型用于注入一组键值对,键和值都只能为 String 类型。<!-- 给属性汇合配置 --> <property name="pp"> <props> <prop key="pp1">hello</prop> <prop key="pp2">world</prop> </props> </property>2、什么是 bean 的主动拆卸?~毋庸在 Spring 配置文件中形容 javaBean 之间的依赖关系(如配置<property>、<constructor-arg>)。IOC 容器会主动建设 javabean 之间的关联关系。~Spring IOC容器能够主动拆卸Bean,须要做的仅仅切实<bean>的autowire属性里指定主动拆卸的模式。~Spring2.5开始反对注解@Autowired和@Resource来主动拆卸3、解释不同形式的主动拆卸?有五种主动拆卸的形式,能够用来领导 Spring 容器用主动拆卸形式来进行依赖注入。1)no:默认的形式是不进行主动拆卸,通过显式设置 ref 属性来进行拆卸。2)byName:通过参数名主动拆卸,Spring 容器在配置文件中发现 bean 的 autowire 属性被设置成 byname,之后容器试图匹配、拆卸和该 bean 的属性具备雷同名字的 bean。<bean id="cat" class="pojo.Cat"/><bean id="dog" class="pojo.Dog"/><bean id="people" class="pojo.People" autowire="byName"> ...

July 6, 2021 · 2 min · jiezi

关于spring:配置文件里数据的读取以及乱码的解决

1 应用@ConfigurationProperties(prefix = "persion")默认从全局配置文件中找2 应用@Value("${persion.name}")3 两者比拟 4乱码问题 spring.thymeleaf.cache=false #spring.thymeleaf.mode=LEGACYHTML5 spring.thymeleaf.encoding=utf-85 加载制订配置文件(1)@PropertySource(value={"classpath:persion.properties"})(2)@ImportResource((locations={"classpath:persion.properties"})导入spring的配置文件,让其失效(3)应用@Configuration

July 5, 2021 · 1 min · jiezi

关于spring:Spring系列缓存注解Cacheable-CacheEvit-CachePut-使用姿势介绍

SpringBoot系列缓存注解@Cacheable @CacheEvit @CachePut应用姿态介绍Spring在3.1版本,就提供了一条基于注解的缓存策略,理论应用起来还是很丝滑的,本文将针对几个罕用的注解进行简略的介绍阐明,有须要的小伙伴能够尝试一下 本文次要知识点: @Cacheable: 缓存存在,则应用缓存;不存在,则执行办法,并将后果塞入缓存@CacheEvit: 生效缓存@CachePut: 更新缓存<!-- more --> I. 我的项目环境1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA + redis5.0进行开发 开一个web服务用于测试 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency></dependencies>全程应用默认配置,redis本机,端口6379,无明码 II. 缓存注解介绍1. @Cacheable这个注解用于润饰办法or类,当咱们拜访它润饰的办法时,优先从缓存中获取,若缓存中存在,则间接获取缓存的值;缓存不存在时,执行办法,并将后果写入缓存 这个注解,有两个比拟外围的设置 /** * 与 cacheNames 成果等价 */ @AliasFor("cacheNames") String[] value() default {}; @AliasFor("value") String[] cacheNames() default {}; /** * 缓存key */ String key() default "";cacheNames能够了解为缓存key的前缀,能够为组件缓存的key变量;当key不设置时,应用办法参数来初始化,留神key为SpEL表达式,因而如果要写字符串时,用单引号括起来 一个简略的应用姿态 /** * 首先从缓存中查,查到之后,间接返回缓存数据;否则执行办法,并将后果缓存 * <p> * redisKey: cacheNames + key 组合而成 --> 反对SpEL * redisValue: 返回后果 * * @param name * @return */@Cacheable(cacheNames = "say", key = "'p_'+ #name")public String sayHello(String name) { return "hello+" + name + "-->" + UUID.randomUUID().toString();}如咱们传参为 yihuihui, 那么缓存key为 say::p_yihuihui ...

July 2, 2021 · 2 min · jiezi

关于spring:设计模式学习13Java实现模板方法模式

写在后面 记录学习设计模式的笔记进步对设计模式的灵活运用学习地址 https://www.bilibili.com/vide... https://www.bilibili.com/vide... 参考文章 http://c.biancheng.net/view/1... **我的项目源码https://gitee.com/zhuang-kang/DesignPattern** 15,模板办法模式15.1 模板办法模式的定义和特点模板办法(Template Method)模式的定义如下:定义一个操作中的算法骨架,而将算法的一些步骤提早到子类中,使得子类能够不扭转该算法构造的状况下重定义该算法的某些特定步骤,它是一品种行为型模式。 该模式的次要长处如下。 它封装了不变局部,扩大可变局部。它把认为是不变局部的算法封装到父类中实现,而把可变局部算法由子类继承实现,便于子类持续扩大。它在父类中提取了公共的局部代码,便于代码复用。局部办法是由子类实现的,因而子类能够通过扩大形式减少相应的性能,合乎开闭准则。该模式的次要毛病如下。 对每个不同的实现都须要定义一个子类,这会导致类的个数减少,零碎更加宏大,设计也更加形象,间接地减少了零碎实现的复杂度。父类中的形象办法由子类实现,子类执行的后果会影响父类的后果,这导致一种反向的控制结构,它进步了代码浏览的难度。因为继承关系本身的毛病,如果父类增加新的形象办法,则所有子类都要改一遍。15.2 模板办法模式的构造与实现15.2.1 模板办法模式的构造模板办法(Template Method)模式蕴含以下次要角色: 抽象类(Abstract Class):负责给出一个算法的轮廓和骨架。它由一个模板办法和若干个根本办法形成。 模板办法:定义了算法的骨架,按某种顺序调用其蕴含的根本办法。根本办法:是实现算法各个步骤的办法,是模板办法的组成部分。根本办法又能够分为三种: 形象办法(Abstract Method) :一个形象办法由抽象类申明、由其具体子类实现。具体方法(Concrete Method) :一个具体方法由一个抽象类或具体类申明并实现,其子类能够进行笼罩也能够间接继承。钩子办法(Hook Method) :在抽象类中曾经实现,包含用于判断的逻辑办法和须要子类重写的空办法两种。 个别钩子办法是用于判断的逻辑办法,这类办法名个别为isXxx,返回值类型为boolean类型。 具体子类(Concrete Class):实现抽象类中所定义的形象办法和钩子办法,它们是一个顶级逻辑的组成步骤。 15.2.2 代码实现AbstractClass package com.zhuang.template;/** * @Classname AbstractClass * @Description 抽象类 * @Date 2021/3/26 20:06 * @Created by dell */public abstract class AbstractClass { public final void work() { //起床 this.wake(); //刷牙 this.brush(); //吃早饭 this.breakfast(); //交通工具 this.transport(); //睡觉 this.sleep(); } //步骤一样 间接实现 public void wake() { System.out.println("起床..."); } //步骤一样 间接实现 public void brush() { System.out.println("刷牙..."); } // 步骤不一样 (一个是吃面包 一个是喝牛奶) public abstract void breakfast(); // 步骤不一样 (一个是开车 一个是坐地铁) public abstract void transport(); // 步骤一样 间接实现 public void sleep() { System.out.println("睡觉..."); }}ConcreteClass_BreakFast ...

July 2, 2021 · 3 min · jiezi

关于spring:手写一个Spring-Boot-Starter

嗨~大家好,我是阿壮,一个后端程序员,明天和大家分享Spring Boot的主动拆卸原理,并手写一个Starter加深对Spring Boot的了解。 家喻户晓,Spring Boot为了简化开发,省去了Spring中很多的XML文件。为了了解Spring Boot中主动拆卸的原理,明天咱们基于这个机制本人入手实现一个Starter组件。 我的项目构造 步骤新建一个名为:redis-spring-boot-starter的Maven我的项目增加Maven依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.redisson</groupId> <artifactId>redisson</artifactId> <version>3.11.1</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency> </dependencies> <dependencyManagement> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-dependencies</artifactId> <version>2.3.2.RELEASE</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement>定义属性,实现在applications.properties中的Redis的连贯参数。 @ConfigurationProperties(prefix = "gp.redisson")public class RedissonProperties { private String host = "localhost"; private String password; private int port = 6379; private int timeout; private boolean ssl; public String getHost() { return host; } public void setHost(String host) { this.host = host; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public int getPort() { return port; } public void setPort(int port) { this.port = port; } public int getTimeout() { return timeout; } public void setTimeout(int timeout) { this.timeout = timeout; } public boolean isSsl() { return ssl; } public void setSsl(boolean ssl) { this.ssl = ssl; }}定义须要主动拆卸的配置类 ...

June 29, 2021 · 1 min · jiezi

关于spring:Why-using-constructor-based-dependency-injection-Spring

IntroductionWhen I am diving into spring framework, I noticed different ways to initialize dependency injection. The normal way is field based dependency injection, the most easy-read and simple way by merely add the annotation @Autowired @Controllerpublic class testController { @Autowired priviate testService testService;}The second way is called setter based dependency injection @Controllerpublic class testController { priviate testService testService; @Autowired public void testService(testService testService) { this.testService = testService; }}I seldom used this kind of way, explanation from spring doc. ...

June 29, 2021 · 2 min · jiezi

关于spring:设计模式学习09Java实现桥接模式

写在后面 记录学习设计模式的笔记进步对设计模式的灵活运用学习地址 https://www.bilibili.com/video/BV1G4411c7N4 https://www.bilibili.com/video/BV1Np4y1z7BU 参考文章 http://c.biancheng.net/view/1317.html 我的项目源码https://gitee.com/zhuang-kang/DesignPattern 11,桥接模式11.1 桥接模式的定义和特点桥接(Bridge)模式的定义如下:将形象与实现拆散,使它们能够独立变动。它是用组合关系代替继承关系来实现,从而升高了形象和实现这两个可变维度的耦合度。 通过下面的解说,咱们能很好的感觉到桥接模式遵循了里氏替换准则和依赖倒置准则,最终实现了开闭准则,对批改敞开,对扩大凋谢。 桥接(Bridge)模式的长处是: 形象与实现拆散,扩大能力强合乎开闭准则合乎合成复用准则其实现细节对客户通明毛病是: 因为聚合关系建设在形象层,要求开发者针对抽象化进行设计与编程,能正确地辨认出零碎中两个独立变动的维度,这减少了零碎的了解与设计难度。 11.2 桥接模式的构造与实现11.2.1 桥接模式的构造抽象化(Abstraction)角色:定义抽象类,并蕴含一个对实现化对象的援用。扩大抽象化(Refined Abstraction)角色:是抽象化角色的子类,实现父类中的业务办法,并通过组合关系调用实现化角色中的业务办法。实现化(Implementor)角色:定义实现化角色的接口,供扩大抽象化角色调用。具体实现化(Concrete Implementor)角色:给出实现化角色接口的具体实现。11.2.2 代码实现Brand 抽象化角色 package com.zhuang.bridge;/** * @Classname Brand * @Description 品牌类 * @Date 2021/3/22 9:31 * @Created by dell */public interface Brand { void open(); void call(); void close();}Vivo 拓展抽象化角色 package com.zhuang.bridge;/** * @Classname Vivo * @Description 手机品牌 实现品牌接口 * @Date 2021/3/22 9:30 * @Created by dell */public class Vivo implements Brand { @Override public void open() { System.out.println("Vivo手机开机"); } @Override public void call() { System.out.println("Vivo手机打电话"); } @Override public void close() { System.out.println("Vivo手机关机"); }}XiaoMi 拓展抽象化角色 ...

June 28, 2021 · 2 min · jiezi

关于spring:SpringBootSpring容器工具类SpringContextUtilsjava

SpringBoot——容器工具类SpringContextUtils.java更多精彩内容,欢送关注我的微信公众号:编程Thinker (code_thinker_666) <img src="http://jeff.spring4all.com/FoAxSKtLwp-tLYc4XdMCr9SVT8DU" style="zoom: 25%;" /> 背景 在SpringBoot我的项目中,通常会遇到工具类中调用Spring容器中的Bean,因为工具类通常是静态方法,咱们通常不应用主动注入,这时,就须要一种不主动注入便能够从Spring容器中拿出Bean的工具了,这里我把我日常用的工具类SpringContextUtils.java,分享给大家,心愿能够帮到你。 工具类git地址:https://gitee.com/learning-wo... 源码如下: import lombok.Data;import org.springframework.context.ApplicationContext;import org.springframework.context.ApplicationContextAware;import org.springframework.stereotype.Component;import java.util.Map;/** * Spring容器工具类 * * @author chentiefeng * @date 2020-10-23 11:37 */@Componentpublic class SpringContextUtils implements ApplicationContextAware { /** * 上下文对象 */ private static final AppContainer APP_CONTAINER = new AppContainer(); @Override public void setApplicationContext(ApplicationContext applicationContext) { APP_CONTAINER.setApplicationContext(applicationContext); } /** * 获取ApplicationContext * * @return */ public static ApplicationContext getApplicationContext() { return APP_CONTAINER.getApplicationContext(); } /** * 通过clazz,从spring容器中获取bean * * @param clazz * @param <T> * @return */ public static <T> T getBean(Class<T> clazz) { return getApplicationContext().getBean(clazz); } /** * 获取某一类型的bean汇合 * * @param clazz * @param <T> * @return */ public static <T> Map<String, T> getBeans(Class<T> clazz) { return getApplicationContext().getBeansOfType(clazz); } /** * 通过name和clazz,从spring容器中获取bean * * @param clazz * @param <T> * @return */ public static <T> T getBean(String name, Class<T> clazz) { return getApplicationContext().getBean(name, clazz); } /** * 动态外部类,用于寄存ApplicationContext */ @Data public static class AppContainer { private ApplicationContext applicationContext; } /** * 获取配置文件配置项的值 * * @param key 配置项key */ public static String getEnvironmentProperty(String key) { return getApplicationContext().getEnvironment().getProperty(key); } /** * 获取spring.profiles.active */ public static String[] getActiveProfile() { return getApplicationContext().getEnvironment().getActiveProfiles(); }}应用示例/** * 用户工具类,用于获取用户信息 * * @author chentiefeng * @date 2021/2/8 17:38 */public class UserUtils { /** * 获取以后用户的ID * @return */ public static UUID getCurrentUserId(){ UserService userService = SpringContextUtils.getBeans(UserService.class); return userService.getCurrentUser().getId(); }}本文由博客一文多发平台 OpenWrite 公布!

June 24, 2021 · 2 min · jiezi

关于spring:聊聊spring-bean名称命名的那些事儿

前言用了多年spring,始终想当然把spring默认的beanName当成是类名的首字母小写,比方HelloService其beanName为helloService。直到有天对接了供方厂商的接口,他有个类形如ABService,于是用 getBean(“aBService”) 的形式获取bean,后果取到是null,一开始认为是ABservice没注入,前面采纳 getBean(ABService.class) 能胜利获取到bean,阐明ABService是有注入到IOC容器中,然而为啥用aBService获取不到bean?于是就用如下代码段,打印出相应ABService对应的beanName applicationContext.getBeansOfType(ABService.class).forEach((beanName,bean)->{ System.out.println(beanName + ":" + bean); });打印进去的后果,如下 ABService:com.github.lybgeek.ABService@245b6b85beanName居然是ABService,这就和之前的想当然有出入。于是只好查看源码 源码查看源码查看有2种形式,本文的示例是springboot我的项目 办法一:从main办法间接调试断点从图能够看出如果是以扫描注解注入模式,其beanName的生成规定是由 org.springframework.context.annotation.AnnotationBeanNameGenerator#generateBeanName决定。 ps: 这种间接从main启动类调试起,比拟实用于工夫比拟多,或者排查毫无脉络 办法二:带着问题查看,靠猜加验证的形式利用idea的find Usage查找援用,比方ABService的注解@service,咱们能够间接查看哪个援用到@Service,再猜想下beanName的生成规定通过猜,咱们基本上就能够定位出比拟合乎咱们需要的办法 源码验证从下面的剖析,咱们能够晓得如果是扫描bean注解注入的形式,其生成beanName规定,是在 org.springframework.context.annotation.AnnotationBeanNameGenerator其生成规定代码如下 @Override public String generateBeanName(BeanDefinition definition, BeanDefinitionRegistry registry) { if (definition instanceof AnnotatedBeanDefinition) { String beanName = determineBeanNameFromAnnotation((AnnotatedBeanDefinition) definition); if (StringUtils.hasText(beanName)) { // Explicit bean name found. return beanName; } } // Fallback: generate a unique default bean name. return buildDefaultBeanName(definition, registry); }从代码段,咱们能够看出,注解上有取名,比方@Service(“abService”),则beanName为abService,如果没有取名,则看 protected String buildDefaultBeanName(BeanDefinition definition) { String beanClassName = definition.getBeanClassName(); Assert.state(beanClassName != null, "No bean class name set"); String shortClassName = ClassUtils.getShortName(beanClassName); return Introspector.decapitalize(shortClassName); }public static String decapitalize(String name) { if (name == null || name.length() == 0) { return name; } if (name.length() > 1 && Character.isUpperCase(name.charAt(1)) && Character.isUpperCase(name.charAt(0))){ return name; } char chars[] = name.toCharArray(); chars[0] = Character.toLowerCase(chars[0]); return new String(chars); }其实从代码咱们就很容易看出答案了,如果类名前两个或以上个字母都是大写,则beanName和类名就一样了,不会进行首字母小写转换。 ...

June 24, 2021 · 2 min · jiezi

关于spring:SpringIOC2

IOC根底 Inversion of controller 管制反转, 领有对象创立的控制权与对象生命周期的管理权.BeanFactory与ApplicationContext区别 BeanFactory容器是顶层接口,定义一些根底性能与根底标准, 而ApplicationContext 容器是高级接口也是BeanFactory的子接口.ApplicationContext领有更多的性能, 国际化、资源文件读取、类列表展现 Bean的作用范畴与生命周期 作用范畴与生命周期的关系, 作用范畴决定了他的生命周期作用范畴 singleton、property、request、session、application、websocketsingleton(单例模式)与property(多例模式)应用较多; singleton生命周期为程序启动时到完结、property生命周期为当其应用时创立新对象. 高级个性 提早加载 ApplicationContext在实例化的时候也会将singleton objects 提前实例化. 提前实例化意味着作为初始化的一部分. 如果不想让某一singleton object 提前被实例化, 能够标识为懒加载(lazy)开启提早加载肯定水平进步容器启动和运行性能 对于不常应用的 Bean 设置提早加载,这样偶然应用的时候再加载,不必要从一开始该 Bean 就占 用资源FactoryBean与BeanFactory FactoryBean生成某种类的工具类. 而BeanFactory是容器的顶级接口spring中存在两种Bean, 一般Bean与FactoryBeanFactoryBean能够生成某一个类型的Bean实例(返回给咱们) 后置解决 两种后置解决Bean的接口, BeanFactoryPostProcessor和BeanPostProcessor循环依赖 循环援用, 也就是两个或两个以上的Bean相互持有对方, 最终造成闭环.结构器依赖与属性依赖; 结构器依赖是无奈解决的, 只能抛出beanCurrentlyCreationException异样, 在解决这个循环依赖的问题时, spring采纳的是提前裸露对象的办法. 通过三级缓存的形式. 

June 24, 2021 · 1 min · jiezi

关于spring:设计模式学习03Java实现软件设计6大原则

写在后面 记录学习设计模式的笔记进步对设计模式的灵活运用学习地址 https://www.bilibili.com/vide... https://www.bilibili.com/vide... 参考文章 http://c.biancheng.net/view/1... 我的项目源码https://gitee.com/zhuang-kang/DesignPattern 3,软件设计准则在软件开发中,为了进步软件系统的可维护性和可复用性,减少软件的可扩展性和灵活性,程序员要尽量依据6条准则来开发程序,从而进步软件开发效率、节约软件开发老本和保护老本。 3.1 开闭准则对扩大凋谢,对批改敞开。在程序须要进行拓展的时候,不能去批改原有的代码,实现一个热插拔的成果。简言之,是为了使程序的扩展性好,易于保护和降级。 想要达到这样的成果,咱们须要应用接口和抽象类。 因为形象灵活性好,适应性广,只有形象的正当,能够根本放弃软件架构的稳固。而软件中易变的细节能够从抽象派生来的实现类来进行扩大,当软件须要发生变化时,只须要依据需要从新派生一个实现类来扩大就能够了。 3.2 里氏代换准则里氏代换准则是面向对象设计的根本准则之一。 里氏代换准则:任何基类能够呈现的中央,子类肯定能够呈现。艰深了解:子类能够扩大父类的性能,但不能扭转父类原有的性能。换句话说,子类继承父类时,除增加新的办法实现新增性能外,尽量不要重写父类的办法。 如果通过重写父类的办法来实现新的性能,这样写起来尽管简略,然而整个继承体系的可复用性会比拟差,特地是使用多态比拟频繁时,程序运行出错的概率会十分大。 里氏代换准则谬误示范 package com.zhuang.principle.liskov;/** * @Classname Liskov * @Description 里氏代换准则谬误示范 * @Date 2021/3/15 13:58 * @Created by dell */public class Liskov { public static void main(String[] args) { A a = new A(); System.out.println("11-3=" +a.fun1(11,3)); System.out.println("11-8=" +a.fun1(11,8)); System.out.println("==================="); B b = new B(); System.out.println("11-3="+b.fun1(11,3)); System.out.println("1-8="+b.fun1(1,8)); System.out.println("11+3+9="+b.fun2(11,3)); }}class A{ //返回两个数的差 public int fun1(int num1,int num2){ return num1-num2; }}//B类继承A 减少新性能,实现两个数相加,而后和9求和class B extends A{ @Override public int fun1(int a, int b) { return a+b; } public int fun2(int a, int b) { return fun1(a,b)+9; }}里氏代换准则正确示范 ...

June 21, 2021 · 4 min · jiezi

关于spring:Spring-AOP使用篇熟悉使用前置通知后置通知返回通知异常通知并了解其相关特性

前言本次将会总结5篇对于spring aop的知识点,次要围绕:AOP应用篇、AOP原理篇、事务应用篇、事务原理篇、事务同步器应用篇 五个主题进行论述。AOP原理篇分为两个主题: 1、源码中是如何将咱们定义的各种告诉与指标办法绑定起来的2、咱们的aop代理对象的执行程序是怎么的事务原理篇次要是以一个事务流传机制的案例来解说spring事务在底层的执行过程而本次总结的外围为:如何应用Spring AOP,以及理解相干告诉的个性(相干的概念在这就不再累述了,大家能够参考其余文章),废话不多说,咱们间接开始吧!一、测试案例1.1 预览测试案例我的项目构造我的项目启动入口类Entry.java: public class Entry { public static void main(String[] args) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class); ObjectService objectService = context.getBean(ObjectService.class); objectService.list("hi"); } // 配置扫描类,启动了aop性能 @Configuration @ComponentScan("com.eugene.sumarry.aop.csdn") @EnableAspectJAutoProxy(proxyTargetClass = false, exposeProxy = true) public static class AppConfig { } // 被这个注解标识的办法会被加强 @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface AspectAnnotation { }}接口类ObjectService.java public interface ObjectService { String[] list(String str); String findOne();}接口实现类ObjectServiceImpl.java @Servicepublic class ObjectServiceImpl implements ObjectService { @Entry.AspectAnnotation @Override public String[] list(String str) { return new String[] {str, "avenger", "eug"}; } @Override public String findOne() { return "avengerEug"; }}定义切面AspectDefinition.java ...

June 21, 2021 · 3 min · jiezi

关于spring:这篇文章让你搞懂-SpringMVC-国际化

松哥之前写过 Spring Boot 国际化的问题,不过那一次没讲源码,这次咱们整点源码来深刻了解下这个问题。 国际化,也叫 i18n,为啥叫这个名字呢?因为国际化英文是 internationalization ,在 i 和 n 之间有 18 个字母,所以叫 i18n。咱们的利用如果做了国际化就能够在不同的语言环境下,不便的进行切换,最常见的就是中文和英文之间的切换,国际化这个性能也是相当的常见。 1.SpringMVC 国际化配置还是先来说说用法,再来说源码,这样大家不容易犯迷糊。咱们先说在 SSM 中如何解决国际化问题。 首先国际化咱们可能有两种需要: 在页面渲染时实现国际化(这个借助于 Spring 标签实现)在接口中获取国际化匹配后的音讯大抵上就是下面这两种场景。接下来松哥通过一个简略的用法来和大家演示下具体玩法。 首先咱们在我的项目的 resources 目录下新建语言文件,language_en_US.properties 和 language_zh-CN.properties,如下图: 内容别离如下: language_en_US.properties: login.username=Usernamelogin.password=Passwordlanguage_zh-CN.properties: login.username=用户名login.password=用户明码这两个别离对应英中文环境。配置文件写好之后,还须要在 SpringMVC 容器中提供一个 ResourceBundleMessageSource 实例去加载这两个实例,如下: <bean class="org.springframework.context.support.ResourceBundleMessageSource" id="messageSource"> <property name="basename" value="language"/> <property name="defaultEncoding" value="UTF-8"/></bean>这里配置了文件名 language 和默认的编码格局。 接下来咱们新建一个 login.jsp 文件,如下: <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %><%@ page contentType="text/html;charset=UTF-8" language="java" %><html><head> <title>Title</title></head><body><spring:message code="login.username"/> <input type="text"> <br><spring:message code="login.password"/> <input type="text"> <br></body></html>在这个文件中,咱们通过 spring:message 标签来援用变量,该标签会依据以后的理论状况,抉择适合的语言文件。 接下来咱们为 login.jsp 提供一个控制器: @Controllerpublic class LoginController { @Autowired MessageSource messageSource; @GetMapping("/login") public String login() { String username = messageSource.getMessage("login.username", null, LocaleContextHolder.getLocale()); String password = messageSource.getMessage("login.password", null, LocaleContextHolder.getLocale()); System.out.println("username = " + username); System.out.println("password = " + password); return "login"; }}控制器中间接返回 login 视图即可。 ...

June 21, 2021 · 5 min · jiezi

关于spring:什么年代了你还不知道-Servlet30-中的文件上传方式

其实文件上传这块松哥之前和大家聊过很屡次了,这次因为最近正在进行 SpringMVC 的源码剖析,所以又再次把这个话题拉进去“鞭尸”,不过这次松哥想从源码角度来聊聊这个话题。 了解源码的前提是先会用,所以咱们还是先来看看用法,而后再来剖析源码。 1.两种文件解析计划对于上传文件的申请,SpringMVC 中目前共有两种不同的解析计划: StandardServletMultipartResolverCommonsMultipartResolverStandardServletMultipartResolver 反对 Servlet3.0 中规范的文件上传计划,应用非常简单;CommonsMultipartResolver 则须要联合 Apache Commons fileupload 组件一起应用,这种形式兼容低版本的 Servlet。 StandardServletMultipartResolver 先来回顾下 StandardServletMultipartResolver 的用法。 应用 StandardServletMultipartResolver,能够间接通过 HttpServletRequest 自带的 getPart 办法获取上传文件并保留,这是一种规范的操作形式,这种形式也不必增加任何额定的依赖,只须要确保 Servlet 的版本在 3.0 之上即可。 首先咱们须要为 Servlet 配置 multipart-config,哪个 Servlet 负责解决上传文件,就为哪个 Servlet 配置 multipart-config。在 SpringMVC 中,咱们的申请都是通过 DispatcherServlet 进行散发的,所以咱们就为 DispatcherServlet 配置 multipart-config。 配置形式如下: <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</serv <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring-servlet.xml</param-value> </init-param> <multipart-config> <location>/tmp</location> <max-file-size>1024</max-file-size> <max-request-size>10240</max-request-size> </multipart-config></servlet><servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>而后在 SpringMVC 的配置文件中提供一个 StandardServletMultipartResolver 实例,留神该实例的 id 必须为 multipartResolver(具体起因参见:SpringMVC 初始化流程剖析一文)。 ...

June 18, 2021 · 6 min · jiezi

关于spring:使用java的方式配置Spring

咱们当初要齐全不应用Spring的xml配置了,全权交给Java来做!JavaConfig是Spring的一个子项目,在Spring4之后,他成为了一个外围性能。实体类 package com.jialidun.pojo;//@Component注解,阐明这个类被Spring接管了,注册到了容器中@Componentpublic class User(){ private String name; @Value("RYGAR")//属性注入值 public void setName(String name){ this.name=name; } public String getName(){ return name; } @Override public String toString(){ return "User{" + "name='" + name + '\'' + '}' ; }}配置类 package com.jialidun.fonfig;//这个@Configuration注解也会Spring容器托管,注册到容器中。因为它原本就是一个@Component//@Configuration代表这是一个配置类,就和咱们之前看的beans.xml@Configuration @ComponentScan("com.jialidun.pojo")@Import(RygarConfig2.class)public class RygarConfig{ //注册一个bean,就相当于咱们之前写的一个bean标签 //这个办法的名字就相当于bean标签中的id属性 //这个办法的返回值就相当于bean标签中的class属性 @Bean public User getUser(){ return new User();//返回要注入到bean的对象 }}配置类2 package com.jialidun.config;@Configurationpublic class RygarConfig2{}测试类 public class TestDemo{ public static void main(String[] args){ //如果齐全应用了配置类形式去做,咱们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载! ApplicationContext context = new AnnotationConfigApplicationContext(RygarConfig.class); User user = context.getBean("getUser"); }}这种纯java的配置形式,在SpringBoot中随处可见! ...

June 17, 2021 · 1 min · jiezi

关于spring:SpringBoot基础系列之手把手实现国际化支持实例开发

【SpringBoot根底系列】手把手实现国际化反对实例开发国际化的反对,对于app开发的小伙伴来说应该比价常见了;作为java后端的小伙伴,一般来讲接触国际化的机会不太多,毕竟业务发展到海内的企业并没有太多 SpringBoot提供了国际化的反对,网上也有相干的教程,然而理论体验的时候,发现并没有预期的那么顺利;本文将介绍一下SpringBoot如何反对国家化,以及在反对的过程中,一些注意事项 <!-- more --> I. 我的项目环境1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 开一个web服务用于测试 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency></dependencies>2. 配置文件配置文件中,指定国际化的参数,thmeleaf的配置信息 application.yml spring: messages: basename: i18n/messages/messages encoding: UTF-8 fallbackToSystemLocale: false thymeleaf: mode: HTML encoding: UTF-8 servlet: content-type: text/html cache: false3. 国际化信息文件下面的配置 spring.messages.basename 指定国际化配置文件的目录与前缀,取值为i18n/messages/messages 所以在资源目录下,新建文件 i18n/messages,国际化文件名为 messages-xxx.properties,我的项目后果如 对应的信息如简体中文 messages_zh_CN.properties 200=胜利500=內部异样name=用户名pwd=明码英文 messages_en_US.properties 200=success500=unexpected exceptionname=user namepwd=password繁体 messages_zh_TW.properties 200=胜利500=內部異常name=用戶名pwd=密碼阐明 留神spring.messages.basename 这个配置的取值为国际化文件的目录 + 文件名前缀,比方下面若少了最初一层的messages,会提醒取不到配置 其次在IDEA中,选中国家化文件之后,点击下方的Resource Bundle,能够进入如上图中更敌对的编辑框,反对一次批改多个语言的信息 II. 国际化反对后面是国际化的根本配置,那么如何依据后面配置中的key,获取不同语言的value呢? ...

June 16, 2021 · 3 min · jiezi

关于spring:Spring配置类替代xml配置文件

@Configuration //示意该类是配置类@ComponentScan(backPackages={"com.zong.spring"}) //示意注解扫描哪个文件public class SpringConfig{}//加载配置文件 ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);

June 15, 2021 · 1 min · jiezi

关于spring:Spring基于注解方式实现属性注入

有4种形式1)@Autowired:依据属性类型进行注入2)@Qualifier:依据属性名称进行注入,须要和@Autowired一起应用3)@Resource:能够依据属性类型注入,也能够依据名称进行注入,在javax包中,而不是spring包中,官网举荐1)2)两种形式注入4)@Value:注入一般属性

June 15, 2021 · 1 min · jiezi

关于spring:小伙伴们在催更Spring系列于是我写下了这篇注解汇总

写在后面因为在更新其余专题的文章,Spring系列文章有很长一段时间没有更新了,很多小伙伴都在公众号后盾留言或者间接私信我微信催更Spring系列文章。 看来是要持续更新Spring文章了。想来想去,写一篇对于Spring中注解相干的文章吧,因为之前更新Spring系列的文章始终也是在更新Spring注解驱动开发。这篇文章也算是对之前文章的一个小小的总结吧,预计更新完这篇,咱们会进入Spring的AOP章节的更新。 没有看过Spring其余文章的小伙伴,能够到【冰河技术】公号的【Spring系列】专题中进行浏览。 文章已收录到: https://github.com/sunshinelyz/technology-binghe https://gitee.com/binghe001/technology-binghe xml配置与类配置1.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"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/sp <bean id="person" class="com.binghe.spring.Person"></bean></beans>获取Person实例如下所示。 public static void main( String[] args ){ ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml"); System.out.println(ctx.getBean("person"));}2.类配置@Configurationpublic class MainConfig { @Bean public Person person(){ return new Person(); }} 这里,有一个须要留神的中央:通过@Bean的模式是应用的话, bean的默认名称是办法名,若@Bean(value="bean的名称")那么bean的名称是指定的 。 获取Person实例如下所示。 public static void main( String[] args ){ AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(MainConfig.class); System.out.println(ctx.getBean("person"));}@CompentScan注解咱们能够应用@CompentScan注解来进行包扫描,如下所示。 @Configuration@ComponentScan(basePackages = {"com.binghe.spring"}) public class MainConfig {} excludeFilters 属性当咱们应用@CompentScan注解进行扫描时,能够应用@CompentScan注解的excludeFilters 属性来排除某些类,如下所示。 @Configuration@ComponentScan(basePackages = {"com.binghe.spring"},excludeFilters = {@ComponentScan.Filter(type = FilterType.ANNOTATION,value = {Controller.class}),@ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE,value = {PersonService.class})})public class MainConfig {}includeFilters属性当咱们应用@CompentScan注解进行扫描时,能够应用@CompentScan注解的includeFilters属性将某些类蕴含进来。这里须要留神的是:须要把useDefaultFilters属性设置为false(true示意扫描全副的) ...

June 15, 2021 · 4 min · jiezi

关于spring:Spring

1、Spring1.1、简介Spring框架是一个凋谢源代码的J2EE应用程序框架,由Rod Johnson发动,是针对bean的生命周期进行治理的轻量级容器(lightweight container)。 Spring解决了开发者在J2EE开发中遇到的许多常见的问题,提供了功能强大IOC、AOP及Web MVC等性能。Spring能够独自利用于构筑应用程序,也能够和Struts、Webwork、Tapestry等泛滥Web框架组合应用,并且能够与 Swing等桌面应用程序AP组合。因而, Spring不仅仅能利用于J2EE应用程序之中,也能够利用于桌面应用程序以及小应用程序之中。Spring框架次要由七局部组成,别离是 Spring Core、 Spring AOP、 Spring ORM、 Spring DAO、Spring Context、 Spring Web和 Spring Web MVC。 Spring:春天--->给软件行业带来了春天!2002年,首次推出了Spring框架的雏形:Interface21框架!2004年3月24日,Spring框架以interface21框架为根底,通过从新设计,公布了1.0正式版很难设想Rod Johnson的学历,他是悉尼大学的博士,然而他学习的业余不是计算机而是音乐学Spring理念:使现有技术更加实用,自身就是一个大杂烩,整合现有的框架技术SSH:Struct2 + Spring +HibernateSSM:SpringMVC + Spring + Mybatis官网:https://spring.io/projects/spring-framework#overview官网下载地址GitHub:https://github.com/spring-projects/spring-framework <!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.8</version></dependency><!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.3.8</version></dependency>1.2、Spring长处Spring是一个开源的收费的框架(容器)!Spring是一个轻量级的、非入侵式的框架!管制反转(IOC),面向切面编程(AOP)反对事务的解决,对框架整合的反对!==总结:Spring就是一个轻量级的管制反转(IOC)和面向切面编程(AOP)的框架!== 1.3、组成 1.4、拓展在Spring的官网有这个介绍:现代化的Java开发,就是基于Spring的开发! Spring Boot一个疾速开发的脚手架基于SpringBoot能够疾速的开发单个微服务约定大于配置Spring CloudSpringCloud是基于SpringBoot实现的学习SpringBoot的前提,须要齐全把握Spring及SpringMVC!承前启后的作用!

June 15, 2021 · 1 min · jiezi

关于spring:Spring中的工厂Bean

Spring中有两种bean类型,一般bean和工厂bean一般bean:定义的类型和返回的类型统一工厂bean:定义的类型和返回的类型能够不统一工厂bean的实现:实现FactoryBean接口中的getObject办法,如 public class MyBean implements FactoryBean<Course>{ public Course getBean(){ Course course = new Course(); course.setName("语文"); return course; }}xml创建对象 <bean id="myBean" class="com.zong.spring.MyBean"></bean>测试 public void test(){ ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml"); Course course = context.getBean("myBean",Course.class); System.out.println(course);}

June 13, 2021 · 1 min · jiezi

关于spring:Springbooot的多数据源配置

JdbcTemplate依赖配置依赖如下 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency></dependencies>增加多数据源的配置先在Spring Boot的配置文件application.properties中设置两个你要链接的数据库配置,比方这样: spring.datasource.primary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test1spring.datasource.primary.username=testspring.datasource.primary.password=123456spring.datasource.primary.driver-class-name=com.mysql.cj.jdbc.Driver# 次数据源spring.datasource.secondary.jdbc-url=jdbc:mysql://192.168.56.101:3306/test2spring.datasource.secondary.username=testspring.datasource.secondary.password=123456spring.datasource.secondary.driver-class-name=com.mysql.cj.jdbc.Driver阐明与留神: 多数据源配置的时候,与单数据源不同点在于spring.datasource之后多设置一个数据源名称primary和secondary来辨别不同的数据源配置,这个前缀将在后续初始化数据源的时候用到。数据源连贯配置2.x和1.x的配置项是有区别的: 2.x应用spring.datasource.secondary.jdbc-url而1.x版本应用spring.datasource.secondary.url。如果你在配置的时候产生了这个报错java.lang.IllegalArgumentException: jdbcUrl is required with driverClassName.,那么就是这个配置项的问题。初始化数据源和JdbcTemplate实现多数据源的配置信息之后,就来创立个配置类来加载这些配置信息,初始化数据源,以及初始化每个数据源要用的JdbcTemplate。你只须要在你的Spring Boot利用下增加上面的这个配置类即可实现! package tk.fulsun.demo.config;import javax.sql.DataSource;import org.springframework.beans.factory.annotation.Qualifier;import org.springframework.boot.context.properties.ConfigurationProperties;import org.springframework.boot.jdbc.DataSourceBuilder;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.jdbc.core.JdbcTemplate;/** * @author fsun7 * @description: 数据源的配置信息 * @date 6/11/2021 3:20 PM */@Configurationpublic class DataSourceConfiguration { @Bean @ConfigurationProperties(prefix = "spring.datasource.primary") public DataSource primaryDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.secondary") public DataSource secondaryDataSource() { return DataSourceBuilder.create().build(); } @Bean public JdbcTemplate primaryJdbcTemplate( @Qualifier("primaryDataSource") DataSource primaryDataSource) { return new JdbcTemplate(primaryDataSource); } @Bean public JdbcTemplate secondaryJdbcTemplate( @Qualifier("secondaryDataSource") DataSource secondaryDataSource) { return new JdbcTemplate(secondaryDataSource); }}阐明与留神: ...

June 12, 2021 · 6 min · jiezi

关于spring:Configuration中的来声明BeanPostProcessor和BeanFactoryPostProcessor

最近在遇到比拟一个问题,通常咱们在spring中应用@Configuration来注解一个类时,能够同时用Autowire 和@Value 给该配置类主动注入属性值,如下: @Configurationpublic class ConfigA { @Autowired private Environment environment; @Value("${name}") private string name; @Bean public A a() { A a = new A(); a.name = name; return a; }}咱们失常状况下都是能主动注入environment和name的,然而当在configration 中如果申明了一个BeanPostProcessor时,如下: @Bean public MyBeanPostProcessor myBeanPostProcessor(A a) { log.info("env=:{}", environment); MyBeanPostProcessor myBeanPostProcessor = new MyBeanPostProcessor(); myBeanPostProcessor.setA(a); return myBeanPostProcessor; }你会发现打进去的log env 为null,同时A的name 也为null,这个配置类的 @Autowired和@Value都没有失效。 翻阅spring 的官网,其中有一段小tip,如下 Make sure that the dependencies you inject that way are of the simplest kind only. @Configuration classes are processed quite early during the initialization of the context, and forcing a dependency to be injected this way may lead to unexpected early initialization. Whenever possible, resort to parameter-based injection, as in the preceding example.Also, be particularly careful with BeanPostProcessor and BeanFactoryPostProcessor definitions through @Bean. Those should usually be declared as static @Bean methods, not triggering the instantiation of their containing configuration class. Otherwise, @Autowired and @Value may not work on the configuration class itself, since it is possible to create it as a bean instance earlier than AutowiredAnnotationBeanPostProcessor.意思就是BeanPostProcessor 和BeanFactoryPostProcessor 会在configuration 自身这个bean初始化之前,能够在spring onrefresh 办法中找到这段代码 ...

June 12, 2021 · 1 min · jiezi

关于spring:SpringBoot基础系列之自定义配置源使用姿势实例演示

【SpringBoot根底系列】自定义配置源的应用姿态介绍后面一篇博文介绍了一个@Value的一些知识点,其中提了一个点,@Value对应的配置,除了是配置文件中之外,能够从其余的数据源中获取么,如从 redis,db,http 中获取配置? 理解过 SpringCloud Config 的能够给出确切的答案,能够,而且用起来还老爽了,近程配置,反对配置动静刷新,接下来咱们来看一下,在 SpringBoot 中,如何配置自定义的数据源 <!-- more --> I. 我的项目环境1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 开一个 web 服务用于测试 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies>II. 自定义配置源@Value润饰的成员,绑定配置时,是从Envrionment中读取配置的,所以咱们须要做的就是注册一个自定义的配置源,借助MapPropertySource能够来实现咱们需要场景 1. 自定义数据源演示一个最简略自定义的配置数据源,重写MapPropertySource的getProperties办法 实现如下 public class SimplePropertiesSource extends MapPropertySource { public SimplePropertiesSource(String name, Map<String, Object> source) { super(name, source); } public SimplePropertiesSource() { this("filePropertiesSource", new HashMap<>()); } /** * 笼罩这个办法,实用于实时获取配置 * * @param name * @return */ @Override public Object getProperty(String name) { // 留神,只针对自定义结尾的配置才执行这个逻辑 if (name.startsWith("selfdefine.")) { return name + "_" + UUID.randomUUID(); } return super.getProperty(name); }}2. 数据源注册下面只是申明了配置源,接下来把它注册到 Environment 中,这样就能够供给用应用了 ...

June 12, 2021 · 2 min · jiezi

关于spring:SpringBoot基础系列之AOP结合SpEL实现日志输出中两点注意事项

【SpringBoot 根底系列】AOP联合SpEL实现日志输入的注意事项一二应用 AOP 来打印日志大家一把都很相熟了,最近在应用的过程中,发现了几个有意思的问题,一个是 SpEL 的解析,一个是参数的 JSON 格局输入 <!-- more --> I. 我的项目环境1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 开一个 web 服务用于测试 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency></dependencies>II. AOP & SpEL对于 AOP 与 SpEL 的知识点,之前都有过专门的介绍,这里做一个聚合,一个非常简单的日志输入切面,在须要打印日志的办法上,增加注解@Log,这个注解中定义一个key,作为日志输入的标记;key 反对 SpEL 表达式 1. AOP 切面注解定义 @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface Log { String key();}切面逻辑 @Slf4j@Aspect@Componentpublic class AopAspect implements ApplicationContextAware { private ExpressionParser parser = new SpelExpressionParser(); private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer(); @Around("@annotation(logAno)") public Object around(ProceedingJoinPoint joinPoint, Log logAno) throws Throwable { long start = System.currentTimeMillis(); String key = loadKey(logAno.key(), joinPoint); try { return joinPoint.proceed(); } finally { log.info("key: {}, args: {}, cost: {}", key, JSONObject.toJSONString(joinPoint.getArgs()), System.currentTimeMillis() - start); } } private String loadKey(String key, ProceedingJoinPoint joinPoint) { if (key == null) { return key; } StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new BeanFactoryResolver(applicationContext)); String[] params = parameterNameDiscoverer.getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod()); Object[] args = joinPoint.getArgs(); for (int i = 0; i < args.length; i++) { context.setVariable(params[i], args[i]); } return parser.parseExpression(key).getValue(context, String.class); } private ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; }}下面这个逻辑比较简单,和大家熟知的应用姿态没有太大的区别 ...

June 12, 2021 · 2 min · jiezi

关于spring:SpringBoot基础篇之Value中哪些你不知道的知识点

SpringBoot根底篇@Value中哪些你不晓得的知识点看到这个题目,有点夸大了啊,@Value 这个谁不晓得啊,不就是绑定配置么,还能有什么非凡的玩法不成? (如果上面列出的这些问题,曾经熟练掌握,那的确没啥往下面看的必要了) @Value对应的配置不存在,会怎么?默认值如何设置配置文件中的列表能够间接映射到列表属性上么?配置参数映射为简略对象的三种配置形式除了配置注入,字面量、SpEL反对是否理解?近程(如db,配置核心,http)配置注入可行否?<!-- more --> 接下来,限于篇幅问题,将针对下面提出的问题的后面几条进行阐明,最初两个放在下篇 I. 我的项目环境先创立一个用于测试的SpringBoot我的项目,源码在最初贴出,情谊提醒源码浏览更敌对 1. 我的项目依赖本我的项目借助SpringBoot 2.2.1.RELEASE + maven 3.5.3 + IDEA进行开发 2. 配置文件在配置文件中,加一些用于测试的配置信息 application.yml auth: jwt: token: TOKEN.123 expire: 1622616886456 whiteList: 4,5,6 blackList: - 100 - 200 - 300 tt: token:tt_token; expire:1622616888888II. 应用case1. 根本姿态通过${}来引入配置参数,当然前提是所在的类被Spring托管,也就是咱们常说的bean 如下,一个常见的应用姿态 @Componentpublic class ConfigProperties { @Value("${auth.jwt.token}") private String token; @Value("${auth.jwt.expire}") private Long expire;}2. 配置不存在,抛异样接下来,引入一个配置不存在的注入,在我的项目启动的时候,会发现抛出异样,导致无奈失常启动 /** * 不存在,应用默认值 */@Value("${auth.jwt.no")private String no;抛出的异样属于BeanCreationException, 对应的异样提醒 Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'auth.jwt.no' in value "${auth.jwt.no}" ...

June 12, 2021 · 3 min · jiezi

关于spring:Spring-Security-密码验证动态加盐的验证处理

本文集体博客地址:https://www.leafage.top/posts/detail/21697I2R 最近几天在革新我的项目,须要将gateway整合security在一起进行认证和鉴权,之前gateway和auth是两个服务,auth是shiro写的一个,一个filter和一个配置,内容很简略,生成token,验证token,没有其余的安全检查,而后让对我的项目进行重构。 先是要整合gateway和shiro,然而因为gateway是webflux,而shiro-spring是webmvc,所以没搞胜利,如果有做过并胜利的,请通知我如何进行整合,非常感谢。 那整合security呢,因为spring cloud gateway基于webflux,所以网上很多教程是用不了的,webflux的配置会有一些变动,具体看如下代码示例: import io.leafage.gateway.api.HypervisorApi;import io.leafage.gateway.handler.ServerFailureHandler;import io.leafage.gateway.handler.ServerSuccessHandler;import io.leafage.gateway.service.JdbcReactiveUserDetailsService;import org.springframework.context.annotation.Bean;import org.springframework.http.HttpMethod;import org.springframework.http.HttpStatus;import org.springframework.security.config.annotation.web.reactive.EnableWebFluxSecurity;import org.springframework.security.config.web.server.ServerHttpSecurity;import org.springframework.security.core.userdetails.ReactiveUserDetailsService;import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;import org.springframework.security.crypto.password.PasswordEncoder;import org.springframework.security.web.server.SecurityWebFilterChain;import org.springframework.security.web.server.authentication.HttpStatusServerEntryPoint;import org.springframework.security.web.server.authentication.ServerAuthenticationFailureHandler;import org.springframework.security.web.server.authentication.ServerAuthenticationSuccessHandler;import org.springframework.security.web.server.authentication.logout.HttpStatusReturningServerLogoutSuccessHandler;import org.springframework.security.web.server.csrf.CookieServerCsrfTokenRepository;/** * spring security config . * * @author liwenqiang 2019/7/12 17:51 */@EnableWebFluxSecuritypublic class ServerSecurityConfiguration { // 用于获取近程数据 private final HypervisorApi hypervisorApi; public ServerSecurityConfiguration(HypervisorApi hypervisorApi) { this.hypervisorApi = hypervisorApi; } /** * 明码配置,应用BCryptPasswordEncoder * * @return BCryptPasswordEncoder 加密形式 */ @Bean protected PasswordEncoder passwordEncoder() { return new BCryptPasswordEncoder(); } /** * 用户数据加载 * * @return JdbcReactiveUserDetailsService 接口 */ @Bean public ReactiveUserDetailsService userDetailsService() { // 自定义的ReactiveUserDetails 实现 return new JdbcReactiveUserDetailsService(hypervisorApi); } /** * 平安配置 */ @Bean SecurityWebFilterChain springSecurityFilterChain(ServerHttpSecurity http) { http.formLogin(f -> f.authenticationSuccessHandler(authenticationSuccessHandler()) .authenticationFailureHandler(authenticationFailureHandler())) .logout(l -> l.logoutSuccessHandler(new HttpStatusReturningServerLogoutSuccessHandler())) .csrf(c -> c.csrfTokenRepository(CookieServerCsrfTokenRepository.withHttpOnlyFalse())) .authorizeExchange(a -> a.pathMatchers(HttpMethod.OPTIONS).permitAll() .anyExchange().authenticated()) .exceptionHandling(e -> e.authenticationEntryPoint(new HttpStatusServerEntryPoint(HttpStatus.UNAUTHORIZED))); return http.build(); } /** * 登陆胜利后执行的处理器 */ private ServerAuthenticationSuccessHandler authenticationSuccessHandler() { return new ServerSuccessHandler(); } /** * 登陆失败后执行的处理器 */ private ServerAuthenticationFailureHandler authenticationFailureHandler() { return new ServerFailureHandler(); }}下面的示例代码,是我开源我的项目中的一段,个别的配置就如下面写的,就能够应用了,然而因为咱们之前的我的项目中的是shiro,而后有一个自定义的加密解密的逻辑。 ...

June 9, 2021 · 2 min · jiezi

关于spring:SpringBoot-基础系列接口上注解-AOP-拦截不到场景兼容实例演示

【SpringBoot 根底系列】接口上注解 AOP 拦挡不到场景兼容在 Java 的开发过程中,面向接口的编程可能是大家的常态,切面也是各位大佬应用 Spring 时,或多或少会应用的一项基本技能;后果这两个碰到一起,有意思的事件就产生了,接口办法上增加注解,面向注解的切面拦挡,竟然不失效 这就有点奇怪了啊,最开始遇到这个问题时,示意难以相信;事务注解也挺多是写在接口上的,如同也没有遇到这个问题(难道是也不失效,只是本人没有关注到?) 接下来咱们好好瞅瞅,这到底是怎么个状况 <!-- more --> I. 场景复现这个场景复现相对而言比较简单了,一个接口,一个实现类;一个注解,一个切面完事 1. 我的项目环境采纳SpringBoot 2.2.1.RELEASE + IDEA + maven 进行开发 增加 aop 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId></dependency>2. 复现 case申明一个注解 @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface AnoDot {}拦挡切面,上面这段代码来自之前分享的博文 【根底系列】AOP 实现一个日志插件(利用篇) @Aspect@Componentpublic class LogAspect { private static final String SPLIT_SYMBOL = "|"; @Pointcut("execution(public * com.git.hui.boot.aop.demo.*.*(..)) || @annotation(AnoDot)") public void pointcut() { } @Around(value = "pointcut()") public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { Object res = null; String req = null; long start = System.currentTimeMillis(); try { req = buildReqLog(proceedingJoinPoint); res = proceedingJoinPoint.proceed(); return res; } catch (Throwable e) { res = "Un-Expect-Error"; throw e; } finally { long end = System.currentTimeMillis(); System.out.println(req + "" + JSON.toJSONString(res) + SPLIT_SYMBOL + (end - start)); } } private String buildReqLog(ProceedingJoinPoint joinPoint) { // 指标对象 Object target = joinPoint.getTarget(); // 执行的办法 Method method = ((MethodSignature) joinPoint.getSignature()).getMethod(); // 申请参数 Object[] args = joinPoint.getArgs(); StringBuilder builder = new StringBuilder(target.getClass().getName()); builder.append(SPLIT_SYMBOL).append(method.getName()).append(SPLIT_SYMBOL); for (Object arg : args) { builder.append(JSON.toJSONString(arg)).append(","); } return builder.substring(0, builder.length() - 1) + SPLIT_SYMBOL; }}而后定义一个接口与实现类,留神上面的两个办法,一个注解在接口上,一个注解在实现类上 ...

June 8, 2021 · 3 min · jiezi

关于spring:SpringMVCRequestMappingHandlerAdapter

前言在SpringMVC-RequestMappingHandlerMapping中对web申请到来时如何获取解决该申请的handler进行了剖析,那么获取到web申请对应的handler之后,SpringMVC框架还会依据handler获取其对应的HandlerAdapter,handler的理论执行就是由HandlerAdapter实现,并且HandlerAdapter一共会做三件事件:入参解析,执行解决逻辑,返回值解决。艰深的说就是执行办法前要把办法须要的参数筹备好,而后执行办法,最初办法执行后要对办法返回值进行解决。如果handler是由RequestMappingHandlerMapping取得,那么执行该handler的HandlerAdapter应该为RequestMappingHandlerAdapter,该篇文章将对RequestMappingHandlerAdapter的入参解析,执行解决逻辑,返回值解决三局部进行学习。 SpringBoot版本:2.4.1 注释一. RequestMappingHandlerAdapter初始化首先通过类图认识一下RequestMappingHandlerAdapter。 由类图可知RequestMappingHandlerAdapter实现了InitializingBean接口,因而在容器加载RequestMappingHandlerAdapter时会执行其实现的afterPropertiesSet()办法。该办法如下所示。 public void afterPropertiesSet() { initControllerAdviceCache(); if (this.argumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultArgumentResolvers(); this.argumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.initBinderArgumentResolvers == null) { List<HandlerMethodArgumentResolver> resolvers = getDefaultInitBinderArgumentResolvers(); this.initBinderArgumentResolvers = new HandlerMethodArgumentResolverComposite().addResolvers(resolvers); } if (this.returnValueHandlers == null) { List<HandlerMethodReturnValueHandler> handlers = getDefaultReturnValueHandlers(); this.returnValueHandlers = new HandlerMethodReturnValueHandlerComposite().addHandlers(handlers); }}afterPropertiesSet()办法顺次为RequestMappingHandlerAdapter加载ControllerAdviceBean相干内容,加载参数解析器,加载返回后果处理器。其中参数解析器和返回后果处理器默认状况下会将SpringMVC框架提供的和用户自定义的一并进行加载,而加载ControllerAdviceBean相干内容理论就是先获取容器中所有由@ControllerAdvice注解润饰的bean,而后将这些bean的由@ModelAttribute注解和@InitBinder注解润饰的办法加载到RequestMappingHandlerAdapter中,最初再判断由@ControllerAdvice注解润饰的bean是否实现了RequestBodyAdvice或ResponseBodyAdvice接口,如果是,则将该bean也加载到RequestMappingHandlerAdapter中。initControllerAdviceCache()办法如下所示。 由@ControllerAdvice注解润饰的bean是性能增强型Controller,通常搭配@ModelAttribute注解和@InitBinder注解应用,能够实现:全局数据绑定;全局数据预处理;全局异样解决。private void initControllerAdviceCache() { if (getApplicationContext() == null) { return; } //将容器中所有带@ControllerAdvice注解的bean获取进去并包装成ControllerAdviceBean对象 List<ControllerAdviceBean> adviceBeans = ControllerAdviceBean.findAnnotatedBeans(getApplicationContext()); List<Object> requestResponseBodyAdviceBeans = new ArrayList<>(); //遍历ControllerAdviceBean对象汇合 for (ControllerAdviceBean adviceBean : adviceBeans) { Class<?> beanType = adviceBean.getBeanType(); if (beanType == null) { throw new IllegalStateException("Unresolvable type for ControllerAdviceBean: " + adviceBean); } //获取ControllerAdviceBean对象(包含父对象)和其实现的接口的不禁@RequestMapping注解润饰且由@ModelAttribute注解润饰的办法汇合 Set<Method> attrMethods = MethodIntrospector.selectMethods(beanType, MODEL_ATTRIBUTE_METHODS); if (!attrMethods.isEmpty()) { this.modelAttributeAdviceCache.put(adviceBean, attrMethods); } //获取ControllerAdviceBean对象(包含父对象)和其实现的接口的由@InitBinder注解润饰的办法汇合 Set<Method> binderMethods = MethodIntrospector.selectMethods(beanType, INIT_BINDER_METHODS); if (!binderMethods.isEmpty()) { this.initBinderAdviceCache.put(adviceBean, binderMethods); } //判断ControllerAdviceBean对象是否实现了RequestBodyAdvice或ResponseBodyAdvice接口 if (RequestBodyAdvice.class.isAssignableFrom(beanType) || ResponseBodyAdvice.class.isAssignableFrom(beanType)) { requestResponseBodyAdviceBeans.add(adviceBean); } } if (!requestResponseBodyAdviceBeans.isEmpty()) { this.requestResponseBodyAdvice.addAll(0, requestResponseBodyAdviceBeans); } if (logger.isDebugEnabled()) { int modelSize = this.modelAttributeAdviceCache.size(); int binderSize = this.initBinderAdviceCache.size(); int reqCount = getBodyAdviceCount(RequestBodyAdvice.class); int resCount = getBodyAdviceCount(ResponseBodyAdvice.class); if (modelSize == 0 && binderSize == 0 && reqCount == 0 && resCount == 0) { logger.debug("ControllerAdvice beans: none"); } else { logger.debug("ControllerAdvice beans: " + modelSize + " @ModelAttribute, " + binderSize + " @InitBinder, " + reqCount + " RequestBodyAdvice, " + resCount + " ResponseBodyAdvice"); } }}大节:RequestMappingHandlerAdapter初始化时会将解析参数,参数预处理,执行后果解决等步骤须要用到的bean进行加载。 ...

June 8, 2021 · 12 min · jiezi

关于spring:Spring是什么

Spring是轻量级的开源的JavaEE框架,目标在于解决企业应用开发的复杂性。 外围的两个局部:1)IOC:管制反转,把创建对象过程交给Spring进行治理2)AOP:面向切面,不批改源代码进行性能加强 外围的4个JAR包:bean、context、core、expression

June 7, 2021 · 1 min · jiezi

关于spring:Spring-MVC日期序列化问题

Spring MVC日期序列化1. 问题Spring MVC默认会将LocalData,LocalDataTime,LocalTime序列化为ArrayList,将工夫信息拆分后放到数组中,可能会呈现反序列化谬误 2. 示例示例Spring MVC将LocalData,LocalDataTime,LocalTime序列化为什么内容序列化对象 @Data@NoArgsConstructor@AllArgsConstructor class UserDto { private String userName; private LocalDateTime birthday; }处理器,用于解决申请的 申请会传入UserDTO对象,处理器返回UserDTO对象序列化后的Json字符串,通过它能够看到UserDTO中的类型为LocalDateTime的birthday字段会序列化为什么内容@RestControllerclass HelloController { @PostMapping("/user") public UserDto user(@RequestBody UserDto userDto) throws Exception { return userDto; }}发送申请 响应内容 从响应内容能够看出,birthday被序列化为一个数组,由此能够证实Spring MVC默认将LocalDateTime序列化为数组 3. 解决方案应用@JsonFormat注解 阐明 只须要在LocalDateTime字段上加上@JsonFormat注解,申明工夫的字符串格局即可 示例 须要留神的时,申请时发送的字符串格局须要跟pattern申明的字符串格局保持一致@Data@NoArgsConstructor@AllArgsConstructorclass UserDto { private String userName; @JsonFormat(shape = JsonFormat.Shape.STRING, pattern="yyyy-MM-dd HH:mm:ss") private LocalDateTime birthday; }后果 此时Spring MVC序列化LocalDateTime类型的形式的确产生了变动 定制序列化形式 阐明 jackson也为此提供了一整套的序列化计划,咱们只须要在pom.xml中引入jackson-datatype-jsr310依赖,而后在利用主类中减少这个序列化模块,并禁用对日期以工夫戳形式输入的个性即可 示例 maven引入依赖 <dependency> <groupId>com.fasterxml.jackson.datatype</groupId> <artifactId>jackson-datatype-jsr310</artifactId></dependency - 增加序列化模块 ```java @Bean public ObjectMapper serializingObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); //禁止将Date序列化为工夫戳 objectMapper.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS); objectMapper.registerModule(new JavaTimeModule());//注册模块 return objectMapper; } ```后果展现 ...

June 5, 2021 · 1 min · jiezi

关于spring:springcloudalibabanacos的自动注册流程

有趣味的能够下载源码调试 spring-cloud-alibaba源码地址 版本2021.1本篇文章次要目标是阐明流程,所以除了办法名是源码以外,其余代码有简写或重写,可看作伪代码,只为了表白逻辑和展现流程。 一个代码块中调用办法的内容根本能够本代码块或上面一个代码块中找到。 服务注册nacos服务器就是一个web我的项目,对nacos的所有操作都就是发送一个http申请,所以只须要找到spring-cloud-alibaba-nacos在什么中央发送了这个申请。 spring boot启动时,会调用spring的refresh办法实现spring的启动,在启动的最初,调用finishRefresh办法。这个办法的最终目标是调用spring容器中所有SmartLifecycle实现类的start办法 @Overridepublic void refresh() throws BeansException, IllegalStateException { //... finishRefresh(); //...}protected void finishRefresh() { //... //初始化生命周期处理器,默认为DefaultLifecycleProcessor initLifecycleProcessor(); //调用生命周期处理器的onRefresh getLifecycleProcessor().onRefresh(); //...}public class DefaultLifecycleProcessor{ @Override public void onRefresh() { startBeans(true); } private void startBeans(boolean autoStartupOnly) { //获取spring容器中SmartLifecycle的实现类的bean SmartLifecycle beans=getSmartLifecycleBeans(); for(SmartLifecycle bean:beans){ //SmartLifecycle实现了Phased接口,能够返回一个int值,示意调用程序 //数字越小的越先调用 int phase=beans.getPhase(); //把bean放入LifecycleGroup中,phase雷同的放到同一个LifecycleGroup //目标在于能够对立调用phase雷同的bean的办法 LifecycleGroup lifecycleGroups=new LifecycleGroup(phase,bean)); } //依据phase的值排序 sort(lifecycleGroups); //依据顺序调用LifecycleGroup的start lifecycleGroups.start(); } //这是一个外部类 private class LifecycleGroup { public void start() { doStart(bean); } private void doStart(){ bean.start() } }}spring boot提供了一个SmartLifecycle的实现类WebServerStartStopLifecycle,并且重写了start办法 ...

June 5, 2021 · 2 min · jiezi

关于spring:Failed-to-determine-a-suitable-driver-class

Failed to determine a suitable driver class启动就报这个谬误: Description:Failed to auto-configure a DataSource: 'spring.datasource.url' is not specified and no embedded datasource could be auto-configured.Reason: Failed to determine a suitable driver classAction:Consider the following:If you want an embedded database (H2, HSQL or Derby), please put it on the classpath.If you have database settings to be loaded from a particular profile you may need to activate it (no profiles are currently active)看字面意思是没找到数据源。网上说是在利用中没有用到数据源然而增加了mybatis的依赖: ...

June 1, 2021 · 1 min · jiezi

关于spring:疯狂膜拜阿里出品Spring-Security王者晋级文档

Spring 是一个十分风行和胜利的 Java 利用开发框架。Spring Security 是 Spring 家族中的一个平安治理框架,提供了一套 Web 利用安全性的残缺解决方案。在用户认证方面,Spring Security 框架反对支流的认证形式,包含 HTTP 根本认证、HTTP 表单验证、HTTP 摘要认证、OpenID 和 LDAP 等。在用户受权方面,Spring Security 提供了基于角色的访问控制和访问控制列表(Access Control List,ACL),能够对利用中的畛域对象进行细粒度的管制。 喋喋不休说不清道不明,凑巧前些日子去敌人那边抠过去全套的Spring Security王者升级文档,认真刷完之后才发现真的是捡到宝了,不仅有具体的解释还有对应案例的源代码都有提供,不便咱本人能够实操,我给两个字评估:膜拜!王者升级,就在眼前,不置信的能够跟着一起来看看! Spring Security手绘思维脑图Spring Security王者升级文档-Spring Security 4.2内部资料Spring Security王者升级文档-01认证性能实现Spring Security王者升级文档-02Spring Security王者升级文档-03整合SpringBootSpring Security王者升级文档-04OAuth2.0申明:Spring Security王者升级文档 以及相干梳理思维脑图 等因为篇幅等的起因,大部分以截图展现,所以可提供源文件给每位感兴趣的小伙伴,<这里(点击下载)>一起升级王者!Spring Security观后感——手绘思维脑(供参考) 手绘的思维导图,是我本人依据本身的状况读完这套阿里出品的Spring Security王者升级文档之后所绘的,相当于是一个常识的总结与梳理,我将其分为“外围组件”与“工作原理/认证流程”。 Spring Security王者升级文档-Spring Security 4.2内部资料Spring Security 4.2内部资料——次要专一于企业级 Java 平安的钻研,尤其是 Spring Security。因而,咱们的所有内容会以 Java EE 安全性编程模型及 Spring Security为核心。 一、Java EE利用的安全性 1.1 Apache Tomcat 8.5内置的Java EE安全性反对1.2传统Java EE安全性编程模型的局限性二、Spring Security 4.2介绍 2.1揭秘Spring Security2.2下载Spring Security 4.2正式公布包2.3下载Spring Security 4.2源码公布包2.4运行及剖析Spring Security内置的局部Demo三、Spring Security架构设计 ...

May 29, 2021 · 1 min · jiezi

关于spring:阿里P7大佬首次分享Spring-Retry不为人知的技巧

今日分享开始啦,请大家多多指教~ 内部服务对于调用者来说个别都是不牢靠的,尤其是在网络环境比拟差的状况下,网络抖动很容易导致申请超时等异常情况,这时候就须要应用失败重试策略从新调用 API 接口来获取。重试策略在服务治理方面也有很宽泛的应用,通过定时检测,来查看服务是否存活。 Spring 异样重试框架 Spring RetrySpring Retry 反对集成到 Spring 或者 Spring Boot 我的项目中,而它反对 AOP 的切面注入写法,所以在引入时必须引入 aspectjweaver.jar 包。 1.引入 maven 依赖 2.增加 @Retryable 和 @Recover 注解@Retryable 注解,被注解的办法产生异样时会重试 value:指定产生的异样进行重试include:和 value 一样,默认空,当 exclude 也为空时,所有异样都重试exclude:指定异样不重试,默认空,当 include 也为空时,所有异样都重试maxAttemps:重试次数,默认 3backoff:重试弥补机制,默认没有@Backoff 注解 delay:指定提早后重试multiplier:指定提早的倍数,比方 delay=5000l,multiplier=2 时,第一次重试为 5 秒后,第二次为 10 秒,第三次为 20 秒@Recover 注解:当重试达到指定次数时,被注解的办法将被回调,能够在该办法中进行日志解决。须要留神的是产生的异样和入参类型统一时才会回调。 3.启用重试性能启动类下面增加 @EnableRetry 注解,启用重试性能,或者在应用 retry 的 service 下面增加也能够,或者 Configuration 配置类下面。倡议所有的 Enable 配置加在启动类上,能够清晰地对立治理应用的性能。 4.启动服务,运行测试 基于 guava 的重试组件 Guava-Retryer 间接看组件作者对此组件的介绍: This is a small extension to Google’s Guava library to allow for the creation of configurable retrying strategies for an arbitrary function call, such as something that talks to a remote service with flaky uptime.(这是对 Google 的 guava 库的一个小扩大,容许为任意函数调用创立可配置的重试策略,例如与运行工夫不稳固的近程服务对话的策略。) ...

May 29, 2021 · 1 min · jiezi

关于spring:SpringMVCRequestMappingHandlerMapping

前言应用SpringBoot进行web开发时,控制器类由@RestController注解润饰,通常@RestController注解与@RequestMapping配合应用,被润饰的类用于解决由DispatcherServlet散发下来的web申请。那么当一个web申请达到时,DispatcherServlet是如何将申请下发给对应的控制器解决呢。该篇文章将联合SpringMVC源码,对在散发申请过程中起重要作用的类RequestMappingHandlerMapping进行学习。 SpringBoot版本:2.4.1 注释一. DispatcherServlet散发申请当一个web申请到来时,DispatcherServlet负责接管申请并响应后果。DispatcherServlet首先须要找到以后申请对应的Handler(处理器)来解决申请,流程如下图所示。 HandlerMapping称为处理器映射器,是一个接口,定义web申请和Handler之间的映射。DispatcherServlet中有一个成员变量叫做handlerMappings,是一个HandlerMapping的汇合,当申请到来时,DispatcherServlet遍历handlerMappings中的每一个HandlerMapping以获取对应的handler。上述步骤产生在DispatcherServlet的doDispatch()办法中,局部源码如下所示。 protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception { HttpServletRequest processedRequest = request; HandlerExecutionChain mappedHandler = null; boolean multipartRequestParsed = false; WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request); try { ModelAndView mv = null; Exception dispatchException = null; try { processedRequest = checkMultipart(request); multipartRequestParsed = (processedRequest != request); //依据申请获取Handler mappedHandler = getHandler(processedRequest); if (mappedHandler == null) { noHandlerFound(processedRequest, response); return; } ...... } catch (Exception ex) { ...... } catch (Throwable err) { ...... } ...... } catch (Exception ex) { ...... } catch (Throwable err) { ...... } finally { ...... }}Handler的获取由DispatcherServlet的getHandler()办法实现,上面再看一下getHandler()具体做了什么事件。 ...

May 29, 2021 · 6 min · jiezi

关于spring:Spring的Xml和JavaConfig-扩展你选哪一个

引言上一篇文章咱们有怎么介绍到如何通过XML的模式来定义Spring的扩大《Spring面试高频题如何:自定义XML schema 扩大》,好多人都在吐槽当初都什么年代了,xml还有人再用吗?这玩意早就过期了吧,还有必要去把握它吗?Spring官网都把这种形式放在最初面了,可想而知它的重要性到底怎么了?既然大家都吐槽了,那咱们明天持续来介绍下基于注解的Spring扩大。 JavaConfig 配置扩大从Spring3.0开始Spring提供了JavaConfig的形式能够用来代替以前XML的这种形式,原来在XML配置里的都能够通过注解来一一替换实现。次要通过@Configuration,@Bean, @Import,和@DependsOn这几个注解来搭配实现的。这种形式也是SpringBoot所应用的。 @Configuration@Configuration只能标记在类上,示意该类为JavaConfig类,使其能够被Spring IOC容器扫描辨认并创立Bean退出到容器中。@Configuration类就相当于以往的一个xml文件。上面咱们看一个官网提供的例子: @Configurationpublic class AppConfig { @Bean public MyService myService() { return new MyServiceImpl(); }这个定义的JavaConfig就相当于原来如下XML的配置: <beans> <bean id="myService" class="cn.javajr.services.MyServiceImpl"/></beans>@Bean@Bean只能被标记在办法上,示意该办法返回一个Spring Bean,能够被IOC容器托管,相当于以前在xml文件中写的<bean/>元素。 name:指定一个或者多个bean的名字,当没有设置name时,Spring容器会默认将@Bean办法名作为bean name,当设置了name后,就不会再应用办法名,同时设置多个name时,除第一个name外,其余的都会作为bean的别名。相当于xml配置中的name属性。initMethod:指定容器在初始化完bean后调用的办法。相当于xml配置中的init-method属性。destroyMethod:指定在容器在销毁bean前调用的办法。相当于xml配置中的 destroy-method。autowire:指定bean在主动拆卸时依赖注入应用的策略,取值能够参考Enum类Autowire 的三个常量:Autowire.BY_NAME,Autowire.BY_TYPE,Autowire.NO。@ImportXML配置中的<import/>标签,基于JavaConfig提供了@Import来组合模块化的配置类,应用形式如下所示: @Configuration() @Import({ApplicationContextConfig.class}) public class ApplicationContextConfig {下面就比较简单的介绍了几种通过JavaConfig注解来替换XML模式的注解,应用起来还是非常简单的,如果你有对以前的XML配置文件都比拟理解的话,应用JavaConfig就更加简略不便了。 Dubbo的JavaConfig上篇文章咱们介绍了dubbo通过XML的形式自定义扩大,明天咱们就接着看看dubbo是如何通过JavaConfig来代替XML模式的扩大的。咱们看看dubbo的服务提供者是如何通过注解来实现的 @Configuration@EnableDubbo(scanBasePackages = "org.apache.dubbo.samples.annotation.impl")@PropertySource("classpath:/spring/dubbo-provider.properties")static class ProviderConfiguration {}@Configuration这个注解咱们下面曾经介绍过了,咱们重点看下@EnableDubbo这个注解@EnableDubbo 其实又是@EnableDubboConfig @DubboComponentScan是通过这两个组合注解来实现的,@EnableDubboConfig注解实现如下: @Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documented@Import(DubboConfigConfigurationRegistrar.class)public @interface EnableDubboConfig {这个注解应用了@Import(DubboConfigConfigurationRegistrar.class) 所以Spring 在解决@EnableDubboConfig 注解 的时候就会去实例化DubboConfigConfigurationRegistrar并且调用它的registerBeanDefinitions办法,这个办法次要是对propties文件进行解析并依据不同的配置项生成对应类型的Bean对象。 总结通过基于XML和基于Java的配置扩大,能够使用户通过Spring应用咱们研发的组件,提供很好的易用性。尽管当初大多数都是采纳JavaConfig这种形式了,然而还是有人会比拟喜爱xml这种形式xml能够让配置集中化,所有的组件并不是扩散的,因而使你对beans有一个很好的概览,比方mybais配置文件、SpingMvc配置文件,都放在一起,如果你须要宰割文件,Spring能够帮你实现。而后(Spring)会通过外部<import>标签进行重新组合或者内部上下文文件进行聚合。xml和JavaConfig 当然也是能够混合应用的,至于应用哪种形式还是看集体的编程习惯,没有哪种形式是相对的好,各有千秋。看完这两篇对于不同形式Spring的 扩大咱们是不是能够本人入手去实现一个了。 完结因为本人满腹经纶,难免会有纰漏,如果你发现了谬误的中央,还望留言给我指出来,我会对其加以修改。如果你感觉文章还不错,你的转发、分享、赞叹、点赞、留言就是对我最大的激励。感谢您的浏览,非常欢送并感谢您的关注。伟人的肩膀摘苹果:https://javajr.cn

May 26, 2021 · 1 min · jiezi

关于spring:真香现场全网首发Spring5秘籍手册知识导图我爱了

Spring 5 于 2017 年 9 月公布了通用版本 (GA),它标记着自 2013 年 12 月以来第一个次要 Spring Framework 版本。它提供了一些人们期待已久的改良,还采纳了一种全新的编程范例,以反应式宣言中陈说的反应式准则为根底。几天前小编从敌人那边嫖来Spring5秘籍手册+常识导图,通过本人的梳理才发现,这齐全就是真香现场!我爱了!咱明天就来开掘真香现场——Spring5 请留神:明天所分享的Spring5秘籍手册+架构导图,因为篇幅无限,所以很多内容都是以截图模式展现,不过全副的 残缺原件pdf(已整顿) 都是可100%free分享提供的。通过我本人的梳理,手绘了整个Spring5的架构脑图 这份Spring5的架构脑图我总共是将其整个常识分为以下6个局部: 1、Spring框架介绍2、IOC容器3、AOP4、JdbcTemplate5、事务管理6、Spring5新个性一步一个脚印,一起来梳理整个常识框架!! 1.1 Spring5的架构脑图——Spring框架介绍 1.2 Spring5的架构脑图——IOC容器 1.3 Spring5的架构脑图——AOP 1.4 Spring5的架构脑图——JdbcTemplate 1.5 Spring5的架构脑图——事务管理 1.6 Spring5的架构脑图——Spring5新个性 再者——对应的Spring5学习手册以上所绘的Spring5的架构脑图其实就是对这份Spring5学习手册的了解,每个局部都有其对应的架构常识导图作为梳理的笔记,下图则是整个手册的全目录。 2.1 Spring5学习手册内容介绍——Spring框架介绍定义:轻量级、开源的JavaEE框架。目标:解决企业应用开发的复杂性。 2.2 Spring5学习手册内容介绍——IOC容器IOC:管制反转,把对象的创立和对象间的调用通过Spring去治理,目标是为了升高程序的耦合度。 2.3 Spring5学习手册内容介绍——AOPAOP,面向切面编程,利用AOP能够对业务逻辑的各个局部进行隔离,从而使得业务逻辑各局部之间的耦合度升高,进步程序的可重用性,同时进步了开发的效率。 2.4 Spring5学习手册内容介绍——JdbcTemplateJdbcTemplate:Spring 框架对JDBC进行封装,应用JDBCTemplate不便实现对数据库的才做 2.5 Spring5学习手册内容介绍——事务管理事务是数据库操作最根本单元,逻辑上的一组操作,要么都胜利,如果有一个失败所有操作都失败 2.6 Spring5学习手册内容介绍——Spring5新个性1.JDK 8+和Java EE7+以上版本 整个框架的代码基于java8通过应用泛型等个性进步可读性对java8进步间接的代码撑持运行时兼容JDK9Java EE 7API须要Spring相干的模块反对运行时兼容Java EE8 API勾销的包,类和办法包 beans.factory.access包 dbc.support.nativejdbc从spring-aspects 模块移除了包mock.staicmock,不再提AnnotationDrivenStaticEntityMockingControl反对许多不倡议应用的类和办法在代码库中删除2.外围个性 JDK8的加强: 拜访Resuouce时提供getFile或和isFile进攻式形象无效的办法参数拜访基于java 8反射加强在Spring外围接口中减少了申明default办法的反对一贯应用JDK7 Charset和StandardCharsets的加强兼容JDK9Spring 5.0框架自带了通用的日志封装继续实例化via构造函数(批改了异样解决)Spring 5.0框架自带了通用的日志封装spring-jcl代替了通用的日志,依然反对可重写自动检测log4j 2.x, SLF4J, JUL(java.util.Logging)而不是其余的反对拜访Resuouce时提供getFile或和isFile进攻式形象基于NIO的readableChannel也提供了这个新个性3.外围容器 ...

May 25, 2021 · 1 min · jiezi

关于spring:聊聊spring事务在异常场景下发生不按套路出牌的事儿

前言最近看了一下网上总结的spring事务生效的N个场景,网上列出来的场景有如下 数据库引擎不反对事务没有被 Spring 治理办法不是 public 的本身调用问题数据源没有配置事务管理器不反对事务异样被吃了异样类型谬误其中有条异样被吃了,会导致事务无奈回滚,这个引起我的好奇,是否真的是这样,刚好也没写文素材了,就来聊聊事务与异样在某些场景产生的化学反应 示例素材1、一张没啥业务含意的表,就单纯用来演示用CREATE TABLE `tx_test` ( `id` bigint NOT NULL AUTO_INCREMENT, `tx_id` varchar(255) COLLATE utf8mb4_general_ci DEFAULT NULL, PRIMARY KEY (`id`)) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci2、一份不按编码标准来的service接口public interface TxTestService { void saveTxTestA(); void saveTxTestB();}3、一份非必需品的单元测试@SpringBootTestclass TransactionDemoApplicationTests { @Autowired private TxTestService txTestService; @Test void testTxA() { txTestService.saveTxTestA(); } @Test void testTxB() { txTestService.saveTxTestB(); }}注: 用的是junit5,所以不必加上 @RunWith(SpringRunner.class)就能够主动注入 正餐注: 每个示例演示完,我会先做清表操作,再演示下个例子 场景一:异样被吃1、示例一:代码如下private String addSql = "INSERT INTO tx_test (tx_id) VALUES (?);"; @Override @Transactional(rollbackFor = Exception.class) public void saveTxTestA() { jdbcTemplate.update(addSql, "TX-A"); try { int i = 1 % 0; } catch (Exception e) { e.printStackTrace(); } }问题思考: ...

May 25, 2021 · 2 min · jiezi

关于spring:分析Springcloud-Stream-消费者端的工作流程

通过剖析SpringCloud Stream 消费者端的工作流程,波及到的次要依赖有:spring-cloud-streamspring-rabbitspring-amqpspring-messagingamqp-client 1、音讯驱动1.1 剖析过程1.1.1 筹备工作案例中通过rabbitMQ作为消息中间件,实现SpringCloud Stream音讯驱动的剖析 1.1.2 音讯生产者1.1.2-1 创立工程引入依赖<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency></dependencies>1.1.2-2 定义BINGDING发送音讯时须要定义一个接口,不同的是接口办法的返回对象是 MessageChannel,上面是 Spring Cloud Stream 内置的接口: public interface Source {String OUTPUT = "output"; @Output(Source.OUTPUT) MessageChannel output();}这就接口申明了一个 binding 命名为 “output”。这个binding 申明了一个音讯输入流,也就是音讯的生产者。 1.1.2-3 配置APPLICATION.YMLserver: port: 7001 #服务端口spring: application: name: stream_producer #指定服务名 rabbitmq: addresses: 192.168.142.128 username: root password: 123456 virtual-host: /test cloud: stream: bindings: output: destination: root-default #指定音讯发送的目的地,在rabbitmq中,发送到一个root-default的exchange中 binders: #配置绑定器 defaultRabbit: type: rabbit1.1.2-4 测试发送音讯* 启动类* 入门案例:* 1.引入依赖* 2.配置application.yml文件* 3.发送音讯的话,定义一个通道接口,通过接口中内置的messagechannel* SpringCloudStream中内置接口 Source* 4.@EnableBinding : 绑定对应通道* 5.发送音讯的话,通过MessageChannel发送音讯* 如果须要MessageChannel --> 通过绑定的内置接口获取** @author*/@SpringBootApplication@EnableBinding(Source.class)public class ProducerApplicationDemo implements CommandLineRunner { @Autowired private MessageChannel output; public static void main(String[] args) { SpringApplication.run(ProducerApplicationDemo.class); } @Override public void run(String... args) throws Exception { //发送MQ音讯 //messagesBuilder:工具类:创立音讯 output.send(MessageBuilder.withPayload("hello world").build()); }}1.1.3 音讯消费者1.1.3-1 创立工程引入依赖<dependencies> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-stream-rabbit</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-stream-binder-rabbit</artifactId> </dependency></dependencies>1.1.3-2 定义BINGDING同发送音讯统一,在Spring Cloud Stream中承受音讯,须要定义一个接口,如下是内置的一个接口。 ...

May 23, 2021 · 4 min · jiezi

关于spring:如何实现一个简易版的-Spring-如何实现-AOP上

前言本文是「如何实现一个简易版的 Spring 系列」的第五篇,在之前介绍了 Spring 中的核心技术之一 IoC,从这篇开始咱们再来看看 Spring 的另一个重要的技术——AOP。用过 Spring 框架进行开发的敌人们置信或多或少应该接触过 AOP,用中文形容就是面向切面编程。学习一个新技术理解其产生的背景是至关重要的,在刚开始接触 AOP 时不晓得你有没有想过这个问题,既然在面向对象的语言中曾经有了 OOP 了,为什么还须要 AOP 呢?换个问法也就是说在 OOP 中有哪些场景其实解决得并不优雅,须要从新寻找一种新的技术去解决解决?(P.S. 这里倡议暂停十秒钟,本人先想一想...) 为什么须要 AOP咱们做软件开发的最终目标是为了解决公司的各种需要,为业务赋能,留神,这里的需要蕴含了业务需要和零碎需要,对于绝大部分的业务需要的一般关注点,都能够通过面向对象(OOP)的形式对其进行很好的形象、封装以及模块化,然而对于零碎需要应用面向对象的形式尽管很好的对其进行合成并对其模块化,然而却不能很好的防止这些相似的零碎需要在零碎的各个模块中到处散落的问题。 因而,须要去从新寻找一种更好的方法,能够在基于 OOP 的根底上提供一套全新的办法来解决下面的问题,或者说是对 OOP 面向对象的开发模式做一个补充,使其能够更优雅的解决下面的问题,迄今为止 Spring 提供一个的解决方案就是面向切面编程——AOP。有了 AOP 后,咱们能够将这些事务管理、系统日志以及安全检查等零碎需要(横切关注点:cross-cutting concern)进行模块化的组织,使得整个零碎更加的模块化不便后续的治理和保护。仔细的你应该发现在 AOP 外面引入了一个要害的形象就是切面(Aspect),用于对于零碎中的一些横切关注点进行封装,要明确的一点是 AOP 和 OOP 不是非此即彼的对抗关系,AOP 是对 OOP 的一种补充和欠缺,能够相互协作来实现需要,Aspect 对于 AOP 的重要水平就像 Class 对 OOP 一样。 几个重要的概念咱们最终的目标是要模拟 Spring 框架本人去实现一个简易版的 AOP 进去,尽管是简易版然而会波及到 Spring AOP 中的核心思想和次要实现步骤,不过在此之前先来看看 AOP 中的重要概念,同时也是为当前的实现打下实践根底,这里须要阐明一点是我不会应用中文翻译去形容这些 AOP 定义的术语(另外,业界 AOP 术语原本就不太对立),你须要重点了解的是术语在 AOP 中代表的含意,就像咱们不会把 Spring 给翻译成春天一样,在软件开发交换你晓得它示意一个 Java 开发框架就能够了。上面对其要害术语进行一一介绍: ...

May 23, 2021 · 3 min · jiezi

关于spring:Spring5学习4AOP概念

1.什么是AOP面向切面编程 AOP底层应用动静代理(1)有接口状况 【应用JDK动静代理】 创立代理对象要想创立一个代理对象,须要应用Proxy类的newProxyInstance办法。这个办法有三个参数: 一个类加载器(class loader)。一个Class对象数组,每个元素都是须要实现的接口。一个调用处理器(2)无接口状况 【应用CGLIB动静代理】 AOP术语(1)连接点:能够被加强的办法就是连接点(2)切入点:理论被加强的办法就是切入点(3)告诉【加强】:理论加强办法中加强的局部 - 前置告诉 - 后置告诉 - 盘绕告诉 - 异样告诉 - 最终告诉(4)切面: 应用AspectJ进行AOP操作 execution(权限修饰符 返回类型 类全门路 办法名称(参数列表)) (1)创立类 @Componentpublic class User { public void add(){ System.out.println("add..."); } }(2)创立加强类 @Component@Aspectpublic class UserProxy { //前置告诉 @Before(value = "execution(* com.example.demo.entity.User.add())") public void before(){ System.out.println("before..."); } //最终告诉[有没有异样都会执行] @After(value = "execution(* com.example.demo.entity.User.add())") public void after(){ System.out.println("after..."); } //后置告诉 @AfterReturning(value = "execution(* com.example.demo.entity.User.add())") public void afterReturing(){ System.out.println("afterReturning..."); } //异样告诉 @AfterThrowing(value = "execution(* com.example.demo.entity.User.add())") public void afterThrowing(){ System.out.println("afterThrowing..."); } //盘绕告诉 @Around(value = "execution(* com.example.demo.entity.User.add())") public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("around before..."); proceedingJoinPoint.proceed(); System.out.println("around after..."); }}(3)进行告诉的配置 ...

May 20, 2021 · 1 min · jiezi

关于spring:Spring5学习3Bean

1.Spring里,默认bean是单实例对象 ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml"); User user1 = context.getBean("user", User.class); User user2 = context.getBean("user",User.class); System.out.println(user1.equals(user2)); //输入true,user1 = user2Spring配置文件bean标签外面有属性scope用于设置单实例还是多实例(1)singleton:默认值、单实例(2)prototype:多实例 <!-- id:实例化对象的惟一标识; class:类的全门路 --> <bean id="user" class="com.dk.entity.User" scope="prototype"> <!--属性注入:name示意属性名称,value示意属性值--> <property name="name" value="LiHui"></property> <property name="age" value="25"></property> </bean>tips: 当应用Lombok的@Data注解时,则有了@EqualsAndHashCode注解,那么就会在此类中存在equals(Object other) 和 hashCode()办法,且不会应用父类的属性。如果两个对象的属性雷同,就会认为这两个对象相等,即重写了hashCode和equls办法。 2.Bean的生命周期 (1)通过结构器创立Bean实例(无参数结构) (2)为Bean的属性设置值和对其余Bean进行援用(调用Set办法) (3)调用Bean的初始化办法 (4)获取Bean对象 (5)容器敞开时,调用Bean的销毁办法 3.主动拆卸 什么是主动拆卸: 依据属性名称和属性类型,主动将匹配的属性值进行注入 通过xml文件 bean标签中autowire = "byName" / "byType" 通过注解形式 步骤: (1)引入依赖:spring-aop.jar (2)开启组件扫描: <!-- 控件扫描--> <context:component-scan base-package="demo"></context:component-scan><!--局部扫描--><context:component-scan base-package="com.dk" use-default-filters="false"> <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>(3)创立类,在类下面增加创建对象注解 (1)@Component:一般组件(2)@Service:业务逻辑层或service层(3)@Controller:web层(4)@Repository:view层或dao层(4)基于注解形式实现属性注入 (1)@Autowired:依据属性类型进行主动拆卸(2)@Qualifier:依据属性名称进行注入(和@Autowired一起应用)(3)@Resource:能够依据类型,也能够依据名称(4)@Value:注入一般类型属性值

May 19, 2021 · 1 min · jiezi

关于spring:Spring源码解析自定义标签解析和SPI机制3

1. 波及SPI机制中央自定义标签:例如:context aop 等等都是自定义标签,须要利用SPI机制默认标签中的自定义元素加载也波及到SPI机制2. SPI机制:SPI就是一个服务的扩大机制,能够把接口的实现类配置到META-INF元数据区,框架启动时加载到缓存,最后的版本是jdk中实现的,起初在spring、springboot、dubbo中都有相应的应用。 3. JDK的SPI机制:META-INF下创立services目录,而后以接口全限定名为文件名,将实现类的全限定名放进去,这样运行程序时,会加载实现类的名称进jvm,调用的时候会调用newInstance()办法实例化对象。 示例: 创立一个IAnimal接口:package com.hello.spi; public interface IAnimal { void sing();}创立两个实现类:package com.hello.spi; public class Cat implements IAnimal { @Override public void sing() { System.out.println("cat sing......"); }}package com.hello.spi; public class Dog implements IAnimal { @Override public void sing() { System.out.println("dog sing......"); }}//全门路需和类门路保持一致resource\META-INF.services\com.hello.spi.IAnimal//此门路文件下有com.hello.spi.Catcom.hello.spi.Dog测试代码:public class TestSPI { public static void main(String[] args) { ServiceLoader<IAnimal> animals = ServiceLoader.load(IAnimal.class); for (Iterator<IAnimal> iter = animals.iterator();iter.hasNext();) { IAnimal animal = iter.next(); animal.sing(); } }}5. spring的spi机制:获取spring中所有jar包外面的"META-INF/spring.handlers"文件,并且建设映射关系spring的类DefaultNamespaceHandlerResolver这个类,会懒加载spring.handler文件内配置的实现类进内存读取META-INF/spring.handlers目录下的实现类进jvm而后缓存到handlerMappings,期待前面应用这个是spring-context工程下spring.handlers文件内容: key为命名空间url、value为类的全限定名,加载实现后会缓存到handlerMappings中//此办法上一篇中有提到 private Map<String, Object> getHandlerMappings() { Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { //加载"META-INF/spring.handlers"文件过程 Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } //所有"META-INF/spring.handlers"文件外面的内容建设映射关系 handlerMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return handlerMappings; }6. 自定义标签的解析6.1 解析自定义元素 ...

May 18, 2021 · 6 min · jiezi

关于spring:Spring5学习2IOC容器

一、概念什么是IOC(1)管制反转,把对象的创立和调用过程交给Spring进行治理(2)应用目标:升高耦合度 底层原理(1)xml解析(2)工厂模式(3)反射 反射创建对象 class ObjectFactory{ public satic Object getObject(){ String classvalue = class属性值; //xml解析 Class clazz = Class.forName(classValue); return (Object)clazz.newInstance(); }}IOC接口(1)BeanFactory:是Spring外部的应用接口 【加载配置文件时不会创建对象,在获取对象时创立】(2)ApplicationContext:BeanFactory的子接口,提供更弱小的性能 【加载配置文件时就会创建对象】 Ⅰ、ClassPathXmlApplicationContext("src下类门路") Ⅱ、FileSystemXmlApplicationContext("系统盘门路") Bean治理(1)Spring创建对象 【反射形式创建对象时,默认采纳无参数构造方法】(2)Spring注入属性 基于xml形式(1)创建对象 <!-- id:实例化对象的惟一标识; class:类的全门路 --> <bean id="user" class="com.dk.entity.User"></bean>(2)注入属性 【依赖注入 DI-Dependence Injection】 <!-- id:实例化对象的惟一标识; class:类的全门路 --> <bean id="user" class="com.dk.entity.User"> <!--调用set办法进行属性注入:name示意属性名称,value示意属性值--> <property name="name" value="LiHui"></property> <property name="age" value="25"></property> </bean> <!--调用有参构造方法进行属性注入--> <bean id="user2" class="com.dk.entity.User"> <constructor-arg name="name" value="Zhang"></constructor-arg> <constructor-arg name="age" value="18"></constructor-arg> </bean><!--级联注入--> <bean id="school" class="spring.service.ba01.school"> <property name="name" value="清华"/> <property name="address" value="北京"/> </bean> <bean id="student" class="spring.service.ba01.student"> <property name="name" value="张三"/> <property name="school" ref="school"/> </bean><!--汇合属性注入--> <property name="list"> <list> <value>a</value> <value>b</value> <value>c</value> </list> </property> <property name="map"> <map> <entry key="a" value="aaa"></entry> <entry key="b" value="bbb"></entry> </map> </property> <property name="set"> <set> <value>1</value> <value>2</value> <value>1</value> </set> </property>(3)获取bean对象 ...

May 18, 2021 · 1 min · jiezi

关于spring:熟练掌握spring框架第五篇

接上篇【熟练掌握spring框架第四篇】 spring 数据源主动配置程序员的日常工作中操作数据库无疑是最频繁的事件了。很多刚毕业的求职者很自信,不就是CURD嘛,谁不会呢。的确咱们处在一个轮子满天飞时代,很多事件框架都曾经代劳了。与其说写代码是盖房子,不如说是在搭积木。咱们也不须要一砖一瓦的垒房子。那样老本太大了。但既然是搭积木,那么咱们就要分明每块积木的构造。这样能力搭建简略可靠的房子呢。上面咱们就通过源码学习下spring给咱们提供的针对数据库操作的模块spring-data-jdbc是如何工作的吧。为了更好的阐明问题,先来个简略的例子。 程序启动后会往coffee这个表里插入一条数据。那么问题来了。数据源,连接池呢。没错他们都被spring主动配置了。首先咱们看下数据源主动配置。 DataSourceProperties实现了InitializingBean,它的afterPropertiesSet办法会依据以后加载的类主动判断内嵌数据库类型。 DataSourceProperties能够生成一个DataSource的建造器DataSourceBuilder,次要是设置驱动class,连贯url,用户名和明码还有数据源类型。重点来了。创立进去的DataSource用来干嘛的呢?咱们发现DataSource的所在的包是tomcat-jdbc,阐明这件事件曾经交给了tomcat-jdbc这个东东。 tomcat-jdbc首先它是Apache Commons DBCP的替代品,官网益处说了一堆:https://tomcat.apache.org/tom... 反对高并发。能够自定义拦截器高性能反对 JMX超简略:tomcat-jdbc只有8个外围文件,最重要的是ConnectionPool类更智能的闲暇连贯解决能够异步获取Connection:Future<Connection>自定义连贯抛弃实机会:是连接池满了,还是连贯应用超时了。额……他说了不算,咱们只置信源码。 连接池的初始化咱们将断点设置在DataSourceProxy的pCreatePool里,看下它的调用栈。 随着第一次从数据源中获取Connection,ConnectionPool被创立了。创立连接池的时候次要执行了ConnectionPool的init办法。 protected void init(PoolConfiguration properties) throws SQLException { poolProperties = properties; //查看连接池配置有无问题 checkPoolConfiguration(properties); //初始化用于寄存连贯的忙碌队列 busy = new LinkedBlockingQueue<>(); //初始化用于寄存连贯的闲暇队列 idle = new FairBlockingQueue<>(); //初始化定时工作:每5000毫秒一次进行连接池的健康检查。 initializePoolCleaner(properties); //如果开启JMX,注册Mbean if (this.getPoolProperties().isJmxEnabled()) createMBean(); //创立10个连贯。 PooledConnection[] initialPool = new PooledConnection[poolProperties.getInitialSize()]; try { for (int i = 0; i < initialPool.length; i++) { //通过org.h2.Driver.connect办法一一创立java.sql.Connection,并增加到忙碌队列中。 initialPool[i] = this.borrowConnection(0, null, null); //don't wait, should be no contention } //for } catch (SQLException x) { log.error("Unable to create initial connections of pool.", x); if (!poolProperties.isIgnoreExceptionOnPreLoad()) { if (jmxPool!=null) jmxPool.notify(org.apache.tomcat.jdbc.pool.jmx.ConnectionPool.NOTIFY_INIT, getStackTrace(x)); close(true); throw x; } } finally { //return the members as idle to the pool for (int i = 0; i < initialPool.length; i++) { if (initialPool[i] != null) { //将10个连贯一一偿还到idle队列中 try {this.returnConnection(initialPool[i]);}catch(Exception x){/*NOOP*/} } //end if } //for } //catch closed = false;}通过源码发现,borrowConnection,找谁借?找idle借。借了往哪放,往busy队列放,还的时候天然也是将busy的连贯还给idle,如果连贯不够,外部会再去创立新的连贯,当然此时借的流程还是免不了的。那么问题来了。 ...

May 17, 2021 · 2 min · jiezi

关于spring:Spring源码解析默认标签解析2

1. 默认标签判断类型别离解析importaliasbean(重点)beans//默认标签解析private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) { //import标签解析 重要水平 1 ,可看可不看 if (delegate.nodeNameEquals(ele, IMPORT_ELEMENT)) { importBeanDefinitionResource(ele); } //alias标签解析 别名标签 重要水平 1 ,可看可不看 else if (delegate.nodeNameEquals(ele, ALIAS_ELEMENT)) { processAliasRegistration(ele); } //bean标签,重要水平 5,必须看 else if (delegate.nodeNameEquals(ele, BEAN_ELEMENT)) { processBeanDefinition(ele, delegate); } else if (delegate.nodeNameEquals(ele, NESTED_BEANS_ELEMENT)) { // recurse doRegisterBeanDefinitions(ele); }}2. 默认标签bean元素的解析//bean标签解析protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) { //重点看这个办法,重要水平 5 ,解析document,封装成BeanDefinition BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele); if (bdHolder != null) { //该办法性能不重要,设计模式重点看一下,装璜者设计模式,加上SPI设计思维 bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder); try { //实现document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册 // Register the final decorated instance. BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry()); } catch (BeanDefinitionStoreException ex) { getReaderContext().error("Failed to register bean definition with name '" + bdHolder.getBeanName() + "'", ele, ex); } // Send registration event. getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder)); }}3. BeanDefinitionParserDelegate解析document,封装成BeanDefinitionpublic BeanDefinitionHolder parseBeanDefinitionElement(Element ele, @Nullable BeanDefinition containingBean) { // bean元素的属性 id、name(别名,能够配置多个 以,;分隔) String id = ele.getAttribute(ID_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); List<String> aliases = new ArrayList<>(); if (StringUtils.hasLength(nameAttr)) { String[] nameArr = StringUtils.tokenizeToStringArray(nameAttr, MULTI_VALUE_ATTRIBUTE_DELIMITERS); aliases.addAll(Arrays.asList(nameArr)); } // id就是bean的名称,如果id没有配置,然而name配置了,就应用name的值作为bean的名称 String beanName = id; if (!StringUtils.hasText(beanName) && !aliases.isEmpty()) { beanName = aliases.remove(0); if (logger.isTraceEnabled()) { logger.trace("No XML 'id' specified - using '" + beanName + "' as bean name and " + aliases + " as aliases"); } } //查看beanName是否反复 if (containingBean == null) { // 查看配置的beanName和别名是否惟一,之前是否有bean曾经应用了这个名称 checkNameUniqueness(beanName, aliases, ele); } // 解析并创立beanDefinition对象 // 最初解析实现后,把名称、类型以及值的信息封装到对象上, // 而后设置到beanDefinition对象上。 // bean元素解析实现,将属性设置到beanDefinition对象上, // 而后返回到上一步,封装到了BeanDefinitionHolder对象上保护 AbstractBeanDefinition beanDefinition = parseBeanDefinitionElement(ele, beanName, containingBean); if (beanDefinition != null) { if (!StringUtils.hasText(beanName)) { try { if (containingBean != null) { beanName = BeanDefinitionReaderUtils.generateBeanName( beanDefinition, this.readerContext.getRegistry(), true); } else { beanName = this.readerContext.generateBeanName(beanDefinition); // Register an alias for the plain bean class name, if still possible, // if the generator returned the class name plus a suffix. // This is expected for Spring 1.2/2.0 backwards compatibility. String beanClassName = beanDefinition.getBeanClassName(); if (beanClassName != null && beanName.startsWith(beanClassName) && beanName.length() > beanClassName.length() && !this.readerContext.getRegistry().isBeanNameInUse(beanClassName)) { aliases.add(beanClassName); } } if (logger.isTraceEnabled()) { logger.trace("Neither XML 'id' nor 'name' specified - " + "using generated bean name [" + beanName + "]"); } } catch (Exception ex) { error(ex.getMessage(), ele); return null; } } String[] aliasesArray = StringUtils.toStringArray(aliases); return new BeanDefinitionHolder(beanDefinition, beanName, aliasesArray); } return null;}4. 解析bean元素的属性、子元素的次要逻辑都在这个办法外面:public AbstractBeanDefinition parseBeanDefinitionElement( Element ele, String beanName, @Nullable BeanDefinition containingBean) { this.parseState.push(new BeanEntry(beanName)); String className = null; if (ele.hasAttribute(CLASS_ATTRIBUTE)) { className = ele.getAttribute(CLASS_ATTRIBUTE).trim(); } String parent = null; if (ele.hasAttribute(PARENT_ATTRIBUTE)) { parent = ele.getAttribute(PARENT_ATTRIBUTE); } try { //1.创立GenericBeanDefinition对象 AbstractBeanDefinition bd = createBeanDefinition(className, parent); //2.解析bean标签的属性,并把解析进去的属性设置到BeanDefinition对象中(scope等)(**********) parseBeanDefinitionAttributes(ele, beanName, containingBean, bd); bd.setDescription(DomUtils.getChildElementValueByTagName(ele, DESCRIPTION_ELEMENT)); //3.解析bean中的meta标签(参见spring-xml) parseMetaElements(ele, bd); //4.解析bean中的lookup-method标签(代理,作用:多态形式) 重要水平:2,可看可不看 parseLookupOverrideSubElements(ele, bd.getMethodOverrides()); //5.解析bean中的replaced-method标签 重要水平:2,可看可不看 parseReplacedMethodSubElements(ele, bd.getMethodOverrides()); //6.解析bean中的constructor-arg标签 重要水平:2,可看可不看 parseConstructorArgElements(ele, bd); //7.解析bean中的property标签 重要水平:2,可看可不看 parsePropertyElements(ele, bd); //8.解析qualifier 能够不看,用不到 parseQualifierElements(ele, bd); bd.setResource(this.readerContext.getResource()); bd.setSource(extractSource(ele)); return bd; } //catch省略...... return null;}4.1 创立beanDefinition对象:public static AbstractBeanDefinition createBeanDefinition( @Nullable String parentName, @Nullable String className, @Nullable ClassLoader classLoader) throws ClassNotFoundException { // 创立BeanDefinition对象,并设置beanClass和beanClassName属性 GenericBeanDefinition bd = new GenericBeanDefinition(); bd.setParentName(parentName); if (className != null) { if (classLoader != null) { bd.setBeanClass(ClassUtils.forName(className, classLoader)); } else { bd.setBeanClassName(className); } } return bd;}4.2 解析bean标签的属性,并把解析进去的属性设置到BeanDefinition对象中public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName, @Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) { if (ele.hasAttribute(SINGLETON_ATTRIBUTE)) { error("Old 1.x 'singleton' attribute in use - upgrade to 'scope' declaration", ele); } else if (ele.hasAttribute(SCOPE_ATTRIBUTE)) { bd.setScope(ele.getAttribute(SCOPE_ATTRIBUTE)); } else if (containingBean != null) { // Take default from containing bean in case of an inner bean definition. bd.setScope(containingBean.getScope()); } if (ele.hasAttribute(ABSTRACT_ATTRIBUTE)) { bd.setAbstract(TRUE_VALUE.equals(ele.getAttribute(ABSTRACT_ATTRIBUTE))); } String lazyInit = ele.getAttribute(LAZY_INIT_ATTRIBUTE); if (DEFAULT_VALUE.equals(lazyInit)) { lazyInit = this.defaults.getLazyInit(); } bd.setLazyInit(TRUE_VALUE.equals(lazyInit)); String autowire = ele.getAttribute(AUTOWIRE_ATTRIBUTE); bd.setAutowireMode(getAutowireMode(autowire)); if (ele.hasAttribute(DEPENDS_ON_ATTRIBUTE)) { String dependsOn = ele.getAttribute(DEPENDS_ON_ATTRIBUTE); bd.setDependsOn(StringUtils.tokenizeToStringArray(dependsOn, MULTI_VALUE_ATTRIBUTE_DELIMITERS)); } //这个属性有代码案例演示,请参看案例 String autowireCandidate = ele.getAttribute(AUTOWIRE_CANDIDATE_ATTRIBUTE); if ("".equals(autowireCandidate) || DEFAULT_VALUE.equals(autowireCandidate)) { String candidatePattern = this.defaults.getAutowireCandidates(); if (candidatePattern != null) { String[] patterns = StringUtils.commaDelimitedListToStringArray(candidatePattern); bd.setAutowireCandidate(PatternMatchUtils.simpleMatch(patterns, beanName)); } } else { bd.setAutowireCandidate(TRUE_VALUE.equals(autowireCandidate)); } //这个属性有代码案例演示,请参看案例 if (ele.hasAttribute(PRIMARY_ATTRIBUTE)) { bd.setPrimary(TRUE_VALUE.equals(ele.getAttribute(PRIMARY_ATTRIBUTE))); } //这个属性有代码案例演示,请参看案例 if (ele.hasAttribute(INIT_METHOD_ATTRIBUTE)) { String initMethodName = ele.getAttribute(INIT_METHOD_ATTRIBUTE); bd.setInitMethodName(initMethodName); } else if (this.defaults.getInitMethod() != null) { bd.setInitMethodName(this.defaults.getInitMethod()); bd.setEnforceInitMethod(false); } //这个属性有代码案例演示,请参看案例 if (ele.hasAttribute(DESTROY_METHOD_ATTRIBUTE)) { String destroyMethodName = ele.getAttribute(DESTROY_METHOD_ATTRIBUTE); bd.setDestroyMethodName(destroyMethodName); } else if (this.defaults.getDestroyMethod() != null) { bd.setDestroyMethodName(this.defaults.getDestroyMethod()); bd.setEnforceDestroyMethod(false); } //这个属性有代码案例演示,请参看案例 if (ele.hasAttribute(FACTORY_METHOD_ATTRIBUTE)) { bd.setFactoryMethodName(ele.getAttribute(FACTORY_METHOD_ATTRIBUTE)); } //这个属性有代码案例演示,请参看案例 if (ele.hasAttribute(FACTORY_BEAN_ATTRIBUTE)) { bd.setFactoryBeanName(ele.getAttribute(FACTORY_BEAN_ATTRIBUTE)); } return bd;}4.3 解析bean中的meta标签(参见spring-xml)4.4 设置元数据,做一个标识解析bean中的lookup-method标签(代理,作用:多态形式) 重要水平:2,可看可不看public void parseLookupOverrideSubElements(Element beanEle, MethodOverrides overrides) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, LOOKUP_METHOD_ELEMENT)) { Element ele = (Element) node; String methodName = ele.getAttribute(NAME_ATTRIBUTE); String beanRef = ele.getAttribute(BEAN_ELEMENT); LookupOverride override = new LookupOverride(methodName, beanRef); override.setSource(extractSource(ele)); overrides.addOverride(override); } }}4.5 解析bean中的replaced-method标签 重要水平:2,可看可不看public void parseReplacedMethodSubElements(Element beanEle, MethodOverrides overrides) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, REPLACED_METHOD_ELEMENT)) { Element replacedMethodEle = (Element) node; String name = replacedMethodEle.getAttribute(NAME_ATTRIBUTE); String callback = replacedMethodEle.getAttribute(REPLACER_ATTRIBUTE); //一个replaced-method标签封装成一个ReplaceOverride对象,最初退出到BeanDefinition对象中 ReplaceOverride replaceOverride = new ReplaceOverride(name, callback); // Look for arg-type match elements. List<Element> argTypeEles = DomUtils.getChildElementsByTagName(replacedMethodEle, ARG_TYPE_ELEMENT); for (Element argTypeEle : argTypeEles) { //依据办法参数类型来辨别同名的不同的办法 String match = argTypeEle.getAttribute(ARG_TYPE_MATCH_ATTRIBUTE); match = (StringUtils.hasText(match) ? match : DomUtils.getTextValue(argTypeEle)); if (StringUtils.hasText(match)) { replaceOverride.addTypeIdentifier(match); } } replaceOverride.setSource(extractSource(replacedMethodEle)); overrides.addOverride(replaceOverride); } }}4.6 解析bean中的constructor-arg标签,结构器注入public void parseConstructorArgElements(Element beanEle, BeanDefinition bd) { NodeList nl = beanEle.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (isCandidateElement(node) && nodeNameEquals(node, CONSTRUCTOR_ARG_ELEMENT)) { //解析结构器参数元素 parseConstructorArgElement((Element) node, bd); } }}public void parseConstructorArgElement(Element ele, BeanDefinition bd) { String indexAttr = ele.getAttribute(INDEX_ATTRIBUTE); String typeAttr = ele.getAttribute(TYPE_ATTRIBUTE); String nameAttr = ele.getAttribute(NAME_ATTRIBUTE); //蕴含index属性 if (StringUtils.hasLength(indexAttr)) { try { int index = Integer.parseInt(indexAttr); if (index < 0) { error("'index' cannot be lower than 0", ele); } else { try { this.parseState.push(new ConstructorArgumentEntry(index)); // 解析value值,解析元素的value值,过程和解析set注入property的流程一样, // 而后封装到valueHolder对象中, // 最初把index 和 valueHolder设置到beanDefinition的contructorArgumentValues的属性上 Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); // 如果index索引反复,那么赋值时会报错 if (bd.getConstructorArgumentValues().hasIndexedArgumentValue(index)) { error("Ambiguous constructor-arg entries for index " + index, ele); } else { // 把index与valueHolder封装到ConstructorArgumentValues对象,再把该对象设置到bd上 bd.getConstructorArgumentValues().addIndexedArgumentValue(index, valueHolder); } } finally { this.parseState.pop(); } } } catch (NumberFormatException ex) { error("Attribute 'index' of tag 'constructor-arg' must be an integer", ele); } } //index不存在 else { try { this.parseState.push(new ConstructorArgumentEntry()); Object value = parsePropertyValue(ele, bd, null); ConstructorArgumentValues.ValueHolder valueHolder = new ConstructorArgumentValues.ValueHolder(value); if (StringUtils.hasLength(typeAttr)) { valueHolder.setType(typeAttr); } if (StringUtils.hasLength(nameAttr)) { valueHolder.setName(nameAttr); } valueHolder.setSource(extractSource(ele)); // 会把valueHolder的值封装到GenericArgumengValue中 bd.getConstructorArgumentValues().addGenericArgumentValue(valueHolder); } finally { this.parseState.pop(); } }}4.7 解析 bean中 的 property 标签(和结构相似)4.8 解析 qualifier 能够不看,用不到4.9 返回 BeanDefinition 对象5. (承接第4点)该办法性能不重要,设计模式重点看一下,装璜者设计模式,加上SPI设计思维,将持有的BeanDefinitionHolder 对象进行装璜public BeanDefinitionHolder decorateBeanDefinitionIfRequired( Element ele, BeanDefinitionHolder definitionHolder, @Nullable BeanDefinition containingBd) { BeanDefinitionHolder finalDefinition = definitionHolder; //依据bean标签属性装璜BeanDefinitionHolder,比方<bean class="xx" p:username="jack"/> // // Decorate based on custom attributes first. NamedNodeMap attributes = ele.getAttributes(); for (int i = 0; i < attributes.getLength(); i++) { Node node = attributes.item(i); finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); } //依据bean标签子元素装璜BeanDefinitionHolder // Decorate based on custom nested elements. NodeList children = ele.getChildNodes(); for (int i = 0; i < children.getLength(); i++) { Node node = children.item(i); if (node.getNodeType() == Node.ELEMENT_NODE) { finalDefinition = decorateIfRequired(node, finalDefinition, containingBd); } } return finalDefinition;}6. 装璜BeanDefinitionHolderpublic BeanDefinitionHolder decorateIfRequired( Node node, BeanDefinitionHolder originalDef, @Nullable BeanDefinition containingBd) { //依据node获取到node的命名空间,形如:http://www.springframework.org/schema/p String namespaceUri = getNamespaceURI(node); if (namespaceUri != null && !isDefaultNamespace(namespaceUri)) { //这里有SPI服务发现的思维,依据配置文件获取namespaceUri对应的解决类 NamespaceHandler handler = this.readerContext.getNamespaceHandlerResolver().resolve(namespaceUri); if (handler != null) { //调用NamespaceHandler解决类的decorate办法,开始具体装璜过程,并返回装璜完的对象 BeanDefinitionHolder decorated = handler.decorate(node, originalDef, new ParserContext(this.readerContext, this, containingBd)); if (decorated != null) { return decorated; } } //略...... } return originalDef;}spi机制public NamespaceHandler resolve(String namespaceUri) { //获取spring中所有jar包外面的 "META-INF/spring.handlers"文件,并且建设映射关系 Map<String, Object> handlerMappings = getHandlerMappings(); //依据namespaceUri:http://www.springframework.org/schema/p,获取到这个命名空间的解决类 Object handlerOrClassName = handlerMappings.get(namespaceUri); if (handlerOrClassName == null) { return null; } else if (handlerOrClassName instanceof NamespaceHandler) { return (NamespaceHandler) handlerOrClassName; } else { String className = (String) handlerOrClassName; try { //反射进去类 Class<?> handlerClass = ClassUtils.forName(className, this.classLoader); if (!NamespaceHandler.class.isAssignableFrom(handlerClass)) { throw new FatalBeanException("Class [" + className + "] for namespace [" + namespaceUri + "] does not implement the [" + NamespaceHandler.class.getName() + "] interface"); } //实例化 NamespaceHandler namespaceHandler = (NamespaceHandler) BeanUtils.instantiateClass(handlerClass); //调用解决类的init办法,在init办法中实现标签元素解析类的注册***重要 namespaceHandler.init(); handlerMappings.put(namespaceUri, namespaceHandler); return namespaceHandler; } catch (ClassNotFoundException ex) { throw new FatalBeanException("Could not find NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", ex); } catch (LinkageError err) { throw new FatalBeanException("Unresolvable class definition for NamespaceHandler class [" + className + "] for namespace [" + namespaceUri + "]", err); } }}private Map<String, Object> getHandlerMappings() { Map<String, Object> handlerMappings = this.handlerMappings; if (handlerMappings == null) { synchronized (this) { handlerMappings = this.handlerMappings; if (handlerMappings == null) { if (logger.isTraceEnabled()) { logger.trace("Loading NamespaceHandler mappings from [" + this.handlerMappingsLocation + "]"); } try { //加载"META-INF/spring.handlers"文件过程 Properties mappings = PropertiesLoaderUtils.loadAllProperties(this.handlerMappingsLocation, this.classLoader); if (logger.isTraceEnabled()) { logger.trace("Loaded NamespaceHandler mappings: " + mappings); } //所有"META-INF/spring.handlers"文件外面的内容建设映射关系 handlerMappings = new ConcurrentHashMap<>(mappings.size()); CollectionUtils.mergePropertiesIntoMap(mappings, handlerMappings); this.handlerMappings = handlerMappings; } catch (IOException ex) { throw new IllegalStateException( "Unable to load NamespaceHandler mappings from location [" + this.handlerMappingsLocation + "]", ex); } } } } return handlerMappings;}7. 返回第2点,实现document到BeanDefinition对象转换后,对BeanDefinition对象进行缓存注册public static void registerBeanDefinition( BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) throws BeanDefinitionStoreException { // Register bean definition under primary name. String beanName = definitionHolder.getBeanName(); //实现BeanDefinition的注册,重点看,重要水平 5 registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition()); //建设别名和 id的映射,这样就能够依据别名获取到id (别名逻辑暂不关注) // Register aliases for bean name, if any. String[] aliases = definitionHolder.getAliases(); if (aliases != null) { for (String alias : aliases) { registry.registerAlias(beanName, alias); } }}public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } //先判断BeanDefinition是否曾经注册 BeanDefinition existingDefinition = this.beanDefinitionMap.get(beanName); if (existingDefinition != null) { if (!isAllowBeanDefinitionOverriding()) { throw new BeanDefinitionOverrideException(beanName, beanDefinition, existingDefinition); } else if (existingDefinition.getRole() < beanDefinition.getRole()) { // e.g. was ROLE_APPLICATION, now overriding with ROLE_SUPPORT or ROLE_INFRASTRUCTURE if (logger.isInfoEnabled()) { logger.info("Overriding user-defined bean definition for bean '" + beanName + "' with a framework-generated bean definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else if (!beanDefinition.equals(existingDefinition)) { if (logger.isDebugEnabled()) { logger.debug("Overriding bean definition for bean '" + beanName + "' with a different definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } else { if (logger.isTraceEnabled()) { logger.trace("Overriding bean definition for bean '" + beanName + "' with an equivalent definition: replacing [" + existingDefinition + "] with [" + beanDefinition + "]"); } } this.beanDefinitionMap.put(beanName, beanDefinition); } else { if (hasBeanCreationStarted()) { // Cannot modify startup-time collection elements anymore (for stable iteration) synchronized (this.beanDefinitionMap) { this.beanDefinitionMap.put(beanName, beanDefinition); List<String> updatedDefinitions = new ArrayList<>(this.beanDefinitionNames.size() + 1); updatedDefinitions.addAll(this.beanDefinitionNames); updatedDefinitions.add(beanName); this.beanDefinitionNames = updatedDefinitions; if (this.manualSingletonNames.contains(beanName)) { Set<String> updatedSingletons = new LinkedHashSet<>(this.manualSingletonNames); updatedSingletons.remove(beanName); this.manualSingletonNames = updatedSingletons; } } } else { //把beanDefinition缓存到map中 // Still in startup registration phase this.beanDefinitionMap.put(beanName, beanDefinition); //把beanName放到beanDefinitionNames list中,这个list着重记住,bean实例化的时候须要用到 this.beanDefinitionNames.add(beanName); this.manualSingletonNames.remove(beanName); } this.frozenBeanDefinitionNames = null; } if (existingDefinition != null || containsSingleton(beanName)) { //重置容器,外部会递归 resetBeanDefinition(beanName); }}到这里默认标签的解析工作就实现了总结:默认标签次要包含import bean alias 等,以bean元素为例,首先解析id、class 属性,创立beanDefinition对象解析bean元素可能配置的属性,子元素等信息。例如:property元素,constructor元素,lookup等元素,解析实现后,将数据封装到对应的对象上,而后设置到beanDefinition对象属性中,而后返回。将beanDefinition放在到holder上,判断默认元素是否蕴含自定义元素(<bean class="xx" p:username="jack"/>),包含则进行解析,将数据设置到beanDefinition对象上注册beanDefinition到beanDefinitionMap汇合中,注册beanName到beanDefinitionNames汇合中,注册别名aliasMap中

May 17, 2021 · 8 min · jiezi

关于spring:Spring源码解析xml加载流程1

1. 类的层级图DefaultResourceLoaderAbstractApplicationContextAbstractRefreshableApplicationContextAbstractRefreshableConfigApplicationContextAbstractXmlApplicationContextClassPathXmlApplicationContext2. spring容器启动入口,执行胜利容器就启动实现了ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");3. 进入ClassPathXmlApplicationContext类的构造方法public ClassPathXmlApplicationContext( String[] configLocations, boolean refresh, @Nullable ApplicationContext parent) throws BeansException { super(parent); //初始化配置文件xml的地位,解析configLocations,即.xml文件 //办法实现所在位置:父类 AbstractRefreshableConfigApplicationContext setConfigLocations(configLocations); if (refresh) { //spring容器启动的主流程(*****重要*****) //办法实现所在位置:父类 AbstractApplicationContext refresh(); }}//此办法所在类:AbstractRefreshableConfigApplicationContextpublic void setConfigLocations(@Nullable String... locations) { if (locations != null) { Assert.noNullElements(locations, "Config locations must not be null"); //将xml文件维护在configLocations属性上 this.configLocations = new String[locations.length]; for (int i = 0; i < locations.length; i++) { //resolvePath办法波及含糊匹配,先不看 this.configLocations[i] = resolvePath(locations[i]).trim(); } } else { this.configLocations = null; }}4. spring容器启动的外围办法refresh();/** * 该办法是spring容器初始化的外围办法。 * 是spring容器初始化的外围流程,是一个典型的父类模板设计模式的使用 * 依据不同的上下文对象,会掉到不同的上下文对象子类办法中 * * 外围上下文子类有: * ClassPathXmlApplicationContext * FileSystemXmlApplicationContext * AnnotationConfigApplicationContext * EmbeddedWebApplicationContext(springboot) */@Overridepublic void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { // Prepare this context for refreshing. // 为容器初始化做筹备,能够不看 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. /* * 1、创立BeanFactory对象 * 2、xml解析 * 传统标签解析:bean、import等 * 自定义标签解析:例如:<context:component-scan base-package="com.xiangxue.jack"/> * 自定义标签解析流程: * a、依据以后解析标签的头信息找到对应的namespaceUri * b、加载spring所以jar中的spring.handlers文件。并建设映射关系 * c、依据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类 * d、调用类的init办法,init办法是注册了各种自定义标签的解析类 * e、依据namespaceUri找到对应的解析类,而后调用paser办法实现标签解析 * 3、把解析进去的xml标签信息封装成BeanDefinition对象 */ ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context. prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. //钩子办法,由子类实现 postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context. // 注册beanFactoryPostProcessor对象 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. // 注册beanPostProcessor实例,在bean创立的时候实现拦挡 registerBeanPostProcessors(beanFactory); // Initialize message source for this context. // 国际化,重要水平2 initMessageSource(); // Initialize event multicaster for this context. // 初始化工夫治理类 initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. //此办法着重了解模板设计模式,因为在springboot中,这个办法是用来做内嵌tomcat启动的 onRefresh(); // Check for listener beans and register them. // 往工夫治理类中注册事件类 registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. /* * 在之前曾经实例化了BeanFactoryPostProcessor以及beanPostProcessor * 上面开始实例化剩下的所有非懒加载的单例对象 */ finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); } }}5. obtainFreshBeanFactory()辨析//父类AbstractApplicationContext办法protected ConfigurableListableBeanFactory obtainFreshBeanFactory() { //外围办法,必须读,重要水平:5 //此办法是钩子办法,在子类中实现 refreshBeanFactory(); return getBeanFactory();}6. refreshBeanFactory()辨析//AbstractRefreshableApplicationContext(继承AbstractApplicationContext)protected final void refreshBeanFactory() throws BeansException { //如果BeanFactory不为空,则革除BeanFactory和外面的实例 if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { //创立DefaultListableBeanFactory DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); //设置是否能够循环依赖 allowCircularReferences //是否容许应用雷同名称从新注册不同的bean实现. customizeBeanFactory(beanFactory); //解析xml,并把xml中的标签封装成BeanDefinition对象,调用子类办法 loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); }}7. loadBeanDefinitions()辨析//AbstractXmlApplicationContext类(继承AbstractRefreshableConfigApplicationContext)protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException { // Create a new XmlBeanDefinitionReader for the given BeanFactory. //创立xml的解析器,这里是一个委托模式 XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory); // Configure the bean definition reader with this context's // resource loading environment. beanDefinitionReader.setEnvironment(this.getEnvironment()); //这里传一个this进去,因为ApplicationContext是实现了ResourceLoader接口的 beanDefinitionReader.setResourceLoader(this); beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this)); // Allow a subclass to provide custom initialization of the reader, // then proceed with actually loading the bean definitions. initBeanDefinitionReader(beanDefinitionReader); //次要看这个办法 重要水平 5 loadBeanDefinitions(beanDefinitionReader);}//以后类protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException { Resource[] configResources = getConfigResources(); if (configResources != null) { reader.loadBeanDefinitions(configResources); } //获取须要加载的xml配置文件 // 在第一步classPathXmlApplicationContext结构器中,曾经初始化了configLocation String[] configLocations = getConfigLocations(); if (configLocations != null) { reader.loadBeanDefinitions(configLocations); }}8. 委托给reader来解析 reader.loadBeanDefinitions()//AbstractBeanDefinitionReader类中办法public int loadBeanDefinitions(String... locations) throws BeanDefinitionStoreException { Assert.notNull(locations, "Location array must not be null"); int count = 0; //配置文件有多个,加载多个配置文件,循环解析 for (String location : locations) { //调用以后类的办法 count += loadBeanDefinitions(location); } return count;}@Overridepublic int loadBeanDefinitions(String location) throws BeanDefinitionStoreException { //调用以后类的办法 return loadBeanDefinitions(location, null);}public int loadBeanDefinitions(String location, @Nullable Set<Resource> actualResources) throws BeanDefinitionStoreException { //之前AbstractXmlApplicationContext类中loadBeanDefinitions()办法中 //beanDefinitionReader.setResourceLoader(this);传入的this,用于此处拿到上下文对象 ResourceLoader resourceLoader = getResourceLoader(); if (resourceLoader == null) { throw new BeanDefinitionStoreException( "Cannot load bean definitions from location [" + location + "]: no ResourceLoader available"); } if (resourceLoader instanceof ResourcePatternResolver) { // Resource pattern matching available. try { //把字符串类型的xml文件门路,形如:classpath*:user/**/*-context.xml,转换成Resource对象类型,其实就是用流 //的形式加载配置文件,而后封装成Resource对象,不重要,能够不看 Resource[] resources = ((ResourcePatternResolver) resourceLoader).getResources(location); //次要看这个办法 ** 重要水平 5 //调用以后类的办法-重载 int count = loadBeanDefinitions(resources); if (actualResources != null) { Collections.addAll(actualResources, resources); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location pattern [" + location + "]"); } return count; } catch (IOException ex) { throw new BeanDefinitionStoreException( "Could not resolve bean definition resource pattern [" + location + "]", ex); } } else { // Can only load single resources by absolute URL. // 这个Resource仅仅可能加载单个的绝对路径的xml配置文件 Resource resource = resourceLoader.getResource(location); int count = loadBeanDefinitions(resource); if (actualResources != null) { actualResources.add(resource); } if (logger.isTraceEnabled()) { logger.trace("Loaded " + count + " bean definitions from location [" + location + "]"); } return count; }}public int loadBeanDefinitions(Resource... resources) throws BeanDefinitionStoreException { Assert.notNull(resources, "Resource array must not be null"); int count = 0; for (Resource resource : resources) { //模板设计模式,调用到子类中的办法 count += loadBeanDefinitions(resource); } return count;}9. EncodedResource带编码的对Resource对象的封装,将inputSource对象封装成Documet对象//子类XmlBeanDefinitionReader中的办法(继承AbstractBeanDefinitionReader)//对接口BeanDefinitionReader的实现public int loadBeanDefinitions(Resource resource) throws BeanDefinitionStoreException { //EncodedResource带编码的对Resource对象的封装 return loadBeanDefinitions(new EncodedResource(resource));}//从资源Resource中拿到输出流InputStream,保护到InputSource中,而后调用doLoaderBeanDefinitions解析public int loadBeanDefinitions(EncodedResource encodedResource) throws BeanDefinitionStoreException { Assert.notNull(encodedResource, "EncodedResource must not be null"); if (logger.isTraceEnabled()) { logger.trace("Loading XML bean definitions from " + encodedResource); } Set<EncodedResource> currentResources = this.resourcesCurrentlyBeingLoaded.get(); if (currentResources == null) { currentResources = new HashSet<>(4); this.resourcesCurrentlyBeingLoaded.set(currentResources); } if (!currentResources.add(encodedResource)) { throw new BeanDefinitionStoreException( "Detected cyclic loading of " + encodedResource + " - check your import definitions!"); } try { //获取Resource对象中的xml文件流对象 InputStream inputStream = encodedResource.getResource().getInputStream(); try { //InputSource是jdk中的sax xml文件解析对象 InputSource inputSource = new InputSource(inputStream); if (encodedResource.getEncoding() != null) { inputSource.setEncoding(encodedResource.getEncoding()); } //次要看这个办法 ** 重要水平 5- *****加载beanDefinition***** return doLoadBeanDefinitions(inputSource, encodedResource.getResource()); } finally { inputStream.close(); } } catch (IOException ex) { throw new BeanDefinitionStoreException( "IOException parsing XML document from " + encodedResource.getResource(), ex); } finally { currentResources.remove(encodedResource); if (currentResources.isEmpty()) { this.resourcesCurrentlyBeingLoaded.remove(); } }}protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException { try { //把inputSource 封装成Document文件对象,这是jdk的API Document doc = doLoadDocument(inputSource, resource); //次要看这个办法,依据解析进去的document对象,拿到外面的标签元素封装成BeanDefinition int count = registerBeanDefinitions(doc, resource); if (logger.isDebugEnabled()) { logger.debug("Loaded " + count + " bean definitions from " + resource); } return count; } //异样此处略去......}10. 解析为document对象,之后就要注册beanDefinition了在spring的加载过程中,BeanDefinition是一个重要的数据结构,它是在创建对象之前,对象数据的一种存在模式xml —— beanDefinition ——bean 从xml配置bean,到解析xml创立 beanDefinition,到从beanDefinition实例为 bean对象,这是一个流程。//XmlBeanDefinitionReader类public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException { //又来一记委托模式,BeanDefinitionDocumentReader委托这个类进行document的解析 BeanDefinitionDocumentReader documentReader = createBeanDefinitionDocumentReader(); int countBefore = getRegistry().getBeanDefinitionCount(); //次要看这个办法,createReaderContext(resource) XmlReaderContext上下文,封装了XmlBeanDefinitionReader对象 documentReader.registerBeanDefinitions(doc, createReaderContext(resource)); return getRegistry().getBeanDefinitionCount() - countBefore;}11. 通过上一个委托进行注册beanDefinitionspring将xml文件封装成了Document对象,而后委托给BeanDefinitionDocumentReader来解析//DefaultBeanDefinitionDocumentReader(实现BeanDefinitionDocumentReader接口)public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) { this.readerContext = readerContext; //次要看这个办法,把root节点传进去 //注册beanDefinition,将document中root元素传入 doRegisterBeanDefinitions(doc.getDocumentElement());}//委托给document的解析器,入参为document的根元素,就是spring-context.xml的beans元素:protected void doRegisterBeanDefinitions(Element root) { BeanDefinitionParserDelegate parent = this.delegate; this.delegate = createDelegate(getReaderContext(), root, parent); if (this.delegate.isDefaultNamespace(root)) { String profileSpec = root.getAttribute(PROFILE_ATTRIBUTE); if (StringUtils.hasText(profileSpec)) { String[] specifiedProfiles = StringUtils.tokenizeToStringArray( profileSpec, BeanDefinitionParserDelegate.MULTI_VALUE_ATTRIBUTE_DELIMITERS); // We cannot use Profiles.of(...) since profile expressions are not supported // in XML config. See SPR-12458 for details. if (!getReaderContext().getEnvironment().acceptsProfiles(specifiedProfiles)) { if (logger.isDebugEnabled()) { logger.debug("Skipped XML bean definition file due to specified profiles [" + profileSpec + "] not matching: " + getReaderContext().getResource()); } return; } } } //冗余设计,这里有两个钩子办法,典型的模板设计,由子类去实现 preProcessXml(root); //次要看这个办法,标签具体解析过程 //具体的解析document对象,注册beanDefinition的逻辑在这里实现 parseBeanDefinitions(root, this.delegate); postProcessXml(root); this.delegate = parent;}protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { //判断根元素的命名空间是否为空或者是 xmlns="http://www.springframework.org/schema/beans" 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)) { //默认标签解析,bean,import等 parseDefaultElement(ele, delegate); } else { //自定义标签解析, context等 // a、依据以后解析标签的头信息找到对应的namespaceUri // b、加载spring所以jar中的spring.handlers文件。并建设映射关系 // c、依据namespaceUri从映射关系中找到对应的实现了NamespaceHandler接口的类 // d、调用类的init办法,init办法是注册了各种自定义标签的解析类 // e、依据namespaceUri找到对应的解析类,而后调用paser办法实现标签解析 delegate.parseCustomElement(ele);//***********重点 } } } } else { delegate.parseCustomElement(root); }}总结:上述是启动spring流程的第一步,解析配置文件,当然咱们这里是以xml配置的形式剖析。也可能是注解配置的办法,后续再来剖析注解形式。创立applicationContext对象,将xml文件的门路保护到AbstractRefreshableApplicationContext的属性上refresh启动spring流程,这里是spring启动的外围流程第一步 obtainBeanFactory ,这这个办法里,会创立bean工厂,加载xml文件,委托给XmlBeanDefinitionReader解析XmlBeanDefinitionReader 将xml字符串门路封装为Resource对象,再转为InputStream流,最初把输出流生成Document对象,而后委托给BeanDefinitionDocumentReader解析。

May 14, 2021 · 6 min · jiezi

关于spring:彻底理解Spring-Interceptor和Servlet-Filter

微服务时代,java依附SpringBoot又再度晋升热度。本来认为php, python之类的会继续鲸吞Java的领地,熟知微服务又复原了Java来日的位置。SpringBoot依赖Spring生态圈满满圈粉,热度更胜当年。 SpringBoot的实质就是实现了主动拆卸,解决了Spring研发的配置地区问题。然而它的根底仍然是Spring, 对于web研发的根底仍然是SpringMVC。因而有必要深刻理解Spring, SpringMVC。 对于Spring来说,拦截器和过滤器是十分外围和重要的两个概念。因而本文针对这两个概念进行深入分析,让咱们彻底了解拦截器和过滤器。 对于拦截器和过滤器是什么,有大量文章曾经做了阐明,咱们这里不具体论述。本文分为五个局部: 拦截器和过滤器的区别拦截器应用过滤器应用拦截器和过滤器的利用场景拦截器和过滤器的执行过程及流程1. 拦截器和过滤器的区别过滤器(Filter)拦截器(Interceptor)总结Filter接口定义在javax.servlet包中HandlerInterceptor接口定义在org.springframework.web.servlet包中 Filter定义在web.xml中HandlerInterceptor是在应用程序上下文配置的。 Filter只在Servlet前后起作用。Filter通常将申请和响应当做黑盒子,Filter通常不思考Servlet的实现。拦截器能深刻到办法前后、异样抛出前后等,因而拦截器的应用具备更大的弹性。容许用户接入(hook into)申请的生命周期,在申请过程中获取信息,Interceptor通常和申请更加耦合。在Spring架构的程序中,要无限应用拦截器。简直所有Filter能做的事件,Interceptor都可能轻松的实现。Filter是Servlet标准规定的。拦截器既能够用于Web程序,也能够用于Application、Swing程序中。应用范畴不同Filter是在Servlet标准中定义的,是Servlet容器反对的。拦截器是在Spring容器内的,是Spring框架反对的。标准不同Filter不可能应用Spring容器资源。拦截器是一个Spring的组件,归Spring治理,配置在Spring文件中,因而能应用Spring里的任何资源、对象,例如Service对象、数据源、事务管理等,通过IoC注入到拦截器即可。Spring中应用拦截器更容易Filter是被Servlet(就像Tomcat)调用的。拦截器(Interceptor)是被Spring调用的。因而Filter总是优于Interceptor执行。上面援用他人的一张图阐明一下,在tomcat容器下,拦截器、过滤器、Servlet以及Spring Controller之间的关系。 在申请解决和响应过程中过滤器和拦截器等组件的执行流程及关系: 申请首先达到Servlet容器,将申请转给web容器。web容器调用Servlet过滤器,而后持续将申请转交给Spring的DispatcherServlet, 申请就转交给Spring上下文。DispatcherServlet就能够调用Spring的拦截器对申请进行拦挡解决,最初交给资源控制器。最终有资源控制器联合业务模型进行业务解决,而后沿着4到1一层层朝外对相应进行解决,最终返回给客户端。 2. 拦截器的应用Interceptor的执行程序大抵为: 申请达到DispatcherServletDispatcherServlet发送至Interceptor, 执行preHandle申请达到Controller申请完结后,postHandle执行Spring中次要通过HandlerInterceptor接口来实现申请的拦挡,实现HandlerInterceptor接口须要实现上面三个办法: preHandle(): 在handle执行之前,返回boolean值,true示意继续执行,false为进行执行并返回。postHandle(): 在handle执行之后,能够在返回之前对返回的后果进行批改。afterCompletion(): 在申请实现,视图生成后调用。2.1 利用拦截器进行申请耗时统计申请耗时统计能够通过在申请解决之前记录一个工夫,申请解决实现后再记录一个工夫,打印出两个工夫的差就是申请耗时。 2.1.1 定义一个拦截器public class ExecuteTimeInterceptor extends HandlerInterceptorAdapter { // private static final Logger log = LoggerFactory.getLogger(ExecuteTimeInterceptor.class); /** * 在handler执行之前执行 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { long startTime = System.currentTimeMillis(); request.setAttribute("startTime", startTime); return super.preHandle(request, response, handler); } /** * 在handler执行之后执行 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { long startTime = (long) request.getAttribute("startTime"); long endTime = System.currentTimeMillis(); long executeTime = endTime - startTime; System.out.println("[" + handler + "] executeTime : " + executeTime + "ms"); super.postHandle(request, response, handler, modelAndView); }} ...

May 13, 2021 · 3 min · jiezi

关于spring:关于spring总结了一篇上万字的图文笔记不管你工作几年都应该看看

spring bean的实例化结构器实例化 <!-- 无参结构器实例化 --> <bean id="student1" class="com.qfedu.entity.Student"> <property name="id" value="100"></property> <property name="name" value="xiaoming"></property> </bean>动态工厂实例化容器创建对象,不间接调用对象构造方法,而是调用动态工厂的创建对象的办法益处:便于咱们定制化创建对象,对象的初始化,须要拜访网络中的数据 /** * 动态工厂 * 静态方法创建对象 */public class StaticFactory { /** * 静态方法 创建对象 * @return */ public static Student createStudent(){ Student student = new Student(); // 益处就是 程序员能够 自定义 初始化对象,并交给spring容器创立 student.setId(123); student.setName("小明"); return student; }}<!-- 通知容器应用动态工厂 创建对象 class="com.qfedu.factory.StaticFactory" 动态工厂 factory-method="createStudent" 调用动态工厂的办法名 --> <bean id="student2" class="com.qfedu.factory.StaticFactory" factory-method="createStudent"> </bean>集体整顿了一些材料,有须要的敌人能够间接点击支付。 Java基础知识大全 22本Java架构师外围书籍 从0到1Java学习路线和材料 1000+道2021年最新面试题 实例工厂实例化/** * 实例工厂 创建对象 */public class Factory { /** * 实例办法 创建对象 * @return */ public Student createStudent(){ Student student = new Student(); // 益处就是 程序员能够 自定义 初始化对象,并交给spring容器创立 student.setId(123); student.setName("小明"); return student; }}<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!-- 创立实例工厂 --> <bean id="factory" class="com.qfedu.factory.Factory"> </bean> <!-- 指标创立 student 应用实例工厂 factory-bean="factory" 援用实例工厂 factory-method="createStudent" 调用实例工厂中的实例 办法 --> <bean id="student" factory-bean="factory" factory-method="createStudent"> </bean></beans>工厂实例化的应用场景工厂实例化作用:便于程序员自定义创建对象应用场景:初始化数据库数据时,对数据库明码进行解密,将数据源搁置到容器中,进步安全性 ...

May 13, 2021 · 5 min · jiezi