记录一些了解的高逼格、好用的java代码
欢迎大家补充,共同学习
1. 函数式接口—@FunctionalInterface
好处:高逼格、代码收拢、解藕、统一处理
适用范围:具有共性的接口调用代码
举个栗子:
在我们平时的微服务开发中,调用其他服务的接口,通常要把接口调用部分做异常处理(try catch),或者打印异常日志或者结果日志,并且也有可能做一些统一的调用处理,比如微服务接口熔断等处理,这个时候可以适用函数式接口收拢所有的微服务调用集中处理
这是一个微服务接口
public interface TestServer {
T test();
void test2();
}
普通的方式调用上面的微服务方法
public class RpcTestServerImpl{
//引用微服务接口
private TestServer testServer;
public T test(){
try{
return testServer.test();
}catch (Exception e){
log.error("RPC error: ", e);
}
return null;
}
public void test2(){
try{
testServer.test2();
}catch (Exception e){
log.error("RPC error: ", e);
}
}
}
使用函数式接口后的写法:先定义统一调用类
//首先定义一个ServerExecutor
public class ServerExecutor {
//不需要返回值的
public static void execute(Runnable runnable) {
try {
//调用前和调用后可以做一些处理,比如熔断、日志等等
runnable.run();
//调用前和调用后可以做一些处理,比如熔断、日志等等
} catch (Exception e) {
log.error("RPC error: ", e);
}
}
//需要返回值的
public static <T> T executeAndReturn(Callable<T> callable) {
try {
//调用前和调用后可以做一些处理,比如熔断、日志等等
return callable.call();
} catch (Exception e) {
log.error("RPC invoke error", e);
}
return null;
}
}
使用统一定义的接口调用之后:
public class RpcTestServerImpl{
//引用微服务接口
private TestServer testServer;
public T test(){
return ServerExecutor.executeAndReturn(() -> testServer.test());
}
public T test2(){
ServerExecutor.execute(() -> testServer.test2());
}
}
扩展知识:
@FunctionalInterface
官方文档:https://docs.oracle.com/javase/8/docs/api/java/lang/FunctionalInterface.html 通过文档可以知道:
1、该注解只能标记在有且仅有一个抽象方法的接口上。
2、JDK8接口中的静态方法和默认方法,都不算是抽象方法。
3、接口默认继承java.lang.Object,所以如果接口显示声明覆盖了Object中方法,那么也不算抽象方法。
4、该注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface,那么编译器会报错。
CountDownLatch的使用,创建多个异步任务,在主线程中等待每个任务执行完成
CompletableFuture的方法有很多,各种组合可以实现不同的功能。大致通过方法名称可以总结为:
创建类runAsync 异步执行,无返回值supplyAsync 异步执行,有返回值anyOf 任意一个执行完成,就可以进行下一步动作allOf 全部完成所有任务,才可以进行下一步任务接续类以Async结尾的方法,都是异步方法,对应的没有Async则是同步方法,一般都是一个异步方法对应一个同步方法。以Async后缀结尾的方法,都有两个重载的方法,一个是使用内容的forkjoin线程池,一种是使用自定义线程池以run开头的方法,其入口参数一定是无参的,并且没有返回值,类似于执行Runnable方法。以supply开头的方法,入口也是没有参数的,但是有返回值以Accept开头或者结尾的方法,入口参数是有参数,但是没有返回值以Apply开头或者结尾的方法,入口有参数,有返回值带有either后缀的方法,表示谁先完成就消费谁whenCompleteAsync:处理完成或异常,无返回值handleAsync:处理完成或异常,有返回值状态取值类join 合并结果,等待get 合并等待结果,可以增加超时时间;get和join区别,join只会抛出unchecked异常,get会返回具体的异常
8. @ConditionalOnMissingBean使用
@ConditionalOnMissingBea该注解表示,如果存在它修饰的类的bean,则不需要再创建这个bean。日常代码中写公共jar包代码,经常会有接口需要默认实现和自定义实现,jar包中写公共实现,如果需要对接口实现自定义实现,就可以使用@ConditionalOnMissingBean快速实现
/**
* 默认实现配置
*/
@Configuration
public class DefaultConfiguration {
@Bean
@ConditionalOnMissingBean(name = "TestDefaultService")//指定Bean的名称
public TestDefaultService testService() {
return (param) -> {
//...默认公共实现
};
}
}
@Qualifier("TestDefaultService")//指定Bean名称
@Resource
private TestDefaultService testDefaultService;
这样使用之后,当手动实现了TestDefaultService
并指定BeanName为“TestDefaultService”
,比如在接口实现类上这样写之后
@Service("TestDefaultService")
手动实现的接口就会被注入,而默认实现的就不会被注入,如果不手动实现接口,那么默认实现就会被注入。
9. 使用函数式编程实现代理模式
先上代码:
import lombok.Data;
import lombok.EqualsAndHashCode;
/**
* 返回值
*/
@EqualsAndHashCode(callSuper = true)
@Data
public class Test1DTO extends TestBaseDTO {
private String name;
}
import lombok.Data;
/**
* 返回值基类
*/
@Data
public class TestBaseDTO {
private Integer value;
}
public interface Test1Service {
TestBaseDTO handle() throws Exception;
}
public class Test1Util {
public static <R extends TestBaseDTO> R execute(Test1Service test1Service) throws Exception {
System.out.println("在执行之前做一些事情");
R handle = (R) test1Service.handle();
System.out.println("在执行之后做一些事情");
return handle;
}
}
/**
* 测试类
*/
public class Test1 {
public static void main(String[] args) throws Exception {
TestBaseDTO execute = Test1Util.execute(() -> {
// 这里可以写想要执行的逻辑代码
// 返回值可以自定义 只要继承TestBaseDTO就行
Test1DTO testBaseDTO = new Test1DTO();
testBaseDTO.setValue(1);
testBaseDTO.setName("test");
return testBaseDTO;
});
System.out.println(execute);
}
}
如上代码,就是通过函数式编程搞了一个Test1Util
来处理逻辑代码执行时被代理的逻辑。这个方式可改造的方式很多,可以轻易改造成适合自己的业务的写法。