首页
归档
留言
友链
广告合作
壁纸
更多
美女主播
Search
1
博瑞GE车机升级/降级
5,610 阅读
2
Mac打印机设置黑白打印
4,952 阅读
3
修改elementUI中el-table树形结构图标
4,896 阅读
4
Mac客户端添加腾讯企业邮箱方法
4,675 阅读
5
intelliJ Idea 2022.2.X破解
4,358 阅读
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
登录
/
注册
Search
标签搜索
Spring Boot
Java
Vue
Spring Cloud
Mac
MyBatis
WordPress
MacOS
asp.net
Element UI
Nacos
.Net
Spring Cloud Alibaba
MySQL
Mybatis-Plus
Typecho
jQuery
Java Script
IntelliJ IDEA
微信小程序
Laughing
累计撰写
627
篇文章
累计收到
1,421
条评论
首页
栏目
后端开发
HarmonyOS Next
Web前端
微信开发
开发辅助
App开发
数据库
随笔日记
页面
归档
留言
友链
广告合作
壁纸
美女主播
搜索到
224
篇与
的结果
2021-07-18
Spring Cloud 使用Feign实现声明式Rest调用
Feign是Netflix开发的声明式、模块化的HTTP客户端。Feign可以帮我们更加便捷、优雅的调用HTTP API。在Spring Cloud中,使用Feign非常简单,创建一个接口,并在接口上添加一些注解,代码就完成了。基本使用添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> 增加配置文件配置文件与之前类似,不再赘诉。server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie修改启动类,增加@EnableFeignClients@SpringBootApplication @EnableFeignClients public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } }增加Feign接口/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 12:52 */ @FeignClient(name = "microservice-provider-user") public interface UserFeignClient { @RequestMapping(value = "{id}",method = RequestMethod.GET) User findById(@PathVariable Long id); } 调用@RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; // @Resource // private RestTemplate restTemplate; @Resource private UserFeignClient userFeignClient; // @Resource // private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo() { return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id) { // return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); return userFeignClient.findById(id); } // @GetMapping("log/microservice-provider-user") // public ServiceInstance userLog() { // return loadBalancerClient.choose("microservice-provider-user"); // } }基于代码Feign自定义配置创建配置类创建Feign的配置类UserFeignConfig,需要注意的是,该类不能添加@Configuration注解,如果添加了@Configuration注解,那么该类不能在主应用程序的@ComponentScan注解扫描包内。/** * 博客:https://lisen.cc * Description:feign配置文件 * * @Author: 香草物语 * DateTime: 2021-07-18 15:39 */ public class UserFeignConfig { /** * 将契约改成Feign原生的契约,这样就可以使用Feign自带的注解了 * @return */ @Bean public Contract feignContract(){ return new Contract.Default(); } }修改Feign接口/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 12:52 */ @FeignClient(name = "microservice-provider-user",configuration = UserFeignConfig.class) public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); }测试@RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; // @Resource // private RestTemplate restTemplate; @Resource private UserFeignClient userFeignClient; // @Resource // private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo() { return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id) { // return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); return userFeignClient.findById(id); } // @GetMapping("log/microservice-provider-user") // public ServiceInstance userLog() { // return loadBalancerClient.choose("microservice-provider-user"); // } }再次访问http://localhost:8081/user/1,查看输出全局配置以上我们是根据服务进行配置的,我们也可以进行全局配置。在启动类中添加@EnableFeignClients注解@SpringBootApplication @EnableFeignClients(defaultConfiguration = UserFeignConfig.class) public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } } 使用属性自定义Feign配置配置指定名称的Feign Client修改配置文件server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie feign: client: config: microservice-provider-user: contract: feign.Contract.Default修改Feign接口@FeignClient(name = "microservice-provider-user") public interface UserFeignClient { /** * 使用Feign自带的注解 @RequestLine * @param id * @return */ @RequestLine("GET /{id}") User findById(@Param("id") Long id); }配置全局Feign Client修改配置文件,将指定名称的Feign Client改成default即可。server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie feign: client: config: default: contract: feign.Contract.DefaultFeign对压缩的支持可以通过以下配置对请求或相应进行压缩。feign: compression: request: enabled: true mime-types: text/xml,application/xml,application/json min-request-size: 2048 response: enabled: true配置Feign日志feign: client: config: default: contract: feign.Contract.Default logging-level: full compression: request: enabled: true mime-types: text/xml,application/xml,application/json min-request-size: 2048 response: enabled: true logging: level: cc.lisen.microserviceconsumermovie.service.UserFeignClient: debug
2021年07月18日
978 阅读
0 评论
0 点赞
2021-07-17
Spring Cloud 服务消费者通过ribbon调用服务提供者
在Spring Cloud Eureka配置集群中,我们介绍了Eureka集群部署的方式。在[pring Cloud Eureka消费者获取服务者信息](https://lisen.cc/back/spring-cloud-eureka-consumer-access-to-service-provider-information.html)中,我们介绍了Eureka集群部署的方式,)中,我们介绍了Eureka服务消费者获取服务提供者信息的方式。这篇文章,我通过ribbon的方式,介绍服务消费者调用服务提供者的方式。服务提供者代码无需改动,我们只基于microservice-consumer-movie增加调用服务提供者的相关代码。基本调用方式修改启动类主要是提供RestTemplate相关Bean,增加@LoadBalanced注解使其具备负载均衡能力。@SpringBootApplication public class MicroserviceConsumerMovieApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceConsumerMovieApplication.class, args); } @Bean @LoadBalanced //基于rest+ribbon的调用方式需要加此注解 public RestTemplate restTemplate(){ return new RestTemplate(); } }调用@RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @Resource private RestTemplate restTemplate; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); } }其他配置其他配置比如依赖、配置文件,请参考之前的文章。测试访问http://localhost:8081/user/1,正确返回用户信息负载均衡算法Ribbon是Netflix发布的负载均衡器,他有助于控制HTTP和TCP客户端的行为。Ribbon默认为我们提供了很多负载均衡算法,比如轮询、随机等,我们也可为Ribbon实现自定义的轮询算法。使用默认轮询算法测试启动两个客户端还是使用前面的用户服务,我们启动两个服务提供者。java -jar microservice-simple-provider-user-0.0.1-SNAPSHOT.jar --server.port=9001 java -jar microservice-simple-provider-user-0.0.1-SNAPSHOT.jar --server.port=9002此时查看Eureka Server可以查看注册的服务,如下编写测试代码,查看服务信息/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-17 22:11 */ @RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @Resource private RestTemplate restTemplate; @Resource private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); } @GetMapping("log/microservice-provider-user") public ServiceInstance userLog(){ return loadBalancerClient.choose("microservice-provider-user"); } }多次访问http://localhost:8081/log/microservice-provider-user,查看输出信息可以看到,服务会依次调用9001、9002端口,也就是通过轮询的方式,依次访问两个服务提供者。Ribbon自定义配置很多场景下,可以根据需要自定义Ribbon的配置,例如修改Ribbon负载均衡器规则等。Spring Cloud允许通过Java代码或属性自定义Ribbon的配置。使用代码自定义Ribbon配置使用Ribbon时一定要注意版本的问题,不然会各种报错。springcloud 2020.0.1 版本之后 删除了eureka中的ribbon,替代ribbon的是spring cloud自带的LoadBalancer,默认使用的是轮询的方式。先说一下我这边使用的组件的版本信息:Spring Boot 2.5.2Spring Cloud 2020.0.3添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>{message type="success" content="无需添加ribbon依赖,eureka-client中已包含"/}配置文件配置文件与上面配置一致,不再单独说明。server: port: 8081 eureka: instance: prefer-ip-address: false client: service-url: defaultZone: http://peer1:8761/eureka,http://peer2:8762/eureka spring: application: name: microservice-consumer-movie自定义轮询规则PeachLoadBalancer.java/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 11:54 */ public class PeachLoadBalancer implements ReactorServiceInstanceLoadBalancer { private static final Log log = LogFactory.getLog(PeachLoadBalancer.class); final AtomicInteger position;//请求的次数 final String serviceId; //服务名称 用于提示报错信息的 private int flag = 0; //自己定义的计数器 //两个参数的构造方法 需要服务名称和实例提供者 这个在方法中传递进来 public PeachLoadBalancer(ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider, String serviceId) { //如果不传人请求次数就自己初始化 反正每次都+1 this(new Random().nextInt(1000), serviceId,serviceInstanceListSupplierProvider); } public PeachLoadBalancer(int seedPosition, String serviceId, ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider) { this.position = new AtomicInteger(seedPosition); this.serviceId = serviceId; this.serviceInstanceListSupplierProvider = serviceInstanceListSupplierProvider; } ObjectProvider<ServiceInstanceListSupplier> serviceInstanceListSupplierProvider; @Override public Mono<Response<ServiceInstance>> choose(Request request) { //从服务提供者中获取到当前request请求中的serviceInstances并且遍历 ServiceInstanceListSupplier supplier = serviceInstanceListSupplierProvider .getIfAvailable(NoopServiceInstanceListSupplier::new); return supplier.get(request).next() .map(serviceInstances -> processInstanceResponse(supplier, serviceInstances)); } private Response<ServiceInstance> processInstanceResponse(ServiceInstanceListSupplier supplier, List<ServiceInstance> serviceInstances) { Response<ServiceInstance> serviceInstanceResponse = getInstanceResponse(serviceInstances); if (supplier instanceof SelectedInstanceCallback && serviceInstanceResponse.hasServer()) { ((SelectedInstanceCallback) supplier).selectedServiceInstance(serviceInstanceResponse.getServer()); } return serviceInstanceResponse; } private Response<ServiceInstance> getInstanceResponse(List<ServiceInstance> instances) { if (instances.isEmpty()) { if (log.isWarnEnabled()) { log.warn("No servers available for service: " + serviceId); } return new EmptyResponse(); } //pos是当前请求的次数 这样可以自定义负载均衡的切换 这个每次+1的操作是复制的 最好是不删 int pos = Math.abs(this.position.incrementAndGet()); if (pos%4==0){ //是4的倍数就切换 flag += 1; } if (flag >= instances.size()){ flag = 0; } //主要的就是这句代码设置负载均衡切换 ServiceInstance instance = instances.get(flag); return new DefaultResponse(instance); } }增加轮询配置/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-18 11:21 */ public class CustomLoadBalancerConfiguration { @Bean ReactorLoadBalancer<ServiceInstance> randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new PeachLoadBalancer(loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class), name); } }配置轮询对应的服务/** * 博客:https://lisen.cc * Description:使用RibbonClient为特定name的Ribbon Client自定义配置 * 使用@RibbonClient的configuration,指定Ribbon的配置类 * * @Author: 香草物语 * DateTime: 2021-07-18 10:16 */ @Configuration @LoadBalancerClient(name = "microservice-provider-user",configuration = CustomLoadBalancerConfiguration.class) public class MyRibbonConfig { @Bean @LoadBalanced //基于rest+ribbon的调用方式需要加此注解 public RestTemplate restTemplate() { return new RestTemplate(); } }测试/** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-17 22:11 */ @RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @Resource private RestTemplate restTemplate; @Resource private LoadBalancerClient loadBalancerClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } @GetMapping("/user/{id}") public User findById(@PathVariable Long id){ return restTemplate.getForObject("http://microservice-provider-user/"+id, User.class); } @GetMapping("log/microservice-provider-user") public ServiceInstance userLog(){ return loadBalancerClient.choose("microservice-provider-user"); } }多次访问http://localhost:8081/log/microservice-provider-user,可以查看端口号,判断是否根据配置的轮询规则分别访问9001和9002端口。
2021年07月17日
1,279 阅读
0 评论
0 点赞
2021-07-17
Spring Cloud Eureka消费者获取服务者信息
在Spring Cloud Eureka配置集群中,我们介绍了Eureka集群部署的方式,这篇文章,我们继续编写服务的提供者及服务的消费者。服务提供者我们创建microservice-simple-provider-user工程,作为服务的提供者。添加依赖<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>添加配置文件spring: application: name: microservice-provider-user eureka: client: service-url: default-zone: http://peer1:8761/eureka,http://peer2:8762/eureka instance: prefer-ip-address: true metadata-map: my-metadata: 自定义元数据创建用户实体User.java@Data public class User { private Long id; private String userName; private String name; private Integer age; private BigDecimal balance; }创建服务提供者@RestController public class UserController { @GetMapping("{id}") public User findById(@PathVariable Long id){ User user = new User(); user.setId(id); user.setName("zhangsan"); user.setName("张三"); user.setAge(31); user.setBalance(new BigDecimal(300)); return user; } }服务消费者我们创建microservice-consumer-movie工程,作为服务的消费者。添加依赖 <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId> </dependency>添加配置文件spring: application: name: microservice-provider-user eureka: client: service-url: default-zone: http://peer1:8761/eureka,http://peer2:8762/eureka instance: prefer-ip-address: true metadata-map: my-metadata: 自定义元数据我们通过metadata-map可以自定义元数据。测试获取服务提供者信息import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.discovery.DiscoveryClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; import java.util.List; /** * 博客:https://lisen.cc * Description: * * @Author: 香草物语 * DateTime: 2021-07-17 22:11 */ @RestController public class MovieController { @Resource private DiscoveryClient discoveryClient; @GetMapping("/user-instance") public List<ServiceInstance> showInfo(){ return discoveryClient.getInstances("microservice-provider-user"); } }我们访问http://localhost:8081/user-instance,可以获取到服务提供者的详细信息[{ "scheme": "http", "host": "192.168.3.48", "port": 8080, "metadata": { "management.port": "8080", "my-metadata": "自定义元数据" }, "secure": false, "uri": "http://192.168.3.48:8080", "serviceId": "MICROSERVICE-PROVIDER-USER", "instanceId": "DESKTOP-PPDVS3E:microservice-provider-user", "instanceInfo": { "instanceId": "DESKTOP-PPDVS3E:microservice-provider-user", "app": "MICROSERVICE-PROVIDER-USER", "appGroupName": null, "ipAddr": "192.168.3.48", "sid": "na", "homePageUrl": "http://192.168.3.48:8080/", "statusPageUrl": "http://192.168.3.48:8080/actuator/info", "healthCheckUrl": "http://192.168.3.48:8080/actuator/health", "secureHealthCheckUrl": null, "vipAddress": "microservice-provider-user", "secureVipAddress": "microservice-provider-user", "countryId": 1, "dataCenterInfo": { "@class": "com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo", "name": "MyOwn" }, "hostName": "192.168.3.48", "status": "UP", "overriddenStatus": "UNKNOWN", "leaseInfo": { "renewalIntervalInSecs": 30, "durationInSecs": 90, "registrationTimestamp": 1626531693579, "lastRenewalTimestamp": 1626531693579, "evictionTimestamp": 0, "serviceUpTimestamp": 1626531693579 }, "isCoordinatingDiscoveryServer": false, "metadata": { "management.port": "8080", "my-metadata": "自定义元数据" }, "lastUpdatedTimestamp": 1626531693579, "lastDirtyTimestamp": 1626531693019, "actionType": "ADDED", "asgName": null } }]
2021年07月17日
1,246 阅读
0 评论
0 点赞
2021-07-17
Spring Cloud Eureka配置集群
Eureka Server可以通过运行多个实例并通过相互注册的方式实现高可用性的部署。Eureka Server实例会彼此通过增量同步的方式同步信息,确保节点数据之间的一致性。配置hosts由于我这里部署到同一个电脑上,因此需要配置hosts,以便进行服务之间的区分。win电脑hosts文件位于:C:\Windows\System32\drivers\etc\hostsMac及Linux电脑hosts文件位于:/etc/hosts在hosts文件末尾增加以下内容:C:\Windows\System32\drivers\etc配置服务提供者分别创建两个Spring Boot应用,分别为microservice-discovery-eureka、microservice-discovery-eureka-ha,代表两个服务提供者工程。添加依赖两个工程,同时添加以下依赖。<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-eureka-server</artifactId> </dependency>修改启动类两个工程的启动类,分别添加@EnableEurekaServer注解。@SpringBootApplication @EnableEurekaServer public class MicroserviceDiscoveryEurekaApplication { public static void main(String[] args) { SpringApplication.run(MicroserviceDiscoveryEurekaApplication.class, args); } }microservice-discovery-eureka配置文件如下spring: application: name: microservice-discovery-eureka server: port: 8761 eureka: instance: hostname: peer1 appname: microservice-discovery-eureka prefer-ip-address: false client: serviceUrl: defaultZone: http://peer2:8762/eureka # false表示不向注册中心注册自己 register-with-eureka: true # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 fetch-registry: truemicroservice-discovery-eureka-ha配置文件如下spring: application: name: microservice-discovery-eureka server: port: 8762 eureka: instance: hostname: peer2 appname: microservice-discovery-eureka client: serviceUrl: defaultZone: http://peer1:8761/eureka # false表示不向注册中心注册自己 register-with-eureka: true # false表示自己端就是注册中心,我的职责就是维护服务实例,并不需要去检索服务 fetch-registry: true可以看到,两个工程通过相互注册的方式,实现集群。配置文件,需要注意以下内容:hostname需要不同appname必须保持一致defaultZone需要使用我们配置的IP映射,而不是使用IP
2021年07月17日
1,010 阅读
0 评论
1 点赞
2021-07-17
spring boot 通过actuator监控应用程序
Spring Boot Actuator提供了很多的监控端点,可以帮助实现对程序内部运行情况监控,比如监控状况、Bean加载情况、环境变量、日志信息、线程信息等。我们可以使用http://{ip}/{port}/actuator/{endpoint}的形式访问这些端点。这些端点大部分是GET方法,当然也有POST方法,比如shutdown。actuator提供的部分端点如下表所示序号端点描述HTTP方法1conditions(老版本为autoconfig)显示自动配置的信息GET2beans显示应用程序所有的Spring beanGET3configprops显示所有@ConfigurationPropertiesGET4threaddump(老版本为dump)显示线程活动快照GET5env显示所有的环境变量GET6health显示应用程序的健康指标,如需显示详情,需要在配置文件配置management.endpoint.health.show-details=alwaysGET7info显示应用信息。GET8mappings显示所有@RequestMapping的路径列表GET9metrics显示应用的度量标准信息GET10shutdown关闭应用,默认关闭,需要在配置文件配置management.endpoint.shutdown=true开启POST11loggers显示日志信息GET12heapdump下载应用程序堆栈信息GET部分配置文件如下,具体内容看注释management: endpoint: beans: enabled: true health: # 显示详细信息 show-details: always shutdown: enabled: true endpoints: web: exposure: # 配置暴漏的端点 include: '*'
2021年07月17日
834 阅读
0 评论
0 点赞
2021-07-17
Spring Boot Yaml文件配置特殊字符
Spring Boot中我们一般使用yaml作为配置文件,yaml都是通过键值对的形式,一般情况下,我们的键值都是字母、数字之类的,但是偶尔的,我们可能使用到特殊字符。比如,有时候我们设置actuator时,可以设置暴漏的网址,我们可能为了方便,就直接设置所有的网址,也就是设置*,但是如果直接设置,启动时会报错。对于这种特殊字符,我们可以通过'将特殊字符包裹起来,如下management: endpoint: beans: enabled: true health: # 显示详细信息 show-details: always endpoints: web: exposure: include: '*'
2021年07月17日
2,145 阅读
0 评论
0 点赞
2021-06-25
Spring Boot国际化
Spring Boot国际化虽然在目前项目上用的比较少,但是总归是要学习一ha(四声)的。添加Resource Bundle首先在resources文件夹下创建i18n文件夹,然后右键i18n创建Resource Bundle添加配置文件添加三个配置文件,分别是login.properties、login_en_US.properties、login_zh_CN.properties,分别对应默认的国际化配置、英文配置、中文配置。login.properties是默认的国际化配置文件,如果找不到国际化信息时,默认会读取login.properties的配置信息。这里为了演示,我只添加了一条信息,即login.password。login.propertieslogin.password=请输入密码login_en_US.propertieslogin.password=please input passwordlogin_zh_CN.propertieslogin.password=请输入密码温馨提示 批量选择配置文件后,右键点击,选择Combine to Resource Bundle,可以批量编辑国际化配置文件修改配置文件修改application.properties,配置国际化文件spring.messages.basename=i18n.login修改加载国际化配置国际化支持,我们默认通过前端传递的lang(放到header),读取对应的国际化信息。@Component public class MyLocaleResolverConfig implements LocaleResolver { private static final String PATH_PARAMETER = "lang"; private static final String PATH_PARAMETER_SPLIT = "_"; @Override public Locale resolveLocale(HttpServletRequest httpServletRequest) { String lang = httpServletRequest.getHeader(PATH_PARAMETER); Locale locale = httpServletRequest.getLocale(); if (!StringUtils.isEmpty(lang)) { String[] split = lang.split(PATH_PARAMETER_SPLIT); locale = new Locale(split[0], split[1]); } return locale; } @Override public void setLocale(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Locale locale) { } @Bean public LocaleResolver localeResolver() { return new MyLocaleResolverConfig(); } }增加读取配置文件工具增加MessageUtils.java,用于读取国际化配置信息@Component public class MessageUtils { @Resource private MessageSource messageSource; private static MessageSource staticMessageSource; @PostConstruct public void init() { MessageUtils.staticMessageSource = messageSource; } /** * 获取消息 * @param messageKey * @return */ public static String getMessage(String messageKey) { Locale locale = LocaleContextHolder.getLocale(); return staticMessageSource.getMessage(messageKey, null, locale); } }增加静态类增加配置文件key对应的静态类,方便读取配置文件。/** * Description: * * @author : laughing * DateTime: 2021-06-25 17:06 */ public class MessageKey { public static String LOGIN_PASSWORD="login.password"; }增加测试controller/** * Description: * * @author : laughing * DateTime: 2021-06-25 17:20 */ @RestController public class MessageController { @GetMapping("message") public String message(){ return MessageUtils.getMessage(MessageKey.LOGIN_PASSWORD); } }通过postman测试请求不带参数当我们不携带lang参数时,系统默认加载login.properties测试中文当我们测试中文时,lang参数携带zh_CN测试英文当我们测试中文时,lang参数携带en_US
2021年06月25日
1,156 阅读
0 评论
2 点赞
2021-06-22
《深入理解Java虚拟机:JVM高级特性与最佳实践》Java经典书籍 PDF下载
{card-default label="介绍" width=""}作为一名Java开发工程师,怎么能对JVM不了解呢,《深入理解Java虚拟机:JVM高级特性与最佳实践》是一部经典的Java书籍,对JVM进行了详细的剖析,此书从核心理论和实际运用这两个角度去探讨java虚拟机,不仅理论分析得透彻,而且书中包含的典型案例和最佳实践也极具现实指导意义,适合所有java工程师、系统调优师和系统架构师阅读。{/card-default}下载地址{cloud title="" type="bd" url="http://pan.baidu.com/s/1mh5wQX6" password="5xkn"/}目录第一部分 走近Java 第1章 走近Java 1.1 概述 1.2 Java技术体系 1.3 Java发展史 1.4 Java虚拟机发展史 1.4.1 Sun Classic Exact VM 1.4.2 Sun HotSpot VM 1.4.3 Sun Mobile-Embedded VM Meta-Circular VM 1.4.4 BEA JRockit IBM J9 VM 1.4.5 Azul VM BEA Liquid VM 1.4.6 Apache Harmony GoogleAndroidDalvik VM 1.4.7 Microsoft JVM及其他 1.5 展望Java技术的未来 1.5.1 模块化 1.5.2 混合语言 1.5.3 多核并行 1.5.4 进一步丰富语法 1.5.5 64位虚拟机 1.6 实战:自己编译JDK 1.6.1 获取JDK源码 1.6.2 系统需求 1.6.3 构建编译环境 1.6.4 进行编译 1.6.5 在IDE工具中进行源码调试 1.7 本章小结 第二部分 自动内存管理机制 第2章 Java内存区域与内存溢出异常 2.1 概述 2.2 运行时数据区域 2.2.1 程序计数器 2.2.2 Java虚拟机栈 2.2.3 本地方法栈 2.2.4 Java堆 2.2.5 方法区 2.2.6 运行时常量池 2.2.7 直接内存 2.3 HotSpot虚拟机对象探秘 2.3.1 对象的创建 2.3.2 对象的内存布局 2.3.3 对象的访问定位 2.4 实战:OutOfMemoryError异常 2.4.1 Java堆溢出 2.4.2 虚拟机栈和本地方法栈溢出 2.4.3 方法区和运行时常量池溢出 2.4.4 本机直接内存溢出 2.5 本章小结 第3章 垃圾收集器与内存分配策略 3.1 概述 3.2 对象已死吗 3.2.1 引用计数算法 3.2.2 可达性分析算法 3.2.3 再谈引用 3.2.4 生存还是死亡 3.2.5 回收方法区 3.3 垃圾收集算法 3.3.1 标记-清除算法 3.3.2 复制算法 3.3.3 标记-整理算法 3.3.4 分代收集算法 3.4 HotSpot的算法实现 3.4.1 枚举根节点 3.4.2 安全点 3.4.3 安全区域 3.5 垃圾收集器 3.5.1 Serial收集器 3.5.2 ParNew收集器 3.5.3 Parallel Scavenge收集器 3.5.4 Serial Old收集器 3.5.5 Parallel Old收集器 3.5.6 CMS收集器 3.5.7 G1收集器 3.5.8 理解GC日志 3.5.9 垃圾收集器参数总结 3.6 内存分配与回收策略 3.6.1 对象优先在Eden分配 3.6.2 大对象直接进入老年代 3.6.3 长期存活的对象将进入老年代 3.6.4 动态对象年龄判定 3.6.5 空间分配担保 3.7 本章小结 第4章 虚拟机性能监控与故障处理工具 4.1 概述 4.2 JDK的命令行工具 4.2.1 jps:虚拟机进程状况工具 4.2.2 jstat:虚拟机统计信息监视工具 4.2.3 jinfo:Java配置信息工具 4.2.4 jmap:Java内存映像工具 4.2.5 jhat:虚拟机堆转储快照分析工具 4.2.6 jstack:Java堆栈跟踪工具 4.2.7 HSDIS:JIT生成代码反汇编 4.3 JDK的可视化工具 4.3.1 JConsole:Java监视与管理控制台 4.3.2 VisualVM:多合一故障处理工具 4.4 本章小结 第5章 调优案例分析与实战 5.1 概述 5.2 案例分析 5.2.1 高性能硬件上的程序部署策略 5.2.2 集群间同步导致的内存溢出 5.2.3 堆外内存导致的溢出错误 5.2.4 外部命令导致系统缓慢 5.2.5 服务器JVM进程崩溃 5.2.6 不恰当数据结构导致内存占用过大 5.2.7 由Windows虚拟内存导致的长时间停顿 5.3 实战:Eclipse运行速度调优 5.3.1 调优前的程序运行状态 5.3.2 升级JDK 1.6的性能变化及兼容问题 5.3.3 编译时间和类加载时间的优化 5.3.4 调整内存设置控制垃圾收集频率 5.3.5 选择收集器降低延迟 5.4 本章小结 第三部分 虚拟机执行子系统 第6章 类文件结构 6.1 概述 6.2 无关性的基石 6.3 Class类文件的结构 6.3.1 魔数与Class文件的版本 6.3.2 常量池 6.3.3 访问标志 6.3.4 类索引、父类索引与接口索引集合 6.3.5 字段表集合 6.3.6 方法表集合 6.3.7 属性表集合 6.4 字节码指令简介 6.4.1 字节码与数据类型 6.4.2 加载和存储指令 6.4.3 运算指令 6.4.4 类型转换指令 6.4.5 对象创建与访问指令 6.4.6 操作数栈管理指令 6.4.7 控制转移指令 6.4.8 方法调用和返回指令 6.4.9 异常处理指令 6.4.10 同步指令 6.5 公有设计和私有实现 6.6 Class文件结构的发展 6.7 本章小结 第7章 虚拟机类加载机制 7.1 概述 7.2 类加载的时机 7.3 类加载的过程 7.3.1 加载 7.3.2 验证 7.3.3 准备 7.3.4 解析 7.3.5 初始化 7.4 类加载器 7.4.1 类与类加载器 7.4.2 双亲委派模型 7.4.3 破坏双亲委派模型 7.5 本章小结 第8章 虚拟机字节码执行引擎 8.1 概述 8.2 运行时栈帧结构 8.2.1 局部变量表 8.2.2 操作数栈 8.2.3 动态连接 8.2.4 方法返回地址 8.2.5 附加信息 8.3 方法调用 8.3.1 解析 8.3.2 分派 8.3.3 动态类型语言支持 8.4 基于栈的字节码解释执行引擎 8.4.1 解释执行 8.4.2 基于栈的指令集与基于寄存器的指令集 8.4.3 基于栈的解释器执行过程 8.5 本章小结 第9章 类加载及执行子系统的案例与实战 9.1 概述 9.2 案例分析 9.2.1 Tomcat:正统的类加载器架构 9.2.2 OSGi:灵活的类加载器架构 9.2.3 字节码生成技术与动态代理的实现 9.2.4 Retrotranslator:跨越JDK版本 9.3 实战:自己动手实现远程执行功能 9.3.1 目标 9.3.2 思路 9.3.3 实现 9.3.4 验证 9.4 本章小结 第四部分 程序编译与代码优化 第10章 早期(编译期)优化 10.1 概述 10.2 Javac编译器 10.2.1 Javac的源码与调试 10.2.2 解析与填充符号表 10.2.3 注解处理器 10.2.4 语义分析与字节码生成 10.3 Java语法糖的味道 10.3.1 泛型与类型擦除 10.3.2 自动装箱、拆箱与遍历循环 10.3.3 条件编译 10.4 实战:插入式注解处理器 10.4.1 实战目标 10.4.2 代码实现 10.4.3 运行与测试 10.4.4 其他应用案例 10.5 本章小结 第11章 晚期(运行期)优化 11.1 概述 11.2 HotSpot虚拟机内的即时编译器 11.2.1 解释器与编译器 11.2.2 编译对象与触发条件 11.2.3 编译过程 11.2.4 查看及分析即时编译结果 11.3 编译优化技术 11.3.1 优化技术概览 11.3.2 公共子表达式消除 11.3.3 数组边界检查消除 11.3.4 方法内联 11.3.5 逃逸分析 11.4 Java与CC++的编译器对比 11.5 本章小结 第五部分 高效并发 第12章 Java内存模型与线程 12.1 概述 12.2 硬件的效率与一致性 12.3 Java内存模型 12.3.1 主内存与工作内存 12.3.2 内存间交互操作 12.3.3 对于volatile型变量的特殊规则 12.3.4 对于long和double型变量的特殊规则 12.3.5 原子性、可见性与有序性 12.3.6 先行发生原则 12.4 Java与线程 12.4.1 线程的实现 12.4.2 Java线程调度 12.4.3 状态转换 12.5 本章小结 第13章 线程安全与锁优化 13.1 概述 13.2 线程安全 13.2.1 Java语言中的线程安全 13.2.2 线程安全的实现方法 13.3 锁优化 13.3.1 自旋锁与自适应自旋 13.3.2 锁消除 13.3.3 锁粗化 13.3.4 轻量级锁 13.3.5 偏向锁 13.4 本章小结
2021年06月22日
1,033 阅读
0 评论
0 点赞
2021-06-22
《图灵程序设计丛书:Java程序员修炼之道》Java经典图书 PDF下载
{card-default label="介绍" width=""}《Java程序员修炼之道》是每个Java工程师都必看,也是必须掌握里面知识的图书,他从Java 7 的新特性到,到Java编程技术,再到JVM,最后跨平台和跨语言做了详细的剖析。适合有一定Java基础并决定提高自身能力的Java工程师学习。{/card-default}下载地址{cloud title="" type="bd" url="http://pan.baidu.com/s/1o8wL5UE" password="lne2"/}目录第一部分 用Java 7做开发第1章 初识Java 7 21.1 语言与平台 21.2 Coin项目:浓缩的都是精华 41.3 Coin项目中的修改 71.3.1 switch语句中的String 71.3.2 更强的数值文本表示法 81.3.3 改善后的异常处理 91.3.4 try-with-resources(TWR) 111.3.5 钻石语法 131.3.6 简化变参方法调用 141.4 小结 15第2章 新I/O 172.1 Java I/O简史 182.1.1 Java 1.0到1.3 192.1.2 在Java 1.4中引入的NIO 192.1.3 下一代I/O-NIO.2 202.2 文件I/O的基石:Path 202.2.1 创建一个Path 232.2.2 从Path中获取信息 232.2.3 移除冗余项 242.2.4 转换Path 252.2.5 NIO.2 Path和Java已有的File类 252.3 处理目录和目录树 262.3.1 在目录中查找文件 262.3.2 遍历目录树 272.4 NIO.2的文件系统I/O 282.4.1 创建和删除文件 292.4.2 文件的复制和移动 302.4.3 文件的属性 312.4.4 快速读写数据 342.4.5 文件修改通知 352.4.6 SeekableByteChannel 372.5 异步 I/O操作 372.5.1 将来式 382.5.2 回调式 402.6 Socket和Channel的整合 412.6.1 NetworkChannel 422.6.2 MulticastChannel 422.7 小结 43第二部分 关键技术第3章 依赖注入 463.1 知识注入:理解IoC和DI 463.1.1 控制反转 473.1.2 依赖注入 483.1.3 转成DI 493.2 Java中标准化的DI 533.2.1 @Inject注解 543.2.2 @Qualifier注解 553.2.3 @Named注解 573.2.4 @Scope注解 573.2.5 @Singleton注解 573.2.6 接口Provider<T> 583.3 Java中的DI参考实现:Guice 3 593.3.1 Guice新手指南 593.3.2 水手绳结:Guice的各种绑定 623.3.3 在Guice中限定注入对象的生命周期 643.4 小结 66第4章 现代并发 674.1 并发理论简介 684.1.1 解释Java线程模型 684.1.2 设计理念 694.1.3 这些原则如何以及为何会相互冲突 704.1.4 系统开销之源 714.1.5 一个事务处理的例子 714.2 块结构并发(Java 5之前) 724.2.1 同步与锁 734.2.2 线程的状态模型 744.2.3 完全同步对象 744.2.4 死锁 764.2.5 为什么是synchronized 774.2.6 关键字volatile 784.2.7 不可变性 794.3 现代并发应用程序的构件 804.3.1 原子类:java.util. concurrent.atomic 814.3.2 线程锁:java.util. concurrent.locks 814.3.3 CountDownLatch 854.3.4 ConcurrentHashMap 864.3.5 CopyOnWriteArrayList 874.3.6 Queue 904.4 控制执行 954.4.1 任务建模 964.4.2 ScheduledThread-PoolExecutor 974.5 分支/合并框架 984.5.1 一个简单的分支/合并例子 994.5.2 ForkJoinTask与工作窃取 1014.5.3 并行问题 1024.6 Java内存模型 1034.7 小结 104第5章 类文件与字节码 1065.1 类加载和类对象 1075.1.1 加载和连接概览 1075.1.2 验证 1085.1.3 Class对象 1085.1.4 类加载器 1095.1.5 示例:依赖注入中的类加载器 1105.2 使用方法句柄 1115.2.1 MethodHandle 1125.2.2 MethodType 1125.2.3 查找方法句柄 1135.2.4 示例:反射、代理与方法句柄 1145.2.5 为什么选择MethodHandle 1165.3 检查类文件 1175.3.1 介绍javap 1175.3.2 方法签名的内部形式 1185.3.3 常量池 1195.4 字节码 1215.4.1 示例:反编译类 1215.4.2 运行时环境 1235.4.3 操作码介绍 1245.4.4 加载和储存操作码 1255.4.5 数学运算操作码 1255.4.6 执行控制操作码 1265.4.7 调用操作码 1265.4.8 平台操作操作码 1275.4.9 操作码的快捷形式 1275.4.10 示例:字符串拼接 1275.5 invokedynamic 1295.5.1 invokedynamic如何工作 1295.5.2 示例:反编译invokedynamic调用 1305.6 小结 132第6章 理解性能调优 1336.1 性能术语 1346.1.1 等待时间 1356.1.2 吞吐量 1356.1.3 利用率 1356.1.4 效率 1356.1.5 容量 1366.1.6 扩展性 1366.1.7 退化 1366.2 务实的性能分析法 1366.2.1 知道你在测量什么 1376.2.2 知道怎么测量 1376.2.3 知道性能目标是什么 1386.2.4 知道什么时候停止优化 1396.2.5 知道高性能的成本 1396.2.6 知道过早优化的危险 1406.3 哪里出错了?我们担心的原因 1406.3.1 过去和未来的性能趋势:摩尔定律 1416.3.2 理解内存延迟层级 1426.3.3 为什么Java性能调优存在困难 1436.4 一个来自于硬件的时间问题 1446.4.1 硬件时钟 1446.4.2 麻烦的nanoTime() 1446.4.3 时间在性能调优中的作用 1466.4.4 案例研究:理解缓存未命中 1476.5 垃圾收集 1496.5.1 基本算法 1496.5.2 标记和清除 1506.5.3 jmap 1526.5.4 与GC相关的JVM参数 1556.5.5 读懂GC日志 1566.5.6 用VisualVM查看内存使用情况 1576.5.7 逸出分析 1596.5.8 并发标记清除 1606.5.9 新的收集器:G1 1616.6 HotSpot的JIT编译 1626.6.1 介绍HotSpot 1636.6.2 内联方法 1646.6.3 动态编译和独占调用 1656.6.4 读懂编译日志 1666.7 小结 167第三部分 JVM上的多语言编程第7章 备选JVM语言 1707.1 Java 太笨?纯粹诽谤 1707.1.1 整合系统 1717.1.2 函数式编程的基本原理 1727.1.3 映射与过滤器 1737.2 语言生态学 1747.2.1 解释型与编译型语言 1757.2.2 动态与静态类型 1757.2.3 命令式与函数式语言 1767.2.4 重新实现的语言与原生语言 1767.3 JVM上的多语言编程 1777.3.1 为什么要用非Java语言 1787.3.2 崭露头角的语言新星 1797.4 如何挑选称心的非Java语言 1807.4.1 低风险 1817.4.2 与Java的交互操作 1817.4.3 良好的工具和测试支持 1827.4.4 备选语言学习难度 1827.4.5 使用备选语言的开发者 1827.5 JVM对备选语言的支持 1837.5.1 非Java语言的运行时环境 1837.5.2 编译器小说 1847.6 小结 185第8章 Groovy:Java的动态伴侣 1878.1 Groovy入门 1898.1.1 编译和运行 1898.1.2 Groovy控制台 1908.2 Groovy 101:语法和语义 1918.2.1 默认导入 1928.2.2 数字处理 1928.2.3 变量、动态与静态类型、作用域 1938.2.4 列表和映射语法 1958.3 与Java的差异——新手陷阱 1968.3.1 可选的分号和返回语句 1968.3.2 可选的参数括号 1978.3.3 访问限定符 1978.3.4 异常处理 1988.3.5 Groovy中的相等 1988.3.6 内部类 1998.4 Java不具备的Groovy特性 1998.4.1 GroovyBean 1998.4.2 安全解引用操作符 2008.4.3 猫王操作符 2018.4.4 增强型字符串 2018.4.5 函数字面值 2028.4.6 内置的集合操作 2038.4.7 对正则表达式的内置支持 2048.4.8 简单的XML处理 2058.5 Groovy与Java的合作 2078.5.1 从Groovy调用Java 2078.5.2 从Java调用Groovy 2088.6 小结 211第9章 Scala:简约而不简单 2129.1 走马观花Scala 2139.1.1 简约的Scala 2139.1.2 match表达式 2159.1.3 case类 2179.1.4 actor 2189.2 Scala能用在我的项目中吗 2199.2.1 Scala和Java的比较 2199.2.2 何时以及如何开始使用Scala 2209.2.3 Scala可能不适合当前项目的迹象 2209.3 让代码因Scala重新绽放 2219.3.1 使用编译器和REPL 2219.3.2 类型推断 2229.3.3 方法 2239.3.4 导入 2249.3.5 循环和控制结构 2249.3.6 Scala的函数式编程 2259.4 Scala对象模型:相似但不同 2269.4.1 一切皆对象 2269.4.2 构造方法 2289.4.3 特质 2289.4.4 单例和伴生对象 2309.4.5 case类和match表达式 2329.4.6 警世寓言 2349.5 数据结构和集合 2359.5.1 List 2359.5.2 Map 2389.5.3 泛型 2399.6 actor介绍 2429.6.1 代码大舞台 2429.6.2 用mailbox跟actor通信 2439.7 小结 244第10章 Clojure:更安全地编程 24510.1 Clojure介绍 24510.1.1 Clojure的Hello World 24610.1.2 REPL入门 24710.1.3 犯了错误 24810.1.4 学着去爱括号 24810.2 寻找Clojure:语法和语义 24910.2.1 特殊形式新手营 24910.2.2 列表、向量、映射和集 25010.2.3 数学运算、相等和其他操作 25210.3 使用函数和循环 25310.3.1 一些简单的Clojure函数 25310.3.2 Clojure中的循环 25510.3.3 读取器宏和派发器 25610.3.4 函数式编程和闭包 25710.4 Clojure序列 25810.4.1 懒序列 26010.4.2 序列和变参函数 26110.5 Clojure与Java的互操作 26210.5.1 从Clojure中调用Java 26210.5.2 Clojure值的Java类型 26310.5.3 使用Clojure代理 26410.5.4 用REPL做探索式编程 26410.5.5 在Java中使用Clojure 26510.6 Clojure并发 26510.6.1 未来式与并行调用 26610.6.2 ref形式 26710.6.3 代理 27110.7 小结 272第四部分 多语种项目开发第11章 测试驱动开发 27411.1 TDD概览 27511.1.1 一个测试用例 27611.1.2 多个测试用例 28011.1.3 深入思考红—绿—重构循环 28211.1.4 JUnit 28311.2 测试替身 28511.2.1 虚设对象 28611.2.2 存根对象 28711.2.3 伪装替身 29011.2.4 模拟对象 29511.3 ScalaTest 29611.4 小结 298第12章 构建和持续集成 30012.1 与Maven 3相遇 30212.2 Maven 3入门项目 30312.3 用Maven 3构建Java7developer项目 30512.3.1 POM 30512.3.2 运行示例 31112.4 Jenkins:满足CI需求 31412.4.1 基础配置 31512.4.2 设置任务 31612.4.3 执行任务 31912.5 Maven和Jenkins代码指标 32012.5.1 安装Jenkins插件 32112.5.2 用Checkstyle保持代码一致性 32212.5.3 用FindBugs设定质量标杆 32312.6 Leiningen 32512.6.1 Leiningen入门 32612.6.2 Leiningen的架构 32612.6.3 Hello Lein 32712.6.4 用Leiningen做面向REPL的TDD 32912.6.5 用Leiningen打包和部署 33012.7 小结 332第13章 快速Web开发 33313.1 Java Web框架的问题 33413.1.1 Java编译为什么不好 33513.1.2 静态类型为什么不好 33513.2 选择Web框架的标准 33613.3 Grails入门 33813.4 Grails快速启动项目 33813.4.1 创建域对象 34013.4.2 测试驱动开发 34013.4.3 域对象持久化 34213.4.4 创建测试数据 34313.4.5 控制器 34313.4.6 GSP/JSP页面 34413.4.7 脚手架和UI的自动化创建 34613.4.8 快速周转的开发 34713.5 深入Grails 34713.5.1 日志 34713.5.2 GORM:对象关系映射 34813.5.3 Grails插件 34913.6 Compojure入门 35013.6.1 Hello Compojure 35013.6.2 Ring和路由 35213.6.3 Hiccup 35313.7 我是不是一只水獭 35313.7.1 项目设置 35413.7.2 核心函数 35713.8 小结 359第14章 保持优秀 36114.1 对Java 8的期待 36114.1.1 lambda表达式(闭包) 36214.1.2 模块化(拼图Jigsaw) 36314.2 多语言编程 36514.2.1 语言的互操作性及元对象协议 36514.2.2 多语言模块化 36614.3 未来的并发趋势 36714.3.1 多核的世界 36714.3.2 运行时管理的并发 36714.4 JVM的新方向 36814.4.1 VM的合并 36814.4.2 协同程序 36914.4.3 元组 37014.5 小结 372
2021年06月22日
846 阅读
0 评论
0 点赞
2021-06-22
《Java编程思想 第四版》Thinking in Java 经典书籍 中文完整版 PDF下载
{card-default label="介绍" width=""}Thinking in java是一本经典的java学习图书,本书共22章,包括操作符、控制执行流程、访问权限控制、复用类、多态、接口、通过异常处理错误、字符串、泛型、数组、容器深入研究、JavaI/O系统、枚举类型、并发以及图形化用户界面等内容。这些丰富的内容,包含了Java语言基础语法以及高级特性,适合各个层次的Java程序员阅读。{/card-default}下载地址百度云盘下载地址(提取密码:isnx):链接:http://pan.baidu.com/s/1skEGhjv目录第1章 对象导论1.1 抽象过程1.2 每个对象都有一个接口1.3 每个对象都提供服务1.4 被隐藏的具体实现1.5 复用具体实现1.6 继承1.6.1 “是一个”(is-a)与“像是一个”(is-like-a)关系1.7 伴随多态的可互换对象1.8 单根继承结构1.9 容器1.9.1 参数化类型(范型)1.10 对象的创建和生命期1.11 异常处理:处理错误1.12 并发编程1.13 Java与Internet1.13.1 Web是什么1.13.2客户端编程1.13.3 服务器端编程1.22 总结第2章 一切都是对象2.1 用引用操纵对象2.2 必须由你创建所有对象2.2.1 存储到什么地方2.2.2 特例:基本类型2.2.3 Java中的数组2.3 永远不需要销毁对象2.3.1 作用域2.3.2 对象的作用域2.4 创建新的数据类型:类2.4.1 域和方法2.4.2 基本成员默认值2.5 方法、参数和返回值2.5.1 参数列表2.6 构建一个Java程序2.6.1 名字可见性2.6.2 运用其他构件2.6.3 static 关键字2.7 你的第一个Java程序编译和运行2.8 注释和嵌入式文档2.8.1 注释文档2.8.2 语法2.8.3 嵌入式HTML2.8.4 一些标签示例2.8.5 文档示例2.9 编码风格2.10 总结2.11 练习第3章 操作符3.1 更简单的打印语句3.2 使用Java操作符3.3 优先级3.4 赋值3.4.1 方法调用中的别名问题3.5 算术操作符3.5.1 一元加、减操作符3.6 自动递增和递减3.7 关系操作符3.7.1 测试对象的等价性3.8 逻辑操作符3.8.1 短路3.9 直接常量3.9.1 指数记数法3.10 按位操作符3.11 移位操作符3.12 三元操作符 if-else3.13 字符串操作符 + 和 +=3.14 使用操作符时常犯的错误3.15 类型转换操作符3.15.1 截尾和舍入3.15.2提升3.16 Java没有“sizeof”3.17 操作符小结3.18 总结第4章 控制执行流程4.1 true和false4.2 if-else4.3 迭代4.3.1 do-while4.3.2 for4.3.3 逗号操作符4.4 Foreach语法4.5 return4.6 break和 continue4.7 臭名昭著的“goto”4.8 switch4.9 总结第5章 初始化与清理5.1 用构造器确保初始化5.2 方法重载5.2.1 区分重载方法5.2.2 涉及基本类型的重载5.2.3 以返回值区分重载方法5.3 缺省构造器5.4 this关键字5.4.1 在构造器中调用构造器5.4.2 static的含义5.5 清理:终结处理和垃圾回收5.5.1 finalize()的用途何在5.5.2 你必须实施清理5.5.3 终结条件5.5.4 垃圾回收器如何工作5.6 成员初始化5.6.1 指定初始化5.7 构造器初始化5.7.1 初始化顺序5.7.2. 静态数据的初始化5.7.3. 显式的静态初始化5.7.4. 非静态实例初始化5.8 数组初始化5.8.1 可变参数列表5.9 枚举类型5.10 总结第6章 访问权限控制第7章 复用类第8章 多态第9章 接口第10章 内部类第11章 持有对象第12章 通过异常处理错误第13章 字符串第14章 类型信息第15章 泛型第16章 数组第17章 容器深入研究第18章 Java I/O系统第19章 枚举类型第20章 注解第21章 并发第22章 图形化用户界面
2021年06月22日
1,444 阅读
0 评论
0 点赞
2021-06-22
Spring Boot汉字转拼音工具
pinyin4j这个java工具包(官方网站:http://pinyin4j.sourceforge.net/)。这个工具包是开源的,对于一般常用汉字,转化正确率还是不错的。添加依赖<!-- 拼音转换工具--> <dependency> <groupId>com.belerweb</groupId> <artifactId>pinyin4j</artifactId> <version>2.5.1</version> </dependency>工具类 public class ChineseCharToEnUtils { /** * 将字符串中的中文转化为拼音,其他字符不变 * * @param inputString * @return */ public static String getPingYin(String inputString) { HanyuPinyinOutputFormat format = new HanyuPinyinOutputFormat(); format.setCaseType(HanyuPinyinCaseType.LOWERCASE); format.setToneType(HanyuPinyinToneType.WITHOUT_TONE); format.setVCharType(HanyuPinyinVCharType.WITH_V); char[] input = inputString.trim().toCharArray(); StringBuilder output = new StringBuilder(); try { for (char c : input) { if (Character.toString(c).matches("[\\u4E00-\\u9FA5]+")) { String[] temp = PinyinHelper.toHanyuPinyinStringArray(c, format); output.append(temp[0]); } else output.append(Character.toString(c)); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } return output.toString(); } /** * 获取汉字串拼音首字母,英文字符不变 * @param chinese 汉字串 * @return 汉语拼音首字母 */ public static String getFirstSpell(String chinese) { StringBuilder pybf = new StringBuilder(); char[] arr = chinese.toCharArray(); HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); for (char c : arr) { if (c > 128) { try { String[] temp = PinyinHelper.toHanyuPinyinStringArray(c, defaultFormat); if (temp != null) { pybf.append(temp[0].charAt(0)); } } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } } else { pybf.append(c); } } return pybf.toString().replaceAll("\\W", "").trim(); } /** * 获取汉字串拼音,英文字符不变 * @param chinese 汉字串 * @return 汉语拼音 */ public static String getFullSpell(String chinese) { StringBuilder pybf = new StringBuilder(); char[] arr = chinese.toCharArray(); HanyuPinyinOutputFormat defaultFormat = new HanyuPinyinOutputFormat(); defaultFormat.setCaseType(HanyuPinyinCaseType.LOWERCASE); defaultFormat.setToneType(HanyuPinyinToneType.WITHOUT_TONE); for (char c : arr) { if (c > 128) { try { pybf.append(PinyinHelper.toHanyuPinyinStringArray(c, defaultFormat)[0]); } catch (BadHanyuPinyinOutputFormatCombination e) { e.printStackTrace(); } } else { pybf.append(c); } } return pybf.toString(); } }
2021年06月22日
986 阅读
0 评论
0 点赞
2021-06-11
Spring Boot 使用mybatis-plus逻辑删除选装件
试想一下我们的系统,我们所有的表都有五个字段,分别是创建者(create_by)、创建时间(create_time)、修改者(update_by)、修改时间(update_time)、删除标志(del_flag), 同时通过mybatis拦截器,自动注入创建人、创建时间、修改人、修改时间。目前在数据库新增,能自动注入创建人、创建时间,修改时,能自动注入修改人、 修改时间。如果我们调用mybatis-plus提供的删除方法(deleteById),既然是逻辑删除,我们设想的肯定是此时能够自动注入修改人、修改时间,但是,实际测试你会发现,此时sql并没有注入修改人、修改时间。为了解决这个问题,mybatis-plus提供了一个LogicDeleteByIdWithFill的逻辑删除组装件。通过此方法,我们在进行逻辑删除时,便能自动注入修改人、修改时间。继承DefaultSqlInjector/** * Description:官方逻辑删除选装件 * * @author : laughing * DateTime: 2021-06-11 15:21 */ @Component public class MySqlInjector extends DefaultSqlInjector { @Override public List<AbstractMethod> getMethodList(Class<?> mapperClass) { List<AbstractMethod> methodList = super.getMethodList(mapperClass); //2.官方选装件,逻辑删除时,自动填充其他字段(例如逻辑删除时,自动填充删除人是谁,什么时候删除的) methodList.add(new LogicDeleteByIdWithFill()); return methodList; } }配置逻辑删除插件/** * Mybatis-Plus配置 * * @author laughing */ @Configuration @EnableTransactionManagement public class MyBatisPlusConfig { @javax.annotation.Resource private Environment env; static final String DEFAULT_RESOURCE_PATTERN = "**/*.class"; private final Logger logger = LoggerFactory.getLogger(MyBatisPlusConfig.class); @Bean public MybatisPlusInterceptor mybatisPlusInterceptor() { MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor(); // 如果用了分页插件注意先 add TenantLineInnerInterceptor 再 add PaginationInnerInterceptor // 用了分页插件必须设置 MybatisConfiguration#useDeprecatedExecutor = false // interceptor.addInnerInterceptor(new PaginationInnerInterceptor()); interceptor.addInnerInterceptor(new TenantLineInnerInterceptor( new TenantLineHandler() { // manager_id = 1088248166370832385 // 获取租户 ID 值表达式,只支持单个 ID 值 @Override public Expression getTenantId() { return new StringValue(SecurityUtils.getOrgCode()); } // 这是 default 方法,默认返回 false 表示所有表都需要拼多租户条件, // 这里设置 role表不需要该条件 @Override public boolean ignoreTable(String tableName) { logger.info("orgCode=======" + SecurityUtils.getOrgCode()); return StringUtils.isEmpty(SecurityUtils.getOrgCode()) || WisdomAgricultureConfig.getFilterTableList().stream().anyMatch(e -> e.equalsIgnoreCase(tableName)); } @Override public String getTenantIdColumn() { return "org_code"; } })); interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); return interceptor; } @Bean public ConfigurationCustomizer configurationCustomizer() { return configuration -> configuration.setUseDeprecatedExecutor(false); } public static String setTypeAliasesPackage(String typeAliasesPackage) { ResourcePatternResolver resolver = (ResourcePatternResolver) new PathMatchingResourcePatternResolver(); MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory(resolver); List<String> allResult = new ArrayList<String>(); try { for (String aliasesPackage : typeAliasesPackage.split(",")) { List<String> result = new ArrayList<String>(); aliasesPackage = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(aliasesPackage.trim()) + "/" + DEFAULT_RESOURCE_PATTERN; Resource[] resources = resolver.getResources(aliasesPackage); if (resources != null && resources.length > 0) { MetadataReader metadataReader = null; for (Resource resource : resources) { if (resource.isReadable()) { metadataReader = metadataReaderFactory.getMetadataReader(resource); try { result.add(Class.forName(metadataReader.getClassMetadata().getClassName()).getPackage().getName()); } catch (ClassNotFoundException e) { e.printStackTrace(); } } } } if (result.size() > 0) { HashSet<String> hashResult = new HashSet<String>(result); allResult.addAll(hashResult); } } if (allResult.size() > 0) { typeAliasesPackage = String.join(",", (String[]) allResult.toArray(new String[0])); } else { throw new RuntimeException("mybatis typeAliasesPackage 路径扫描错误,参数typeAliasesPackage:" + typeAliasesPackage + "未找到任何包"); } } catch (IOException e) { e.printStackTrace(); } return typeAliasesPackage; } public Resource[] resolveMapperLocations(String[] mapperLocations) { ResourcePatternResolver resourceResolver = new PathMatchingResourcePatternResolver(); List<Resource> resources = new ArrayList<Resource>(); if (mapperLocations != null) { for (String mapperLocation : mapperLocations) { try { Resource[] mappers = resourceResolver.getResources(mapperLocation); resources.addAll(Arrays.asList(mappers)); } catch (IOException e) { // ignore } } } return resources.toArray(new Resource[resources.size()]); } @Bean public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception { String typeAliasesPackage = env.getProperty("mybatis-plus.typeAliasesPackage"); String mapperLocations = env.getProperty("mybatis-plus.mapperLocations"); String configLocation = env.getProperty("mybatis-plus.configLocation"); typeAliasesPackage = setTypeAliasesPackage(typeAliasesPackage); VFS.addImplClass(SpringBootVFS.class); final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean(); sessionFactory.setDataSource(dataSource); sessionFactory.setTypeAliasesPackage(typeAliasesPackage); sessionFactory.setMapperLocations(resolveMapperLocations(StringUtils.split(mapperLocations, ","))); sessionFactory.setConfigLocation(new DefaultResourceLoader().getResource(configLocation)); sessionFactory.setPlugins(mybatisSqlInterceptor(), mybatisPlusInterceptor()); sessionFactory.setGlobalConfig(globalConfig()); return sessionFactory.getObject(); } @Bean public MybatisSqlInterceptor mybatisSqlInterceptor() { MybatisSqlInterceptor mybatisSqlInterceptor = new MybatisSqlInterceptor(); Properties properties = new Properties(); mybatisSqlInterceptor.setProperties(properties); return mybatisSqlInterceptor; } /** * 逻辑删除插件 */ @Bean public GlobalConfig globalConfig() { GlobalConfig globalConfig = new GlobalConfig(); GlobalConfig.DbConfig dbConfig = new GlobalConfig.DbConfig(); dbConfig.setLogicDeleteValue("Y"); dbConfig.setLogicNotDeleteValue("N"); globalConfig.setDbConfig(dbConfig); globalConfig.setSqlInjector(sqlInjector()); return globalConfig; } /** * 自定义Sql注入器 */ @Bean public MySqlInjector sqlInjector() { return new MySqlInjector(); } }扩展BaseMapper/** * Description: * * @author : laughing * DateTime: 2021-06-11 15:23 */ public interface MyBaseMapper<T> extends BaseMapper<T> { /** * 逻辑删除,并且带自动填充 */ int deleteByIdWithFill(T entity); }修改Mapper接口修改Mapper接口,将原来继承BaseMapper改成我们自定义的MyBaseMapper/** * Description:红外测温 * * @author : laughing * DateTime: 2021-05-18 10:28 */ public interface CulturePigTemperatureMapper extends MyBaseMapper<CulturePigTemperature> { /** * 红外测温列表查询 * @param pig 查询条件 * @return 结果 */ List<CulturePigTemperature> selectCulturePigTemperatureList(CulturePig pig); }修改删除方法将原来的删除方法deleteById改成deleteByIdWithFill再次测试,可以看到修改人、修改时间字段能够在逻辑删除方法时自动注入。
2021年06月11日
1,730 阅读
0 评论
0 点赞
1
...
7
8
9
...
19