1、 Springcloud容错机制有哪些?
(1)Hystrix
1)创建基于Eureka和Ribbon的服务端和两个客户端生产者,消费者。
2)在消费者中添加依赖。
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
3)在启动类ClientApplication上添加@EnableHystrix或@EnableCircuitBreaker。 4)修改HystrixController,在其中添加getUser方法,并添加熔断回调方法注解及回调方法。
@HystrixCommand(fallbackMethod = "getDefaultUser")
@RequestMapping("/getUser")
public String getUser() {
return restTemplate.getForObject("http://client/getUser", String.class);
}
private String getDefaultUser() {
System.out.println("熔断,默认回调函数");
return "{\"username\":\"admin\",\"age\":\"-1\"}";
}
(2)基于Feign使用Hystrix
通常情况下的Hystrix是通过注解@HystrixCommand的fallbackMethod属性实现回调的,而在Feign中,由于Feign是用接口实现的声明式Rest,所以Hystrix的通用方法在这里就不适用于Feign了,实际上在Feign与SpringCloud的依赖库中已经默认的将Hystrix加入其中了。
1)导入依赖。
pom.xml:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
</dependency>
2)application.properties属性配置文件:
server.port=8762
spring.application.name=client-8762
#默认feign的hystrix为关闭状态
feign.hystrix.enabled=true
eureka.client.service-url.defaultZone=http://localhost:8761/eureka/
3)启动类添加@EnableFeignClients,控制层通过注入feign的接口去完成声明式调用:
a) feign的接口:
package com.cn.feign;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
//接口类上加入的注解中添加属性fallback,指定回调类
@FeignClient(name = "CLIENT-87",fallback = FeignClientFallback.class)
public interface UserFeign {
@RequestMapping("/getUser")
public String getUser();
}
b) 创建回调类:
package com.cn.feign;
import org.springframework.stereotype.Component;
@Component
class FeignClientFallback implements UserFeign {
@Override
public String getUser() {
System.out.println("熔断,默认回调函数");
return "{\"username\":\"admin\",\"age\":\"-1\"}";
}
}
2、Mybaits逆向生成代码如何添加手写SQL?添加之后重新生成如何设置不覆盖掉之前修改的代码?
(1)自动生成代码步骤
- 添加依赖。
<!-- https://mvnrepository.com/artifact/org.mybatis.generator/mybatis-generator-core -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>2.3</version>
</dependency>
2)创建代理生成器主类。
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.config.DataSourceConfig;
import com.baomidou.mybatisplus.generator.config.GlobalConfig;
import com.baomidou.mybatisplus.generator.config.PackageConfig;
import com.baomidou.mybatisplus.generator.config.StrategyConfig;
import com.baomidou.mybatisplus.generator.config.rules.DateType;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import org.junit.Test;
/**
* @author
* @since 2018/12/13
*/
public class CodeGenerator {
@Test
public void run() {
// 1、创建代码生成器
AutoGenerator mpg = new AutoGenerator();
// 2、全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("testjava");
gc.setOpen(false); //生成后是否打开资源管理器
gc.setFileOverride(false); //重新生成时文件是否覆盖
gc.setServiceName("%sService"); //去掉Service接口的首字母I
gc.setIdType(IdType.ID_WORKER_STR); //主键策略
gc.setDateType(DateType.ONLY_DATE);//定义生成的实体类中日期类型
gc.setSwagger2(true);//开启Swagger2模式
mpg.setGlobalConfig(gc);
// 3、数据源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/guli_edu?serverTimezone=GMT%2B8");
dsc.setDriverName("com.mysql.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
dsc.setDbType(DbType.MYSQL);
mpg.setDataSource(dsc);
// 4、包配置
PackageConfig pc = new PackageConfig();
//com.atguigu.eduservice
pc.setModuleName("eduservice"); //模块名
pc.setParent("com.atguigu");
pc.setController("controller");
pc.setEntity("entity");
pc.setService("service");
pc.setMapper("mapper");
mpg.setPackageInfo(pc);
// 5、策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setInclude("edu_chapter","edu_video");
strategy.setNaming(NamingStrategy.underline_to_camel);//数据库表映射到实体的命名策略
strategy.setTablePrefix(pc.getModuleName() + "_"); //生成实体时去掉表前缀
strategy.setColumnNaming(NamingStrategy.underline_to_camel);//数据库表字段映射到实体的命名策略
strategy.setEntityLombokModel(true); // lombok 模型 @Accessors(chain = true) setter链式操作
strategy.setRestControllerStyle(true); //restful api风格控制器
strategy.setControllerMappingHyphenStyle(true); //url中驼峰转连字符
mpg.setStrategy(strategy);
// 6、执行
mpg.execute();
}
}
(2)添加自定义 sql语句步骤
1)xml 映射文件中添加自定义sql语句
resultType返回值类型,map是别名,代表的是java.util.Map;基本数据类型考虑到重复的问题,会在其前面加上 ‘_’,比如 byte 对应的别名是 _byte;引用数据类型是将大写字母转小写字母,比如 HashMap 对应的别名是 hashmap;也可以返回 JavaBean 类型。 resultType和resultMap只能使用一个。parameterType是String时,参数名必须是_parameter
${}和#{}的区别:#{}将给参数自动加上单引号’’,而${}不会
2)Mapper接口中添加方法
查询结果是多条数据的,也可以封装成Map:{表中主键, JavaBean},而不用List。同时resultType 返回值类型,不再是map,而是Map 的 value 对应的JavaBean类型。
3)业务层调用接口并实现,表示层调用业务层方法
(3)如何防止再次自动生成代码覆盖掉之前已经添加的代码
目前的解决方式:
- 把mapper和xml中自定义的语句拷贝出来,
- 清理原有xml、mapper文件,
- 用mybatis-generator插件重新生成,
- 把自定义的sql语句拷贝回去。
目前此种方法的缺陷:
1)需要手动操作,拷来拷去的繁琐;
2)手动操作容易误操作和遗漏;
3)项目新成员无意识或意识欠缺,改表后重新生成时容易遗忘把手写部分的sql拷回去,造成线上安全问题;
分析:
mybatis generator自动生成时可选是“追加”还是“覆盖”,不论追加还是覆盖,都不能达到我们想要的同一个文件既能自动修改原来的代码,又能不变我们手写追加的代码。
解决方法:
把我们手写的sql放到一个扩展的mapper和xml中,继承原生的mapper和xml,这样就可以每次重新生成时就只覆盖原生的表而不影响到我们自写的代码。
3、Springcloud使用的协议有哪些?你感觉这个协议怎么样?可以替换成什么协议?
(1)springcloud基于 HTTP 的 REST 方式。
(2)可以将http改为https(http + ssl = https)。
1)HTTP存在的几点不足:
通信使用明文,内容可能会被窃听;不验证通信方的身份,因此有可能遭遇伪装;无法证明报文的完整性,有可能已遭篡改。
2)《网络安全法》的相关规定。
3)越来越多场景的强制性要求。
IOS、谷歌等要求的 APP 分发下载必须使用 https 安全连接; 微信小程序、支付宝小程序强制使用 HTTPS; 银行支付相关业务必须是HTTPS。
4)利于网站搜索引擎优化。
搜索引擎如谷歌,百度站在确保用户信息安全的角度,都在大力倡导网站部署SSL证书实现https加密访问。在搜索、展现、排序方面也给予部署了SSL证书网站优待。
4、重写equals方法,为什么要重写hashcode方法?
判断的时候先根据hashcode进行的判断,相同的情况下再根据equals()方法进行判断。如果只重写了equals方法,而不重写hashcode的方法,会造成hashcode的值不同,而equals()方法判断出来的结果为true。
在Java中的一些容器中,不允许有两个完全相同的对象,插入的时候,如果判断相同则会进行覆盖。这时候如果只重写了equals()的方法,而不重写hashcode的方法,Object中hashcode是根据对象的存储地址转换而形成的一个哈希值。这时候就有可能因为没有重写hashcode方法,造成相同的对象散列到不同的位置而造成对象的不能覆盖的问题。
5、双重检查锁有什么好处?为什么要加两次判空?
第一个if减少性能开销,第二个if避免生成多个对象实例。
现有三个线程A,B,C,假设线程A和线程B同时调用getSingleton()时,判断第一层if判断都为空,这时线程A先拿到锁,线程B在代码块外层等待。线程A进行第二层if判断,条件成立后new了一个新对象,创建完成,释放锁,线程B拿到锁,进行第二层if判断,singleton不为空,直接返回singleton释放锁,避免生成多个对象实例。线程线C调用getSingleton时第一层判断不成立,直接拿到singleton对象返回,避免进入锁,减少性能开销。
使用volatile是因为singleton = new Singleton();这行代码并不是一个原子指令,可能会在JVM中进行指令重排,volatile可以禁止指令重排序。