记录一些了解的高逼格、好用的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 来处理逻辑代码执行时被代理的逻辑。这个方式可改造的方式很多,可以轻易改造成适合自己的业务的写法。