随机数设置及参数间引用

1.8 随机数设置及参数间引用 在Spring Boot配置文件中设置属性时,除了可以像前面示例中显示的配置属性值外,还可以使用随机值和参数间引用对属性值进行设置。下面,针对配置文件中这两种属性值的设置方式进行讲解 1.8.1随机值设置 在Spring Boot配置文件中,随机值设置使用到了Spring Boot内嵌的RandomValuePropertySource类,对一些隐秘属性值或者测试用例属性值进行随机值注入 随机值设置的语法格式为${random.xx},xx表示需要指定生成的随机数类型和范围,它可以生成随机的整数、uuid或字符串,示例代码如下 properties my.secret=${random.value} // 配置随机值 my.number=${random.int} // 配置随机整数 my.bignumber=${random.long} // 配置随机long类型数 my.uuid=${random.uuid} // 配置随机uuid类型数 my.number.less.than.ten=${random.int(10)} // 配置小于10的随机整数 my.number.in.range=${random.int[1024,65536]} // 配置范围在[1024,65536]之间的随机整数 上述代码中,使用RandomValuePropertySource类中random提供的随机数类型,分别展示了不同类型随机值的设置示例 1.8.2参数间引用 在Spring Boot配置文件中,配置文件的属性值还可以进行参数间的引用,也就是在后一个配置的属性值中直接引用先前已经定义过的属性,这样可以直接解析其中的属性值了。 使用参数间引用的好处就是,在多个具有相互关联的配置属性中,只需要对其中一处属性预先配置,其他地方都可以引用,省去了后续多处修改的麻烦 参数间引用的语法格式为${xx},xx表示先前在配置文件中已经配置过的属性名,示例代码如下 properties app.name=MyApp app.description=${app.name} is a Spring Boot application 上述参数间引用设置示例中,先设置了“app.name=MyApp”,将app.name属性的属性值设置为了MyApp;接着,在app.description属性配置中,使用${app.name}对前一个属性值进行了引用 接下来,通过一个案例来演示使用随机值设置以及参数间引用的方式进行属性设置的具体使用和效果,具体步骤如下 (1)打开Spring Boot项目resources目录下的application.properties配置文件,在该配置文件中分别通过随机值设置和参数间引用来配置两个测试属性,示例代码如: properties 随机值设置以及参数间引用配置 tom.age=${random.int[10,20]} tom.description=tom的年龄可能是${tom.age} 在上述application.properties配置文件中,先使用随机值设置了tom.age属性的属性值,该属性值设置在了[10,20]之间,随后使用参数间引用配置了tom.description属性 (2)打开\项目的测试类,在该测试类中新增字符串类型的description属性,并将配置文件中的tom.description属性进行注入,然后新增一个测试方法进行输出测试,示例代码如下 java @Value("${tom.description}") private String description; ...

June 9, 2020 · 1 min · jiezi

自定义RedisCacheManager

自定义RedisCacheManager 在项目的Redis配置类RedisConfig中,按照上一步分析的定制方法自定义名为cacheManager的Bean组件 @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) { // 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换 RedisSerializer<String> strSerializer = new StringRedisSerializer(); Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class); // 解决查询缓存转换异常的问题 ObjectMapper om = new ObjectMapper(); om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY); om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL); jacksonSeial.setObjectMapper(om); // 定制缓存数据序列化方式及时效 RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofDays(1)) .serializeKeysWith(RedisSerializationContext.SerializationPair .fromSerializer(strSerializer)) .serializeValuesWith(RedisSerializationContext.SerializationPair .fromSerializer(jacksonSeial)) .disableCachingNullValues(); RedisCacheManager cacheManager = RedisCacheManager .builder(redisConnectionFactory).cacheDefaults(config).build(); return cacheManager; } 上述代码中,在RedisConfig配置类中使用@Bean注解注入了一个默认名称为方法名的cacheManager组件。在定义的Bean组件中,通过RedisCacheConfiguration对缓存数据的key和value分别进行了序列化方式的定制,其中缓存数据的key定制为StringRedisSerializer(即String格式),而value定制为了Jackson2JsonRedisSerializer(即JSON格式),同时还使用entryTtl(Duration.ofDays(1))方法将缓存数据有效期设置为1天 完成基于注解的Redis缓存管理器RedisCacheManager定制后,可以对该缓存管理器的效果进行测试(使用自定义序列化机制的RedisCacheManager测试时,实体类可以不用实现序列化接口) 学习让人快乐,学习更让人觉得无知!学了1个多月的《Java工程师高薪训练营》,才发现自己对每个技术点的认知都很肤浅,根本深不下去,立个Flag:每天坚持学习一小时,一周回答网上3个技术问题,把自己知道都分享出来。

June 9, 2020 · 1 min · jiezi

Redis注解默认序列化机制

Redis注解默认序列化机制 打开Spring Boot整合Redis组件提供的缓存自动配置类RedisCacheConfiguration(org.springframework.boot.autoconfigure.cache包下的),查看该类的源码信息,其核心代码如下 @Configurationclass RedisCacheConfiguration { @Bean public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory,ResourceLoader resourceLoader) { RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory) .cacheDefaults(this.determineConfiguration(resourceLoader.getClassLoader())); List<String> cacheNames = this.cacheProperties.getCacheNames(); if(!cacheNames.isEmpty()) { builder.initialCacheNames(new LinkedHashSet(cacheNames)); } return (RedisCacheManager)this.customizerInvoker.customize(builder.build()); } private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(ClassLoader classLoader){ if(this.redisCacheConfiguration != null) { return this.redisCacheConfiguration; } else { Redis redisProperties = this.cacheProperties.getRedis(); org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration.defaultCacheConfig(); config = config.serializeValuesWith(SerializationPair.fromSerializer( new JdkSerializationRedisSerializer(classLoader))); ... return config; } }}从上述核心源码中可以看出,同RedisTemplate核心源码类似,RedisCacheConfiguration内部同样通过Redis连接工厂RedisConnectionFactory定义了一个缓存管理器RedisCacheManager;同时定制RedisCacheManager时,也默认使用了JdkSerializationRedisSerializer序列化方式。 如果想要使用自定义序列化方式的RedisCacheManager进行数据缓存操作,可以参考上述核心代码创建一个名为cacheManager的Bean组件,并在该组件中设置对应的序列化方式即可 注意,在Spring Boot 2.X版本中,RedisCacheManager是单独进行构建的。因此,在Spring Boot 2.X版本中,对RedisTemplate进行自定义序列化机制构建后,仍然无法对RedisCacheManager内部默认序列化机制进行覆盖(这也就解释了基 于注解的Redis缓存实现仍然会使用JDK默认序列化机制的原因),想要基于注解的Redis缓存实现也使用自定义序列化机制,需要自定义RedisCacheManager刚学了拉勾教育的《Java工程师高薪训练营》,看到刚学到的点就回答了。希望拉勾能给我推到想去的公司,目标:字节!! ...

June 9, 2020 · 1 min · jiezi

源码分析-手写mybaitspring核心功能干货好文一次学会工厂bean类代理bean注册的使用

作者:小傅哥博客:https://bugstack.cn - 汇总系列原创专题文章 沉淀、分享、成长,让自己和他人都能有所收获!?一、前言介绍一个知识点的学习过程基本分为;运行helloworld、熟练使用api、源码分析、核心专家。在分析mybaits以及mybatis-spring源码之前,我也只是简单的使用,因为它好用。但是他是怎么做的多半是凭自己的经验去分析,但始终觉得这样的感觉缺少点什么,在几次夙兴夜寐,靡有朝矣之后决定彻底的研究一下,之后在去仿照着写一版核心功能。依次来补全自己的技术栈的空缺。在现在技术知识像爆炸一样迸发,而我们多半又忙于工作业务开发。就像一个不会修车的老司机,只能一脚油门,一脚刹车的奔波。车速很快,但经不起坏,累觉不爱。好!为了解决这样问题,也为了钱程似锦(形容钱多的想家里的棉布一样),努力! 开动之前先庆祝下我的iPhone4s又活了,还是那么好用(嗯!有点卡); 二、以往章节关于mybaits & spring 源码分析以及demo功能的章节汇总,可以通过下列内容进行系统的学习,同时以下章节会有部分内容涉及到demo版本的mybaits; 源码分析 | Mybatis接口没有实现类为什么可以执行增删改查源码分析 | 像盗墓一样分析Spring是怎么初始化xml并注册bean的源码分析 | 基于jdbc实现一个Demo版的Mybatis三、一碟小菜类代理往往从最简单的内容才有抓手。先看一个接口到实现类的使用,在将这部分内容转换为代理类。 1. 定义一个 IUserDao 接口并实现这个接口类public interface IUserDao { String queryUserInfo();}public class UserDao implements IUserDao { @Override public String queryUserInfo() { return "实现类"; }}2. new() 方式实例化IUserDao userDao = new UserDao();userDao.queryUserInfo();这是最简单的也是最常用的使用方式,new 个对象。 3. proxy 方式实例化ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Class<?>[] classes = {IUserDao.class};InvocationHandler handler = (proxy, method, args) -> "你被代理了 " + method.getName();IUserDao userDao = (IUserDao) Proxy.newProxyInstance(classLoader, classes, handler);String res = userDao.queryUserInfo();logger.info("测试结果:{}", res);Proxy.newProxyInstance 代理类实例化方式,对应传入类的参数即可ClassLoader,是这个类加载器,我们可以获取当前线程的类加载器InvocationHandler 是代理后实际操作方法执行的内容,在这里可以添加自己业务场景需要的逻辑,在这里我们只返回方法名测试结果: ...

June 9, 2020 · 5 min · jiezi

SpringApplication实例的初始化创建

SpringApplication实例的初始化创建 查看SpringApplication实例对象初始化创建的源码信息,核心代码具体如下 java public SpringApplication(ResourceLoader resourceLoader, Class... primarySources) { this.sources = new LinkedHashSet(); this.bannerMode = Mode.CONSOLE; this.logStartupInfo = true; this.addCommandLineProperties = true; this.addConversionService = true; this.headless = true; this.registerShutdownHook = true; this.additionalProfiles = new HashSet(); this.isCustomEnvironment = false; this.resourceLoader = resourceLoader; Assert.notNull(primarySources, "PrimarySources must not be null"); //把项目启动类.class设置为属性存储起来 this.primarySources = new LinkedHashSet(Arrays.asList(primarySources)); //判断当前webApplicationType应用的类型 this.webApplicationType = WebApplicationType.deduceFromClasspath(); // 设置初始化器(Initializer),最后会调用这些初始化器 this.setInitializers(this.getSpringFactoriesInstances( ApplicationContextInitializer.class)); // 设置监听器(Listener) this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)); // 用于推断并设置项目main()方法启动的主程序启动类 this.mainApplicationClass = this.deduceMainApplicationClass(); ...

June 8, 2020 · 1 min · jiezi

SpringApplication的初始化过程

SpringApplication的初始化过程主要包括4部分,具体说明如下。 (1)this.webApplicationType = WebApplicationType.deduceFromClasspath() 用于判断当前webApplicationType应用的类型。deduceFromClasspath()方法用于查看Classpath类路径下是否存在某个特征类,从而判断当前webApplicationType类型是SERVLET应用(Spring 5之前的传统MVC应用)还是REACTIVE应用(Spring 5开始出现的WebFlux交互式应用) (2)this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class)) 用于SpringApplication应用的初始化器设置。在初始化器设置过程中,会使用Spring类加载器SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的应用初始化器类ApplicationContextInitializer。 (3)this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class)) 用于SpringApplication应用的监听器设置。监听器设置的过程与上一步初始化器设置的过程基本一样,也是使用SpringFactoriesLoader从META-INF/spring.factories类路径下的META-INF下的spring.factores文件中获取所有可用的监听器类ApplicationListener。 (4)this.mainApplicationClass = this.deduceMainApplicationClass() 用于推断并设置项目main()方法启动的主程序启动类 学习让人快乐,学习更让人觉得无知!学了1个多月的《Java工程师高薪训练营》,才发现自己对每个技术点的认知都很肤浅,根本深不下去,立个Flag:每天坚持学习一小时,一周回答网上3个技术问题,把自己知道都分享出来。

June 8, 2020 · 1 min · jiezi

Spring-Security-中的身份认证

原文链接:https://blog.gaoyuexiang.cn/2020/06/07/spring-security-authentication/,内容无差别。 本文介绍 Spring Security 的身份认证的内容,研究 Spring Security 自带的身份认证方式和添加自己的身份认证方式的方法。 身份认证相关组件在上一篇文章中,我们了解到了 Spring Security 会将 DelegatingFilterProxy 插入到 Servlet Filter Chain 中,然后将要过滤的请求通过 FilterChainProxy 代理给匹配的 SecurityFilterChain;这些 SecurityFilterChain 中包含着真正做安全相关工作的 Filter。 这些 Filter 中的一部分,他们的职责就是进行身份验证,比如 UsernamePasswordAuthenticationFilter;而他们中的大多数都有一个共同的父类 AbstractAuthenticationProcessingFilter。 AbstractAuthenticationProcessFilter这个类是很多身份认证的 Filter 的父类,它已经实现了 doFilter 方法,流程如下: 本文不涉及其中的 sessionStrategy 部分doFilter 已经帮我们搭好了这个流程,我们只需要关心其中的几个被调用的方法(红绿蓝三个颜色框)就可以了。 attemptAuthentication这是一个抽象方法。子类实现的时候,需要从 HttpServletRequest 中获取需要的信息,构建出一个 Authentication 实例,然后调用父类中的 AuthenticationManager.authenticate() 方法,对 Authentication 对象进行认证。 unsuccessfulAuthentication这个方法已经被实现,子类也可以选择重写。根据父类的实现,这个方法将完成一下步骤: 清理 SecurityContextHolder清除 RememberMeService 中的信息(默认使用 NullRememberMeService)调用 AuthenticationFailureHandler.onAuthenticationFailure() 方法默认使用的 AuthenticationFailureHandler 是 SimpleUrlAuthenticationFailureHandler,它的逻辑是: 如果没有配置 defaultFailureUrl (默认没有) 发送 401 响应根据配置的布尔值 forwardToDestination (默认为 false)判断 ...

June 7, 2020 · 2 min · jiezi

Spring-整体架构

概览Spring 是一个分层架构、由一系列的模块组成 Core Container核心容器,包含了 Core、Beans、Context、Expression Language 模块。 Core 和 Beans 模块是框架的基础部分、提供 IOC (控制反转)和依赖注入特性,这里的基础概念是 BeanFactory。 Core: 主要包含 Spring 框架基本的核心工具类,Spring 的其他组件都要用到这个包里的类,Core 模块是其他组件的基本核心。Beans: 包含访问配置文件、创建和管理 Bean 以及进行 IOC 操作相关的所有类。Context: 构建在 Core 和 Beans 模块基础上,提供了类似 JNDI 注册期的框架式的对象访问方法。Context 继承了 Beans 的特性,为 Spring 和行提供了大量扩展,添加了国际化,事件传播,资源加载和 Context 的透明创建的支持。ApplicationContext 接口是 Context 模块的关键。Expression Language: 提供了强大的表达式语言,用于在运行时查询和操纵对象。Data Access / Integration包含 JDBC、ORM、JMS、OXM 和 Transaction 模块。 Jdbc: 该模块提供了一个 JDBC 抽象层,它可以消除冗长的 JDBC 编码和解析数据库厂商特有的错误码。这个模块包含了 Spring 对 JDBC 数据访问进行封装的所有类。ORM: ORM 模块为流行的对象-关系映射 API ,如 JPA、Hibernate、Mybatis等,提供了一个交互层。利用 ORM 封装包,可以混合使用所有 Spring 提供的特性进行 O/R 映射。Oxm: 提供了一个对 Object / XML 映射实现对抽象层。Jms: 包含一些制造和消费消息的特性。Transaction: 支持编程性和声明性的事务管理,这些事务类必须实现特定的接口。Webweb 上下文模块建立在应用程序上下文模块之上,为基于 Web 的应用程序提供了上下文。Web 层包含了 Web 模块、Web-servlet、Web-portlet。 ...

June 6, 2020 · 1 min · jiezi

Spring-获取单例流程三

读完这篇文章你将会收获到 Spring 何时将 bean 加入到第三级缓存和第一级缓存中Spring 何时回调各种 Aware 接口、BeanPostProcessor 、InitializingBean 等相关文章 Spring 获取单例流程(一)Spring 获取单例流程(二)Spring 循环依赖 (公众号内查看(同时发布无法获取到链接))概述上两篇文章 Spring 获取单例流程(一) 和 Spring 获取单例流程(二) 介绍了 getBean 前面的流程,今天最后的收尾,把后面的流程继续一起学习下 源码分析// 我依赖的大哥都好了// Create bean instance.if (mbd.isSingleton()) { sharedInstance = getSingleton(beanName, () -> { try { return createBean(beanName, mbd, args); } catch (BeansException ex) { // 从三级缓存中移除这个 beanName 因为它可能被放进去了 因为放进去三级缓存可以解决 setter 的循环依赖 destroySingleton(beanName); throw ex; } }); bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);} 如果我们要创建的 bean 是一个单例, ...

June 6, 2020 · 3 min · jiezi

Spring-循环依赖

读完这篇文章你将会收获到 Spring 循环依赖可以分为哪两种Spring 如何解决 setter 循环依赖Spring 为何是三级缓存 , 二级不行 ?Spring 为啥不能解决构造器循环依赖概述循环依赖就是循环引用,两个或以上的 bean 相互持有对方。比如说 beanA 引用 beanB , beanB 引用 beanC , beanC 引用 beanA , 它们之间的引用关系构成一个环。 Spring 如何解决循环依赖Spring 中的循环依赖包括 构造器循环依赖setter 循环依赖构造器的依赖Spring 对于构造器的依赖、无法解决。只会抛出 BeanCurrentlyInCreationException 异常。 protected void beforeSingletonCreation(String beanName) { if (!this.inCreationCheckExclusions.contains(beanName) && !this.singletonsCurrentlyInCreation.add(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } }setter 的循环依赖不管是 autowireByName 还是 autowireByType 都是属于这种。Spring 默认是能够解决这种循环依赖的,主要是通过 Spring 容器提前暴露刚完成构造器注入但未完成其他步骤的 bean 来完成的。而且只能解决 singleton 类型的循环依赖、对于 prototype 类型的是不支持的,因为 Spring 没有缓存这种类型的 bean ...

June 6, 2020 · 3 min · jiezi

Spring-获取单例流程二

读完这篇文章你将会收获到 Spring 中 prototype 类型的 bean 如何做循环依赖检测Spring 中 singleton 类型的 bean 如何做循环依赖检测前言继上一篇文章 Spring 获取单例流程(一) 我们这次继续往下分析一下后面的流程 上一篇文章中我们说到,首先我们根据 name 找到其对应的 beanName 、然后去缓存中看是否已经创建了/创建中这个对应的 bean,如果在缓存中找到了这个 bean、那么我们需要对这个 bean 可能进行一些处理,比如说用户想要的是一个普通的 bean 、但是在 Spring 缓存中找到的是一个 factoryBean、这个时候就要调用 fatoryBean 的 getObject 方法以及对应的一些前置方法以及回调等。 那么如果我们在缓存中找不到这个 bean 那么流程又是怎么样?这个就是这篇文章要跟大家一起分享的 源码分析if (sharedInstance != null && args == null) { // 这里被我删除了一些spring 的log // 处理一下 factory bean 的情况、包括从 factory beans 的缓存中获取、或者重新调用 factory bean 的 get bean 方法 包括一些回调 bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);} else { // 从 上面的 getSingleton 拿不到对象的bean 、说明这个bean的scope 要么不是 singleton 要这个bean是singleton 但是没有初始化一句 // 因为 Spring 只解决单例模式下得循环依赖,在原型模式下如果存在循环依赖则会抛出异常 // 这里的循环依赖检查使用的 是 threadLocal 因为 prototype 类型的只是 if (isPrototypeCurrentlyInCreation(beanName)) { throw new BeanCurrentlyInCreationException(beanName); } // 如果容器中没有找到,则从父类容器中加载 BeanFactory parentBeanFactory = getParentBeanFactory(); // parentBeanFactory 不为空且 beanDefinitionMap 中不存该 name 的 BeanDefinition if (parentBeanFactory != null && !containsBeanDefinition(beanName)) { // Not found -> check parent. // 这里只是找出他的真正的beanName、并没有去掉 factory bean 的前缀 String nameToLookup = originalBeanName(name); if (parentBeanFactory instanceof AbstractBeanFactory) { return ((AbstractBeanFactory) parentBeanFactory).doGetBean( nameToLookup, requiredType, args, typeCheckOnly); } else if (args != null) { // Delegation to parent with explicit args. return (T) parentBeanFactory.getBean(nameToLookup, args); } else if (requiredType != null) { // No args -> delegate to standard getBean method. return parentBeanFactory.getBean(nameToLookup, requiredType); } else { return (T) parentBeanFactory.getBean(nameToLookup); } } ....... ....... ........}第一步就是判断这个是否是一个 prototype 类型的 bean,如果是并且正在创建中、那么就抛出一个循环依赖的异常 ...

June 6, 2020 · 4 min · jiezi

Spring-容器的初始化

读完这篇文章你将会收获到 了解到 Spring 容器初始化流程ThreadLocal 在 Spring 中的最佳实践面试中回答 Spring 容器初始化流程引言我们先从一个简单常见的代码入手分析 <?xml version="1.0" encoding="UTF-8"?><!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "https://www.springframework.org/dtd/spring-beans-2.0.dtd"><beans> <bean class="com.demo.data.Person"> <description> 微信搜一搜:CoderLi(不妨关注➕一下?这次一定?) </description> </bean></beans>public static void main(String[] args) { Resource resource = new ClassPathResource("coderLi.xml"); DefaultListableBeanFactory defaultListableBeanFactory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(defaultListableBeanFactory); xmlBeanDefinitionReader.loadBeanDefinitions(resource); }上面这段 Java 代码主要做了 资源的获取(定位)创建一个 beanFactory根据 beanFactory (实现了 BeanDefinitionRegistry 接口) 创建一个 beanDefinitionReader装载资源并 registry 资源里面的 beanDefinition所以总体而言就是资源的加载、加载、注册三个步骤 对于资源的加载可以看看我另一篇文章 Spring-资源加载(源码分析)加载的过程则是将 Resource 对象转为一系列的 BeanDefinition 对象注册则是将 BeanDefinition 注入到 BeanDefinitionRegistry 中组件介绍在分析源码流程之前我们一起先对一些重要的组件混个眼熟 ...

June 6, 2020 · 6 min · jiezi

SpringAliasRegistry

使用Spring 的时候我们可以很容易的为某个bean 配置一个或多个别名 <bean id="app:dataSource" class="..."> <alias name="app:dataSoure" alias="user:dataSoure"/> <alias name="app:dataSoure" alias="device:dataSoure"/> </bean> 或者: 直接使用bean标签的name属性,就是别名 <bean id="aaa",name="bbb,ccc,ddd"/> 使用 @Bean 注解的时候 @Bean(value = {"aaa", "bbb", "ccc"})那么 除了第一个是这个 beanName(bean id) 之外、其他的都是 alias public interface AliasRegistry { /** * 为这个 name 注册一个 alias */ void registerAlias(String name, String alias); /** * 从注册表中移除这个alias对应的关系 */ void removeAlias(String alias); /** * 给定的这个 name是否是一个 别名 */ boolean isAlias(String name); /** * 根据这个 bean name 获取所有他的别名 */ String[] getAliases(String name);}AliasRegistry 其中的一个实现类 SimpleAliasRegistry ...

June 6, 2020 · 2 min · jiezi

Spring资源加载

在 Java 中,将不同来源的资源抽象成 URL ,通过注册不同的 handler ( URLStreamHandler ) 来处理不同来源的资源的读取逻辑。 然而 URL 没有默认定义相对 Classpath 或 ServletContext 等资源的 handler ,虽然可以注册自己的 URLStreamHandler 来解析特定的 URL 前缀(协议)。但是 URL 也没有提供基本的方法、如检查当前资源是否存在,检查资源是否存在等方法。 URL: 我可以加载各种的资源.......XXXSpring: 你是个好人 Spring 实现了自己的资源加载策略 职能划分清楚。资源的定义和资源的加载有明确的界限统一的抽象,统一的资源定义和资源加载策略。统一资源org.springframework.core.io.Resource 接口抽象了所有 Spring 内部使用到的底层资源,如 File、URL、Classpath。 public interface Resource extends InputStreamSource { ....... .......}而 org.springframework.core.io.InputStreamSource 封装任何能返回 InputStream 的类、如 File、Classpath 下的资源和 ByteArray,该接口只有一个方法 getInputStream public interface InputStreamSource { /** * 表示任意形式的资源都可以被转换成输入流、最好每一次调用这个方法的时候都是返回一个新的InputStream * 看子类{@link FileSystemResource}符合这个要求 */ InputStream getInputStream() throws IOException;}Resource 接口提供了比 URL 更加多和便捷的方法 ...

June 6, 2020 · 3 min · jiezi

applicationproperties配置文件

1.5.1 application.properties配置文件使用Spring Initializr方式构建Spring Boot项目时,会在resource目录下自动生成一个空的application.properties文件,Spring Boot项目启动时会自动加载application.properties文件。我们可以在application.properties文件中定义Spring Boot项目的相关属性,当然,这些相关属性可以是系统属性、环境变量、命令参数等信息,也可以是自定义配置文件名称和位置 server.port=8081 spring.datasource.driver-class-name=com.mysql.jdbc.Driver spring.config.additional-location= spring.config.location= spring.config.name=application 1234567891011接下来,通过一个案例对Spring Boot项目中application.properties配置文件的具体使用进行讲解演示:预先准备了两个实体类文件,后续会演示将application.properties配置文件中的自定义配置属性注入到Person实体类的对应属性中(1)先在项目的com.lagou包下创建一个pojo包,并在该包下创建两个实体类Pet和Person public class Pet { private String type; private String name; // 省略属性getXX()和setXX()方法 // 省略toString()方法 }1234567891011121314151617181920 @Component //用于将Person类作为Bean注入到Spring容器中 @ConfigurationProperties(prefix ="person") //将配置文件中以person开头的属性注入到该类中 public class Person { private int id; //id private String name; //名称 private List hobby; //爱好 private String[] family; //家庭成员 private Map map; private Pet pet; //宠物 // 省略属性getXX()和setXX()方法 // 省略toString()方法 }1234567891011121314151617181920212223242526272829303132@ConfigurationProperties(prefix =“person”)注解的作用是将配置文件中以person开头的属性值通过setXX()方法注入到实体类对应属性中@Component注解的作用是将当前注入属性值的Person类对象作为Bean组件放到Spring容器中,只有这样才能被@ConfigurationProperties注解进行赋值(2)打开项目的resources目录下的application.properties配置文件,在该配置文件中编写需要对Person类设置的配置属性编写application.properties配置文件时,由于要配置的Person对象属性是我们自定义的,Spring Boot无法自动识别,所以不会有任何书写提示。在实际开发中,为了出现代码提示的效果来方便配置,在使用@ConfigurationProperties注解进行配置文件属性值注入时,可以在pom.xml文件中添加一个Spring Boot提供的配置处理器依赖: ...

June 5, 2020 · 1 min · jiezi

完成数据的页面展示

完成数据的页面展示 1. 创建Spring Boot项目,引入Thymeleaf依赖 <img src="./images/image-20191230160651703.png" alt="image-20191230160651703" /> 2. 编写配置文件 打开application.properties全局配置文件,在该文件中对Thymeleaf模板页面的数据缓存进行设置 properties # thymeleaf页面缓存设置(默认为true),开发中方便调试应设置为false,上线稳定后应保持默认true spring.thymeleaf.cache=false 使用“spring.thymeleaf.cache=false”将Thymeleaf默认开启的缓存设置为了false,用来关闭模板页面缓存 3. 创建web控制类 在项目中创建名为com.lagou.controller的包,并在该包下创建一个用于前端模板页面动态数据替换效果测试的访问实体类LoginController ```java @Controller public class LoginController { /** * 获取并封装当前年份跳转到登录页login.html      */ @RequestMapping("/toLoginPage") public String toLoginPage(Model model){ model.addAttribute("currentYear", Calendar.getInstance().get(Calendar.YEAR)); return "login"; } ``` toLoginPage()方法用于向登录页面login.html跳转,同时携带了当前年份信息currentYear。 4.创建模板页面并引入静态资源文件 在“classpath:/templates/”目录下引入一个用户登录的模板页面login.html ```html <!DOCTYPE html> <html lang="en" xmlns:th="http://www.thymeleaf.org"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1,shrink-to-fit=no"> <title>用户登录界面</title> <link th:href="@{/login/css/bootstrap.min.css}" rel="stylesheet"> <link th:href="@{/login/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <!--  用户登录form表单 --> <form class="form-signin"> ...

June 5, 2020 · 1 min · jiezi

编写Web访问层Controller文件

编写Web访问层Controller文件 java @RestController @RequestMapping("api") //窄化请求路径 public class ApiCommentController { @Autowired private ApiCommentService commentService; @RequestMapping(value = "/findCommentById") public Comment findCommentById(Integer id){ Comment comment = commentService.findCommentById(id); return comment; } @RequestMapping(value = "/updateComment") public void updateComment(Comment comment){ Comment comment2 = commentService.findCommentById(comment.getId()); comment.setAuthor(comment.getAuthor()); commentService.updateComment(comment); } @RequestMapping(value = "/deleteComment") public void deleteComment(int id){ commentService.deleteComment(id); } } * 基于API的Redis缓存实现的相关配置。基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持,所以这里可以选择将添加在项目启动类上的@EnableCaching进行删除或者注释 这些内容,是从拉勾教育的《Java工程师高薪训练营》里学到的,课程内容非常全面,还有拉勾的内推大厂服务,推荐你也看看。

June 5, 2020 · 1 min · jiezi

编写多语言国际化配置文件

编写多语言国际化配置文件 在项目的类路径resources下创建名称为i18n的文件夹,并在该文件夹中根据需要编写对应的多语言国际化文件login.properties、login_zh_CN.properties和login_en_US.properties文件 login.properties properties login.tip=请登录 login.username=用户名 login.password=密码 login.rememberme=记住我 login.button=登录 login_zh_CN.properties properties login.tip=请登录 login.username=用户名 login.password=密码 login.rememberme=记住我 login.button=登录 login_en_US.properties properties login.tip=Please sign in login.username=Username login.password=Password login.rememberme=Remember me login.button=Login login.properties为自定义默认语言配置文件,login_zh_CN.properties为自定义中文国际化文件,login_en_US.properties为自定义英文国际化文件 需要说明的是,Spring Boot默认识别的语言配置文件为类路径resources下的messages.properties;其他语言国际化文件的名称必须严格按照“文件前缀名_语言代码_国家代码.properties”的形式命名 本示例中,在项目类路径resources下自定义了一个i18n包用于统一配置管理多语言配置文件,并将项目默认语言配置文件名自定义为login.properties,因此,后续还必须在项目全局配置文件中进行国际化文件基础名配置,才能引用自定义国际化文件 这些内容,是从拉勾教育的《Java工程师高薪训练营》里学到的,课程内容非常全面,还有拉勾的内推大厂服务,推荐你也看看。

June 5, 2020 · 1 min · jiezi

Spring-Boot-新手指南101

Spring Boot 新手指南101这篇文适合谁?有一定Java基础,且想进一步了解如何利用Java Spring Boot 搭建后台服务的你们。内容将分为两部分 Spring Boot 基础,项目创建,API定义,内存数据库搭建,实现基本的Restful操作增删查改Spring Boot进阶,连接Postgress 数据库Spring Boot基础基础数据层 数据持久: Data Access Layer服务层 业务逻辑: Service LayerAPI层 请求交互: API Layer 以数据层为例,我们不需要提前选定用哪个服务器(不论是Postgress 还是MongoDB),依赖注入框架的好处是,我们可以先定义好接口(Interface),之后根据实际情况调整具体的实现(Implementation)。 创建项目打开浏览器,去这个Spring 官方网站 https://start.spring.io/ 选择一个模板 接下来我们会利用如下的配置: MavenJava 8Spring Boot 2.3.0Name: demoPackaging: JarDependency: Spring Web (Build web, including RESTful, applications using Spring MVC. Uses Apache Tomcat as the default embedded container.)完整的项目依赖文件在 pom.xml 第一次运行打开刚刚下载的文件:demo.zip, 解压后用打开。笔者选用的是IntelliJ IDEA。 项目文件组织结构 src pom.xml main java DemoApplication 等待Spring下载好所有依赖后,打开 DemoApplication class, 找到main(), 点击 run ...

June 5, 2020 · 1 min · jiezi

Spring优缺点分析

1.2.1 spring优缺点分析优点: spring是Java企业版(Java Enterprise Edition,JEE,也称J2EE)的轻量级代替品。无需开发重量级的Enterprise JavaBean(EJB),Spring为企业级Java开发提供了一种相对简单的方法,通过依赖注入和面向切面编程,用简单 的Java对象(Plain Old Java Object,POJO)实现了EJB的功能 缺点: 虽然Spring的组件代码是轻量级的,但它的配置却是重量级的。一开始,Spring用XML配置,而且是很多XML配 置。Spring 2.5引入了基于注解的组件扫描,这消除了大量针对应用程序自身组件的显式XML配置。Spring 3.0引入 了基于Java的配置,这是一种类型安全的可重构配置方式,可以代替XML。 所有这些配置都代表了开发时的损耗。因为在思考Spring特性配置和解决业务问题之间需要进行思维切换,所以编 写配置挤占了编写应用程序逻辑的时间。和所有框架一样,Spring实用,但与此同时它要求的回报也不少。 除此之外,项目的依赖管理也是一件耗时耗力的事情。在环境搭建时,需要分析要导入哪些库的坐标,而且还需要分析导入与之有依赖关系的其他库的坐标,一旦选错了依赖的版本,随之而来的不兼容问题就会严重阻碍项目的开发进度 。 学习让人快乐,学习更让人觉得无知!学了1个多月的《Java工程师高薪训练营》,才发现自己对每个技术点的认知都很肤浅,根本深不下去,立个Flag:每天坚持学习一小时,一周回答网上3个技术问题,把自己知道都分享出来。

June 5, 2020 · 1 min · jiezi

编译Spring520源码

下载 spring-framework-5.2.0.RELEASE.zip https://github.com/spring-projects/spring-framework/releases下载gradle 5.6.3 按照说明配置环境变量 https://gradle.org/install/解压zip、查看根目录下的 import-into-idea.md 执行脚本 (windows 系统) .\gradlew.bat :spring-oxm:compileTestJava成功之后、使用IDEA打开 成功打开项目之后,我们将 spring-aspects 模块unload 然后我们可以新建一个我们自己使用的模块 打开 project structuer 然后我们为这个新建的模块引入一些我们需要用到的 Spring 的一些 jar 包 在我们的新模块中使用 Spring 的时候、可能会出现下面的问题 java: cannot find symbol symbol: variable CoroutinesUtils location: class org.springframework.core.ReactiveAdapterRegistry.CoroutinesRegistrar Error:(348, 51) java: cannot find symbol symbol: variable CoroutinesUtils location: class org.springframework.core.ReactiveAdapterRegistry.CoroutinesRegistrar解决办法 把这个 spring-core/kotlin-coroutines/build/libs/kotlin-coroutines-5.2.0.RELEASE.jar 引入到项目中即可 第二个可能出现的问题 Exception in thread "main" java.lang.NoClassDefFoundError: org/springframework/cglib/core/NamingPolicy 将 spring core 中的 build/lib 的 spring-cglib-repack-3.3.0.jar 和 spring-objenesis-repack-3.0.1.jar 引入到新增的模块中 ...

June 3, 2020 · 1 min · jiezi

使用-Spring-API-实现邮件发送-乐字节java乐字节架构

详细Spring如何进行邮件发送本文由乐字节Java架构课程独家赞助播出 环境准备创建 Maven 项目,在 pom.xml 文件中添加依赖 <!-- spring核心 jar 依赖 --><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.4.RELEASE</version></dependency><!--spring 上下文环境 支持--><dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>5.2.4.RELEASE</version></dependency><dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.2.4.RELEASE</version> <scope>test</scope></dependency><!-- Java Mail坐标依赖 --><dependency> <groupId>com.sun.mail</groupId> <artifactId>javax.mail</artifactId> <version>1.6.2</version></dependency>配置邮件发送 bean在 spring.xml 配置文件中设置邮件发送对应的bean标签 <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启自动化扫描 --> <context:component-scan base-package="com.xxxx"/> <!-- 邮件发送器(提供了邮件发送接口、透明创建Java Mail的MimeMessage、及邮件发送的配置) --> <bean id="mailSender" class="org.springframework.mail.javamail.JavaMailSenderImpl"> <property name="host" value="smtp.163.com" /> <property name="port" value="25" /> <property name="defaultEncoding" value="utf-8"></property> <property name="username" value="用户名"></property> <property name="password" value="密码"></property> </bean> <!-- 普通文本邮件对象 --> <bean id="templateMessage" class="org.springframework.mail.SimpleMailMessage"> <property name="from" value="发件人的邮箱地址" /> <property name="subject" value="spring_mail" /> </bean></beans>定义接口与实现类定义接口 ...

June 2, 2020 · 2 min · jiezi

spring中使用mybatis实现批量插入

有3种实现方式:foreach,spring事务,以及ExecutorType.BATCH. 1. foreach方式这种方式实际是对SQL语句进行拼接,生成一个长长的SQL,对很多变量进行绑定。如果数据量不大(1000个以内),可以用这种方式。如果数据量太大,可能数据库会报错。 定义接口public interface StudentMapper05 { public void insertStudent(List<Student> studentList);}定义mapper适用于Oracle数据库 <insert id="insertStudent"> BEGIN <foreach collection="list" item="student" index="index" separator=""> INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, #{student.name}, #{student.branch}, #{student.percentage}, #{student.phone}, #{student.email}); </foreach> END;</insert>这个mapper的含义,就是把上送的studentList拼接成一个长SQL,拼成的SQL类似: BEGININSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, ?, ?, ?, ?, ?);INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, ?, ?, ?, ?, ?);INSERT INTO test_student(ID, NAME, BRANCH, PERCENTAGE, PHONE, EMAIL) VALUES (SEQ_ID.nextval, ?, ?, ?, ?, ?);...END;studentList有几个,就会生成多少个insert语句拼接到一起,每个?都会进行变量绑定,所以当studentList中数据量较多时,生成的SQL会很长,导致数据库执行报错。 ...

June 1, 2020 · 3 min · jiezi

Spring-Security-Servlet-概览

Spring Security 是 Spring 框架中用于实现 Security 相关需求的项目。我们可以通过使用这个框架来实现项目中的安全需求。 今天这篇文章将会讨论 Spring Security Servlet 是如何工作的。 之所以将内容限定到 Servlet,是因为现在 Spring Security 已经开始支持 Reactive Web Server,因为底层的技术不同,当然需要分开讨论。 Spring Security 在哪里生效我们知道,在 Servlet 中,一次请求会经过这样的阶段: client -> servlet container -> filter -> servlet 而 Spring MVC 虽然引入了一些其他概念,但整体流程差别不大: Spring Security 则是通过实现了 Filter 来实现的 Security 功能。这样一来,只要使用了 Servlet Container,就可以使用 Spring Security,不需要关心有没有使用 Spring Web 或别的 Spring 项目。 DelegatingFilterProxy这是 Spring Security 实现的一个 Servlet Filter。它被加入到 Servlet Filter Chain 中,将 filter 的任务桥接给 Spring Context 管理的 bean。 ...

May 31, 2020 · 2 min · jiezi

从单体架构到分布式架构一万丈高楼平地起环境准备

【从单体架构到分布式架构】本系列文章希望用浅显直白的语言介绍架构发展过程中遇到的各种问题,以及对应的解决方案和优缺点。在正式学习之前,开发环境需要做好哪些准备呢?我们为什么要选择使用 Spring Boot 呢?Spring Boot 和 Spring 是两个截然不同的框架么?1. 环境准备本课程在学习过程中,会有大量的代码配合讲解,所以在正式学习之前,你需要做好以下准备。 1.1 JDK必备;本课程所有代码基于 JDK 1.8 编写。 C:\>java -versionjava version "1.8.0_171"Java(TM) SE Runtime Environment (build 1.8.0_171-b11)Java HotSpot(TM) 64-Bit Server VM (build 25.171-b11, mixed mode)1.2 Maven必备;本课程所有代码基于 Maven 构建;如果你在电脑上安装好了 Maevn,建议把 Maven 仓库的地址修改成一个国内的网站地址。 C:\>mvn -vC:\Apache Maven 3.3.9 (bb52d8502b132ec0a5a3f4c09453c07478323dc5; 2015-11-11T00:41:47+08:00)Maven home: D:\Program Files\apache-maven-3.3.9\bin\..Java version: 1.8.0_171, vendor: Oracle CorporationJava home: D:\Program Files\Java\jdk1.8.0_171\jreDefault locale: zh_CN, platform encoding: GBKOS name: "windows 8.1", version: "6.3", arch: "amd64", family: "dos"1.3 IDE必备;选择一个你熟悉的 IDE,我使用的是 Eclipse,这里要注意,有些 IDE 需要进行一些 Maven 环境变量的配置。 ...

May 31, 2020 · 3 min · jiezi

Thymeleaf货币转换

概述本文,将介绍如何使用页面组件Thymeleaf对货币进行自动转换 Maven依赖<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> <version>2.3.0.RELEASE</version></dependency>创建thymeleaf页面和Controller在resources/templates/下创建页面currencies.html <!DOCTYPE html><html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"><head> <meta charset="UTF-8"> <title>Currency Format</title></head><body><h3 th:text="${#numbers.formatCurrency(param.amount)}"></h3></body></html>创建CurrencyController.java package com.deepincoding.currencyformat;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;@Controllerpublic class CurrencyController { @GetMapping("/currency") public String current(@RequestParam(value = "amount") String amount){ return "currency"; }}我们需要根据浏览器的区域来转换货币。使用Accept-Language表示用户的区域.在Chrome浏览器里可以设置。 启动应用后,在浏览器里输入 http://localhost:8080/currency?amount=1000000.01 返回的结果是:¥1,000,000.01 如果把浏览的语言设置为English (United States) 返回的结果是: $1,000,000.01 列表或数组我们也可以转换一个列表或数组.修改Controller如下: package com.deepincoding.currencyformat;import org.springframework.stereotype.Controller;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.RequestParam;import java.util.List;@Controllerpublic class CurrencyController { @GetMapping("/currency") public String current(@RequestParam(value = "amount") String amount, @RequestParam(value = "amountList") List amountList){ return "currency"; }}修改页面如下: ...

May 30, 2020 · 1 min · jiezi

修炼内功springframework-6-Spring-AOP的其他实现方式

本文已收录【修炼内功】跃迁之路 在Spring AOP是如何代理的一文中介绍了Spring AOP的原理,了解到其通过JDK Proxy及CGLIB生成代理类来实现目标方法的切面(织入),这里有一个比较重要的概念 - 织入(Weaving),本篇就来探讨 什么是织入?织入有哪些类型以及实现手段?Spring分别是如何支持的?什么是织入织入为英文Weaving的直译,可字面理解为将额外代码插入目标代码逻辑中,实现对目标逻辑的增强、监控等,将不同的关注点进行解耦 织入的手段有很多种,不同的手段也对应不同的织入时机 Compile - Time Weaving (CTW) 编译期织入 在源码编译阶段,修改/新增源码,生成预期内的字节码文件 如AspectJ、Lombok、MapStruct等 Lombok、MapStruct等使用了 Pluggable Annotation Processing API 技术实现对目标代码的修改/新增AspectJ则直接使用了acj编译器 Load - Time - Weaving (LTW) 装载期织入 在Class文件的装载期,对将要装载的字节码文件进行修改,生成新的字节码进行替换 如AspectJ、Java Instrumentation等 Java Instrumentation只提供了字节码替换/重装载的能力,字节码文件的修改还需要借助外部框架,如javassist、asm等javassist的使用可以参考 Introduction to Javassist Run - Time - Weaving (RTW)运行时织入 在程序运行阶段,利用代理或者Copy目标逻辑的方式,生成新的Class并加载 如AspectJ、JDK Proxy、CGLIB等 在Spring AOP是如何代理的一文中所介绍的Spring AOP既是运行时织入 以javassist为例,Copy目标逻辑并增强(统计接口耗时),生成新的Class 该示例可应用到 装载期织入(使用新的Class替换目标Class)或 运行时织入(直接使用新的Class) // 目标代码逻辑public interface Animal { default String barkVoice() { return "bark bark"; }}public class Dog implements Animal { private final Random r = new Random(); @Override @Statistics("doggie") public String barkVoice() { try { Thread.sleep(Math.abs(r.nextInt()) % 3000); } catch (InterruptedException e) { e.printStackTrace(); } return "汪~汪~"; }}// 使用javassist,在目标代码基础上添加耗时统计逻辑,生成新的class并加载ClassPool classPool = ClassPool.getDefault();// Copy目标字节码CtClass dogClass = classPool.get(Dog.class.getName());// 设置新的类名dogClass.setName("proxy.Doggie");// 获取目标方法,并在其基础上增强CtMethod barkVoice = dogClass.getDeclaredMethod("barkVoice");barkVoice.addLocalVariable("startTime", CtClass.longType);barkVoice.insertBefore("startTime = System.currentTimeMillis();");barkVoice.insertAfter("System.out.println(\"The Dog bark in \" + (System.currentTimeMillis() - startTime) + \"ms\");");// 生成新的class (由于module机制的引入,在JDK9之后已不建议使用该方法)Class<?> doggieClass = dogClass.toClass();// 使用新的class创建对象Animal doggie = (Animal)doggieClass.getDeclaredConstructor().newInstance();// 输出 // > The Dog bark in 2453ms// > 汪~汪~System.out.println(doggie.barkVoice());JDK中的Load-Time WeavingJVM提供了两种agent包加载能力:static agent load、dynamic agent load,可分别在启动时(main函数运行之前)、运行时(main函数运行之后)加载agent包,并执行内部逻辑 ...

May 30, 2020 · 4 min · jiezi

Spring-邮件发送出现bug出现Bug以及解决方案乐字节

详细Spring如何进行邮件发送本文由乐字节Java架构课程独家赞助播出Spring 邮件发送主要内容 JavaMail 概述 JavaMail,顾名思义,提供给开发者处理电子邮件相关的编程接口。JavaMail 是由 Sun 定义的一套收发电子邮件的 API,它可以方便地执行一些常用的邮件传输,不同的厂商可以提供自己的实现类。但它并没有包含在 JDK 中,而是作为 JavaEE 的一部分。 厂商所提供的 JavaMail 服务程序可以有选择地实现某些邮件协议,常见的邮件协议包括: SMTP:简单邮件传输协议,用于发送电子邮件的传输协议;POP3:用于接收电子邮件的标准协议;IMAP:互联网消息协议,是 POP3 的替代协议。 这三种协议都有对应 SSL 加密传输的协议,分别是 SMTPS,POP3S 和 IMAPS。除 JavaMail 服务提供程序之外, JavaMail 还需要 JAF(JavaBeans Activation Framework)来处理不是纯文本的邮件内容,这包括 MIME(多用途互联网邮件扩展)、URL 页面和文件附件等内容。另外,JavaMail 依赖 JAF(JavaBeans Activation Framework),JAF 在 Java6 之后已经合并到 JDK 中,而 JDK5 之前需要另外下载 JAF 的类库。 协议介绍 在研究 JavaMail API 的细则之前,首先需要对于 API 用到的协议有个认识。对于 java mail 来说用到的协议有常见的几种: SMTP、POP、IMAP、MIME SMTP 简单邮件传输协议(Simple Mail Transfer Protocol,SMTP)由 RFC 821 定义。它定义了发送电子邮件的机制。在 JavaMail API 环境中,您基于 JavaMail 的程序将和您的公司或因特网服务供应商的(Internet ServiceProvider's,ISP's)SMTP 服务器通信。SMTP 服务器会中转消息给接收方 SMTP 服务器以便最终让用户经由 POP 或 IMAP 获得。 ...

May 29, 2020 · 2 min · jiezi

超详细4小时开发一个SpringBootvue前后端分离博客项目

作者:吕一明项目代码:https://github.com/MarkerHub/... 项目视频:https://www.bilibili.com/vide... 转载请保留此引用,感谢! 前后端分离项目文章总体分为2大部分,Java后端接口和vue前端页面,比较长,因为不想分开发布,真正想你4小时学会,哈哈。 先看效果: 不多说,开始敲代码。 Java后端接口开发1、前言从零开始搭建一个项目骨架,最好选择合适,熟悉的技术,并且在未来易拓展,适合微服务化体系等。所以一般以Springboot作为我们的框架基础,这是离不开的了。 然后数据层,我们常用的是Mybatis,易上手,方便维护。但是单表操作比较困难,特别是添加字段或减少字段的时候,比较繁琐,所以这里我推荐使用Mybatis Plus(https://mp.baomidou.com/),为简化开发而生,只需简单配置,即可快速进行 CRUD 操作,从而节省大量时间。 作为一个项目骨架,权限也是我们不能忽略的,Shiro配置简单,使用也简单,所以使用Shiro作为我们的的权限。 考虑到项目可能需要部署多台,这时候我们的会话等信息需要共享,Redis是现在主流的缓存中间件,也适合我们的项目。 然后因为前后端分离,所以我们使用jwt作为我们用户身份凭证。 ok,我们现在就开始搭建我们的项目脚手架! 技术栈: SpringBootmybatis plusshirolombokredishibernate validatiorjwt导图:https://www.markerhub.com/map/131 2、新建Springboot项目这里,我们使用IDEA来开发我们项目,新建步骤比较简单,我们就不截图了。 开发工具与环境: ideamysqljdk 8maven3.3.9新建好的项目结构如下,SpringBoot版本使用的目前最新的2.2.6.RELEASE版本 pom的jar包导入如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-devtools</artifactId> <scope>runtime</scope> <optional>true</optional></dependency><dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional></dependency>devtools:项目的热加载重启插件lombok:简化代码的工具3、整合mybatis plus接下来,我们来整合mybatis plus,让项目能完成基本的增删改查操作。步骤很简单:可以去官网看看:https://mp.baomidou.com/guide... 第一步:导入jar包 pom中导入mybatis plus的jar包,因为后面会涉及到代码生成,所以我们还需要导入页面模板引擎,这里我们用的是freemarker。 <!--mp--><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-boot-starter</artifactId> <version>3.2.0</version></dependency><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId></dependency><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope></dependency><!--mp代码生成器--><dependency> <groupId>com.baomidou</groupId> <artifactId>mybatis-plus-generator</artifactId> <version>3.2.0</version></dependency>第二步:然后去写配置文件 # DataSource Configspring: datasource: driver-class-name: com.mysql.cj.jdbc.Driver url: jdbc:mysql://localhost:3306/vueblog?useUnicode=true&useSSL=false&characterEncoding=utf8&serverTimezone=Asia/Shanghai username: root password: adminmybatis-plus: mapper-locations: classpath*:/mapper/**Mapper.xml上面除了配置数据库的信息,还配置了myabtis plus的mapper的xml文件的扫描路径,这一步不要忘记了。第三步:开启mapper接口扫描,添加分页插件 ...

May 28, 2020 · 15 min · jiezi

SpringSpringMVCSpringBootSpringCloud有什么区别和联系

简单介绍Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。Spring使你能够编写更干净、更可管理、并且更易于测试的代码。 Spring MVC是Spring的一个模块,一个web框架。通过Dispatcher Servlet, ModelAndView 和 View Resolver,开发web应用变得很容易。主要针对的是网站应用程序或者服务开发——URL路由、Session、模板引擎、静态Web资源等等。 Spring配置复杂,繁琐,所以推出了Spring boot,约定优于配置,简化了spring的配置流程。 Spring Cloud构建于Spring Boot之上,是一个关注全局的服务治理框架。 Spring和SpringMVC: Spring是一个一站式的轻量级的java开发框架,核心是控制反转(IOC)和面向切面(AOP),针对于开发的WEB层(springMvc)、业务层(Ioc)、持久层(jdbcTemplate)等都提供了多种配置解决方案; SpringMVC是Spring基础之上的一个MVC框架,主要处理web开发的路径映射和视图渲染,属于Spring框架中WEB层开发的一部分; SpringMVC和SpringBoot: SpringMVC属于一个企业WEB开发的MVC框架,涵盖面包括前端视图开发、文件配置、后台接口逻辑开发等,XML、config等配置相对比较繁琐复杂; SpringBoot框架相对于SpringMVC框架来说,更专注于开发微服务后台接口,不开发前端视图; SpringBoot和SpringCloud: SpringBoot使用了默认大于配置的理念,集成了快速开发的Spring多个插件,同时自动过滤不需要配置的多余的插件,简化了项目的开发配置流程,一定程度上取消xml配置,是一套快速配置开发的脚手架,能快速开发单个微服务; SpringCloud大部分的功能插件都是基于SpringBoot去实现的,SpringCloud关注于全局的微服务整合和管理,将多个SpringBoot单体微服务进行整合以及管理;SpringCloud依赖于SpringBoot开发,而SpringBoot可以独立开发; 总结下来:Spring是核心,提供了基础功能;Spring MVC 是基于Spring的一个 MVC 框架 ;Spring Boot 是为简化Spring配置的快速开发整合包;Spring Cloud是构建在Spring Boot之上的服务治理框架。

May 25, 2020 · 1 min · jiezi

IntelliJ-IDEA创建完整的Spring-Boot项目

使用IntelliJ IDEA创建一个完整的Spring Boot项目流程以IntelliJ IDEA2019.3为例 准备工作 本地安装好JDK电脑已经联网IDEA已经安装IDEA配置好了Maven环境1.打开IDEA开发工具,点击Create New Project创建一个新项目 2.左侧选择Spring Initializr,右侧选择连接https://start.spring.io ,点击next 3.此过程等待加载 4.选择新建项目的相关信息参考如下 Group公司或者组织域名的倒序Artifact 项目或者模块名Type:maven部署方式(一般不更改)packaging:打包方式(一般不更改)Java Version:Java版本(如果你的电脑上有多个JDK版本,请选择)Version:项目或者模块版本(一般不更改)Name:项目名Description:项目描述package:包名(根据需求设置)注意:所有的内容都必须是小写字母,否则IDEA不予通过! 设置完成后,点击Next 5.选择模块和Spring Boot的版本版本建议选择稳定版模块根据需要选择,我这里选择web,Mybatis,MySQL等右边可以看到选择的模块项目 6.填写项目名和项目路径Project name:项目名Project location:存放路径选择好之后,点击Finish. 7.等待Maven部署项目,最好不要乱动项目结构 8.运行项目运行项目方式 左下角的Terminal终端输入命令:mvn spring-boot:run运行运行主程序入口main方法直接运行Spring Boot项目 9.配置数据源运行项目之后,发现有错误,是因为添加了数据库的模块,但是没有配置数据源 数据源配置在项目中依次找到src/main/resources/application.properties文件,将下面的配置信息复制进去,修改为自己的版本 #配置数据库的项目信息(MySQL数据库)#配置数据库的驱动spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver# 旧版使用的驱动#spring.datasource.driver-class-name=com.mysql.jdbc.Driver#配置数据库连接URLspring.datasource.url=jdbc:mysql://127.0.0.1:3306/cms?characterEncoding=utf8&serverTimezone=UTC#配置数据库额度用户名spring.datasource.username=lele#配置数据库的密码spring.datasource.password=lele# 配置服务器的相关信息# 配置服务器的端口server.port=80注:数据库的驱动在不同的版本上不同,注意区分版本,因为还添加了web模块,可能存在服务器端口占用问题,我这里顺便将端口改成了80 10.配置完成后,再次运行项目使用命令:mvn spring-boot:run 运行之后光标闪烁,程序正常启动

November 13, 2019 · 1 min · jiezi

Spring-Cloud-gateway-网关服务二-断言过滤器

微服务当前这么火爆的程度,如果不能学会一种微服务框架技术。怎么能升职加薪,增加简历的筹码?spring cloud 和 Dubbo 需要单独学习。说没有时间?没有精力?要学俩个框架?而Spring Cloud alibaba只需要你学会一个就会拥有俩种微服务治理框架技术。何乐而不为呢?加油吧!骚猿年上一篇我们讲述了gateway 的路由功能其实也类似与zuul服务的路由转发。今天主要讲一下断言机制。 内置的断言工厂介绍 Spring Cloud Gateway将路由作为Spring WebFlux HandlerMapping基础架构的一部分进行匹配。Spring Cloud Gateway包括许多内置的Route Predicate工厂。所有这些断言都与HTTP请求的不同属性匹配。多个Route Predicate工厂可以合并,也可以通过逻辑合并 可以看到gateway 提供如此之多丰富的断言,方式。 比如说时间控住的,我们能想到秒杀场景。ip 的我们能想到金丝雀测试权重我们可以使用灰度等等场景的应用具体使用还得参照业务场景来选择更适合我们的业务场景。 gateway 过滤器 分为全局过滤器,个性化过滤器gateway 已经给我们提供了非常丰富的过滤器 AddRequestHeader GatewayFilter工厂采用名称和值参数。 这会将X-Request-Foo:Bar标头添加到所有匹配请求的下游请求的标头中。 spring: cloud: gateway: routes: - id: add_request_header_route uri: https://example.org filters: - AddRequestHeader=X-Request-Foo, Bar AddRequestHeader知道用于匹配路径或主机的URI变量。URI变量可用于该值,并将在运行时扩展。 spring: cloud: gateway: routes: - id: add_request_header_route uri: https://example.org predicates: - Path=/foo/{segment} filters: - AddRequestHeader=X-Request-Foo, Bar-{segment} -AddResponseHeader GatewayFilter工厂采用名称和值参数。 spring: cloud: gateway: routes: - id: add_request_parameter_route uri: https://example.org filters: - AddRequestParameter=foo, bar这将添加foo=bar到所有匹配请求的下游请求的查询字符串中。 ...

November 5, 2019 · 3 min · jiezi

ThreadLocal弱引用与内存泄漏分析

本文对ThreadLocal弱引用进行一些解析,以及ThreadLocal使用注意事项。 ThreadLocal首先,简单回顾一下,ThreadLocal是一个线程本地变量,每个线程维护自己的变量副本,多个线程互相不可见,因此多线程操作该变量不必加锁,适合不同线程使用不同变量值的场景。 其实现原理这里就不做详细阐述,其数据结构是每个线程Thread类都有个属性ThreadLocalMap,用来维护该线程的多个ThreadLocal变量,该Map是自定义实现的Entry<K,V>[]数组结构,并非继承自原生Map类,Entry其中Key即是ThreadLocal变量本身,Value则是具体该线程中的变量副本值。结构如图: 因此ThreadLocal其实只是个符号意义,本身不存储变量,仅仅是用来索引各个线程中的变量副本。 值得注意的是,Entry的Key即ThreadLocal对象是采用弱引用引入的,如源代码: static class ThreadLocalMap { /** * The entries in this hash map extend WeakReference, using * its main ref field as the key (which is always a * ThreadLocal object). Note that null keys (i.e. entry.get() * == null) mean that the key is no longer referenced, so the * entry can be expunged from table. Such entries are referred to * as "stale entries" in the code that follows. */ static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }本文下面重点分析为何使用弱引用,以及可能存在的问题。 ...

November 5, 2019 · 2 min · jiezi

Spring-Boot-2X五MyBatis-多数据源配置

前言MyBatis 多数据源配置,最近在项目建设中,需要在原有系统上扩展一个新的业务模块,特意将数据库分库,以便减少复杂度。本文直接以简单的代码示例,如何对 MyBatis 多数据源配置。 准备创建数据库db_test SET NAMES utf8mb4;SET FOREIGN_KEY_CHECKS = 0;-- ------------------------------ Table structure for t_user-- ----------------------------DROP TABLE IF EXISTS `t_user`;CREATE TABLE `t_user` ( `id` int(8) NOT NULL AUTO_INCREMENT COMMENT 'ID', `user_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户姓名', `user_sex` char(1) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '用户性别', PRIMARY KEY (`id`)) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;-- ------------------------------ Records of t_user-- ----------------------------BEGIN;INSERT INTO `t_user` VALUES (1, '刘备', '男');INSERT INTO `t_user` VALUES (2, '孙尚香', '女');INSERT INTO `t_user` VALUES (3, '周瑜', '男');INSERT INTO `t_user` VALUES (4, '小乔', '女');INSERT INTO `t_user` VALUES (5, '诸葛亮', '男');INSERT INTO `t_user` VALUES (6, '黄月英', '女');INSERT INTO `t_user` VALUES (7, '关羽', '男');INSERT INTO `t_user` VALUES (8, '张飞', '男');INSERT INTO `t_user` VALUES (9, '赵云', '男');INSERT INTO `t_user` VALUES (10, '黄总', '男');INSERT INTO `t_user` VALUES (11, '曹操', '男');INSERT INTO `t_user` VALUES (12, '司马懿', '男');INSERT INTO `t_user` VALUES (13, '貂蝉', '女');INSERT INTO `t_user` VALUES (14, '吕布', '男');INSERT INTO `t_user` VALUES (15, '马超', '男');INSERT INTO `t_user` VALUES (16, '魏延', '男');INSERT INTO `t_user` VALUES (17, '孟获', '男');INSERT INTO `t_user` VALUES (18, '大乔', '女');INSERT INTO `t_user` VALUES (19, '刘婵', '男');INSERT INTO `t_user` VALUES (20, '姜维', '男');INSERT INTO `t_user` VALUES (21, '廖化', '男');INSERT INTO `t_user` VALUES (22, '关平', '男');COMMIT;SET FOREIGN_KEY_CHECKS = 1;dbb_test2 ...

November 5, 2019 · 4 min · jiezi

Spring-Boot-2X六Spring-Boot-集成-Redis

Redis 简介什么是 RedisRedis 是目前使用的非常广泛的免费开源内存数据库,是一个高性能的 key-value 数据库。 Redis 与其他 key-value 缓存(如 Memcached )相比有以下三个特点: 1.Redis 支持数据的持久化,它可以将内存中的数据保存在磁盘中,重启的时候可以再次加载进行使用。2.Redis 不仅仅支持简单的 key-value 类型的数据,同时还提供 list,set,zset,hash 等数据结构的存储。3.Redis 支持数据的备份,即 master-slave 模式的数据备份。Redis 优势如下: 1.性能极高。Redis 能读的速度是 110000 次/s,写的速度是 81000 次/s。2.丰富的数据类型。Redis 支持二进制案例的 Strings,Lists,Sets 及 Ordered Sets 数据类型操作。3.原子性。Redis 所有的操作都是原子性的,意思是要么成功执行要么失败完全不执行。单个操作是原子性的,多个操作也是,通过 MULTI 和 EXEC 指令抱起来。4.丰富的特性。Redis 还支持 publish/subscribe,通知,key 过期等特性。Spring Boot 集成 Redis1.在项目中添加依赖<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.9.RELEASE</version> <relativePath /> <!-- lookup parent from repository --> </parent> <groupId>cn.zwqh</groupId> <artifactId>spring-boot-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <name>spring-boot-redis</name> <description>spring-boot-redis</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- Redis --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>查看 jar 包时发现,Spring Data Redis 下 org.springframework.data.redis.connection 包路径下面默认有两个包 jedis 和 lettuce,这说明 Spring Boot 已经默认包装适配了这两个 Redis 客户端。 ...

November 5, 2019 · 3 min · jiezi

SpringBoot是如何启动的

本文是通过查看SpringBoot源码整理出来的SpringBoot大致启动流程,整体大方向是以简单为出发点,不说太多复杂的东西,内部实现细节本文不深扣因为每个人的思路、理解都不一样,我个人看的理解跟大家看的肯定不一样,到时候表达的出来的云里雾里也没啥用。 首先我将SpringBoot的启动流程整理成以下阶段: SpringApplicaiton初始化 审查ApplicationContext类型加载ApplicationContextInitializer加载ApplicationListenerEnvironment初始化 解析命令行参数创建Environment配置Environment配置SpringApplicationApplicationContext初始化 创建ApplicationContext设置ApplicationContext刷新ApplicationContext运行程序入口省去了一些不影响主流程的细节,在查看SpringBoot源码之前,不得不提一下spring.factories这个文件的使用和功能。 关于spring.factoriesspring.factories是一个properties文件,它位于classpath:/META-INF/目录里面,每个jar包都可以有spring.factories的文件。Spring提供工具类SpringFactoriesLoader负责加载、解析文件,如spring-boot-2.2.0.RELEASE.jar里面的META-INF目录里面就有spring.factories文件: # PropertySource Loadersorg.springframework.boot.env.PropertySourceLoader=\org.springframework.boot.env.PropertiesPropertySourceLoader,\org.springframework.boot.env.YamlPropertySourceLoader# Run Listenersorg.springframework.boot.SpringApplicationRunListener=\org.springframework.boot.context.event.EventPublishingRunListener...关于spring.factories需要知道些什么? spring.factories是一个properties文件spring.factories里的键值对的value是以逗号分隔的完整类名列表spring.factories里的键值对的key是完整接口名称spring.factories键值对的value是key的实现类spring.factories是由SpringFactoriesLoader工具类加载spring.factories位于classpath:/META-INF/目录SpringFactoriesLoader会加载jar包里面的spring.factories文件并进行合并知道spring.factories的概念后,继续来分析SpringBoot的启动。 SpringApplicaiton初始化Java程序的入口在main方法SpringBoot的同样可以通过main方法启动,只需要少量的代码加上@SpringBootApplication注解,很容易的就启动SpringBoot: @SpringBootApplication@Slf4jpublic class SpringEnvApplication { public static void main(String[] args) { ConfigurableApplicationContext context = SpringApplication.run(SpringEnvApplication.class, args); }}SpringApplicaiton初始化位于SpringApplication的构造函数中: public SpringApplication(Class<?>... primarySources) { this(null, primarySources); } 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(); }简单的说下SpringApplication的构造函数干了些啥: ...

November 5, 2019 · 3 min · jiezi

Spring-Boot-2X四Spring-Boot-自定义-Web-MVC-配置

0.准备Spring Boot 不仅提供了相当简单使用的自动配置功能,而且开放了非常自由灵活的配置类。Spring MVC 为我们提供了 WebMvcConfigurationSupport 类和一个注解 @EnableWebMvc 以帮助我们减少配置 Bean 的声明。本文简单说明如何自定义 Web MVC 配置。首先需要使用 @Configuration 将 WebMvcConfig 类标注为 Spring 配置类,示例代码如下: @Configurationpublic class WebMvcConfig extends WebMvcConfigurationSupport { //通过重写配置方法覆盖}并在启动类上添加 @EnableWebMvc,代码如下: @SpringBootApplication@MapperScan("cn.zwqh.springboot.dao")@EnableWebMvc //启用 Spring MVC 配置public class SpringBootSsmThymeleafApplication { public static void main(String[] args) { SpringApplication.run(SpringBootSsmThymeleafApplication.class, args); }}1.静态资源配置Spring Boot 中默认的静态资源配置,是把类路径下的/static、/public、/resources 和 /METAINF/resources 目录或者 ServletContext 的根目录中的静态文件直接映射为 /**。它使用来自 Spring MVC 的ResourceHttpRequestHandler,以便您可以通过添加自己的WebMvcConfigurer并覆盖addResourceHandlers方法来修改该行为。示例代码如下: @Override public void addResourceHandlers(ResourceHandlerRegistry registry) { registry.addResourceHandler("/statics/**").addResourceLocations("classpath:/statics/");//静态资源路径 css,js,img等 registry.addResourceHandler("/templates/**").addResourceLocations("classpath:/templates/");//视图 registry.addResourceHandler("/mapper/**").addResourceLocations("classpath:/mapper/");//mapper.xml super.addResourceHandlers(registry); } 2.拦截器配置通过重写 addInterceptors() 方法,使用 InterceptorRegistry 注册类来添加拦截器 HandlerInterceptor。示例代码如下: ...

November 5, 2019 · 2 min · jiezi

Spring-Boot-2X三使用-Spring-MVC-MyBatis-Thymeleaf-开发-web-应用

前言Spring MVC 是构建在 Servlet API 上的原生框架,并从一开始就包含在 Spring 框架中。本文主要通过简述 Spring MVC 的架构及分析,并用 Spring Boot + Spring MVC + MyBatis (SSM)+ Thymeleaf(模板引擎) 框架来简单快速构建一个 Web 项目。 Web MVC 架构及分析MVC 三层架构如图所示,红色字体代表核心模块。其中 MVC 各分层分别为: Model (模型层)处理核心业务(数据)逻辑,模型对象负责在数据库中存取数据。这里的“数据”不仅限于数据本身,还包括处理数据的逻辑。View(视图层)用于展示数据,通常数据依据模型数据创建。Controller(控制器层)用于处理用户输入请求和响应输出,从试图读取数据,控制用户输入,并向模型发送数据。Controller 是在 Model 和 View 之间双向传递数据的中间协调者。 Spring MVC 架构及分析Spring MVC 处理一个 HTTP 请求的流程,如图所示:整个过程详细介绍:1.用户发送请求至前端控制器 DispatcherServlet。2.DispatcherServlet 收到请求调用处理器映射器 HandlerMapping。3.处理器映射器根据请求 URL 找到具体的 Controller 处理器返回给 DispatcherServlet。4.DispatcherServlet 通过处理器适配器 HandlerAdapter 调用 Controller 处理请求。5.执行 Controller 处理器的方法。6.Controller 执行完成返回 ModelAndView。7.HandlerAdapter 将 Controller 执行结果 ModelAndView 返回给 DispatcherServlet。8.DispatcherServlet 将 ModelAndView 的 ViewName 传给视图解析器 ViewReslover。9.ViewReslover 解析后返回具体的视图 View。10.DispatcherServlet 传递 Model 数据给 View,对 View 进行渲染(即将模型数据填充至视图中)。11-12.DispatcherServlet 响应用户。 ...

November 5, 2019 · 3 min · jiezi

Spring-讲解四

Spring 中使用注解注入注解:就是一个类,使用 @ 注解名称。实际开发中:使用注解取代 xml 配置文件。 1、常用注解释义 @component 取代 <bean class="">@Component("id") 取代 <bean id="" class="">web开发,提供3个 @Component 注解衍生注解取代<bean class=""> @Repository(“名称”):dao层@Service(“名称”):service层 @Controller(“名称”):web层 web 开发中其他常用注解 @Autowired:自动根据类型注入@Qualifier(“名称”):指定自动注入的id名称 @Resource(“名称”) @ PostConstruct 自定义初始化 @ PreDestroy 自定义销毁 2、案例代码演示 接口和实现类public interface UserService { void add(User user);}=========================================================================================@Componentpublic class UserServiceImpl implements UserService { @Override public void add(User user) { System.out.println("添加用户信息..."+user); }}配置 bean.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 http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 开启注解--> <context:annotation-config/> <!-- 注解的位置--> <context:component-scan base-package="com.example.demo"/></beans>测试函数public class ServiceTest { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); UserService userService = context.getBean(UserServiceImpl.class); User user = new User(); user.setUsername("元始天尊"); user.setAge(999); user.setPassword("555"); userService.add(user); }}控制台日志信息如下:添加用户信息...User{username='元始天尊', password='555', age=999} ...

November 5, 2019 · 1 min · jiezi

简明的图解Redis-RDB持久化AOF持久化

关注我,可以获取最新知识、经典面试题以及微服务技术分享 1.持久化1.1 持久化简介持久化(Persistence),持久化是将程序数据在持久状态和瞬时状态间转换的机制,即把数据(如内存中的对象)保存到可永久保存的存储设备中(如磁盘)。 1.2 redis持久化redis为内存数据库,为了防止服务器宕机以及服务器进程退出后,服务器数据丢失,Redis提供了持久化功能,即将Redis中内存数据持久化到磁盘中。Redis 提供了不同级别的持久化方式: RDB持久化方式:可以在指定的时间间隔能对数据进行快照存储.AOF持久化方式:记录每次对服务器写的操作,当服务器重启的时候会重新执行这些命令来恢复原始的数据,AOF命令以redis协议追加保存每次写的操作到文件末尾.Redis还能对AOF文件进行后台重写,使得AOF文件的体积不至于过大.如果服务器开启了AOF持久化功能。服务器会优先使用AOF文件还原数据。只有关闭了AOF持久化功能,服务器才会使用RDB文件还原数据 2. RDB持久化2.1 RDB文件格式RDB文件是一个经过压缩的二进制文件(默认的文件名:dump.rdb),由多个部分组成,RDB格式: 2.2 RDB文件持久化创建与载入在 Redis持久化时, RDB 程序将当前内存中的数据库状态保存到磁盘文件中, 在 Redis 重启动时, RDB 程序可以通过载入 RDB 文件来还原数据库的状态。 2.3 工作方式当 Redis 需要保存 dump.rdb 文件时, 服务器执行以下操作: Redis 调用forks。同时拥有父进程和子进程。子进程将数据集写入到一个临时 RDB 文件中。当子进程完成对新 RDB 文件的写入时,Redis 用新 RDB 文件替换原来的 RDB 文件,并删除旧的 RDB 文件。这种工作方式使得 Redis 可以从写时复制(copy-on-write)机制中获益。 2.4 创建方式SAVE 同步操作,在执行该命令时,服务器会被阻塞,拒绝客户端发送的命令请求 redis> saveBGSAVE 异步操作,在执行该命令时,子进程执行保存工作,服务器还可以继续让主线程处理客户端发送的命令请求 redis>bgsave自动创建 由于BGSAVE命令可不阻塞服务器进程下执行,可以让用户自定义save属性,让服务器每个一段时间自动执行一次BGSAVE命令(即通过配置文件对 Redis 进行设置, 让它在“ N 秒内数据集至少有 M 个改动”这一条件被满足时, 自动进行数据集保存操作)。 ...

November 5, 2019 · 2 min · jiezi

Spring-Boot-2X一入门篇

什么是 Spring BootSpring Boot 是由 Pivotal 团队提供的全新框架,其设计目的是用来简化新 Spring 应用的初始搭建以及开发过程。该框架遵循”约定优于配置“的思想,清除了原先使用Spring框架的那些样板化的配置,继承了原有Spring框架的优秀基因,从而帮助开发者快速开发应用。 SpringBoot的特性总的来说就是简单、快速、方便。 SpringBoot的核心模块 创建SpringBoot项目本文使用开发工具为eclipse 官网Maven构建项目1、访问 https://start.spring.io/2、选择构建工具中Maven Project、Java、Spring Boot版本2.1.8以及一些项目的基本信息,可参考下图所示: 3、点击 Generate Project 下载项目压缩包4、Import —> Existing Maven Projects —> Next —> 选择解压后的文件夹 —> FinshEclipse构建项目1、首先安装SpringBoot插件,Help —> Eclipse Marketplace —> 搜索'Spring' —> 安装Spring Tools 4 - for Spring Boot··· —> Install,直至完成restart2、File —> New —> Project,弹出新建项目的框3、搜索‘Spring’,找到选择Spring Boot子目录下的Spring Starter Project,点击Next4、填写相关项目信息后,点击Next,选择需要的依赖包,再点击Next,确认无误后Finish,完成创建。HelloWorld我们根据上面构建了一个helloworld项目,基于它我们来实现简单的web示例以及测试示例 1.项目目录结构介绍如上图所示,Spring Boot 的基础结构共三个大块: • src/main/java Java源代码目录,主程序入口 HelloworldApplication,可以通过直接运行该类来启动 Spring Boot 应用• src/main/resources 资源文件目录,该目录用来存放应用的一些配置以及静态资源。application.properties为配置文件,可以配置应用名、服务器端口、数据库链接等等。由于引入了Web模块,因此产生了 static 目录与 templates 目录,static 用于存放静态资源,如图片、CSS、JavaScript等,templates 用于存放 Web 页面的模板文件。• src/test/java 单元测试目录,生成的 HelloworldApplicationTests 通过 JUint 4 实现,可以直接用运行 Spring Boot 应用的测试。2.Maven 配置分析<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.8.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.example</groupId> <artifactId>helloworld</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>helloworld</name> <description>Demo project for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project>Spring Boot 版本:2.1.8.RELEASE打包形式: jar (Spring Boot 默认的打包形式)pom.xml 文件中项目依赖 dependencies 默认有两个模块:• spring-boot-starter-web 全栈Web开发模块,包含嵌入式Tomcat、Spring MVC。• spring-boot-starter-test 通用测试模块,包含JUnit、Hamcrest、Mockito。项目构建的 build 部分:引入了 Spring Boot 的 Maven 插件。 ...

November 5, 2019 · 2 min · jiezi

四docker-仓库让我们的镜像有处可存

前言前面讲完了docker 镜像和容器,以及通过Dockerfile 定制属于我们自己的镜像,那那现在就是需要将我们自己定制的镜像存放到仓库中供他们使用。这一套流程才算是正式走完了。从获取镜像,操作镜像容器,定制镜像,上传镜像。会了这些,也算是docker 正式入门了。 上传到共有仓库docker 官网有一个共有的仓库,大家应该都知道,和github 类似。dockehub可以管理你自己的镜像。我们需要创建一个账号用来管理。 官网:https://hub.docker.com/ 我们创建好账号后,就可以在我们本机的电脑上登录到官网了。 docker login 用户名 网址网址可以不填,默认的就是去登录官网,登录官网之后就可以上传我们自己的镜像了 docker push [OPTIONS] NAME[:TAG] eg: docker push quellanan/hello:1.0.0 我这截图是上传过一次,再上传的时候提示已经存在,说明是上传成功的。我们可以查看一下: docker search quellanan 私有仓库docker 官方提供了一个私用仓库的镜像,我们可以直接使用。docker-registry. 下载我们先下载registry 镜像 docker pull registry 容器运行docker run -d -p 5000:5000 --restart=always --name registry registry 到现在我们私有仓库已经有了,现在我们如何将自己本地镜像上传私有仓库呢? 上传首先我们需要使用docker tag 将镜像重命名,前缀需要和私用仓库一致,才能上传成功。 docker tag java:8 127.0.0.1:5000/java:8docker push 127.0.0.1:5000/java:8 通过下面命令查看是否成功 docker push 127.0.0.1:5000/java:8 上面证明我们已经将镜像上传到我们的私有仓库了。 下载那现在我们先将本地的镜像删除掉,然后从私服上下载镜像,看是否能够下载下来。 docker image rm 127.0.0.1:5000/java:8 docker pull 127.0.0.1:5000/java:8 ...

November 5, 2019 · 1 min · jiezi

Spring-Cloud-gateway-网关服务-一

之前我们介绍了 zuul网关服务,今天聊聊spring cloud gateway 作为spring cloud的亲儿子网关服务。很多的想法都是参照zuul,为了考虑zuul 迁移到gateway 提供了一个便利的条件。 gateway 他的核心功能也是和zuul 类似。但是他的实现方式与zuul 却有些不一样,他的核心是基于 Spring Boot 2.x, Spring WebFlux和Project Reactor 构建的。 Spring WebFlux 响应式Web框架。 Spring WebFlux是基于响应式流的,因此可以用来建立异步的、非阻塞的、事件驱动的服务。它采用Reactor作为首选的响应式流的实现库,不过也提供了对RxJava的支持。由于响应式编程的特性,Spring WebFlux和Reactor底层需要支持异步的运行环境,比如Netty和Undertow;也可以运行在支持异步I/O的- Servlet 3.1的容器之上,比如Tomcat(8.0.23及以上)和Jetty(9.0.4及以上)。spring-webflux上层支持两种开发模式: - 类似于Spring WebMVC的基于注解(@Controller、@RequestMapping)的开发模式;Java 8 lambda 风格的函数式开发模式。Spring WebFlux也支持响应式的Websocket服务端开发。所以spring cloud gateway 不是基于阻塞的web 开发。他与传统的Servlet是存在冲突的。在创建功能的时候要排除掉传统的Servlet jar包引用工作原理客户端向Spring Cloud Gateway发出请求。如果网关处理程序映射确定请求与路由匹配,则将其发送到网关Web处理程序。该处理程序运行通过特定于请求的筛选器链发送请求。筛选器由虚线分隔的原因是,筛选器可以在发送代理请求之前或之后执行逻辑。执行所有“前置”过滤器逻辑,然后发出代理请求。发出代理请求后,将执行“发布”过滤器逻辑。 注意: 在没有端口的路由中定义的URI将分别将HTTP和HTTPS URI的默认端口分别设置为80和443 Predicate 断言:这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。Route 路由转发 它由一个 serverID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。Filter 请求过滤 对web资源进行拦截,做一些处理后再交给处理器处理修改之前工程的pom 文件总pom 里面我们有一个 spring-boot-starter-web 工程引用,删除掉。在服务里面单独依赖。上面已经讲述过,传统Servlet的jar包冲突问题。 在服务消费者和 服务提供者分别添加 ...

November 4, 2019 · 2 min · jiezi

Spring-讲解二

1、Spring 容器加载的3种方式 public class ServiceTest { public static void main(String[] args) { //Spring容器加载有3种方式 //第一种:ClassPathXmlApplicationContext ClassPath类路径加载,指的就是classes路径 //第一种:最常用,spring的配置文件路径以后就直接放在src ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml"); //第二种方式:文件系统路径获得配置文件【绝对路径】 //ApplicationContext context = new FileSystemXmlApplicationContext("C:\\Users\\Desktop\\IDEAWorkspace\\spring-01\\src\\com\\rookie\\beans.xml"); //第三种方式:使用BeanFactory(了解) //String path = "C:\\Users\\Desktop\\IDEAWorkspace\\spring-01\\src\\com\\rookie\\beans.xml"; //BeanFactory factory = new XmlBeanFactory(new FileSystemResource(path)); //IUserService user = (IUserService) factory.getBean("userService"); //user.add(); IUserService user = (IUserService) context.getBean("userService"); user.add(); } }Spring内部创建对象的原理 a.解析xml文件,获取类名,id,属性等。 b.通过反射,用类型创建对象。 c.给创建的对象赋值。 2、BeanFactory 和 ApplicationContext对比 BeanFactory 采取延迟加载,第一次 getBean 时才会初始化 Bean。ApplicationContext 是即时加载,对 BeanFactory 扩展,提供了更多功能。 ...

November 4, 2019 · 2 min · jiezi

Spring-AOP-注解方式使用介绍长文详解

前言之前的源码解析章节,本人讲解了Spring IOC 的核心部分的源码。如果你熟悉Spring AOP的使用的话,在了解Spring IOC的核心源码之后,学习Spring AOP 的源码,应该可以说是水到渠成,不会有什么困难。 但是直接开始讲Spring AOP的源码,本人又觉得有点突兀,所以便有了这一章。Spring AOP 的入门使用介绍:包括Spring AOP的一些概念性介绍和配置使用方法。 这里先贴一下思维导图。 AOP 是什么AOP : 面向切面编程(Aspect Oriented Programming)Aspect是一种新的模块化机制,用来描述分散在对象、类或函数中的横切关注点(crosscutting concern)。从关注点中分离出横切关注点是面向切面的程序设计的核心概念。分离关注点使解决特定领域问题的代码从业务逻辑中独立出来,业务逻辑的代码中不再含有针对特定领域问题代码的调用,业务逻辑同特定领域问题的关系通过切面来封装、维护,这样原本分散在整个应用程序中的变动就可以很好地管理起来。 最近在看李智慧的《大型网站技术架构》一书中,作者提到,开发低耦合系统是软件设计的终极目标之一。AOP这种面向切面编程的的方式就体现了这样的理念。将一些重复的、和业务主逻辑不相关的功能性代码(日志记录、安全管理等)通过切面模块化地抽离出来进行封装,实现关注点分离、模块解耦,使得整个系统更易于维护管理。 这样分而治之的设计,让我感觉到了一种美感。 AOP 要实现的是在我们原来写的代码的基础上,进行一定的包装,如在方法执行前、方法返回后、方法抛出异常后等地方进行一定的拦截处理或者叫增强处理。 AOP 的实现并不是因为 Java 提供了什么神奇的钩子,可以把方法的几个生命周期告诉我们,而是我们要实现一个代理,实际运行的实例其实是生成的代理类的实例。 名词概念前面提到过,Spring AOP 延用了 AspectJ 中的概念,使用了 AspectJ 提供的 jar 包中的注解。也就是Spring AOP里面的概念和术语,并不是Spring独有的,而是和AOP相关的。 概念可以草草看过,在看了之后的章节之后再回来看会对概念理解的更深。 术语概念Aspect切面是Pointcut和Advice的集合,一般单独作为一个类。Pointcut和Advice共同定义了关于切面的全部内容,它是什么时候,在何时和何处完成功能。Joinpoint这表示你的应用程序中可以插入AOP方面的一点。也可以说,这是应用程序中使用Spring AOP框架采取操作的实际位置。Advice这是在方法执行之前或之后采取的实际操作。 这是在Spring AOP框架的程序执行期间调用的实际代码片段。Pointcut这是一组一个或多个切入点,在切点应该执行Advice。 您可以使用表达式或模式指定切入点,后面示例会提到。Introduction引用允许我们向现有的类添加新的方法或者属性Weaving创建一个被增强对象的过程。这可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。PS:在整理概念的时候有个疑问,为什么网上这么多中文文章把advice 翻译成“通知”呢???概念上说得通吗???我更愿意翻译成“增强”(并发中文网ifeve.com 也是翻译成增强)还有一些注解,表示Advice的类型,或者说增强的时机,看过之后的示例之后会更加的清楚。 术语概念Before在方法被调用之前执行增强After在方法被调用之后执行增强After-returning在方法成功执行之后执行增强After-throwing在方法抛出指定异常后执行增强Around在方法调用的前后执行自定义的增强行为(最灵活的方式)使用方式Spring 2.0 之后,Spring AOP有了两种配置方式。 schema-based:Spring 2.0 以后使用 XML 的方式来配置,使用 命名空间 <aop />@AspectJ 配置:Spring 2.0 以后提供的注解方式。这里虽然叫做 @AspectJ,但是这个和 AspectJ 其实没啥关系。PS:个人比较钟情于@AspectJ 这种方式,使用下来是最方面的。也可能是因为我觉得XML方式配置的Spring Bean很不简洁、写起来不好看吧,所以有点排斥吧。23333~本文主要针对注解方式讲解,并且给出对应的DEMO;之后的源码解析也会以注解的这种方式为范例讲解Spring AOP的源码(整个源码解析看完,会对其他方式触类旁通,因为原理都是一样的) ...

November 4, 2019 · 4 min · jiezi

Spring-Boot从零入门5五脏俱全的RESTful-Web-Service构建

本文属于原创,转载注明出处,欢迎关注微信小程序小白AI博客 微信公众号小白AI或者网站 https://xiaobaiai.net [TOC] 1 前言这一节我们正式进入Spring Boot的WEB服务开发,在WEB服务中,不可缺少的我们需要去提供API出来,那么就少不了设计API,而当前流行的一套API设计风格就是REST API ,接下来我们会介绍什么是RESTful API以及它的特点和如何去设计。完成设计后,我们会使用Spring Boot + MVC架构去实现一个RESTful Web Service。本文的所有内容都是经过多方面考察和参考官方资料,本着严谨的态度为自己也为一起学习的同学们负责,由浅入深,层层展开,让自己有不一样的收获。一起加油吧! 2 名词术语名词术语 释义 RESTful RESTFUL是一种网络应用程序的设计风格和开发方式,是目前流行的 API 设计规范,用于 Web 数据接口的设计。通过使用事先定义好的接口与不同的服务联系起来,浏览器使用POST,DELETE,PUT和GET四种主要请求方式分别对指定的URL资源进行增删改查操作。因此,RESTful是通过URI实现对资源的管理及访问,具有扩展性强、结构清晰的特点。 3 一分钟了解RESTful APIRESTful 是目前流行的 API 设计规范,用于 Web 数据接口的设计。 RESTful 对 URL 或者 API 的设计总的原则就是将所有操作对象都看作一个资源,操作这个(些)资源(名词)的方法通过 HTTP的方法类型(动词)去实现: # GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACEGET:读取(Read)资源信息POST:新建(Create)资源PUT:更新(Update)资源PATCH:更新(Update)资源,通常是部分更新DELETE:删除(Delete)资源通过对于上述概念的理解,我们举一些常用示例来判断设计是否符合RESTful规范。 POST /api/v1/users/login # 否,具体分析见后面POST /api/v1/users # 是,创建一个新用户GET /api/v1/users/:username # 是,获取所有用户信息或者指定用户名的信息DELETE /api/v1/users/:username # 是,删除所有用户或者删除指定用户GET /api/v1/getUserInfo # 否,本身就是利用HTTP的方法做动词,无需另外添加更多的 RESTful API 示例可以参考主流网站的开发API,如码云(https://gitee.com/api/v5/swagger) ...

November 4, 2019 · 3 min · jiezi

spring学习之源码分析FactoryBeanRegistrySupport

FactoryBeanRegistrySupportFactoryBeanRegistrySupport抽象类继承了DefaultSingletonBeanRegistry类,增加了对FactoryBean的处理。 类结构 常量// 缓存factoryBean的对应关系private final Map<String, Object> factoryBeanObjectCache = new ConcurrentHashMap<>(16);方法解析getTypeForFactoryBean调用factoryBean的getObjectType方法返回class类型 protected Class<?> getTypeForFactoryBean(final FactoryBean<?> factoryBean) { try { if (System.getSecurityManager() != null) { return AccessController.doPrivileged((PrivilegedAction<Class<?>>) factoryBean::getObjectType, getAccessControlContext()); } else { return factoryBean.getObjectType();//调用factoryBean的getObjectType方法返回class类型。 } } catch (Throwable ex) { // Thrown from the FactoryBean's getObjectType implementation. logger.info("FactoryBean threw exception from getObjectType, despite the contract saying " + "that it should return null if the type of its object cannot be determined yet", ex); return null; }}getCachedObjectForFactoryBean从缓存中通过制定的beanName获取FactoryBean ...

November 4, 2019 · 3 min · jiezi

spring学习之源码分析DefaultSingletonBeanRegistry

DefaultSingletonBeanRegistryDefaultSingletonBeanRegistry类继承了SimpleAliasRegistry以及实现了SingletonBeanRegistry的接口。处理Bean的注册,销毁,以及依赖关系的注册和销毁。 类结构截取部分 常量// 单例对象的缓存:从beanname到bean实例private final Map<String, Object> singletonObjects = new ConcurrentHashMap<>(256);// 单例工厂的缓存:从beanname到ObjectFactoryprivate final Map<String, ObjectFactory<?>> singletonFactories = new HashMap<>(16);// 早期单例对象的缓存:从beanname到bean实例private final Map<String, Object> earlySingletonObjects = new HashMap<>(16);// 一组已注册的单例,包含按注册顺序排列的beannameprivate final Set<String> registeredSingletons = new LinkedHashSet<>(256);// 正在创建的单例的beanName的集合private final Set<String> singletonsCurrentlyInCreation = Collections.newSetFromMap(new ConcurrentHashMap<>(16));// 当前不检查的bean的集合private final Set<String> inCreationCheckExclusions = Collections.newSetFromMap(new ConcurrentHashMap<>(16));// 异常集合private Set<Exception> suppressedExceptions;// 当前是否在销毁bean中private boolean singletonsCurrentlyInDestruction = false;// 一次性bean实例private final Map<String, Object> disposableBeans = new LinkedHashMap<>();// 内部bean和外部bean之间关系private final Map<String, Set<String>> containedBeanMap = new ConcurrentHashMap<>(16);// 指定bean与依赖指定bean的集合,比如bcd依赖a,那么就是key为a,bcd为valueprivate final Map<String, Set<String>> dependentBeanMap = new ConcurrentHashMap<>(64);// 指定bean与指定bean依赖的集合,比如a依赖bcd,那么就是key为a,bcd为valueprivate final Map<String, Set<String>> dependenciesForBeanMap = new ConcurrentHashMap<>(64);方法解析registerSingleton通过bean的名称和对象进行注册。 ...

November 4, 2019 · 6 min · jiezi

Spring-Cloud-alibaba网关-sentinel-zuul-四-限流熔断

spring cloud alibaba 集成了 他内部开源的 Sentinel 熔断限流框架 Sentinel 介绍官方网址随着微服务的流行,服务和服务之间的稳定性变得越来越重要。Sentinel 以流量为切入点,从流量控制、熔断降级、系统负载保护等多个维度保护服务的稳定性。 Sentinel 具有以下特征:丰富的应用场景:Sentinel 承接了阿里巴巴近 10 年的双十一大促流量的核心场景,例如秒杀(即突发流量控制在系统容量可以承受的范围)、消息削峰填谷、集群流量控制、实时熔断下游不可用应用等。完备的实时监控:Sentinel 同时提供实时的监控功能。您可以在控制台中看到接入应用的单台机器秒级数据,甚至 500 台以下规模的集群的汇总运行情况。 广泛的开源生态:Sentinel 提供开箱即用的与其它开源框架/库的整合模块,例如与 Spring Cloud、Dubbo、gRPC 的整合。您只需要引入相应的依赖并进行简单的配置即可快速地接入 Sentinel。完善的 SPI 扩展点:Sentinel 提供简单易用、完善的 SPI 扩展接口。您可以通过实现扩展接口来快速地定制逻辑。例如定制规则管理、适配动态数据源等。Sentinel 的主要特性: Sentinel 的开源生态: 介绍一下 如果搭建。本次并不会一点点延伸更深层次的地方。只教大家快速使用。等spring cloud gateway更新完成之后会带大家更深层次的挖掘 Sentinel 的功能。 Sentinel的快速搭建直接下载官网已经打好的jar包 release地址 https://github.com/alibaba/Se... 源码编译git clone https://github.com/alibaba/Se... 然后进入目录执行 mvn clean package 命令启动 java -Dserver.port=8080 -Dcsp.sentinel.dashboard.server=localhost:8080 -Dproject.name=sentinel-dashboard -jar sentinel-dashboard.jar如果需要docker 的话 可编写 docker Dockerfile # 基于哪个镜像FROM java:8# 拷贝文件到容器,也可以直接写成ADD microservice-discovery-eureka-0.0.1-SNAPSHOT.jar /app.jarADD ./*.jar app.jarRUN mkdir -p /var/logs/SentinelRUN mkdir -p /var/logs/jvmRUN mkdir -p /var/logs/dumpRUN bash -c 'touch /app.jar'# 开放8080端口EXPOSE 8080# 配置容器启动后执行的命令ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-XX:-PrintGCDetails","-XX:-PrintGCTimeStamps","-XX:-HeapDumpOnOutOfMemoryError","-XX:HeapDumpPath=/var/logs/dump/oom_dump.dump","-Xloggc:/var/logs/jvm/app.log","-Dfile.encoding=UTF8","-Duser.timezone=GMT+08","-XX:CMSInitiatingOccupancyFraction=90","-XX:MaxGCPauseMillis=200","-XX:StringTableSize=20000","-XX:+UseG1GC","-Xss256k","-Xmx1024m","-Xms512m","-jar","/app.jar"]执行 docker 镜像制作 ...

November 4, 2019 · 2 min · jiezi

spring学习之源码分析ConfigurableListableBeanFactory

ConfigurableListableBeanFactoryConfigurableListableBeanFactory继承了ListableBeanFactory, AutowireCapableBeanFactory, ConfigurableBeanFactory。在ConfigurableBeanFactory的基础上,它还提供了分析和修改bean定义以及预实例化单例的工具 类结构 方法解析忽略自动装配 // 在装配的时候忽略指定的依赖类型void ignoreDependencyType(Class<?> type);// 在装配的时候忽略指定的接口void ignoreDependencyInterface(Class<?> ifc);依赖 // 注册可解析的依赖void registerResolvableDependency(Class<?> dependencyType, @Nullable Object autowiredValue);// 指定的bean是否可以作为自动选派的候选,boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor) throws NoSuchBeanDefinitionException;BeanDefinition // 根据bean名称获取BeanDefinitionBeanDefinition getBeanDefinition(String beanName) throws NoSuchBeanDefinitionException;/ /获取bean名称的IteratorIterator<String> getBeanNamesIterator()bean的元数据缓存 void clearMetadataCache();冻结bean的配置 // 冻结所有bean定义,表明注册的bean定义将不再修改或后期处理。void freezeConfiguration();// bean的定义是否被冻结boolean isConfigurationFrozen();lazy-init相关 // 非延迟加载的bean都实例化void preInstantiateSingletons() throws BeansException;

November 4, 2019 · 1 min · jiezi

一分钟面试题令人挠头的三目运算符

面试题 提问:当 a=1,b=2 时,test1 方法的执行结果是什么?思考一分钟。 答案: 解析在使用三目运算符时,尽量保证两个返回值的类型一致,不然会触发类型转换,转换规则如下: 如果返回值 X 和返回值 Y 是同种类型,那么返回类型毫无疑问就是这种类型。如果两个返回值 X 和 Y 的类型不同,那么返回值类型为他们两最接近的父类。举例: // String 和 Boolean 都实现了 Serializable 接口Serializable serializable = a == b ? "true" : Boolean.FALSE;// 所有类都继承了 Object 类Object o = a == b ? new ArrayList<>() : new TernaryOperatorDemo();对于基本数据类型,如果其中一个返回值 X 类型为byte、short或者char,另一个返回值 Y 类型为int,那么若在编译期就能判断出 Y 的取值范围在 X 的取值范围之内,则返回类型为 X 的类型,反之则为 Y 的类型。如果返回值 X 类型不为以上几种,则会触发隐藏类型转换。当基本数据类型和对象数据类型相遇时,三目运算符默认返回结果为基本数据类型。了解以上规则之后,我们再看来一下 test1 方法。 private static void test1(int a, int b) { // 触发隐藏类型转换,int 类型 9 转为 9.0D System.out.println(a == b ? 9.9 : 9); // 编译期判断,98 在 char 之内,转为 b System.out.println(a == b ? 'a' : 98); // 编译期判断,超出char范围,统一转 int System.out.println(a == b ? 'a' : Integer.MAX_VALUE); // 编译期时无法判断 b 的取值,触发隐藏类型转换,统一转 int System.out.println(a == b ? 'a' : b); System.out.println(a != b ? 'a' : b); Map<String, Long> map = new HashMap<>(); map.put("b", 1L); // 基本数据类型和对象数据类型相遇时,默认转为基本数据类, // map.get("a") 返回 null,转为基本数据类型时,报空指针异常 System.out.println(map == null ? -1L : map.get("a"));}作业如何修改 test1 方法,使得代码运行时不抛空指针异常,请至少提供一种解决方案哦。 ...

November 4, 2019 · 1 min · jiezi

分享SpringCloud通用架构图

分享一个SpringCloud通用架构 希望对你有帮助, 如有错误欢迎指正

November 4, 2019 · 1 min · jiezi

spring学习之源码分析ConfigurableBeanFactory

ConfigurableBeanFactoryConfigurableBeanFactory继承了HierarchicalBeanFactory, SingletonBeanRegistry两个接口。这个接口将被大多数bean工厂实现。 类结构这个方法,比之前的都多,没有截全。 常量String SCOPE_SINGLETON = "singleton";//单例String SCOPE_PROTOTYPE = "prototype";//多例方法解析设置父类容器 void setParentBeanFactory(BeanFactory parentBeanFactory) throws IllegalStateException;类加载器 // 设置类加载器void setBeanClassLoader(@Nullable ClassLoader beanClassLoader);// 获取类加载器ClassLoader getBeanClassLoader();// 设置临时加载器,如果涉及到加载时编织,通常只指定一个临时类装入器,以确保实际的bean类被尽可能延迟地装入void setTempClassLoader(@Nullable ClassLoader tempClassLoader);// 获取临时加载器ClassLoader getTempClassLoader();bean的元数据缓存,默认为true。如果为false,每次创建bean都要从类加载器获取信息。 // 设置是否缓存void setCacheBeanMetadata(boolean cacheBeanMetadata);// 获取是否缓存boolean isCacheBeanMetadata();bean的表达式解析器 // 设置表达式解析器void setBeanExpressionResolver(@Nullable BeanExpressionResolver resolver);// 获取表达式解析器BeanExpressionResolver getBeanExpressionResolver();类型转换器 // 设置类型转换器void setConversionService(@Nullable ConversionService conversionService);// 获取类型转换器ConversionService getConversionService();属性编辑器 // 添加属性编辑器void addPropertyEditorRegistrar(PropertyEditorRegistrar registrar);// 注册给定类型的属性编辑器void registerCustomEditor(Class<?> requiredType, Class<? extends PropertyEditor> propertyEditorClass);// 使用在这个BeanFactory中注册的自定义编辑器初始化给定的PropertyEditorRegistryvoid copyRegisteredEditorsTo(PropertyEditorRegistry registry);类型转换器 // 设置类型转换器void setTypeConverter(TypeConverter typeConverter);// 获取类型转换器TypeConverter getTypeConverter();为嵌入的值(如注释属性)添加字符串解析器 ...

November 4, 2019 · 1 min · jiezi

springbootplus是易于使用快速高效功能丰富开源的spring-boot-脚手架

spring-boot-plus是一套集成spring boot常用开发组件的后台快速开发框架Spring-Boot-Plus是易于使用,快速,高效,功能丰富,开源的spring boot 脚手架.前后端分离,专注于后端服务 目标每个人都可以独立、快速、高效地开发项目!版本库GITHUB | GITEE官网springboot.plus主要特性集成spring boot 常用开发组件集、公共配置、AOP日志等集成mybatis plus快速dao操作快速生成后台代码: entity/param/vo/controller/service/mapper/xml集成swagger2,可自动生成api文档集成jwt、shiro/spring security权限控制集成redis、spring cache、ehcache缓存集成rabbit/rocket/kafka mq消息队列集成druid连接池,JDBC性能和慢查询检测集成spring boot admin,实时检测项目运行情况使用assembly maven插件进行不同环境打包部署,包含启动、重启命令,配置文件提取到外部config目录项目架构 项目环境中间件版本备注JDK1.8+JDK1.8及以上 MySQL5.7+5.7及以上 Redis3.2+ 技术选型技术版本备注Spring Boot2.2.0.RELEASE最新发布稳定版 Spring Framework5.2.0.RELEASE最新发布稳定版 Mybatis3.5.2持久层框架 Mybatis Plus3.2.0mybatis增强框架 Alibaba Druid1.1.20数据源 Fastjson1.2.62JSON处理工具集 swagger22.6.1api文档生成工具 commons-lang33.9常用工具包 commons-io2.6IO工具包 commons-codec1.13加密解密等工具包 commons-collections44.4集合工具包 reflections0.9.11反射工具包 hibernate-validator6.0.17.Final后台参数校验注解 Shiro1.4.1权限控制 JWT3.8.3JSON WEB TOKEN hutool-all5.0.3常用工具集 lombok1.18.10注解生成Java Bean等工具 mapstruct1.3.1.Final对象属性复制工具 CHANGELOGCHANGELOG.mdJava DocsJava Api Docs使用克隆 spring-boot-plusgit clone https://github.com/geekidea/spring-boot-plus.gitcd spring-boot-plusMaven 构建默认使用local环境,对应配置文件:application-local.ymlmvn clean package -Plocal5分钟完成增删改查1. 创建数据库表-- ------------------------------ Table structure for foo_bar-- ----------------------------DROP TABLE IF EXISTS `foo_bar`;CREATE TABLE `foo_bar`( `id` bigint(20) NOT NULL COMMENT '主键', `name` varchar(20) NOT NULL COMMENT '名称', `foo` varchar(20) DEFAULT NULL COMMENT 'Foo', `bar` varchar(20) NOT NULL COMMENT 'Bar', `remark` varchar(200) DEFAULT NULL COMMENT '备注', `state` int(11) NOT NULL DEFAULT '1' COMMENT '状态,0:禁用,1:启用', `version` int(11) NOT NULL DEFAULT '0' COMMENT '版本', `create_time` timestamp NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间', `update_time` timestamp NULL DEFAULT NULL COMMENT '修改时间', PRIMARY KEY (`id`)) ENGINE = InnoDB DEFAULT CHARSET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT ='FooBar';-- ------------------------------ Records of foo_bar-- ----------------------------INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) VALUES (1, 'FooBar', 'foo', 'bar', 'remark...', 1, 0, '2019-11-01 14:05:14', null);INSERT INTO foo_bar (id, name, foo, bar, remark, state, version, create_time, update_time) VALUES (2, 'HelloWorld', 'hello', 'world', null, 1, 0, '2019-11-01 14:05:14', null);2.使用代码生成器生成增删改查代码修改数据库信息修改组件名称/作者/数据库表名称/主键id ...

November 3, 2019 · 3 min · jiezi

原创怎样才能写出优雅的-Java-代码这篇文章告诉你答案

本文已经收录自 JavaGuide (59k+ Star):【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。本文比较简短,基本就是推荐一些对于写好代码非常有用的文章或者资源。讲真的,下面推荐的文章或者资源强烈建议阅读 3 遍以上。 团队阿里巴巴Java开发手册(详尽版) https://github.com/alibaba/p3...Google Java编程风格指南: http://hawstein.com/2014/01/2...个人程序员你为什么这么累: https://xwjie.github.io/rule/如何写出优雅的 Java 代码使用 IntelliJ IDEA 作为您的集成开发环境 (IDE)使用 JDK 8 或更高版本使用 Maven/Gradle使用 Lombok编写单元测试重构:常见,但也很慢注意代码规范定期联络客户,以获取他们的反馈上述建议的详细内容:八点建议助您写出优雅的Java代码。 更多代码优化相关内容推荐: 业务复杂=if else?刚来的大神竟然用策略+工厂彻底干掉了他们!:https://juejin.im/post/5dad23...一些不错的 Java 实践!推荐阅读3遍以上!:http://lrwinx.github.io/2017/03/04/细思极恐-你真的会写java吗/解锁新姿势] 兄dei,你代码需要优化了:https://juejin.im/post/5dafbc...消灭 Java 代码的“坏味道”开源项目推荐作者的其他开源项目推荐: JavaGuide:【Java学习+面试指南】 一份涵盖大部分Java程序员所需要掌握的核心知识。springboot-guide : 适合新手入门以及有经验的开发人员查阅的 Spring Boot 教程(业余时间维护中,欢迎一起维护)。programmer-advancement : 我觉得技术人员应该有的一些好习惯!spring-security-jwt-guide :从零入门 !Spring Security With JWT(含权限验证)后端部分代码。公众号

November 3, 2019 · 1 min · jiezi

如何在Spring-Boot中使用Cookies

一、 导读本文大纲 读取HTTP Cookie设置HTTP Cookie读取所有Cookie[]为Cookie设置过期时间Https与CookieHttpOnly Cookie删除CookieHTTP Cookie(也称为Web cookie,浏览器cookie)是服务器在用户浏览器中存储的小部分数据。服务器端应用程序在返回浏览器请求响应的时候设置cookie,浏览器存储cookie,并将它们在下一个请求一起发送的时候自动带回服务器端应用程序。 Cookies提供了一种在服务器和浏览器之间交换信息的方法,以管理会话(登录,购物车,游戏得分),记住用户首选项(主题,隐私策略接受)以及跟踪整个站点的用户行为。Cookies在一定程度上解放了服务器端的压力,因为将一部分数据放在浏览器端存储,所以这部分数据不能是涉及应用安全的数据。在本文中,我们将学习如何在Spring Boot应用程序中读取、设置和删除HTTP cookie。 二、读取HTTP CookieSpring框架提供@CookieValue注释来获取HTTP cookie的值,此注解可直接用在控制器方法参数中。 @GetMapping("/")public String readCookie(@CookieValue(value = "username", defaultValue = "Atta") String username) { return "Hey! My username is " + username;}在上述代码段中,请注意defaultValue = "Atta"。如果没有设置默认值,并且没有找到名称为username的Cookie,Spring将抛出java.lang.IllegalStateException异常。 三、设置HTTP Cookie要在Spring Boot中设置cookie,我们可以使用HttpServletResponse类的方法addCookie()。您需要做的就是创建一个新的Cookie对象并将其添加到响应中。 @GetMapping("/change-username")public String setCookie(HttpServletResponse response) { // 创建一个 cookie对象 Cookie cookie = new Cookie("username", "Jovan"); //将cookie对象加入response响应 response.addCookie(cookie); return "Username is changed!";}四、读取所有Cookie[]除了使用@CookieValue注解,我们还可以使用HttpServletRequest类作为控制器方法参数来读取所有cookie。此类提供了getCookies()方法,该方法以数组形式返回浏览器发送的所有cookie。 @GetMapping("/all-cookies")public String readAllCookies(HttpServletRequest request) { Cookie[] cookies = request.getCookies(); if (cookies != null) { return Arrays.stream(cookies) .map(c -> c.getName() + "=" + c.getValue()) .collect(Collectors.joining(", ")); } return "No cookies";}五、为Cookie设置过期时间如果没有为cookie指定过期时间,则其生命周期将持续到Session过期为止。这样的cookie称为会话cookie。会话cookie保持活动状态,直到用户关闭其浏览器或清除其cookie。但是您可以覆盖此默认行为,并使用类的setMaxAge()方法设置cookie的过期时间。 ...

November 3, 2019 · 1 min · jiezi

SpringBoot整合Flyway完成数据库持久化迭代更新

每次服务的代码更新部署,难免会存在数据库结构的变更以及字典数据的添加,手动执行更新脚本是一个耗时耗力的工作,而且还会出现遗漏或者其他状况,SpringBoot内部集成了一个自动执行数据库脚本的第三方依赖Flyway来解决这个繁琐的问题。 什么是Flyway官网给出的定义是`Version control for your database.Robust schema evolution across all your environments.With ease, pleasure and plain SQL.`(数据库的版本控制,在所有环境中进行稳健的架构演变,轻松,愉快和简单的SQL。) Flyway 是一款开源的数据库版本管理工具,它更倾向于规约优于配置的方式。 Flyway 可以独立于应用实现管理并跟踪数据库变更,支持数据库版本自动升级,并且有一套默认的规约,不需要复杂的配置,Migrations 可以写成 SQL 脚本,也可以写在 Java 代码中,不仅支持 Command Line 和 Java API,还支持 Build 构建工具和 Spring Boot 等,同时在分布式环境下能够安全可靠地升级数据库,同时也支持失败恢复等。 Flyway运行原理当我们运行配置使用Flyway的应用程序时,会自动在配置数据源的数据库内创建一个名为flyway_schema_history的表,该表内存放了数据库的历史记录信息。然后通过扫码应用程序的/reosurces/db/migration目录下的历史版本脚本SQL文件,文件格式为:V?__desc.sql,如:V1__init-db.sql,根据版本号进行排序后,获取最大的版本号与flyway_schema_history表内执行成功的最大版本号进行比对,如果项目内版本较高,则自动执行脚本文件。 创建项目通过idea工具创建SpringBoot项目,在pom.xml添加相关依赖如下所示: <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.flywaydb</groupId> <artifactId>flyway-core</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>com.zaxxer</groupId> <artifactId>HikariCP</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency></dependencies>添加数据库配置在application.yml配置文件内添加数据源信息,如下所示: ...

November 3, 2019 · 1 min · jiezi

使用Java8-Stream-API对Map按键或值进行排序

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

November 2, 2019 · 1 min · jiezi

Spring-Security-架构简介

一、技术概述1.1 Spring vs Spring Boot vs Spring Security1.1.1 Spring FrameworkSpring Framework 为开发 Java 应用程序提供了全面的基础架构支持。它包含了一些不错的功能,如 "依赖注入",以及一些现成的模块: Spring JDBCSpring MVCSpring SecuritySpring AOPSpring ORM这些模块可以大大减少应用程序的开发时间。例如,在 Java Web 开发的早期,我们需要编写大量样板代码以将记录插入数据源。但是,通过使用 Spring JDBC 模块的 JDBCTemplate,我们可以仅通过少量配置将其简化为几行代码。 1.1.2 Spring BootSpring Boot 是基于 Spring Framework,它为你的 Spring 应用程序提供了自动装配特性,它的设计目标是让你尽可能快的上手应用程序的开发。以下是 Spring Boot 所拥有的一些特性: 可以创建独立的 Spring 应用程序,并且基于 Maven 或 Gradle 插件,可以创建可执行的 JARs 和 WARs;内嵌 Tomcat 或 Jetty 等 Servlet 容器;提供自动配置的 "starter" 项目对象模型(POMS)以简化 Maven 配置;尽可能自动配置 Spring 容器;提供一些常见的功能、如监控、WEB容器,健康,安全等功能;绝对没有代码生成,也不需要 XML 配置。1.1.3 Spring SecuritySpring Security 是一个能够为基于 Spring 的企业应用系统提供声明式的安全访问控制解决方案的安全框架。它提供了一组可以在 Spring 应用上下文中配置的 Bean,充分利用了 Spring IoC(Inversion of Control 控制反转),DI(Dependency Injection 依赖注入)和 AOP(面向切面编程)功能,为应用系统提供声明式的安全访问控制功能,减少了为企业系统安全控制编写大量重复代码的工作。 ...

November 2, 2019 · 5 min · jiezi

深入学习Redis四基本类型List剖析

更多精彩文章,关注公众号【ToBeTopJavaer】,更有数万元精品vip资源免费等你来拿!!!接下来我们要剖析的基本类型是List,相信大家对List都不会陌生吧,下面我们将深入源码剖析Redis中List的实现。 存储类型 存储有序的字符串(从左到右),元素可以重复。可以充当队列和栈的角色。 操作命令 元素增减 lpush queue alpush queue b crpush queue d elpop queuerpop queueblpop queuebrpop queue取值 lindex queue 0lrange queue 0 -1 存储( 实现) 原理 在早期的版本中,数据量较小时用 ziplist 存储,达到临界值时转换为linkedlist 进行存储,分别对应 OBJ_ENCODING_ZIPLIST 和OBJ_ENCODING_LINKEDLIST 。 3.2 版本之后,统一用 quicklist 来存储。quicklist 存储了一个双向链表,每个节点都是一个 ziplist。 127.0.0.1:6379> object encoding queue"quicklist"什么是quicklist?quicklist(快速列表)是 ziplist 和 linkedlist 的结合体。 quicklist.h源码如下,head 和 tail 指向双向列表的表头和表尾 typedef struct quicklist { quicklistNode \*head; /\* 指向双向列表的表头 \*/ quicklistNode \*tail; /\* 指向双向列表的表尾 \*/ unsigned long count; /\* 所有的 ziplist 中一共存了多少个元素 \*/ unsigned long len; /\* 双向链表的长度, node 的数量 \*/ int fill : 16; /\* fill factor for individual nodes \*/ unsigned int compress : 16; /\* 压缩深度, 0: 不压缩; \*/} quicklist;redis.conf 相关参数: ...

October 22, 2019 · 2 min · jiezi

深入学习Redis三基本类型Hash剖析

更多精彩文章,关注公众号【ToBeTopJavaer】,更有数万元精品vip资源免费等你来拿!!!接下来我们要剖析的基本类型是Hash,相信大家对Hash都不会陌生吧,下面我们将深入源码剖析Redis中Hash的实现。 首先我们看一张图: 存储类型 包含键值对的无序散列表。value 只能是字符串,不能嵌套其他类型。 同样是存储字符串,Hash 与 String 的主要区别? 1、把所有相关的值聚集到一个 key 中,节省内存空间 2、只使用一个 key,减少 key 冲突 3、当需要批量获取值的时候,只需要使用一个命令,减少内存/IO/CPU 的消耗 Hash 不适合的场景: 1、Field 不能单独设置过期时间 2、没有 bit 操作 3、需要考虑数据量分布的问题(value 值非常大的时候,无法分布到多个节点) 操作命令 存储(实现)原理 Redis 的 Hash 本身也是一个 KV 的结构,类似于 Java 中的 HashMap。 外层的哈希(Redis KV 的实现)只用到了 hashtable。当存储 hash 数据类型时, 我们把它叫做内层的哈希。内层的哈希底层可以使用两种数据结构实现: ziplist:OBJ_ENCODING_ZIPLIST(压缩列表) hashtable:OBJ_ENCODING_HT(哈希表) 如下图所示: 问题一、那么在什么时候会用到ziplist,什么时候用到hashtable呢? 在redis.conf我们可以看到: 在源码中: /* 源码位置: t_hash.c , 当达字段个数超过阈值, 使用 HT 作为编码 */if (hashTypeLength(o) > server.hash_max_ziplist_entries)hashTypeConvert(o, OBJ_ENCODING_HT);/*源码位置: t_hash.c, 当字段值长度过大, 转为 HT */for (i = start; i <= end; i++) {if (sdsEncodedObject(argv[i]) &&sdslen(argv[i]->ptr) > server.hash_max_ziplist_value){hashTypeConvert(o, OBJ_ENCODING_HT);break;}}复制代码从而我们可以得知,当 hash 对象同时满足以下两个条件的时候,使用 ziplist 编码: ...

October 22, 2019 · 3 min · jiezi

SpringBoot-unit-2th关于spring4的那些事面试重点

第二单元SpringBoot配置1【授课重点】1)Application.properties2)Application.yml3)自定义配置文件4)引入xml配置5)@Configuration2【考核要求】1)SpringBoot有配置文件的项目2)Configuration和Bean3)树形配置文件4)传统的配置文件5)Xml方式配置Bean3【教学内容】3.1课程导入1、SpringBoot有灵活的配置方式2、实际开发中选择一种简单使用的配置方式即可3、兼容传统的spring配置方式3.2创建项目pom.xml 文件的内容如下所示 - Spring Boot提供了许多Starters来在类路径中添加jar。 例如,要编写Rest Endpoint,需要在类路径中添加spring-boot-starter-web依赖项。请遵守下面显示的代码以便更好地理解 -【编写web项目必须有的starter web】 3.2.1Main方法Main方法应该是编写Spring Boot Application类。 该类应使用@SpringBootApplication进行注释。这是启动Spring启动应用程序的入口点。以在src/java/main目录下找到主类文件。在此示例中,主类文件位于src/java/main目录中,其默认包为com.yiibai.demo。 请观察此处显示的代码以便更好地理解 -【启动类有两个注解: @SpringBootApplication, @SpringApplication 】 3.2.2编写一个Rest端点【就是写一个helloworld】要在Spring Boot Application主类文件本身中编写一个简单的Hello World Rest 端点,请按照以下步骤操作 - 首先,在类的顶部添加@RestController注释。使用@RequestMapping注释编写Request URI方法。Request URI方法应该返回Hello World字符串。现在,Spring Boot Application类文件将如下面的代码所示 - 3.3创建一个可执行的JAR 创建一个可执行的JAR文件,在命令提示符下使用Maven和Gradle命令运行Spring Boot应用程序,如下所示 - 使用maven命令mvn clean install,如下所示 - 执行命令后,可以在命令提示符下看到 BUILD SUCCESS 的消息,如下所示 - 用Java运行Hello World创建可执行JAR文件后,可以在以下目录中找到它。对于Maven,可以在目标目录下找到JAR文件,如下所示 现在,使用命令java -jar <JARFILE>运行JAR文件。 请注意,在上面的示例中,JAR文件名为demo-0.0.1-SNAPSHOT.jar 运行jar文件后,可以在控制台窗口中看到输出,如下所示 现在,看一下控制台,Tomcat在端口8080(http)上启动。 现在,转到Web浏览器并点击URL => http://localhost:8080/ 3.4SpringBoot构建系统在Spring Boot中,选择构建系统是一项重要任务。建议使用Maven或Gradle,因为它们可以为依赖关系管理提供良好的支持。 Spring不支持其他构建系统。3.5依赖管理Spring Boot团队提供了一个依赖项列表,以支持每个版本的Spring Boot版本。无需在构建配置文件中提供依赖项版本。Spring Boot会根据发行版自动配置依赖项版本。 请记住,升级Spring Boot版本时,依赖项也会自动升级。注 - 如果要指定依赖项的版本,可以在配置文件中指定它。 但是,Spring Boot团队强烈建议不要指定依赖项的版本。3.6Maven依赖对于Maven配置,应该继承Spring Boot Starter父项目来管理Spring Boot Starters依赖项。 因此只需在pom.xml 文件中继承启动父级,如下所示。 ...

October 18, 2019 · 2 min · jiezi

SpringBoot-unit-1th-请深刻理解教案里的提问

第一单元SpringBoot入门1【授课重点】1)SpringBoot空项目搭建,2)SpringBoot发展历史,3)热部署,4)spring-boot-starter-web,2【考核要求】1)SpringBoot空项目搭建,2)RestController与Controller讲解,3)SpringBoot jsp页面4)在boot项目里使用jstl标签3【教学内容】3.1课程导入1、为什么要有SpringBoot项目2、SpringBoot和微服务的关系(没有boot项目就没有微服务)3、公司新建项目采用的全是微服务4、Springboot项目的稳定版本和最新版本(1.5,2.x)5、SpringBoot的微服务比dubbo的微服务在开发和设计上更先进3.2SpringBoot介绍Spring Boot是一个基于Java的开源框架,用于创建微服务。它由Pivotal Team开发,用于构建独立的生产就绪Spring应用。 本章将介绍Spring Boot,并熟悉基本概念。3.2.1微服务是什么?微服务(Micro Service)是一种允许开发人员独立开发和部署服务的体系结构。每个运行的服务都有自己的流程,这实现了轻量级模型以支持业务应用程序。 例如--员工微服务--部门微服务--教师微服务--学生微服务--班级微服务----学生查询的时候,会调用班级微服务----员工查询的时候,也会调用部门微服务----思考在dubbo学习的时候,周考时,是不是让我们建立两个微服务(provider,server)?两个微服务可以分开部署,性能会大大增加。优点:Spring Boot为其开发人员提供以下优势 - 易于理解和开发Spring应用提高生产力缩短开发时间设计目标Spring Boot的设计目标如下 - 避免在Spring中进行复杂的XML配置以更简单的方式开发生产就绪的Spring应用程序隐藏和默认的方式使用Spring4减少开发时间并独立运行应用程序提供一种更简单的应用程序入门方式【前提是会用spring4,其实boot项目是易用,难精通,表面上是使用了boot项目,实际对boot,spring4,和springmvc和spring容器之间的关系难以掌握】,这里理论的内容又往往是面试的重点要想学会SpringBoot项目,一定要深刻理解Spring4,对每个注解都要充分理解。每个注解在项目里有什么功能 3.2.2为什么选择Spring Boot?选择Spring Boot,因为它提供的功能和优点如下 - 它提供了一种灵活的方法来配置Java Bean,XML配置和数据库事务。它提供强大的批处理和管理REST端点。(现在流行的rest请求,而不是我们过去学习的socket的请求方式,或者是httpclient的请求方式)在Spring Boot中,一切都是自动配置的; 无需手动配置。它提供基于注释的spring应用程序。简化依赖管理。它包括嵌入式Servlet容器。支持filter和interceptor。3.2.3Spring Boot是如何工作的?Spring Boot会根据使用@EnableAutoConfiguration批注添加到项目中的依赖项自动配置应用程序。 例如,如果MySQL数据库在类路径上,但尚未配置任何数据库连接,则Spring Boot会自动配置内存数据库。【boot 启动类】重点spring boot应用程序的入口点是包含@SpringBootApplication注释和main方法的类。Spring Boot使用@ComponentScan注释自动扫描项目中包含的所有组件。@ComponentScan(package name)扫描多个包用逗号分隔,Scan,扫描的意思3.2.4Spring Boot Starters处理依赖管理对于大项目来说是一项艰巨的任务。 Spring Boot通过提供一组依赖项来解决此问题,以方便开发人员。每一个模块,在boot项目里,都有一个对应的starter。例如,如果要使用Spring和JPA进行数据库访问,则在项目中包含spring-boot-starter-data-jpa依赖项就足够了。请注意,所有Spring Boot启动程序都遵循相同的命名模式spring-boot-starter-,其中表示它是应用程序的一种类型。例子请看下面的Spring Boot启动器,以便更好地理解 -Actuator 监控boot项目运行时占用内存的情况】Spring Boot Starter Actuator依赖关系用于监视和管理应用程序。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency>XMLSpring Boot Starter Security依赖项用于Spring Security。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency>XMLSpring Boot Starter Web依赖项用于编写Rest端点。 其代码如下所示 -【Web starter 是非常常用的starter】<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>--这个starter非常常用--一般管这个starter叫springboot的web starterXMLSpring Boot Starter Thyme Leaf依赖项用于创建Web应用程序。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>这个是springboot推荐的开发html页面的新技术。XMLSpring Boot Starter Test依赖项用于编写测试用例。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test<artifactId></dependency> ...

October 18, 2019 · 1 min · jiezi

SpringBootVue-unit-1th-请深刻理解教案里的提问

第一单元SpringBoot入门1【授课重点】1)SpringBoot空项目搭建,2)SpringBoot发展历史,3)热部署,4)spring-boot-starter-web,2【考核要求】1)SpringBoot空项目搭建,2)RestController与Controller讲解,3)SpringBoot jsp页面4)在boot项目里使用jstl标签3【教学内容】3.1课程导入1、为什么要有SpringBoot项目2、SpringBoot和微服务的关系(没有boot项目就没有微服务)3、公司新建项目采用的全是微服务4、Springboot项目的稳定版本和最新版本(1.5,2.x)5、SpringBoot的微服务比dubbo的微服务在开发和设计上更先进3.2SpringBoot介绍Spring Boot是一个基于Java的开源框架,用于创建微服务。它由Pivotal Team开发,用于构建独立的生产就绪Spring应用。 本章将介绍Spring Boot,并熟悉基本概念。3.2.1微服务是什么?微服务(Micro Service)是一种允许开发人员独立开发和部署服务的体系结构。每个运行的服务都有自己的流程,这实现了轻量级模型以支持业务应用程序。 例如--员工微服务--部门微服务--教师微服务--学生微服务--班级微服务----学生查询的时候,会调用班级微服务----员工查询的时候,也会调用部门微服务----思考在dubbo学习的时候,周考时,是不是让我们建立两个微服务(provider,server)?两个微服务可以分开部署,性能会大大增加。优点:Spring Boot为其开发人员提供以下优势 - 易于理解和开发Spring应用提高生产力缩短开发时间设计目标Spring Boot的设计目标如下 - 避免在Spring中进行复杂的XML配置以更简单的方式开发生产就绪的Spring应用程序隐藏和默认的方式使用Spring4减少开发时间并独立运行应用程序提供一种更简单的应用程序入门方式【前提是会用spring4,其实boot项目是易用,难精通,表面上是使用了boot项目,实际对boot,spring4,和springmvc和spring容器之间的关系难以掌握】,这里理论的内容又往往是面试的重点要想学会SpringBoot项目,一定要深刻理解Spring4,对每个注解都要充分理解。每个注解在项目里有什么功能 3.2.2为什么选择Spring Boot?选择Spring Boot,因为它提供的功能和优点如下 - 它提供了一种灵活的方法来配置Java Bean,XML配置和数据库事务。它提供强大的批处理和管理REST端点。(现在流行的rest请求,而不是我们过去学习的socket的请求方式,或者是httpclient的请求方式)在Spring Boot中,一切都是自动配置的; 无需手动配置。它提供基于注释的spring应用程序。简化依赖管理。它包括嵌入式Servlet容器。支持filter和interceptor。3.2.3Spring Boot是如何工作的?Spring Boot会根据使用@EnableAutoConfiguration批注添加到项目中的依赖项自动配置应用程序。 例如,如果MySQL数据库在类路径上,但尚未配置任何数据库连接,则Spring Boot会自动配置内存数据库。【boot 启动类】重点spring boot应用程序的入口点是包含@SpringBootApplication注释和main方法的类。Spring Boot使用@ComponentScan注释自动扫描项目中包含的所有组件。@ComponentScan(package name)扫描多个包用逗号分隔,Scan,扫描的意思3.2.4Spring Boot Starters处理依赖管理对于大项目来说是一项艰巨的任务。 Spring Boot通过提供一组依赖项来解决此问题,以方便开发人员。每一个模块,在boot项目里,都有一个对应的starter。例如,如果要使用Spring和JPA进行数据库访问,则在项目中包含spring-boot-starter-data-jpa依赖项就足够了。请注意,所有Spring Boot启动程序都遵循相同的命名模式spring-boot-starter-,其中表示它是应用程序的一种类型。例子请看下面的Spring Boot启动器,以便更好地理解 -Actuator 监控boot项目运行时占用内存的情况】Spring Boot Starter Actuator依赖关系用于监视和管理应用程序。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId></dependency>XMLSpring Boot Starter Security依赖项用于Spring Security。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId></dependency>XMLSpring Boot Starter Web依赖项用于编写Rest端点。 其代码如下所示 -【Web starter 是非常常用的starter】<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency>--这个starter非常常用--一般管这个starter叫springboot的web starterXMLSpring Boot Starter Thyme Leaf依赖项用于创建Web应用程序。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>这个是springboot推荐的开发html页面的新技术。XMLSpring Boot Starter Test依赖项用于编写测试用例。 其代码如下所示 -<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test<artifactId></dependency> ...

October 18, 2019 · 1 min · jiezi

Spring-Security-实战干货玩转自定义登录

1. 前言前面的关于 Spring Security 相关的文章只是一个预热。为了接下来更好的实战,如果你错过了请从 Spring Security 实战系列 开始。安全访问的第一步就是认证(Authentication),认证的第一步就是登录。今天我们要通过对 Spring Security 的自定义,来设计一个可扩展,可伸缩的 form 登录功能。 2. form 登录的流程下面是 form 登录的基本流程: 只要是 form 登录基本都能转化为上面的流程。接下来我们看看 Spring Security 是如何处理的。 3. Spring Security 中的登录昨天 Spring Security 实战干货:自定义配置类入口WebSecurityConfigurerAdapter 中已经讲到了我们通常的自定义访问控制主要是通过 HttpSecurity 来构建的。默认它提供了三种登录方式: formLogin() 普通表单登录oauth2Login() 基于 OAuth2.0 认证/授权协议openidLogin() 基于 OpenID 身份认证规范以上三种方式统统是 AbstractAuthenticationFilterConfigurer 实现的, 4. HttpSecurity 中的 form 表单登录启用表单登录通过两种方式一种是通过 HttpSecurity 的 apply(C configurer) 方法自己构造一个 AbstractAuthenticationFilterConfigurer 的实现,这种是比较高级的玩法。 另一种是我们常见的使用 HttpSecurity 的 formLogin() 方法来自定义 FormLoginConfigurer 。我们先搞一下比较常规的第二种。 ...

October 18, 2019 · 4 min · jiezi

一文读懂Spring事务管理器

为什么需要事务管理器如果没有事务管理器的话,我们的程序可能是这样: Connection connection = acquireConnection();try{ int updated = connection.prepareStatement().executeUpdate(); connection.commit();}catch (Exception e){ rollback(connection);}finally { releaseConnection(connection);}也有可能是这样"优雅的事务": execute(new TxCallback() { @Override public Object doInTx(Connection var1) { //do something... return null; }});public void execute(TxCallback txCallback){ Connection connection = acquireConnection(); try{ txCallback.doInTx(connection); connection.commit(); }catch (Exception e){ rollback(connection); }finally { releaseConnection(connection); }}# lambda版execute(connection -> { //do something... return null;});但是以上两种方式,针对一些复杂的场景是很不方便的。在实际的业务场景中,往往有比较复杂的业务逻辑,代码冗长,逻辑关联复杂,如果一个大操作中有全是这种代码的话我想开发人员可能会疯把。更不用提定制化的隔离级别,以及嵌套/独立事务的处理了。 Spring 事务管理器简介Spring作为Java最强框架,事务管理也是其核心功能之一。Spring为事务管理提供了统一的抽象,有以下优点: 跨不同事务API(例如Java事务API(JTA),JDBC,Hibernate,Java持久性API(JPA)和Java数据对象(JDO))的一致编程模型。支持声明式事务管理(注解形式)与JTA之类的复杂事务API相比, 用于程序化事务管理的API更简单和Spring的Data层抽象集成方便(比如Spring - Hibernate/Jdbc/Mybatis/Jpa...)使用方式事务,自然是控制业务的,在一个业务流程内,往往希望保证原子性,要么全成功要么全失败。 所以事务一般是加载@Service层,一个Service内调用了多个操作数据库的操作(比如Dao),在Service结束后事务自动提交,如有异常抛出则事务回滚。 这也是Spring事务管理的基本使用原则。 下面贴出具体的使用代码: 注解在被Spring管理的类头上增加@Transactional注解,即可对该类下的所有方法开启事务管理。事务开启后,方法内的操作无需手动开启/提交/回滚事务,一切交给Spring管理即可。 @Service@Transactionalpublic class TxTestService{ @Autowired private OrderRepo orderRepo; public void submit(Order order){ orderRepo.save(order); }}也可以只在方法上配置,方法配置的优先级是大于类的 ...

October 17, 2019 · 2 min · jiezi

Spring-Cloud-Alibaba-Nacos-Config-实战

Nacos 提供用于存储配置和其他元数据的 key/value 存储,为分布式系统中的外部化配置提供服务器端和客户端支持。使用 Spring Cloud Alibaba Nacos Config,您可以在 Nacos Server 集中管理你 Spring Cloud 应用的外部属性配置 一、安装 Nacos1、下载 Nacos最新稳定版下载:https://github.com/alibaba/nacos/releases 2、启动 Nacos启动 Nacos (单机模式) sh startup.sh -m standalone关闭 Nacos sh shutdown.sh二、配置 Nacos1、打开 Nacos默认地址:http://127.0.0.1:8848/nacos/#/login 默认账号:账号密码相同,都为nacos 2、添加配置配置数据: Data ID: nacos-dev.propertiesGroup : DEFAULT_GROUP配置格式: Properties配置内容: useLocalCache=true Data ID 的格式说明: ${prefix}-${spring.profile.active}.${file-extension}prefix: 默认为 spring.application.name 的值,也可以通过配置项 spring.cloud.nacos.config.prefix 来配置 spring.profile.active: 即为当前环境对应的 profile, 注意:当 spring.profile.active 为空时,对应的连接符 - 也将不存在,dataId 的拼接格式变成 ${prefix}.${file-extension} file-exetension: 为配置内容的数据格式,可以通过配置项 spring.cloud.nacos.config.file-extension 来配置。目前只支持 properties 和 yaml 类型 ...

October 17, 2019 · 1 min · jiezi

源码解析自动配置的这些细节不知道别说你会-springboot

spring-boot 相对于 spring,很重要的一个特点就是自动配置,使约定大于配置思想成功落地。xxx-spring-boot-starter 一系列引导器能够开箱即用,或者只需要很少的配置(对于初学人员)就是因为已做了默认的自动配置。 自动配置在一开始就初始化了一些配置,同时提供修改配置的入口。 整体结构spring-boot-autoconfigure 包是 spring-boot-starter 中一个非常重要的包,其中提供了自动配置功能,还对常用依赖,设置了默认配置。 依赖其依赖的包有三种: 基础包:spring-boot可选功能依赖包:提供默认配置的常用依赖包,实际使用时由使用者提供测试包可选功能依赖包有 spring-data-redis、validator、thymeleaf、websocket 等等。下面会选几个作为示例具体讲解。 原理@EnableAutoConfiguration@AutoConfigurationPackage@Import(AutoConfigurationImportSelector.class)public @interface EnableAutoConfiguration { /** * 当此名对应属性为 true 时,才开启自动配置 */ String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration"; /** * 需要排除的类(通常当此类在类路径下时使用) */ Class<?>[] exclude() default {}; /** * 需要排除的类名(当此类不在类路径下时使用) */ String[] excludeName() default {};}@AutoConfigurationPackage将使用此注解的类所属于的包注册成 spring bean。此 spring bean 的 beanName 为 AutoConfigurationPackages,beanClass 为 BasePackages。 AutoConfigurationImportSelector自动配置选择器,选择哪些类自动配置。 selectImports核心方法:String[] selectImports(AnnotationMetadata annotationMetadata) ,此方法返回需要自动配置的全类名数组。需要自动配置的类满足以下条件: META-INF/spring.factories 中 key 为 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的类@EnableAutoConfiguration 注解中 exclude 、 excludeName 属性代表的类, 配置中 spring.autoconfigure.exclude 中设置的类(若类路径中存在,但是步骤 1 不存在,则抛异常)满足包含三种注解的条件:OnBeanCondition(如:ConditionalOnBean、ConditionalOnMissingBean) 、OnClassCondition (如:ConditionalOnClass、ConditionalOnMissingClass)、OnWebApplicationCondition(如:ConditionalOnWebApplication、ConditionalOnNotWebApplication)最后,会激活 AutoConfigurationImportEvent 事件。 ...

October 17, 2019 · 2 min · jiezi

Spring-Boot-定时任务-Scheduled

项目开发中经常需要执行一些定时任务,比如在每天凌晨,需要从 implala 数据库拉取产品功能活跃数据,分析处理后存入到 MySQL 数据库中。类似这样的需求还有许多,那么怎么去实现定时任务呢,有以下几种实现方式。 Java 定时任务的几种实现方式基于 java.util.Timer 定时器,实现类似闹钟的定时任务使用 Quartz、elastic-job、xxl-job 等开源第三方定时任务框架,适合分布式项目应用使用 Spring 提供的一个注解: @Schedule,开发简单,使用比较方便,也是本文介绍的一种方式Spring 自身提供了对定时任务的支持,本文将介绍 Spring Boot 中 @Scheduled 定时器的使用。 创建定时任务首先,在项目启动类上添加 @EnableScheduling 注解,开启对定时任务的支持 @SpringBootApplication@EnableSchedulingpublic class ScheduledApplication { public static void main(String[] args) { SpringApplication.run(ScheduledApplication.class, args); }}其中 @EnableScheduling注解的作用是发现注解@Scheduled的任务并后台执行。 其次,编写定时任务类和方法,定时任务类通过 Spring IOC 加载,使用 @Component 注解,定时方法使用 @Scheduled 注解。 @Componentpublic class ScheduledTask { @Scheduled(fixedRate = 3000) public void scheduledTask() { System.out.println("任务执行时间:" + LocalDateTime.now()); }}fixedRate 是 long 类型,表示任务执行的间隔毫秒数,以上代码中的定时任务每 3 秒执行一次。 运行定时工程,项目启动和运行日志如下,可见每 3 秒打印一次日志执行记录。 ...

October 17, 2019 · 1 min · jiezi

EurekaClient自动装配及启动流程解析

在上篇文章中,我们简单介绍了EurekaServer自动装配及启动流程解析,本篇文章则继续研究EurekaClient的相关代码老规矩,先看spring.factories文件,其中引入了一个配置类EurekaDiscoveryClientConfigServiceBootstrapConfiguration @ConditionalOnClass(ConfigServicePropertySourceLocator.class)@ConditionalOnProperty(value = "spring.cloud.config.discovery.enabled", matchIfMissing = false)@Configuration@Import({ EurekaDiscoveryClientConfiguration.class, EurekaClientAutoConfiguration.class })public class EurekaDiscoveryClientConfigServiceBootstrapConfiguration {}上方两个注解则是这个配置类是否能够开启的条件,这里就不再展开,直接看它引入的配置类吧 EurekaDiscoveryClientConfiguration细心的读者可能会发现这里又注册了一个Marker类,可以猜测也是某个地方的开关EurekaClientConfigurationRefresher这个类看名字就知道这是当配置被动态刷新时的一个处理器,这里也不再展开了EurekaHealthCheckHandlerConfiguration这里面注册了一个Eureka健康检查的处理类,这个健康检查相关的原理分析可以参考这篇文章:SpringBoot健康检查实现原理EurekaClientAutoConfiguration这个类里面全是重点,也是我们本文的核心 注解@Configuration@EnableConfigurationProperties@ConditionalOnClass(EurekaClientConfig.class)@Import(DiscoveryClientOptionalArgsConfiguration.class)@ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)@ConditionalOnProperty(value = "eureka.client.enabled", matchIfMissing = true)@AutoConfigureBefore({ NoopDiscoveryClientAutoConfiguration.class, CommonsClientAutoConfiguration.class, ServiceRegistryAutoConfiguration.class })@AutoConfigureAfter(name = {"org.springframework.cloud.autoconfigure.RefreshAutoConfiguration", "org.springframework.cloud.netflix.eureka.EurekaDiscoveryClientConfiguration", "org.springframework.cloud.client.serviceregistry.AutoServiceRegistrationAutoConfiguration"})首先可以看到这个类一共包含这些注解,我们来一一解析比较重要的几个注解吧 @Import(DiscoveryClientOptionalArgsConfiguration.class)引入了两个bean,RestTemplateDiscoveryClientOptionalArgs和MutableDiscoveryClientOptionalArgs ,这两个类的作用暂且不说 @ConditionalOnBean(EurekaDiscoveryClientConfiguration.Marker.class)刚才说的Marker类的作用出来了 @AutoConfigureBefore既然必须在这三个类完成自动装配之后才能进行装配,那就代表着这三个类肯定大有用途,研究一下吧 NoopDiscoveryClientAutoConfiguration故名思意,负责服务发现的类,咱们重点关注一下其中的几个方法 init@PostConstruct public void init() { String host = "localhost"; try { host = InetAddress.getLocalHost().getHostName(); } catch (UnknownHostException e) { log.warn("Cannot get host info: (" + e.getMessage() + ")"); } int port = findPort(); this.serviceInstance = new DefaultServiceInstance( this.environment.getProperty("spring.application.name", "application"), host, port, false); }这里构造了一个DefaultServiceInstance对象,这个对象包含了当前项目的ip+端口+项目名称 ...

October 17, 2019 · 3 min · jiezi

SpringBootSecurity学习25前后端分离版之OAuth20-令牌中继

增加Eureka前面介绍的项目都是授权服务和资源服务单独两个,这样在资源服务中的 check_token 地址都是写死的地址 : 下面我们把eureka加上,这样就可以直接用服务名了。eureka服务的搭建不再讨论,在服务中加eureka只需要改两个地方,就是加依赖和修改配置: 增加配置如下: 然后就可以把资源服务中的固定ip改为服务名: 增加令牌中继从授权服务获取令牌以后,直接访问一个资源服务是没有问题的,但是如果资源服务又去调用其它资源服务,就会产生一个问题,就是令牌无法自动往下传递。要实现令牌中继我们就需要写一个拦截器,将token放到请求的header中。 首先在服务中增加feign组件: 增加注解 @EnableFeignClients : 注意上面的依赖和注解调用方和被调用方都要增加。然后在被调用方写一个接口: 在调用方使用feign调用: 然后在调用方写一个测试接口: 现在直接申请令牌,然后访问调用方测试接口,会出现如下401错误: 下面在调用方增加一个拦截器,在header中增加令牌: 测试下面直接申请令牌,访问调用方的测试接口,可以看到令牌中继的结果: 代码地址:https://gitee.com/blueses/spr... 30 31 32 33 本文由博客一文多发平台 OpenWrite 发布!

October 16, 2019 · 1 min · jiezi

Spring事务传播属性有那么难吗看这一篇就够了

Spring事务传播属性有那么难吗?看这一篇就够了笔者文笔功力尚浅,如有不妥,请慷慨指出,必定感激不尽学习东西要知行合一,如果只是知道理论而没实践过,那么掌握的也不会特别扎实,估计过几天就会忘记,接下来我们一起实践来学习Spring事务的传播属性。 传播属性传播属性定义的是当一个事务方法碰到另一个事务方法时的处理行为,一共有七种行为,定义如下 传播性值描述PROPAGATION_REQUIRED0支持当前事务,如果没有就新建事务PROPAGATION_SUPPORTS1支持当前事务,如果没有就不以事务的方式运行PROPAGATION_MANDATORY2支持当前事务,如果当前没事务就抛异常PROPAGATION_REQUIRES_NEW3无论当前是否有事务,都会新起一个事务PROPAGATION_NOT_SUPPORTED4不支持事务,如果当前存在事务,就将此事务挂起不以事务方式运行PROPAGATION_NEVER5不支持事务,如果有事务就抛异常PROPAGATION_NESTED6如果当前存在事务,在当前事务中再新起一个事务其实只看概念的话已经很直截了当了说明了每个传播性的作用,此时我们再用具体的例子演示一下每个传播性属性下的行为。 此次演示我们使用的是H2数据库,这个数据库是作用在内存里面的,所以对于我们演示事务效果来说正好,无需我们在进行其他的配置了,我们新建一个表。将下面语句放在schema.sql文件里面即可,SpringBoot程序在启动的时候就会自动为我们在内存里面建立这样的一个表。 CREATE TABLE FOO (ID INT IDENTITY, BAR VARCHAR(64));演示之前我们会定义两个类FooService和BarService。我们使用BarService 里面的方法进行调用FooService 中的方法。 环境准备在进行事务演示之前,其实可以分为以下几种情况,根据排列组合,我们可以得出以下八种情况 调用者:有无事务调用者:是否有异常被调用者:有无事务(这个是通过传播属性进行控制的)所以并不在排列组合中被调用者:是否有异常调用者是否有事务调用者是否有异常被调用者是否有异常有有有有有无有无有有无无无有有无有无无无有无无无异常类其中的RollbackException是我们自己定义的一个异常类 @Servicepublic class BarServiceImpl implements BarService{ @Autowired private FooService fooService; // PROPAGATION_REQUIRED演示 无事务 @Override public void testRequiredNoTransactional() throws RollbackException { fooService.testRequiredTransactional(); }}调用者在BarService中定义两个方法,一个是带着事务的,一个是不带事务的 // 有事务@Override@Transactional(rollbackFor = Exception.class)public void hasTransactional() throws RollbackException {}// 无事务@Overridepublic void noTransactional() throws RollbackException { }接下来我们就根据俄上面定义的八种情况进行事务传播属性的学习。 PROPAGATION_REQUIRED在此传播属性下,被调用方是否新建事务取决去调用者是否带着事务。想要了解这个传播属性的特性,其实我们演示上面八种情况的两个例子就够了 调用者是否有事务调用者是否有异常被调用者是否有异常无无有有有无第一种情况我们在被调用者抛出异常的情况下,如果查询不到插入的数据,那么就说明被调用者在调用者没有事务的情况下自己新建了事务。第二种情况我们在调用者抛出异常的情况下,如果查询不到插入的数据,那么就说明被调用者在调用者有事务的情况下就加入当前事务了。我们先来看一下被调用者的类的方法例子。 @Servicepublic class FooServiceImpl implements FooService { @Autowired private JdbcTemplate jdbcTemplate; // REQUIRED传播属性-被调用者有异常抛出 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED) public void testRequiredHasException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ("+Global.REQUIRED_HAS_EXCEPTION+")"); throw new RollbackException(); } // REQUIRED传播属性-被调用者无异常抛出 @Override @Transactional(rollbackFor = Exception.class,propagation = Propagation.REQUIRED) public void testRequiredNoException() throws RollbackException { jdbcTemplate.execute("INSERT INTO FOO (BAR) VALUES ("+Global.REQUIRED_NO_EXCEPTION+")"); }}接下来我们看一下调用者方法的例子 ...

October 16, 2019 · 3 min · jiezi

spring学习之Aware接口

在日常的开发过程中,有时候我们需要用到getBean的方法,此时,我们想要用ApplicationContext来调用这个方法,那这个ApplicationContext是怎么获取到的呢? ApplicationContextAwareApplicationContextAware继承了Aware接口,同时定义了setApplicationContext方法。下面我们看看怎么通过ApplicationContextAware接口获取这个ApplicationContext。MyApplicationContext public class MyApplicationContext implements ApplicationContextAware { ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } public ApplicationContext getApplicationContext() { return applicationContext; }}MyConfig @Configurationpublic class MyConfig { @Bean public MyApplicationContext myApplicationContext() { return new MyApplicationContext(); }}测试代码 @Testpublic void test(){ ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class); MyApplicationContext myApplicationContext =app.getBean("myApplicationContext",MyApplicationContext.class); System.out.println(app); System.out.println(myApplicationContext.getApplicationContext());}运行结果如下:两次打印的地址是一样的,所以我们获取到的ApplicationContext是正确的。 其他Aware接口BeanFactoryAware,可以获取到BeanFactory。BeanNameAware,可以获取到bean的名称。等其他不在描述。

October 16, 2019 · 1 min · jiezi

spring学习之bean生命周期的管理

三种方式init和destroyXML配置中,bean标签,init-method用来指定bean初始化后调用的方法,destroy-method用来指定bean销毁前调用的方法。如果想统一设置,可以在beans中设置default-init-method属性。注解中,@Bean注解,initMethod用来指定bean初始化后调用的方法,destroyMethod用来指定bean销毁前调用的方法。 InitializingBean和DisposableBeanorg.springframework.beans.factory.InitializingBean接口,在其他初始化工作完成后,才开始调用他的afterPropertiesSet()方法.org.springframework.beans.factory.DisposableBean接口,在容器销毁时,会调用destroy()这个方法。 @PostConstruct和@PreDestroy@PostConstruct和@PreDestroy不属于spring,而是属于JSR-250规则,日常用的较多的,还是这两个注解。 三种方式执行顺序当这三种生命周期机制同时作用于同一个bean时,执行顺序如下:初始化:@PostConstruct > InitializingBean的afterPropertiesSet()方法 > init()方法。销毁:@PreDestroy > DisposableBean的destroy()方法 > destroy()方法。 示例MyBean public class MyBean implements InitializingBean, DisposableBean { private String name; public MyBean(String name) { System.out.println("name:" + name); this.name = name; } public void initMethod() { System.out.println("initMethod"); } public void destroyMethod() { System.out.println("destroyMethod"); } @PostConstruct public void postConstruct() { System.out.println("postConstruct"); } @PreDestroy public void preDestroy() { System.out.println("preDestroy"); } @Override public void destroy() throws Exception { System.out.println("destroy"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("afterPropertiesSet"); }}MyConfig ...

October 16, 2019 · 1 min · jiezi

使用ApiBoot-Logging进行统一管理请求日志

ApiBoot Logging通过集成minbox-logging来进行管理每一次请求的日志信息,包含头信息、参数、主体内容、路径、发生的服务器相关信息等,根据接口的响应状态还可以记录响应的头信息、响应的内容以及发生异常时的堆栈信息。 minbox-projects开源组织“org.minbox.framework” 致力于向广大开发者提供一系列的 “开箱即用” 的框架落地实现解决方案。 自从ApiBoot框架的落地,内部集成的第三方插件(plugin)日渐增多也同样导致了ApiBoot的源码太过于冗肿,针对这个问题minbox-projects开源组织就诞生了,ApiBoot第一个加入了该组织,并且会将ApiBoot内集成的第三方插件进行陆续分离,将每一个插件作为独立的开源项目加入minbox-projects开源组织,方便各个项目的单独维护以及更新发版。 组织首页:https://gitee.com/minbox-projects minbox-logging日志组件minbox-logging日志组件是minbox-projects开源组织内的一员,是一款分布式零侵入式、链路式请求日志分析框架。 提供Admin端点进行采集日志、分析日志、日志告警通知、服务性能分析等。通过Admin Ui可查看实时链路日志信息、在线业务服务列表,致力解决request -> response整个业务请求的日志分析以及记录。 minbox-logging日志组件源码:https://gitee.com/minbox-projects/minbox-logging 创建示例项目通过idea开发工具创建一个SpringBoot项目。 pom.xml依赖<!--配置参数--><properties> <java.version>1.8</java.version> <api.boot.version>2.1.4.RELEASE</api.boot.version></properties><dependencies> <!--Web--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!--ApiBoot Logging--> <dependency> <groupId>org.minbox.framework</groupId> <artifactId>api-boot-starter-logging</artifactId> </dependency></dependencies><dependencyManagement> <!--ApiBoot统一版本依赖--> <dependencies> <dependency> <groupId>org.minbox.framework</groupId> <artifactId>api-boot-dependencies</artifactId> <type>pom</type> <scope>import</scope> <version>${api.boot.version}</version> </dependency> </dependencies></dependencyManagement>测试控制器添加一个用于测试的LoggingSampleController控制器,源码如下所示: /** * 请求日志示例 * * @author 恒宇少年 */@RestController@RequestMapping(value = "/test")public class LoggingSampleController { /** * 验证请求参数以及相应内容 * * @param name * @return */ @GetMapping public String hello(@RequestParam("name") String name) { return "你好:" + name; } /** * 验证主体请求内容以及相应内容 * * @param user * @return */ @PostMapping public String bodyHello(@RequestBody User user) { return "你好:" + user.getName(); } /** * RequestBody 示例类 */ @Data public static class User { private String name; }}application.ymlspring: application: name: apiboot-unified-manage-request-logsserver: port: 8080由于ApiBoot Logging需要记录日志产生的服务器相关信息,所以spring.application.name以及server.port这两个参数必须配置,要不然启动项目时会抛出错误信息。 ...

October 16, 2019 · 2 min · jiezi

SpringBoot-Controller接收参数的几种常用方式

第一类:请求路径参数1. @PathVariable 获取路径参数。即url/{id}这种形式。 2. @RequestParam 获取查询参数。即url?name=这种形式 例子 GET http://localhost:8080/demo/123?name=suki_rong 对应的java代码 @GetMapping("/demo/{id}")public void demo(@PathVariable(name = "id") String id, @RequestParam(name = "name") String name) { System.out.println("id="+id); System.out.println("name="+name);}第二类:Body参数因为是POST请求,这里用Postman的截图结合代码说明 1. @RequestBody 例子 对应的java代码: @PostMapping(path = "/demo1")public void demo1(@RequestBody Person person) { System.out.println(person.toString());}输出结果: name:suki_rong;age=18;hobby:programing 也可以是这样: @PostMapping(path = "/demo1")public void demo1(@RequestBody Map<String, String> person) { System.out.println(person.get("name"));}输出结果: suki_rong 2.无注解 例子 对应的java代码: @PostMapping(path = "/demo2")public void demo2(Person person) { System.out.println(person.toString());}输出结果: name:suki_rong;age=18;hobby:programing Person类 public class Person { private long id; private String name; private int age; private String hobby; @Override public String toString(){ return "name:"+name+";age="+age+";hobby:"+hobby; } // getters and setters}第三类:请求头参数以及Cookie1. @RequestHeader 2. @CookieValue ...

October 16, 2019 · 1 min · jiezi

spring学习之bean的作用域

scope包括Singleton、Prototype、Request, Session, Application, WebSocket,这边主要讲常用的Singleton和Prototype。 Singleton当定义一个bean为单例对象时,IoC容器只创建该bean对象的一个实例。这个bean对象存储在容器的缓存(map)中,后续的对该bean的引用,都是从缓存中取(map.get)。这个单例跟设计模式的单例模式是不一样的。spring的单例是指在容器中仅有一个实例,而设计模式的单例是在JVM进程中仅有一个实例。 Prototype当需要每次getBean的时候,都返回不同的实例,这个时候,就需要用Prototype。 Singleton和Prototype如果bean是无状态的,就需要用Singleton,如果是有状态的,就用Prototype。对于Singleton的bean,spring容器会管理他的整个生命周期。对于Prototype的bean,spring容器不管理他的整个生命周期,尽管初始化的方法会被调用,但是与scope的范围无关。而且容器关闭时不会调用销毁方法。 示例XMLXML配置: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="singletonBean" class="com.learn.beanScope.SingletonBean" scope="singleton"/> <bean id="prototypeBean" class="com.learn.beanScope.PrototypeBean" scope="prototype"/></beans>测试代码 @Testpublic void test() { ApplicationContext app = new ClassPathXmlApplicationContext("beanScope.xml"); SingletonBean singletonBean1 = app.getBean("singletonBean",SingletonBean.class); SingletonBean singletonBean2 = app.getBean("singletonBean",SingletonBean.class); PrototypeBean prototypeBean1 =app.getBean("prototypeBean",PrototypeBean.class); PrototypeBean prototypeBean2 =app.getBean("prototypeBean",PrototypeBean.class); System.out.println(singletonBean1); System.out.println(singletonBean2); System.out.println(prototypeBean1); System.out.println(prototypeBean2);}运行结果:作用域为Singleton的时候,可以看出,从容器中取了两次,地址是一样的,所以是同一个bean。作用域为Prototype的时候,可以看出,从容器中取了两次,地址是不一样的,所以不是同一个bean。 注解MyConfig @Configurationpublic class MyConfig { @Bean @Scope("prototype") public PrototypeBean prototypeBean() { return new PrototypeBean(); } @Bean @Scope("singleton") public SingletonBean singletonBean() { return new SingletonBean(); }}测试代码 ...

October 16, 2019 · 1 min · jiezi

Spring-IoC-IoC-容器初始化-源码解析

前言本章主要内容是由以下部分组成, Spring 中容器初始化入口以最经典的ClassPathXmlApplicationContext 为例,讲解Spring IoC 的容器初始化过程在学习源码的过程当中,我想强调两点: 一定要学会抓重点,归纳核心类、核心方法、核心步骤。理解类、变量、方法名的命名,Spring 源码的命名是很讲究的,很多时候是自解释的一定要学会看Java doc ,同上,这种顶级的框架的java doc 描述非常的详尽Spring 容器初始化入口启动容器,实际上指的就是实例化ApplicationContext的这个动作。只是在不同情况下可能有不同的表现形式。 ClassPathXmlApplicationContext 通过XML配置ApplicationContext context = new ClassPathXmlApplicationContext(applicationContext.xml");AnnotationConfigApplicationContext 通过java config 类配置@Configuration@ComponentScan("ric.study.demo.ioc")public class BeanDemoConfig { public static void main(String... strings) { AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BeanDemoConfig.class); System.out.println("Spring container started and is ready"); ... }}类似前面这两种new ***ApplicationContext的方式,很少会用于直接的生产开发。一般都是我们自己在demo中或者单元测试中会用到。 WebApplicationContext SpringMVC这个实际上是我们平常最常用的初始化方式,Spring MVC 中 ServletContext 为 Spring 的 IoC容器提供了宿主环境。是通过ContextLoaderListener 的初始化来建立的。 WebApplicationContext 的初始化调用链路:ContextLoaderListener.contextInitialized --> ContextLoader.initWebApplicationContext --> ContextLoader.createWebApplicationContext --> ContextLoader.determineContextClass --> ContextLoader.determineContextClass。 ...

October 15, 2019 · 10 min · jiezi

SpringBootSecurity学习24前后端分离版之OAuth20-应用登记

应用登记一个应用要求 OAuth 授权,必须先到对方网站登记,让对方知道是谁在请求。举个例子,下面是github的登记页面: https://github.com/settings/a... 下面我们来自己做一个简单的应用登记,根据表 oauth_client_details 的结构,我们登记的时候只填写应用名称和回调地址即可,其它的字段如下: client_id:使用UUID生成client_secret:使用UUID生成,并使用 BCryptPasswordEncoder 加密scope:默认allauthorized_grant_types :默认 authorization_code,password,refresh_token 三个下面是sql语句: Service中的方法: 接口定义: 下面来测试接口: 返回了预期中的客户端id和秘钥。来看一下数据库: 现在我们就可以使用新登记的应用来请求令牌了: http://localhost:8029/oauth/authorize?client_id=52f301a86511406ba5b4fbb4809614b0&response_type=code&redirect_uri=http://localhost:8029/ 令牌请求结果: 状态state字段为了防止CSRF攻击,在申请授权码这一步时,可以在参数中增加一个state状态参数,这个参数是由客户端生成的随机字符串,授权服务会原封不动的返回这个参数和参数值,用户进行授权客户端的请求时也会携带此字符串用于比较。如下: http://localhost:8029/oauth/authorize?client_id=52f301a86511406ba5b4fbb4809614b0&response_type=code&redirect_uri=http://localhost:8029/&state=123456789返回结果如下: 如果传递过去的和回来的不一样,可以认为不合法。 代码地址:https://gitee.com/blueses/spr... 29 本文由博客一文多发平台 OpenWrite 发布!

October 15, 2019 · 1 min · jiezi

spring学习之懒加载

Lazy正常情况下,bean的加载是容器启动后就开始的,这样如果加载的过程中有错误,可以立马发现。由于一些特定的业务需求,需要某些bean在IoC容器在第一次请求时才创建,可以把这些bean标记为延时加载。 XML在XML配置文件中,是通过bean标签的lazy-init属性来控制的,如果控制多个bean都是懒加载,那么可以把bean放入beans标签下面,通过beans标签的default-lazy-init="true"来控制。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:c="http://www.springframework.org/schema/c" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="one" class="com.learn.di.One" lazy-init="true"/> <bean id="two" class="com.learn.di.Two"/></beans>One和Two public class One { public One(){ System.out.println("one init"); }}public class Two { public Two(){ System.out.println("two init"); }}测试代码: @Testpublic void test() { ApplicationContext app = new ClassPathXmlApplicationContext("di7.xml"); System.out.println("加载完毕"); app.getBean("one");}运行结果如下:加载完毕之前,只打印了two init,调用getBean("one")后,才打印出one init,可以看出,是在调用后才加载。 注解在注解中,是通过@Lazy来控制的。MyConfig @Configurationpublic class MyConfig { @Bean() @Lazy public One one(){ return new One(); } @Bean() public Two two(){ return new Two(); }}测试代码 @Testpublic void test() { ApplicationContext app = new AnnotationConfigApplicationContext(MyConfig.class); System.out.println("加载完毕"); app.getBean("one");}运行结果如下: ...

October 15, 2019 · 1 min · jiezi

spring学习之bean的命名

bean的命名每个bean都有一个或者多个标识符,这些标识符在容器中必须是唯一的。 XML如果id和name都不指定,IOC容器会自动生成一个唯一标识符,即全类名。如果仅指定id或者name,则为唯一标识符。如果同时指定,id为标识符,name为别名。xml配置如下: <?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="orderDao" class="com.learn.ch1.OrderDao"/> <bean class="com.learn.ch1.BuyDao"/> <bean id="buyDao2" class="com.learn.ch1.BuyDao2"/> <bean name="buyDao3" class="com.learn.ch1.BuyDao3"/> <bean id="buyDao4" name="buyDao5" class="com.learn.ch1.BuyDao4"/></beans>测试代码: @Testpublic void test() { ApplicationContext app = new ClassPathXmlApplicationContext("daos.xml"); String[] names = app.getBeanDefinitionNames(); for (String name : names) { }}运行结果如下: 注解注解也有两个属性,name和value,但是这两个不能共存,只能用一个。如果都不指定name和value,那么方法名作为bean的唯一标识符。MyConfig @Configurationpublic class MyConfig { @Bean public BuyDao buyDao() { return new BuyDao(); } @Bean(name = "buyDao2") public BuyDao2 buyDao2() { return new BuyDao2(); } @Bean(value = "buyDao3") public BuyDao3 buyDao3() { return new BuyDao3(); } /* 两个一起指定,会报错 @Bean(value = "buyDao4", name = "buyDao5") public BuyDao4 buyDao4() { return new BuyDao4(); }*/}测试代码: ...

October 15, 2019 · 2 min · jiezi

SpringBootSecurity学习23前后端分离版之OAuth20-其它模式

密码模式前面介绍了授权码模式和刷新令牌两种获取最新令牌的方法,下面来看一下其它模式。首先看密码模式,我们默认配置的三种模式中其实就包含密码模式的支持: 因此我们启动项目,直接使用密码模式即可,访问地址是: http://ip:port/oauth/token参数有五个,分别是: grant_type:密码模式值必须为 passwordusername:用户名password:密码client_id:客户端idclient_secret:客户端秘钥访问示例如下: 密码模式适用于用户高度信任的情况,只有高度信息才能传用户名密码,谨慎使用。 隐藏模式有些web应用是纯前端的应用,需要允许授权服务直接向前端颁发令牌。这种方式没有授权码这个中间步骤,所以称为(授权码)"隐藏式"(implicit)。访问链接示例如下: http://localhost:8028/oauth/authorize?client_id=clientId&response_type=token&redirect_uri=http://localhost:8028/&scope=all可以看到这种方式比授权码模式多了一个scope参数,在使用隐藏模式之前,需要授权服务支持隐藏模式,就需要在授权类型中加上 implicit: 然后将上面的链接输入到浏览器,会出现登录页面,登录成功后,链接中会直接出现令牌: 注意,令牌的位置是 URL 锚点(fragment),而不是查询字符串(querystring),这是因为 OAuth 2.0 允许跳转网址是 HTTP 协议,因此存在"中间人攻击"的风险,而浏览器跳转时,锚点不会发到服务器,就减少了泄漏令牌的风险。 凭证模式最后一种方式是凭证式(client credentials),适用于没有前端的命令行应用,即在命令行下请求令牌。请求地址如下: http://localhost:8028/oauth/token?grant_type=client_credentials&client_id=clientId&client_secret=secret请求前,先在授权类型中配置凭证模式: 请求结果示例: 这种方式给出的令牌,是针对第三方应用的,而不是针对用户的,即有可能多个用户共享同一个令牌。 代码地址: https://gitee.com/blueses/spr... 28 本文由博客一文多发平台 OpenWrite 发布!

October 15, 2019 · 1 min · jiezi

spring学习

spring学习之IOC容器

October 15, 2019 · 1 min · jiezi

springbootplus-CORS跨域处理

CORS跨域处理CORS:Cross-Origin Resource SharingCORS是一种允许当前域(domain)的资源(比如html/js/web service)被其他域(domain)的脚本请求访问的机制,通常由于同域安全策略(the same-origin security policy)浏览器会禁止这种跨域请求。处理方法后台设置允许的请求源/请求头等信息后台配置CorsFilter Bean配置使用 Spring 提供的 CorsFilter 过滤器实现跨域配置io.geekidea.springbootplus.core.config.SpringBootPlusCorsConfig/** * CORS跨域设置 * * @return */@Beanpublic FilterRegistrationBean corsFilter(SpringBootPlusCorsProperties corsProperties) { log.debug("corsProperties:{}", corsProperties); UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); CorsConfiguration corsConfiguration = new CorsConfiguration(); // 跨域配置 corsConfiguration.setAllowedOrigins(corsProperties.getAllowedOrigins()); corsConfiguration.setAllowedHeaders(corsProperties.getAllowedHeaders()); corsConfiguration.setAllowedMethods(corsProperties.getAllowedMethods()); corsConfiguration.setAllowCredentials(corsProperties.isAllowCredentials()); corsConfiguration.setExposedHeaders(corsProperties.getExposedHeaders()); corsConfiguration.setMaxAge(corsConfiguration.getMaxAge()); source.registerCorsConfiguration(corsProperties.getPath(), corsConfiguration); FilterRegistrationBean bean = new FilterRegistrationBean(new CorsFilter(source)); bean.setOrder(Ordered.HIGHEST_PRECEDENCE); bean.setEnabled(corsProperties.isEnable()); return bean;}配置文件配置文件类:io.geekidea.springbootplus.core.properties.SpringBootPlusCorsPropertiesapplication.ymlspring-boot-plus: ############################ CORS start ############################ # CORS跨域配置,默认允许跨域 cors: # 是否启用跨域,默认启用 enable: true # CORS过滤的路径,默认:/** path: /** # 允许访问的源 allowed-origins: '*' # 允许访问的请求头 allowed-headers: x-requested-with,content-type,token # 是否允许发送cookie allow-credentials: true # 允许访问的请求方式 allowed-methods: OPTION,GET,POST # 允许响应的头 exposed-headers: token # 该响应的有效时间默认为30分钟,在有效时间内,浏览器无须为同一请求再次发起预检请求 max-age: 1800 ############################ CORS end ##############################参考HTTP访问控制(CORS)

October 15, 2019 · 1 min · jiezi

springbootplus-XSS跨站脚本攻击处理

XSS跨站脚本攻击处理XSS:Cross Site Scripting跨站脚本攻击(XSS),是目前最普遍的Web应用安全漏洞。这类漏洞能够使得攻击者嵌入恶意脚本代码到正常用户会访问到的页面中,当正常用户访问该页面时,则可导致嵌入的恶意脚本代码的执行,从而达到恶意攻击用户的目的。处理方法将参数中的特殊字符进行转换例如 input参数值,用户输入为:<script>alert(1);</script>处理后为:&lt;script&gt;alert(1);&lt;/script&gt;后台处理pom.xml依赖使用 commons-text包中的StringEscapeUtils.escapeHtml4();方法<dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-text</artifactId> <version>1.8</version></dependency>XssHttpServletRequestWrapper对HttpServletRequest 对象的请求参数进行处理public class XssHttpServletRequestWrapper extends HttpServletRequestWrapper { public XssHttpServletRequestWrapper(HttpServletRequest request) { super(request); } @Override public String getQueryString() { String value = super.getQueryString(); return StringEscapeUtils.escapeHtml4(value); } @Override public String getParameter(String name) { String value = super.getParameter(name); return StringEscapeUtils.escapeHtml4(value); } @Override public String[] getParameterValues(String name) { String[] values = super.getParameterValues(name); if (ArrayUtils.isEmpty(values)) { return values; } int length = values.length; String[] escapeValues = new String[length]; for (int i = 0; i < length; i++) { String value = values[i]; escapeValues[i] = StringEscapeUtils.escapeHtml4(value); } return escapeValues; }}XssFilter使用WebFilter注解,拦截所有请求,过滤请求参数@Slf4j@WebFilter(filterName = "xssFilter", urlPatterns = "/*", asyncSupported = true)public class XssFilter implements Filter { @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { HttpServletRequest request = (HttpServletRequest) servletRequest; XssHttpServletRequestWrapper xssHttpServletRequestWrapper = new XssHttpServletRequestWrapper(request); filterChain.doFilter(xssHttpServletRequestWrapper, servletResponse); }}启动类添加@ServletComponentScan注解扫描使用servlet注解的类,启用 XssFilter@ServletComponentScanJSON字符串请求参数处理实现Jackson反序列化方法,将参数值转义处理public class XssJacksonDeserializer extends JsonDeserializer<String> { @Override public String deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException, JsonProcessingException { return StringEscapeUtils.escapeHtml4(jsonParser.getText()); }}JSON字符串响应结果处理实现Jackson序列化方法,将参数值转义处理@Slf4jpublic class XssJacksonSerializer extends JsonSerializer<String> { @Override public void serialize(String s, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException { jsonGenerator.writeString(StringEscapeUtils.escapeHtml4(s)); }}重点,Jackson配置Xss@Configurationpublic class JacksonConfig implements WebMvcConfigurer { @Override public void extendMessageConverters(List<HttpMessageConverter<?>> converters) { // code... // XSS序列化 simpleModule.addSerializer(String.class, new XssJacksonSerializer()); simpleModule.addDeserializer(String.class, new XssJacksonDeserializer()); // code... }}总结实现字符串转义的核心方法:org.apache.commons.text.StringEscapeUtilsStringEscapeUtils.escapeHtml4();

October 15, 2019 · 1 min · jiezi

并发编程之线程池

一、线程池1、什么是线程池Java中的线程池是运用场景最多的并发框架,几乎所有需要异步或并发执行任务的程序都可以使用线程池。在开发过程中,合理地使用线程池能够带来3个好处。 第一:降低资源消耗。通过重复利用已创建的线程降低线程创建和销毁造成的消耗。第二:提高响应速度。当任务到达时,任务可以不需要等到线程创建就能立即执行。第三:提高线程的可管理性。线程是稀缺资源,如果无限制地创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一分配、调优和监控。但是,要做到合理利用频繁的创建多线程,非常占用CPU,线程过多时造成线程池溢出 2、线程池作用线程池是为突然大量爆发的线程设计的,通过有限的几个固定线程为大量的操作服务,减少了创建和销毁线程所需的时间,从而提高效率。 如果一个线程的时间非常长,就没必要用线程池了(不是不能作长时间操作,而是不宜。),况且我们还不能控制线程池中线程的开始、挂起、和中止。 二、线程池的分类ThreadPoolExecutorExecutor框架的最顶层实现是ThreadPoolExecutor类,Executors工厂类中提供的newScheduledThreadPool、newFixedThreadPool、newCachedThreadPool方法其实也只是ThreadPoolExecutor的构造函数参数不同而已。通过传入不同的参数,就可以构造出适用于不同应用场景下的线程池。 corePoolSize: 核心池的大小。 当有任务来之后,就会创建一个线程去执行任务,当线程池中的线程数目达到corePoolSize后,就会把到达的任务放到缓存队列当中maximumPoolSize: 线程池最大线程数,它表示在线程池中最多能创建多少个线程;keepAliveTime: 表示线程没有任务执行时最多保持多久时间会终止。unit: 参数keepAliveTime的时间单位,有7种取值,在TimeUnit类中有7种静态属性1、线程池四种创建方式Java通过Executors(jdk1.5并发包)提供四种线程池,分别为: newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。newFixedThreadPool 创建一个定长线程池,可控制线程最大并发数,超出的线程会在队列中等待。newScheduledThreadPool 创建一个定长线程池,支持定时及周期性任务执行。newSingleThreadExecutor 创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。2、newCachedThreadPool创建一个可缓存线程池,如果线程池长度超过处理需要,可灵活回收空闲线程,若无可回收,则新建线程。示例代码如下: // 无限大小线程池 jvm自动回收 ExecutorService newCachedThreadPool = Executors.newCachedThreadPool(); for (int i = 0; i < 10; i++) { final int temp = i; newCachedThreadPool.execute(new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (Exception e) { // TODO: handle exception } System.out.println(Thread.currentThread().getName() + ",i:" + temp); } }); }总结: 线程池为无限大,当执行第二个任务时第一个任务已经完成,会复用执行第一个任务的线程,而不用每次新建线程。 ...

October 15, 2019 · 2 min · jiezi

SpringBootSecurity学习22前后端分离版之OAuth20自定义授权码

使用JDBC维护授权码前面的代码中,测试流程第一步都是获取授权码,然后再携带授权码去申请令牌,授权码示例如下: 产生的授权码默认是 6 位的,产生以后并没有做任何管理,可以说是一个临时性的授权码,oauth2也提供了将授权码使用jdbc进行管理的功能,首先在数据库中创建表 oauth_code : code:存储服务端系统生成的code的值(未加密)authentication:存储将AuthorizationRequestHolder.java对象序列化后的二进制数据.然后创建bean JdbcAuthorizationCodeServices: 最后在endpoints 中配置 authorizationCodeServices 方法: 这样配置就完成了,我们重启项目,多申请几个授权码,可以看到数据库中有授权码的保存记录: 修改授权码的长度授权码是6位随机的字符串,可以有人会觉得不安全,希望长一些。我们可以自定义 AuthorizationCodeServices : 在原来的 JdbcAuthorizationCodeServices 中,授权码的生成是其父类 RandomValueAuthorizationCodeServices 生成的,使用的是 RandomValueStringGenerator 类,这个类默认的构造器使用的就是6位长度: 不过类中也包含了自定义长度的构造器: 因此我们可以在自定义的类中自定义长度: 然后将自定义的bean修改为自定义的类: 看一下授权码变长后的效果: 来看一下授权码管理的实现类有哪些: 只有内存模式和jdbc模式,如果想实现redis模式,需要自己去实现接口或者继承 RandomValueAuthorizationCodeServices 类,这里不再演示。 代码地址:https://gitee.com/blueses/spr... 28 本文由博客一文多发平台 OpenWrite 发布!

October 14, 2019 · 1 min · jiezi

Spring-IoC-Spring-IoC-的设计

前言本文为解读Spring IoC 模块源码的开篇介绍。介绍Spring IoC 的相关概念与设计。 What is IoC控制反转(Inversion of Control,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫“依赖查找”(Dependency Lookup)。通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递(注入)给它。-- 摘自维基百科 大型应用中,需要多个类组合工作来实现业务逻辑。这使得每个对象都需要在工作的时候获取到与其合作的对象的引用。 如果这个获取过程要靠自身来实现,那么,代码会变得高度耦合并且难以测试。这对复杂的OOP系统的设计是非常不利的。 在OOP系统中,对象封装了数据和对数据的处理动作,对象的依赖关系体现在了对数据和方法的依赖上。这些依赖关系,可以通过把对象的依赖注入交给框架或IoC容器来完成。 简单来说: 控制:当前对象对其内部成员对象的控制权/获取组装对象的过程反转:上述的过程/控制权,交由专门的第三方组件(容器或者说平台)来管理这种从具体对象手中,交出控制的做法,在解耦代码的同时提高了代码的可测试性。好处具体如下: 不用自己组装,拿来就用。享受单例的好处,效率高,不浪费空间。便于单元测试,方便切换mock组件。便于进行AOP操作,对于使用者是透明的。统一配置,便于修改。Spring IoC在Spring中,IoC容器是实现这个模式的载体,它可以在对象生成或初始化时直接将数据注入到对象中,也可以通过将对象引用注入到对象数据域中的方式来注入对方法调用的依赖。这种依赖注入是可以递归的,对象被逐层注入。 就此而言,这种方案有一种完整而简洁的美感,它把对象的依赖关系有序地建立起来,简化了对象依赖关系的管理,在很大程度上简化了面向对象系统的复杂性。 Spring IoC提供了一个基本的JavaBean容器,通过IoC模式管理依赖关系,并通过依赖注入和AOP切面增强了为JavaBean这样的POJO对象赋予事务管理、生命周期管理等基本功能。 IoC 容器的设计在Spring IOC 容器的设计当中,我们可以看到两个主要的容器系列(根据命名), 实现了BeanFactory接口的简单容器系列,只实现了容器的最基本功能;ApplicationContext应用上下文,容器的高级形态,增加了许多面向框架的特性和对应用环境的适配;对于使用者来说,这些都是容器,是容器的不同表现形式,使用什么样的容器完全取决于使用者的需求。 BeanFactoryBeanFactory接口定义了IoC容器最基本的形式,并且提供了IoC容器所应该遵守的最基本的服务契约,同时,这也是我们使用IoC容器所应遵守的最底层和最基本的编程规范,这些接口定义勾画出了IoC的基本轮廓。 往下的整个继承树是蛮复杂的,你也不需要所有都掌握,就想我们之前提过的怎么学习源码,找核心类。现在来挑几个重点的BeanFactory说一下。避免后面源码分析章节会一脸懵逼。 ListableBeanFactory,这个 Listable 的意思就是,通过这个接口,我们可以获取多个 Bean,大家看源码会发现,最顶层 BeanFactory 接口的方法都是获取单个 Bean 的。ApplicationContext 继承了 HierarchicalBeanFactory,Hierarchical 单词本身已经能说明问题了,也就是说我们可以在应用中起多个 BeanFactory,然后可以将各个 BeanFactory 设置为父子关系。AutowireCapableBeanFactory 这个名字中的 Autowire 大家都非常熟悉,它就是用来自动装配 Bean 用的,但是仔细看上图,ApplicationContext 并没有继承它,不过不用担心,不使用继承,不代表不可以使用组合,如果你看到 ApplicationContext 接口定义中的最后一个方法 getAutowireCapableBeanFactory() 就知道了。DefaultListableBeanFactory,在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。包含了基本IoC容器所具有的重要功能。以上接口,推荐大家去阅读一下他们的JavaDoc,来了解作者是怎么描述的。更为准确。 ApplicationContextApplicationContext是一个高级形态意义的IoC容器,ApplicationContext在BeanFactory的基础上集成了MessageSource, ApplicationEventPublisher, ResourcePatternResolver这几个接口,这些接口为ApplicationContext提供了以下BeanFactory不具备的新特性: 支持不同的信息源。我们看到ApplicationContext扩展了MessageSource接口,这些信息源的扩展功能可以支持国际化的实现,为开发多语言版本的应用提供服务。访问资源。这一特性体现在对ResourceLoader和Resource的支持上,这样我们可以从不同地方得到Bean定义资源。这种抽象使用户程序可以灵活地定义Bean定义信息,尤其是从不同的I/O途径得到Bean定义信息。支持应用事件。继承了接口ApplicationEventPublisher,从而在上下文中引入了事件机制。这些事件和Bean的生命周期的结合为Bean的管理提供了便利。在ApplicationContext中提供的附加服务。这些服务使得基本IoC容器的功能更丰富。因为具备了这些丰富的附加功能,使得ApplicationContext与简单的BeanFactory相比,对它的使用是一种面向框架的使用风格,所以一般建议在开发应用时使用ApplicationContext作为IoC容器的基本形式。BeanDefinition在这些Spring提供的基本IoC容器的接口定义和实现的基础上,Spring通过定义BeanDefinition来管理基于Spring的应用中的各种对象以及它们之间的相互依赖关系。 BeanDefinition 中保存了我们的 Bean 信息,比如这个 Bean 指向的是哪个类、是否是单例的、是否懒加载、这个 Bean 依赖了哪些 Bean 等等。 ...

October 14, 2019 · 1 min · jiezi

SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver

关于Web应用的全局异常处理,上一篇介绍了ControllerAdvice结合@ExceptionHandler的方式来实现web应用的全局异常管理; 本篇博文则带来另外一种并不常见的使用方式,通过实现自定义的HandlerExceptionResolver,来处理异常状态 上篇博文链接: SpringBoot系列教程web篇之全局异常处理本篇原文: SpringBoot系列教程web篇之自定义异常处理HandlerExceptionResolver<!-- more --> I. 环境搭建首先得搭建一个web应用才有可能继续后续的测试,借助SpringBoot搭建一个web应用属于比较简单的活; 创建一个maven项目,pom文件如下 <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.1.7</version> <relativePath/> <!-- lookup parent from update --></parent><properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <spring-cloud.version>Finchley.RELEASE</spring-cloud.version> <java.version>1.8</java.version></properties><dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.45</version> </dependency></dependencies><build> <pluginManagement> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </pluginManagement></build><repositories> <repository> <id>spring-milestones</id> <name>Spring Milestones</name> <url>https://repo.spring.io/milestone</url> <snapshots> <enabled>false</enabled> </snapshots> </repository></repositories>II. HandlerExceptionResolver1. 自定义异常处理HandlerExceptionResolver顾名思义,就是处理异常的类,接口就一个方法,出现异常之后的回调,四个参数中还携带了异常堆栈信息 @NullableModelAndView resolveException( HttpServletRequest request, HttpServletResponse response, @Nullable Object handler, Exception ex);我们自定义异常处理类就比较简单了,实现上面的接口,然后将完整的堆栈返回给调用方 public class SelfExceptionHandler implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { String msg = GlobalExceptionHandler.getThrowableStackInfo(ex); try { response.addHeader("Content-Type", "text/html; charset=UTF-8"); response.getWriter().append("自定义异常处理!!! \n").append(msg).flush(); } catch (Exception e) { e.printStackTrace(); } return null; }}// 堆栈信息打印方法如下public static String getThrowableStackInfo(Throwable e) { ByteArrayOutputStream buf = new ByteArrayOutputStream(); e.printStackTrace(new java.io.PrintWriter(buf, true)); String msg = buf.toString(); try { buf.close(); } catch (Exception t) { return e.getMessage(); } return msg;}仔细观察上面的代码实现,有下面几个点需要注意 ...

October 14, 2019 · 2 min · jiezi

六springboot-简单优雅是实现短信服务

前言上一篇讲了 springboot 集成邮件服务,接下来让我们一起学习下springboot项目中怎么使用短信服务吧。项目中的短信服务基本上上都会用到,简单的注册验证码,消息通知等等都会用到。所以我这个脚手架也打算将短息服务继承进来。短息服务我使用的平台是阿里云的。网上有很多的短信服务提供商。大家可以根据自己的需求进行选择。 准备工作在阿里云上开通服务,以及进行配置。这些阿里云官方文档都写的很清楚,怎么做就不细说的,大家可以参考一下这篇文章:https://blog.csdn.net/qq_2779... 配置好之后你需要获取如下信息: accessKeyId 、accessSecret 这两个是秘钥。在用户AccessKey 中可以找到。 signName 是签名名称。 templateCode 是模版code 添加依赖和配置有了上面的准备工作,我们接下来开始在我们的项目中开发吧。一样的先在pom.xml 文件中加入依赖: <!--阿里云短信服务--> <dependency> <groupId>com.aliyun</groupId> <artifactId>aliyun-java-sdk-core</artifactId> <version>4.1.0</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.61</version> </dependency>这个fastjson 不是必须的,就看你项目中有没有用到啦,没有用到的话,添加第一个依赖就好了。 然后在application.properties文件中加入配置,这四个参数,就是准备工作中我们获取的四个参数。 service 层和邮件服务一样,我们这里没有涉及到数据库,就先直接写service 层,创建SmsService 接口和 SmsServiceImpl 类。 SmsServiceImpl的代码如下: @Service@Slf4jpublic class SmsServiceImpl implements SmsService { @Value("${sms.accessKeyId}") private String accessKeyId; @Value("${sms.accessSecret}") private String accessSecret; @Value("${sms.signName}") private String signName; @Value("${sms.templateCode}") private String templateCode; @Override public boolean sendSms(String iponeNUmber) { DefaultProfile profile = DefaultProfile.getProfile("cn-hangzhou", accessKeyId, accessSecret); IAcsClient client = new DefaultAcsClient(profile); CommonRequest request = new CommonRequest(); request.setMethod(MethodType.POST); request.setDomain("dysmsapi.aliyuncs.com"); request.setVersion("2017-05-25"); request.setAction("SendSms"); request.putQueryParameter("RegionId", "cn-hangzhou"); request.putQueryParameter("PhoneNumbers", iponeNUmber); request.putQueryParameter("SignName", signName); request.putQueryParameter("TemplateCode", templateCode); JSONObject object=new JSONObject(); String randCode=getRandCode(6); log.info("验证码为:{}",randCode); object.put("code",randCode); request.putQueryParameter("TemplateParam", object.toJSONString()); try { CommonResponse response = client.getCommonResponse(request); log.info(response.getData()); return true; } catch (Exception e) { log.error("{}",e); } return false; } /** * 生成随机验证码 * @param digits * @return */ public static String getRandCode(int digits) { StringBuilder sBuilder = new StringBuilder(); Random rd = new Random((new Date()).getTime()); for(int i = 0; i < digits; ++i) { sBuilder.append(String.valueOf(rd.nextInt(9))); } return sBuilder.toString(); }}整体的代码逻辑很简单,首先是通过Value注解将配置文件中配置的那四个参数获取到。 ...

October 14, 2019 · 1 min · jiezi

零侵入式分布式链路日志minboxlogging使用文档v10

MinBox LoggingMinBox Logging是一款分布式、零侵入式的链路日志分析框架,支持SpringCloud微服务架构下配置使用,内部封装了RestTemplate、OpenFeign两种方式透传链路信息。 零侵入式MinBox Logging无需使用注解配置采集链路日志,只需要添加依赖后简单配置Minbox Loggin Admin的相关地址或服务名称即可,每次在收到请求时就会把请求对应的链路日志详细信息自动上报到MinBox Logging Admin进行后续分析、告警通知等。 源码地址https://gitee.com/minbox-projects/minbox-logging I. 概念1. 链路架构图 在一次请求中,经过的每一个服务(MicroService)的链路编号(TraceId)保持一致,通过SpanID、ParentSpanID进行链路上下级关系衔接。 2. 提交使用中遇到的问题遇到你在集成使用过程中遇到了问题,请提交issues,提交地址:创建Issues 3. ApiBoot集成实践示例ApiBoot作为MinBox开源组织的组件最佳集成方案,在第一时间会进行整合minbox-projects开源组织内新发布的组件,MinBox Logging整合实践请访问ApiBoot源码,整合源码详见org.minbox.framework.api.boot.autoconfigure.logging。 II. 配置客户端4. 启用客户端在minbox-logging-spring-context依赖内提供了@EnableLoggingClient注解来启用客户端,配置使用该注解后通过@Import自动注册Logging Client运行时所需要的Bean。 @EnableLoggingClient使用示例如下所示: @SpringBootApplication@EnableLoggingClientpublic class ApiBootLoggingApplication { /** * logger instance */ static Logger logger = LoggerFactory.getLogger(ApiBootLoggingApplication.class); public static void main(String[] args) { SpringApplication.run(ApiBootLoggingApplication.class, args); logger.info("{}服务启动成功.", "ApiBoot Logging Client"); }}5. 透传链路信息每发送一个请求时就会产生一条链路信息,而链路单元(Span)之前的相互访问目前则以http、rpc等方式作为主要占比。 链路信息(Trace)的传递,Logging Client内部提供了提取请求header内的链路信息编号(TraceID)、上级单元编号(Parent SpanID),整条链路都通过这种方式来进行上下级单元关系、链路关系绑定。 5.1. RestTemplate透传链路信息RestTemplate是Spring Web组件提供的请求封装对象,可用于发送指定方式的请求到目标地址,可携带header信息进行传递身份认证信息、请求、响应等信息。 Logging Client则是利用RestTemplate的拦截器将链路(Trace)信息写入请求的header进行传递到下一个单元(Span)。 Logging Client已经提供了RestTemplate拦截器实现类LoggingRestTemplateInterceptor,在LoggingFactoryBean#afterPropertiesSet方法内进行实例化并且已经设置了拦截器,在Logging Client上报请求日志信息时,都是通过LoggingFactoryBean#restTemplate来执行发送请求到Admin,因此只需要实例化LoggingFactoryBean即可。5.2. OpenFeign透传链路信息OpenFeign是SpringCloud为服务之间方法相互调用的实现方式,根据接口配置信息来发送请求并获取响应内容。 Logging Client同样是利用OpenFeign提供的拦截器将链路(Trace)信息写入服务相互调用的请求header,进行传递到下一个服务。 ...

October 14, 2019 · 5 min · jiezi

Github-上热门的-Spring-Boot-项目实战推荐

最近经常被读者问到有没有 Spring Boot 实战项目可以学习,于是,我就去 Github 上找了 10 个我觉得还不错的实战项目。对于这些实战项目,有部分是比较适合 Spring Boot 刚入门的朋友学习的,还有一部分可能要求你对 Spring Boot 相关技术比较熟悉。需要的朋友可以根据个人实际情况进行选择。如果你对 Spring Boot 不太熟悉的话,可以看我最近开源的 springboot-guide:https://github.com/Snailclimb... 入门(还在持续更新中)。 mallGithub地址: https://github.com/macrozheng/mallstar: 22.9k介绍: mall项目是一套电商系统,包括前台商城系统及后台管理系统,基于SpringBoot+MyBatis实现。 前台商城系统包含首页门户、商品推荐、商品搜索、商品展示、购物车、订单流程、会员中心、客户服务、帮助中心等模块。 后台管理系统包含商品管理、订单管理、会员管理、促销管理、运营管理、内容管理、统计报表、财务管理、权限管理、设置等模块。jeecg-bootGithub地址:https://github.com/zhangdaiscott/jeecg-bootstar: 6.4k介绍: 一款基于代码生成器的JAVA快速开发平台!采用最新技术,前后端分离架构:SpringBoot 2.x,Ant Design&Vue,Mybatis,Shiro,JWT。强大的代码生成器让前后端代码一键生成,无需写任何代码,绝对是全栈开发福音!! JeecgBoot的宗旨是提高UI能力的同时,降低前后分离的开发成本,JeecgBoot还独创在线开发模式,No代码概念,一系列在线智能开发:在线配置表单、在线配置报表、在线设计流程等等。eladminGithub地址:https://github.com/elunez/eladminstar: 3.9k介绍: 项目基于 Spring Boot 2.1.0 、 Jpa、 Spring Security、redis、Vue的前后端分离的后台管理系统,项目采用分模块开发方式, 权限控制采用 RBAC,支持数据字典与数据权限管理,支持一键生成前后端代码,支持动态路由。paascloud-masterGithub地址:https://github.com/paascloud/paascloud-masterstar: 5.9k介绍: spring cloud + vue + oAuth2.0全家桶实战,前后端分离模拟商城,完整的购物流程、后端运营平台,可以实现快速搭建企业级微服务项目。支持微信登录等三方登录。vhrGithub地址:https://github.com/lenve/vhrstar: 10.6k介绍: 微人事是一个前后端分离的人力资源管理系统,项目采用SpringBoot+Vue开发。One mallGithub地址:https://github.com/YunaiV/onemallstar: 1.2k介绍: mall 商城,基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。GunsGithub地址:https://github.com/stylefeng/Gunsstar: 2.3k介绍: Guns基于SpringBoot 2,致力于做更简洁的后台管理系统,完美整合springmvc + shiro + mybatis-plus + beetl!Guns项目代码简洁,注释丰富,上手容易,同时Guns包含许多基础模块(用户管理,角色管理,部门管理,字典管理等10个模块),可以直接作为一个后台管理系统的脚手架!SpringCloudGithub地址:https://github.com/YunaiV/onemallstar: 1.2k介绍: mall 商城,基于微服务的思想,构建在 B2C 电商场景下的项目实战。核心技术栈,是 Spring Boot + Dubbo 。未来,会重构成 Spring Cloud Alibaba 。SpringBoot-Shiro-VueGithub地址:https://github.com/Heeexy/SpringBoot-Shiro-Vuestar: 1.8k介绍: 提供一套基于Spring Boot-Shiro-Vue的权限管理思路.前后端都加以控制,做到按钮/接口级别的权限。newbee-mall最近开源的一个商城项目。 ...

October 14, 2019 · 1 min · jiezi

Java中使用HttpPost上传文件以及HttpGet进行API请求包含HttpPost上传文件

一、HttpPost上传文件public static String getSuffix(final MultipartFile file){ if(file == null || file.getSize() == 0){ return null; } String fileName = file.getOriginalFilename(); return fileName.substring(fileName.lastIndexOf(".")+1); }public static JSONObject uploadFile(String urlStr, MultipartFile file, String token) throws IOException { // 后缀 String suffix = getSuffix(file); CloseableHttpClient httpClient = HttpClients.createDefault(); HttpPost uploadFile = new HttpPost(urlStr); uploadFile.setHeader("authorization","Bearer " + token); DecimalFormat df = new DecimalFormat("#.##"); MultipartEntityBuilder builder = MultipartEntityBuilder.create(); // HTTP.PLAIN_TEXT_TYPE,HTTP.UTF_8 builder.addTextBody("name", file.getOriginalFilename(), ContentType.create("text/plain", Consts.UTF_8)); builder.addTextBody("size", df.format((double) file.getSize() / 1024), ContentType.TEXT_PLAIN); builder.addTextBody("suffix", suffix, ContentType.TEXT_PLAIN); // 把文件加到HTTP的post请求中 // String filepath = "/user/test/123.png" // File f = new File(filepath); builder.addBinaryBody( "file", file.getInputStream(), // new FileInputStream(f), ContentType.APPLICATION_OCTET_STREAM, file.getOriginalFilename() ); HttpEntity multipart = builder.build(); uploadFile.setEntity(multipart); CloseableHttpResponse response = httpClient.execute(uploadFile); HttpEntity responseEntity = response.getEntity(); String sResponse= EntityUtils.toString(responseEntity, "UTF-8"); JSONObject jsonObject = JSONObject.parseObject(sResponse); // {"code":1,"data":"7efb19980373dd90f5077576afa7481a","message":""} // {"code":401,"httpStatus":null,"data":"373656a2-baff-423a-93fb-704f51003509","message":"error"} return jsonObject; }二、HttpGet对API进行Get请求两张方式: ...

October 14, 2019 · 2 min · jiezi

SpringBoot系列Spring-Boot使用模板引擎JSP

一、Java模板引擎模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。 在java中,主要的模板引擎有JSP、Thymeleaf、FreeMarker、Velocity等。 虽然随着前后端分离的崛起和流行,模板引擎已遭受到冷落,但不少旧项目依然使用java的模板引擎渲染界面,而偶尔自己写一些练手项目,使用模板引擎也比起前后端分离要来的快速。 本系列会分别讲解SpringBoot怎么集成JSP、Thymeleaf和FreeMarker,至于Velocity,高版本的SpringBoot已经不支持Velocity了,这里也就不进行讲解了。 而这一篇,主要讲解Spring Boot如何集成JSP。 一、Spring Boot集成JSP首先我们要引入依赖,除了核心的web依赖外,就是jstl和tomcat-embed-jasper了。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!--jsp页面使用jstl标签--><dependency> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId></dependency><!-- 使用jsp引擎,springboot内置tomcat没有此依赖 --><dependency> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId></dependency>然后就是配置文件了。主要在spring.mvc.view节点下配置视图目录prefix和文件后缀suffix。 server: port: 10900spring: profiles: active: dev mvc: view: prefix: /templates/ #view视图文件(jsp)文件的存储位置,不能直接放在resources目录下,需要放在webapp目录下 #prefix: /pages/ #或者resources下创建META-INF/resources目录 suffix: .jsp #视图文件后缀这里配置了文件目录为/templates/,需要主要注意的是,jsp无法直接放在resources目录下,默认会去src/main/webapp目录下去寻找,如果没有,则会去src/main/resources/MATE-INF/resources目录去寻找,因此使用JSP,我们就需要将JSP文件放在这两个目录下,而配置的/templates/,即为src/main/webapp/templates或者src/main/resources/MATE-INF/resources/templates。 在src/main下创建webapp/templates目录,分别新建了hello.jsp和mv.jsp文件。 <h3>hello jsp</h3><h3>mv jsp</h3><span>I'm ${name} from mv method</span>这里主要讲解如何集成JSP,不对JSP语法做过多的讲解,所以仅仅提供了两个简单的JSP文件,并简单使用el表达式${name}取值。 接着再创建Controller类路由页面,该类也十分简单,跳转hello页面,以及携带name=imyang跳转mv页面。 @Controller@RequestMapping("index")public class Index { @RequestMapping("/hello") public String hello(){ return "hello"; } @RequestMapping("/mv") public ModelAndView mv(){ ModelAndView mv = new ModelAndView("mv"); mv.addObject("name","yanger"); return mv; }}启动项目,分别访问http://localhost:10900/index/hello和http://localhost:10900/index/mv,可以看到已经可以展示页面信息了。 ...

October 13, 2019 · 1 min · jiezi

SpringBoot系列Spring-Boot使用模板引擎Thymeleaf

一、Java模板引擎模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。 在java中,主要的模板引擎有JSP、Thymeleaf、FreeMarker、Velocity等。 虽然随着前后端分离的崛起和流行,模板引擎已遭受到冷落,但不少旧项目依然使用java的模板引擎渲染界面,而偶尔自己写一些练手项目,使用模板引擎也比起前后端分离要来的快速。 本系列会分别讲解SpringBoot怎么集成JSP、Thymeleaf和FreeMarker,至于Velocity,高版本的SpringBoot已经不支持Velocity了,这里也就不进行讲解了。 而这一篇,主要讲解Spring Boot如何集成Thymeleaf。 一、Spring Boot集成Thymeleaf首先我们要引入依赖,除了核心的web依赖外,只需引入thymeleaf的statrer即可。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!-- thymeleaf模板 --><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId></dependency>然后就是配置文件了。spring.thymeleaf下配置视图文件目录prefix以及文件后缀suffix,如果是本地开发,cache可以设置为false关闭缓存,避免修改文件后需要重新启动服务。 server: port: 10900spring: profiles: active: dev thymeleaf: prefix: classpath:/templates/ check-template-location: true #是否检查模板位置是否存在 suffix: .html encoding: utf-8 #模板编码 servlet: content-type: text/html mode: HTML5 cache: false #禁用缓存,本地开发设置为false,避免修改后重启服务器然后resoucres目录下新建templates目录,分别新建了hello.html和mv.html文件。 <h3>hello thymeleaf</h3><!DOCTYPE html><html lang="en" xmlns:th="http://www.thymeleaf.org"> <h3>mv thymeleaf</h3> <span>I'm <span th:text="${name}"></span> from mv method</span></html>这里主要讲解如何集成Thymeleaf,不对Thymeleaf语法做过多的讲解,所以仅仅提供了两个简单的html文件作为演示。 接着再创建Controller类路由页面,该类十分简单,跳转hello页面,以及携带name=imyang跳转mv页面。 @Controller@RequestMapping("index")public class IndexApi { @RequestMapping("/hello") public String hello(){ return "hello"; } @RequestMapping("/mv") public ModelAndView mv(){ ModelAndView mv = new ModelAndView("mv"); mv.addObject("name","yanger"); return mv; }}启动项目,分别访问http://localhost:10900/index/hello和http://localhost:10900/index/mv,可以看到已经可以展示页面信息了。 ...

October 13, 2019 · 1 min · jiezi

SpringBoot系列Spring-Boot使用模板引擎FreeMarker

一、Java模板引擎模板引擎(这里特指用于Web开发的模板引擎)是为了使用户界面与业务数据(内容)分离而产生的,它可以生成特定格式的文档,用于网站的模板引擎就会生成一个标准的HTML文档。 在java中,主要的模板引擎有JSP、Thymeleaf、FreeMarker、Velocity等。 虽然随着前后端分离的崛起和流行,模板引擎已遭受到冷落,但不少旧项目依然使用java的模板引擎渲染界面,而偶尔自己写一些练手项目,使用模板引擎也比起前后端分离要来的快速。 本系列会分别讲解SpringBoot怎么集成JSP、Thymeleaf和FreeMarker,至于Velocity,高版本的SpringBoot已经不支持Velocity了,这里也就不进行讲解了。 而这一篇,主要讲解Spring Boot如何集成FreeMarker。 一、Spring Boot集成FreeMarker首先我们要引入依赖,除了核心的web依赖外,只需引入freemarker的statrer即可。 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId></dependency><!--freemarker依赖--><dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-freemarker</artifactId></dependency>然后就是配置文件了。主要配置spring.freemarker节点下的视图文件目录template-loader-path以及文件后缀suffix,如果是本地开发,cache可以设置为false关闭缓存,避免修改文件后需要重新启动服务。 server: port: 10900spring: profiles: active: dev freemarker: enabled: true #是否启用freemarker template-loader-path: classpath:/templates/ #设定模板的加载路径,多个以逗号分隔 suffix: .ftl #设定模板的后缀 content-type: text/html check-template-location: true #是否检查模板位置是否存在 cache: false #是否启用模板缓存 charset: UTF-8 #模板编码 #一些常用配置 allow-request-override: false #是否允许HttpServletRequest属性覆盖(隐藏)控制器生成的同名模型属性 allow-session-override: false #是否允许HttpSession属性覆盖(隐藏)控制器生成的同名模型属性 expose-request-attributes: false #设定所有request的属性在merge到模板的时候,是否要都添加到model中 expose-session-attributes: false #是否在merge模板的时候,将HttpSession属性都添加到model中 expose-spring-macro-helpers: true #设定是否以springMacroRequestContext的形式暴露RequestContext给Spring’s macro library使用 prefer-file-system-access: true #是否优先从文件系统加载template,以支持热加载,默认为true然后resoucres目录下新建templates目录,分别新建了hello.ftl和mv.ftl文件。 <h3>hello freemarker</h3><!DOCTYPE html><html lang="en"> <h3>mv freemarker</h3> <span>I'm ${name} from mv method</span></html>这里主要讲解如何集成FreeMarker,不对FreeMarker语法做过多的讲解,所以仅仅提供了两个简单的html文件作为演示。 ...

October 13, 2019 · 1 min · jiezi

并发编程之并发队列

一、并发队列在并发队列上JDK提供了两套实现, 一个是以ConcurrentLinkedQueue为代表的高性能队列非阻塞, 一个是以BlockingQueue接口为代表的阻塞队列,无论哪种都继承自Queue。 1、阻塞队列与非阻塞队阻塞队列与普通队列的区别在于: 阻塞队列: 当队列是空的时,从队列中获取元素的操作将会被阻塞,试图从空的阻塞队列中获取元素的线程将会被阻塞,直到其他的线程往空的队列插入新的元素;当队列是满时,往队列里添加元素的操作会被阻塞。试图往已满的阻塞队列中添加新元素的线程同样也会被阻塞,直到其他的线程使队列重新变得空闲起来,如从队列中移除一个或者多个元素,或者完全清空队列.2、ConcurrentLinkedQequeConcurrentLinkedQueue : 是一个适用于高并发场景下的队列,通过无锁的方式,实现了高并发状态下的高性能,通常ConcurrentLinkedQueue性能好于BlockingQueue.它是一个基于链接节点的无界线程安全队列。该队列的元素遵循先进先出的原则。头是最先加入的,尾是最近加入的,该队列不允许null元素。 // 非阻塞式队列,无界队列ConcurrentLinkedDeque q = new ConcurrentLinkedDeque(); q.offer("张三"); q.offer("李四"); q.offer("王五"); //从头获取元素,删除该元素 System.out.println(q.poll()); //从头获取元素,不刪除该元素 System.out.println(q.peek()); //获取总长度 System.out.println(q.size());3、BlockingQueue阻塞队列(BlockingQueue)是一个支持两个附加操作的队列。这两个附加的操作是: 在队列为空时,获取元素的线程会等待队列变为非空。当队列满时,存储元素的线程会等待队列可用。在Java中,BlockingQueue的接口位于java.util.concurrent 包中(在Java5版本开始提供),由上面介绍的阻塞队列的特性可知,阻塞队列是线程安全的。 1)、ArrayBlockingQueueArrayBlockingQueue是一个有边界的阻塞队列,它的内部实现是一个数组。有边界的意思是它的容量是有限的,我们必须在其初始化的时候指定它的容量大小,容量大小一旦指定就不可改变。 ArrayBlockingQueue是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。下面是一个初始化和使用ArrayBlockingQueue的例子: <String> arrays = new ArrayBlockingQueue<String>(3); arrays.offer("张三"); arrays.offer("李四"); arrays.offer("王五"); arrays.offer("666", 3, TimeUnit.SECONDS); // 队列满了,阻塞3秒后向下执行 System.out.println(arrays.poll()); // 张三 System.out.println(arrays.poll()); // 李四 System.out.println(arrays.poll()); // 王五 System.out.println(arrays.poll(3, TimeUnit.SECONDS)); //队列为空,阻塞3秒后结束2)、LinkedBlockingQueueLinkedBlockingQueue阻塞队列大小的配置是可选的,如果我们初始化时指定一个大小,它就是有边界的,如果不指定,它就是无边界的。说是无边界,其实是采用了默认大小为Integer.MAX_VALUE的容量 。它的内部实现是一个链表。 和ArrayBlockingQueue一样,LinkedBlockingQueue 也是以先进先出的方式存储数据,最新插入的对象是尾部,最新移出的对象是头部。下面是一个初始化和使LinkedBlockingQueue的例子: LinkedBlockingQueue linkedBlockingQueue = new LinkedBlockingQueue(3);linkedBlockingQueue.add("张三");linkedBlockingQueue.add("李四");linkedBlockingQueue.add("李四");System.out.println(linkedBlockingQueue.size()); // 33)、PriorityBlockingQueue(有界,快满时自动扩容,看似无界)PriorityBlockingQueue是一个没有边界的队列,它的排序规则和 java.util.PriorityQueue一样。需要注意,PriorityBlockingQueue中允许插入null对象。 所有插入PriorityBlockingQueue的对象必须实现 java.lang.Comparable接口,队列优先级的排序规则就是按照我们对这个接口的实现来定义的。 另外,我们可以从PriorityBlockingQueue获得一个迭代器Iterator,但这个迭代器并不保证按照优先级顺序进行迭代。 4)、SynchronousQueueSynchronousQueue队列内部仅允许容纳一个元素。当一个线程插入一个元素后会被阻塞,除非这个元素被另一个线程消费。 5)、使用BlockingQueue模拟生产者与消费者class ProducerThread implements Runnable { private BlockingQueue<String> blockingQueue; private AtomicInteger count = new AtomicInteger(); private volatile boolean FLAG = true; public ProducerThread(BlockingQueue<String> blockingQueue) { this.blockingQueue = blockingQueue; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "生产者开始启动...."); while (FLAG) { String data = count.incrementAndGet() + ""; try { boolean offer = blockingQueue.offer(data, 2, TimeUnit.SECONDS); if (offer) { System.out.println(Thread.currentThread().getName() + ",生产队列" + data + "成功.."); } else { System.out.println(Thread.currentThread().getName() + ",生产队列" + data + "失败.."); } Thread.sleep(1000); } catch (Exception e) { } } System.out.println(Thread.currentThread().getName() + ",生产者线程停止..."); } public void stop() { this.FLAG = false; }}class ConsumerThread implements Runnable { private volatile boolean FLAG = true; private BlockingQueue<String> blockingQueue; public ConsumerThread(BlockingQueue<String> blockingQueue) { this.blockingQueue = blockingQueue; } @Override public void run() { System.out.println(Thread.currentThread().getName() + "消费者开始启动...."); while (FLAG) { try { String data = blockingQueue.poll(2, TimeUnit.SECONDS); if (data == null || data == "") { FLAG = false; System.out.println("消费者超过2秒时间未获取到消息."); return; } System.out.println("消费者获取到队列信息成功,data:" + data); } catch (Exception e) { // TODO: handle exception } } }}public class Test0008 { public static void main(String[] args) { BlockingQueue<String> blockingQueue = new LinkedBlockingQueue<>(3); ProducerThread producerThread = new ProducerThread(blockingQueue); ConsumerThread consumerThread = new ConsumerThread(blockingQueue); Thread t1 = new Thread(producerThread); Thread t2 = new Thread(consumerThread); t1.start(); t2.start(); //10秒后 停止线程.. try { Thread.sleep(10*1000); producerThread.stop(); } catch (Exception e) { // TODO: handle exception } }}ArrayDeque, (数组双端队列) PriorityQueue, (优先级队列) ConcurrentLinkedQueue, (基于链表的并发队列) DelayQueue, (延期阻塞队列)(阻塞队列实现了BlockingQueue接口) ArrayBlockingQueue, 常用(基于数组的并发阻塞队列) LinkedBlockingQueue, 常用(基于链表的FIFO阻塞队列) LinkedBlockingDeque, (基于链表的FIFO双端阻塞队列) PriorityBlockingQueue,常用 (带优先级的无界阻塞队列,) SynchronousQueue常用 (并发同步阻塞队列)本文由博客一文多发平台 OpenWrite 发布!

October 13, 2019 · 2 min · jiezi

springdatarediscache-使用及源码走读

预期读者准备使用 spring 的 data-redis-cache 的同学了解 @CacheConfig,@Cacheable,@CachePut,@CacheEvict,@Caching 的使用深入理解 data-redis-cache 的实现原理文章内容说明如何使用 redis-cache自定义 keyGenerator 和过期时间源码解读自带缓存机制的不足快速入门maven 加入 jar 包 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId></dependency>配置 redis spring.redis.host=127.0.0.1开启 redis-cache @EnableCaching@CacheConfig,@Cacheable,@CachePut,@CacheEvict,@Caching 的功能 @Cacheable 会查询缓存中是否有数据,如果有数据则返回,否则执行方法@CachePut 每次都执行方法,并把结果进行缓存@CacheEvict 会删除缓存中的内容@Caching 相当于上面三者的综合,用于配置三者的行为@CacheConfig 配置在类上,用于配置当前类的全局缓存配置详细配置经过上面的配置,就已经可以使用 redis-cache 了,但是还是有些问题需要问自己一下,比如 存储在 redis 的 key 是什么样子的,我可以自定义 key 吗存储到 redis 的 value 是怎么序列化的存储的缓存是多久过期并发访问时,会不会直接穿透从而不断的修改缓存内容过期时间,序列化方式由此类决定 RedisCacheConfiguration,可以覆盖此类达到自定义配置。默认配置为RedisCacheConfiguration.defaultCacheConfig() ,它配置为永不过期,key 为 String 序列化,并加上了一个前缀做为命名空间,value 为 Jdk 序列化,所以你要存储的类必须要实现 java.io.Serializable 。 存储的 key 值的生成由 KeyGenerator 决定,可以在各缓存注解上进行配置,默认使用的是 SimpleKeyGenerator 其存储的 key 方式为 SimpleKey [参数名1,参数名2],如果在同一个命名空间下,有两个同参数名的方法就公出现冲突导致反序列化失败。 并发访问时,确实存在多次访问数据库而没有使用缓存的情况 https://blog.csdn.net/clementad/article/details/52452119 Srping 4.3提供了一个sync参数。是当缓存失效后,为了避免多个请求打到数据库,系统做了一个并发控制优化,同时只有一个线程会去数据库取数据其它线程会被阻塞。自定义存储 key根据上面的说明 ,很有可能会存在存储的 key 一致而导致反序列化失败,所以需要自定义存储 key ,有两种实现办法 ,一种是使用元数据配置 key(简单但难维护),一种是全局设置 keyGenerator ...

October 13, 2019 · 3 min · jiezi

RESTful规范Api最佳设计实践

RESTful是目前比较流行的接口路径设计规范,基于HTTP,一般使用JSON方式定义,通过不同HttpMethod来定义对应接口的资源动作,如:新增(POST)、删除(DELETE)、更新(PUT、PATCH)、查询(GET)等。 路径设计在RESTful设计规范内,每一个接口被认为是一个资源请求,下面我们针对每一种资源类型来看下API路径设计。 路径设计的注意事项如下所示: 资源名使用复数资源名使用名词路径内不带特殊字符避免多级URL新增资源请求方式示例路径POSThttps://api.yuqiyu.com/v1/users新增资源使用POST方式来定义接口,新增资源数据通过RequestBody方式进行传递,如下所示: curl -X POST -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users -d '{ "name": "恒宇少年", "age": 25, "address": "山东济南"}'新增资源后接口应该返回该资源的唯一标识,比如:主键值。 { "id" : 1, "name" : "恒宇少年"}通过返回的唯一标识来操作该资源的其他数据接口。 删除资源请求方式示例路径备注DELETEhttps://api.yuqiyu.com/v1/users批量删除资源DELETEhttps://api.yuqiyu.com/v1/users/{id}删除单个资源删除资源使用DELETE方式来定义接口。 根据主键值删除单个资源 curl -X DELETE https://api.yuqiyu.com/v1/users/1将资源的主键值通过路径的方式传递给接口。 删除多个资源 curl -X DELETE -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users -d '{ "userIds": [ 1, 2, 3 ]}'删除多个资源时通过RequestBody方式进行传递删除条件的数据列表,上面示例中通过资源的主键值集合作为删除条件,当然也可以通过资源的其他元素作为删除的条件,比如:name 更新资源请求方式示例路径备注PUThttps://api.yuqiyu.com/v1/users/{id}更新单个资源的全部元素PATCHhttps://api.yuqiyu.com/v1/users/{id}更新单个资源的部分元素在更新资源数据时使用PUT方式比较多,也是比较常见的,如下所示: curl -X PUT -H 'Content-Type: application/json' https://api.yuqiyu.com/v1/users/1 -d '{ "name": "恒宇少年", "age": 25, "address": "山东济南"}'查询单个资源请求方式示例路径备注GEThttps://api.yuqiyu.com/v1/users/{id}查询单个资源GEThttps://api.yuqiyu.com/v1/use...{name}非唯一标识查询资源唯一标识查询单个资源 curl https://api.yuqiyu.com/v1/users/1通过唯一标识查询资源时,使用路径方式传递标识值,体现出层级关系。 ...

October 9, 2019 · 1 min · jiezi

Spring-Security-实战干货用户信息UserDetails相关入门

1. 前言前一篇介绍了 Spring Security 入门的基础准备。从今天开始我们来一步步窥探它是如何工作的。我们又该如何驾驭它。请多多关注公众号: Felordcn 。本篇将通过 Spring Boot 2.x 来讲解 Spring Security 中的用户主体UserDetails。以及从中找点乐子。 2. Spring Boot 集成 Spring Security这个简直老生常谈了。不过为了照顾大多数还是说一下。集成 Spring Security 只需要引入其对应的 Starter 组件。Spring Security 不仅仅能保护Servlet Web 应用,也可以保护Reactive Web应用,本文我们讲前者。我们只需要在 Spring Security 项目引入以下依赖即可: <dependencies> <!-- actuator 指标监控 非必须 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <!-- spring security starter 必须 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-security</artifactId> </dependency> <!-- spring mvc servlet web 必须 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <!-- lombok 插件 非必须 --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <!-- 测试 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-test</artifactId> <scope>test</scope> </dependency> </dependencies>3. UserDetailsServiceAutoConfiguration启动项目,访问Actuator端点http://localhost:8080/actuator会跳转到一个登录页面http://localhost:8080/login如下: ...

October 9, 2019 · 3 min · jiezi