关于spring:如何安全的开放后端接口

前言当今互联网Web各种利用H5、Android、ios、web、小程序等开发时大都采纳前后端拆散架构,公司为了商业变现会凋谢本人零碎接口给其它公司应用。例如: 调用微信领取。 既然波及到前后端拆散,前端页面调用后端API接口,那么接口的平安设计是十分重要的一项工作。我的项目的架构师在我的项目布局过程中,会着重思考平安,最常见的平安问题就是,用户在挪动端提交数据向后端传输,黑客在传输过程中拦挡提交的数据,进行篡改,进而达到伪造申请数据的目标。 例如前端提交金额,商品编号信息,黑客中途拦挡,批改成低价商品,而后申请下单,早年间国内某电商技术不成熟时,抓包剖析下单是很常见的。这时如果咱们对一些惯例的我的项目能够通过申请数据报文进行签名、加密、加盐、加工夫戳、后端依据数据再次加密,与报文中的签名进行比照是否统一来管制接口平安,这种做法在大厂我的项目中也是罕用手法。 什么是加密解密加密:数据加密的根本过程,就是对原来为明文,用户输出的数据通过某种解决,变成一串不可间接提取信息的代码,相似于英文字母加阿拉伯数字组合,通常称之为 密文。在和平年代的电报发报加密成密文,对方电台人员收到电文,依据约定的密码本进行破译便可失去明文,这就是为什么密码本对一个军队如此重要。解密:加密的逆过程,也就是破译电报。常见的加密算法加密技术通常分为三大类:对称式、非对称式、散列算法。 对称式:艰深的说就是锁上一把锁与关上这把锁,用的都是同一样一把钥匙。常见的对称加密算法有:DES、3DES、AES等非对称式:俗名公开秘钥加密算法,它须要一对代码,一个为公钥 (public key)、另一个为私钥(private key) 加密解密用的不是一个秘钥,所以被称之非对称加密。 应用公钥对明文加密,有且只有对应的私钥能力解开密文。应用私钥对明文加密,有且只有对应的公钥能力解开密文。大多数做法:公钥加密,私钥解密,公钥会在加密前发放给解密方。 例子:Git 中ssh连贯Github,本地电脑生成public key,与private key,将public key提前配置到GitHub账户中,private key留在本地,上传文件时Git便会自动识别认证身份。常见的非对称性加密算法:RSA、DSA 等 散列算法:次要用于验证,避免信息被修。具体用处如:文件校验、数字签名、鉴权协定。常见的Hash散列算法:MD5、SHA1、SHA256、HMAC等等 MD5: MD5是一种不可逆的加密算法,目前是最可靠的加密算法之一,尚没有可能逆运算的程序被开发进去,它对应任何字符串都能够加密成一段惟一的固定长度的代码。其余算法介绍查看连贯详情 应用MD5算法凋谢接口加密验签实现需要剖析: 内部利用调用接口,做到极简丝滑调用。接口提供方零碎不能影响原有业务。对接口需求方提交的数据进行校验,若不非法在接口被申请前就应终止这一次申请。合乎支流大厂接口凋谢形式。实现思路: 接口提供方给接口需求方也就是第三方公司发放appid、secret,并要求严格保存。在零碎内新建一个单干公司表,应用UUID生成appid与secret,对单干公司进行增删改查。简略 在此文章中略接口需求方应用约定的MD5算法将appid利用惟一辨认、secret秘钥、timestamp工夫戳、nonce随机数、业务参数、生成sign签名并一起传递给接口提供方。接口提供方接管获取appid、secret、timestamp、nonce、并一一判断是否为空,为空就进行申请,并给第三方敌对提醒。接口提供方获取第三方公司提交的timestamp与以后零碎工夫做比照,如果差值大于120秒,则timestamp有效,如果差值小于120秒,则timestamp无效。目标是避免过期的提交。依据第三方提交的appid查询数据库内secret,与提交的secret进行比照,这一步能够依据appid判断权限 高级做法接口提供方获取第三方公司提交的nonce,比拟redis中存储的nonce,不统一则通过。避免暴力申请接口。接口提供方将获取的appid、secret、timestamp、nonce、业务参数通过MD5算法运算失去sign2,与第三方公司提交的sign比照,如果不统一则为不非法申请。将nonce存入redis,过期工夫设置为120秒。上代码pom.xml 引入依赖 <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </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> <!-- Shiro+JWT start --> <dependency> <groupId>org.apache.shiro</groupId> <artifactId>shiro-spring</artifactId> <version>1.5.1</version> </dependency> <dependency> <groupId>com.auth0</groupId> <artifactId>java-jwt</artifactId> <version>3.10.1</version> </dependency> <!-- Shiro+JWT end --> <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson --> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.75</version> </dependency> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-lang3</artifactId> </dependency> <!-- XSS --> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-text</artifactId> <version>1.8</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <excludes> <exclude> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </exclude> </excludes> </configuration> </plugin> </plugins> </build>MD5签名算法 ...

May 13, 2021 · 5 min · jiezi

关于spring:Bean注解详解

@Bean注解详解Bean 用法@Bean示意办法产生一个由Spring治理的bean,属性的名称语义与Spring XML中的<bean> 标签配置的一样 public MyBean myBean() { // instantiate and configure MyBean obj return obj; }当应用name属性可用时,用于确定bean名称的默认策略是应用办法的名称。这是不便和直观的,然而如果须要显式命名,则能够在注解上应用 name 属性(或其别名{value})。 另请留神 name承受一个字符串数组,容许为单个bean应用多个名称(即主bean名称加上一个或多个别名)。 @Bean({"b1", "b2"}) // bean available as 'b1' and 'b2', but not 'myBean' public MyBean myBean() { // instantiate and configure MyBean obj return obj; }Bean 加强@Bean正文不提供Profile, Scope, Lazy, DependsOn, Primary, Order性能 意思是,如果要在bean上配置Profile, Scope, Lazy, DependsOn, Primary, Order这些的时候,不再是用属性了,而是在该bean上应用它们对应的注解 @Bean@Profile("production")@Scope("prototype")@Order(-100)public MyBean myBean() { // instantiate and configure MyBean obj return obj;}上述正文的语义与它们在组件类级别的用法相匹配: ...

May 12, 2021 · 4 min · jiezi

关于spring:spring-aop通知的各种配置方式

编入的优先级优先级最高的会最先被织入,在退出连接点的时候,具备最高的优先级的最初被织入 @Before注解的配置形式参数args外面能够放多个, args0的参数能够穿对象当然理论切入点调用的办法传的参数要跟这里的参数能匹配的上能力执行这个加强办法 @After注解的配置形式同@Before一样@Around注解的配置形式其中proceedingJoinPoint.proceed();是指标办法

May 12, 2021 · 1 min · jiezi

关于spring:阿里一面如何实现Spring的XML-schema-扩展

引言自从SpringBoot时代的到来,去除了Spring的各种繁琐的XML配置,让咱们能够腾出双手以便于更加专一的搬砖。记得那时候刚学Spring的时候,每天被Spring的各种XMl配置文件折磨的不行,每引入一个新的框架,最放心的就是jar抵触、哪个配置文件又配的不对、配置文件没有起作用。所以每次搭建好一个我的项目就把配置文件用小笔记记录下来,不便下次在整合我的项目的时候间接copy复制就好。上面咱们就以Spring整合dubbo的事例看下 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://dubbo.apache.org/schema/dubbo" xmlns="http://www.springframework.org/schema/beans" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"> <dubbo:application name="demo-provider"/> <dubbo:registry address="zookeeper://127.0.0.1:2181"/> <dubbo:protocol name="dubbo" port="20890"/> <bean id="demoService" class="org.apache.dubbo.samples.basic.impl.DemoServiceImpl"/> <dubbo:service interface="org.apache.dubbo.samples.basic.api.DemoService" ref="demoService"/></beans>上述代码中咱们有看到dubbo自定义了一套本人的标签,<dubbo:application> ,<dubbo:registry> ,<dubbo:protocol>,<dubbo:service>咱们心中是不是有点小疑难:这些标签在Spring我的项目启动的时候是如何被Spring治理的?是怎么被Spring来辨认的?如果咱们本人轻易定义一个标签Spring是否可能辨认?咱们去翻翻Spring的官网发现这玩意其实就是Spring提供的 XML schema 的扩大反对。只有依照它的步骤来,咱们就能够配置任何咱们自定义的标签。XML schema 扩大机制是什么?这个兴许好多人没听过: Spring 为基于 XML 构建的利用提供了一种扩大机制,用于定义和配置 Bean。 它容许使用者编写自定义的 XML bean 解析器,并将解析器自身以及最终定义的 Bean 集成到 Spring IOC 容器中。咱们能够看看官网https://docs.spring.io/spring...10.2. XML Schema Authoring 这个是次要介绍它的。 如何实现一个自定义 XML 扩大官网有介绍,要实现一个自定义的XML Schema 总共须要4步: 编写一个 XML schema 文件形容的你节点元素。编写一个 NamespaceHandler 的实现类编写一个或者多个 BeanDefinitionParser 的实现 (关键步骤).注册上述的 schema 和 handler。既然只有依照这四步来,那咱们就照着这个文档来本人实现一个。 Authoring the Schema编写一个javajr.xsd 放入我的项目的resources/META-INF文件夹外面(这个也能够是其余路劲) <?xml version="1.0" encoding="UTF-8" standalone="no"?><xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:tool="http://www.springframework.org/schema/tool" xmlns="https://www.javajr.cn/schema/javajr" targetNamespace="https://www.javajr.cn/schema/javajr"> <xsd:import namespace="http://www.springframework.org/schema/beans"/> <xsd:element name="application"> <xsd:complexType> <xsd:complexContent> <xsd:extension base="beans:identifiedType"> <xsd:attribute name="website" type="xsd:string" use="required"/> <xsd:attribute name="weixin" type="xsd:string" use="required"/> </xsd:extension> </xsd:complexContent> </xsd:complexType> </xsd:element></xsd:schema>targetNamespace="https://www.javajr.cn/schema/javajr" 这里targetNamespace的地址前面有用到。这里咱们就定义了一个元素application 外面有两个属性别离为website和weixin。 ...

May 9, 2021 · 2 min · jiezi

关于spring:springboot启动出现DialectResolutionInfo

明天遇见了一个奇怪的问题,springboot2.0版本启动报这个错 Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set。

May 7, 2021 · 1 min · jiezi

关于spring:强推Java大牛熬夜一周梳理的-Spring-IOC笔记收藏一波

Hello,明天给各位童鞋们分享Spring IOC,连忙拿出小本子记下来吧! 1. IoC原理IoC全称Inversion of Control,直译为管制反转。 为什么要应用IoC? 咱们假设一个在线书店,通过BookService获取书籍: public class BookService { private HikariConfig config = new HikariConfig();private DataSource dataSource = new HikariDataSource(config);public Book getBook(long bookId) { try (Connection conn = dataSource.getConnection()) { ... return book; }}} 为了从数据库查问书籍,BookService持有一个DataSource。为了实例 HikariDataSource,又不得不实例化一个HikariConfig。当初,咱们持续编 UserService获取用户: public class UserService { private HikariConfig config = new HikariConfig();private DataSource dataSource = new HikariDataSource(config);public User getUser(long userId) { try (Connection conn = dataSource.getConnection()) { ... return user; }}} 因为UserService也须要拜访数据库,因而,咱们不得不也实例化一个HikariDataSource。 每一次调用办法, 都须要实例化一个HikariDataSource,容易造成资源节约。如果用某种办法实现了共享资源,那么怎么确保在所有性能残缺的状况下,销毁以开释资源呢? ...

May 7, 2021 · 3 min · jiezi

关于spring:清华学霸手抄万字-Spring-MVC笔记真香堪称完美教程

Hello,明天给各位童鞋们分享Spring MVC,连忙拿出小本子记下来吧! 1.SpringMVC概述1.1 三层架构三层架构:体现层:负责数据展现业务层:负责业务解决数据层:负责数据操作 1.2 MVCMVC(Model View Controller),一种用于设计创立web应用程序体现层的模式Model(模型):数据模型,用户封装数据View(视图):页面视图,用户展现数据 jsphtmlController(控制器):解决用户交互的调度器,用于依据用户需要处理程序逻辑ServletSpringMVC2.入门案例2.1 入门案例制作(重点)XML版 XML+注解版(主体)纯注解版(变形)基于Spring环境开发步骤:导入坐标<dependencies><!--servlet3.1标准坐标--><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>3.1.0</version><scope>provided</scope></dependency><!--jsp坐标--><dependency><groupId>javax.servlet.jsp</groupId><artifactId>jsp-api</artifactId><version>2.1</version><scope>provided</scope></dependency><!--spring坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.1.9.RELEASE</version><scope>provided</scope></dependency><!--springmvc坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.1.9.RELEASE</version><scope>provided</scope></dependency><!--spring web坐标--><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.1.9.RELEASE</version><scope>provided</scope></dependency></dependencies><!--构建--><build><!--设置插件--><plugins><!--具体的插件配置--><plugin><groupId>org.apache.tomcat.maven</groupId><artifactId>tomcat7-maven-plugin</artifactId><version>2.1</version><configuration><port>80</port><path>/</path></configuration></plugin></plugins></build>2.定义业务层处理器Controller,并配置成spring的bean(等同于Servlet) 该bean的解决须要应用独立的配置文件扫描(XML版):spring-mvc.xml 3.web.xml中配置SpringMVC外围控制器,用于将申请转发到对应的具体业务处理器Controller中(等同于Servlet配置) 4.设定具体Controller的拜访路劲(等同于Servlet在web.xml中的配置),并设置返回页面 此处记录一个问题:问题景象:org.apache.catalina.core.ContainerBase.addChildInternal ContainerBase.addChild: start: org.apache.catalina…问题解决:查看web.xml是否无误,门路等是否有误,一个servlet不能同时应用xml和注解配置,否则也很会呈现该谬误;查看我的项目lib目录是否存在,idea默认不创立,进入Project Structures(ctrl+shift+alt+S)–>点击左侧Project Settings下的Artifacts。点击两头栏我的项目,这里会两个文件,一个是:我的项目名:war(war压缩包),一个是我的项目名:war exploded(war未压缩包)。点击war exploded我的项目,在右侧中第一栏Output Layout(我的项目公布生成的文件)下,开展WEB-INF文件夹,此时该目录下只有classes目录,无lib目录,本人手动创立一个lib目录,并点击下面+抉择Library File增加maven导入的jar包重启tomcat就解决了(我是这样解决的,不保障所有相似问题都能解决) 2.2 入门案例工作流程剖析(重点)服务器启动:加载web.xml中的DispatcherServlet读取spring-mvc.xml中的配置,加载所有com.itheima包中所有标记为bean的类读取bean中办法上标注@RequestMapping("/save")的内容解决申请:DispatcherServlet配置拦挡所有申请 /应用申请门路与所有加载的@RequestMapping的内容进行比对执行对应的办法依据办法的返回值在webapp目录中查找对应的页面并展现 2.3 SpringMVC技术架构图(重点)SpringMVC技术架构图: DispatcherServlet:前端控制器,是整体流程控制中心,由其调用其余组件解决用户的申请,无效的升高了组件间的耦合性HandleMapping:处理器映射器,负责依据用户申请找到对应具体的Handler处理器Handler:处理器,业务解决的外围类,通常由开发者编写,形容具体的业务HandlerAdapter:处理器适配器,通过它对处理器进行执行ViewResolver:视图解析器,将处理结果生成View视图View:视图,最终产出后果,罕用视图入jsp、html 3.根底配置3.1 Controller加载管制(重点)SpringMVC的处理器对应bean必须依照标准格局开发,为防止退出有效的bean可通过bean加载过滤器进行蕴含设定或排除设定,体现层bean标注通常设为@Controller<context:component-scan base-package="com.itheima"><context:include-filtertype="annotation"expression="org.springframework.stereotype.Controller"/></context:component-scan>此处记录一个问题:问题景象:启动tomcat实现时,idea弹窗提醒“http://localhost:80/找不到应...解决办法:点击idea主界面运行图标旁下拉列表,抉择Edit Configuration,进入配置界面,Open Browser抉择一个固定浏览器,不要应用默认的浏览器(tomcat启动实现主动应用该浏览器拜访)制作案例:和后面案例一样,只需批改spring-mvc.xml配置文件:<context:component-scan base-package="com.itheima"><!--增加过滤,蕴含该注解才会被扫描到--><context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/></context:component-scan>bean加载管制阐明:业务层与数据层bean加载由spring管制,参照spring课程加载形式体现层bean加载由SpringMVC独自管制体现层处理器bean应用注解@Controller申明bean加载管制应用蕴含性过滤器过滤器类型为通过注解进行过滤过滤的注解名称为Controller 3.2 动态资源加载外围控制器拦挡的是所有申请,须要对非一般资源申请进行放行,通过配置放行资源实现<mvc:resources mapping="/img/**" location="/img/"/><mvc:resources mapping="/js/**" location="/img/"/><mvc:resources mapping="/css/**" location="/img/"/>应用简化格局能够放行所有一般资源调用,无需一一枚举<mvc:default-servlet-handler/>还是之前案例内容,只需做如下批改:在webap下新建img目录,并导入一张图片批改spring-mvc.xml配置文件: 留神:webapp下img目录需设置为resources目录,否则无法访问 3.3 中文乱码解决SpringMVC提供专用的中文字符过滤器,用于解决乱码问题。在web.xml进行如下配置: 3.4 注解驱动应用注解模式转化SpringMVC外围配置文件为配置类@Configuration@ComponentScan(value = "com.itheima",excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,classes = Controller.class))public class SpringMvcConfiguration {}操作过程:创立配置类 基于servlet3.0标准,自定义servlet容器初始化配置类,加载SpringMVC外围配置类动态资源加载过滤(注解版)配置类实现WebMvcConfigurer接口,笼罩addResourceHandlers办法,在其中对具体的资源进行设定@Overridepublic void addResourceHandlers(ResourceHandlerRegistry registry) {registry.addResourceHandler("/img/**").addResourceLocations("/img/");registry.addResourceHandler("/js/**").addResourceLocations("/js/");registry.addResourceHandler("/css/**").addResourceLocations("/css/");}或者笼罩configureDefaultServletHandling办法,应用Servlet默认过滤规定@Overridepublic void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {configurer.enable();}4.中文乱码解决(注解版)Servlet3.0标准启动服务器时做的工作通过实现ServletContainerInitializer接口,在onStartup办法中实现,包含监听器注册、过滤器注册等4.申请4.1 申请参数(重点)SpringMVC将传递的参数封装到处理器办法的形参中,达到快速访问参数的目标拜访URL:http://localhost/requestParam...@RequestMapping("/requestParam")public String requestParam(String name) {System.out.println("name = "+name);return "page.jsp";}申请参数类型:一般类型参数POJO类型参数数组类型参数汇合类型参数一般类型参数参数名与处理器办法形参名保持一致拜访URL:http://localhost/requestParam... ...

May 7, 2021 · 2 min · jiezi

关于spring:Spring-aop-通过获取代理对象实现事务切换

Spring aop 通过获取代理对象实现事务切换在我的项目中,波及到同一个类中一个办法调用另外一个办法,并且两个办法的事务不相干。这外面波及到一个事务切换的问题,通过spring aop类外面的AopContext类获取以后类的代理对象,这样就能切换对应的事务管理器了,具体做法如下: 1. 配置1.1 在applicationContext.xml文件中配置如下:<!-- 开启裸露Aop代理到ThreadLocal反对 --> <aop:aspectj-autoproxy expose-proxy="true"/> 1.2 在所在类或者启动类上配置@EnableAspectJAutoProxy注解@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true) 2. 应用办法在须要切换的中央获取代理对象,再调用对应的办法,如下: ((类名) AopContext.currentProxy()).办法(); 3. 留神点AopContext.currentProxy()的实质是应用的ThreadLocal生成本地代理,这样的做法可能影响性能。被代理对象应用的办法必须是public类型的办法,不然获取不到代理对象,会报上面的谬误: java.lang.IllegalStateException: Cannot find current proxy: Set 'exposeProxy' property on Advised to 'true' to make it available.开启裸露AOP代理即可。 因为开启事务和事务回滚,理论这个过程是aop代理帮忙实现的,当调用一个办法时,它会先查看时候有事务,有则开启事务。 当调用本类的办法时,它并没有将其视为proxy调用,而是办法的间接调用,所以也就没有查看该办法是否含有事务这个过程, 那么本地办法调用的事务也就有效了。 4. demo@EnableAspectJAutoProxy(proxyTargetClass = true, exposeProxy = true)public class TransactionTest { /** * 办法A没事务 * * @param * @return void */ public String A(){ System.out.println("A办法执行开始!!!"); //生成TransactionTest代理类,再调用B办法,B的事务就回独自失效,并且异样回滚 String result = ((TransactionTest)AopContext.currentProxy()).B(); System.out.println("A办法执行完结!!!"); return result; } /** * 事务B独自的事务 * 应用代理,同一类中A办法调用B办法事务能够失效 * @param * @return void */ @Transactional(rollbackFor = Exception.class) public String B(){ return "事务B触发胜利!!!"; }}

May 6, 2021 · 1 min · jiezi

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

接上篇【熟练掌握spring框架第一篇】 spring依赖注入是怎么实现的依赖注入的形式有哪些基于结构器注入 setter-based injection基于set办法注入 constructor-based injection属性注入 field-based injection为什么spring举荐应用结构器注入参考文章: https://blog.marcnuri.com/fie... https://www.javacodegeeks.com... 英文原文档:https://docs.spring.io/spring... 应用结构器注入能够保障强依赖能够保障对象不可变如果结构器参数过多,就要思考是否对象承当了太多了职责,是否应该进行职责拆分。应用setter办法注入就不容易发现能够升高容器耦合度,不便单元测试。应用setter办法注入暗藏了组件之间的依赖关系。结构器注入 如上图:组件A通过结构器注入组件B留神:在 Spring4.x 中减少了新的个性:如果类只提供了一个带参数的构造方法,则不须要对其外部的属性写 @Autowired 注解,Spring 会主动为你注入属性。 产生在refreshContext阶段初始化单例bean调用bean工厂的getBean触发创立bean创立bean主动拆卸构造方法创立依赖的beanfield-based injection 如上图:组件A通过属性注入B 依然产生在refreshContext阶段初始化单例bean创立bean填充bean执行每个InstantiationAwareBeanPostProcessor的postProcessProperties包含AutowiredAnnotationBeanPostProcessor执行bean工厂的resolveDependency生成依赖bean利用反射设置属性setter办法注入 如上图:组件A应用setter办法注入组件B 能够看出setter办法注入和属性注入流程是一样的。惟一不同的是属性注入是通过调用Field的set办法,而setter办法注入是调用Method的invoke办法。 小结通过下面的学习,咱们曾经对spring的外围性能依赖注入有了一个全面的理解。另外置信读者对整个bean创立流程也有了一个大略的理解,spring框架依照天然程序加载每个类,如果有依赖的组件,会间接创立依赖的组件。下面抉择的几个例子都是比较简单易懂的。spring轻松搞定。那如果组件A依赖B,组件B依赖A这种循环依赖的状况,spring是怎么解决的呢。上面咱们就来一起揭秘spring为了解决循环依赖都做了什么? spring循环依赖和三级缓存参考资料: https://www.cnblogs.com/lwh10... https://blog.csdn.net/zhouyu5... https://juejin.cn/post/684490... 首先咱们还是演示一下呈现循环依赖导致程序启动失败的一个demo 如上图:A组件通过结构器注入B,B组件通过结构器注入A 运行报错: 提醒很明确了。A和B的依赖造成了一个环。咱们再来看下报错的地位。 用个流程图解释下大略就是这么回事。 整个过程没故障!接下来咱们看看三级缓存是怎么回事。DefaultSingletonBeanRegistry有三个map。 一级缓存singletonFactories二级缓存earlySingletonObjects三级缓存singletonFactories 整个缓存应用的过程大抵如上图所示。 比如说A依赖B,B又依赖A,那么在populateBean(a)的时候会去创立B,而后创立B的时候又会去调getBean(a),此时调用getSingleton(a)就会从之前的三级缓存里去找A 找到之后并且将A移入到二级缓存中。留神此时A还是未初始化的bean是不能进入一级缓存的。B创立好了并填充到A,而后初始化A,接着调用getSingleton(a)从二级缓存中找到A,最初执行addSingleton(a)增加到一级缓存,并从二级缓存中移除。 问题1:给B填充的是未初始化的A,那B是不是有问题。答案是否定的。尽管在创立B时会提前给B注入了一个还未初始化的A对象,然而在创立A的流程中始终应用的是注入到B中的A对象的援用,之后会依据这个援用对A进行初始化,所以这是没有问题的。 问题2:三级缓存为什么是个ObjectFactory咱们看下工厂的getObject办法的实现。如果条件hasInstantiationAwareBeanPostProcessors不满足的话,和二级缓存是没有什么区别的。hasInstantiationAwareBeanPostProcessors这个条件是干啥的呢?这和spring aop无关。如果开启的话,会返回一个代理后的对象。而不是实例化阶段创立的对象。 protected Object getEarlyBeanReference(String beanName, RootBeanDefinition mbd, Object bean) { Object exposedObject = bean; if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) { for (SmartInstantiationAwareBeanPostProcessor bp : getBeanPostProcessorCache().smartInstantiationAware) { exposedObject = bp.getEarlyBeanReference(exposedObject, beanName); } } return exposedObject;}未完待续,更多内容请关注【熟练掌握spring框架】第三篇 ...

May 6, 2021 · 1 min · jiezi

关于spring:SpringMVC常用注解前后端分离

1 Spring MVC的职责阐明:本文中框架间接应用Spring Boot,因而除了特地阐明,都应用默认配置。并且只解说相干操作,不波及深刻的原理。咱们能够将前后端开发中的各个组成部分做一个形象,它们之间的关系如下图所示: 在浏览器-服务器的交互过程中,Spring MVC起着“邮局”的作用。它一方面会从浏览器接管各种各样的“来信”(HTTP申请),并把不同的申请分发给对应的服务层进行业务解决;另一方面会发送“回信”(HTTP响应),将服务器解决后的后果回应给浏览器。 因而,开发人员就像是“邮递员”,次要须要实现三方面工作: 指定散发地址:应用@RequestMapping等注解指定不同业务逻辑对应的URL。接管申请数据:应用@RequestParam等注解接管不同类型的申请数据。发送响应数据:应用@ResponseBody等注解发送不同类型的响应数据。本文波及到的相干依赖: <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.1.4</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>在介绍Spring MVC这三方面的工作内容之前,咱们先来看一下如何应用@Controller或@RestController标注XxxController类。 @Controller:package com.xianhuii.controller;import org.springframework.stereotype.Controller;@Controllerpublic class StudentController {}最根底的做法是应用@Controller注解将咱们的XxxController类申明为Spring容器治理的Controller,其源码如下。 package org.springframework.stereotype;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Componentpublic @interface Controller { @AliasFor(annotation = Component.class) String value() default "";}@Controller的元注解是@Component,它们的性能雷同,只不过@Controller显得更加有语义,便于开发人员了解。 此外,须要留神的是@Controller头上@Target的值是ElementType.Type,阐明它只能标注在类上。 @Controller有且仅有一个value属性,该属性指向@Component注解,用来批示对应的beanName。如果没有显式指定该属性,Spring的自动检测组件会将首字母小写的类名设置为beanName。即下面实例代码StudentController类的beanName为studentController。 @RestController:package com.xianhuii.controller;import org.springframework.web.bind.annotation.RestController;@RestControllerpublic class StudentController {}在前后端拆散的开发环境下,@RestController是开发人员更好的抉择。它除了具备上述@Controller申明Controller的性能外,还能够主动将类中所有办法的返回值绑定到HTTP响应体中(而不再是视图相干信息),其源码如下。 package org.springframework.web.bind.annotation;import java.lang.annotation.Documented;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;import org.springframework.core.annotation.AliasFor;import org.springframework.stereotype.Controller;@Target({ElementType.TYPE})@Retention(RetentionPolicy.RUNTIME)@Documented@Controller@ResponseBodypublic @interface RestController { @AliasFor( annotation = Controller.class ) String value() default "";}@RestController的元注解包含@Controller和@ResponseBody,别离起着申明Controller和绑定办法返回值的作用。 此外,须要留神的是@RestController头上@Target的值也是ElementType.Type,阐明它只能标注在类上。 @Controller有且仅有一个value属性,该属性指向@Controller注解(最终指向@Component),用来批示对应的beanName。如果没有显式指定该属性,Spring的自动检测组件会将首字母小写的类名设置为beanName。即下面实例代码StudentController类的beanName为studentController。 2 指定散发地址映射申请散发地址的注解以@Mapping为根底,并有丰盛的实现: 2.1 @RequestMapping2.1.1 标注地位@RequestMapping是最根底的指定散发地址的注解,它既能够标注在XxxController类上,也能够标注在其中的办法上。实践上有三种组合形式:类、办法和类+办法。然而,实际上只有前面两种形式能起作用。 ...

May 5, 2021 · 14 min · jiezi

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

前言熟练掌握这个词置信很多同行在写简历的时候都用到过。熟练掌握这四个字是依据每个人程度不一样,了解也不一样。比方一个刚毕业的大学生,他可能也会在简历外面写熟练掌握spring框架,但实际上他并没有看过spring源码,也没有太多实际。可能只是看了几本书,应用spring框架写了一个毕业设计,而后就说本人熟练掌握spring框架了。再比方一个有二三年工作教训的求职者,也在简历外面说本人熟练掌握spring框架,他的理由是他看过spring 的一些关键性代码了,也在理论工作中应用spring开发了工程项目。甚至本人依据一些业务须要,写了几个不错的针对spring框架的扩大。再比方一个工作了四五年的程序员,他在本人简历上写熟练掌握spring框架。因为他把spring和spring boot源码大部分都看过,工作中解决了不少波及原理性的问题。对spring框架的整个架构图,根本了然于胸。另外总结出了十分多的spring相干的最佳实际。再比方一个工作了七八年的程序员,他们对spring的了解可能又不太一样。其实我想说的是,程度和教训决定了一个人的见解。每个人都应该虚心学习丰盛和坚固本人的常识体系,拓宽常识的广度,增强常识的深度。这样才会在无限的工夫外面,成长成参天大树。好了,废话不多说,明天的主题是熟练掌握spring框架。那么我就从n个问题着手。说一下工作了n年的我对熟练掌握的见解吧。 BeanFactory和FactoryBean的区别首先说下BeanFactory他是springIOC容器的顶层接口。负责管理和生产Bean。他的默认实现是:DefaultListableBeanFactory。在spring boot我的项目启动时,执行createApplicationContext()后返回的实例类型是AnnotationConfigApplicationContext,能够看下该类的层次结构图。发现他也是BeanFactory接口的实现类。代码是在ApplicationContext的抽象类AbstractApplicationContext中,能够看出所有BeanFactory的实现还是调用了GenericApplicationContext的成员DefaultListableBeanFactory beanFactory 的具体实现。所以Bean治理的外围代码天然就是DefaultListableBeanFactory。 查看它的类层次结构图发现,这个类次要表演两个角色,第一个就是bean工厂,第二个就是BeanDefinition的注册核心。bean工厂提供了对单例bean注册核心(DefaultSingletonBeanRegistry),FactoryBean注册核心(FactoryBeanRegistrySupport)的反对。首先咱们看下他是如何实现bean工厂的。 注册单例bean应用@Component注解定义一个UserService 断点设置在DefaultSingletonBeanRegistry单例bean注册核心的addSingleton中。 看下调用栈: 产生在refreshContext阶段对单例模式bean进行初始化通过getBean触发实例化userService ,getBean进而调用doGetBeandoGetBean办法能够略微开展下 它调用了DefaultSingletonBeanRegistry的getSingleton 其中第二个参数就是ObjectFactory,创立bean的工厂ObjectFactory 理论调用的是AbstractAutowireCapableBeanFactory的createBean办法,进而调用它的doCreateBean,接着调用createBeanInstance办法,进而调用instantiateBean办法,应用SimpleInstantiationStrategy 策略类(理论应用的java反射技术动静创建对象)创立实例。 获取单例bean还是方才那个例子。在ApplicationRunner的run办法调用getBean办法。 在doGetBean办法里会首先检查单利缓存外面是否有,如果有的话,间接返回。 BeanDefinition注册咱们再来看下BeanDefinition注册核心是如何实现的。咱们依然是在DefaultListableBeanFactory的registerBeanDefinition设置断点,看下调用栈。 同样产生在refreshContext阶段执行所有的BeanFactoryPostProcessor执行所有的BeanDefinitionRegistryPostProcessor,此处默认就一个名为:ConfigurationClassPostProcessor此办法次要工作就是首先调用ConfigurationClassUtils.checkConfigurationClassCandidate找到所有ConfigurationClass的候选人,而后应用ConfigurationClassParser解析每个ConfigurationClass。如果是ComponentScans的ConfigurationClass,就调用ClassPathBeanDefinitionScanner的doScan进行扫描。将扫描的BeanDefinition 增加到DefaultListableBeanFactory 的 beanDefinitionMap里。至此实现BeanDefinition的注册。FactoryBean说了这么多,置信读者对spring框架的BeanFactory有了一个比拟全面的理解了。上面聊聊FactoryBean。FactoryBean是spring 容器里的一种非凡的bean。 该接口的实现以BeanFactory为工厂。假如某个Bean实现了该接口,它用作生产对象的工厂。而不是像一般的Bean那样间接裸露本人。通常应用getObject办法裸露bean,FactoryBean反对单例和原型,并且能够能够按需提早或者在启动的时候创建对象。这个接口在spring框架外部大量应用,比方AOP的 org.springframework.aop.framework.ProxyFactoryBean jpa的org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean,然而在理论业务代码外面并不常见。 起源java doc 上面就以JpaRepositoryFactoryBean具体介绍下FactoryBean是如何工作的。 读者能够翻看下之前的一篇文章【JpaRepositoryBean创立流程剖析】此处对于repository bean的初始化就不具体介绍了。咱们还是在ApplicationRunner的run办法里调用ApplicationContext的getBean获取一个UserRepository,如果是第一次获取,流程是这样的。 调用BeanFactory的getBean办法。理论逻辑在AbstractBeanFactory的doGetBean办法里。依据beanName userRepository在SingletonBeanRegistry里找到相应的JpaRepositoryFactoryBean而后依据在FactoryBeanRegistry 的缓存里(factoryBeanObjectCache)依据beanName查找userRepository,因为是第一次,所以找不到。调用FactoryBeanRegistrySupport的getObjectFromFactoryBean办法。该办法有个同步代码块,目标是保障并发状况下,创立的对象依然是单例的。同步代码块里回去调用JpaRepositoryFactoryBean的getObject办法获取之前就曾经创立好的repository,而后退出到factoryBeanObjectCache中去并返回相应的bean小结通过源码学习,咱们发现BeanFactory和FactoryBean是两个齐全不同的概念,然而他们的代码又是严密关联的。FactoryBean是BeanFactory里的一种非凡bean,因为他自身也是一个工厂,能够生产本人的Bean ,有个非凡的中央须要咱们留神一下。如果传入的beanName是以&为前缀的话。会调用BeanFactoryUtils的transformedBeanName办法,去掉前缀,而后在Singleton注册核心获取相应的bean。如果找不到的话会有很长的一段代码进行解决。这里就不做深入探讨了,感兴趣的读者能够钻研下。 BeanPostProcessor和BeanFactoryPostProcessor两个类都以PostProcessor结尾,中文名为后置处理器,意思就在Bean创立或者BeanFactory创立之后进行的操作。spring外围代码外面PostProcessor随处可见。 首先咱们来看下BeanFactoryPostProcessor,来张UML图。这个图很简略,BeanFactoryPostProcessor定义了一个办法。参数就是刚刚创立的bean工厂。而BeanDefinitionRegistryPostProcessor定义的办法,参数就是刚刚创立好的BeanDefinition注册核心。 咱们以一个最具备代表性的类ConfigurationClassPostProcessor 看下它是何时调用的,它做了什么。咱们将断点设置在它的postProcessBeanFactory和postProcessBeanDefinitionRegistry办法里。发现产生在spring启动的refreshContext阶段,此时Bean工厂曾经创立了。先调用所有的BeanDefinitionRegistryPostProcessor再调用所有BeanFactoryPostProcessor。详见PostProcessorRegistrationDelegate的invokeBeanFactoryPostProcessors。ConfigurationClassPostProcessor两次都会被调到。 public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called on this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + registry); } this.registriesPostProcessed.add(registryId); //外围逻辑在processConfigBeanDefinition()办法中,用来解决BeanDefinition的注册 processConfigBeanDefinitions(registry);}public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) { int factoryId = System.identityHashCode(beanFactory); if (this.factoriesPostProcessed.contains(factoryId)) { throw new IllegalStateException( "postProcessBeanFactory already called on this post-processor against " + beanFactory); } this.factoriesPostProcessed.add(factoryId); if (!this.registriesPostProcessed.contains(factoryId)) { // BeanDefinitionRegistryPostProcessor hook apparently not supported... // Simply call processConfigurationClasses lazily at this point then. processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory); } // 对加了@Configuration注解的配置类进行Cglib加强代理 enhanceConfigurationClasses(beanFactory); // 增加一个BeanPostProcessor后置处理器 beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}ConfigurationClassPostProcessor无疑是spring中最重要也是最简单的后置处理器了,此处就不具体开展了。再来看下BeanPostProcessor的UML图。 ...

May 5, 2021 · 1 min · jiezi

关于spring:77道Spring面试题及答案整理2021最新版

Spring的77道常问面试题和答案大汇总(2021版),分享给大家,心愿对你们有帮忙哈~ 本文77道Spring面试题和答案的PDF版曾经为大家筹备好了,关注微信公众号:Java团长,而后发送“ spring001 ”即可获取哈~ 一、Spring概述1. 什么是spring?Spring是一个轻量级Java开发框架,最早有Rod Johnson创立,目标是为了解决企业级利用开发的业务逻辑层和其余各层的耦合问题。它是一个分层的JavaSE/JavaEE full-stack(一站式)轻量级开源框架,为开发Java应用程序提供全面的基础架构反对。Spring负责基础架构,因而Java开发者能够专一于应用程序的开发。 Spring最基本的使命是解决企业级利用开发的复杂性,即简化Java开发。 Spring能够做很多事件,它为企业级开发提供给了丰盛的性能,然而这些性能的底层都依赖于它的两个外围个性,也就是依赖注入(dependency injection,DI)和面向切面编程(aspect-oriented programming,AOP)。 为了升高Java开发的复杂性,Spring采取了以下4种要害策略 基于POJO的轻量级和最小侵入性编程;通过依赖注入和面向接口实现松耦合;基于切面和常规进行申明式编程;通过切面和模板缩小样板式代码。2. Spring框架的设计指标,设计理念,和外围是什么?Spring设计指标:Spring为开发者提供一个一站式轻量级利用开发平台; Spring设计理念:在JavaEE开发中,反对POJO和JavaBean开发方式,使利用面向接口开发,充沛反对OO(面向对象)设计办法;Spring通过IoC容器实现对象耦合关系的治理,并实现依赖反转,将对象之间的依赖关系交给IoC容器,实现解耦; Spring框架的外围:IoC容器和AOP模块。通过IoC容器治理POJO对象以及他们之间的耦合关系;通过AOP以动静非侵入的形式加强服务。 IoC让相互协作的组件放弃涣散的耦合,而AOP编程容许你把遍布于利用各层的性能分离出来造成可重用的性能组件。 3. Spring的优缺点是什么?长处①. 不便解耦,简化开发 Spring就是一个大工厂,能够将所有对象的创立和依赖关系的保护,交给Spring治理。 ②. AOP编程的反对 Spring提供面向切面编程,能够不便的实现对程序进行权限拦挡、运行监控等性能。 ③. 申明式事务的反对 只须要通过配置就能够实现对事务的治理,而无需手动编程。 ④. 不便程序的测试 Spring对Junit4反对,能够通过注解不便的测试Spring程序。 ⑤. 不便集成各种优良框架 Spring不排挤各种优良的开源框架,其外部提供了对各种优良框架的间接反对(如:Struts、Hibernate、MyBatis等)。 ⑥. 升高JavaEE API的应用难度 Spring对JavaEE开发中十分难用的一些API(JDBC、JavaMail、近程调用等),都提供了封装,使这些API利用难度大大降低。 毛病Spring明明一个很轻量级的框架,却给人感觉大而全Spring依赖反射,反射影响性能应用门槛升高,入门Spring须要较长时间4. Spring有哪些利用场景利用场景:JavaEE企业应用开发,包含SSH、SSM等 Spring价值: Spring是非侵入式的框架,指标是使利用程序代码对框架依赖最小化;Spring提供一个统一的编程模型,使利用间接应用POJO开发,与运行环境隔离开来;Spring推动利用设计格调向面向对象和面向接口开发转变,进步了代码的重用性和可测试性;5. Spring由哪些模块组成?Spring 总共大概有 20 个模块, 由 1300 多个不同的文件形成。 而这些组件被别离整合在外围容器(Core Container) 、 AOP(Aspect Oriented Programming)和设施反对(Instrmentation) 、数据拜访与集成(Data Access/Integeration) 、 Web、 音讯(Messaging) 、 Test等 6 个模块中。 以下是 Spring 5 的模块结构图: ...

May 2, 2021 · 5 min · jiezi

关于spring:太厉害了终于有人把Spring条件注解讲明白了送你上岸

Hello,明天给各位童鞋们分享Spring条件注解,连忙拿出小本子记下来吧! 条件注解条件注解就是在满足肯定条件下,配置才会失效。咱们以Linux和Windows操作系统为例,在Linux零碎下查看目录命令为ls,在Windos零碎下目录命令为dir,来实现依据以后操作系统实现不同的性能。1、显示目录命令接口先定义一个显示目录命令的接口。 2、实现接口别离实现Linux下和Windows下的实例 实现接口Condition实现Linux和Windows下的条件 配置Bean 测试 结果显示 多环境切换在公司开发中,咱们须要在开发、生产环境下进行疾速切换,这时能够是用Spring中的Profile来解决这个问题,如下: 能够看出Profile注解底层也是Condition实现的,接下来定义一个数据源类DataSource 配置Bean 加载配置类,测试后果 会发现,当设置prod环境时,数据源为prod环境下的参数。证实实现了多环节切换。好啦,明天的文章就到这里,心愿能帮忙到屏幕前迷茫的你们!

April 30, 2021 · 1 min · jiezi

关于spring:Spring-全家桶Java之Sping-MVC-SpringBoot常见面试题总结附答案

Spring 全家桶100问,常见面试题总结,一共分为三篇,本篇为第1篇!一,Spring boot 有哪些形式能够实现热部署? 应用 devtools 启动热部署,增加 devtools 库,在配置文件中把 spring. devtools. restart. enabled 设置为 true; 应用 Intellij Idea 编辑器,勾上主动编译或手动从新编译。 109.jpa 和 hibernate 有什么区别? jpa 全称 Java Persistence API,是 Java 长久化接口标准,hibernate 属于 jpa 的具体实现。 二,Spring boot 外围配置文件是什么? 配置文件有 . properties 格局和 . yml 格局,它们次要的区别是书法格调不同。properties 配置如下: spring. RabbitMQ. port=5672yml 配置如下: spring: RabbitMQ: port: 5672yml 格局不反对 @PropertySource 注解导入。三,为什么要用 spring boot? 配置简略独立运行主动拆卸无代码生成和 xml 配置提供利用监控易上手晋升开发效率四,什么是 spring boot? spring boot 是为 spring 服务的,是用来简化新 spring 利用的初始搭建以及开发过程的。 ...

April 29, 2021 · 3 min · jiezi

关于spring:Spring-Cloud-Stream-体系及原理介绍

简介: Spring Cloud Stream在 Spring Cloud 体系内用于构建高度可扩大的基于事件驱动的微服务,其目标是为了简化音讯在 Spring Cloud 应用程序中的开发。 作者 | 洛夜起源 | 阿里巴巴云原生公众号 Spring Cloud Stream在 Spring Cloud 体系内用于构建高度可扩大的基于事件驱动的微服务,其目标是为了简化音讯在 Spring Cloud 应用程序中的开发。 Spring Cloud Stream (前面以 SCS 代替 Spring Cloud Stream) 自身内容很多,而且它还有很多内部的依赖,想要相熟 SCS,必须要先理解 Spring Messaging 和 Spring Integration 这两个我的项目,接下来,文章将围绕以下三点进行开展: 什么是 Spring Messaging什么是 Spring Integration什么是 SCS 体系及其原理 本文配套可交互教程已登录阿里云知口头手实验室,PC 端登录 start.aliyun.com_ _在浏览器中立刻体验。 Spring MessagingSpring Messaging 是 Spring Framework 中的一个模块,其作用就是对立音讯的编程模型。 比方音讯 Messaging 对应的模型就包含一个音讯体 Payload 和音讯头 Header:package org.springframework.messaging;public interface Message<T> { T getPayload(); MessageHeaders getHeaders();}音讯通道 MessageChannel 用于接管音讯,调用send办法能够将音讯发送至该音讯通道中: ...

April 29, 2021 · 3 min · jiezi

关于spring:面试突击spring看这一篇就够了给你总结的清新脱俗

Spring及IOC介绍Spring简介Rod Johnson,Spring Framework创始人,驰名作者。很难设想Rod Johnson的学历,真的让好多人大吃一惊,他是悉尼大学的博士,然而他的业余不是计算机,而是音乐学。Spring理念:使现有的技术更加容易应用,自身是一个大杂烩,整合了现有的技术框架!SSH:Struct2 + Spring + Hibernate!SSM:SpringMVC + Spring + Mybatis!依赖导入: <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.3.5</version> </dependency>总结:Spring就是一个轻量级的管制反转(IOC)和面向切面编程(AOP)的框架 毛病:“配置繁琐,配置天堂” 所以SpringBoot就进去了IOC:以前程序主动权在程序员手中,用户需要得通过程序员批改,IOC就是扭转这种伎俩,将控制权交到用户手中:管制反转。大大解除了程序的耦合性,这样程序员就能够更加专一于业务层开发 IOC创立及重点配置新建Maven我的项目,导入依赖,编写实体类@Datapublic class Student { private String name; private int id;编写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 https://www.springframework.org/schema/beans/spring-beans.xsd"><!-- 实体类映射--><!-- 默认应用无参构造函数创建对象,--><!-- 若要应用有参,须要用<constructor-arg--> <bean id="student" class="com.song.pojo.Student"> <constructor-arg name="name" value="mike"/> <constructor-arg name="id" value="2"/> <property name="name" value="hehe"/> </bean><!-- <bean id="..." class="...">--></beans>测试public class myTest { @Test public void test(){ ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml"); Student student = (Student) applicationContext.getBean("student"); System.out.println(student.toString()); }}思考:对象是如何创立的?Spring主动创建对象,由Spring容器给对象属性赋值这个过程就称为管制反转管制:谁来管制对象的创立,传统应用程序的对象是由程序自身管制创立的,应用Spring后,对象是由Spring来创立的。反转:程序自身不创建对象,变成被动的承受Spring容器创立的对象容器通过set办法进行属性值的注入对象由Spring来创立,治理,拆卸! Spring重点配置!<!-- id:bean的惟一标识符,也就是相当于咱们学的对象名 class:bean对象所对应的全限定名:包名+类名 name:也是别名,而且name能够同时取多个别名 --> <bean id="userT" class="com.kuang.pojo.UserT" name="user2 u2,u3;u4"> <property name="name" value="吴亦凡"/> </bean> <import resource=baen.xml/>依赖注入以及主动装Bean当实体类呈现简单属性时如下: ...

April 28, 2021 · 4 min · jiezi

关于spring:spring中那些让你爱不释手的代码技巧

前言最近越来越多的读者认可我的文章,还是件挺让人快乐的事件。有些读者私信我说心愿前面多分享spring方面的文章,这样可能在理论工作中派上用场。正好我对spring源码有过肯定的钻研,并联合我这几年理论的工作教训,把spring中我认为不错的知识点总结一下,心愿对您有所帮忙。 一 如何获取spring容器对象1.实现BeanFactoryAware接口`@Service``public class PersonService implements BeanFactoryAware {` `private BeanFactory beanFactory;` `@Override` `public void setBeanFactory(BeanFactory beanFactory) throws BeansException {` `this.beanFactory = beanFactory;` `}` `public void add() {` `Person person = (Person) beanFactory.getBean("person");` `}``}``复制代码`实现BeanFactoryAware接口,而后重写setBeanFactory办法,就能从该办法中获取到spring容器对象。 2.实现ApplicationContextAware接口`@Service``public class PersonService2 implements ApplicationContextAware {` `private ApplicationContext applicationContext;` `@Override` `public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {` `this.applicationContext = applicationContext;` `}` `public void add() {` `Person person = (Person) applicationContext.getBean("person");` `}``}``复制代码`实现ApplicationContextAware接口,而后重写setApplicationContext办法,也能从该办法中获取到spring容器对象。 3.实现ApplicationListener接口`@Service``public class PersonService3 implements ApplicationListener<ContextRefreshedEvent> {` `private ApplicationContext applicationContext;` `@Override` `public void onApplicationEvent(ContextRefreshedEvent event) {` `applicationContext = event.getApplicationContext();` `}` `public void add() {` `Person person = (Person) applicationContext.getBean("person");` `}``}``复制代码`实现ApplicationListener接口,须要留神的是该接口接管的泛型是ContextRefreshedEvent类,而后重写onApplicationEvent办法,也能从该办法中获取到spring容器对象。 ...

April 28, 2021 · 5 min · jiezi

关于spring:一坨一坨的-ifelse-参数校验终于被-SpringBoot-参数校验组件整干净了

本文曾经收录进 SpringBootGuide (SpringBoot2.0+从入门到实战!) Github地址:https://github.com/CodingDocs/springboot-guide码云地址:https://gitee.com/SnailClimb/springboot-guide(Github无法访问或者访问速度比较慢的小伙伴能够看码云上的对应内容)数据的校验的重要性就不用说了,即便在前端对数据进行校验的状况下,咱们还是要对传入后端的数据再进行一遍校验,防止用户绕过浏览器间接通过一些 HTTP 工具间接向后端申请一些守法数据。 最一般的做法就像上面这样。咱们通过 if/else 语句对申请的每一个参数一一校验。 @RestController@RequestMapping("/api/person")public class PersonController { @PostMapping public ResponseEntity<PersonRequest> save(@RequestBody PersonRequest personRequest) { if (personRequest.getClassId() == null || personRequest.getName() == null || !Pattern.matches("(^Man$|^Woman$|^UGM$)", personRequest.getSex())) { } return ResponseEntity.ok().body(personRequest); }}这样的代码,小伙伴们在日常开发中肯定不少见,很多开源我的项目都是这样对申请入参做校验的。 然而,不太倡议这样来写,这样的代码显著违反了 繁多职责准则。大量的非业务代码混淆在业务代码中,十分难以保护,还会导致业务层代码繁杂! 实际上,咱们是能够通过一些简略的伎俩对下面的代码进行改良的!这也是本文次要要介绍的内容! 废话不多说!上面我会联合本人在我的项目中的理论应用教训,通过实例程序演示如何在 SpringBoot 程序中优雅地的进行参数验证(一般的 Java 程序同样实用)。 不理解的敌人肯定要好好看一下,学完马上就能够实际到我的项目下来。 并且,本文示例我的项目应用的是目前最新的 Spring Boot 版本 2.4.5!(截止到 2021-04-21) 示例我的项目源代码地址:https://github.com/CodingDocs/springboot-guide/tree/master/source-code/bean-validation-demo 。 增加相干依赖如果开发一般 Java 程序的的话,你须要可能须要像上面这样依赖: <dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.9.Final</version></dependency><dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>3.0.0</version></dependency><dependency> <groupId>org.glassfish.web</groupId> <artifactId>javax.el</artifactId> <version>2.2.6</version></dependency>不过,置信大家都是应用的 Spring Boot 框架来做开发。 基于 Spring Boot 的话,就比较简单了,只须要给我的项目增加上 spring-boot-starter-web 依赖就够了,它的子依赖蕴含了咱们所须要的货色。另外,咱们的示例我的项目中还应用到了 Lombok。 ...

April 27, 2021 · 4 min · jiezi

关于spring:Java-synchronized对象级别与类级别的同步锁

Java synchronized 关键字 能够将一个代码块或一个办法标记为同步代码块。同步代码块是指同一时间只能有一个线程执行的代码,并且执行该代码的线程持有同步锁。synchronized关键字能够作用于 一个代码块一种办法当一个办法或代码块被申明为synchronized时,如果一个线程正在执行该synchronized 办法或代码块,其余线程会被阻塞,直到持有同步锁的线程开释。依据锁定的范畴能够分为 类级别的锁能够避免多个线程在运行时同时进入该类所有实例化对象的 synchronized代码块中。对象级别的锁能够避免多个线程在运行时同时进入以后(或某一个)实例化对象的 synchronized代码块中。1. 对象级别的同步锁对象级别的同步锁:当咱们想要在多线程环境下同步执行一个非静态方法或非动态代码块时,在类的办法或代码块加上synchronized关键字,能够保障对象实例级别数据的线程平安。(比拟后文的类级别的同步锁,回头来了解这句话) 对象级别的加锁的代码如下,如:在办法上加锁,锁对象为以后类的实例化对象 public class DemoClass{ public synchronized void demoMethod(){}}如:为代码块加锁,锁对象为this对象 public class DemoClass{ public void demoMethod(){ synchronized (this){ //同步代码块 } }}如:为代码块加锁,锁对象为咱们创立的任意一个对象。不要应用非final的成员变量作为同步锁对象,因为非final成员变量能够被从新赋值,导致不同的线程应用不同的对象作为锁,达不到同步锁定的成果。 public class DemoClass{ //留神这里的关键字final十分重要,看阐明 private final Object lock = new Object(); public void demoMethod(){ synchronized (lock){ //同步代码块 } }}2. 类级别的同步锁类级别的锁能够避免多个线程在运行时进入该类所有实例化对象的 "synchronized块中。也就是说如果运行时有100个DemoClass的实例,那么每次只有一个线程可能在任何一个实例中执行demoMethod(),所有其余实例的所有其余线程都被锁定。 为了保障静态数据线程平安,应该应用类级别的锁定。咱们晓得static关键字将办法的数据关联到类的级别上,所以在静态方法上应用锁。 静态方法加锁,对该类所有的实例化对象失效 public class DemoClass{ //静态方法加锁,对该类所有的实例化对象失效 public synchronized static void demoMethod(){ }}获取 .class类的援用,类级别的锁 public class DemoClass{ public void demoMethod(){ //获取 .class类的援用,类级别的锁,对该类所有的实例化对象失效 synchronized (DemoClass.class){ //同步代码块 } }}应用动态对象的锁,类级别的锁 ...

April 26, 2021 · 1 min · jiezi

关于spring:SSM整合

Spring整合SpringMVC,MyBatis一 Spring整合SpringMVC 思路:首先让Spring和SpringMVC各自可能运行,而后再整合到一起第一步:搭建Spring环境1.引入坐标(pom.xml) <!--版本锁定--><properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.0.2.RELEASE</spring.version> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <mysql.version>5.1.6</mysql.version> <mybatis.version>3.4.5</mybatis.version> </properties><!--导入坐标--> <dependencies> <!-- spring --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>compile</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- log start --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- log end --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> <type>jar</type> <scope>compile</scope> </dependency> </dependencies>2.编写Spring的配置文件:applicationContext.xml将配置文件都放在rescourse目录下,便于管理 ...

April 25, 2021 · 2 min · jiezi

关于spring:python数据可视化-matplotlibpyplot函数绘制散点图

微信公众号:Python 集中营简略的事件反复做,反复的事件保持做,保持的事件用心做;你的必定是我保持的能源,如果这篇文章对你有帮忙,点个关注吧! 相干依赖库 1# -*- coding: UTF-8 -*- 2 3''' 4散点图 5''' 6# matplotlib 数据可视化库 7 8import matplotlib.pyplot as plt 910# 科学计算库1112import numpy as np根本散点图 1''' 2根本散点图 3''' 4 5# 随机生成10个数作为X轴 6 7x = np.random.rand(10) 8 9# 随机生成10个数作为Y轴1011y = np.random.rand(10)1213# 绘制散点图1415# plt.scatter(x, y)1617# 展现图1819# plt.show()散点图参数设置 1''' 2散点图参数设置 3''' 4# 设置10个散点的大小(列表形式) 5 6size_ = [10,20,30,40,50,60,70,80,90,100] 7 8# s参数,设置每个散点的大小 910# plt.scatter(x, y,s=size_)1112# 设置色彩(列表形式)1314color_ = [1,2,3,4,5,6,7,8,9,10]1516# c参数,设置每个散点的色彩1718# plt.scatter(x, y,s=size_,c=color_)1920# alpha参数,设置透明度2122# plt.scatter(x, y,s=size_,c=color_,alpha=0.5)2324# 设置散点形态,marker='o',marker='^'2526# plt.scatter(x, y,s=size_,c=color_,alpha=0.5,marker='^')2728# 设置标签名称2930plt.scatter(x, y,s=size_,c=color_,alpha=0.5,marker='^',label='triangle')3132# 增加图例3334plt.legend(loc='best')3536# 展现图3738plt.show() ...

April 24, 2021 · 1 min · jiezi

关于spring:SSM整合

Spring整合SpringMVC,MyBatis一 Spring整合SpringMVC 思路:首先让Spring和SpringMVC各自可能运行,而后再整合到一起第一步:搭建Spring环境1.引入坐标(pom.xml) <!--版本锁定--><properties> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> <spring.version>5.0.2.RELEASE</spring.version> <slf4j.version>1.6.6</slf4j.version> <log4j.version>1.2.12</log4j.version> <mysql.version>5.1.6</mysql.version> <mybatis.version>3.4.5</mybatis.version> </properties><!--导入坐标--> <dependencies> <!-- spring --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.6.8</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>compile</scope> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>${mysql.version}</version> </dependency> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.0</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <!-- log start --> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>${log4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-api</artifactId> <version>${slf4j.version}</version> </dependency> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>${slf4j.version}</version> </dependency> <!-- log end --> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>${mybatis.version}</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>1.3.0</version> </dependency> <dependency> <groupId>c3p0</groupId> <artifactId>c3p0</artifactId> <version>0.9.1.2</version> <type>jar</type> <scope>compile</scope> </dependency> </dependencies>2.编写Spring的配置文件:applicationContext.xml将配置文件都放在rescourse目录下,便于管理 ...

April 23, 2021 · 2 min · jiezi

关于springboot:四登录验证使用自定义界面登录

2、自定义登录界面(thymeleaf) 1、html文件,放在resources/templates下为前面权限校验做筹备,权限校验看五、的security配置类 1) login.html <!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title></head><body><form action="/login" method="post"> 用户名 <input type="text" name="username"><br>明码 <input type= "password" name="password"><input type="submit" value="提交"></form> </body></html> 2)失败页面<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"><title>Title</title></head><body>失败的页面返回登录</body></html> 3)胜利页面<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"><title>Title</title></head><body><div> <h3>首页</h3><a href="/Vip1">领有vip1 能力看的</a><br><a href="/Vip2">领有vip2 能力看的</a><br><a href="/role">领有abc 能力看的</a></div></body></html> 4)Vip1.html页面<!DOCTYPE html><html lang="en"><head> <meta charset="UTF-8"><title>Title</title></head><body>这是 vip1 角色能力看见的信息</body></html> 2、controller文件 @Controller@RequestMapping()public class UserController { // 登录页,跳转到/templates/login.html页面@RequestMapping("/login")public String login() { return "login";}// 首页,跳转到/templates/index.html页面@RequestMapping("/home")public String index() { return "home";}@RequestMapping("/toFail")public String toFail(){ return "fail";}} 3、 security配置 /** SpringSecurity的配置Created by macro on 2018/4/26. */@Configuration@EnableWebSecurity@EnableGlobalMethodSecurity(prePostEnabled=true)public class SecurityConfig extends WebSecurityConfigurerAdapter { //申请权限配置@Overrideprotected void configure(HttpSecurity http) throws Exception { http.formLogin().loginPage("/login") // 放行userController中的login办法/login是对应办法名哦 .loginProcessingUrl("/login") // 必须和表单提交 action 的名字 一样的,提交 username 和password // 设置登陆胜利调用的接口,在springmvc中返回的值是string的话会先去找对应字符串名称的html,如果找不到就会抛出异样 .successForwardUrl("/home") //重定向 .successHandler(new AuthenticationSuccessHandler() {@Overridepublic void onAuthenticationSuccess(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException { httpServletResponse.sendRedirect("https://www.baidu.com");}}) ...

April 23, 2021 · 1 min · jiezi

关于spring:三登录验证使用security自带login界面登录

1、据用户名获取用户是否存在同时设置用户的 权限实现了:UserDetailsService 接口 /** @author :wenye@date :Created in 2021/4/7 11:49@description:校验身份@version: 1.0.0$ */@Componentpublic class MyuserDetailsService implements UserDetailsService { private Logger logger = LoggerFactory.getLogger(MyuserDetailsService.class);@Overridepublic UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { //据用户名查找用户信息,用户名和明码 //AuthorityUtils.commaSeparatedStringToAuthorityList("admin") 把字符串转化为对应的对象,以逗号分隔字符串 logger.info("用户名为:{}",username); return new User(username,"123456", AuthorityUtils.commaSeparatedStringToAuthorityList("admin"));}} 2、校验用户名和明码是否匹配 ,实现了PasswordEncoder 接口/** 用于编码明码的服务接口的实现类。 */@Componentpublic class MyPasswordEncoder implements PasswordEncoder { private Logger logg = LoggerFactory.getLogger(MyPasswordEncoder.class);/** * 编码原始明码。通常,良好的编码算法利用SHA-1或更大的哈希与8字节或更大的随机生成的盐相结合。 * @param rawPassword 明码,一个可读的字符值序列 * @return */@Overridepublic String encode(CharSequence rawPassword) { logg.info("原始明码:{}", rawPassword); return rawPassword.toString();}/** * 验证从存储中取得的编码明码是否与提交的原始明码匹配。如果明码匹配,返回true;如果不匹配,返回false。存储的明码自身永远不会被解码。 * @param rawPassword 预设的验证明码。要编码和匹配的原始明码 * @param encodedPassword 表单输出的明码。来自存储的编码明码与之比拟 * @return */@Overridepublic boolean matches(CharSequence rawPassword, String encodedPassword) { logg.info("预设的验证明码:{}", rawPassword); logg.info("表单输出的明码:{}", encodedPassword); return encodedPassword.equals(rawPassword.toString());}} ...

April 23, 2021 · 1 min · jiezi

关于springboot:二spring-security基本原理

1、前两个绿色的第一个:过滤器次要是用来校验用户名和明码信息,第二个查看申请头中是否有basic信息。2、exceptionTranslationFilter 次要是用来过滤在这个流程中,抛出的异样该进行什么解决3、FilterSecurityInterceptor这个用来是读取你在config(Http)中所进行的配置 Security 可扩大的有鉴权失败处理器验证器登录胜利处理器投票器自定义token解决过滤器登出胜利处理器登录失败处理器自定义 UsernamePasswordAuthenticationFilter 鉴权失败处理器Security 鉴权失败默认跳转登录页面,咱们能够实现 AccessDeniedHandler 接口,重写 handle() 办法来自定义解决逻辑;而后参考配置类阐明将处理器退出到配置当中。 验证器实现 AuthenticationProvider 接口来实现本人验证逻辑。须要留神的是在这个类外面就算你抛出异样,也不会中断验证流程,而是算你验证失败,咱们由流程图晓得,只有有一个验证器验证胜利,就算验证胜利,所以你须要注意这一点。 登录胜利处理器在 Security 中验证胜利默认跳转到上一次申请页面或者门路为 “/” 的页面,咱们同样能够自定义:继承 SimpleUrlAuthenticationSuccessHandler 这个类或者实现 AuthenticationSuccessHandler 接口。我这里倡议采纳继承的形式,SimpleUrlAuthenticationSuccessHandler 是默认的处理器,采纳继承能够符合里氏替换准则,进步代码的复用性和防止不必要的谬误。 投票器投票器可继承 WebExpressionVoter 或者实现 AccessDecisionVoter接口;WebExpressionVoter 是 Security 默认的投票器;我这里同样倡议采纳继承的形式;增加到配置的形式参考 上文; 留神:投票器 vote 办法返回一个int值;-1代表拥护,0代表弃权,1代表赞成;投票管理器收集投票后果,如果最终后果大于等于0则放行该申请。 自定义token解决过滤器自定义 token 处理器继承自 OncePerRequestFilter 或者 GenericFilterBean 或者 Filter 都能够,在这个处理器外面须要实现的逻辑是:获取申请里的 token,验证 token 是否非法而后填充 SecurityContextHolder ,尽管说过滤器只有增加在投票器之前就能够,但我这里还是倡议增加在 http.addFilterAfter(new MyFittler(), LogoutFilter.class); 登出胜利处理器实现LogoutSuccessHandler接口,增加到配置的形式参考上文。 登录失败处理器登录失败默认跳转到登录页,咱们同样能够自定义。继承 SimpleUrlAuthenticationFailureHandler 或者实现 AuthenticationFailureHandler,倡议采纳继承。 自定义UsernamePasswordAuthenticationFilter咱们自定义UsernamePasswordAuthenticationFilter能够极大进步咱们 Security的灵活性(比方增加验证验证码是否正确的性能)。 咱们间接继承 UsernamePasswordAuthenticationFilter ,而后在配置类中初始化这个过滤器,给这个过滤器增加登录失败处理器,登录胜利处理器,登录管理器,登录申请 url 。 这里配置稍微简单,贴一下代码清单 初始化过滤器: MyUsernamePasswordAuthenticationFilte getAuthenticationFilter(){ MyUsernamePasswordAuthenticationFilter myUsernamePasswordAuthenticationFilter = new MyUsernamePasswordAuthenticationFilter(redisService); myUsernamePasswordAuthenticationFilter.setAuthenticationFailureHandler(new MyUrlAuthenticationFailureHandler()); myUsernamePasswordAuthenticationFilter.setAuthenticationSuccessHandler(new MyAuthenticationSuccessHandler()); myUsernamePasswordAuthenticationFilter.setFilterProcessesUrl("/sign_in"); myUsernamePasswordAuthenticationFilter.setAuthenticationManager(getAuthenticationManager()); return myUsernamePasswordAuthenticationFilter; } ...

April 23, 2021 · 1 min · jiezi

关于sentinel:聊聊因不恰当使用alibaba-sentinel而踩到的坑

前言sentinel 是面向分布式服务架构的流量管制组件,次要以流量为切入点,从限流、流量整形、熔断降级、零碎负载爱护、热点防护等多个维度来帮忙开发者保障微服务的稳定性。自从hytrix 2018年进入保护状态,再到springcloud 2020.0版本hytrix被移除,就能够预想将来一段时间springcloud全家桶的熔断降级组件基本上的首选就是alibaba sentinel。 明天就来聊聊因不失当应用alibaba sentinel,而导致熔断降级生效的一些例子。因为sentinel还在一直迭代更新中,不同版本会有一些差别,而且在版本的迭代中,有些问题可能也曾经修复。 本文演示的版本应用的sentinel-dashboard是1.8.0。应用springcloud alibaba的版本为2.2.3.RELEASE 生效场景例子1、降级不失效问题a、起因剖析我的项目中应用了自定义全局异样解决,而异样数或者异样比例的统计在 com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor.afterCompletion这个办法执行,自定义全局异样的解决会先于 com.alibaba.csp.sentinel.adapter.spring.webmvc.AbstractSentinelInterceptor.afterCompletion这个办法执行执行,因为咱们在全局异样外面曾经对异样进行解决,比方转换为一个对象,这样导致AbstractSentinelInterceptor.afterCompletion无奈获取到异样,进而无奈统计异样数或者异样比例。 b、解决方案在官网的issue中曾经有网友提出了解决思路https://github.com/alibaba/Sentinel/issues/1281和https://github.com/alibaba/Sentinel/issues/404 因为我是在查issue的之前,就通过源码跟踪,找到答案,这边说下我的实现思路。我的思路是定义一个切面,在切面的AfterThrowing进行异样统计。因为切面会在全局异样之前执行。统计的源码我是间接把sentinel统计的源拷贝过去,外围代码如下 @Aspect@Component@Slf4jpublic class StatisticsExceptionCountAspect { @Autowired @Lazy private BaseWebMvcConfig baseWebMvcConfig; @Pointcut("execution(* com.github.lybgeek.sentinel.controller..*.*(..))") public void pointcut(){ } @AfterThrowing(pointcut = "pointcut()",throwing = "ex") public void afterAfterThrowing(Throwable ex){ log.info("statisticsExceptionCount..."); traceException(ex); } /** * 统计异样 * @param ex */ private void traceException(Throwable ex) { Entry entry = getEntryInRequest(); if (entry != null) { Tracer.traceEntry(ex, entry); } } protected Entry getEntryInRequest() { RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes(); ServletRequestAttributes attributes = (ServletRequestAttributes)requestAttributes; HttpServletRequest request = attributes.getRequest(); Object entryObject = request.getAttribute(baseWebMvcConfig.getRequestAttributeName()); return entryObject == null ? null : (Entry)entryObject; }}2、受权规定不失效问题a、起因剖析我的项目中没有实现 ...

April 21, 2021 · 1 min · jiezi

关于golang:手撸golang-仿spring-iocaop-之10-增强1

手撸golang 仿spring ioc/aop 之10 加强1 缘起最近浏览 [Spring Boot技术底细: 架构设计与实现原理] (朱智胜 , 2020.6)本系列笔记拟采纳golang练习之Talk is cheap, show me the code. SpringSpring的次要个性:1. 管制反转(Inversion of Control, IoC)2. 面向容器3. 面向切面(AspectOriented Programming, AOP)源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标参考spring boot罕用注解,应用golang编写“基于注解的动态代码增强器/生成器”子目标(Day 10)代码扫描差不多了,接下来思考如何加强Q:子目标?A:以最罕用的@RestController注解为例,革新代码实现ioc注入/aop钩子,bean注册,gin整合Q:具体?A:一种性能加强会波及到一族相干注解,比方RestController通常波及RequestMapping, GetMapping和PostMapping。而简直所有的Bean都会用到Autowired。Q:如何实现,组织和扩大?A:凡扩大必对应链式/表式构造。零碎将定义增强器接口,以及多个增强器的实现。每种增强器辨认一个外围注解(如RestController)及其相干注解(如GetMapping),并增加相干代码到代码模型中。Q:谁来运行和调用这些增强器?A:原本想着做成standlone app,当初看来,间接在我的项目中集成增强器的包,写个xx_test.go并运行可能更不便。Q:难点是什么?A:毫无疑问,Autowired是要害。在什么范畴,如何匹配Implementation,循环依赖,以及适合的初始化。Q:如何折衷?A:答案简直总是简化问题模型。Q:如何开始?A:第一步,是给所有代码模型增加Clone()办法,为增强器提供资料。domain/IClonable.go克隆(深度拷贝)接口 package domaintype IClonable interface { Clone() interface{}}domain/StructInfo.go所有代码模型实现IClonable接口,这里以StructInfo为例。 func (me *StructInfo) Clone() interface{} { it := new(StructInfo) it.CodeFile = me.CodeFile it.LineNO = me.LineNO it.Name = me.Name it.Fields = make([]*FieldInfo, len(me.Fields)) for i,v := range me.Fields { it.Fields[i] = v.Clone().(*FieldInfo) it.Fields[i].Struct = it } it.Methods = make([]*MethodInfo, len(me.Methods)) for i,v := range me.Methods { it.Methods[i] = v.Clone().(*MethodInfo) it.Methods[i].Struct = it } it.Annotations = make([]*AnnotationInfo, len(me.Annotations)) for i,v := range me.Annotations { it.Annotations[i] = v.Clone().(*AnnotationInfo) } return it}(未完待续) ...

April 18, 2021 · 1 min · jiezi

关于spring:SpringBoot增强库-yuelibrary-232发布优雅实现密钥交换加解密

yue-library简介yue-library是一个基于SpringBoot封装的加强库内置丰盛的JDK工具主动拆卸了一系列的根底Bean与环境配置项疾速构建SpringCloud我的项目,让微服务变得更简略版本更新日志此版本重点实现:密钥替换加解密、加强Bean转换能力、JDBC新增Elasticsearch-SQL、达梦、PostgreSQL方言。 新个性【base】ParamUtils提醒优化,增加谬误起因【base】增加JSONListConverter类型转换器从而反对List<JSONObject>类型解决(JDBC实体数据库查问映射时JSONArray格局文本数据不反对映射成List<JSONObject>)【base】优化fastjson bean转换的jsonstr辨认形式【base】加强DateUtils与标准UUID工具类为IdUtils并优化IdUtils实现【base】加强fastjson JavaBean转换能力,反对Character类型【base】MapUtils加强值提取,反对list依据key提取map提取值反对map、fastjson pulls !17【crypto】新增重磅个性-密钥替换加密:反对@RequestDecrypt注解实现申请主动解密【crypto】新增重磅个性-密钥替换加密:反对@ResponseEncrypt注解实现响应内容加密【crypto】密钥替换加密:默认提供本地Map与Redis两种替换密钥存储计划【crypto】密钥替换加密:@RequestDecrypt与@ResponseEncrypt注解反对应用替换密钥加密或自定义密钥等个性【web】修复ApiVersion注解minimumVersion值等于的状况下410【web】优化响应后果处理器在规范HTTP状态码时的空值解决【web】新增ServletUtils.getAuthToken()办法,获取申请中的OAuth2 Token【webflux】修复ApiVersion注解minimumVersion值等于的状况下410【jdbc】对jdbc方言实现进行欠缺与优化,新增Elasticsearch-SQL、达梦、PostgreSQL方言【jdbc】db.queryForObject 自动识别Bean类型与简略类型【jdbc】参数丑化加强反对JSONArray数据类型与List<JSONObject>数据类型【jdbc】优化多行查问后果转换为单行查问后果实现【jdbc】所有mappedClass查询方法自动识别所需RowMapper类型,实现JavaBean、map、根本类型后果主动匹配【jdbc】标准外部局部常量命名与移除分页中不优雅的泛型实例PageTVO【jdbc】加强主动方言辨认,依据驱动类自动识别所需方言类型【jdbc】默认Db Bean实现依据不同驱动类型,应用对应方言配置【jdbc】优化DAO实现,形象根底DAO【jdbc】优化所有jdbc办法正文,形容更简洁,表白更清晰,正文更标准【jdbc】删除晚期存在的局部过期办法【es】反对配置ConnectTimeout与SocketTimeout,并调大各自默认值为25与15秒Bug修复【base】修复fastjson JavaBean转换BUG #3688【jdbc】修复isDataSize()办法可能因为数据库存在多行数据,而返回false的隐患【jdbc】修复因谬误测试而删除的参数类型丑化(现已反对:Character、JSONObject、LocalDateTime进行非凡转换解决与布尔值映射辨认)Maven仓库理论公布版本号j8.2.3.2、j11.2.3.2 要害pom.xml依赖: 依赖版本spring-boot2.3.8.RELEASEspring-cloudHoxton.SR10spring-cloud-alibaba2.2.5.RELEASEhutool5.6.3fastjson1.2.76工程构造. yue-library├── yue-library 根底库│ ├── yue-library-dependencies 父pom│ ├── yue-library-base 根底库提供了丰盛的Java工具包,同时也主动拆卸了一系列根底Bean等│ ├── yue-library-base-crypto 基于Hutool实现的加解密模块,提供诸如数据脱敏此类的更多个性│ ├── yue-library-web 根底库WebMvc实现,用于servlet我的项目│ ├── yue-library-webflux 根底库WebFlux实现,用于响应式编程我的项目(如:SpringCloudGateway)│ ├── yue-library-data-jdbc 基于SpringJDBC进行二次封装,领有着弱小性能的同时又不失简略、灵便等│ ├── yue-library-data-redis 基于SpringRedis进行二次封装,更简略灵便,提供全局token与登录相干个性等│ ├── yue-library-auth-service 基于SpringSecurity进行二次封装,更简略灵便,提供全局token与登录等个性│ ├── yue-library-auth-client auth-client为auth-service客户端模块,提供获取以后登录用户状态信息等个性│ ├── yue-library-pay 基于pay-java-parent进行二次封装,让你真正做到一行代码实现领取聚合│ ├── yue-library-cloud-oss│ └── yue-library-cloud-sms├── yue-library-samples 根底库示例│ ├── yue-library-test yue-library代码测试项目:单元测试、接口测试、代码示例│ ├── yue-library-test-webflux yue-library-webflux代码测试项目:单元测试、接口测试、代码示例│ ├── yue-library-template-simple yue-library模版:SpringBoot我的项目模版│ └── yue-library-template-ssc yue-library模版:SpringCloud我的项目模版,SOA共享架构(阿里巴巴中台)└── yue疾速开始引入我的项目依赖maven我的项目,在pom.xml文件中增加如下一段代码,并将${version}替换为对应版本号: <parent> <groupId>ai.ylyue</groupId> <artifactId>yue-library-dependencies</artifactId> <version>${version}</version></parent>随后引入所须要的模块,如WebMvc我的项目引入:yue-library-web ...

April 18, 2021 · 1 min · jiezi

关于spring-mvc:SpringMVC-异常处理体系深入分析

@[toc]SpringMVC 中针对异样问题有一套残缺的解决体系,这套体系十分好用,明天松哥就花点工夫来和大家聊一聊 SpringMVC 中的异样解决体系,咱们把 SpringMVC 中的异样体系从头到尾梳理一下。 1.异样解析器概览在 SpringMVC 的异样体系中,处于最顶层的大 Boss 是 HandlerExceptionResolver,这是一个接口,里边只有一个办法: public interface HandlerExceptionResolver { @Nullable ModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);}resolveException 办法就用来解析申请处理过程中所产生的异样,并最终返回一个 ModelAndView。 咱们来看下 HandlerExceptionResolver 的实现类: 间接实现 HandlerExceptionResolver 接口的类有三个: HandlerExceptionResolverComposite:这个一看又是一个组合,在最近的源码剖析中咱们曾经屡次见到 xxxComposite 了,这里就不再赘述。DefaultErrorAttributes:这个用来保留异样属性。AbstractHandlerExceptionResolver:这个的子类比拟多: - SimpleMappingExceptionResolver:通过提前配置好的异样类和 View 之间的对应关系来解析异样。- AbstractHandlerMethodExceptionResolver:解决应用 `@ExceptionHandler` 注解自定义的异样类型。- DefaultHandlerExceptionResolver:依照不同类型来解决异样。- ResponseStatusExceptionResolver:解决含有 `@ResponseStatus` 注解的异样。在 SpringMVC 中,大抵的异样解析器就是这些,接下来咱们来一一学习这些异样解析器。 2.AbstractHandlerExceptionResolverAbstractHandlerExceptionResolver 是真正干活的异样解析器的父类,咱们就先从他的 resolveException 办法开始看起。 @Override@Nullablepublic ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex) { if (shouldApplyTo(request, handler)) { prepareResponse(ex, response); ModelAndView result = doResolveException(request, response, handler, ex); if (result != null) { logException(ex, request); } return result; } else { return null; }}首先调用 shouldApplyTo 办法判断以后解析器是否能够解决传入的处理器所抛出的异样,如果不反对,则间接返回 null,这个异样将交给下一个 HandlerExceptionResolver 去解决。调用 prepareResponse 办法解决 response。调用 doResolveException 办法理论解决异样,这是一个模版办法,具体的实现在子类中。调用 logException 办法记录异样日志信息。记录异样日志没啥好说的,doResolveException 则是一个空的模版办法,所以这里对咱们来说次要就是两个办法:shouldApplyTo 和 prepareResponse,咱们别离来看。 ...

April 15, 2021 · 6 min · jiezi

关于spring:SpringMVC实现原理及详解

一、首先,咱们先来认识一下SpringMVC的次要组件 前端控制器(DisatcherServlet):接管申请,响应后果,返回能够是json,String等数据类型,也能够是页面(Model)。 处理器映射器(HandlerMapping):依据URL去查找处理器,个别通过xml配置或者注解进行查找。 处理器(Handler):就是咱们常说的controller控制器啦,由程序员编写。 处理器适配器(HandlerAdapter):能够将处理器包装成适配器,这样就能够反对多种类型的处理器。 视图解析器(ViewResovler):进行视图解析,返回view对象(常见的有JSP,FreeMark等)。 二、SpingMVC的工作原理上面是文字步骤阐明: 1、用户发送申请到前端控制器(DispatcherServlet)。 2、前端控制器申请处理器映射器(HandlerMapping)去查找处理器(Handler)。 3、找到当前处理器映射器(HandlerMappering)向前端控制器返回执行链(HandlerExecutionChain)。 4、前端控制器(DispatcherServlet)调用处理器适配器(HandlerAdapter)去执行处理器(Handler)。 5、处理器适配器去执行Handler。 6、处理器执行完给处理器适配器返回ModelAndView。 7、处理器适配器向前端控制器返回ModelAndView。 8、前端控制器申请视图解析器(ViewResolver)去进行视图解析。 9、视图解析器向前端控制器返回View。 10、前端控制器对视图进行渲染。 11、前端控制器向用户响应后果。 三、SpingMVC的应用须要在web.xml中配置DispatcherServlet。并且须要配置spring监听器ContextLoaderListener <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class></listener>        <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!-- 如果不设置init-param标签,则必须在/WEB-INF/下创立xxx-servlet.xml文件,其中xxx是servlet-name中配置的名称。 --> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:spring/springmvc-servlet.xml</param-value> </init-param> <load-on-startup>1</load-on-startup></servlet><servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern></servlet-mapping>

April 15, 2021 · 1 min · jiezi

关于spring:Spring-Framework

1、IOC和DI IOC: 管制反转即控制权的转移,将咱们创建对象的形式反转了,以前对象的创立是由咱们开发人员本人保护,包含依赖关系也是本人注入。应用了spring之后,对象的创立以及依赖关系能够由spring实现创立以及注入,反转管制就是反转了对象的创立形式,从咱们本人创立反转给了程序创立(spring) DI:  Dependency Injection  依赖注入spring这个容器中,替你治理着一系列的类,前提是你须要将这些类交给spring容器进行治理,而后在你须要的时候,不是本人去定义,而是间接向spring容器索取,当spring容器晓得你的需要之后,就会去它所治理的组件中进行查找,而后间接给你所须要的组件.实现IOC思维须要DI做反对注入形式:   1.set形式注入    2.构造方法注入   3.字段注入注入类型:   1.值类型注入      2.援用类型注入益处:  1.升高组件之间的耦合度,实现软件各层之间的解耦.2.能够使容器提供泛滥服务如事务管理音讯服务解决等等。当咱们应用容器治理事务时,开发人员就不须要手工 管制事务,也不须要解决简单的事务流传3.容器提供单例模式反对,开发人员不须要本人编写实现代码.4.容器提供了AOP技术,利用它很容易实现如权限拦挡,运行期监控等性能5.容器提供泛滥的辅助类,使这些类能够放慢利用的开发.如jdbcTemplate HibernateTemplate2.applicationContext & BeanFactory区别 BeanFactory接口(1) spring的原始接口,针对原始接口的实现类性能较为繁多(2)BeanFactory接口实现类的容器,特点是每次在取得对象时才会创建对象 ApplicationContext接口(1)每次容器启动时就会创立容器中配置的所有对象(2)提供了更多功能(3)从类门路下加载配置文件: ClassPathXmlApplicationContext从硬盘的绝对路径下加载配置文件:FileSystemXmlApplication   3.spring配置详解 3.1、元素属性bean元素:应用该元素形容须要spring容器治理对象name属性:给被治理的对象起个名字,取得对象时getBean("name值")class属性:被治理对象的残缺类名id属性:与name属性截然不同,名称不可反复,不能应用特殊字符 name和id之间的一些留神点:1、配置两个雷同的 id 或者 name 都不能通过。2、如果既配置了 id ,也配置了 name ,则两个都失效。如果id和name都没有指定,则用类全名作为name,如<bean class="com.stamen.BeanLifeCycleImpl">,则你能够通过getBean("com.stamen.BeanLifeCycleImpl")返回该实例。3、如果配置根本类的时候,注解和配置文件都应用的时候,注解和配置文件中 name 雷同的时候, 则两个抵触,配置文件失效。      如果配置根本类的时候,注解和配置文件都应用的时候,注解和配置文件中 name 不雷同的时候, 则两个不抵触,都可能失效。 3.2、bean元素进阶(  scope属性   生命周期属性)—————单例多例 (1)scope属性    (1)singleton   默认值   单例对象   :被标识为单例的对象在spring容器中只会存在一个实例    (2)prototype    多例原型:被标识为多例的对象,每次在取得才会被创立,每次创立都是新的对象    (3)requestWeb环境下,对象与request生命周期统一        (4)sessionWeb环境下,对象与session生命周期统一总结:绝大多数状况下,应用单例singleton(默认值),然而在与struts整合时候,务必要用prototype多例,因为struts2在每次申请都会创立一个新的Action,若为单例,在多申请状况下,每个申请找找spring拿的都是同一个action。   (2)生命周期属性(理解)———初始化和销毁    (1)配置一个办法作为生命周期初始化办法,spring会在对象创立之后立即调用 init-method    (2)配置一个办法作为生命周期的销毁办法,spring容器在敞开并销毁所有容器中的对象之前调用destory-method    <bean init-method=“init”  destory-method=“destory”></bean>        对应注解为@PostConstruct ...

April 15, 2021 · 2 min · jiezi

关于golang:手撸golang-仿spring-iocaop-之6-扫码1

手撸golang 仿spring ioc/aop 之6 扫码1 缘起最近浏览 [Spring Boot技术底细: 架构设计与实现原理] (朱智胜 , 2020.6)本系列笔记拟采纳golang练习之Talk is cheap, show me the code. SpringSpring的次要个性:1. 管制反转(Inversion of Control, IoC)2. 面向容器3. 面向切面(AspectOriented Programming, AOP)源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标参考spring boot罕用注解,应用golang编写“基于注解的动态代码增强器/生成器” 配置: ComponentScan,Configuration, BeanBean申明:Component, Service, ControllerBean注入:AutowriedAOP注解:Before, After, Around, PointCut子目标(Day 6)昨天把思路撸分明了,明天入手实现各种词法元素的扫描 project.go: 扫描整个我的项目的所有代码文件。module名从go.mod文件外面取packages.go: 递归扫描某个代码目录files.go: 扫描某个go代码文件,并解析import/struct/field/method等元素imports: 扫描指定代码文件的所有importdomain/*.go:词法元素模型集,码略project.go扫描整个我的项目的所有代码文件。module名从go.mod文件外面取 package scannerimport ( "errors" "io/ioutil" "learning/gooop/spring/autogen/common" "learning/gooop/spring/autogen/domain" "os" "path" "strings")func ScanProject(name, dir string) (error, *domain.ProjectInfo) { e, module := parseModFileAndGetModuleName(dir) if e != nil { return e, nil } files, e := ioutil.ReadDir(dir) if e != nil { return e, nil } project := domain.NewProjectInfo() project.Name = name project.LocalDir = dir project.Module = module for _, file := range files { if !file.IsDir() { continue } e, pkg := ScanPackage(project, nil, dir+"/"+file.Name()) if e != nil { return e, nil } else { project.AppendPackage(pkg) } } return nil, project}func parseModFileAndGetModuleName(dir string) (error, string) { modfile := path.Join(dir, gModuleFile) _, e := os.Stat(modfile) if e != nil { return gErrorModuleFileNotFound, "" } data, e := ioutil.ReadFile(modfile) if e != nil { return e, "" } text := string(data) for _, line := range strings.Split(text, "\n") { line := strings.TrimSpace(line) if !common.Tokens.MatchString(line, gModulePrefix) { continue } if ok, s := common.Tokens.MatchRegexp(line, gModulePattern); ok { return nil, strings.TrimSpace(s[len(gModulePrefix)+1:]) } } return gErrorProjectModuleNotFound, ""}var gModuleFile = "go.mod"var gModulePrefix = "module"var gModulePattern = "^module\\s+\\w+(/\\w+)*"var gErrorModuleFileNotFound = errors.New("module file not found: go.mod")var gErrorProjectModuleNotFound = errors.New("project module not found in go.mod")packages.go递归扫描某个代码目录 ...

April 14, 2021 · 4 min · jiezi

关于golang:手撸golang-仿spring-iocaop-之5-如何扫描

手撸golang 仿spring ioc/aop 之5 如何扫描 缘起最近浏览 [Spring Boot技术底细: 架构设计与实现原理] (朱智胜 , 2020.6)本系列笔记拟采纳golang练习之Talk is cheap, show me the code. SpringSpring的次要个性:1. 管制反转(Inversion of Control, IoC)2. 面向容器3. 面向切面(AspectOriented Programming, AOP)源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标参考spring罕用注解,应用golang编写“基于注解的动态代码增强器/生成器” 配置: ComponentScan,Configuration, BeanBean申明:Component, Service, ControllerBean注入:AutowriedAOP注解:Before, After, Around, PointCut子目标(Day 5)Q:搞点啥?A:扫描go代码文件Q:具体点?A:递归扫描指定的目录下所有go代码,并提取构造体,字段,办法和注解信息Q:什么思路?A: 读取go文件>解析package>解析import>解析struct列表>解析field列表>解析method列表逐struct,解析注解逐field,解析注解逐method,解析注解Q:如何解析?A:读取文本>革除正文>逐行正则扫描Q:革除正文?A:因为正文外面能够写任何货色,所以革除正文后能够缩小解析谬误的可能Q:如何革除?A: LINE_COMMENT = '//' 1*PARA_COMMENT = '/' . '*/'Q:扫描package?A:PACKAGE = ‘package' SPACE+ \w+Q:扫描import?A: SINGLE_IMPORT = 'import' SPACE+ PACKAGE_LITERALPACKAGE_LITERAL = (PACKAGE_ALIAS SPACE+)? "\w+(/\w+)*"MULTI_IMPORT = 'import' SPACE+ '(\r\n' (SPACE* PACKAGE_LITERAL)+ '\r\n)'Q:扫描struct?A: STRUCT_START = 'type' SPACE+ \w+ SPACE+ 'struct' SPACE+ '{'STRUCT_END = '}'Q:扫描field?A: ...

April 13, 2021 · 1 min · jiezi

关于spring:SpringMVC请求参数绑定

1.对根本数据类型的绑定2.对POJO类型的绑定 3.POJO类型中含有汇合类型 操作和对类的操作类似

April 12, 2021 · 1 min · jiezi

关于golang:手撸golang-仿spring-iocaop-之3

手撸golang 仿spring ioc/aop 之3 缘起最近浏览 [Offer来了:Java面试外围知识点精讲(框架篇)] (王磊 , 2020.6)本系列笔记拟采纳golang练习之Talk is cheap, show me the code. SpringSpring基于J2EE技术实现了一套轻量级的Java Web Service零碎利用框架。它有很多优良的个性,很多公司都抉择把Spring作为产品或我的项目的根底开发架构。Spring的次要个性包含:1. 轻量2. 管制反转(Inversion of Control, IoC)3. 面向容器4. 面向切面(AspectOriented Programming, AOP)5. 框架灵便源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标参考spring罕用注解,应用golang编写“基于注解的动态代码增强器/生成器” 配置: ComponentScan,Configuration, BeanBean申明:Component, Service, ControllerBean注入:AutowriedAOP注解:Before, After, Around, PointCut子目标(Day 3)增加ProjectCmd以反对我的项目定义增加文本扫描的辅助类:Chars.go, Tokens.go定义代码生成服务接口ICodingService及其撑持接口设计command/ProjectCmd: 我的项目定义指令common/Chars: 字符识别辅助类common/Tokens: 组合文本辨认辅助类service/ICodingService: 代码生成服务接口service/iCodingContext: 代码生成上下文service/iCodingState:状态模式下的服务状态service/iCmdRunner:定义指令执行器接口,具体执行拟走责任链模式以便扩大service/tInitialState:默认的服务状态command/ProjectCmd.go我的项目定义指令 package project_cmdimport ( "errors" "fmt" "learning/gooop/spring/autogen/command" "learning/gooop/spring/autogen/common" "os" "strings")// ProjectCmd defines a project with name and dirtype ProjectCmd struct { name string dir string}// ProjectCmdBuilder parses cli input and creates a ProjectCmd instancetype ProjectCmdBuilder intconst gProjectCmdPrefix = "project "var gErrorInvalidProjectCmd = errors.New("invalid project cmd")func (me *ProjectCmd) String() string { return fmt.Sprintf("project %s %s", me.name, me.dir)}func (me *ProjectCmd) Apply(ctx command.ICmdContext) error { panic("implements me")}func (me *ProjectCmdBuilder) Build(line string) (error, command.ICmd) { if !common.Tokens.MatchString(line, gProjectCmdPrefix) { return nil, nil } line = strings.TrimSpace(line[len(gProjectCmdPrefix):]) b,name := common.Tokens.MatchIdentifier(line) if !b { return gErrorInvalidProjectCmd, nil } line = line[len(name):] b, spaces := common.Tokens.MatchSpaces(line) if !b { return gErrorInvalidProjectCmd, nil } line = line[len(spaces):] b, dir := common.Tokens.MatchDir(line) if !b { return gErrorInvalidProjectCmd, nil } _,e := os.Stat(dir) if e != nil { return e, nil } return nil, &ProjectCmd{ name, dir }}common/Chars.go字符识别辅助类 ...

April 11, 2021 · 3 min · jiezi

关于spring:thymeleaf学习一

规范表达式语法${...}:变量表达式*{...}:抉择表达式\#{...}:音讯(i18n)表达式@{...}:URL表达式~{...}:片段表达式变量表达式将Thymeleaf与Spring集成-在上下文变量(在Spring行话中也称为 Spring jargon)上执行 OGNL表达式长这样 : ${session.user.name}他们能够作为属性的值,像这样: <span th:text="${book.author.name}">以上代码和这样是相等的(OGNL和SpEL语言) ((Book)context.getVariable("book")).getAuthor().getName()咱们能够应用迭代拜访变量 <li th:each="book : ${books}">$ {books}从上下文中抉择称为books的变量,并将其评估为可迭代的变量,以在th:each循环中应用 抉择表达式抉择表达式和变量表达式相似,而抉择表达式只能在先前抉择的对象上执行,不能全局执行抉择表达式长这样: *{customer.name}它们作用的对象由th:object属性指定 <div th:object="${book}"> ... <span th:text="*{title}">...</span> ...</div>等效于: { // th:object="${book}" final Book selection = (Book) context.getVariable("book"); // th:text="*{title}" output(selection.getTitle());}音讯表达式常常称为text externalization, internationalization或者 i18n音讯表达式容许咱们从.properties文件中检索特定于语言环境的音讯,通过键援用它们,并可选地利用一组参数。他们长这样: #{main.title}#{message.entrycreated(${entryId})}在模板中长这样: <table> ... <th th:text="#{header.address.city}">...</th> <th th:text="#{header.address.country}">...</th> ...</table>如果心愿音讯关键字由上下文变量的值确定,或者心愿将变量指定为参数。能够在音讯表达式内应用变量表达式,像这样: #{${config.adminWelcomeKey}(${session.user.name})}链接表达式链接表达式用于构建URL并向其增加有用的上下文和会话信息(此过程通常称为URL重写)一个网页利用部署在你的web服务器的myapp上下文,表达式长这样: <a th:href="@{/order/list}">...</a>他会被转换为: <a href="/myapp/order/list">...</a>如果咱们须要保留会话且未启用Cookie(或服务器尚不晓得),请执行以下操作 <a href="/myapp/order/list;jsessionid=23fa31abd41ea093">...</a>URL还能够退出变量 <a th:href="@{/order/details(id=${orderId},type=${orderType})}">...</a>他理论是会被转换为这样的: <!-- Note ampersands (&) should be HTML-escaped in tag attributes... --><a href="/myapp/order/details?id=23&amp;type=online">...</a>URL表达式能够是绝对的,在这种状况下,没有应用程序上下文将作为URL的前缀: <a th:href="@{../documents/report}">...</a>也能够是服务器的绝对(同样也没有URL前缀) <a th:href="@{~/contents/main}">...</a>绝对协定(与相对URL雷同,但浏览器将应用与所显示页面雷同的HTTP或HTTPS协定): <a th:href="@{//static.mycompany.com/res/initial}">...</a>url表达式也当然能够是相对的 <a th:href="@{http://www.mycompany.com/main}">...</a>片段表达式片段表达式是示意标记片段并在模板中应用它们的简略办法。最常见的用处是应用th:insert或th:replace插入片段: ...

April 10, 2021 · 1 min · jiezi

关于golang:手撸golang-spring-iocaop-之2

手撸golang spring ioc/aop 之2 缘起最近浏览 [Offer来了:Java面试外围知识点精讲(框架篇)] (王磊 , 2020.6)本系列笔记拟采纳golang练习之Talk is cheap, show me the code. SpringSpring基于J2EE技术实现了一套轻量级的Java Web Service零碎利用框架。它有很多优良的个性,很多公司都抉择把Spring作为产品或我的项目的根底开发架构。Spring的个性包含轻量、管制反转(Inversion of Control, IoC)、面向容器、面向切面(AspectOriented Programming, AOP)和框架灵便等。源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标参考spring罕用注解,应用golang编写“基于注解的动态代码增强器/生成器” 配置: ComponentScan,Configuration, BeanBean申明:Component, Service, ControllerBean注入:AutowriedAOP注解:Before, After, Around, PointCut子目标(Day 2)构思app的运行模式: 本地standlone模式运行提供基于cli的命令行实时交互生成旁路代码:只扫描源代码,不批改源代码,加强后的代码加对立后缀设计cli交互指令集: config save:保留配置config saveas <name>:另存配置watch add <dir>:增加代码扫描目录watch del <dir>:移除代码扫描目录watch list:显示以后扫描的代码目录汇合gen:生成加强代码,也就是扫描所有注解,并生成加强类和加强办法设计config/IConfiguration:配置接口command/ICmd:指令接口command/ICmdBuilder:指令构建器接口command/ICmdContext:指令执行上下文接口config_cmd/SaveCmd: 保留配置config_cmd/SaveASCmd: 另存配置watch_cmd/AddCmd: 增加监督watch_cmd/DelCmd: 移除监督watch_cmd/ListCmd: 显示已监督目录的列表gen_cmd/GenCmd: 生成加强类和加强办法model/IEventDrivenModel:“事件驱动”的逻辑编排模型logger: 日志接口,略config/IConfiguration.go配置接口 package config// IConfiguration defines system configuration interfacetype IConfiguration interface { GetProjectName() string SetProjectName(string) GetWatchPaths() []string AddWatchPath(string) DelWatchPath(string) Save() error SaveAS(string) error}command/ICmd.go指令接口 ...

April 10, 2021 · 4 min · jiezi

关于spring:SpringBoot-部署-Jar-文件瘦身优化指南

本文截取代码片段来自于对应的残缺示例源码工程: https://gitee.com/xautlx/pack...https://github.com/xautlx/pac...相干代码和配置均理论执行测试过,如在验证过程发现有任何问题可Issue反馈以便及时更正,感激反对! 概要阐明 随着Spring Boot的风行,大家体验到只需构建输入一个jar文件,而后只需一个java -jar命令就能部署运行利用的痛快。常见一些单体利用随着我的项目规模的扩大单个jar文件的大小越来越大,动辄两三百MB。如果再引入微服务架构,动辄一二十个微服务,所有模块jar加起来整个零碎光部署文件就一两个GB。 一个零碎一旦上线运行,无论新需要迭代还是Bug修复,免不了须要做部署更新,尤其对于一些交付类型我的项目,首次部署或异地更新, 动不动就须要传输几百MB或几个GB的部署文件,的确是一个让人头疼的问题。 能够设想一下,线上零碎发现一个紧急重大Bug捅到了主管那里,交代马上紧急修复解决,研发共事火速剖析排查分分钟搞定提交代码并实现构建打包并交付给运维。过一会领导焦急上火来过问问题更新解决了吗?运维只能很难堪的答复:还没呢,部署包文件比拟大,正在上传有点慢… 一听领导就火了,就改了几行代码,部署更新为啥要上传几百MB的文件呢?难道没有方法优化一下吗? 遇到这样的状况,倡议你往下看,或者能找到你想要的答案。 本文内容包含: 如何把一两百MB的繁多Spring Boot jar文件,拆散为依赖组件lib目录和一个业务jar来进行部署,优化单个jar文件大小到一两百KB。。如何把一二十个微服务高度重叠的依赖组件合并到繁多lib目录和多个一两百KB的业务jar来进行部署,优化整个我的项目部署文件大小从一两个GB大小到两三百MB。本文内容不包含: 不包含进行Spring Boot配置文件拆散相干,个别简略采纳通过指定active profile从内部yaml配置文件笼罩jar文件中配置即可或是采纳Nacos等配置服务模式。不包含Maven最佳实际用法,列入样例工程中出于演示不便的思考比方把一些本应放到各个Boot模块特定的配置申明间接放到顶层的parent中定义,请留神按理论状况优化调整应用。不包含可执行jar的运行模式反对参考,文中实现形式次要面向java -jar运行模式。瘦身打怪降级过程 Level 0:惯例的Fat Jar构建参考我的项目目录:package-optimize-level0 次要配置: <build> <finalName>${project.artifactId}</finalName> <!-- 特地留神: 我的项目仅仅是为了演示配置不便,间接在parent的build局部做了插件配置和运行定义。 然而理论我的项目中须要把这些定义只放到spring boot模块我的项目(可优化应用pluginManagement模式),防止烦扰其余util、common等模块我的项目 --> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins></build> 配置输入: cd package-optimize-level0mvn clean installls -lh package-optimize-app1/target/package-optimize-app1.jar-rw-r--r-- 1 lixia wheel 16M Feb 24 21:06 package-optimize-app1/target/package-optimize-app1.jarjava -jar package-optimize-app1/target/package-optimize-app1.jar 重点阐明: (以后演示利用仅依赖了spring-boot-starter-web极少组件,所有构建输入只有十来MB)理论状况繁多构建依据我的项目依赖组件量输入jar个别在几十MB到一两百MB甚至更大。如果有十来个微服务须要部署,那就意味着须要传输一两个GB的文件,耗时可想而知。就算是繁多更新个别微服务也须要传输一两百MB。Level 1:常见的依赖jar拆散构建形式 参考我的项目目录:package-optimize-level1 关解决问题: 升高单个微服务jar的文件大小,以便部署过程秒传文件。 次要配置:重点配置阐明请详见如下正文阐明: <build> <finalName>${project.artifactId}</finalName> <!-- 特地留神: 我的项目仅仅是为了演示配置不便,间接在parent的build局部做了插件配置和运行定义。 然而理论我的项目中须要把这些定义只放到spring boot模块我的项目(可优化应用pluginManagement模式),防止烦扰其余util、common等模块我的项目 --> <plugins> <!-- 拷贝我的项目所有依赖jar文件到构建lib目录下 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-dependency-plugin</artifactId> <executions> <execution> <id>copy-dependencies</id> <phase>package</phase> <goals> <goal>copy-dependencies</goal> </goals> <configuration> <outputDirectory>${project.build.directory}/lib</outputDirectory> <excludeTransitive>false</excludeTransitive> <stripVersion>false</stripVersion> <silent>true</silent> </configuration> </execution> </executions> </plugin> <!-- Spring Boot模块jar构建 --> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <includes> <!-- 不存在的include援用,相当于排除所有maven依赖jar,没有任何三方jar文件打入输入jar --> <include> <groupId>null</groupId> <artifactId>null</artifactId> </include> </includes> <layout>ZIP</layout> </configuration> <executions> <execution> <goals> <goal>repackage</goal> </goals> </execution> </executions> </plugin> </plugins></build> 配置输入: ...

April 9, 2021 · 3 min · jiezi

关于golang:手撸golang-spring-iocaop-之1

手撸golang spring ioc/aop 之1 缘起最近浏览 [Offer来了:Java面试外围知识点精讲(框架篇)] (王磊 , 2020.6)本系列笔记拟采纳golang练习之 SpringSpring基于J2EE技术实现了一套轻量级的Java Web Service零碎利用框架。它有很多优良的个性,很多公司都抉择把Spring作为产品或我的项目的根底开发架构。Spring的个性包含轻量、管制反转(Inversion of Control, IoC)、面向容器、面向切面(AspectOriented Programming, AOP)和框架灵便等。源码gitee地址:https://gitee.com/ioly/learning.gooop原文链接:https://my.oschina.net/ioly指标应用golang高仿spring罕用注解 Bean申明:Component, Service, ControllerBean注入:Autowried配置: Configuration, Bean, ComponentScanAOP注解:Before, After, Around, PointCut思路golang的反射API比拟弱,没有动静代理,也没有CGLib此等神器简略点只能走AspectJ路线了:动态编译/autogenBean申明:就是容器单例/多例模式的利用Bean注入:增加setter办法,供容器注入配置:增加setter办法,容器将系统配置注入AOP注解:搜寻正则匹配的函数,在前后插入切面函数调用(未完待续)

April 9, 2021 · 1 min · jiezi

关于spring:java并发编程JUC第十二篇AtomicInteger原子整型

AtomicInteger 类底层存储一个int值,并提供办法对该int值进行原子操作。AtomicInteger 作为java.util.concurrent.atomic包的一部分,从Java 1.5开始引入。 1. AtomicInteger根底用法通过下文的AtomicInteger构造方法,能够创立一个AtomicInteger对象,该对象的初始值默认为0。AtomicInteger提供get和set办法,获取底层int整数值,与设置int整数值 //初始值为0的atomicInteger对象AtomicInteger atomicInteger = new AtomicInteger(); //初始值为200的atomicInteger对象AtomicInteger atomicInteger = new AtomicInteger(200); int currentValue = atomicInteger.get(); //100atomicInteger.set(2453); //当初的值是 2453然而下面的办法,对于AtomicInteger而言并不是它的核心内容,AtomicInteger核心内容体现在它的原子性,咱们下文介绍。 2. 什么时候须要应用AtomicInteger咱们通常在以下的两种场景下应用AtomicInteger 多线程并发场景下操作一个计数器,须要保障计数器操作的原子性。进行数值比拟,如果给定值与以后值相等,进行数值的更新操作,并实现操作的非阻塞算法。2.1. 原子计数器场景把AtomicInteger作为一个计数器应用,AtomicInteger提供了若干办法进行加法、减法的原子操作。 比方从一个map外面获取值,用get()办法,这是第一个操作;获取到值之后给这个值加上n,这是第二个操作;将进行过加法运算的值,再次放入map外面是第三个操作。所谓操作的原子性是指:在多线程并发的场景下,下面的三个操作是原子性的,也就是不可分割的。不会呈现A线程get了数值,B线程同时也get到了该数值,两个线程同时为该值做运算并先后再次放入的状况,这种状况对于AtomicInteger而言是不会呈现的,AtomicInteger操作是线程平安的、不可分割的。addAndGet()- 将给定的值加到以后值上,并在加法后返回新值,并保障操作的原子性。getAndAdd()- 将给定的值加到以后值上,并返回旧值,并保障操作的原子性。incrementAndGet()- 将以后值减少1,并在减少后返回新值。它相当于++i操作,并保障操作的原子性。getAndIncrement()- 将以后值减少1并返回旧值。相当于++i操作,并保障操作的原子性。decrementAndGet()- 将以后值减去1,并在减去后返回新值,相当于i--操作,并保障操作的原子性。getAndDecrement()- 将以后值减去1,并返回旧值。它相当于 --i操作,并保障操作的原子性。上面是AtomicInteger原子性操作方法的例子 public class Main { public static void main(String[] args) { //初始值为100的atomic Integer AtomicInteger atomicInteger = new AtomicInteger(100); System.out.println(atomicInteger.addAndGet(2)); //加2并返回102 System.out.println(atomicInteger); //102 System.out.println(atomicInteger.getAndAdd(2)); //先获取102,再加2 System.out.println(atomicInteger); //104 System.out.println(atomicInteger.incrementAndGet()); //加1再获取105 System.out.println(atomicInteger); //105 System.out.println(atomicInteger.getAndIncrement()); //先获取105再加1 System.out.println(atomicInteger); //106 System.out.println(atomicInteger.decrementAndGet()); //减1再获取105 System.out.println(atomicInteger); //105 System.out.println(atomicInteger.getAndDecrement()); //先获取105,再减1 System.out.println(atomicInteger); //104 }}2.2. 数值比对及替换操作compareAndSet操作将一个内存地位的内容与一个给定的值进行比拟,只有当它们雷同时,才会将该内存地位的内容批改为一个给定的新值。这个过程是以单个原子操作的形式实现的。 ...

April 8, 2021 · 1 min · jiezi

关于spring:java并发编程JUC第十一篇如何在线程之间进行对等数据交换

java.util.concurrent.Exchanger能够用来进行数据交换,或者被称为“数据交换器”。两个线程能够应用Exchanger替换数据,下图用来阐明Exchanger的作用 在上面的代码中 首先咱们定义了一个Exchanger,用于数据交换而后定义了两个线程对象bookExchanger1和bookExchanger2,两个线程都持有Exchanger交换器对象用于数据交换两个线程中的每个线程都有本人的数据,比方上面代码中的String[] 书籍数组。 public static void main(String[] args) {//数据交换器-数据为bookExchanger<String> exchanger = new Exchanger<>();//换书线程1BookExchanger bookExchanger1 = new BookExchanger(exchanger, new String[]{"Java从入门到放弃","Java编程思维"});//换书线程2BookExchanger bookExchanger2 = new BookExchanger(exchanger, new String[]{"C语言程序设计","实战Python数据分析"});new Thread(bookExchanger1).start();new Thread(bookExchanger2).start();}BookExchanger 继承自Runnable代表参加换书的换书读者,他持有Exchanger数据交换器用于替换图书。 public class BookExchanger implements Runnable{ Exchanger<String> exchanger = null; //数据交换器 String[] books = null; //图书数组 public BookExchanger(Exchanger<String> exchanger, String[] books) { this.exchanger = exchanger; this.books = books; } @Override public void run() { try { for(String bookName : books) { //替换数据,bookName为我的书,exBook为我换回来的书 String exBook = this.exchanger.exchange(bookName); System.out.println( Thread.currentThread().getName() + " 用《 " + bookName + "》 换 《 " + exBook + "》" ); } } catch (InterruptedException e) { e.printStackTrace(); } }}执行上文中的代码,失去如下的打印输出。能够看到只有两个线程实现一次替换之后,能力再进行下一次的替换。 ...

April 6, 2021 · 1 min · jiezi

关于spring:阿里一面Spring和SpringMvc父子容器你能说清楚吗

以前写了几篇对于SpringBoot的文章《面试高频题:springBoot主动拆卸的原理你能说进去吗》、《保姆级教程,手把手教你实现一个SpringBoot的starter》,这几天忽然有个读者问:能说一说Spring的父子容器吗?说实话这其实也是Spring八股文外面一个比拟常见的问题。在我的印象外面Spring就是父容器,SpringMvc就是子容器,子容器能够拜访父容器的内容,父容器不能拜访子容器的货色。有点相似java外面的继承的滋味,子类能够继承父类共有办法和变量,能够拜访它们,父类不能够拜访子类的办法和变量。在这里就会衍生出几个比拟经典的问题: 为什么须要父子容器?是否能够把所有类都通过Spring容器来治理?(Spring的applicationContext.xml中配置全局扫描)是否能够把咱们所需的类都放入Spring-mvc子容器外面来治理(springmvc的spring-servlet.xml中配置全局扫描)?同时通过两个容器同时来治理所有的类?如果可能把下面这四个问题能够说个所以然来,集体感觉Spring的父子容器应该问题不大了。咱们能够看下官网提供的父子容器的图片上图中显示了2个WebApplicationContext实例,为了进行辨别,别离称之为:Servlet WebApplicationContext(子容器)、Root WebApplicationContext(父容器)。Servlet WebApplicationContext:这是对J2EE三层架构中的web层进行配置,如控制器(controller)、视图解析器(view resolvers)等相干的bean。通过spring mvc中提供的DispatchServlet来加载配置,通常状况下,配置文件的名称为spring-servlet.xml。Root WebApplicationContext:这是对J2EE三层架构中的service层、dao层进行配置,如业务bean,数据源(DataSource)等。通常状况下,配置文件的名称为applicationContext.xml。在web利用中,其个别通过ContextLoaderListener来加载。Spring的启动要想很好的了解它们之间的关系,咱们就有必要先弄清楚Spring的启动流程。要弄清楚这个启动流程咱们就须要搭建一个SpringMvc我的项目,说句实话,用惯了SpringBooot开箱即用,忽然在回过头来搭建一个SpringMvc我的项目还真有点不习惯,一大堆的配置文件。(尽管也能够用注解来实现)具体怎么搭建SpringMvc我的项目这个就不介绍了,搭建好我的项目咱们运行起来能够看到控制台会输入如下日志:日志外面别离打印出了父容器和子容器别离的一个耗时。 如何验证是有两个容器?咱们只须要Controller与咱们的Service中实现ApplicationContextAware接口,就能够得悉对应的治理容器:在Service所属的父容器外面咱们能够看到父容器对应的对象是XmlWebApplicationContext@3972在Controller中对应的容器对象是XmlWebApplicationContext@4114由此可见它们是两个不同的容器。 源码剖析咱们晓得SpringServletContainerInitializer从 servlet 3.0 开始,Tomcat 启动时会主动加载实现了 ServletContainerInitializer 接口的类(须要在 META-INF/services 目录下新建配置文件)也称为 SPI(Service Provider Interface) 机制,SPI的利用还是挺广的比方咱们的JDBC、还有Dubbo框架外面都有用到,如果还有不是很理解SPI机制的 能够去学习下。所以咱们的入口就是SpringServletContainerInitializer的onStartup办法,这也应该是web容器启动调用Spring相干的第一个办法。 初始化SpringIoc如果切实找不到入口的话,咱们能够 依据控制台打印的日志,而后拿着日志进行反向查找这应该总能找到开始加载父容器的中央。启动的时候控制台应该会打印出“Root WebApplicationContext: initialization started” 咱们拿着这个日志就能定位到代码了 `public WebApplicationContext initWebApplicationContext(ServletContext servletContext) {` `if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) {` `throw new IllegalStateException(` `"Cannot initialize context because there is already a root application context present - " +` `"check whether you have multiple ContextLoader* definitions in your web.xml!");` `}` `servletContext.log("Initializing Spring root WebApplicationContext");` `Log logger = LogFactory.getLog(ContextLoader.class);` `if (logger.isInfoEnabled()) {` `logger.info("Root WebApplicationContext: initialization started");` `}` `long startTime = System.currentTimeMillis();` `try {` `// Store context in local instance variable, to guarantee that` `// it is available on ServletContext shutdown.` `if (this.context == null) {` `// 通过反射去创立context` `this.context = createWebApplicationContext(servletContext);` `}` `if (this.context instanceof ConfigurableWebApplicationContext) {` `ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) this.context;` `if (!cwac.isActive()) {` `// The context has not yet been refreshed -> provide services such as` `// setting the parent context, setting the application context id, etc` `if (cwac.getParent() == null) {` `// The context instance was injected without an explicit parent ->` `// determine parent for root web application context, if any.` `ApplicationContext parent = loadParentContext(servletContext);` `cwac.setParent(parent);` `}` `// IOC容器初始化` `configureAndRefreshWebApplicationContext(cwac, servletContext);` `}` `}` `servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context);` `ClassLoader ccl = Thread.currentThread().getContextClassLoader();` `if (ccl == ContextLoader.class.getClassLoader()) {` `currentContext = this.context;` `}` `else if (ccl != null) {` `currentContextPerThread.put(ccl, this.context);` `}` `if (logger.isInfoEnabled()) {` `long elapsedTime = System.currentTimeMillis() - startTime;` `logger.info("Root WebApplicationContext initialized in " + elapsedTime + " ms");` `}` `return this.context;` `}` `catch (RuntimeException | Error ex) {` `logger.error("Context initialization failed", ex);` `servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex);` `throw ex;` `}` `}`这段代码就是创立父容器的中央。 ...

April 2, 2021 · 1 min · jiezi

关于spring:java并发编程JUC第十篇CyclicBarrier线程同步

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、BlockingDeque接口、ConcurrentHashMap、CountDownLatch,本文为系列文章第十篇。 java.util.concurrent.CyclicBarrier提供了一种多线程彼此期待的同步机制,能够把它了解成一个阻碍,所有先达到这个阻碍的线程都将将处于期待状态,直到所有线程都达到这个阻碍处,所有线程能力继续执行。 举个例子:CyclicBarrier的同步形式有点像敌人们约好了去游览,在景点入口处汇合,这个景点入口就是一个Barrier阻碍,期待大家都到了才一起进入景点旅行参观。 进入景点后大家去爬山,有的人爬得快,有的人爬的慢,大家约好了山顶汇合,所以山顶就又是一个Barrier阻碍,期待大家都到了山顶才一起下山。上面是一张图来阐明这个问题。每个线程通过调用await(),在CyclicBarrier阻碍处“彼此期待”,一旦所有的线程都达到了CyclicBarrier(都调用了CyclicBarrier办法),所有的线程将一起再次被唤醒继续执行。 1.创立CyclicBarrier阻碍当创立CyclicBarrier的时候,须要指定须要管制多少个线程同步。比方上面的CyclicBarrier设置为管制2个线程同步。 CyclicBarrier barrier = new CyclicBarrier(2);2. 在CyclicBarrier阻碍处期待通过调用CyclicBarrier的await()办法进入期待状态,通常在线程实现本人的阶段性工作之后调用该办法。 barrier.await();CyclicBarrier也提供了另一种办法指定期待超时的工夫,当等待时间大于超时工夫之后,即便还有其余的线程没调用await办法,该线程将主动唤醒继续执行。(敌人们约好了去游览,等了10分钟你还不来,我就本人先去了)。 barrier.await(10, TimeUnit.SECONDS);The waiting threads waits at theCyclicBarrieruntil either: 在CyclicBarrier处期待的线程被开释,继续执行的条件(满足上面的任一条件即可) 最初达到的线程调用了await() 办法该线程被另一个线程打断(另一个线程调用其interrupt()办法)。另一个处于期待状态的线程被打断另一个处于期待状态的线程在CyclicBarrier处期待时超时。某个内部线程调用了CyclicBarrier.reset()拆除阻碍。3. CyclicBarrier ActionCyclicBarrier Action 绝对不太好了解,能够把它了解为阻碍本身的行为。该Action动作是一个线程,所有的线程都达到阻碍之后,该线程将被执行。 Runnable barrierAction = 创立线程;CyclicBarrier barrier = new CyclicBarrier(2, barrierAction);如果这段代码依然无奈了解CyclicBarrier Action的作用,看上面的例子。 4. CyclicBarrier 例子上面的代码演示了如何应用CyclicBarrier进行线程同步: Runnable barrier1Action = new Runnable() { public void run() { System.out.println("阻碍1汇合胜利了,所有人都到了景点门口 "); }};Runnable barrier2Action = new Runnable() { public void run() { System.out.println("阻碍2汇合胜利了,所有人都到了山顶"); }};//阻碍1 景点门口CyclicBarrier barrier1 = new CyclicBarrier(2, barrier1Action);//阻碍2 山顶CyclicBarrier barrier2 = new CyclicBarrier(2, barrier2Action);//游览打算,阶段指标一:景点门口汇合CyclicBarrierRunnable barrierRunnable1 = new CyclicBarrierRunnable(barrier1, barrier2);//游览打算,阶段指标二:爬山到山顶汇合CyclicBarrierRunnable barrierRunnable2 = new CyclicBarrierRunnable(barrier1, barrier2);new Thread(barrierRunnable1).start(); //游客A,Thread-0new Thread(barrierRunnable2).start(); //游客B,Thread-1上面是一个线程类CyclicBarrierRunnable,启动一个就代表一个游客 ...

April 1, 2021 · 1 min · jiezi

关于spring:java并发编程JUC第九篇CountDownLatch线程同步

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、BlockingDeque接口、ConcurrentHashMap,本文为系列文章第九篇。 CountDownLatch是一种线程同步辅助工具,它容许一个或多个线程期待其余线程正在执行的一组操作实现。CountDownLatch的概念在java并发编程中十分常见,面试也会常常被问到,所以肯定要好好了解把握。在这篇文章中,我将介绍以下几点 CountDownLatch是什么?CountDownLatch 如何工作CountDownLatch 代码例子CountDownLatch是什么?CountDownLatch与其余并发编程工具类,如CyclicBarrier、Semaphore、ConcurrentHashMap和BlockingQueue等在java.util.concurrent包中与JDK 1.5一起被引入。CountDownLatch能让一个java线程期待其余线程实现工作,比方Application的主线程期待,直到其余负责启动框架服务的服务线程实现所有服务的启动。 CountDownLatch用线程数来初始化一个计数器,每当一个线程实现执行时,这个计数器就会递加。当计数为零时,示意所有线程都已实现执行,处于期待状态的主线程能够继续执行。 上面咱们应用伪代码的形式形容CountDownLatch 的作用 主线程启动,并为N个线程(假如n=3)初始化CountDownLatch(n)启动n个线程主线程阻塞期待线程1执行实现,CountDownLatch -1 = 2,主线程持续阻塞线程3执行实现,CountDownLatch -1 = 1,主线程持续阻塞线程4执行实现,CountDownLatch -1 = 0,主线程复原执行CountDownLatch 如何工作CountDownLatch.java类外面定义了一个构造函数。count本质上是线程数,这个值只能设置一次,CountDownLatch没有提供办法来重置这个数。 CountDownLatch.public CountDownLatch(int count) {...}应用CountDownLatch的主线程要去期待其余线程执行实现,所以这个主线程必须在启动其余线程后立刻调用 CountDownLatch.await() 办法,该办法阻塞主线程处于期待状态,直到其余线程执行结束,才会进行阻塞。 其余N个线程必须有CountDownLatch对象的援用,因为它们须要告诉CountDownLatch对象它们曾经实现工作。这个告诉是由办法CountDownLatch.countDown()来实现的,每调用一次该办法,就会将构造函数中设置的初始计数count缩小1,所以当所有N个线程都调用了这个办法后count计数达到0,主线程就能够不受await()办法阻塞复原执行了。 所以CountDownLatch特地适宜于那些须要期待N个线程实现后再开始执行的场景。例如一个应用程序的启动类,在解决用户申请之前,要确保所有N个内部零碎都是处于运行状态的。 CountDownLatch 代码例子假如咱们的应用程序主线程启动之前,要查看另外4个程序是否准备就绪,只有其余的4个程序准备就绪,咱们的主程序能力继续执行。就能够应用上面的代码来操作: import java.util.concurrent.CountDownLatch;public class Tester {   public static void main(String args[]) {      //设置计数器 counter = 4 ,等于线程数      CountDownLatch countDownLatch = new CountDownLatch(4);      Thread app1 = new Thread(new Application("App1",  countDownLatch));      Thread app2 = new Thread(new Application("App2",  countDownLatch));                Thread app3 = new Thread(new Application("App3",  countDownLatch));      Thread app4 = new Thread(new Application("App4",  countDownLatch));         // 启动多线程去查看其余四个程序的可用状态      app1.start();      app2.start();      app3.start();      app4.start();      try {         //主线程调用await进行期待,期待上述四个线程失常实现         countDownLatch.await();                     //上述四个线程查看的应用程序启动失常之后, 打印如下信息         System.out.println("All applications are up and running.");      } catch(InterruptedException e) {         System.out.println(e.getMessage());      }           }}子线程程序,每一个线程都持有countDownLatch对象,线程失常执行实现之时,应用countDownLatch.countDown()办法将countDownLatch对象的计数器减1。 ...

March 31, 2021 · 2 min · jiezi

关于spring:java并发编程工具类JUC第八篇ConcurrentHashMap

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue、BlockingDeque接口,本文为系列文章第八篇。 因为Java程序员罕用的HashMap的操作方法不是同步的,所以在多线程环境下会导致存取操作数据不统一的问题,Map接口的另一个实现类Hashtable 尽管是线程平安的,然而在多线程下执行效率很低。为了解决这个问题,在java 1.5版本中引入了线程平安的汇合类ConcurrentMap。 java.util.concurrent.ConcurrentMap接口是Java汇合类框架提供的线程平安的map,这意味着多线程同时拜访它,不会影响map中每一条数据的一致性。ConcurrentMap接口有两个实现类ConcurrentHashMap和ConcurrentSkipListMap,常常被应用的是ConcurrentHashMap,咱们来重点关注它。 1.创立ConcurrentHashMap对象通过上面的代码创立ConcurrentHashMap // 创立容量为8,负载系数为0.6的ConcurrentHashMapConcurrentHashMap<Key, Value> numbers = new ConcurrentHashMap<>(8, 0.6f);应用下面的代码,咱们创立一个叫做numbers的ConcurrentHashMap对象。 Key - 用于关联Map中每个元素的惟一标识Value - Map中每个元素,能够通过key值获取value须要咱们特地留神的是new ConcurrentHashMap<>(8, 0.6). capacity容量 - 第一个参数示意这个map的容量是8,也就是说这个对象能够存储8个键值对.loadFactor负载因子 - 这个map对象的负载因子是 0.6. 这意味着,每当咱们的哈希表被填满60%的时候,条目就会被挪动到一个新的哈希表,其容量大小是原来哈希表的两倍。默认容量与负载因子咱们还能够通过上面的代码初始化一个ConcurrentHashMap对象,默认状况下capacity=16,loadFactor=0.75 ConcurrentHashMap<Key, Value> numbers1 = new ConcurrentHashMap<>();2.ConcurrentHashMap罕用办法2.1. 向ConcurrentHashMap插入元素put(K,V) - 向map中插入key/value 键值对数据putAll(map) - 把另一个map中的所有entries插入到以后的map中putIfAbsent(K,V) - 向map中插入key/value 键值对数据,如果该键值对的key在map不存在则插入数据,否则不做操作。import java.util.concurrent.ConcurrentHashMap;class Main { public static void main(String[] args) { // 创立ConcurrentHashMap 用于保留偶数 ConcurrentHashMap<String, Integer> evenNumbers = new ConcurrentHashMap<>(); // 应用put()办法插入数据 evenNumbers.put("Two", 2); evenNumbers.put("Four", 4); // 应用putIfAbsent()插入数据 evenNumbers.putIfAbsent("Six", 6); System.out.println("偶数汇合ConcurrentHashMap: " + evenNumbers); //创立ConcurrentHashMap用于保留整数 ConcurrentHashMap<String, Integer> numbers = new ConcurrentHashMap<>(); numbers.put("One", 1); // 应用putAll()插入数据 numbers.putAll(evenNumbers); System.out.println("整数汇合ConcurrentHashMap: " + numbers); }}输入后果: ...

March 30, 2021 · 2 min · jiezi

关于spring:java并发编程工具类JUC第七篇BlockingDeque双端阻塞队列

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue、SynchronousQueue,本文为系列文章第七篇。 BlockingDeque接口和BlockingQueue 接口一样都是在java.util.concurrent中定义的,它代表了一个线程平安的“双端队列”,以线程平安的形式向队列中增加元素或获取元素。本篇文章将带大家进一步理解BlockingDeque。 deque是 "Double Ended Queue "的缩写。因而“双端队列”的含意就是能够从两端(队首或队尾)插入和取出元素的队列。如果某个线程既生产又生产同一个队列的元素,那么就能够应用BlockingDeque双端队列。如果生产线程须要在队列的两端插入,而生产线程须要从队列的两端删除,也能够只应用BlockingDeque双端队列。参考上面的图进行了解 一个线程生产元素并将它们插入到队列两端中的任何一端。如果BlockingDeque以后是满的,插入线程将被阻塞,直到移除线程从BlockingDeque中取出一个元素。如果BlockingDeque以后为空,那么移除线程将被阻塞,直到插入线程将一个元素插入到BlockingDeque中。 BlockingDeque 办法BlockingDeque有4组不同的办法,用于插入、删除和查看deque中的元素。每组办法在所要求的操作不能被立刻执行的状况下体现也有所不同。参考上面的表格 队首操作抛出异样返回特定值阻塞后始终期待阻塞后期待超时插入对象addFirst(o)offerFirst(o)putFirst(o)offerFirst(o, timeout, timeunit)移除对象removeFirst(o)pollFirst()takeFirst()pollFirst(timeout, timeunit)查看对象存在getFirst()peekFirst() 队尾操作抛出异样返回特定值阻塞后始终期待阻塞后期待超时插入对象addLast(o)offerLast(o)putLast(o)offerLast(o, timeout, timeunit)移除对象removeLast(o)pollLast()takeLast()pollLast(timeout, timeunit)查看对象存在getLast()peekLast() 大家能够看到,这些办法和和BlockingQueue 的办法有些类似,只是在办法的根底上加了xxxFirst和xxxLast,所以能够参考我之前的文章比照学习),下面的办法的四种行为别离的含意是 抛出异样: 如果调用办法后不能立刻响应后果(空队列或满队列),则抛出异样。返回特定值: 如果调用办法后不能立刻响应后果(空队列或满队列),则返回特定的值(通常是true/false),true示意办法执行胜利,否则示意办法执行失败。阻塞后始终期待: 如果调用办法后不能立刻响应后果(空队列或满队列),该办法将被阻塞始终处于期待状态。阻塞后期待超时: 如果调用办法后不能立刻响应后果(空队列或满队列),该办法将在肯定工夫范畴内被阻塞期待,也就是在超时工夫范畴内阻塞。当超出超时工夫之后,办法线程将不再阻塞,而是返回一个特定的值(通常是true/false),true示意办法执行胜利,否则示意办法执行失败。BlockingDeque继承BlockingQueueBlockingDeque接口继承了BlockingQueue接口。这意味着你能够将BlockingDeque作为一个BlockingQueue应用。如果你这样做,各种插入方法将把元素增加到deque的开端,而移除办法将从deque的队首移除元素。BlockingQueue接口的插入和删除办法,就是这样做的。 上面是BlockingQueue的办法在BlockingDeque实现中的作用对照表 BlockingQueueBlockingDequeadd()addLast()offer()offerLast()put()putLast() remove()removeFirst()pollpollFirst()take()takeFirst() element()getFirst()peek()peekFirst()BlockingDeque 接口实现类BlockingDeque是一个接口,所以当咱们真正对它进行实例化的时候,须要应用它的接口实现类。在java.util.concurrent包中的LinkedBlockingDeque办法实现了BlockingDeque接口。 其应用办法也与BlockingQueue 大同小异,所以此处只做简略的介绍。 //初始化一个LinkedBlockingDequeBlockingDeque<String> deque = new LinkedBlockingDeque<String>();deque.addFirst("1");//向队首增加元素deque.addLast("2"); //向队尾增加元素String two = deque.takeLast(); //从队尾获取元素String one = deque.takeFirst(); //从队首获取元素欢送关注我的博客,外面有很多精品合集本文转载注明出处(必须带连贯,不能只转文字):字母哥博客 - zimug.com 感觉对您有帮忙的话,帮我点赞、分享!您的反对是我不竭的创作能源! 。另外,笔者最近一段时间输入了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》《Spring Security-JWT-OAuth2一本通》《实战前后端拆散RBAC权限管理系统》《实战SpringCloud微服务从青铜到王者》《VUE深入浅出系列》

March 29, 2021 · 1 min · jiezi

关于spring:java并发编程工具类JUC第六篇SynchronousQueue同步队列

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue、PriorityBlockingQueue,本文为系列文章第六篇。 本篇文章将为大家介绍并发编程汇合类SynchronousQueue,它是BlockingQueue接口的实现类。与所有的BlockingQueue接口实现类不同的是:SynchronousQueue队列的容量永远是0(或者能够了解为容量为1的队列,然而说队列容量为1并不精确),这是因为SynchronousQueue实际上它不是一个真正的队列,因为它不会为队列中元素保护存储空间,它只是多个线程之间数据交换的媒介。 SynchronousQueue队列的一个线程的插入动作总是会期待另一个线程的移除操作,反之亦然。put()办法放入元素对象到 SynchronousQueue不会返回后果(处于阻塞状态),直到另一个线程执行take()移除元素对象.peek()办法在BlockingQueue接口实现类中能够获取元素,但不从队列中移除该元素。但在SynchronousQueue队列中该办法不可用。SynchronousQueue 同步队列例子上面咱们写一个例子,来帮忙咱们了解SynchronousQueue的个性及用法。SynchronousQueueProducer.java 启动线程每隔一秒向队列中放入一个Integer对象。 public class SynchronousQueueProducer implements Runnable { protected BlockingQueue<Integer> blockingQueue; public SynchronousQueueProducer(BlockingQueue<Integer> queue) { this.blockingQueue = queue; } @Override public void run() { int i = 0; while (true) { System.out.println(Thread.currentThread().getName() + " Put: " + ++i); try { blockingQueue.put(i); Thread.sleep(1000); //每隔一秒生产一次 } catch (InterruptedException e) { e.printStackTrace(); } } }}SynchronousQueueConsumer.java启动线程每5秒从队列中取出一个元素对象。 public class SynchronousQueueConsumer implements Runnable { protected BlockingQueue<Integer> blockingQueue; public SynchronousQueueConsumer(BlockingQueue<Integer> queue) { this.blockingQueue = queue; } @Override public void run() { while (true) { try { Integer data = blockingQueue.take(); System.out.println(Thread.currentThread().getName() + " take(): " + data); Thread.sleep(5000); //每隔5秒生产一次 } catch (InterruptedException e) { e.printStackTrace(); } } }}SynchronousQueueExample.java 新建一个SynchronousQueue同步队列,启动一个生产者线程插入对象,两个消费者线程移除对象。进行测试,看看成果。 ...

March 27, 2021 · 1 min · jiezi

关于spring:java并发编程工具类JUC第五篇PriorityBlockingQueue优先级队列

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue、LinkedBlockingQueue,本文为系列文章第五篇。 Java PriorityBlockingQueue队列是BlockingQueue接口的实现类,它依据priority优先级确定队列内元素对象的解决程序,也就是说在一个PriorityBlockingQueue队列中,被增加到队列中的元素,依据priority进行排序。PriorityBlockingQueue具备BlockingQueue阻塞队列的一些个性,如果您不相熟BlockingQueue能够参看我之前的文章。 1. PriorityBlockingQueue 个性PriorityBlockingQueue 是一个无界队列(队列内元素个数没有下限),队列容量能够主动增长。其初始化队列容量为11,也能够通过结构函数参数initialCapacity指定其初始化容量。不承受 NULL对象插入到PriorityBlockingQueue增加到PriorityBlockingQueue队列中的元素对应的Java类,通常须要实现Comparable接口或者是能够默认排序的对象(如数字、字符串),否则会抛出ClassCastException能够应用java8 的Comparator提供自定义队列内元素的排序规定,后文会举例说明。如果存在多个对象领有相等的优先级,从队列中poll获取元素的时候可能获取到其中任何一个元素。PriorityBlockingQueue 是线程平安的2. PriorityBlockingQueue 利用实例咱们写一个类Employee,该类实现了Comparable接口,所以其实例对象能够依据compareTo()函数定义的规定进行排序。 public class Employee implements Comparable<Employee> { private Long id; private String name; private LocalDate dob; //Getters and setters public Employee(Long id, String name, LocalDate dob) { super(); this.id = id; this.name = name; this.dob = dob; } @Override public int compareTo(Employee emp) { return this.getId().compareTo(emp.getId()); //依据id排序 } @Override public String toString() { return "Employee [id=" + id + ", name=" + name + ", dob=" + dob + "]"; }}结构一个PriorityBlockingQueue对象,并向其外部退出若干Employee对象,并应用poll办法从队列内取出元素。 ...

March 26, 2021 · 1 min · jiezi

关于spring:java并发编程工具类JUC第四篇LinkedBlockingQueue链表队列

在之前的文章中曾经为大家介绍了java并发编程的工具:BlockingQueue接口、ArrayBlockingQueue、DelayQueue。 LinkedBlockingQueue 队列是BlockingQueue接口的实现类,所以它具备BlockingQueue接口的所有性能特点。LinkedBlockingQueue队列 依照first-in-first-out (FIFO)先进先出的形式对元素进行排序。LinkeBlockingQueue 提供了两种构造函数,一个构造函数结构一个队列容量为固定个数的队列,另一个无参构造函数结构一个队列容量为Integer.MAX_VALUE的队列. public LinkedBlockingQueue() { this(Integer.MAX_VALUE);}public LinkedBlockingQueue(int capacity) { if (capacity <= 0) throw new IllegalArgumentException(); this.capacity = capacity; last = head = new Node<E>(null);}ArrayBlockingQueue和LinkedBlockingQueue比照ArrayBlockingQueue和LinkedBlockingQueue都是实现BlockingQueue接口,所以在应用形式上是统一的,上面咱们就不介绍应用办法,而是从二者的性能及底层数据结构的实现角度进行 ArrayBlockingQueue插入和删除数据,只采纳了一个lock锁,读取和写入操作无奈并行。 所以在高并发场景下执行效率会比LinkedBlockingQueue慢一些。 LinkedBlockingQueue采纳“two lock queue”算法变体,双锁(ReentrantLock):takeLock、putLock,容许读写并行,remove(e)和迭代器iterators须要获取2个锁。这样能够升高线程因为线程无奈获取到lock而进入WAITING状态的可能性,从而进步了线程并发执行的效率。 ArrayBlockingQueue底层代码是采纳数组实现的,创立的时候必须指定队列的容量并调配存储空间;LinkedBlockingQueue采纳的是链表数据结构实现的,其链表节点的存储空间调配是动静的,新的元素对象退出队列调配空间,元素对象从队列取出之后存储空间GC,初始化时指定的是队列的最大容量。然而应用链表数据结构既是LinkedBlockingQueue劣势也是它的劣势,高并发场景下因为空间动态分配须要java JVM频繁的进行垃圾回收。总体来说在并发场景下,LinkedBlockingQueue的吞吐量比ArrayBlockingQueue更好。然而在java实现高性能队列的首选是disruptor,它不是JDK自带的。java程序员十分相熟的Log4j2底层性能比logback和log4j有了较大的晋升,究其原因就是应用了disruptor高性能队列实现的异步日志 欢送关注我的博客,外面有很多精品合集本文转载注明出处(必须带连贯,不能只转文字):字母哥博客 - zimug.com 感觉对您有帮忙的话,帮我点赞、分享!您的反对是我不竭的创作能源! 。另外,笔者最近一段时间输入了如下的精品内容,期待您的关注。 《手摸手教你学Spring Boot2.0》《Spring Security-JWT-OAuth2一本通》《实战前后端拆散RBAC权限管理系统》《实战SpringCloud微服务从青铜到王者》《VUE深入浅出系列》

March 25, 2021 · 1 min · jiezi

关于spring:java并发编程工具类JUC第三篇DelayQueue延时队列

DelayQueue 是BlockingQueue接口的实现类,它依据"延时工夫"来确定队列内的元素的解决优先级(即依据队列元素的“延时工夫”进行排序)。另一层含意是只有那些超过“延时工夫”的元素能力从队列外面被拿进去进行解决。 DelayQueue 队列将阻止其元素对象从队列中被取出,直到达到为元素对象设置的延迟时间。DelayQueue 在队列的头部存储最近过期的元素,如果队列内没有元素过期,应用poll()办法获取队列内的元素将会返回null。DelayQueue 类及其迭代器实现了Collection和Iterator接口的所有可选办法,但迭代器办法iterator()不能保障以特定的程序遍历DelayQueue的元素。 DelayQueue 不接管null元素,DelayQueue 只承受那些实现了java.util.concurrent.Delayed接口的对象,并将其放入队列内。DelayQueue 通过调用元素对象的getDelay(TimeUnit) 办法获取该元素残余的“延迟时间”。getDelay()的 TimeUnit工夫单位是一个枚举类型 : DAYS(天), HOURS(小时), MINUTES(分钟), SECONDS(秒), MILLISECONDS(毫秒), MICROSECONDS(奥妙), NANOSECONDS(纳秒)public interface Delayed extends Comparable{   long getDelay(TimeUnit unit);}上面咱们就写一个java Class实现Delayed 接口,只有实现了Delayed 接口的类对象能力放入DelayQueue。因为Delayed接口继承自Comparable接口,所以咱们必须实现getDelay办法和compareTo办法。 class DelayObject implements Delayed { private String name; private long time; //延时工夫 public DelayObject(String name, long delayTime) { this.name = name; this.time = System.currentTimeMillis() + delayTime; } @Override public long getDelay(TimeUnit unit) { long diff = time - System.currentTimeMillis(); return unit.convert(diff, TimeUnit.MILLISECONDS); } @Override public int compareTo(Delayed obj) { if (this.time < ((DelayObject)obj).time) { return -1; } if (this.time > ((DelayObject)obj).time) { return 1; } return 0; } @Override public String toString() { Date date = new Date(time); SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); return "\nDelayObject:{" + "name=" + name + ", time=" + sd.format(date) + "}"; } } 测试延时队列DelayQueue的应用成果 ...

March 24, 2021 · 1 min · jiezi

关于spring:SpringMVC-源码分析之-FrameworkServlet

后面和小伙伴们聊了 SpringMVC 的初始化流程,置信大家对于 SpringMVC 的初始化过程都有一个根本认知了,明天咱们就来看看当一个申请达到后,它的执行流程是什么样的?当然这个流程比拟长,松哥这里可能会分两篇文章来和大家分享。 很多小伙伴都晓得 SpringMVC 的外围是 DispatcherServlet,而 DispatcherServlet 的父类就是 FrameworkServlet,因而咱们先来看看 FrameworkServlet,这有助于咱们了解 DispatcherServlet。 1.FrameworkServletFrameworkServlet 继承自 HttpServletBean,而 HttpServletBean 继承自 HttpServlet,HttpServlet 就是 JavaEE 里边的货色了,这里咱们不做探讨,从 HttpServletBean 开始就是框架的货色了,然而 HttpServletBean 比拟非凡,它的非凡在于它没有进行任何的申请解决,只是参加了一些初始化的操作,这些比较简单,而且咱们在上篇文章中也曾经剖析过了,所以这里咱们对 HttpServletBean 不做剖析,就间接从它的子类 FrameworkServlet 开始看起。 和所有的 Servlet 一样,FrameworkServlet 对申请的解决也是从 service 办法开始,咱们先来看看该办法 FrameworkServlet#service: @Overrideprotected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { HttpMethod httpMethod = HttpMethod.resolve(request.getMethod()); if (httpMethod == HttpMethod.PATCH || httpMethod == null) { processRequest(request, response); } else { super.service(request, response); }}能够看到,在该办法中,首先获取到以后申请办法,而后对 patch 申请额定关照了下,其余类型的申请通通都是 super.service 进行解决。 ...

March 23, 2021 · 4 min · jiezi

关于spring:java并发编程工具类JUC第二篇ArrayBlockingQueue

类ArrayBlockingQueue是BlockingQueue接口的实现类,它是有界的阻塞队列,外部应用数组存储队列元素。这里的“有界”是指存储容量存在下限,不能有限存储元素。在同一时间内存储容量存在着一个上限值,这个上限度在初始实例化的时候指定,之后便不能批改了。 ArrayBlockingQueue外部采纳FIFO (First In, First Out)先进先出的办法实现队列数据的存取,队首的元素是在队列中保留工夫最长的元素对象,队尾的元素是在队列中保留工夫最短的元素对象。 上面的代码阐明如何初始化一个ArrayBlockingQueue,并向其中增加一个对象: BlockingQueue queue = new ArrayBlockingQueue(1024);queue.put("1"); //向队列中增加元素Object object = queue.take(); //从队列中取出元素BlockingQueue能够通过泛型来限定队列中存储数据的类型,上面的代码以String为泛型,示意该队列只能存储String类型。 BlockingQueue<String> queue = new ArrayBlockingQueue<String>(1024);queue.put("1");String string = queue.take();实现一个生产生产的实例在后面的文章中咱们已经讲过:BlockingQueue常常被用于生产生产的缓冲队列。上面咱们就应用ArrayBlockingQueue来真正的实现一个生产生产的例子。 类BlockingQueueExample开启两个独立线程,一个是Producer生产者线程,负责向队列中增加数据;另一个是Consumer消费者线程,负责从队列中取出数据进行解决。 public class BlockingQueueExample { public static void main(String[] args) throws Exception { //应用ArrayBlockingQueue初始化一个BlockingQueue,指定容量的下限为1024 BlockingQueue queue = new ArrayBlockingQueue(1024); Producer producer = new Producer(queue); //生产者 Consumer consumer = new Consumer(queue); //消费者 new Thread(producer).start(); //开启生产者线程 new Thread(consumer).start(); //开启消费者线程 Thread.sleep(4000); }}类Producer为生产者,每隔10秒钟应用put()办法向队列中放入一个对象,放入三次。在这10秒的距离内,队列数据被消费者取走之后将导致消费者线程阻塞。 public class Producer implements Runnable{ protected BlockingQueue queue = null; public Producer(BlockingQueue queue) { this.queue = queue; } public void run() { try { queue.put("1"); Thread.sleep(10000); queue.put("2"); Thread.sleep(10000); queue.put("3"); } catch (InterruptedException e) { e.printStackTrace(); } }}上面的代码是消费者类Consumer,它从队列中获取待处理的元素对象,并调用System.out将其打印进去。 ...

March 23, 2021 · 1 min · jiezi

关于spring:java并发编程工具类JUC第一篇BlockingQueue阻塞队列

Java BlockingQueue接口java.util.concurrent.BlockingQueue示意一个能够存取元素,并且线程平安的队列。换句话说,当多线程同时从 JavaBlockingQueue中插入元素、获取元素的时候,不会导致任何并发问题(元素被插入屡次、解决屡次等问题)。 从java BlockingQueue能够引申出一个概念:阻塞队列,是指队列自身能够阻塞线程向队列外面插入元素,或者阻塞线程从队列外面获取元素。比方:当一个线程尝试去从一个空队列外面获取元素的时候,这个线程将被阻塞直到队列内元素数量不再为空。当然,线程是否会被阻塞取决于你调用什么办法从BlockingQueue获取元素,有的办法会阻塞线程,有的办法会抛出异样等等,下文咱们会具体介绍。 一、BlockingQueue 接口实现类本文不会去介绍如何本人实现BlockingQueue接口,JUC曾经为咱们做好了相干的一些接口实现类。BlockingQueue是一个java接口,当咱们须要应用阻塞队列的时候,能够应用它的实现类。java.util.concurrent包外面有如下的一些实现类实现了BlockingQueue接口。 ArrayBlockingQueueDelayQueueLinkedBlockingQueueLinkedBlockingDequeLinkedTransferQueuePriorityBlockingQueueSynchronousQueue在本文以及后续的文章中,会顺次为大家介绍这些实现类的作用及应用场景,期待您的关注。 二、BlockingQueue 利用场景介绍BlockingQueue通常被利用在一个线程生产对象放入队列,与此同时另一个线程生产队列内的对象的场景下。上面的这张图阐明了应用场景: 生产者线程一直的生产新的对象,并将他们插入到BlockingQueue,直到队列中object的数量达到队列存储容量的下限。也就是说当队列中对象达到容量下限的时候,生产者线程将被阻塞,不能再向队列中插入新的对象。生产者线程将放弃阻塞期待状态,直到消费者线程从队列中拿走Object,让队列有空余地位放入新的对象。 消费者线程一直的从BlockingQueue取出对象并将其进行解决。如果消费者线程尝试从一个空队列中获取一个对象,消费者线程将被阻塞处于期待状态,直到生产者向队列中放入一个新的对象。 所以BlockingQueue常常被用于生产生产的缓冲队列,如果你不想用分布式的或者中间件音讯队列(redis、kafka)等(因为对于一个小性能会减少比拟大的独立中间件运维老本),BlockingQueue能够能是一个备选的选项。 2.1.BlockingQueue 办法介绍JavaBlockingQueue 提供了四组不同的办法用于向队列中插入、移除、查看队列中蕴含某一元素对象。每一组办法在被调用之后的响应行为上有所不同,如下: 抛出异样返回特定值阻塞后始终期待阻塞后期待超时插入对象add(o)offer(o)put(o)offer(o, timeout, timeunit)移除对象remove(o)poll()take()poll(timeout, timeunit)查看对象存在element()peek() 下面的办法的四种行为别离的含意是 抛出异样: 如果调用办法后不能立刻响应后果(空队列或满队列),则抛出异样。返回特定值: 如果调用办法后不能立刻响应后果(空队列或满队列),则返回特定的值(通常是true/false),true示意办法执行胜利,否则示意办法执行失败。阻塞后始终期待: 如果调用办法后不能立刻响应后果(空队列或满队列),该办法将被阻塞始终处于期待状态。阻塞后期待超时: 如果调用办法后不能立刻响应后果(空队列或满队列),该办法将在肯定工夫范畴内被阻塞期待,也就是在超时工夫范畴内阻塞。当超出超时工夫之后,办法线程将不再阻塞,而是返回一个特定的值(通常是true/false),true示意办法执行胜利,否则示意办法执行失败。另外,BlockingQueue队列不容许向其外部插入null,如果你向队列中插入null,将会引发NullPointerException异样。个别的队列都是从队首放入对象,从队尾获取对象,BlockingQueue不仅反对从队首队尾操作数据对象,还反对从队列中其余任何地位操作数据。比方:你曾经向队列中放入一个对象并期待解决,然而出于某些非凡起因心愿将这个对象从队列中删除掉。你能够调用remove(o)办法来删除队列中的一个特定的o对象。当然咱们的程序不能经常性的这样做,因为队列这种数据结构常常从两头地位操作数据的效率是极低的,所以除非必要不倡议这样做。 add(o)BlockingQueueadd() 办法能够将o对象以参数的模式插入到队列外面,如果队列外面有残余空间,将被立刻插入;如果队列外面没有残余空间,add()办法将跑出 IllegalStateException. offer(o)BlockingQueueoffer() 办法能够将o对象以参数的模式插入到队列外面,如果队列外面有残余空间,将被立刻插入;如果队列外面没有残余空间,offer()办法将返回特定的值false. offer(o, long millis, TimeUnit timeUnit)BlockingQueueoffer() 办法有另外一个版本的实现,存在超时工夫的设置参数。这个版本的offer()办法将o对象以参数的模式插入到队列外面,如果队列外面有残余空间,将被立刻插入;如果队列外面没有残余空间,调用offer办法的线程在超时工夫内将被阻塞处于等到状态,当阻塞工夫大于超时工夫之后,队列内如果依然没有残余空间放入新对象,offer()办法将返回false. put(o)BlockingQueueput() 办法能够将o对象以参数的模式插入到队列外面,如果队列外面有残余空间,将被立刻插入;如果队列外面没有残余空间,调用put的办法的线程将被阻塞,直到BlockingQueue外面腾出新的空间能够放入对象为止。 take()BlockingQueuetake() 办法取出并移除队列中的第一个元素(对象),如果BlockingQueue队列中不蕴含任何的元素,调用take()办法的线程将被阻塞,直到有新的元素对象插入到队列中为止。 poll()BlockingQueuepoll() 办法取出并移除队列中的第一个元素(对象),如果BlockingQueue队列中不蕴含任何的元素,poll()办法将返回null. poll(long timeMillis, TimeUnit timeUnit)BlockingQueuepoll(long timeMillis, TimeUnit timeUnit)办法同样存在一个超时工夫限度的版本,失常状况下该办法取出并移除队列中的第一个元素(对象)。如果BlockingQueue队列中不蕴含任何的元素,在超时工夫范畴内,如果依然没有新的对象放入队列,这个版本的poll()办法将被阻塞处于期待状态;当阻塞工夫大于超时工夫之后,poll(long timeMillis, TimeUnit timeUnit)返回null remove(Object o)BlockingQueueremove(Object o) 办法能够从队列中删除一个以参数模式给定的元素对象,remove()办法应用o.equals(element)将传入参数o与队列中的对象进行一一比对,从而断定要删除的对象是否在队列中存在,如果存在就从队列中删除并返回true,否则返回false。 须要留神的是:如果队列中有多个与传入参数equals相等的对象,只删除其中一个,不会将队列中所有匹配的对象都删除。 peek()BlockingQueuepeek() 办法将取出队列中的第一个元素对象,然而并不会将其从队列中删除。如果队列中目前没有任何的元素,也就是空队列,peek()办法将返回null. element()BlockingQueueelement()办法将取出队列中的第一个元素对象,然而并不会将其从队列中删除。如果队列中目前没有任何的元素,也就是空队列,element()办法将抛出 NoSuchElementException. contains(Object o)BlockingQueuecontains(Object o) 办法用来判断以后队列中是否存在某个对象,该对象与传入参数o相等(Objects.equals(o, element)被用来断定对象的相等性)。遍历队列中的所有元素,一旦在队列中发现匹配的元素对象,该办法将返回true;如果没有任何的元素匹配相等,该办法返回false。 ...

March 22, 2021 · 1 min · jiezi

关于spring:idea配置远程Debug补充缺失的图片

增加启动参数 -Drebel.remoting_plugin=true -Xms256m -Xmx256m -Xdebug -Xrunjdwp:transport=dt_socket,address=18302,server=y,suspend=n配置idea

March 17, 2021 · 1 min · jiezi

关于spring:Idea配置远程Debug

增加启动参数 -Drebel.remoting_plugin=true -Xms256m -Xmx256m -Xdebug -Xrunjdwp:transport=dt_socket,address=18302,server=y,suspend=n配置idea

March 17, 2021 · 1 min · jiezi

关于spring:tomcat改jar

<!-- <plugin>--><!-- <groupId>org.apache.maven.plugins</groupId>--><!-- <artifactId>maven-war-plugin</artifactId>--><!-- <version>3.1.0</version>--><!-- <configuration>--><!-- <resourceEncoding>UTF-8</resourceEncoding>--><!-- </configuration>--><!-- </plugin>--> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <configuration> <mainClass>HuishanProviderApplication</mainClass> </configuration> </plugin> <artifactId>huishan-provider</artifactId><!-- <packaging>war</packaging>--> <packaging>jar</packaging> <dependencies>package com;import com.fedtech.commons.util.SpringContextUtils;import org.springframework.boot.SpringApplication;import org.springframework.boot.autoconfigure.SpringBootApplication;import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;import org.springframework.boot.builder.SpringApplicationBuilder;import org.springframework.boot.web.support.SpringBootServletInitializer;import org.springframework.context.annotation.Bean;import org.springframework.scheduling.annotation.EnableScheduling;@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})@EnableSchedulingpublic class HuishanProviderApplication extends SpringBootServletInitializer { public static void main(String[] args) { System.setProperty("spring.devtools.restart.enabled", "false"); SpringApplication.run(HuishanProviderApplication.class, args); } @Bean public SpringContextUtils springContextUtils(){ return new SpringContextUtils(); }// @Override// protected SpringApplicationBuilder configure(SpringApplicationBuilder application){// return application.sources(HuishanProviderApplication.class);// }}

March 17, 2021 · 1 min · jiezi

关于spring:搭建文件服务器

下载httpdyum install -y httpd看看胜利了没有http://localhost 配置监听端口和暗藏目录vim /etc/httpd/conf/httpd.conf批改端口 ## Listen: Allows you to bind Apache to specific IP addresses and/or# ports, instead of the default. See also the <VirtualHost># directive.## Change this to Listen on specific IP addresses as shown below to # prevent Apache from glomming onto all bound IP addresses.##Listen 12.34.56.78:80Listen 9090去掉Indexes,这样页面就看不到目录构造了 <Directory "/var/www/html/file"> # # Possible values for the Options directive are "None", "All", # or any combination of: # Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews # # Note that "MultiViews" must be named *explicitly* --- "Options All" # doesn't give it to you. # # The Options directive is both complicated and important. Please see # http://httpd.apache.org/docs/2.4/mod/core.html#options # for more information. # Options FollowSymLinks # # AllowOverride controls what directives may be placed in .htaccess files. # It can be "All", "None", or any combination of the keywords: # Options FileInfo AuthConfig Limit # AllowOverride None # # Controls who can get stuff from this server. # Require all granted</Directory>systemctl restart httpdnetstat -nltp | grep 9090指定目录先到默认的映射地址下 ...

March 17, 2021 · 1 min · jiezi

关于spring:文件上传

private void upload(List<MultipartFile> multipartFiles) throws Exception { for (MultipartFile multipartFile : multipartFiles) { String fileName = multipartFile.getOriginalFilename(); String filePath = "F:/test"; String fileTotalName = filePath + File.separator + fileName; File f = new File(fileTotalName); multipartFile.transferTo(f); } } @PostMapping("upload") public R upload(HttpServletRequest request) throws Exception { List<MultipartFile> files = new ArrayList<>(); MultipartHttpServletRequest multipartHttpServletRequest = (MultipartHttpServletRequest) request; Iterator<String> a = multipartHttpServletRequest.getFileNames();//返回的数量与前端input数量雷同, 返回的字符串即为前端input标签的name while (a.hasNext()) { String name = a.next(); List<MultipartFile> multipartFiles = multipartHttpServletRequest.getFiles(name);//获取单个input标签上传的文件,可能为多个 files.addAll(multipartFiles); } upload(files); return R.success(); }

March 17, 2021 · 1 min · jiezi

关于spring:传参统一jwt加密坑

阐明这个波及到的次要是和前端约定加密形式,我这边被动采纳了jwt形式,起因吗就不解释了。 实现起来大抵思维就是做拦挡,之前尝试了一版注解形式,做了一半想到一个问题。。注解拦挡的参数是曾经绑定之后的了。。要是本人再去解决的话就会十分麻烦。。 起初果决放弃,想了想还是应用过滤不便,想着只有把申请参数改了不就行了,实际上这样的确是可行的,就是有个坑,得同时批改getParamNames办法,不然参数是绑定不了的 先说实现吧定义一个过滤器package com.fedtech.common.filter.jwt;import org.springframework.stereotype.Component;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import java.io.IOException;/** * 将约定的申请头参数应用jwt转化为一般参数,供零碎应用 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */@Componentpublic class JwtFilter implements Filter { @Override public void doFilter(ServletRequest arg0, ServletResponse arg1, FilterChain arg2) throws IOException, ServletException { ParameterRequestWrapper paramsRequest = new ParameterRequestWrapper((HttpServletRequest) arg0); String[] secretParams = paramsRequest.getParameterMap().get("secretParam"); if (secretParams == null) { arg2.doFilter(arg0, arg1); } else { arg2.doFilter(paramsRequest, arg1); } } @Override public void init(FilterConfig arg0) { } @Override public void destroy() { }}重写获取参数相干的办法package com.fedtech.common.filter.jwt;import com.alibaba.fastjson.JSON;import com.fedtech.common.util.StringJsonUtils;import io.jsonwebtoken.Claims;import io.jsonwebtoken.Jws;import io.jsonwebtoken.Jwts;import lombok.extern.slf4j.Slf4j;import org.apache.commons.io.IOUtils;import org.springframework.util.StringUtils;import org.springframework.web.util.WebUtils;import javax.servlet.ReadListener;import javax.servlet.ServletInputStream;import javax.servlet.ServletRequest;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.io.ByteArrayInputStream;import java.io.IOException;import java.nio.charset.StandardCharsets;import java.util.*;import static com.fedtech.common.constants.Request.RequestConstants.CONTENT_TYPE;import static org.apache.commons.lang3.StringUtils.equalsAny;import static org.apache.commons.lang3.StringUtils.isNotBlank;/** * 1. 接口申请过滤去除前后空格 * 2. 解析jwt参数 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/1/26 * @since 1.0.0 */@Slf4jpublic class ParameterRequestWrapper extends HttpServletRequestWrapper { /** * 这个东东就是request.getParam()的params */ private final Map<String, String[]> params = new HashMap<>(); /** * 解析jwt就是在这边做的,次要思维就是批改params,毕竟官网没提供setParam办法 * 该办法还同时对参数前后空格做了解决 * * @param request 申请 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */ public ParameterRequestWrapper(HttpServletRequest request) { // 将request交给父类,以便于调用对应办法的时候,将其输入,其实父亲类的实现形式和第一种new的形式相似 super(request); //将参数表,赋予给以后的Map以便于持有request中的参数 Map<String, String[]> requestMap = new HashMap<>(request.getParameterMap()); //这边拿到和前端约定的参数名 String[] secretParams = requestMap.get("secretParam"); if (secretParams == null) { return; } String secretParam = secretParams[0]; //没有加密参数的话就不解决了 if (isNotBlank(secretParam)) { //jwt解析一波 Jws<Claims> jws = Jwts.parser() .setSigningKey("secret".getBytes(StandardCharsets.UTF_8)) .parseClaimsJws(secretParam); //这边只有过滤掉几个不须要的参数就行啦啦啦啦!!!! jws.getBody().forEach((x, y) -> { if (!equalsAny(x, "sub", "exp", "lat", "jti", "iat")) { requestMap.put(x, new String[]{String.valueOf(y)}); } }); } //在这边全副存起来哈!! this.params.putAll(requestMap); //这个是div的,去除前后空格 this.modifyParameterValues(); } /** * 重写getInputStream办法 post类型的申请参数必须通过流能力获取到值 * * @return javax.servlet.ServletInputStream * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */ @Override public ServletInputStream getInputStream() throws IOException { //非json类型,间接返回 if (!super.getHeader(CONTENT_TYPE).equalsIgnoreCase("json")) { return super.getInputStream(); } //为空,间接返回 String json = IOUtils.toString(super.getInputStream(), StandardCharsets.UTF_8); if (StringUtils.isEmpty(json)) { return super.getInputStream(); } log.info("转化前参数:{}", json); Map<String, Object> map = StringJsonUtils.jsonStringToMap(json); log.info("转化后参数:{}", JSON.toJSONString(map)); ByteArrayInputStream bis = new ByteArrayInputStream(JSON.toJSONString(map).getBytes(StandardCharsets.UTF_8)); return new MyServletInputStream(bis); } /** * 将parameter的值去除空格并且空串返回 null值 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */ private void modifyParameterValues() { Set<String> set = params.keySet(); for (String key : set) { String[] values = params.get(key); String[] newValues = new String[values.length]; for (int i = 0; i < values.length; i++) { newValues[i] = values[i].trim(); if (newValues[i].length() <= 0) { newValues[i] = null; } } params.put(key, newValues); } } /** * 这个吗是最次要的,去看mvn解析链的话,在{@link WebUtils#getParametersStartingWith(ServletRequest, String)}这个外面获取参数就是走的这个办法 * * @return java.util.Enumeration<java.lang.String> * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */ @Override public Enumeration<String> getParameterNames() { Vector<String> vector = new Vector<>(params.keySet()); return vector.elements(); } /** * 重写getParameter 参数从以后类中的map获取 * * @return java.lang.String * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */ @Override public String getParameter(String name) { String[] values = params.get(name); if (values == null || values.length == 0) { return null; } return values[0]; } /** * 重写getParameterValues * * @return java.lang.String[] * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/9 * @since 1.0.0 */ @Override public String[] getParameterValues(String name) {//同上 return params.get(name); } static class MyServletInputStream extends ServletInputStream { private final ByteArrayInputStream bis; public MyServletInputStream(ByteArrayInputStream bis) { this.bis = bis; } @Override public boolean isFinished() { return true; } @Override public boolean isReady() { return true; } @Override public void setReadListener(ReadListener listener) { } @Override public int read() { return bis.read(); } }}这边就说下入口和重点办法吧 ...

March 16, 2021 · 3 min · jiezi

关于spring:XSS防御

自定义json反序列化器package cc.fedtech.filter;import com.fasterxml.jackson.core.JsonParser;import com.fasterxml.jackson.core.JsonProcessingException;import com.fasterxml.jackson.databind.DeserializationContext;import com.fasterxml.jackson.databind.JsonDeserializer;import org.springframework.web.util.HtmlUtils;import java.io.IOException;public class XssJsonDeserializer extends JsonDeserializer<String> { @Override public String deserialize(JsonParser jsonParser, DeserializationContext ctxt) throws IOException, JsonProcessingException { String value = jsonParser.getValueAsString(); if (value != null) { //对于值进行HTML本义 return HtmlUtils.htmlEscape(value); } return value; } @Override public Class<String> handledType() { return String.class; } }自定义json序列化器package cc.fedtech.filter;import com.fasterxml.jackson.core.JsonGenerator;import com.fasterxml.jackson.databind.JsonSerializer;import com.fasterxml.jackson.databind.SerializerProvider;import org.springframework.web.util.HtmlUtils;import java.io.IOException;public class XssJsonSerializer extends JsonSerializer<String> { @Override public Class<String> handledType() { return String.class; } @Override public void serialize(String value, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { if (value != null) { //对字符串进行HTML本义 jsonGenerator.writeString(HtmlUtils.htmlEscape(value)); } }}自定义拦截器package com.fedtech.common.filter.xss;import org.apache.commons.lang3.StringUtils;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.stereotype.Component;import javax.servlet.*;import javax.servlet.http.HttpServletRequest;import java.io.IOException;/** * 自定义过滤器 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/8 * @since 1.0.0 */@Component@Order(Ordered.HIGHEST_PRECEDENCE)public class XssFilter implements Filter { @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { String path = ((HttpServletRequest) request).getServletPath(); if (path.equals("/")) { chain.doFilter(request, response); } if (StringUtils.containsAny(path, "/assets", "/templates", "/mapper","/oauth")) { chain.doFilter(request, response); } chain.doFilter(new XssRequestWrapper((HttpServletRequest) request), response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { }}申请参数解决package cc.fedtech.filter;import org.apache.commons.lang3.StringUtils;import org.springframework.web.util.HtmlUtils;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletRequestWrapper;import java.util.Arrays;public class XssRequestWrapper extends HttpServletRequestWrapper { public XssRequestWrapper(HttpServletRequest request) { super(request); } @Override public String[] getParameterValues(String parameter) { //获取多个参数值的时候对所有参数值利用clean办法逐个清洁 return Arrays.stream(super.getParameterValues(parameter)).map(this::clean).toArray(String[]::new); } @Override public String getHeader(String name) { //同样清洁申请头 return clean(super.getHeader(name)); } @Override public String getParameter(String parameter) { //获取参数繁多值也要解决 return clean(super.getParameter(parameter)); } //clean办法就是对值进行HTML本义 private String clean(String value) { return StringUtils.isEmpty(value)? "" : HtmlUtils.htmlEscape(value); }} 注册反序列化器 //注册自定义的Jackson反序列器 @Bean public Module xssModule() { SimpleModule module = new SimpleModule(); module.addDeserializer(String.class, new XssJsonDeserializer()); module.addSerializer(String.class, new XssJsonSerializer()); return module; }

March 16, 2021 · 2 min · jiezi

关于spring:二bean的生命周期

前言:记得20年年初在B站上看过Bean的生命周期的视频,最近实习到了尾期,想筹备一下面试,就看了一下这部分的内容,发现网上博客各个版本都有,与之前看视频的解说也是不一样的。所以只能本人看一下源码,做此笔记。先理清上面两个单词吧 Instantiate: 实例化 ,java中体现为 new 构造方法 或 反射模式等…Initialization:初始化 ,初始化就是给变量一个初始值生命周期Bean的生命周期总的来说只有四个。实例化,属性填充,初始化和销毁。 而BeanPostProcessor 及其子接口 InstantiationAwareBeanPostProcessor就是在这生命周期的不同阶段之间做加强的。 在AbstractAutowireCapableBeanFactory # createBean具体细分为上面的流程 留神:有的Bean只须要通过BeforeInstantiation 和 postProcessAfterInitialization 这两个解决点就能够间接到就绪状态,这个也是aop实现原理,前面会另开一篇写。文字描述BeanDefinition:bean的定义信息。 BeforeInstantiation:Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. 容许实现InstantiationAwareBeanPostProcessor接口的bean 通过postProcessBeforeInstantiation办法做非凡解决返回一个代理对象。 Instantiate:实例化bean modify bean definition:容许MergedBeanDefinitionPostProcessor对bean定义信息批改。 AfterInstantiation:实例化后置加强。容许实现InstantiationAwareBeanPostProcessor接口的bean通过postProcessAfterInstantiation办法决定是否须要调用接口的postProcessProperties办法,若返回值为false,那么后续的postProcessProperties和图里的autowire 、applyPropertyValues就间接跳过了。 autowire:依据BeanDefinition的autowireMode注入属性值。 applyPropertyValues:依据InstantiationAwareBeanPostProcessor接口的postProcessProperties返回值设置属性值。 invokeAwareMethods: 如果bean是一个BeanNameAware,调用其setBeanName;如果bean是一个BeanClassLoaderAware调用其setBeanClassLoader办法,如果bean是一个BeanFactoryAware,调用其setBeanFactory办法设置以后工厂。 postProcessBeforeInitialization:初始化前加强。容许BeanPostProcessor通过postProcessBeforeInitialization办法对bean进行加强。 invokeInitMethods:对于实现InitializingBean接口的bean,调用其afterPropertiesSet办法初始化bean。调用自定义的init办法。进行bean的初始化。 postProcessAfterInitialization:初始化后加强。容许BeanPostProcessor通过postProcessAfterInitialization办法对bean进行加强。 registerDisposableBean:如果是单例bean,往beanFactory的disposableBeans增加。注册bean的destroy办法。 从下面的文字描述能够看出BeanPostProcessor和InstantiationAwareBeanPostProcessor接口是对所有的bean的失效的,而Aware接口是对单个bean失效的。 源码剖析上面还是开始看源码吧。就依照第三张=图的流程来剖析源码。BeforeInstantiation先看看AbstractAutowireCapableBeanFactory #createBean 办法 // 已删除无关代码 protected Object createBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args) throws BeanCreationException { try { // Give BeanPostProcessors a chance to return a proxy instead of the target bean instance. Object bean = resolveBeforeInstantiation(beanName, mbdToUse); if (bean != null) { return bean; } } Object beanInstance = doCreateBean(beanName, mbdToUse, args); }resolveBeforeInstantiation办法对应是实例化之前阶段的代码,doCreateBean是后续实例化、初始化阶段。 ...

March 16, 2021 · 6 min · jiezi

关于spring:Linux7搭建MySQL8

一、筹备查看Linux版本信息uname -a 卸载Linux7自带的mariadb# 查看相干包rpm -qa | grep mariadb# 卸载相干包rpm -e 查出来的包名,有依赖的一起删掉就行 下载相干包https://downloads.mysql.com/archives/community/ 二、装置写文章用的安装包上传将下载的包上传到服务器上RPM装置程序曾经列出来了,不要错了rpm -ivh mysql-community-common-8.0.23-1.el7.x86_64.rpmrpm -ivh mysql-community-client-plugins-8.0.23-1.el7.x86_64.rpmrpm -ivh mysql-community-libs-8.0.23-1.el7.x86_64.rpmrpm -ivh mysql-community-client-8.0.23-1.el7.x86_64.rpmrpm -ivh mysql-community-server-8.0.23-1.el7.x86_64.rpm查看mysqladmin --version三、启动配置默认的dataDir是对不上的。。须要批改下vim /etc/my.cnfdatadir=/var/lib/mysql/mysql启动systemctl start mysqldsystemctl status mysqld四、批改明码查看默认明码 cat /var/log/mysqld.log | grep password登录mysql -u root -p长期批改合乎规定的明码alter user root@localhost identified by 'Root_123456';更改明码策略SHOW VARIABLES LIKE 'validate_password%'; set global validate_password.policy = low;set global validate_password.length = 4; alter user root@localhost identified by '123456';update user set host='%' where host='localhost';systemctl restart mysqld;

March 15, 2021 · 1 min · jiezi

关于spring:Spring-Security权限注解

启用注解@EnableGlobalMethodSecurity(prePostEnabled = true)失常启用开启那个注解就行,上面放下我的配置 package com.fedtech.sys.provider.config.config;import org.springframework.context.annotation.Bean;import org.springframework.context.annotation.Configuration;import org.springframework.data.redis.connection.RedisConnectionFactory;import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter;import org.springframework.security.oauth2.config.annotation.web.configurers.ResourceServerSecurityConfigurer;import org.springframework.security.oauth2.provider.token.TokenStore;import org.springframework.security.oauth2.provider.token.store.redis.RedisTokenStore;import javax.annotation.Resource;/** * 资源配置 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/1/13 * @since 1.0.0 */@Configuration@EnableResourceServer@EnableGlobalMethodSecurity(prePostEnabled = true)public class ResourceServerConfig extends ResourceServerConfigurerAdapter { @Resource RedisConnectionFactory redisConnectionFactory; @Resource private TokenStore tokenStore; @Bean public TokenStore redisTokenStore() { return new RedisTokenStore(redisConnectionFactory); } @Override public void configure(ResourceServerSecurityConfigurer resources) { resources.tokenStore(tokenStore); }}角色 /** * 查问单个用户 * * @param query {@link UserQuery} * * @return com.fedtech.common.util.result.R<com.fedtech.sys.provider.view.UserView> * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/20 * @since 1.0.0 */ @GetMapping("select") @PreAuthorize("hasAuthority('admin')") public R<UserView> selectUser(UserQuery query) { UserDto dto = userService.selectUser(query); return R.successWithData(userMapper.dto2View(dto)); }权限默认的是DenyAllPermissionEvaluator,所有权限都回绝,所以要自定义 ...

March 15, 2021 · 3 min · jiezi

关于spring:XXLJOB

分布式任务调度平台XXL-JOB 初始化数据库执行官网提供的SQL即可/xxl-job/doc/db/tables_xxl_job.sql源码编译xuxueli/xxl-job 下载好源码后,须要对局部配置进行批改 xxl-job-admin:调度核心xxl-job-core:公共依赖xxl-job-executor-samples:执行器Sample示例(抉择适合的版本执行器,可间接应用,也能够参考其并将现有我的项目革新成执行器) :xxl-job-executor-sample-springboot:Springboot版本,通过Springboot治理执行器,举荐这种形式; :xxl-job-executor-sample-frameless:无框架版本;因为是间接部署,所以只须要批改调度核心配置即可 ### xxl-job, datasourcespring.datasource.url=jdbc:mysql://localhost:3306/xxl_job?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghaispring.datasource.username=guestspring.datasource.password=123456spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver### xxl-job, emailspring.mail.host=smtp.qq.comspring.mail.port=25spring.mail.username=njpkhuan@foxmail.comspring.mail.password=yltkhbpxjeacbbfjspring.mail.properties.mail.smtp.auth=truespring.mail.properties.mail.smtp.starttls.enable=truespring.mail.properties.mail.smtp.starttls.required=truespring.mail.properties.mail.smtp.socketFactory.class=javax.net.ssl.SSLSocketFactory装置调度核心 部署我的项目java -jar xxx &http://localhost:8080/xxl-job-admin 账号:明码 admin/123456 开发本人的工作官网有具体的教程 分布式任务调度平台XXL-JOB 依赖compile group: 'com.xuxueli', name: 'xxl-job-core'配置### xxl-job admin address list, such as "http://address" or "http://address01,http://address02"xxl.job.admin.addresses=http://127.0.0.1:18301/xxl-job-admin### xxl-job, access tokenxxl.job.accessToken=### xxl-job executor appnamexxl.job.executor.appname=pension-job### xxl-job executor registry-address: default use address to registry , otherwise use ip:port if address is nullxxl.job.executor.address=### xxl-job executor server-infoxxl.job.executor.ip=xxl.job.executor.port=9999### xxl-job executor log-pathxxl.job.executor.logpath=/data/applogs/xxl-job/jobhandler### xxl-job executor log-retention-daysxxl.job.executor.logretentiondays=30定时办法package com.fedtech.job.provider.service.jobHandler;import com.xxl.job.core.biz.model.ReturnT;import com.xxl.job.core.handler.IJobHandler;import com.xxl.job.core.handler.annotation.XxlJob;import com.xxl.job.core.log.XxlJobLogger;import com.xxl.job.core.util.ShardingUtil;import lombok.extern.slf4j.Slf4j;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.stereotype.Component;import java.io.BufferedInputStream;import java.io.BufferedReader;import java.io.DataOutputStream;import java.io.InputStreamReader;import java.net.HttpURLConnection;import java.net.URL;import java.util.Arrays;import java.util.concurrent.TimeUnit;/** * XxlJob开发示例(Bean模式) * <p> * 开发步骤: * 1、在Spring Bean实例中,开发Job办法,形式格局要求为 "public ReturnT<String> execute(String param)" * 2、为Job办法增加注解 "@XxlJob(value="自定义jobhandler名称", init = "JobHandler初始化办法", destroy = "JobHandler销毁办法")",注解value值对应的是调度核心新建工作的JobHandler属性的值。 * 3、执行日志:须要通过 "XxlJobLogger.log" 打印执行日志; * * @author xuxueli 2019-12-11 21:52:51 */@Component@Slf4jpublic class SampleXxlJob { /** * 1、简略工作示例(Bean模式) */ @XxlJob("demoJobHandler") public ReturnT<String> demoJobHandler(String param) throws Exception { XxlJobLogger.log("XXL-JOB, Hello World."); for (int i = 0; i < 5; i++) { XxlJobLogger.log("beat at:" + i); TimeUnit.SECONDS.sleep(2); } return ReturnT.SUCCESS; } /** * 2、分片播送工作 */ @XxlJob("shardingJobHandler") public ReturnT<String> shardingJobHandler(String param) throws Exception { // 分片参数 ShardingUtil.ShardingVO shardingVO = ShardingUtil.getShardingVo(); XxlJobLogger.log("分片参数:以后分片序号 = {}, 总分片数 = {}", shardingVO.getIndex(), shardingVO.getTotal()); // 业务逻辑 for (int i = 0; i < shardingVO.getTotal(); i++) { if (i == shardingVO.getIndex()) { XxlJobLogger.log("第 {} 片, 命中分片开始解决", i); } else { XxlJobLogger.log("第 {} 片, 疏忽", i); } } return ReturnT.SUCCESS; } /** * 3、命令行工作 */ @XxlJob("commandJobHandler") public ReturnT<String> commandJobHandler(String param) throws Exception { String command = param; int exitValue = -1; BufferedReader bufferedReader = null; try { // command process Process process = Runtime.getRuntime().exec(command); BufferedInputStream bufferedInputStream = new BufferedInputStream(process.getInputStream()); bufferedReader = new BufferedReader(new InputStreamReader(bufferedInputStream)); // command log String line; while ((line = bufferedReader.readLine()) != null) { XxlJobLogger.log(line); } // command exit process.waitFor(); exitValue = process.exitValue(); } catch (Exception e) { XxlJobLogger.log(e); } finally { if (bufferedReader != null) { bufferedReader.close(); } } if (exitValue == 0) { return IJobHandler.SUCCESS; } else { return new ReturnT<String>(IJobHandler.FAIL.getCode(), "command exit value(" + exitValue + ") is failed"); } } /** * 4、跨平台Http工作 * 参数示例: * "url: http://www.baidu.com\n" + * "method: get\n" + * "data: content\n"; */ @XxlJob("httpJobHandler") public ReturnT<String> httpJobHandler(String param) throws Exception { // param parse if (param == null || param.trim().length() == 0) { XxlJobLogger.log("param[" + param + "] invalid."); return ReturnT.FAIL; } String[] httpParams = param.split("\n"); String url = null; String method = null; String data = null; for (String httpParam : httpParams) { if (httpParam.startsWith("url:")) { url = httpParam.substring(httpParam.indexOf("url:") + 4).trim(); } if (httpParam.startsWith("method:")) { method = httpParam.substring(httpParam.indexOf("method:") + 7).trim().toUpperCase(); } if (httpParam.startsWith("data:")) { data = httpParam.substring(httpParam.indexOf("data:") + 5).trim(); } } // param valid if (url == null || url.trim().length() == 0) { XxlJobLogger.log("url[" + url + "] invalid."); return ReturnT.FAIL; } if (method == null || !Arrays.asList("GET", "POST").contains(method)) { XxlJobLogger.log("method[" + method + "] invalid."); return ReturnT.FAIL; } // request HttpURLConnection connection = null; BufferedReader bufferedReader = null; try { // connection URL realUrl = new URL(url); connection = (HttpURLConnection) realUrl.openConnection(); // connection setting connection.setRequestMethod(method); connection.setDoOutput(true); connection.setDoInput(true); connection.setUseCaches(false); connection.setReadTimeout(5 * 1000); connection.setConnectTimeout(3 * 1000); connection.setRequestProperty("connection", "Keep-Alive"); connection.setRequestProperty("Content-Type", "application/json;charset=UTF-8"); connection.setRequestProperty("Accept-Charset", "application/json;charset=UTF-8"); // do connection connection.connect(); // data if (data != null && data.trim().length() > 0) { DataOutputStream dataOutputStream = new DataOutputStream(connection.getOutputStream()); dataOutputStream.write(data.getBytes("UTF-8")); dataOutputStream.flush(); dataOutputStream.close(); } // valid StatusCode int statusCode = connection.getResponseCode(); if (statusCode != 200) { throw new RuntimeException("Http Request StatusCode(" + statusCode + ") Invalid."); } // result bufferedReader = new BufferedReader(new InputStreamReader(connection.getInputStream(), "UTF-8")); StringBuilder result = new StringBuilder(); String line; while ((line = bufferedReader.readLine()) != null) { result.append(line); } String responseMsg = result.toString(); XxlJobLogger.log(responseMsg); return ReturnT.SUCCESS; } catch (Exception e) { XxlJobLogger.log(e); return ReturnT.FAIL; } finally { try { if (bufferedReader != null) { bufferedReader.close(); } if (connection != null) { connection.disconnect(); } } catch (Exception e2) { XxlJobLogger.log(e2); } } } /** * 5、生命周期工作示例:工作初始化与销毁时,反对自定义相干逻辑; */ @XxlJob(value = "demoJobHandler2", init = "init", destroy = "destroy") public ReturnT<String> demoJobHandler2(String param) throws Exception { XxlJobLogger.log("XXL-JOB, Hello World."); return ReturnT.SUCCESS; } public void init() { log.info("init"); } public void destroy() { log.info("destory"); }}新建工作 ...

March 14, 2021 · 4 min · jiezi

关于spring:Jenkins自动构建项目

装置官网上下载war包扔到tomcat的webapp下,启动tomcat即可 配置Gradle push | merge主动构建配置触发器 Gitlab增加WebHook 主动杀死上个过程并启动新的jar杀死上个过程#!/bin/shtomcat_id=`ps -ef | grep sso | grep -v "grep" | awk '{print $2}'`echo $tomcat_idfor id in $tomcat_iddo kill -9 $id echo "killed $id" done启动新的过程BUILD_ID=dontKillMenohup java -jar /root/.jenkins/workspace/邳州养老平台/default/pension-sso/build/libs/pension-sso-0.0.1-SNAPSHOT.jarBUILD_ID=dontKillMenohup java -agentpath:/root/jrebel/lib/libjrebel64.so -Drebel.remoting_port=18303 -Drebel.remoting_plugin=true -Xms256m -Xmx256m -Xdebug -Xrunjdwp:transport=dt_socket,address=18302,server=y,suspend=n -jar /root/.jenkins/workspace/邳州养老平台/default/pension-sso/build/libs/pension-sso-0.0.1-SNAPSHOT.jar > /opt/logs/sso/dev.log 2>&1 &Idea配置可视化Jenkins增加Token/jenkins/user/用户/configure 复制生成的Token,就是拜访明码 启用代理兼容 获取CrumbcrumbIssuer/api/xml?tree=crumb# 配置ideaby 朱永胜

March 14, 2021 · 1 min · jiezi

关于spring:HikariCP

背景剖析咱们拜访数据库时,须要通过TCP协定与数据库建设连贯,连贯应用完当前要开释连贯,TCP协定是一个面向连贯的协定,而建设连贯须要三次握手,开释连贯须要四次挥手,这个过程是比拟耗时的,如果频繁拜访数据库,每次都是间接与数据建设连贯,会带来的很大的性能问题,对于这样的问题如何解决呢?连接池诞生了. 什么连接池?连接池是池化思维一种利用,基于享元模式做了落地的实现,就是在内存开拓一块区域,存储创立好的连贯,让这些连贯能够失去重用,进而进步数据库的拜访性能。 Java中连接池的实现? 在java中所有连接池的实现(c3p0,dbcp,druid,hikariCP,...)必须恪守java中的一个设计标准,此标准为javax.sql.DataSource,通过这个标准获取连接池的具体实现,进而取到连贯,实现与数据库通信. 1)Java中与数据库建设连贯须要什么?数据库驱动2)这个数据库驱动的设计须要恪守什么标准吗?JDBC3)当咱们通过JDBC API获取到一个连贯当前,利用完结咱们会将连贯返回到池中吗?会4)连接池在利用时有什么弊病吗?(会带来肯定的内存开销,以空间换工夫)5)如果当初让你去设计一个连接池,你会思考哪些问题?5.1)存储构造 (数组->随机拜访效率比拟高,链表-适宜随机插入和删除)5.2)算法 (FIFO,FILO,.........)5.3)线程平安 (锁-锁的粒度会影响性能和并发)5.4)参数的设计(最多有多少连贯,多余的连贯什么工夫被开释,.....) Bug?1.SqlException (Access denied for user --->拜访被回绝)2.CommunicationException(通信失败,查看数据库服务是否启动了)3.'Url' attribute is not specified (url没有配置或配置谬误)4.Unknow Host 'dbgoods' (查看数据库中是否有dbgoods数据库) SpringBoot 工程中基于HikariCP连接池实现一个JDBC业务 1).业务形容?(从数据库将商品信息查问进去并在测试类中做具体输入)2).API设计?(GoodsDao,DefaultGoodsDao,GoodsDaoTests)3).时序拜访设计?(GoodsDaoTest-->GoodsDao-->DefaultGoodsDao-->DataSource-->HikariDataSource->HikariPool->Connection-->JDBC)4)代码的具体落地实现?(GoodsDao,DefaultGoodsDao,GoodsDaoTests)5)具体JDBC代码实现如下:4.1)建设连贯(从池中获取Connection对象)4.2)创立Statement(基于此对象发送SQL)4.3)发送SQL执行查问操作4.4)解决查问后果,将后果存储List<Map<String,Object>>4.5)开释资源 1)数据元对象DataSource从哪里获取?(由Spring进行值的注入)2)如果DataSource对象的值为null是什么起因?(是否有个性注解形容)3)后果集映射时,Map中的key是谁?(字段名)4)Map对象存储数据时,key雷同,值会笼罩呢?会5)HashMap对象中的key是有序的吗?(无序,不能保障增加的程序)6)资源开释时有什么规定吗?(先创立的后敞开)7)你理解什么是元数据吗?(形容数据的数据,对于一张表而言元数据就是字段名)8)构建JDBC利用时你应用的是标准还是标准的实现?(标准,Connection,Statement,ResultSet,...) Bug?1.SQLException?(SQL语法问题)2.NullPointerException(空指针异样)

March 13, 2021 · 1 min · jiezi

关于spring:Spring-Boot-工程中Bean的依赖注入分析

背景剖析Spring框架除了要解决对象的资源耗费,对象的利用性能问题,是否还要思考对象的可保护,可扩展性.如果要思考这个个性,那么该如何实现呢?个别程序中会借助继承或组合实现这样个性,但继承或组合就离不开耦合,如何升高这种耦合呢? Spring 工程中的依赖注入剖析?Spring框架作为一个资源整合框架,为了升高类于类之间的耦合,进步其程序的可扩展性,举荐存在耦合时,要尽量耦合于接口或工厂(咱们当初我的项目中对象的创立底层是耦合了Spring的BeanFactory工厂).并由spring框架实现具体值的注入(DI). 1.@Autowire注解形容属性时,其依赖注入规定是怎么的? 当spring框架检测到由它治理的bean外部有属性应用了@Autowired注解形容,则会从spring容器中按属性类型查找对应的Bean对象(这个过程叫依赖查找-DL),如果只有一个则间接注入.如果有多个,还会基于属性名与bean对象的名字进行匹配,如果有匹配的则间接注入,没有匹配则抛出异样(依赖注入异样),当然咱们也能够借助@Qualifier注解配合@Autowired注解应用,指定要注入的bean的名字. 2.Spring 中的依赖注入(DI)是基于什么技术实现的?反射技术3.Spring 学习过程中有一个设计思维,称之为IOC(管制反转),如何实现管制反转呢?能够借助DI Bug剖析:1.NoUniqueBeanDefinitionException (非惟一bean的定义)

March 13, 2021 · 1 min · jiezi

关于spring:代码审查从-ArrayList-说线程安全

本文从代码审查过程中发现的一个 ArrayList 相干的「线程平安」问题登程,来分析和了解线程平安。 案例剖析前两天在代码 Review 的过程中,看到有小伙伴用了相似以下的写法: List<String> resultList = new ArrayList<>();paramList.parallelStream().forEach(v -> { String value = doSomething(v); resultList.add(value);});印象中 ArrayList 是线程不平安的,而这里会多线程改写同一个 ArrayList 对象,感觉这样的写法会有问题,于是看了下 ArrayList 的实现来确认问题,同时温习下相干常识。 先贴个概念: 线程平安 是程式设计中的术语,指某个函数、函数库在多线程环境中被调用时,可能正确地解决多个线程之间的共享变量,使程序性能正确实现。 ——维基百科咱们来看下 ArrayList 源码里与本话题相干的要害信息: public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable{ // ... /** * The array buffer into which the elements of the ArrayList are stored. * The capacity of the ArrayList is the length of this array buffer... */ transient Object[] elementData; // non-private to simplify nested class access /** * The size of the ArrayList (the number of elements it contains). */ private int size; // ... /** * Appends the specified element to the end of this list... */ public boolean add(E e) { ensureCapacityInternal(size + 1); // Increments modCount!! elementData[size++] = e; return true; } // ...}从中咱们能够关注到对于 ArrayList 的几点信息: ...

March 13, 2021 · 2 min · jiezi

关于spring:Spring-Cloud-Alibaba-Seata-配置

这个东东整了我2天多。。我有点无语。。原本就很简略的货色。。有点无辜。开发还是本地搞环境好,问题少???? seata/seata Seata 是什么 一、配置数据库create database seata;use seata;-- -------------------------------- The script used when storeMode is 'db' ---------------------------------- the table to store GlobalSession dataCREATE TABLE IF NOT EXISTS `global_table`( `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `status` TINYINT NOT NULL, `application_id` VARCHAR(32), `transaction_service_group` VARCHAR(32), `transaction_name` VARCHAR(128), `timeout` INT, `begin_time` BIGINT, `application_data` VARCHAR(2000), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`xid`), KEY `idx_gmt_modified_status` (`gmt_modified`, `status`), KEY `idx_transaction_id` (`transaction_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;-- the table to store BranchSession dataCREATE TABLE IF NOT EXISTS `branch_table`( `branch_id` BIGINT NOT NULL, `xid` VARCHAR(128) NOT NULL, `transaction_id` BIGINT, `resource_group_id` VARCHAR(32), `resource_id` VARCHAR(256), `branch_type` VARCHAR(8), `status` TINYINT, `client_id` VARCHAR(64), `application_data` VARCHAR(2000), `gmt_create` DATETIME(6), `gmt_modified` DATETIME(6), PRIMARY KEY (`branch_id`), KEY `idx_xid` (`xid`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;-- the table to store lock dataCREATE TABLE IF NOT EXISTS `lock_table`( `row_key` VARCHAR(128) NOT NULL, `xid` VARCHAR(96), `transaction_id` BIGINT, `branch_id` BIGINT NOT NULL, `resource_id` VARCHAR(256), `table_name` VARCHAR(32), `pk` VARCHAR(36), `gmt_create` DATETIME, `gmt_modified` DATETIME, PRIMARY KEY (`row_key`), KEY `idx_branch_id` (`branch_id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8;二、配置nacos这边次要是把相干配置上传到nacos中,不便seata-center读取下载nacos-config.sh和config.txtseata/seata ...

March 12, 2021 · 4 min · jiezi

关于spring:统一响应模板

2021年03月11日 16:19 package com.fedtech.common.util.result;import cn.hutool.json.JSONUtil;import com.fedtech.common.constants.Result.ResponseCodeConstants;import lombok.Data;import org.apache.commons.lang3.StringUtils;import java.io.Serializable;import java.util.HashMap;import java.util.Map;/** * 全局对立响应模板 * * @author <a href="mailto:njpkhuan@gmail.com">huan</a> * @version 1.0.0 * @date 2021/1/9 */@SuppressWarnings("rawtypes")@Datapublic class R<T> implements Serializable { private static final long serialVersionUID = 6426193889384507066L; private Boolean status; private Integer code; private String message; private boolean jsonData = Boolean.FALSE; private Map<String, Object> data = new HashMap<>(); private R() {} public static R success() { R r = new R(); r.setStatus(true); r.setCode(ResponseCodeConstants.SUCCESS); r.setMessage("申请胜利!"); return r; } public static <T> R<T> success(String message) { R<T> r = new R<>(); r.setStatus(true); r.setCode(ResponseCodeConstants.SUCCESS); r.setMessage(message); return r; } public static <T> R<T> fail() { R<T> r = new R<>(); r.setStatus(false); r.setCode(ResponseCodeConstants.ERROR); r.setMessage("申请失败!"); return r; } public static <T> R<T> fail(String key, Object value) { R<T> r = new R<>(); r.setStatus(false); r.data.put(key, value); r.setCode(ResponseCodeConstants.ERROR); r.setMessage("申请失败!"); return r; } public static <T> R<T> fail(String message) { R<T> r = new R<>(); r.setStatus(false); r.setCode(ResponseCodeConstants.ERROR); r.setMessage(message); return r; } public static <T> R<T> fail(String message, Integer code) { R<T> r = new R<>(); r.setStatus(false); r.setCode(code); r.setMessage(message); return r; } public static <T> R<T> successWithJsonData(T data) { R<T> r = new R<>(); r.setStatus(Boolean.TRUE); r.setCode(ResponseCodeConstants.SUCCESS); r.data.put("data", data); r.jsonData = Boolean.TRUE; return r; } public static <T> R<T> successWithData(T data) { R<T> r = new R<>(); r.setStatus(Boolean.TRUE); r.setCode(ResponseCodeConstants.SUCCESS); r.data.put("data", data); return r; } public static <T> R<T> failedWithJsonData(T data) { R<T> r = new R<>(); r.setStatus(Boolean.FALSE); r.setCode(ResponseCodeConstants.ERROR); r.data.put("data", data); r.jsonData = Boolean.TRUE; return r; } public static <T> R<T> failedWithData(T data) { R<T> r = new R<>(); r.setStatus(Boolean.FALSE); r.setCode(ResponseCodeConstants.ERROR); r.data.put("data", data); return r; } public R message(String message) { setMessage(message); return this; } public R code(Integer code) { setCode(code); return this; } public R data(String key, Object value) { data.put(key, value); return this; } public R data(Map<String, Object> map) { setData(map); return this; } public String toJson() { if (jsonData) { String tem = String.valueOf(data.get("data")); data.remove("data"); String jsonStr = JSONUtil.toJsonPrettyStr(this); return StringUtils.replace(jsonStr, jsonStr.substring(30, 38), tem); } return JSONUtil.toJsonPrettyStr(this); }}本文由博客群发一文多发等经营工具平台 OpenWrite 公布

March 12, 2021 · 2 min · jiezi

关于spring:统一500异常-非抛出的异常

%DATE% %TIME% 2021年03月11日 16:21 这里的解决那些没有抛出的500谬误 定义一个过滤器,并把级别调到最高package com.fedtech.common.filter.response;import com.fedtech.common.util.result.R;import org.apache.commons.lang3.StringUtils;import org.springframework.core.Ordered;import org.springframework.core.annotation.Order;import org.springframework.http.MediaType;import org.springframework.stereotype.Component;import javax.servlet.*;import javax.servlet.http.HttpServletResponse;import java.io.IOException;/** * 对局部响应内容进行解决 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/24 * @since 1.0.0 */@Component@Order(Ordered.HIGHEST_PRECEDENCE)public class ResponseFilter implements Filter { @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void destroy() { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { ResponseWrapper responseWrapper = new ResponseWrapper((HttpServletResponse) servletResponse); filterChain.doFilter(servletRequest, responseWrapper); String contentType = responseWrapper.getContentType(); byte[] content = responseWrapper.getResponseData(); String str = ""; if (StringUtils.isNotBlank(contentType) && (contentType.contains(MediaType.APPLICATION_JSON_VALUE) || contentType.contains(MediaType.TEXT_HTML_VALUE))) { str = new String(content); HttpServletResponse response = (HttpServletResponse) servletResponse; if (response.getStatus() != 200) { if (StringUtils.containsAny(str, "\"status\":\"false\"")) { writeResponse(response, response.getStatus(), str); } else { writeResponse(response, response.getStatus(), R.failedWithJsonData(str).toJson()); } } else { if (StringUtils.containsAny(str, "\"status\":\"true\"")) { writeResponse(response, response.getStatus(), str); } else { writeResponse(response, response.getStatus(), R.successWithJsonData(str).toJson()); } } } } private static void writeResponse(HttpServletResponse response, int status, String json) { try { response.setHeader("Access-Control-Allow-Origin", "*"); response.setHeader("Access-Control-Allow-Methods", "*"); response.setContentType("application/json;charset=UTF-8"); response.setStatus(status); response.getWriter().write(json); } catch (IOException e) { e.printStackTrace(); } }}对立响应解决package com.fedtech.common.filter.response;import com.fedtech.common.util.result.R;import org.apache.commons.lang3.StringUtils;import javax.servlet.ServletOutputStream;import javax.servlet.WriteListener;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpServletResponseWrapper;import java.io.*;public class ResponseWrapper extends HttpServletResponseWrapper { private ByteArrayOutputStream buffer = null; private ServletOutputStream out = null; private PrintWriter writer = null; ResponseWrapper(HttpServletResponse resp) throws IOException { super(resp); buffer = new ByteArrayOutputStream();// 真正存储数据的流 out = new WapperedOutputStream(buffer); writer = new PrintWriter(new OutputStreamWriter(buffer)); } @Override public ServletOutputStream getOutputStream() throws IOException { return out; } @Override public PrintWriter getWriter() throws UnsupportedEncodingException { return writer; } @Override public void flushBuffer() throws IOException { if (out != null) { out.flush(); } if (writer != null) { writer.flush(); } } @Override public void reset() { buffer.reset(); } byte[] getResponseData() throws IOException { flushBuffer(); if (StringUtils.contains(buffer.toString(), "\"data\":") && StringUtils.contains(buffer.toString(), "\"code\":") && StringUtils.contains(buffer.toString(), "\"status\":")) { return buffer.toByteArray(); } ByteArrayOutputStream stream = new ByteArrayOutputStream(); stream.write(R.successWithJsonData(buffer.toString()).toJson().getBytes()); return stream.toByteArray(); } public String getContent() throws IOException { flushBuffer(); if (StringUtils.contains(buffer.toString(), "\"data\":") && StringUtils.contains(buffer.toString(), "\"code\":") && StringUtils.contains(buffer.toString(), "\"status\":")) { return buffer.toString(); } ByteArrayOutputStream stream = new ByteArrayOutputStream(); stream.write(R.successWithJsonData(buffer.toString()).toJson().getBytes()); return stream.toString(); } private class WapperedOutputStream extends ServletOutputStream { private ByteArrayOutputStream bos = null; WapperedOutputStream(ByteArrayOutputStream stream) throws IOException { bos = stream; } @Override public void write(int b) throws IOException { bos.write(b); } @Override public void write(byte[] b) throws IOException { bos.write(b, 0, b.length); } @Override public void write(byte[] b, int off, int len) throws IOException { bos.write(b, off, len); } @Override public boolean isReady() { return false; } @Override public void setWriteListener(WriteListener writeListener) { } }}本文由博客群发一文多发等经营工具平台 OpenWrite 公布

March 12, 2021 · 2 min · jiezi

关于spring:统一404异常

%DATE% %TIME% 2021年03月11日 16:18 自定义对立异样解决形式,这里间接抛出 package com.fedtech.common.handler;import cn.hutool.core.io.resource.NoResourceException;import org.springframework.boot.web.servlet.error.ErrorController;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;/** * 自定义404异样 * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/3/11 * @apiNote todo * @since 1.0.0 */@Controller@RequestMapping("${server.error.path:${error.path:/error}}")public class MyErrorController implements ErrorController { @Override public String getErrorPath() { return null; } @RequestMapping public void error() throws Exception { throw new NoResourceException("申请资源不存在!"); }}在异样解决类中新增对应解决 /** * 资源不村在 * * @return com.fedtech.common.util.result.R * * @author <a href = "mailto:njpkhuan@gmail.com" > huan </a > * @date 2021/2/19 * @since 1.0.0 */ @ExceptionHandler(value = NoResourceException.class) @ResponseBody public R exception(NoResourceException e) { e.printStackTrace(); return R.fail(e.getMessage()).code(404); }本文由博客群发一文多发等经营工具平台 OpenWrite 公布

March 12, 2021 · 1 min · jiezi

关于spring:9-种设计模式在-Spring-中的运用一定要非常熟练

公众号:MarkerHub,网站:https://markerhub.com小Hub领读:简略工厂、工厂办法、单例模式、适配器模式、装璜器模式、代理模式、观察者模式、策略模式、模版办法模式。9种模式,在spring中如何使用的,看看吧孩子~ 作者:iCoding91blog.csdn.net/caoxiaohong1005Spring 中波及的设计模式总结 1. 简略工厂 (非 23 种设计模式中的一种)实现形式:BeanFactory。Spring 中的 BeanFactory 就是简略工厂模式的体现,依据传入一个惟一的标识来取得 Bean 对象,但是否是在传入参数后创立还是传入参数前创立这个要依据具体情况来定。 本质:由一个工厂类依据传入的参数,动静决定应该创立哪一个产品类。 实现原理:bean 容器的启动阶段: 读取 bean 的 xml 配置文件, 将 bean 元素别离转换成一个 BeanDefinition 对象。而后通过 BeanDefinitionRegistry 将这些 bean 注册到 beanFactory 中,保留在它的一个 ConcurrentHashMap 中。将 BeanDefinition 注册到了 beanFactory 之后,在这里 Spring 为咱们提供了一个扩大的切口,容许咱们通过实现接口 BeanFactoryPostProcessor 在此处来插入咱们定义的代码。典型的例子就是:PropertyPlaceholderConfigurer,咱们个别在配置数据库的 dataSource 时应用到的占位符的值,就是它注入进去的。 容器中 bean 的实例化阶段: 实例化阶段次要是通过反射或者 CGLIB 对 bean 进行实例化,在这个阶段 Spring 又给咱们裸露了很多的扩大点: 各种的 Aware 接口,比方 BeanFactoryAware,对于实现了这些 Aware 接口的 bean,在实例化 bean 时 Spring 会帮咱们注入对应的 BeanFactory 的实例。BeanPostProcessor 接口,实现了 BeanPostProcessor 接口的 bean,在实例化 bean 时 Spring 会帮咱们调用接口中的办法。InitializingBean 接口,实现了 InitializingBean 接口的 bean,在实例化 bean 时 Spring 会帮咱们调用接口中的办法。DisposableBean 接口,实现了 BeanPostProcessor 接口的 bean,在该 bean 死亡时 Spring 会帮咱们调用接口中的办法。设计意义:松耦合。能够将原来硬编码的依赖,通过 Spring 这个 beanFactory 这个工厂来注入依赖,也就是说原来只有依赖方和被依赖方,当初咱们引入了第三方——spring 这个 beanFactory,由它来解决 bean 之间的依赖问题,达到了松耦合的成果. ...

March 12, 2021 · 3 min · jiezi

关于spring:springaop

Spring AOP 简介如果说 IoC 是 Spring 的外围,那么面向切面编程就是 Spring 最为重要的性能之一了,在数据库事务中切面编程被宽泛应用。 AOP 即 Aspect Oriented Program 面向切面编程首先,在面向切面编程的思维外面,把性能分为外围业务性能,和周边性能。 所谓的外围业务,比方登陆,减少数据,删除数据都叫外围业务所谓的周边性能,比方性能统计,日志,事务管理等等周边性能在 Spring 的面向切面编程AOP思维里,即被定义为切面 在面向切面编程AOP的思维外面,外围业务性能和切面性能分别独立进行开发,而后把切面性能和外围业务性能 "编织" 在一起,这就叫AOP AOP 的目标AOP可能将那些与业务无关,却为业务模块所独特调用的逻辑或责任(例如事务处理、日志治理、权限管制等)封装起来,便于缩小零碎的反复代码,升高模块间的耦合度,并有利于将来的可拓展性和可维护性。 AOP 当中的概念:切入点(Pointcut)在哪些类,哪些办法上切入(where)告诉(Advice)在办法执行的什么理论(when:办法前/办法后/办法前后)做什么(what:加强的性能)切面(Aspect)切面 = 切入点 + 告诉,艰深点就是:在什么机会,什么中央,做什么加强!织入(Weaving)把切面退出到对象,并创立出代理对象的过程。(由 Spring 来实现)一个例子为了更好的阐明 AOP 的概念,咱们来举一个理论中的例子来阐明: 在下面的例子中,包租婆的外围业务就是签合同,收房租,那么这就够了,灰色框起来的局部都是反复且边缘的事,交给中介商就好了,这就是 AOP 的一个思维:让关注点代码与业务代码拆散! 理论的代码咱们来理论的用代码感受一下 1.在 Package【pojo】下新建一个【Landlord】类(我百度翻译的包租婆的英文): package pojo;import org.springframework.stereotype.Component;@Component("landlord")public class Landlord { public void service() { // 仅仅只是实现了外围的业务性能 System.out.println("签合同"); System.out.println("收房租"); }} 2.在 Package【aspect】下新建一个中介商【Broker】类(我还是用的翻译...): package aspect;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;import org.springframework.stereotype.Component;@Component@Aspectclass Broker { @Before("execution(* pojo.Landlord.service())") public void before(){ System.out.println("带租客看房"); System.out.println("谈价格"); } @After("execution(* pojo.Landlord.service())") public void after(){ System.out.println("交钥匙"); }} 3.在 applicationContext.xml 中配置主动注入,并通知 Spring IoC 容器去哪里扫描这两个 Bean: ...

March 10, 2021 · 2 min · jiezi

关于spring:Spring-Boot概述

1.什么是Spring Boot?Craig Walls所著《Spring Boot in Action》里讲到:Spring Boot旨在简化Spring开发,Spring Boot 将很多魔法带入了Spring应用程序的开发中,其中最重要的是四个外围: 主动配置起步依赖命令行界面Actuator因而咱们能够概括地说 Spring Boot = Spring + 主动配置 + 起步依赖 + 命令行界面 + Actuator 2.什么是Spring?为什么须要Spring?任何应用程序都是由很多组件组成的,每个组件负责整个利用性能的一部分,这些组件须要与其余的利用元素协调以实现本人的工作。Spring 的外围提供了一个容器(Container),通常称为Spring利用上下文(Spring Context),它会创立和治理利用组件bean,通过一种基于依赖注入(dependency injection)的模式将利用上下文中的bean拆卸在一起。Spring 外围和其提供的各种功能模块组合在一起咱们称之为 Spring Framework。 3.鸟瞰Spring 风景线Spring 有100多个可选依赖项,重点有: 3.1 Spring外围框架它提供了外围容器和依赖注入框架 3.2 Spring Boot主动配置起步依赖命令行界面Actuator3.3 Spring Data不同于Spring外围框架提供的根本的数据长久化反对,Spring Data 将应用程序的数据repository定义为简略的java接口。 3.4 Spring Security一个强壮的平安框架,解决了利用陈旭通用的安全性需要,包含身份验证、受权和API安全性。 3.5 Spring Integration 和 Spring Batch解决与其余利用以及和本利用中其余组件的集成问题,别离解决实时集成和批量集成。 3.6 Spring CloudSpring Cloud 是应用Spring开发云原生应用程序的一组我的项目。 4.以后Spring Boot的几个关注点:面向云的由微服务组成的利用各种类型的数据库反应式编程

March 9, 2021 · 1 min · jiezi

关于spring:spring的原理机制

1,对于spring容器: spring容器是Spring的外围,该容器负责管理spring中的java组件, 2,AOP编程的反对 通过Spring提供的AOP性能,不便进行面向切面的编程,许多不容易用传统OOP实现的性能能够通过AOP轻松应酬。 3,Spring的外围机制:依赖注入。 不论是依赖注入(Dependency Injection)还是管制反转(Inversion of Conctrol),其含意完全相同: 当某个java实例(调用者)须要调用另一个java实例(被调用者)时,传统状况下,通过调用者来创立被调用者的实例,通常通过new来创立, 而在依赖注入的模式下创立被调用者的工作不再由调用者来实现,因而称之为“管制反转”;创立被调用者实例的工作通常由Spring来实现,而后注入调用者,所以也称之为“依赖注入”。4,依赖注入个别有2中形式: 设置注入:IoC容器应用属性的setter形式注入被依赖的实例。 结构注入:IoC容器应用结构器来注入被依赖的实例。

March 9, 2021 · 1 min · jiezi

关于spring:一spring-aop注解失效

前言:在重构公司的风控系统的时候,发现旧代码中经常出现获取以后service的bean在调用办法的状况。起初才发现其中的问题。如下:private AccountActionProfileService me() { return applicationContext.getBean(AccountActionProfileService.class);}@Transactionalpublic void addAccountActionProfile() {....... if (.....) { me().verifyBanAccountRule(target); }}@Asyncpublic void verifyBanAccountRule(AccountActionProfileEntity accountAction, String originalAccountName) { ......... }起初才发现若没有me()办法而间接应用verifyBanAccountRule办法的话,该办法并没有被新的线程执行,还是在旧线程中串行执行。通过谷歌搜寻了一下,才晓得其起因。 起因理解过spring的aop原理的同学应该都晓得spring的aop是基于cglib或jdk动静代理生成代理类,而咱们在办法中间接调用带有Aop注解的办法,实际上是通过以后对象的this指针拜访该办法,此时以后对象并不是代理对象,故下面公司的旧代码应用的是从spring容器中获取bean,再通过bean来调相应的办法。解决办法从spring容器中获取bean,再通过bean来调相应的办法。 @Autowiredprivate ApplicationContext applicationContext;private AccountActionProfileService me() { return applicationContext.getBean(AccountActionProfileService.class);}

March 8, 2021 · 1 min · jiezi

关于spring:Spring-Aop实现原理

JDK动静代理jdk动静代理是利用反射机制生成一个实现代理接口的匿名类,在调用业务办法前调用InvocationHandler解决。代理类必须实现InvocationHandler接口,并且,JDK动静代理只能代理实现了接口的类,没有实现接口的类是不能实现JDK动静代理。应用JDK动静代理类根本步骤:1、须要被代理的类和接口 public interface OrderService { public void save(UUID orderId, String name);}public class OrderServiceImpl implements OrderService { @Override public void save(UUID orderId, String name) { System.out.println("call save()办法,save:" + name); }}2、代理类,须要实现InvocationHandler接口,重写invoke办法 public class JDKProxy implements InvocationHandler { //须要代理的指标对象 private Object targetObject; public Object createProxyInstance(Object targetObject) { this.targetObject = targetObject; return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(), this.targetObject.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //被代理对象 OrderServiceImpl bean = (OrderServiceImpl) this.targetObject; Object result = null; //切面逻辑(advise),此处是在指标类代码执行之前 System.out.println("---before invoke----"); if (bean.getUser() != null) { result = method.invoke(targetObject, args); } System.out.println("---after invoke----"); return result; }}3、应用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)动态创建代理类对象,通过代理类对象调用业务办法。 ...

March 7, 2021 · 2 min · jiezi

关于spring:五Spring事务提交

接第2节内容,这是第6点具体解析 事务提交逻辑如下: 判断事务是否曾经实现,如果实现抛出异样判断事务是否曾经被标记成回滚,则执行回滚操作嵌入事务标记回滚,如果嵌入事务抛出了异样执行了回滚,然而在调用方把嵌入事务的异样个捕捉没有抛出,就会执行这一步。提交事务代码如下: @Overridepublic final void commit(TransactionStatus status) throws TransactionException { //1. 判断事务是不是曾经实现 if (status.isCompleted()) { throw new IllegalTransactionStateException( "Transaction is already completed - do not call commit or rollback more than once per transaction"); } DefaultTransactionStatus defStatus = (DefaultTransactionStatus) status; //2. 如果在事务链中曾经被标记回滚,那么不会尝试提交事务,间接回滚,不过我没找到在哪设置这个值 if (defStatus.isLocalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Transactional code has requested rollback"); } processRollback(defStatus); return; } //3. shouldCommitOnGlobalRollbackOnly()默认返回false,isGlobalRollbackOnly是在嵌入事务回滚的时候赋值的 if (!shouldCommitOnGlobalRollbackOnly() && defStatus.isGlobalRollbackOnly()) { if (defStatus.isDebug()) { logger.debug("Global transaction is marked as rollback-only but transactional code requested commit"); } processRollback(defStatus); // Throw UnexpectedRollbackException only at outermost transaction boundary // or if explicitly asked to. if (status.isNewTransaction() || isFailEarlyOnGlobalRollbackOnly()) { throw new UnexpectedRollbackException( "Transaction rolled back because it has been marked as rollback-only"); } return; } //4. 提交事务 processCommit(defStatus);}

March 7, 2021 · 1 min · jiezi

关于spring:四Spring事务回滚

接第2节事务流程,这是第5点解析 回滚逻辑如下: 判断是否存在事务,只有存在事务才执行回滚依据异样类型判断是否回滚。如果异样类型不合乎,依然会提交事务回滚解决具体解析 判断是否存在事务,只有存在事务才执行回滚,即是否有@Transactional事务注解或相干事务切面依据异样类型判断是否回滚。如果异样类型不合乎,依然会提交事务依据@Transactional注解中rollbackFor、rollbackForClassName、noRollbackForClassName配置的值,找到最合乎ex的异样类型,如果合乎的异样类型不是NoRollbackRuleAttribute,则能够执行回滚。如果@Transactional没有配置,则默认应用RuntimeException和Error异样。代码如下: @Overridepublic boolean rollbackOn(Throwable ex) { if (logger.isTraceEnabled()) { logger.trace("Applying rules to determine whether transaction should rollback on " + ex); } RollbackRuleAttribute winner = null; int deepest = Integer.MAX_VALUE; //rollbackRules保留@Transactional注解中rollbackFor、rollbackForClassName、noRollbackForClassName配置的值 if (this.rollbackRules != null) { for (RollbackRuleAttribute rule : this.rollbackRules) { int depth = rule.getDepth(ex); if (depth >= 0 && depth < deepest) { deepest = depth; winner = rule; } } } if (logger.isTraceEnabled()) { logger.trace("Winning rollback rule is: " + winner); } // User superclass behavior (rollback on unchecked) if no rule matches. //若@Transactional没有配置,默认调用父类的 if (winner == null) { logger.trace("No relevant rollback rule found: applying default rules"); return super.rollbackOn(ex); } return !(winner instanceof NoRollbackRuleAttribute);}//super@Overridepublic boolean rollbackOn(Throwable ex) { return (ex instanceof RuntimeException || ex instanceof Error);}回滚解决如果存在平安点,则回滚事务至平安点,这个次要是解决嵌套事务,回滚平安点的操作还是交给了数据库解决.以后事务是一个新事务时,那么间接回滚,应用的是DataSourceTransactionManager事务管理器,所以调用DataSourceTransactionManager#doRollback,间接调用数据库连贯的回滚办法。以后存在事务,但又不是一个新的事务,只把事务的状态标记为read-only,等到事务链执行结束后,对立回滚,调用DataSourceTransactionManager#doSetRollbackOnly清空记录的资源并将挂起的资源复原代码如下: ...

March 7, 2021 · 2 min · jiezi

关于spring:三Spring创建事务

接上节事务执行流程,这是第3点的解析。创立事务次要两局部: 获取事务状态构建事务信息获取事务状态代码如下: @Override public final TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { //1.获取事务 Object transaction = doGetTransaction(); // Cache debug flag to avoid repeated checks. boolean debugEnabled = logger.isDebugEnabled(); if (definition == null) { // Use defaults if no transaction definition given. definition = new DefaultTransactionDefinition(); } //判断以后线程是否存在事务,判断根据为以后线程记录连贯不为空且连贯中的(connectionHolder)中的transactionActive属性不为空 if (isExistingTransaction(transaction)) { // Existing transaction found -> check propagation behavior to find out how to behave. return handleExistingTransaction(definition, transaction, debugEnabled); } // Check definition settings for new transaction. //事务超时设置验证 if (definition.getTimeout() < TransactionDefinition.TIMEOUT_DEFAULT) { throw new InvalidTimeoutException("Invalid transaction timeout", definition.getTimeout()); } // No existing transaction found -> check propagation behavior to find out how to proceed. //如果以后线程不存在事务,然而@Transactional却申明事务为PROPAGATION_MANDATORY抛出异样 if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_MANDATORY) { throw new IllegalTransactionStateException( "No existing transaction found for transaction marked with propagation 'mandatory'"); } //如果以后线程不存在事务,PROPAGATION_REQUIRED、PROPAGATION_REQUIRES_NEW、PROPAGATION_NESTED都得创立事务 else if (definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRED || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_REQUIRES_NEW || definition.getPropagationBehavior() == TransactionDefinition.PROPAGATION_NESTED) { //空挂起 SuspendedResourcesHolder suspendedResources = suspend(null); if (debugEnabled) { logger.debug("Creating new transaction with name [" + definition.getName() + "]: " + definition); } try { //默认返回true boolean newSynchronization = (getTransactionSynchronization() != SYNCHRONIZATION_NEVER); //构建事务状态 DefaultTransactionStatus status = newTransactionStatus( definition, transaction, true, newSynchronization, debugEnabled, suspendedResources); //结构transaction、包含设置connectionHolder、隔离级别、timeout //如果是新事务,绑定到以后线程 doBegin(transaction, definition); //新事务同步设置,针对以后线程 prepareSynchronization(status, definition); return status; } catch (RuntimeException ex) { resume(null, suspendedResources); throw ex; } catch (Error err) { resume(null, suspendedResources); throw err; } } else { // Create "empty" transaction: no actual transaction, but potentially synchronization. if (definition.getIsolationLevel() != TransactionDefinition.ISOLATION_DEFAULT && logger.isWarnEnabled()) { logger.warn("Custom isolation level specified but no actual transaction initiated; " + "isolation level will effectively be ignored: " + definition); } //申明事务是PROPAGATION_SUPPORTS boolean newSynchronization = (getTransactionSynchronization() == SYNCHRONIZATION_ALWAYS); return prepareTransactionStatus(definition, null, true, newSynchronization, debugEnabled, null); }}构建事务信息 ...

March 7, 2021 · 2 min · jiezi

关于spring:SpringMVC执行流程及源码分析

SpringMVC流程及源码剖析前言 学了一遍SpringMVC当前,想着做一个总结,温习一下。温习写上面的总结的时候才发现,其实本人学的并不彻底、牢固、也没有学全,视频跟书本是要联合起来一起,每一位老师的视频可能提到的货色都不统一,也导致也不是很全面,书本上会讲的笔记零碎、全面。同时我本人也是一个初学者,上面总结的可能并不欠缺、正确,心愿看到的大神给我指出,在此非常感谢。 [TOC] 一 、Spring外围模块1、外围模块 Spring Web MVC (下文简称为 SpringMVC )是 Spring 提供 Web 利用的框架设计,属于体现层的框架。SpringMVC是Spring框架的一部分。 Spring框架包含大抵六大模块,外围容器(Core Container)、AOP和设施反对、数据拜访及集成、Web、报文发送、Test 图片来源于Spring官网5.0.0.M5: https://docs.spring.io/spring-framework/docs/5.0.0.M5/spring-framework-reference/html/overview.html#overview-modules 对于Spring5模块图,有2点疑难: 1、不分明为什么在Spring官网上5.0版本当前,Release版(稳定版)的都未找到模块图,然而在M(里程碑版)版找到 了,如果有人在5.0当前的Release版(稳定版)找到,麻烦给我留个言,谢谢。2、在其余博文中看到Spring5模块结构图是这样的: 挺奇怪这个图是哪里来的?(路过的大神请指导) 对于问题2,我在Spring5.2.13.RELEASE GA中,找到了如下所示信息: 拷贝以上信息: Spring Framework Documentation Version 5.2.13.RELEASE What’s New, Upgrade Notes, Supported Versions, and other topics, independent of release cadence, are maintained externally on the project’s Github Wiki. Overviewhistory, design philosophy, feedback, getting started.CoreIoC Container, Events, Resources, i18n, Validation, Data Binding, Type Conversion, SpEL, AOP.TestingMock Objects, TestContext Framework, Spring MVC Test, WebTestClient.Data AccessTransactions, DAO Support, JDBC, O/R Mapping, XML Marshalling.Web ServletSpring MVC, WebSocket, SockJS, STOMP Messaging.Web ReactiveSpring WebFlux, WebClient, WebSocket.IntegrationRemoting, JMS, JCA, JMX, Email, Tasks, Scheduling, Caching.LanguagesKotlin, Groovy, Dynamic Languages.依照以上信息的Web Servlet、Web Reactive曾经是分属于不同的模块了。 ...

March 6, 2021 · 9 min · jiezi

关于spring:农田治理效率低下还赔本智慧农业力保粮食品质效率事半功倍

智慧农业零碎就是通过自动化的智能农业设施和产品,推动农业产业链革新降级;实现农业精细化、高效化与绿色化,保障农产品平安、农业竞争力晋升和农业可继续倒退。简略一点来说就是用技术实现降本增效。铺设农田里的物联网,监测打药的环境,以无人机的应用优化反哺智慧农业的落地等性能。 农业可视化监控是利用数字孪生技术,作为推动企业实现企业数字化转型、促成数字经济倒退的 重要抓手,已建设了广泛适应的实践技术体系,并在产品设计制作、工程建设和其余学科剖析等畛域都有较为深刻的利用。在以后我国各产业畛域 强调技术自主和数字平安的倒退阶段,数字孪生技术自身具备的高效决策、 深度剖析等特点,将无力推动数字产业化和产业数字化过程。 水肥一体化 近程智能农业监控:通过在农业生产现场搭建“物联网” 监控网络,实现对农业生产现场气象环境、作物长势、土壤情况、病虫害状况的实时监测。并依据设定的阈值,针对现场各种农业设施设施进行近程自动化管制,实现农业生产环节的海量数据采集与精准管制执行。对各利用零碎的数据进行集中存储和剖析,帮助公司领导层及时发现问题、剖析问题起因、进行危险预警,实现决策的科学化。 智慧水肥管理系统 图扑(Hightopo)通过自主研发,为农作物品类逐渐建设起“气象,土壤,农事,生理”四位一体的农业生产与评估模型,将农业生产从以人为核心的传统模式,以数字化为核心的可视化模式,通过数据驱动农业生产标准化的真正落地,进而实现农产品定制化生产。集成平台从信息导引与公布零碎获取实时的设施运行状态数据,同时监督信息导引与公布零碎的运行、近程开启 / 敞开等。 除了精准感知、管制与决策治理外,从宽泛意义上讲,智慧农业还包含农业电子商务、食品溯源防伪、农业休闲游览、农业信息服务等方面的内容,有了智慧农业的帮忙可实现更齐备的信息化根底撑持、更透彻的农业信息感知、更集中的数据资源、更宽泛的互联互通、更深刻的智能管制、更贴心的公众服务。 农产品平安追溯及防伪鉴真:通过采集农产品在智慧生产、智慧加工、智慧仓储、智慧物流等环节的相干数据,向消费者充沛展现产品安全与品质相干信息,实现从原野到饭桌的双向可追溯。 “智慧农业”与古代生物技术、种植技术等高新技术交融于一体,对建设世界程度农业具备重要意义。新一轮科技反动和产业改革,为传统农业向现代农业、智慧农业转型带来强劲能源。深刻施行翻新驱动倒退策略,放慢农业科技翻新,是时代赋予咱们的重要使命。

March 5, 2021 · 1 min · jiezi

关于spring:SpringMVC-拦截器

SpringMVC学习记录留神:以下内容是学习 北京能源节点 的SpringMVC视频后所记录的笔记、源码以及集体的了解等,记录下来仅供学习_ 第4章 SpringMVC 核心技术4.3 拦截器 SpringMVC 中的 Interceptor 拦截器是十分重要和相当有用的,它的次要作用是拦挡指定的用户申请,并进行相应的预处理与后处理。其拦挡的工夫点在“处理器映射器依据用户提交的申请映射出了所要执行的处理器类,并且也找到了要执行该处理器类的处理器适配器,在处理器适配器执行处理器之前”。当然,在处理器映射器映射出所要执行的处理器类时,曾经将拦截器与处理器组合为了一个处理器执行链,并返回给了地方调度器。 4.3.1 一个拦截器的执行自定义拦截器 package com.bjpowernode.handler;import org.springframework.web.servlet.HandlerInterceptor;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import java.util.Date;//拦截器类:拦挡用户的申请。public class MyInterceptor implements HandlerInterceptor { private long btime = 0; /* * preHandle叫做预处理办法。 */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { btime = System.currentTimeMillis(); System.out.println("拦截器的MyInterceptor的preHandle()"); //计算的业务逻辑,依据计算结果,返回true或者false //给浏览器一个返回后果 //request.getRequestDispatcher("/tips.jsp").forward(request,response); return true; } /* postHandle:后处理办法。 */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView mv) throws Exception { System.out.println("拦截器的MyInterceptor的postHandle()"); //对原来的doSome执行后果,须要调整。 if( mv != null){ //批改数据 mv.addObject("mydate",new Date()); //批改视图 mv.setViewName("other"); } } /* afterCompletion:最初执行的办法 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("拦截器的MyInterceptor的afterCompletion()"); long etime = System.currentTimeMillis(); System.out.println("计算从preHandle到申请解决实现的工夫:"+(etime - btime )); }}自定义拦截器,须要实现Handlerinterceptor接口。而该接口中含有三个办法:1、 preHandle(request,response, Object handler) 预处理办法: 该办法在处理器办法执行之前执行。其返回值为boolean,若为true,则紧接着会执行处理器方 法,且会将afterCompletion()办法放入到一个专门的办法栈中期待执行。 ...

March 4, 2021 · 3 min · jiezi

关于spring:SpringMVC-请求重定向和转发

SpringMVC学习记录留神:以下内容是学习 北京能源节点 的SpringMVC视频后所记录的笔记、源码以及集体的了解等,记录下来仅供学习第4章 SpringMVC 核心技术4.1 申请重定向和转发 当处理器对申请处理完毕后,向其它资源进行跳转时,有两种跳转形式:申请转发与重 定向。而依据所要跳转的资源类型,又可分为两类:跳转到页面与跳转到其它处理器。留神,对于申请转发的页面,能够是WEB-INF中页面;而重定向的页面,是不能为WEB-INF 中页的。因为重定向相当于用户再次收回一次申请,而用户是不能间接拜访WEB-INF中资 源的。 SpringMVC框架把原来Servlet中的申请转发和重定向操作进行了封装。当初能够应用简 单的形式实现转发和重定向。 forward:示意转发,实现 request.getRequestDispatcher("xx.jsp").forward()redirect:示意重定向,实现 response.sendRedirect("xxx.jsp")4.1.1 申请转发4.1.2 申请重定向 我的项目构造:以下是源码: colntrollerpackage com.bjpowernode.controller;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.servlet.ModelAndView;import javax.servlet.http.HttpServletRequest;import javax.servlet.http.HttpServletResponse;import javax.servlet.http.HttpSession;import javax.xml.ws.RequestWrapper;/** * @RequestMapping: * value : 所有申请地址的公共局部,叫做模块名称 * 地位: 放在类的下面 */@Controllerpublic class MyController { /** * 处理器办法返回ModelAndView,实现转发forward * 语法: setViewName("forward:视图文件残缺门路") * forward特点: 不和视图解析器一起应用,就当我的项目中没有视图解析器 */ @RequestMapping(value = "/doForward.do") public ModelAndView doSome(){ //解决some.do申请了。 相当于service调用解决实现了。 ModelAndView mv = new ModelAndView(); mv.addObject("msg","欢送应用springmvc做web开发"); mv.addObject("fun","执行的是doSome办法"); //显示转发 //mv.setViewName("forward:/WEB-INF/view/show.jsp"); mv.setViewName("forward:/hello.jsp"); return mv; } /** * 处理器办法返回ModelAndView,实现重定向redirect * 语法:setViewName("redirect:视图残缺门路") * redirect特点: 不和视图解析器一起应用,就当我的项目中没有视图解析器 * * 框架对重定向的操作: * 1.框架会把Model中的简略类型的数据,转为string应用,作为hello.jsp的get申请参数应用。 * 目标是在 doRedirect.do 和 hello.jsp 两次申请之间传递数据 * * 2.在指标hello.jsp页面能够应用参数汇合对象 ${param}获取申请参数值 * ${param.myname} * * 3.重定向不能拜访/WEB-INF资源 */ @RequestMapping(value = "/doRedirect.do") public ModelAndView doWithRedirect(String name,Integer age){ //解决some.do申请了。 相当于service调用解决实现了。 ModelAndView mv = new ModelAndView(); //数据放入到 request作用域 mv.addObject("myname",name); mv.addObject("myage",age); //重定向 //mv.setViewName("redirect:/hello.jsp"); //http://localhost:8080/ch08_forard_redirect/hello.jsp?myname=lisi&myage=22 //重定向不能拜访/WEB-INF资源 mv.setViewName("redirect:/WEB-INF/view/show.jsp"); return mv; }}springmvc.xml<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd"> <!--申明组件扫描器--> <context:component-scan base-package="com.bjpowernode.controller" /> <!--申明 springmvc框架中的视图解析器, 帮忙开发人员设置视图文件的门路--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!--前缀:视图文件的门路--> <property name="prefix" value="/WEB-INF/view/" /> <!--后缀:视图文件的扩展名--> <property name="suffix" value=".jsp" /> </bean></beans>maven pom.xml<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.bjpowernode</groupId> <artifactId>ch08-forard-redirect</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.8</maven.compiler.source> <maven.compiler.target>1.8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!--servlet依赖--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency> <!--jsp依赖--> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <version>2.2.1-b03</version> <scope>provided</scope> </dependency> <!--springmvc依赖--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.2.5.RELEASE</version> </dependency> </dependencies> <build> <plugins> <!-- 编码和编译和JDK版本 --> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.1</version> <configuration> <source>1.8</source> <target>1.8</target> </configuration> </plugin> </plugins> </build></project>forward:示意转发redirect:示意重定向forward和redirect都是关键字, 有一个独特的特点不和视图解析器一起工作 ...

March 4, 2021 · 2 min · jiezi

关于spring:spring-validation

从springboot-2.3开始,校验包被独立成了一个starter组件,所以须要引入如下依赖: <!--校验组件--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId></dependency><!--web组件--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>而springboot-2.3之前的版本只须要引入 web 依赖就能够了。@AssertFalse 必须是false@AssertTrue 必须是true@DecimalMax 小于等于给定的值@DecimalMin 大于等于给定的值@Digits 可设定最大整数位数和最大小数位数@Email 校验是否合乎Email格局@Future 必须是未来的工夫@FutureOrPresent 以后或未来工夫@Max 最大值@Min 最小值@Negative 正数(不包含0)@NegativeOrZero 正数或0@NotBlank 不为null并且蕴含至多一个非空白字符@NotEmpty 不为null并且不为空@NotNull 不为null@Null 为null@Past 必须是过来的工夫@PastOrPresent 必须是过来的工夫,蕴含当初@PositiveOrZero 负数或0@Size 校验容器的元素个数

March 4, 2021 · 1 min · jiezi

关于spring:SpringMVC-异常处理

SpringMVC学习记录留神:以下内容是学习 北京能源节点 的SpringMVC视频后所记录的笔记、源码以及集体的了解等,记录下来仅供学习第4章 SpringMVC 核心技术4.2异样解决 SpringMVC框架解决异样的罕用形式:应用@ExceptionHandler注解解决异样。 异样解决步骤: 新建maven web我的项目退出依赖新建一个自定义异样类 MyUserException , 再定义它的子类NameException ,AgeException在controller抛出NameException , AgeException创立一个一般类,作用全局异样解决类 (1). 在类的下面退出@ControllerAdvice(2). 在类中定义方法,办法的下面退出@ExceptionHandler创立解决异样的视图页面.创立springmvc的配置文件 (1).组件扫描器 ,扫描@Controller注解 (2).组件扫描器,扫描@ControllerAdvice所在的包名 (3).申明注解驱动我的项目构造: 4.2.1 @ExceptionHandler 注解 应用注解@ExceptionHandler能够将一个办法指定为异样解决办法。该注解只有一个可 选属性value,为一个Class<?>数组,用于指定该注解的办法所要解决的异样类,即所要匹 配的异样。 而被注解的办法,其返回值能够是ModelAndView、String,或void,办法名随便,办法 参数能够是 Exception 及其子类对象、HttpServletRequest、HttpServletResponse 等。零碎会 主动为这些办法参数赋值。对于异样解决注解的用法,也能够间接将异样解决办法注解于Controller之中。 (1) 自定义异样类 定义三个异样类:NameException、AgeException、MyUserException。其中 MyUserException 是另外两个异样的父类。MyUserException.java package com.bjpowernode.exception;public class MyUserException extends Exception { public MyUserException() { super(); } public MyUserException(String message) { super(message); }}AgeException.java package com.bjpowernode.exception;//当年龄有问题时,抛出的异样public class AgeException extends MyUserException { public AgeException() { super(); } public AgeException(String message) { super(message); }}NameException.java ...

March 4, 2021 · 3 min · jiezi

关于spring:深入aoc

前言应用切面一段时间,写个根本切面没有问题。然而这次应用切面时遇到了问题,发现切面没有执行,找谬误又无从找起,只能硬看代码。大略代码是这样的 public class ImportExcelServiceImpl { public void readFromFileAndSetAnswerSheets() { ... setAnswerSheet(currentRow); ... } @UpdateAnswerSheetTotalNumber public AnswerSheet setAnswerSheet(Row currentRow) { ... return answerSheet; }}这是被切办法 @Aspect@Componentpublic class UpdateAnswerSheetTotalNumberAspect { @Pointcut("@annotation(....UpdateAnswerSheetTotalNumber)") public void annotationPointCut() { } @AfterReturning(value = "annotationPointCut()", returning = "answerSheet") public void after(AnswerSheet answerSheet) { ... }这是切面 起因找了半天找不进去哪里错了,就去问了老师,老师看出了问题所在。原来我的被切办法是setAnswerSheet(),调用被切办法是通过this.setAnswerSheet()调用的,这就是对象内调用。而切面是基于代理模式,对象内调用办法是不走代理的,当然是不起作用的。原来写的被切办法都是在一个对象中的办法调用另一个对象中的a办法的状况下。此时spring会为被调用办法所在对象生成一个代理,此代理领有与服务雷同的办法,如果办法没有被执行切面,则在代理中间接将执行的办法转发给理论的服务,如果有切面,则会在代理中实现切面,这就是切面的原理。咱们在类中打入断点其中userServiceImpl.frozen使咱们的被切办法。注入的类名总是相似UserServiceImpl$$EnhancerBySpringCGLIB$$1c76af9d。为了让调用方取得UserServiceImpl的援用,它必须继承自UserServiceImpl。而后,该代理类会覆写所有public和protected办法,并在外部将调用委托给原始的UserServiceImpl实例。~~~~ 解决解决的方法就是本人注入本人 class A { @Autowired A a; public void test() { // 这样应用切不到,是对象的外部调用 this->setXxx(); // 这样用就能够,因为注入的a实际上是a的代理 a->setXxx(); } @Xxxxx public xxx setXxx() { }}而这种依赖注入只能应用@Autowired的模式,不能应用构造函数的模式,构造函数模式会造成依赖注入的死循环。 ...

February 27, 2021 · 1 min · jiezi

关于spring:快速入门Redis调用Lua脚本及使用场景介绍

Redis 是一种十分风行的内存数据库,罕用于数据缓存与高频数据存储。大多数开发人员可能据说过redis能够运行 Lua 脚本,然而可能不晓得redis在什么状况下须要应用到Lua脚本。 一、浏览本文前置条件能够遵循这个链接中的办法在操作系统上装置 Redis如果你对redis命令不相熟,查看《Redis 命令援用》二、为什么须要Lua脚本简而言之:Lua脚本带来性能的晋升。 很多利用的服务工作蕴含多步redis操作以及应用多个redis命令,这时你能够应用Redis联合Lua脚本,会为你的利用带来更好的性能。另外蕴含在一个Lua脚本外面的redis命令具备原子性,当你面对高并发场景下的redis数据库操作时,能够无效防止多线程操作产生脏数据。三、学点Lua语法说了那么多,Lua不会怎么办?不要慌!Lua其实很简略,如果你已经学习过任何一门编程语言,学习Lua都非常简单。上面给大家举几个例子学习一下: 3.1.一个简略的例子Lua脚本通过各种语言的redis客户端都能够调用,咱们就简略一点应用redis-cli看上面的redis命令行: eval "redis.call('set', KEYS[1], ARGV[1])" 1 key:name valueEVAL命令行前面跟着的是Lua脚本:"redis.call('set', KEYS[1], ARGV[1])",放到编程语言外面就是一段字符串,跟在Lua脚本字符串前面的三个参数顺次是: redis Lua脚本所须要的KEYS的数量 ,只有一个KEYS[1],所以紧跟脚本之后的参数值是1Lua 脚本须要的参数KEYS[1]的参数值,在咱们的例子中值为key:nameLua 脚本须要的参数ARGV[1]的参数值,在咱们的例子中值为valueLua脚本中包含两组参数:KEYS[]和ARGV[],两个数组下标从1开始。一个值得去恪守的最佳实际是:把redis操作所需的key通过KEYS进行参数传递,其余的Lua脚本所需的参数通过ARGV进行传递。 下面的脚本执行实现之后,咱们应用上面的Lua脚本来进行验证,如果该脚本的返回值是”value”,与咱们之前设置的key:name的值雷同,则示意咱们的Lua脚本被正确执行了。 eval "return redis.call('get', KEYS[1])" 1 key:name3.2.认真看下Lua脚本里的内容咱们的第一个Lua脚本只蕴含一条语句,调用redis.call redis.call('set', KEYS[1], ARGV[1])所以在Lua脚本外面能够通过redis.call执行redis命令,call办法的第一个参数就是redis命令的名称,因为咱们调用的是redis 的set命令,所以须要传递key和value两个参数。 咱们第二个脚本不只是执行了一个脚本,因为执行get命令还返回了执行后果。留神脚本中有一个return 关键字。 eval "return redis.call('get', KEYS[1])" 1 key:name当然如果只是下面的这么简略的Lua脚本,还不如间接应用命令行更不便。咱们理论应用到的Lua脚本会比下面的简单,下面的Lua脚本只是一个Hello World。 3.3. 简单点的例子我曾应用Lua脚本从一个hash map外面依照肯定的程序获取若干key对应的值。对应的程序在一个zset排序汇合中进行保留,数据设置及排序能够通过上面的实现。 # 设置hkeys为键Hash值hmset hkeys key:1 value:1 key:2 value:2 key:3 value:3 key:4 value:4 key:5 value:5 key:6 value:6# 建一个order为键的汇合,并给出程序zadd order 1 key:3 2 key:1 3 key:2如果不晓得hmset和zadd命令的作用,能够参考hmset 和 zadd执行上面的Lua脚本 ...

February 26, 2021 · 1 min · jiezi

关于spring:Spring事务

一、什么是事务的流传?简略的了解就是多个事务办法互相调用时,事务如何在这些办法间流传。 二、Spring事务流传类型Propagation介绍在Spring中对于事务的流传行为定义了七种类型别离是:REQUIRED、SUPPORTS、MANDATORY、REQUIRES_NEW、NOT_SUPPORTED、NEVER、NESTED。现有两个办法A和B,办法A执行会在数据库ATable插入一条数据,办法B执行会在数据库BTable插入一条数据,伪代码如下: //将传入参数a存入ATable pubilc void A(a){ insertIntoATable(a); }//将传入参数b存入BTable public void B(b){ insertIntoBTable(b);}无事务public void testMain(){ A(a1); //调用A入参a1 testB(); //调用testB}public void testB(){ B(b1); //调用B入参b1 throw Exception; //产生异样抛出 B(b2); //调用B入参b2}a1数据胜利存入ATable表,b1数据胜利存入BTable表,而在抛出异样后b2数据存储就不会执行,也就是b2数据不会存入数据库,这就是没有事务的场景。 REQUIRED(Spring默认的事务流传类型)如果以后没有事务,则本人新建一个事务,如果以后存在事务,则退出这个事务 @Transactional(propagation = Propagation.REQUIRED)public void testMain(){ A(a1); //调用A入参a1 testB(); //调用testB}@Transactional(propagation = Propagation.REQUIRED)public void testB(){ B(b1); //调用B入参b1 throw Exception; //产生异样抛出 B(b2); //调用B入参b2}后果:数据库没有插入新的数据,数据库还是放弃着执行testMain办法之前的状态,没有产生扭转。testMain上申明了事务,在执行testB办法时就退出了testMain的事务(以后存在事务,则退出这个事务),在执行testB办法抛出异样后事务会产生回滚,又testMain和testB应用的同一个事务,所以事务回滚后testMain和testB中的操作都会回滚,也就使得数据库依然放弃初始状态 public void testMain(){ A(a1); //调用A入参a1 testB(); //调用testB}@Transactional(propagation = Propagation.REQUIRED)public void testB(){ B(b1); //调用B入参b1 throw Exception; //产生异样抛出 B(b2); //调用B入参b2}后果:数据a1存储胜利,数据b1和b2没有存储。因为testMain没有申明事务,testB有申明事务且流传行为是REQUIRED,所以在执行testB时会本人新建一个事务(如果以后没有事务,则本人新建一个事务),testB抛出异样则只有testB中的操作产生了回滚,也就是b1的存储会产生回滚,但a1数据不会回滚,所以最终a1数据存储胜利,b1和b2数据没有存储 SUPPORTS以后存在事务,则退出以后事务,如果以后没有事务,就以非事务办法执行 public void testMain(){ A(a1); //调用A入参a1 testB(); //调用testB}@Transactional(propagation = Propagation.SUPPORTS)public void testB(){ B(b1); //调用B入参b1 throw Exception; //产生异样抛出 B(b2); //调用B入参b2}后果:a1,b1存入数据库,b2没有存入数据库。因为testMain没有申明事务,且testB的事务流传行为是SUPPORTS,所以执行testB时就是没有事务的(如果以后没有事务,就以非事务办法执行),则在testB抛出异样时也不会产生回滚,所以最终后果就是a1和b1存储胜利,b2没有存储。 ...

February 23, 2021 · 1 min · jiezi

关于spring:Spring-IOC-容器源码分析

Spring容器源码学习参考链接:javadoop.com/post/spring…

February 19, 2021 · 1 min · jiezi

关于spring:Spring-IOC

Spring Boot加载 Spring的IOC即管制反转,外围行将类的创立交给Spring工厂.Spring会扫描@MapperScan()指定的地址,将其中所有交给Spring容器治理的类(@Component,@Service,@Controller,@@Configuration等...)以Bean的模式放到Spring工厂.当须要某个类的时候,间接通过@Resource或者@Autowired即可实现类实例的拆卸.如果没有指定@MapperScan(),默认扫描启动类下所有包及子包. 1.启动类@MapperScan("com.trevis.sharding.mapper")@SpringBootApplicationpublic class ShardingApplication { public static void main(String[] args) { SpringApplication.run(ShardingApplication.class, args); }}2.进入SpringApplication.Java构造函数初始化SpringApplication对象后调用run()办法;public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); this.webApplicationType = WebApplicationType.deduceFromClasspath(); setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); this.mainApplicationClass = deduceMainApplicationClass(); }public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args); }3.这里的返回值是ConfigurableApplicationContext,即返回利用上下文.在这里实现Bean的拆卸Stopwatch: Spring计时器SpringApplicationRunListeners: 监听器ApplicationArguments: main办法启动时携带的参数,args大多数时候都是空的Banner: 启动banner图context = createApplicationContext(): 创立利用上下文prepareContext(): 将主类信息加载到Spring容器refreshContext(): 将扫描到的须要Sping治理的类加载到IOC容器public ConfigurableApplicationContext run(String... args) { StopWatch stopWatch = new StopWatch(); stopWatch.start(); ConfigurableApplicationContext context = null; Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>(); configureHeadlessProperty(); SpringApplicationRunListeners listeners = getRunListeners(args); listeners.starting(); try { ApplicationArguments applicationArguments = new DefaultApplicationArguments(args); ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments); configureIgnoreBeanInfo(environment); Banner printedBanner = printBanner(environment); context = createApplicationContext(); exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class, new Class[] { ConfigurableApplicationContext.class }, context); prepareContext(context, environment, listeners, applicationArguments, printedBanner); refreshContext(context); afterRefresh(context, applicationArguments); stopWatch.stop(); if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch); } listeners.started(context); callRunners(context, applicationArguments); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, listeners); throw new IllegalStateException(ex); } try { listeners.running(context); } catch (Throwable ex) { handleRunFailure(context, ex, exceptionReporters, null); throw new IllegalStateException(ex); } return context;}依据初始化构造函数时的webApplicationType创立上下文.这里应该是SERVLET容器. ...

February 12, 2021 · 4 min · jiezi

关于spring:欢迎光临Spring时代绪论

在编码一段时间之后,对框架、库又有了新的了解,于是打算重新学习下框架、库。重回Servlet让我回顾下我的Java之路,刚开始是学Java SE,在控制台外面输入看后果,过后感觉这个控制台还是比拟枯燥的,不如动画桌面活泼,原本打算学习下Swing,起初发现这玩意有点丑,起初发现Java的市场在WEB端,也就是浏览器这里,而后就开始学习JavaScript、CSS、HTML、Jquery。学完这三个,还是没有感应到Java的用途,我那个时候在想浏览器中输入框的值该怎么到Java外面呢? 答案就是Servlet,Servlet是浏览器和服务端的桥梁。像上面这样: 而后接到值之后,咱们还是通常须要存储一下,也就是跟数据库打交道,也就是JDBC,起初发现JDBC有些步骤是反复的,不用每次和数据库打交道的时候都反复写一遍,也就是DBUtils。这仿佛是一个残缺的流程了,从页面接收数据,存储到数据库,而后页面发动申请再将数据库中的数据返回给数据库。然而这个过程并不完满,第一个就是Java和数据库的单干,每次发送SQL语句都是新建一个连贯,这相当耗费资源,于是在这里咱们又引入了数据库连接池。那个时候还不怎么回用maven,解决依赖,那个时候总是会去找许多jar包粘贴在lib目录下。那个时候的一个数据处理链条大抵是这样的: 而后很快咱们就发现代码越写越多了,于是咱们就是开始划分构造,也就是MVC,下面的图就被切成了上面这样: view是视图层提供给用户,供用户操作,是程序的外壳。Controller依据用户输出的指令,选取对应的model,读取,而后进行相应的操作,返回给用户。这三层是紧密联系在一起的,但又是相互独立的,每一层外部的变动不影响其余层。每一层都对外提供接口(Interface),供下面一层调用,这样一来,软件就能够实现模块化,批改外观和变更数据都不必批改其余层,大大不便了软件的保护和降级。然而还不是很欠缺,当初来看咱们的代码构造变得清晰了。软件设计的一个指标就是写的代码越来越少,原生的JDBC还是有些繁琐,那么是否更进一步,更加简洁一些呢? 这是许多Java界的开发者提出的问题,答案就是ORM(Object Relational Mapping)框架, 比拟有代表性的就是MyBatis、Hibernate。这是在缩小模型层的代码量,然而目前整合ORM框架、连接池还是编码,整合的优劣受制于编写代码人的程度。当初咱们引入的框架之后,咱们的引入的框架之间的关系就变成了就变成了上面这样: 看起来仿佛构造很参差的样子,但并不是咱们想要的指标,因为通常一个WEB工程并不止会引入一个框架,咱们会陆陆续续引入其余框架,比方定时工作框架,日志框架,监控框架,权限管制框架。像上面这样: 凌乱而无序,兴许会为每个框架筹备一个工具类,封装罕用的办法。然而这仍不是咱们想要的指标,因为代码不是变化无穷的,比方ORM框架,可能你在和数据库连接池整合的时候是一个版本,然而后续发现要降级,这个时候如果你是硬编码的话,你就得该代码,可能还须要考虑一番 ,那么咱们能不能不硬编码呢?对立治理这些框架之间的依赖关系,做到可配置化。 这也就是工厂模式,Spring的前身,当然Spring不只是工厂模式。 如何取对象 - IOC与DI绪论在Java的世界里,咱们取得对象的形式次要是new,对于简略的对象来说,这一点问题都没有。然而有些对象创立起来,比较复杂,咱们心愿可能暗藏这些细节。留神强调一遍,咱们心愿对于对象的使用者来说,暗藏这些细节。这是框架采纳工厂模式的一个次要目标,咱们来看一下MyBatis中的工厂: 咱们通过SqlSessionFactory对象很容易就拿到了SqlSession对象,然而SqlSession对象的就比较复杂,咱们来看下源码: 这是工厂模式的一种典型的利用场景。Java的世界是对象的世界,咱们关注的问题很多时候都是在创建对象、应用对象上。如果你想用一个HashMap,大可不必用上设计模式,间接new就能够了。然而不是所有的对象都像HashMap那样,简略。在我对设计模式和Spring框架还不是很理解之前,没用Spring系的框架,在Service层调用Dao层的时候,还是通过new,每个Service的办法调用Dao,都是通过new,事实上在这个Service类,同样类的对象能够只用一个的,也就是单例模式。不懂什么叫单例模式的,能够参看我这篇博客: 明天咱们来聊聊单例模式和枚举 那么这个时候咱们的愿景是什么呢? 在面向对象的世界里,咱们次要的关注的就是取(创立)对象和存对象,就像数据结构一样,咱们次要关注的两点是如何存进去,如何取出来。创立的对象也有着不同的场景,下面咱们提到的有些对象创立起来比较复杂,咱们心愿对对象的使用者屏蔽掉这类细节。这类场景在Java中是很常见的,比方线程池的创立: 尽管咱们并不举荐间接应用这种形式创立线程池。那么还有一种状况就是扩展性设计,比如说刚开始我定义了一种对象,将该对象对应的操作方法搁置到了一个接口中,这也就是面向对象的设计理念,做什么用接口,是什么用对象。然而随着倒退,原来的对象曾经无奈满足我的要求了,然而这个对象从属于某一类库,原来的对象对应的事实实体还在应用,为了向前兼容,我只得又建设了一个类,之前的操作还是独特的。你可能感觉我说的有点形象,这里举一个比拟典型的例子,Java的POI库,用于操纵excel,应用频率是很高的,excel是有几种不同的版本的,Excel 2003文件后缀为.xls, Excel 2007及以上版本文件后缀为.xlsx。excel-2003的最大行数是65536行,这个行数有些时候是无奈满足需要的,在2007版冲破了65536行,最大为1048576行。POI也思考到了这个,扩展性还是比拟高的,将对excel的操作形象进去放在workbook中,专属于某个版本的操作放在对应的excel类中,像上面这样: HSSFWorkbook对应03版,Xssfworkbook对应07版,SXSSFWorkbook为了解决读取大型excel数据内存溢出而推出的。少数状况下,咱们要应用的其实是workbook中的办法,没必要还要去看看以后读取的excel是哪个版本的,这也就是WorkbookFactory的初衷,你读文件须要文件对象,你将这个文件对象交给我,我来向你返回对应的Workbook对象。这里形象一点的形容就是,应用该对象不再通过new,而是向工厂传递标识符,由工厂来返回对应的对象。这也就是简略工厂模式。很多解说简略工厂模式的博客或者视频(讲Spring框架入门的或者讲设计模式的)通常会从解耦合的角度登程,我在看视频或者看博客的时候,心里就会有一个问题,你解的是谁和谁的耦合,如果是从下面的介绍的WorkbookFactory来说的话,没用这种简略工厂模式,那么我认为就是心愿应用WorkBook对象的编码者和具体的Excel版本的类耦合了起来,像上面这样: public class ExcelDemo { public static void main(String[] args) throws IOException { XSSFWorkbook xssfWorkbook = new XSSFWorkbook(""); HSSFWorkbook hssfWorkbook = new HSSFWorkbook(); SXSSFWorkbook sxssfWorkbook = new SXSSFWorkbook(); }}我在刚开始做Excel导入的时候,就是先去依据Excel版本去找对应的类,而后做读取数据工作。在写完之后我又想了想,这样不是强制限定了用户上传的Excel版本了吗? 假如用户上传的不是这个版本,我应该给个异样提醒? 这样仿佛有点不敌对吧,03版本的Excel也有人再用啊。我想了想,把我的代码改成了上面这样: public void uploadExcel(MultipartFile file) throws IOException { Workbook workbook = null; if (file.getName().endsWith(".xls")){ workbook = new HSSFWorkbook(file.getInputStream()); }else if (file.getName().endsWith(".xlsx")){ workbook = new XSSFWorkbook(file.getInputStream()); } // 做真正的业务解决 }这样我的代码就和用户上传的excel版本解除了耦合,用户上传哪个版本我都能做导入,那个时候我还不晓得工厂模式,只是下意识的实现需要的时候,想让上传的Excel版本不受限制。而后某天我偶尔看到了WorkbookFactory这个类,这个类解除了WorkBook对象和具体Excel版本文件对象的耦合。用代码来演示下面介绍的工厂模式就是: ...

February 10, 2021 · 2 min · jiezi

关于spring:Ebean-ORM框架介绍1增强注解

在理解Ebeam框架之前,始终都在应用JPA作为Spring Boot的ORM框架。JPA用起来比较简单的,对对象的增删改操作,简直齐全不须要接触SQL语句,更适宜畛域驱动设计的建模办法。但对一些非业务操作的技术解决和查问尤其是简单查问的反对较弱,这也是有人抉择Mybatis的重要起因。 Ebean ORM框架,能够说简直反对所有的JPA的性能同时也兼顾了Mybatis的灵活性,并且还有一些较实用的减少性能。本系列文章将一一介绍Ebean特有的较实用的性能。明天介绍Ebean的加强注解性能。 ebean文档 https://ebean.io/docs/ 一、数据库反对Ebean和JPA相似可反对多种数据库 SummaryPlatformIdentityDbArrayDbJsonUUIDHistoryH2*Identity & SequencePartialSimulatedNativeTriggersPostgres*Identity & SequenceFullFullNativeTriggersMySqlIdentitySimulatedFull TriggersSQL Server 17*Sequence & IdentitySimulatedFullNativeNativeSQL Server 16*IdentitySimulatedFull NativeOracle*Sequence & IdentitySimulatedFull NativeDB2*Identity & SequenceNone SAP Hana-- SQLite*IdentityPartialSimulated NoneClickHouse-- Cockroach-- NuoDB-- 援用至:https://ebean.io/docs/database/ 二、装置ebean插件想要在idea应用调试代码必须要装置Ebean enhancer插件 关上idea, File > Settings > Plugins > Ebean enhancer 来装置插件,如图所示: 启用ebean减少工具,确保build下的 "Ebean enhancer"后面有一个勾 如果不勾运行的时候就可能报错 三、注解介绍public abstract class BaseModel extends Model { @Id long id; @Version Long version; @WhenCreated Instant whenCreated; @WhenModified Instant whenModified;}public class Customer extends BaseModel { public enum Status { @EnumValue("N") NEW, @EnumValue("A") APPROVED, @EnumValue("S") SHIPPED, @EnumValue("C") COMPLETE, @EnumValue("F") FOO } @DbComment("the name") private String name; @DbArray @DbComment("the array") private List<UUID> uids = new ArrayList<>(); @DbJson private SomeEmbedd some; @SoftDelete private boolean deleted; Status status; @DbMap(length = 800) Map<String, SomeEmbedd> map;}数据库格局: ...

February 7, 2021 · 2 min · jiezi

关于spring:聊聊Autowired的常考面试题

金三银四,很快又到了招聘淡季了,最近常常须要去做各种面试,发现很多几年工作教训的候选人,对Spring理解也是知之甚少,更多的只是会用,比方一个@Autowired原理都能够问倒一大片。为此,趁着女朋友狗泽明天加班,长话短说的聊聊这个话题吧! @Autowired注解是如何实现主动拆卸的@Autowired注解之所以能够实现主动拆卸,次要是依赖Spring提供的处理器AutowiredAnnotationBeanPostProcessor,该处理器在初始化的时候便退出了对@Autowired、@Inject、@Value三个注解的解决; 该处理器实现了接口InstantiationAwareBeanPostProcessor,因而能够在bean对象实例化的时候,对其应用了@Autowired的成员进行主动拆卸。 源码参考如下: public AutowiredAnnotationBeanPostProcessor() { // 退出了对@Autowired、@Inject、@Value三个注解的解决 this.autowiredAnnotationTypes.add(Autowired.class); this.autowiredAnnotationTypes.add(Value.class); try { this.autowiredAnnotationTypes.add((Class<? extends Annotation>) ClassUtils.forName("javax.inject.Inject", AutowiredAnnotationBeanPostProcessor.class.getClassLoader())); logger.info("JSR-330 'javax.inject.Inject' annotation found and supported for autowiring"); } catch (ClassNotFoundException ex) { // JSR-330 API not available - simply skip. }} 该处理器何时被退出的当结构Spring容器的时候,Spring会向容器注册几个内置的处理器对象,其中就包含了AutowiredAnnotationBeanPostProcessor。 源码能够间接看AnnotationConfigUtils.registerAnnotationConfigProcessors办法。源码参考如下: public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors( BeanDefinitionRegistry registry, @Nullable Object source) { // 省略代码... if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) { RootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class); def.setSource(source); beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)); } // 省略代码... return beanDefs;} ...

February 6, 2021 · 1 min · jiezi

关于spring:SpringIoC容器及DI依赖注入最彻底通俗的理解

首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的了解,写得十分通俗易懂,以下内容全副来自原文,原文地址:http://jinnianshilongnian.ite... 1.1、IoC是什么 Ioc—Inversion of Control,即“管制反转”,不是什么技术,而是一种设计思维。在Java开发中,Ioc意味着将你设计好的对象交给容器管制,而不是传统的在你的对象外部间接管制。如何了解好Ioc呢?了解好Ioc的要害是要明确“谁管制谁,管制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那咱们来深入分析一下: ●谁管制谁,管制什么:传统Java SE程序设计,咱们间接在对象外部通过new进行创建对象,是程序被动去创立依赖对象;而IoC是有专门一个容器来创立这些对象,即由Ioc容器来管制对 象的创立;谁管制谁?当然是IoC 容器管制了对象;管制什么?那就是次要管制了内部资源获取(不只是对象包含比方文件等)。 ●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由咱们本人在对象中被动管制去间接获取依赖对象,也就是正转;而反转则是由容器来帮忙创立及注入依赖对象;为何是反转?因为由容器帮咱们查找及注入依赖对象,对象只是被动的承受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。 用图例阐明一下,传统程序设计如图2-1,都是被动去创立相干对象而后再组合起来: 图1-1 传统应用程序示意图 当有了IoC/DI的容器后,在客户端类中不再被动去创立这些对象了,如图2-2所示: 图1-2有IoC/DI容器后程序结构示意图 1.2、IoC能做什么 IoC 不是一种技术,只是一种思维,一个重要的面向对象编程的法令,它能领导咱们如何设计出松耦合、更低劣的程序。传统应用程序都是由咱们在类外部被动创立依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创立和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 涣散耦合,这样也不便测试,利于性能复用,更重要的是使得程序的整个体系结构变得非常灵活。 其实IoC对编程带来的最大扭转不是从代码上,而是从思维上,产生了“主从换位”的变动。应用程序本来是老大,要获取什么资源都是主动出击,然而在IoC/DI思维中,应用程序就变成被动的了,被动的期待IoC容器来创立并注入它所须要的资源了。 IoC很好的体现了面向对象设计法令之一—— 好莱坞法令:“别找咱们,咱们找你”;即由IoC容器帮对象找相应的依赖对象并注入,而不是由对象被动去找。 1.3、IoC和DI DI—Dependency Injection,即“依赖注入”:组件之间依赖关系由容器在运行期决定,形象的说,即由容器动静的将某个依赖关系注入到组件之中。依赖注入的目标并非为软件系统带来更多功能,而是为了晋升组件重用的频率,并为零碎搭建一个灵便、可扩大的平台。通过依赖注入机制,咱们只须要通过简略的配置,而无需任何代码就可指定指标须要的资源,实现本身的业务逻辑,而不须要关怀具体的资源来自何处,由谁实现。 了解DI的要害是:“谁依赖谁,为什么须要依赖,谁注入谁,注入了什么”,那咱们来深入分析一下: ●谁依赖于谁:当然是应用程序依赖于IoC容器; ●为什么须要依赖:应用程序须要IoC容器来提供对象须要的内部资源; ●谁注入谁:很显著是IoC容器注入应用程序某个对象,应用程序依赖的对象; ●注入了什么:就是注入某个对象所须要的内部资源(包含对象、资源、常量数据)。 IoC和DI由什么关系呢?其实它们是同一个概念的不同角度形容,因为管制反转概念比拟含混(可能只是了解为容器管制对象这一个层面,很难让人想到谁来保护对象关系),所以2004年大师级人物Martin Fowler又给出了一个新的名字:“依赖注入”,绝对IoC 而言,“依赖注入”明确形容了“被注入对象依赖IoC容器配置依赖对象”。 看过很多对Spring的Ioc了解的文章,好多人对Ioc和DI的解释都艰涩难懂,反正就是一种说不清,道不明的感觉,读完之后仍然是一头雾水,感觉就是开涛这位技术牛人写得特地通俗易懂,他分明地解释了IoC(管制反转) 和DI(依赖注入)中的每一个字,读完之后给人一种恍然大悟的感觉。我置信对于初学Spring框架的人对Ioc的了解应该是有很大帮忙的。 二、分享Bromon的blog上对IoC与DI浅显易懂的解说2.1、IoC(管制反转) 首先想说说IoC(Inversion of Control,管制反转)。这是spring的外围,贯通始终。所谓IoC,对于spring框架来说,就是由spring来负责管制对象的生命周期和对象间的关系。这是什么意思呢,举个简略的例子,咱们是如何找女朋友的?常见的状况是,咱们到处去看哪里有长得丑陋身材又好的mm,而后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想方法意识她们,投其所好送其所要,而后嘿嘿……这个过程是简单深奥的,咱们必须本人设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要应用另外的对象,就必须失去它(本人new一个,或者从JNDI中查问一个),应用完之后还要将对象销毁(比方Connection等),对象始终会和其余的接口或类藕合起来。 那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介治理了很多男男女女的材料,我能够向婚介提出一个列表,通知它我想找个什么样的女朋友,比方长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,而后婚介就会依照咱们的要求,提供一个mm,咱们只须要去和她谈恋爱、结婚就行了。简单明了,如果婚介给咱们的人选不符合要求,咱们就会抛出异样。整个过程不再由我本人管制,而是有婚介这样一个相似容器的机构来管制。Spring所提倡的开发方式就是如此,所有的类都会在spring容器中注销,通知spring你是个什么货色,你须要什么货色,而后spring会在零碎运行到适当的时候,把你要的货色被动给你,同时也把你交给其余须要你的货色。所有的类的创立、销毁都由 spring来管制,也就是说管制对象生存周期的不再是援用它的对象,而是spring。对于某个具体的对象而言,以前是它管制其余对象,当初是所有对象都被spring管制,所以这叫管制反转。 2.2、DI(依赖注入)IoC的一个重点是在零碎运行中,动静的向某个对象提供它所须要的其余对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。比方对象A须要操作数据库,以前咱们总是要在A中本人编写代码来取得一个Connection对象,有了 spring咱们就只须要通知spring,A中须要一个Connection,至于这个Connection怎么结构,何时结构,A不须要晓得。在零碎运行时,spring会在适当的时候制作一个Connection,而后像打针一样,注射到A当中,这样就实现了对各个对象之间关系的管制。A须要依赖 Connection能力失常运行,而这个Connection是由spring注入到A中的,依赖注入的名字就这么来的。那么DI是如何实现的呢? Java 1.3之后一个重要特色是反射(reflection),它容许程序在运行的时候动静的生成对象、执行对象的办法、扭转对象的属性,spring就是通过反射来实现注入的。 了解了IoC和DI的概念后,所有都将变得简单明了,剩下的工作只是在spring的框架中沉积木而已。 三、我对IoC(管制反转)和DI(依赖注入)的了解在平时的java利用开发中,咱们要实现某一个性能或者说是实现某个业务逻辑时至多须要两个或以上的对象来合作实现,在没有应用Spring的时候,每个对象在须要应用他的单干对象时,本人均要应用像new object() 这样的语法来将单干对象创立进去,这个单干对象是由本人被动创立进去的,创立单干对象的主动权在本人手上,本人须要哪个单干对象,就被动去创立,创立单干对象的主动权和创立机会是由本人把控的,而这样就会使得对象间的耦合度高了,A对象须要应用单干对象B来共同完成一件事,A要应用B,那么A就对B产生了依赖,也就是A和B之间存在一种耦合关系,并且是严密耦合在一起,而应用了Spring之后就不一样了,创立单干对象B的工作是由Spring来做的,Spring创立好B对象,而后存储到一个容器外面,当A对象须要应用B对象时,Spring就从寄存对象的那个容器外面取出A要应用的那个B对象,而后交给A对象应用,至于Spring是如何创立那个对象,以及什么时候创立好对象的,A对象不须要关怀这些细节问题(你是什么时候生的,怎么生进去的我可不关怀,能帮我干活就行),A失去Spring给咱们的对象之后,两个人一起合作实现要实现的工作即可。 所以管制反转IoC(Inversion of Control)是说创建对象的控制权进行转移,以前创建对象的主动权和创立机会是由本人把控的,而当初这种势力转移到第三方,比方转移交给了IoC容器,它就是一个专门用来创建对象的工厂,你要什么对象,它就给你什么对象,有了 IoC容器,依赖关系就变了,原先的依赖关系就没了,它们都依赖IoC容器了,通过IoC容器来建设它们之间的关系。 这是我对Spring的IoC(管制反转)的了解。DI(依赖注入)其实就是IOC的另外一种说法,DI是由Martin Fowler 在2004年初的一篇论文中首次提出的。他总结:管制的什么被反转了?就是:取得依赖对象的形式反转了。 ...

February 4, 2021 · 1 min · jiezi

关于spring:Spring-AOP

面向切面编程(AOP)是Spring的两大外围之一.AOP的思维能够帮忙咱们在不侵入代码的状况下对原有性能进行加强,批改; 大抵是以下流程反射 -> proxy(动静代理) -> AOP(面向切面) @Aspect* 定义切面类@Pointcut* 切点Advice* 在切入点上执行的加强解决* @Before* @After* @AfterReturning 切点办法拿到返回值后执行* @AfterThrowing 切点办法抛出异样后执行* @Around 盘绕加强失常的执行程序是:@Around ->@Before->主办法体->@Around中pjp.proceed()->@After->@AfterReturning@Around如果不执行proceedingJoinPoint.proceed()办法,那么指标办法不会执行;能够决定是否执行指标办法;@before只是拦挡办法;如果加了@around,原办法交给proceed()管制//xx()是切点@Around("xx()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ log.info("进入around") return proceedingJoinPoint.proceed(); }语法规定反对&&,||,!@Pointcut(execution(url)) 指定切面办法@Pointcut("@annotation(url)") 自定义注解指定切面办法within,this,target...demospd零碎中应用aop对向外提供的api接口进行token校验权限管制. 1.首先自定义注解并在须要权限认证的接口上加上注解 @Target(ElementType.METHOD)@Retention(RetentionPolicy.RUNTIME)public @interface ReportPermission {}2. @Component@Aspect@Slf4jpublic class OrderReportAspect {@Autowiredprivate ISysUserService sysUserService;@Pointcut("@annotation(com.x.common.annotation.ReportPermission)") public void pointCut() {} //依据角色管制容许拜访接口的用户@Around("pointCut()") public Object before(ProceedingJoinPoint pjp) throws Throwable { Object[] in = pjp.getArgs(); if (in.length >= 1) { HttpServletRequest request = (HttpServletRequest) in[0]; log.info("切面获取request:{}",request); String userId = JwtUtil.checkToken(request.getHeader("token")); //校验以后userId是否有读取接口的权限 int count = sysUserService.isSpecified(userId,Constant.LOGISTICS_REPORT_RECEIVER); if (count == 1) {return pjp.proceed();} log.warn("api接口角色校验失败"); return null; } log.warn("api接口角色校验失败"); return null; }}

February 3, 2021 · 1 min · jiezi

关于spring:spring-AuthenticationInfo

背景http协定是无状态的,即每次发送的报文没有任何分割;这就带来一个问题:如何判断用户的登录状态?总不可能每次申请的时候都从新输出用户名明码吧.于是人们应用客户端cookie保留sessionId+服务端保留session的形式解决了这个问题.这也带来了额定的问题: session最后是存储在内存中的,当用户量过大时,服务器须要接受额外负担.session不利于横向拓展.比方当你搭建集群的时候,应用nginx转发申请后你并不能确定每次申请下发到了哪台服务器.当然也衍生出了解决办法nginx应用ip_hash模式.将用户ip hash计算后失去一个固定值,确保每次申请都落在同一台服务器.当然这种做法很蠢,因为这象征一旦某台服务器挂了,该用户短时间将无法访问了.将session保留到缓存数据库中,如redis.应用token保留登录信息,将token存储到缓存数据库如redis.应用jwt生成token,服务端生成token时会用密钥进行签名.每次客户端申请时携带token能够间接验证是否是服务端签发的.当然这种形式也存在缺点:token的过期工夫不可批改.这意味着一旦token颁发,你无法控制其生效工夫.某种意义上存在安全隐患.所以有时候还是会在里面套一层redis管制token生效工夫.demomvc拦截器实现登录认证shiro单机环境shiro-redis 集群环境jwt1.mvc拦截器初始化拦截器配置,过滤登录申请及动态资源 @Configurationpublic class WebMvcConfig implements WebMvcConfigurer { @Autowired private loginInteceptor loginInteceptor; @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/statics/**") .addResourceLocations("classpath:/statics/"); registry.addResourceHandler("/*.html") .addResourceLocations("classpath:/templates/"); } @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(loginInteceptor).excludePathPatterns("/login.html", "/statics/**" ,"/shiro/login"); }}自定义拦截器,若没有登录则重定向到登录页面 @Component@Slf4jpublic class loginInteceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { if (request.getSession().getAttribute("username") != null) { return true; } response.sendRedirect(request.getContextPath() + "/login.html"); return false; }}2.Shiro单机环境Shiro是一款杰出的平安框架.相较与SpringSecurity配置简略,宽泛使用于SpringBoot中.单机架构中session会交给shiro治理. Shiro外围模块1.subject:subject即以后拜访零碎的用户,能够通过SecurityUtils获取.2.realm:shiro拜访数据库校验的dao层.shiro反对繁多realm认证和多realm认证.3.SecurityManager:shiro的外围管理器,负责认证与受权,manager从relam中获取数据库数据.4.ShiroFilterFactoryBean:shiro拦截器,负责拦挡申请和放开申请,拦挡胜利后会被申请交还给manager判断. 1.登录接口这里session曾经交给shiro治理.ShiroHttpSession实现了HttpSession接口.Shiro内置了多种异样,这边就不展现了. ...

February 1, 2021 · 2 min · jiezi

关于spring:Spring-动态代理时是如何解决循环依赖的为什么要使用三级缓存

前言在钻研 『 Spring 是如何解决循环依赖的 』 的时候,理解到 Spring 是借助三级缓存来解决循环依赖的。 同样在上一节留下了疑难: 循环依赖为什么要应用三级缓存?而不是应用二级缓存?AOP 动静代理对循环依赖的有没有什么影响?本篇文章也是围绕下面的内容进行开展。 笔记也在一直整顿,之前可能会有点芜杂。循序渐进,看一看什么是循环依赖?开始先简略回顾一下 Bean 的创立过程,当然小伙伴也能够间接浏览『 单例 Bean 的创立 』这篇文章。 不过思考到浏览本文前再浏览上一篇文章、Debug 等等,会比拟耗时,所以本篇文章后面一小部分会先对之前的文章内容做简要概括,也相当于对我本人学习的常识进行一个总结。 先来回顾一下三级缓存的概念。 singletonObjects: 一级缓存,存储单例对象,Bean 曾经实例化,初始化实现。earlySingletonObjects: 二级缓存,存储 singletonObject,这个 Bean 实例化了,还没有初始化。 singletonFactories: 三级缓存,存储 singletonFactory。 Bean 的创立过程@Servicepublic class CircularServiceA { private String fieldA = "字段 A";} 通过下面的流程,能够看出 Spring 在创立 Bean 的过程中重点是在 AbstractAutowireCapableBeanFactory 中的以下三个步骤: 实例化 createBeanInstance: 其中实例化 Bean 并对 Bean 进行赋值,像例子中的 fieldA 字段在这里就会赋值。属性注入 populateBean: 能够了解为对 Bean 外面的属性进行赋值。(会依赖其余 Bean)初始化 initializeBean: 执行初始化和 Bean 的后置处理器。实例化赋值源码能够浏览:BeanUtils.instantiateClass(constructorToUse) 如果要依赖其余 Bean 呢?那如果 CircularServiceA 依赖了其余 Bean 呢? ...

January 31, 2021 · 3 min · jiezi

关于spring:亲自动手设计一个高并发的秒杀系统

一、需要有多款商品,每款商品均100件,每人限每款商品最多购买一件。在X月X日 X时X分0秒开启购买。在约定工夫之前,只能看到产品页面,购买按钮置灰。二、流动预估预计每种商品数万人参加流动开始后半分钟内,预计每种商品收到10W次交易申请,预计总TPS:20W/s流动开始半分钟后,预计绝大多数商品已售罄,剩下的商品仍反对秒杀,预计总TPS:2000/s三、零碎现状零碎可放弃长期稳固运行的最大TPS:1000/s短时间「1分钟内」零碎未拒绝服务的最高TPS:1500/s四、设计预研计划一:革新现有零碎,微服务零碎整体大幅减少TPS长处: 大幅减少TPS后,业务零碎可间接承载巨量交易申请,应酬秒杀需要会更加从容。毛病: 秒杀流动次数少,革新老本、危险较大,性价比低革新后,零碎架构会更加简单,可维护性升高计划二:开发新利用,承载该需要;革新现有利用以反对新利用长处: 对现有业务零碎革新小,成本低。应用新利用承载秒杀交易申请,灵便度高,不必过多思考向后兼容问题。毛病: 须要肯定水平就义用户体验。论断基于老本和可行性思考,采纳计划二 五、架构设计5.1 现有架构设计前台利用间接承接用户拜访、交易申请,无CDN服务。前台利用接管到交易申请后,间接RPC同步申请中台利用中台利用受到交易申请后,同步实现创立订单、领取、扣减商品数、实现交易等业务流程前台利用接管到中台利用的处理结果,向用户展现交易商品的物流信息。5.2 最新架构设计5.2.1 架构设计一览图 5.2.2服务模块设计1. 前端、客户端随机抛弃交易申请依据用户查看商品详情页的次数,预估商品的热门水平,对不同商品设定不同的抛弃比率,设计字段:discard,取值为 [0, 100),作为商品属性。前端、客户端,在用户提交交易申请时,生成 [0, 100] 范畴内的随机数,discard < 随机数的订单间接抛弃,但向用户展现排队页面,排队10秒后间接显示交易失败。2. 提供CDN服务动态资源均保留在CDN服务中前端展现的专门秒杀页面,除了「交易发动」申请外,用户的任何操作均不会申请到公司的微服务业务零碎。3. 开发专门的「秒杀利用」,提供熔断服务秒杀利用间接承载前端、客户端的交易申请,受权通过的申请发往前台利用解决,受权回绝的申请间接拦挡,返回交易失败。秒杀利用通过配置核心,实时获取最新配置。可配置对交易的抛弃比率,开发共事在秒杀时间段内,调整配置,保障发往业务零碎的交易申请,TPS放弃在 10000/s 以内。秒杀利用无数据库,无状态,程度扩大后可成比例减少吞吐量。4. 现有业务系统优化吞吐前台、中台各利用,通过优化设计进而优化吞吐,有如下优化形式: 采纳分库分表等程度拆分形式,横向扩大数据库,此时也能够大幅减少POD数了静态数据缓存在Redis中,数据优先从Redis中取,缓存定时生效,Redis中没有时查问可数据库。「轻微的缓存击穿无理论影响」分库分表策略: 依照商品拆分数据库与利用依照用户id二次拆分数据库与利用通过上述优化,至多能够将中台业务零碎的短时间「1分钟内」零碎未拒绝服务的最高TPS,由2000/s晋升至4000/s因为前台应用逻辑较简略,TPS可晋升至10000/s,无需再优化。因为木桶效应,业务零碎整体的短时间「1分钟内」零碎未拒绝服务的最高TPS,为4000/s5. 异步队列前台利用效率较高,此处仅优化中台利用即可。前台利用将秒杀交易申请均发往 Kafka,中台利用通过 Kafka 生产音讯,进行理论业务解决。因为业务顶峰时间段最多仅有半分钟 ~ 1分钟,中台利用在 2 分钟左右解决齐全部交易也能够承受。Kafka 的 TPS 为数十 W/s,承载前台 5000/s ~ 10000/s 的TPS毫无压力。经此优化,业务零碎整体的短时间「1分钟内」零碎未拒绝服务的最高TPS,可从 4000/s 晋升至 10000/s毛病:极其状况下,交易解决会较为迟缓。 6. 服务降级秒杀开始前半小时到秒杀完结后的半小时,这个时间段内,局部服务降级时间段内,不展现物流信息、不展现优惠券,保障外围业务零碎的稳固

January 30, 2021 · 1 min · jiezi