搜索词>>

spring boot 301���������

耗时0.0020
  • java c++通用DES/AES对称加密算法(包含源代码)

    java c++通用DES加密算法(包含源代码),本来觉得DES、AES这种流行加密算法,使用起来应该很简单。但研究后发现有两个变数:1分块的方式。加密是逐块进行的。2.padding的方式。当数据的位数不及块的大小时,需要填充。<h2>一说明</h2> 本来觉得DES、AES这种流行加密算法,使用起来应该很简单。但研究后发现有两个变数: <ul> <li>分块的方式。加密是逐块进行的。分块方法有:CBC、ECB、CFB……</li> <li>padding的方式。当数据的位数不及块的大小时,需要填充。填充方式有:NoPadding、PKCS5Padding……</li> </ul> 如果加解密端采用不同的分块方式或padding方式,即使都是采用DES/AES算法,同样无法解密成功。上次需要C端和Java端进行密文传输,就跪在这一点上(那时候没时间研究)。<br /> 参考文章:<a href="http://my.oschina.net/u/267094/blog/174035" rel="external nofollow" target="_blank">Java AES算法和openssl配对</a> ,主要通过其了解openssl里比较高级的EVP系列API(其默认padding和java一样都是PKCS5Padding),节约了搜索时间。<br /> 贴代码了,以下代码测试通过了。Java和C++均可以正确解密对方的密文。<br /> 约定:分块和padding采用Java默认的 ECB + PKCS5Padding。 <h2>二 DES加解密</h2> <h3>Java端DES加解密</h3> <pre> <code class="language-java">import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; public class DESUtil { /** 默认算法格式 **/ static String default_transformation = "DES/ECB/PKCS5Padding"; /** * 根据key获取加密器 * * @param keyData * key 8byte * @return 加密器 * @throws Exception * 异常 */ private static Cipher getEncryptCipher(byte[] keyData) throws Exception { if (keyData.length != 8) { throw new Exception("Key Data Must 8 byte!"); } SecretKeySpec key = new SecretKeySpec(keyData, "DES"); // 指定分块ECB模式,填充PKCS5Padding模式 Cipher encryptCipher = Cipher.getInstance(default_transformation); // 初始化加密的容器 encryptCipher.init(Cipher.ENCRYPT_MODE, key); return encryptCipher; } /** * 根据key获取解码器 * * @return 解码器 * @throws Exception * 异常 */ private static Cipher getDecryptCipher(byte[] keyData) throws Exception { if (keyData.length != 8) { throw new Exception("Key Data Must 8 byte!"); } SecretKeySpec key = new SecretKeySpec(keyData, "DES"); Cipher decryptCipher = Cipher.getInstance(default_transformation); // 初始化解密的容器 decryptCipher.init(Cipher.DECRYPT_MODE, key); return decryptCipher; } /** * DES加密 * * @param data * 待加密数据 * @param keyData * key值 * @return 加密后的数据 * @throws Exception * 异常 */ public static byte[] encrypt(byte[] data, byte[] keyData) throws Exception { return getEncryptCipher(keyData).doFinal(data); } /** * DES解密 * * @param data * 加密后的数据 * * @param keyData * key值 * @return 解密数据 * @throws Exception * 异常 */ public static byte[] decrypt(byte[] data, byte[] keyData) throws Exception { return getDecryptCipher(keyData).doFinal(data); } /** * 测试 * * @param args */ public static void main(String[] args) { try { byte[] data = "测试123456".getBytes(); byte[] keyData = "12345678".getBytes(); System.out.println("原文:" + new String(data)); byte[] enData = encrypt(data, keyData); System.out.println("加密后:" + new String(enData)); byte[] deData = decrypt(enData, keyData); System.out.println("解密后:" + new String(deData)); } catch (Exception e) { e.printStackTrace(); } } } </code></pre> <strong><span style="color:#27ae60">说明:Java必须指定填充模式和padding模式。DES/ECB/PKCS5Padding,好与C++同步</span></strong><br /> 测试执行结果: <pre> <code class="language-html">原文:测试123456 加密后:�a���O���?I�]�Y 解密后:测试123456</code></pre> <h3>C++端DES加解密</h3> c++DES加密代码 <pre> <code class="language-cpp">#include <string> #include <iostream> #include <stdio.h> #include <assert.h> #include <openssl/objects.h> #include <openssl/evp.h> // 注意:参数和返回值全部是二进制数据 std::string desEncrypt(const std::string& source, const std::string& key) {     EVP_CIPHER_CTX ctx;     EVP_CIPHER_CTX_init(&ctx);     int ret = EVP_EncryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);     assert(ret == 1);     unsigned char* result = new unsigned char[source.length() + 64]; // 弄个足够大的空间     int len1 = 0;     ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());     assert(ret == 1);     int len2 = 0;     ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);      assert(ret == 1);     std::cout << len1 << "," << len2 << std::endl;     ret = EVP_CIPHER_CTX_cleanup(&ctx);     assert(ret == 1);     std::string res((char*)result, len1+len2);     delete[] result;     return res; } int main() {     std::string key("hellodes", 8);    // 二进制数据,而不是以0结尾的字符串     // 读取文件内容(简单起见认为文件内容<100K)     char buf[1024*100];     FILE* fp = fopen("src.txt", "rb");     int bytes = fread(buf, 1, 1024*100, fp);     fclose(fp);     std::string source(buf, bytes); // 二进制数据     // 加密     std::string enc = desEncrypt(source, key);     std::cout << "desEncrypt:" << source.length() << "->" << enc.length() << std::endl;     // 输出到文件     fp =  fopen("enc.bin", "wb");     fwrite(enc.data(), 1, enc.length(), fp);     fclose(fp); }</code></pre> c++DES解密 <pre> <code class="language-cpp">#include <string> #include <iostream> #include <stdio.h> #include <assert.h> #include <openssl/objects.h> #include <openssl/evp.h> // 注意:参数和返回值全部是二进制数据 std::string desDecrypt(const std::string& ciphertext, const std::string& key) {     EVP_CIPHER_CTX ctx;     EVP_CIPHER_CTX_init(&ctx);     int ret = EVP_DecryptInit_ex(&ctx, EVP_des_ecb(), NULL, (const unsigned char*)key.data(), NULL);     assert(ret == 1);     unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄个足够大的空间     int len1 = 0;     ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());     assert(ret == 1);     int len2 = 0;     ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);      assert(ret == 1);     std::cout << len1 << "," << len2 << std::endl;     ret = EVP_CIPHER_CTX_cleanup(&ctx);     assert(ret == 1);     std::string res((char*)result, len1+len2);     delete[] result;     return res; } int main() {     std::string key("hellodes", 8);    // 二进制数据,而不是以0结尾的字符串     // 读取文件内容(简单起见认为文件内容<100K)     char buf[1024*100];     FILE* fp = fopen("enc.bin", "rb");     int bytes = fread(buf, 1, 1024*100, fp);     fclose(fp);     std::string data(buf, bytes); // 二进制数据     // 加密     std::string dec = desDecrypt(data, key);     std::cout << "desDecrypt:" << data.length() << "->" << dec.length() << std::endl;     // 输出到文件     fp =  fopen("dec.txt", "wb");     fwrite(dec.data(), 1, dec.length(), fp);     fclose(fp); }</code></pre> <br /> 说明:DES、AES加密算法都是针对数据块,Java加解密函数参数使用byte数组。C++用std::string,那是因为这是C++中使用byte数组的最简单方式(std::string可以存储二进制数据,很多人没想到吧),缺点是拷贝内存的次数可能会略多些。如果想要优化拷贝效率,可以使用自己封装的Buffer类来代替std::string。 <h2>三 AES加解密</h2> <h3>Java端AES加解密</h3> <pre> <code class="language-java">import javax.crypto.Cipher; import javax.crypto.spec.SecretKeySpec; /** * DES加解密算法工具类 * * @author xqlee * */ public class AESUtil { /** 默认算法格式 **/ static String default_transformation = "AES/ECB/PKCS5Padding"; /** * 根据key获取加密器 * * @param keyData * key 8byte * @return 加密器 * @throws Exception * 异常 */ private static Cipher getEncryptCipher(byte[] keyData) throws Exception { if (keyData.length != 16) { throw new Exception("Key Data Must 8 byte!"); } SecretKeySpec key = new SecretKeySpec(keyData, "AES"); // 指定分块ECB模式,填充PKCS5Padding模式 Cipher encryptCipher = Cipher.getInstance(default_transformation); // 初始化加密的容器 encryptCipher.init(Cipher.ENCRYPT_MODE, key); return encryptCipher; } /** * 根据key获取解码器 * * @return 解码器 * @throws Exception * 异常 */ private static Cipher getDecryptCipher(byte[] keyData) throws Exception { if (keyData.length != 16) { throw new Exception("Key Data Must 8 byte!"); } SecretKeySpec key = new SecretKeySpec(keyData, "AES"); Cipher decryptCipher = Cipher.getInstance(default_transformation); // 初始化解密的容器 decryptCipher.init(Cipher.DECRYPT_MODE, key); return decryptCipher; } /** * AES加密 * * @param data * 待加密数据 * @param keyData * key值 * @return 加密后的数据 * @throws Exception * 异常 */ public static byte[] encrypt(byte[] data, byte[] keyData) throws Exception { return getEncryptCipher(keyData).doFinal(data); } /** * AES解密 * * @param data * 加密后的数据 * * @param keyData * key值 * @return 解密数据 * @throws Exception * 异常 */ public static byte[] decrypt(byte[] data, byte[] keyData) throws Exception { return getDecryptCipher(keyData).doFinal(data); } /** * 测试 * * @param args */ public static void main(String[] args) { try { byte[] data = "测试123456".getBytes(); byte[] keyData = "1234567887654321".getBytes(); System.out.println("原文:" + new String(data)); byte[] enData = encrypt(data, keyData); System.out.println("加密后:" + new String(enData)); byte[] deData = decrypt(enData, keyData); System.out.println("解密后:" + new String(deData)); } catch (Exception e) { e.printStackTrace(); } } } </code></pre> <h3>C++端AES加解密</h3> 加密: <pre> <code class="language-cpp">#include <string> #include <iostream> #include <stdio.h> #include <assert.h> #include <openssl/objects.h> #include <openssl/evp.h> // 注意:参数和返回值全部是二进制数据 std::string aesEncrypt(const std::string& source, const std::string& key) {     EVP_CIPHER_CTX ctx;     EVP_CIPHER_CTX_init(&ctx);     int ret = EVP_EncryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);     assert(ret == 1);     unsigned char* result = new unsigned char[source.length() + 64]; // 弄个足够大的空间     int len1 = 0;     ret = EVP_EncryptUpdate(&ctx, result, &len1, (const unsigned char*)source.data(), source.length());     assert(ret == 1);     int len2 = 0;     ret = EVP_EncryptFinal_ex(&ctx, result+len1, &len2);      assert(ret == 1);     std::cout << len1 << "," << len2 << std::endl;     ret = EVP_CIPHER_CTX_cleanup(&ctx);     assert(ret == 1);     std::string res((char*)result, len1+len2);     delete[] result;     return res; } int main() {     std::string key("helloaeshelloaes", 16);    // 二进制数据,而不是以0结尾的字符串     // 读取文件内容(简单起见认为文件内容<100K)     char buf[1024*100];     FILE* fp = fopen("src.txt", "rb");     int bytes = fread(buf, 1, 1024*100, fp);     fclose(fp);     std::string source(buf, bytes); // 二进制数据     // 加密     std::string enc = aesEncrypt(source, key);     std::cout << "aesEncrypt:" << source.length() << "->" << enc.length() << std::endl;     // 输出到文件     fp =  fopen("enc.bin", "wb");     fwrite(enc.data(), 1, enc.length(), fp);     fclose(fp); }</code></pre> 解密: <pre> <code class="language-cpp">#include <string> #include <iostream> #include <stdio.h> #include <assert.h> #include <openssl/objects.h> #include <openssl/evp.h> // 注意:参数和返回值全部是二进制数据 std::string aesDecrypt(const std::string& ciphertext, const std::string& key) {     EVP_CIPHER_CTX ctx;     EVP_CIPHER_CTX_init(&ctx);     int ret = EVP_DecryptInit_ex(&ctx, EVP_aes_128_ecb(), NULL, (const unsigned char*)key.data(), NULL);     assert(ret == 1);     unsigned char* result = new unsigned char[ciphertext.length() + 64]; // 弄个足够大的空间     int len1 = 0;     ret = EVP_DecryptUpdate(&ctx, result, &len1, (const unsigned char*)ciphertext.data(), ciphertext.length());     assert(ret == 1);     int len2 = 0;     ret = EVP_DecryptFinal_ex(&ctx, result+len1, &len2);      assert(ret == 1);     std::cout << len1 << "," << len2 << std::endl;     ret = EVP_CIPHER_CTX_cleanup(&ctx);     assert(ret == 1);     std::string res((char*)result, len1+len2);     delete[] result;     return res; } int main() {     std::string key("helloaeshelloaes", 16);    // 二进制数据,而不是以0结尾的字符串     // 读取文件内容(简单起见认为文件内容<100K)     char buf[1024*100];     FILE* fp = fopen("enc.bin", "rb");     int bytes = fread(buf, 1, 1024*100, fp);     fclose(fp);     std::string data(buf, bytes); // 二进制数据     // 加密     std::string dec = aesDecrypt(data, key);     std::cout << "aesDecrypt:" << data.length() << "->" << dec.length() << std::endl;     // 输出到文件     fp =  fopen("dec.txt", "wb");     fwrite(dec.data(), 1, dec.length(), fp);     fclose(fp); }</code></pre>
  • Spring Boot 301 重定向RedirectView

    前言       在写作前,作者也百度查了很多Spring boot 301重定向资料前言       在写作前,作者也百度查了很多Spring boot 301重定向资料。发现结果写的都参差不齐,有大讲源码理论的,也有贴了部分代码的,结果大多数不是那种拿来就可以上手的。很多时候我们查资料是想先快速解决问题,再空闲理解源码。这里综合自己的理解和独立搭建一个简单的项目来实现Spring Boot301跳转,并且支持项目源码下载,到手就能用方便各位快速解决问题。       为何需要301重定向呢?主要的还是做网站的朋友们需要,因为这个影响SEO。Spring Boot 301跳转实现Spring Boot 项目结构写了一个独立的demo来实现Spring Boot 301跳转,方便快速上手,下面是结构图:项目结构图从上面结构来看,项目已经非常简单了,一个controller类,两个页面,搞定。301跳转核心代码 @GetMapping("getA") public String getA(){ return "a"; } @GetMapping("getBToA") public ModelAndView getBToA(){ RedirectView redirectView=new RedirectView("/getA"); redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY); ModelAndView view=new ModelAndView(); view.setView(redirectView); return view; } 在ModelAndView 的view中,设置为重定向专用视图RedirectView,且设置状态码HttpStatus.MOVED_PERMANENTLY(也就是301)小结以上为Spring Boot 项目301跳转的实现,下面提供项目源码下载【网盘访问密码:9987】SpringBoot-redirect-301.zip如果实际项目中因为某些过滤器导致失败(这种情况尽量不要一个mapping多个value,就是项目最后那种),但是情况也紧急,也可以尝试在前一点的反向代理下功夫,比如nginx配置301就很简单,一句话的事儿。如有疑问,欢迎留言讨论。
  • Spring MVC controller简单处理301跳转

    Java spring mvc项目,Spring MVC中怎么实现301跳转spring mvc项目是spring的一个子项目用于处理视图层的请求,类似Struts框架。<br /> 302跳转很简单: <pre> <code class="language-java">@RequestMapping(value = "blog.html") public String rindex() { return "redirect:/blog"; }</code></pre> 按照不重复造轮子的理念,其实301跳转也很简单: <pre> <code class="language-java"> @RequestMapping(value = "blog.html") public RedirectView rindex(HttpServletRequest request) { RedirectView redirectView = new RedirectView("/blog"); redirectView.setStatusCode(HttpStatus.MOVED_PERMANENTLY); return redirectView; }</code></pre> spring mvc 提供了一个专门用于处理各种重定向的view视图。<br /> 跳转结果如下图:<br /> <img alt="301" class="img-thumbnail" src="/assets/upload/blog/thumbnail/2017-03/afadaa21-801c-4dd9-8181-df5cb9e16ad7.png" style="height:255px; width:605px" />
  • apache 24 配置301跳转 解决cdn问题

    apache 24 配置301跳转 解决cdn问题<IfModule rewrite_module> Options +FollowSymLinks RewriteEngine on RewriteCond Host: ^localhost$ #RedirectMatch 301 /static/(.*) http\://cdn\.icdicn\.com/static/$1 RedirectMatch 301 /static/img/(.*) http\://cdn\.icdicn\.com/static/img/$1 RedirectMatch 301 /static/echarts\.min\.js http\://cdn\.icdicn\.com/static/echarts\.min\.js RedirectMatch 301 /static/public-framework\.js http\://cdn\.icdicn\.com/static/public-framework\.js </IfModule>​​​​​​​代码段 小部件
  • typecho 301伪静态配置

    typecho 301伪静态配置typecho 301伪静态配置,步骤一:apache启用加载htaccess文件
  • spring boot mybatis 整合_spring boot mybatis3 事物配置

    引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写sql,无需任何得mapper xml文件)s引言    通过之前spring boot mybatis 整合的讲解: spring boot mybaties整合  (spring boot mybaties 整合 基于Java注解方式写sql,无需任何得mapper xml文件)spring boot mybatis 整合_spring boot与mybaties的使用  (spring boot mybaties 整合 xml mapper方式,也是实际应用最多得方式) 我们对于spring boot mybaties 整合有了一个基础的认知。这里主要正对上面得两篇文章中spring boot mybaties整合讲解得一个扩展学习,事物的配置,整合到spring 的事物控制中。一.环境准备 本博客讲沿用上面的项目进行进一步讲解二.实战编码2.1 spring boot 核心配置文件application.properties#==================DataSource Config Start================== #默认采用Tomcat-jdbc-pool性能和并发最好,注意查看maven依赖中是否有tomcat-jdbc #name #spring.datasource.name=test #url #spring.datasource.url=jdbc:sqlserver://192.168.xxx.xxx;instanceName=sql_03;DatabaseName=edu;integratedSecurity=false spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8 #DriverClass #spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.tomcat.driver-class-name=com.mysql.jdbc.Driver #DB username spring.datasource.tomcat.username=root #DB password spring.datasource.tomcat.password=root #最大连接数量 spring.datasource.tomcat.max-active=150 #最大闲置连接数量 spring.datasource.tomcat.max-idle=20 #最大等待时间 #spring.datasource.tomcat.max-wait=5000 #==================DataSource Config End================== #==================mybaties Config Start================== #ORM Bean Package mybatis.type-aliases-package=com.example.pojo mybatis.mapper-locations=classpath:/mapper/*.xml #打印mybatiesSql语句 logging.level.com.example.mapper=DEBUG #==================mybaties Config End ================== #模板引擎配置缓存为FALSE。开发调试用 spring.thymeleaf.cache=false 这里注意关注数据连接配置和mybaties的xml mapper文件配置。2.2spring boot mybaties 整合 事物关键配置 MyBatiesConfig.javapackage com.example.config; import javax.sql.DataSource; import org.mybatis.spring.annotation.MapperScan; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.datasource.DataSourceTransactionManager; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.annotation.EnableTransactionManagement; /** * mybaties配置扫描mapper路径 * * @author leftso * */ @Configuration @MapperScan(basePackages = { "com.example.mapper" }) /** 注意,这个注解是扫描mapper接口不是xml文件,使用xml模式必须在配置文件中添加xml的配置 **/ @EnableTransactionManagement /** * 启用事物管理 ,在需要事物管理的service类或者方法上使用注解@Transactional **/ public class MyBatiesConfig { @Autowired private DataSource dataSource; /** * 配合注解完成事物管理 * * @return */ @Bean public PlatformTransactionManager annotationDrivenTransactionManager() { return new DataSourceTransactionManager(dataSource); } } 注意必须把当前的数据源配置进入spring的注解事物管理器。否则通过spring框架的注解标签@Transactional是不会有事物作用的。提示:spring boot 2.1.4.RELEASE 版本无需配置PlatformTransactionManager 也能起作用,也就说仅需要一个注解@EnableTransactionManagementSpring boot 2.x (Spring 5.0为基础的情况)无需使用@EnableTransactionManagement注解,spring boot 项目内部已经启用三.事物演示3.1编写测试代码package com.example.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class TransactionalTest { @Autowired private UserMapper userMapper; @Test public void name() { User user=new User("leftso", "男", 1); userMapper.insert(user); int t=1/0; System.out.println(t); } } 执行前查询数据库:​执行测试代码并观察eclipse的控制台和数据库的数据查询结果:​​很明显在报错的情况下,数据还是插入进了数据库。这并不是我们正常业务想要的结果。3.2编辑测试代码,添加spring框架的事物注解package com.example.test; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import org.springframework.transaction.annotation.Transactional; import com.example.mapper.UserMapper; import com.example.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class TransactionalTest { @Autowired private UserMapper userMapper; @Test @Transactional public void name() { User user=new User("测试哈哈", "女", 2); userMapper.insert(user); int t=1/0; System.out.println(t); } } 执行代码并观察eclipse和数据库:​​这次的操作姿势似乎对了。在报错的情况下数据并没有插入数据库。我们仔细观察spring 控制台输出的日志可以发现事物已经在spring的控制下回滚了。​从上图也可以看到回滚的日志
  • Spring Boot 2.0 绑定属性资源文件 Spring Boot 2.0 读取配置文件值 Spring Boot 2.0获取配置文件值

    Spring Boot 2.0 绑定properties属性资源文件 Spring Boot 2.0 读取properties配置文件值 Spring Boot 2.0获取properties配置文件值Spring Boot 2.0 绑定properties属性资源文件 Spring Boot 2.0 读取配置文件值 Spring Boot 2.0获取配置文件值<br /> 自Spring Boot首次发布以来,就可以使用<code>@ConfigurationProperties</code>注释将属性绑定到类。也可以用不同的形式指定属性名称。 例如,person.first-name,person.firstName和PERSON_FIRSTNAME可以互换使用。 我们称这个功能为“宽松绑定”。<br /> <br /> 不幸的是,在Spring Boot 1.x中,“宽松绑定”实际上有点过于宽松。 确切地定义绑定规则是什么以及什么时候可以使用特定格式是相当困难的。 我们也开始获得有关我们的1.x实施难以解决的问题的报告。 例如,在Spring Boot 1.x中,不可能将项目绑定到java.util.Set。<br /> 所以,在Spring Boot 2.0中,我们已经着手重新设计绑定发生的方式。 我们添加了几个新的抽象,并且我们开发了一个全新的绑定API。 在这篇博文中,我们介绍了一些新的类和接口,并描述了为什么添加了它们,它们做了什么,以及如何在自己的代码中使用它们。 <h2>Property Sources</h2> 如果你一直在使用Spring,你可能很熟悉<code>Environment</code>抽象类。这个接口是一个<code>PropertyResolver</code>,它可以让你从一些底层的<code>PropertySource</code>实现中解析属性。<br /> Spring框架为常见的东西提供<code>PropertySource</code>实现,例如系统属性,命令行标志和属性文件。 Spring Boot 会以对大多数应用程序有意义的方式自动配置这些实现(例如,加载application.properties)。 <h2>Configuration Property Sources</h2> Spring Boot 2.0不是直接使用现有的<code>PropertySource</code>接口进行绑定,而是引入了一个新的<code>ConfigurationPropertySource</code>接口。 我们引入了一个新的接口,为我们提供了一个合理的地方来实施放松绑定规则,这些规则以前是活页夹的一部分<br /> 接口的主要API非常简单: code:>>ConfigurationProperty getConfigurationProperty(ConfigurationPropertyName name);<br /> 还有一个<code>IterableConfigurationPropertySource</code>变相的实现了<code>Iterable </code>接口,以便您可以发现源包含的所有名称。<br /> <br /> 通过使用以下代码,可以将Spring <code>Environment</code>调整为<code>ConfigurationPropertySources</code><br /> Iterable sources = ConfigurationPropertySources.get(environment);<br /> 如果您需要它,我们还提供一个简单的<code>MapConfigurationPropertySource</code>实现。 <h2>Configuration Property Names</h2> 事实证明,如果将其限制为一个方向,放宽属性名称的实现更容易实现。 您应该始终使用规范形式访问代码中的属性,而不管它们在基础源中的表示方式如何。<br /> ConfigurationPropertyName类强制执行这些规范的命名规则,这些规则基本归结为“使用小写的kebab-case名称”。<br /> 因此,例如,即使在基础源中使用person.firstName或PERSON_FIRSTNAME,也应该将代码中的属性称为person.first-name。<br /> Origin Support起源支持<br /> 一个Origin是Spring Boot 2.0中引入的一个新接口,可以让您精确定位从某个值加载的确切位置。 有许多Origin实现,可能最有用的是TextResourceOrigin。 这提供了加载的资源的详细信息,以及值的行号和列号。 <pre> <code class="language-html">*************************** APPLICATION FAILED TO START *************************** Description: Binding to target org.springframework.boot.context.properties.bind.BindException: Failed to bind properties under 'person' to scratch.PersonProperties failed: Property: person.name Value: Joe Origin: class path resource [application.properties]:1:13 Reason: length must be between 4 and 2147483647 Action: Update your application's configuration </code></pre>   <h2>Binder API</h2> Binder类(在org.springframework.boot.context.properties.bind中)可以让你获取一个或多个ConfigurationPropertySource并从它们中绑定一些东西。 更准确地说,一个Binder采用一个Bindable并返回一个BindResult。<br /> Bindable<br /> Bindable可能是现有的Java bean,类类型或复杂的ResolvableType(如List )。 这里有些例子 <pre> <code class="language-java">Bindable.ofInstance(existingBean); Bindable.of(Integer.class); Bindable.listOf(Person.class); Bindable.of(resovableType);</code></pre> <br /> Bindable也用于携带注释信息,但通常不需要关心这一点。 <h2>BindResult</h2> <br /> 绑定器不是直接返回一个绑定对象,而是返回一个名为BindResult的东西。 类似于Java 8 Streams返回Optional的方式,BinderResult表示可能绑定或可能未绑定的内容。<br /> <br /> 如果您尝试获取未绑定对象的实际结果,则会引发异常。 我们还提供了一些方法,可以让您在没有任何约束或映射到不同类型时提供替代值 <pre> <code class="language-java">var bound = binder.bind("person.date-of-birth",Bindable.of(LocalDate.class)); // Return LocalDate or throws if not bound bound.get(); // Return a formatted date or "No DOB" bound.map(dateFormatter::format).orElse("No DOB"); // Return LocalDate or throws a custom exception bound.orElseThrow(NoDateOfBirthException::new);</code></pre> <h2>Formatting and Conversion </h2> 大多数ConfigurationPropertySource实现将其基础值作为字符串公开。 当Binder需要将源值转换为其他类型时,它将委托给Spring的ConversionService API。<br /> 如果您需要调整值的转换方式,则可以自由使用格式化程序注释(如@NumberFormat或@DateFormat)。<br /> <br /> Spring Boot 2.0还引入了一些对绑定特别有用的新注释和转换器。 例如,您现在可以将诸如4s之类的值转换为持续时间。 有关详细信息,请参阅org.springframework.boot.convert包。 <h2>BindHandler</h2> 有时候,绑定时可能需要实现额外的逻辑,而BindHandler接口提供了一个很好的方法来实现这一点。 每个BindHandler都可以实现onStart,onSuccess,onFailure和onFinish方法来覆盖行为。<br /> <br /> Spring Boot提供了一些处理程序,主要用于支持现有的@ConfigurationProperties绑定。 例如,ValidationBindHandler可用于对绑定对象应用Validator验证。 <h2>@ConfigurationProperties</h2> 正如本文开始时提到的,@ConfigurationProperties从一开始就一直是Spring Boot的特色。 @ConfigurationProperties很可能仍然是大多数人执行绑定的方式。<br /> <br /> 尽管我们重写了整个绑定过程,但大多数人在升级Spring Boot 1.5应用程序时似乎并没有太多问题。 只要您遵循迁移指南中的建议,您应该会发现事情继续良好。<br /> 如果您在升级应用程序时发现问题,请在GitHub问题跟踪器上以小样本向他们报告,以便重现问题。 <h2>Future Work</h2> 我们计划在Spring Boot 2.1中继续开发Binder,我们希望支持的第一个特性是不可变的配置属性。 如果当前需要getter和setter的配置属性可以使用基于构造函数的绑定,那将是非常好的: <pre> <code class="language-java">public class Person { private final String firstName; private final String lastName; private final LocalDateTime dateOfBirth; public Person(String firstName, String lastName, LocalDateTime dateOfBirth) { this.firstName = firstName; this.lastName = lastName; this.dateOfBirth = dateOfBirth; } // getters }</code></pre> 我们认为构造函数绑定也可以很好地处理Kotlin数据类。<br /> <br /> 小结<br /> 我们希望您能够在Spring Boot 2.0中发现新的绑定功能,并且您会考虑升级现有的Spring Boot应用程序。
  • spring boot 整合redis实现spring的缓存框架

    spring boot 1.5整合redis实现spring的缓存框架,spring boot,redisspring boot 整合redis实现spring的缓存框架<br /> <br /> 1.创建一个spring boot项目<br /> <img alt="项目" class="img-thumbnail" src="/assets/upload/blog/thumbnail/2017-05/87917c2a16564cc08bf8e7670ec943ea.jpg" /><br /> 2.配置pom.xml文件 <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.leftso</groupId> <artifactId>demo-springboot-redis</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>demo-springboot-redis</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.3.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <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> </code></pre> <br /> 3.在spring boot的application配置文件中配置redis的相关信息 <pre> <code>####################Redis 配置信息 ########################## # Redis数据库分片索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=10.1.1.134 # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) spring.redis.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.pool.max-wait=-1 # 连接池中的最大空闲连接 spring.redis.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0 # 连接超时时间(毫秒) spring.redis.timeout=0</code></pre> <br /> 4.配置redis整合入spring的缓存框架 <pre> <code class="language-java">package com.leftso.config; import java.lang.reflect.Method; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.CacheManager; import org.springframework.cache.annotation.CachingConfigurerSupport; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cache.interceptor.KeyGenerator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.data.redis.cache.RedisCacheManager; import org.springframework.data.redis.connection.RedisConnectionFactory; import org.springframework.data.redis.core.RedisTemplate; @Configuration @EnableCaching // 继承CachingConfigurerSupport并重写方法,配合该注解实现spring缓存框架的启用 public class RedisConfig extends CachingConfigurerSupport { /** 载入通过配置文件配置的连接工场 **/ @Autowired RedisConnectionFactory redisConnectionFactory; @SuppressWarnings("rawtypes") @Autowired RedisTemplate redisTemplate; @Bean RedisTemplate<String, Object> objRedisTemplate() { RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>(); redisTemplate.setConnectionFactory(redisConnectionFactory); return redisTemplate; } /* * (non-Javadoc) * * @see org.springframework.cache.annotation.CachingConfigurerSupport# * cacheManager() */ @Bean // 必须添加此注解 @Override public CacheManager cacheManager() { RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate); // 设置缓存过期时间 // redisCacheManager.setDefaultExpiration(60);//秒 return redisCacheManager; } /** * 重写缓存的key生成策略,可根据自身业务需要进行自己的配置生成条件 * * @see org.springframework.cache.annotation.CachingConfigurerSupport# * keyGenerator() */ @Bean // 必须项 @Override public KeyGenerator keyGenerator() { return new KeyGenerator() { @Override public Object generate(Object target, Method method, Object... params) { StringBuilder sb = new StringBuilder(); sb.append(target.getClass().getName()); sb.append(method.getName()); for (Object obj : params) { sb.append(obj.toString()); } return sb.toString(); } }; } } </code></pre> <br /> 5.创建一个测试的pojo和一个测试的controller <pre> <code class="language-java">package com.leftso.pojo; import java.io.Serializable; /** * * <pre> * [Summary] * 测试用简单类型 * [Detail] * TODO * [Author] * XQLEE * [Version] * v1.0 * 2017年5月8日上午9:58:42 * </pre> */ public class Address implements Serializable{ /** * */ private static final long serialVersionUID = 1L; private String id; private String city; private String detail; public Address() { } public Address(String id, String city, String detail) { super(); this.id = id; this.city = city; this.detail = detail; } /** * @return the id */ public String getId() { return id; } /** * @param id * the id to set */ public void setId(String id) { this.id = id; } /** * @return the city */ public String getCity() { return city; } /** * @param city * the city to set */ public void setCity(String city) { this.city = city; } /** * @return the detail */ public String getDetail() { return detail; } /** * @param detail * the detail to set */ public void setDetail(String detail) { this.detail = detail; } } </code></pre>   <pre> <code class="language-java">package com.leftso.controller; import org.springframework.cache.annotation.Cacheable; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import com.leftso.pojo.Address; @RestController public class CacheTestCtrl { @GetMapping("/getAddr") @Cacheable(value = "address-cache") // 设置缓存的名称,也可以通过key属性指定缓存的key,keyGenerator指定key生成策略器(keyGenerator一般推荐在重写CachingConfigurerSupport类里面的方法适合全局指定) public Address getAddress() { Address addr = new Address("001", "重庆市", "渝北区"); System.out.println("==========你看见这句话表示没有缓存时候打印出来的========"); return addr; } } </code></pre> <br /> 6.启动spring boot项目,访问地址localhost:8080/getAddr<br /> 第一次访问将会在控制台中看到以下输出:<br /> <img alt="1" class="img-thumbnail" src="/assets/upload/blog/thumbnail/2017-05/8273e3645def48cfb209a482857ff0ab.jpg" /><br /> <br /> 后面多次刷新都不会进入到该方法中去读取信息,而是通过缓存直接读取了缓存信息。<br /> <br /> <br /> 至此spring boot整合redis 实现基本的缓存已经完成。
  • Spring Boot 2.0 中的Spring Boot Actuator变化

    Spring Boot 2.0,Spring框架的Spring Boot 中的Spring Boot Actuator变化讲解。并且了解如何在Spring Boot 2.0中使用Actuator端点,包括创建自定义端点和使用健康指示器。Spring Boot 2.0 中的Spring Boot Actuator变化
  • spring boot mybatis 整合_spring boot与mybaties的使用

    spring boot mybatis 整合使用讲解介绍,spring boot与mybaties的使用讲解介绍。spring boot mybatis xml mapper方式的入门和通过一个简单的例子带新手入门spring boot整合mybaties基本使用。本文主要讲解在Java编程中,spring boot mybatis xml mapper方式的入门和通过一个简单的例子带新手入门mybaties基本使用。<br /> 项目结构图: <div><img alt="项目结构图" class="img-thumbnail" src="/assets/upload/blog/thumbnail/2017-03/fcb23b60-db2a-4cb6-b44f-663c8ed9c4ea.png" /><br /> 项目结构图<br /> <br /> 代码清单:<br /> <br /> spring boot配置文件application.properties: <pre> <code>#==================DataSource Config Start================== #name #spring.datasource.name=test #url #spring.datasource.url=jdbc:sqlserver://192.168.xxx.xxx;instanceName=sql_03;DatabaseName=edu;integratedSecurity=false spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8 #DriverClass #spring.datasource.driver-class-name=com.microsoft.sqlserver.jdbc.SQLServerDriver spring.datasource.driver-class-name=com.mysql.jdbc.Driver #DB username spring.datasource.username=root #DB password spring.datasource.password=root #==================DataSource Config End================== #==================mybaties Config Start================== #ORM Bean Package mybatis.type-aliases-package=com.leftso.pojo mybatis.mapper-locations=classpath:/mapper/*.xml #==================mybaties Config End ==================</code></pre> </div> <br /> spring boot log简单配置,用于打印sql日志logback-spring.xml: <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE configuration> <configuration> <include resource="org/springframework/boot/logging/logback/base.xml" /> <!-- 打印sql --> <logger name="com.leftso.mapper" level="DEBUG" /> </configuration></code></pre> <br /> <br /> mybaties Java config 配置MyBatiesConfig.java: <pre> <code class="language-java">package com.leftso.config; import org.mybatis.spring.annotation.MapperScan; import org.springframework.context.annotation.Configuration; /** * mybaties配置扫描mapper路径 * * @author leftso * */ @Configuration @MapperScan(basePackages = { "com.leftso.mapper" }) /** 注意,这个注解是扫描mapper接口不是xml文件,使用xml模式必须在配置文件中添加xml的配置 **/ public class MyBatiesConfig { } </code></pre> <br /> 简单测试的用户pojo对象User.java: <pre> <code class="language-java">package com.leftso.pojo; /** * 用户 * * @author leftso * */ public class User { private Long id; private String userName; private int age; private String sex; public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getSex() { return sex; } public void setSex(String sex) { this.sex = sex; } } </code></pre> <br /> User对象的mapper接口:UserMapper.java: <pre> <code class="language-java">package com.leftso.mapper; import java.util.List; import java.util.Map; import org.springframework.stereotype.Component; import com.leftso.pojo.User; @Component("userMapper") public interface UserMapper { // 新增 int add(User user); // 修改 int remove(Long id); // 删除 int update(User user); // 查一个 User findOne(Long id); // 查多个 List<User> findList(Map<String, Object> params); } </code></pre> <br /> mapper对应的xml文件UserMapper.xml: <pre> <code class="language-xml"><?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" > <mapper namespace="com.leftso.mapper.UserMapper"> <!-- 创建一个数据库user表与java中user对象关联的返回映射map --> <resultMap type="com.leftso.pojo.User" id="UserMap"> <id column="id" property="id" jdbcType="NUMERIC" /> <result column="user_name" property="userName" jdbcType="VARCHAR" /> <result column="age" property="age" jdbcType="NUMERIC" /> <result column="sex" property="sex" jdbcType="VARCHAR" /> </resultMap> <!-- 新增 --> <insert id="add" parameterType="com.leftso.pojo.User" useGeneratedKeys="true" keyProperty="id"> insert into t_user <trim prefix="(" suffix=")" suffixOverrides=","><!-- 说明:使用trim的方式组合sql,属性suffixOverrides表示去除末端多余的该属性值(这里是,) --> user_name, age, sex </trim> <trim prefix="values (" suffix=")" suffixOverrides=","> #{name,jdbcType=VARCHAR}, #{age,jdbcType=NUMERIC},<!-- 说明:数字类型都是NUMERIC,没有INT,LONG类型;其他对应类型请参照mybaties官方文档 --> #{sex,jdbcType=VARCHAR} </trim> </insert> <!-- 删除 --> <delete id="remove" parameterType="java.lang.Long"> delete from t_user where id=#{id} </delete> <!-- 修改属性 --> <update id="update" parameterType="com.leftso.pojo.User"> update t_user <set> <trim suffixOverrides=","> <if test="null!=name and ''!=name"> user_name=#{name,jdbcType=VARCHAR}, </if> <if test="null!=age"> age=#{age,jdbcType=NUMERIC}, </if> <if test="null!=sex and ''!= sex"> sex=#{sex,jdbcType=VARCHAR}, </if> </trim> </set> where id=#{id,jdbcType=NUMERIC} </update> <!-- 查询一个 --> <select id="findOne" parameterType="java.lang.Long" resultMap="UserMap"> select * from t_user where id=#{id} </select> <!-- 查询多个 --> <select id="findList" parameterType="java.util.Map" resultMap="UserMap"> select * from t_user where 1=1 <if test="null!=name and '' != name"> and user_name like '%${name}%' </if> <if test=" sex != null and '' != sex"> and sex = #{sex} </if> <if test="age > 0"> and age <![CDATA[ >=]]>#{age} <!-- 说明:在xml里面大于小于大于等于需要逆转 --> </if> </select> </mapper></code></pre> <br /> 单元测试类: <pre> <code class="language-java">package com.leftso; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Test; import org.junit.runner.RunWith; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; import com.leftso.mapper.UserMapper; import com.leftso.pojo.User; @RunWith(SpringRunner.class) @SpringBootTest public class ApplicationTests { Logger log = LoggerFactory.getLogger(getClass()); @Autowired UserMapper userMapper; // @Test public void add() { try { User user = new User(); user.setUserName("测试用户名"); user.setAge(25); user.setSex("男"); log.info("新增前ID:" + user.getId()); userMapper.add(user); log.info("新增后ID:" + user.getId()); } catch (Exception e) { e.printStackTrace(); } } // @Test public void remove() { try { userMapper.remove(2l); } catch (Exception e) { e.printStackTrace(); } } // @Test public void update() { try { User user = new User(); user.setId(3l); user.setUserName("我是修改"); user.setAge(2); userMapper.update(user); } catch (Exception e) { e.printStackTrace(); } } // @Test public void findOne() { try { User user = userMapper.findOne(3l); log.info("\nName:" + user.getUserName() + "\nAge:" + user.getAge() + "\nSex:" + user.getSex()); } catch (Exception e) { e.printStackTrace(); } } @Test public void findList() { try { Map<String, Object> params = new HashMap<String, Object>(); params.put("name", "我"); params.put("age", 3); List<User> list = userMapper.findList(params); for (User user : list) { log.info("\nName:" + user.getUserName() + "\nAge:" + user.getAge() + "\nSex:" + user.getSex()); } } catch (Exception e) { e.printStackTrace(); } } } </code></pre> <br /> 源码下载:<br /> GitHub:<a href="https://github.com/leftso/demo-spring-boot-mybaties3" rel="external nofollow" target="_blank">https://github.com/leftso/demo-spring-boot-mybaties3</a>