1.@Configuration和@Component的区别?
实际上Configuration本质就是component,Configuration修饰的类中带有Bean注解的方法都会被动态代理,调用时的实例是同一个,而Component每次调用都是新的实例
2.创建了自定义异常后该如何配置使用?
需要用@RestControllerAdvice注解添加在一个全局异常拦截器类中,然后@ExceptionHandler里修饰要拦截的异常类型,符合的异常就会拦截后就可以执行该注解下的方法,我们可以在此方法中返回自定义的异常消息以便抛出
3.Springboot如何执行一个异步任务?
可以主动创建一个线程来执行;也可以通过@Async注解
首先要在启动类上添加@EnableAsync开启异步支持,然后将@Async注解加在需要异步执行的方法上
该如何返回异步方法的执行结果呢?
- Future接口作方法的返回类型,new一个AsyncResult<>(result)返回,AysncResult类是间接实现了Future接口的(当然你用AysncResult类作返回类型也可以)。AysncResult类是阻塞的,调用者在调用时要等该方法时
- 使用CompletableFuture类作方法的返回类型,调用CompletableFuture.completedFuture(result)返回
@Test
void testAsyncMethods() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(2);
CompletableFuture.runAsync(() -> {
try {
System.out.println("test1开始执行");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test1执行完成");
latch.countDown();
});
CompletableFuture.runAsync(() -> {
try {
System.out.println("test2开始执行");
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("test2执行完成");
latch.countDown();
});
latch.await();
}
4.如何解决跨域问题?
一开始是使用nginx反向代理的方法,nginx配置文件里设置一个统一的访问路径,nginx接收到路径后根据路径代理到不同的服务接口,但比较麻烦的是需要为每个服务添加@CrossOrigin的注解。后面就改用Spring 网关了,配置CorsWebFilter配置类, 在配置文件里添加需要转发的服务路径就可以了
5.定时任务怎么实现的?
专门开了个定时任务的模块,通过@EnableScheduleing注解在类上开启定时,对方法注解@Schedule(cron=???)进行定时执行发送mq消息调用短信模块的发送手机信息方法
6.在用到短信服务时是如何配置其密钥这些信息的?
通过在properties配置文件里先写好对于的配置信息,然后创建一个实现了InitializingBean的组件进行读取,通过@Value给变量赋值
7.像密钥这些信息其实是很私密的,你在上传git中是如何保护这些信息的?
第一种是使用jasypt (/ʼdʒæsɪpt/ ),不让数据明文显示。导入jasypt-spring-boot-starter依赖,创建自定义的password(密钥)使用对私密数据进行加密得到加密后的数据作为配置文件中的配置数据源,并且加上指定的自己的解密密钥,这样springboot在加载注入时就会根据密钥自行解密
<dependency>
<groupId>com.github.ulisesbocchio</groupId>
<artifactId>jasypt-spring-boot-starter</artifactId>
<version>3.0.5</version>
</dependency>
编写一个工具方法来生成密文
@Test
public void test(){
StandardPBEStringEncryptor basicTextEncryptor = new StandardPBEStringEncryptor();
basicTextEncryptor.setPassword("你的自定义密钥");
basicTextEncryptor.setAlgorithm("PBEWithMD5AndDES");
String pwd = basicTextEncryptor.encrypt("你的私密数据");
System.out.println(pwd);
}
// 比如得到了 PqWHcF7DiS3Uh6jqyl1i7HSVrUJV0Suv
这时候就可以在配置文件进行以下配置,密钥要用ENC()包裹起来
# 配置文件
jasypt:
encryptor:
password: rin
algorithm: PBEWithMD5AndDES
iv-generator-classname: org.jasypt.iv.NoIvGenerator
mypwd: ENC(PqWHcF7DiS3Uh6jqyl1i7HSVrUJV0Suv)
这样就能springboot在启动的时候就会自动读取mypwd,然后根据设置好的配置进行解码注入到变量中
那么这个密钥不还是在这吗?别人看到人家自己不就能解码了?
所以我们把密钥(rin)添加自己本地系统的环境变量中,配置文件中写环境变量的key,这样就能读取了
第二种是不把这些信息上传:
如果用的是docker-compose,可以单独创建一个docker-compose-override.yaml,在里面专门写上这些私密配置信息,这个文件和docker-compose.yaml是有关系的,如果两个文件中出现同样的配置,override会覆盖,而不一样的信息会合并执行,所以最终直接docker-compose -f docker-compose.yml -f docker-compose.prod.yml up
就可以,上传时把覆盖文件填到gitignore文件里就可以了
8.Serializable接口的作用?
类通过实现Serializable接口能够实现序列化或反序列化,当我们需要将内存中的java对象进行传输或存储的时候,就需要将对象进行序列化。
序列化之后,就可以把序列化后的内容写入磁盘,或者通过网络传输到别的机器上。等要用到的时候将反序列化回Java对象。但这时对象的地址是改变了的
9.如何通过gitignore在上传时隐藏springboot的application.properties文件?
有两种情况,区别在于需要设置忽略文件时该文件是否已经提交
- 如果未提交:直接在.gitignore文件里添加所需忽略的文件路径
- 如果已经提交:需要先通过git命令将提交缓存删除:
git rm -r --cached .
。然后再添加文件路径到gitignore文件里再提交推送。此时被忽略的文件是显示黄色的。
- 红色:表明文件未添加
- 绿色:表明文件已添加
- 蓝色:表明文件已提交,但是内容被修改了
- 黄色:表明文件已提交,但是被忽略了
- 白色或黑色(视主题而定):表明文件已提交
gitignore文件里的忽略文件路径一般填 存储库根路径 就可以
10.springboot的配置文件类型中,yml、yaml、和properties的区别和优先级?
写法上的区别:yml和yaml的写法上没区别,都是层级写法通过冒号赋值;而properties是链式写法,通过等号赋值
优先级:
如果yml和properties类型同时存在并且配置冲突的话,加载优先级如下: properties > yml > yaml
11.使用mybatis进行insert操作后希望能直接获取到插入数据的id,该如何实现?
对于需要新增的数据,如果我们新增的数据中id是自增,并且需要在新增后获取这条新增数据的id值时,可以在
- useGeneratedKeys :表示如果进行新增操作的表以自增字段为主键,则允许jdbc自动生成主键值,该属性只对insert操作有效
- keyProperty:指定获取的是此java映射对象的某个属性(比如id)
这两个属性一起用后,在代码中可以这样写就直接获取到新增数据的自增id值,而不需要先insert后再条件查询出id
int insert(){
dao.create(student);
return student.getId();
}
需要注意的是,要传入新增对象,插入成功后会把ID给setter到对象中
12.Feign能够进行服务间的调用,如果某个服务被客户端调用出现了失败,该怎么办?
Feign提供了一个熔断机制,使得在调用其他服务请求时,如果被调用的方法出现了异常,Feign客户端能执行指定的方法进行后续处理
首先在配置文件中开启: feign.hystrix.enabled=true
然后在设置Feign客户端时添加fallback选项,值为XXX(一个实现了Fegin客户端接口的实现类)
@FeignClient(value = "service-name",fallback = XXX.class)
在该实现类重写对应的接口方法,接口方法在Feign客户端中是用来做一个url请求的载体。
当其他模块的被调用方法出现异常后,就会开始熔断,进入对应调用接口的实现类中进行后续处理
// Feigin客户端接口
@FeignClient(name = "user",url = "${user.url}",fallback = UserFeignFallback.class
/*fallbackFactory = UserFeignFactory.class*/)
public interface UserFeign {
@PostMapping
void save(User user);
@GetMapping("/{id}")
User getUserByID(@PathVariable("id") String id);
@GetMapping
List<User> findAll();
}
// Feign客户端接口的实现类
@Component
public class UserFeignFallback implements UserFeign {
@Override
public void save(User user) {
//
}
@Override
public User getUserByID(String id) {
User user = new User();
user.setId("100");
user.setName("fallback 回调用户");
return user;
}
@Override
public List<User> findAll() {
return null;
}
}
13.关于Mybatis的xml和mapper接口的存放位置
①xml和mapper存放在不同的目录:
这种情况文件的存放一般是mapper接口放在java目录下的mapper文件夹;
xml放在resource文件下,为了能够识别到xml文件,需要在配置文件中写: mybatis.mapper-locations=classpath:mapper/*.xml
。
为了能够扫描到mapper目录下的mapper接口,需要在启动类中通过@MapperScan(“com.xxx.mapper”)。当然也可以在每个mapper接口中加上一个@Mapper接口,这样就不需要在启动类中添加扫描注解了。
classpath代表哪个目录?
经过对比,我们要注意到,开发时期的项目里,src/main/
下面的java
和resources
文件夹都被(编译)打包到了生产包的WEB-INF/classes/
目录下;而原来WEB-INF下面的views和web.xml则仍然还是在WEB-INF下面。同时由maven引入的依赖都被放入到了WEB-INF/lib/
下面。最后,编译后的class文件和资源文件都放在了classes目录下。
在编译打包后的项目中,根目录是META-INF
和WEB-INF
。这个时候,我们可以看到classes这个文件夹,它就是我们要找的classpath。
②xml和mapper存放在同一个目录:
这种情况下xml和mapper接口一般都是存放在Java目录下的mapper文件夹下,
由于xml在java目录下而不是在resource目录,application.properties中不需要配置这个:mybatis.mapper-locations=classpath:mapper/*.xml
了。
但在idea中,默认情况下是不会去编译src/main/java下的xml文件的!
所以需要我们自己配置使得src/main/java下的xml文件能被编译。在pom.xml的build标签下中添加以下代码:
<!--资源路径-->
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
</resources>
</build>