首页 文章 精选 留言 我的

精选列表

搜索[网站开发],共10000篇文章
优秀的个人博客,低调大师

Java后端开发三年多线程你都懂,问你异步编程你说你没听过???

前言 以前需要异步执行一个任务时,一般是用Thread或者线程池Executor去创建。如果需要返回值,则是调用Executor.submit获取Future。但是多个线程存在依赖组合,我们又能怎么办?可使用同步组件CountDownLatch、CyclicBarrier等;其实有简单的方法,就是用CompletableFuture 线程任务的创建 线程任务的串行执行 线程任务的并行执行 处理任务结果和异常 多任务的简单组合 取消执行线程任务 任务结果的获取和完成与否判断 1、创建异步线程任务 根据supplier创建CompletableFuture任务 //使用内置线程ForkJoinPool.commonPool(),根据supplier构建执行任务 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier) //指定自定义线程,根据supplier构建执行任务 public static <U> CompletableFuture<U> supplyAsync(Supplier<U> supplier, Executor executor) 根据runnable创建CompletableFuture任务 //使用内置线程ForkJoinPool.commonPool(),根据runnable构建执行任务 public static CompletableFuture<Void> runAsync(Runnable runnable) //指定自定义线程,根据runnable构建执行任务 public static CompletableFuture<Void> runAsync(Runnable runnable, Executor executor) 使用示例 ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> rFuture = CompletableFuture .runAsync(() -> System.out.println("hello siting"), executor); //supplyAsync的使用 CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> { System.out.print("hello "); return "siting"; }, executor); //阻塞等待,runAsync 的future 无返回值,输出null System.out.println(rFuture.join()); //阻塞等待 String name = future.join(); System.out.println(name); executor.shutdown(); // 线程池需要关闭 --------输出结果-------- hello siting null hello siting 常量值作为CompletableFuture返回 //有时候是需要构建一个常量的CompletableFuture public static <U> CompletableFuture<U> completedFuture(U value) 2 、线程串行执行 ​任务完成则运行action,不关心上一个任务的结果,无返回值 public CompletableFuture<Void> thenRun(Runnable action) public CompletableFuture<Void> thenRunAsync(Runnable action) public CompletableFuture<Void> thenRunAsync(Runnable action, Executor executor) 使用示例 CompletableFuture<Void> future = CompletableFuture .supplyAsync(() -> "hello siting", executor) .thenRunAsync(() -> System.out.println("OK"), executor); executor.shutdown(); --------输出结果-------- OK 任务完成则运行action,依赖上一个任务的结果,无返回值 public CompletableFuture<Void> thenAccept(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action) public CompletableFuture<Void> thenAcceptAsync(Consumer<? super T> action, Executor executor) 使用示例 ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> future = CompletableFuture .supplyAsync(() -> "hello siting", executor) .thenAcceptAsync(System.out::println, executor); executor.shutdown(); --------输出结果-------- hello siting 任务完成则运行fn,依赖上一个任务的结果,有返回值 public <U> CompletableFuture<U> thenApply(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn) public <U> CompletableFuture<U> thenApplyAsync(Function<? super T,? extends U> fn, Executor executor) 使用示例 ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> "hello world", executor) .thenApplyAsync(data -> { System.out.println(data); return "OK"; }, executor); System.out.println(future.join()); executor.shutdown(); --------输出结果-------- hello world OK thenCompose - 任务完成则运行fn,依赖上一个任务的结果,有返回值 类似thenApply(区别是thenCompose的返回值是CompletionStage,thenApply则是返回 U),提供该方法为了和其他CompletableFuture任务更好地配套组合使用 public <U> CompletableFuture<U> thenCompose(Function<? super T, ? extends CompletionStage<U>> fn) public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn) public <U> CompletableFuture<U> thenComposeAsync(Function<? super T, ? extends CompletionStage<U>> fn, Executor executor) 使用示例 //第一个异步任务,常量任务 CompletableFuture<String> f = CompletableFuture.completedFuture("OK"); //第二个异步任务 ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<String> future = CompletableFuture .supplyAsync(() -> "hello world", executor) .thenComposeAsync(data -> { System.out.println(data); return f; //使用第一个任务作为返回 }, executor); System.out.println(future.join()); executor.shutdown(); --------输出结果-------- hello world OK 3 、线程并行执行 两个CompletableFuture[并行]执行完,然后执行action,不依赖上两个任务的结果,无返回值 public CompletableFuture<Void> runAfterBoth(CompletionStage<?> other, Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action) public CompletableFuture<Void> runAfterBothAsync(CompletionStage<?> other, Runnable action, Executor executor) 使用示例 //第一个异步任务,常量任务 CompletableFuture<String> first = CompletableFuture.completedFuture("hello world"); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> future = CompletableFuture //第二个异步任务 .supplyAsync(() -> "hello siting", executor) // () -> System.out.println("OK") 是第三个任务 .runAfterBothAsync(first, () -> System.out.println("OK"), executor); executor.shutdown(); --------输出结果-------- OK 两个CompletableFuture[并行]执行完,然后执行action,依赖上两个任务的结果,无返回值 //第一个任务完成再运行other,fn再依赖消费两个任务的结果,无返回值 public <U> CompletableFuture<Void> thenAcceptBoth(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) //两个任务异步完成,fn再依赖消费两个任务的结果,无返回值 public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action) //两个任务异步完成(第二个任务用指定线程池执行),fn再依赖消费两个任务的结果,无返回值 public <U> CompletableFuture<Void> thenAcceptBothAsync(CompletionStage<? extends U> other, BiConsumer<? super T, ? super U> action, Executor executor) 使用示例 //第一个异步任务,常量任务 CompletableFuture<String> first = CompletableFuture.completedFuture("hello world"); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> future = CompletableFuture //第二个异步任务 .supplyAsync(() -> "hello siting", executor) // (w, s) -> System.out.println(s) 是第三个任务 .thenAcceptBothAsync(first, (s, w) -> System.out.println(s), executor); executor.shutdown(); --------输出结果-------- hello siting 两个CompletableFuture[并行]执行完,然后执行action,依赖上两个任务的结果,有返回值 //第一个任务完成再运行other,fn再依赖消费两个任务的结果,有返回值 public <U,V> CompletableFuture<V> thenCombine(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) //两个任务异步完成,fn再依赖消费两个任务的结果,有返回值 public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn) //两个任务异步完成(第二个任务用指定线程池执行),fn再依赖消费两个任务的结果,有返回值 public <U,V> CompletableFuture<V> thenCombineAsync(CompletionStage<? extends U> other, BiFunction<? super T,? super U,? extends V> fn, Executor executor) 使用示例 //第一个异步任务,常量任务 CompletableFuture<String> first = CompletableFuture.completedFuture("hello world"); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<String> future = CompletableFuture //第二个异步任务 .supplyAsync(() -> "hello siting", executor) // (w, s) -> System.out.println(s) 是第三个任务 .thenCombineAsync(first, (s, w) -> { System.out.println(s); return "OK"; }, executor); System.out.println(future.join()); executor.shutdown(); --------输出结果-------- hello siting OK 4 、线程并行执行,谁先执行完则谁触发下一任务(二者选其最快) 上一个任务或者other任务完成, 运行action,不依赖前一任务的结果,无返回值 public CompletableFuture<Void> runAfterEither(CompletionStage<?> other, Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action) public CompletableFuture<Void> runAfterEitherAsync(CompletionStage<?> other, Runnable action, Executor executor) 使用示例 //第一个异步任务,休眠1秒,保证最晚执行晚 CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(1000); }catch (Exception e){} System.out.println("hello world"); return "hello world"; }); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> future = CompletableFuture //第二个异步任务 .supplyAsync(() ->{ System.out.println("hello siting"); return "hello siting"; } , executor) //() -> System.out.println("OK") 是第三个任务 .runAfterEitherAsync(first, () -> System.out.println("OK") , executor); executor.shutdown(); --------输出结果-------- hello siting OK 上一个任务或者other任务完成, 运行action,依赖最先完成任务的结果,无返回值 public CompletableFuture<Void> acceptEither(CompletionStage<? extends T> other, Consumer<? super T> action) public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) public CompletableFuture<Void> acceptEitherAsync(CompletionStage<? extends T> other, Consumer<? super T> action, Executor executor) 使用示例 //第一个异步任务,休眠1秒,保证最晚执行晚 CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(1000); }catch (Exception e){} return "hello world"; }); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<Void> future = CompletableFuture //第二个异步任务 .supplyAsync(() -> "hello siting", executor) // data -> System.out.println(data) 是第三个任务 .acceptEitherAsync(first, data -> System.out.println(data) , executor); executor.shutdown(); --------输出结果-------- hello siting 上一个任务或者other任务完成, 运行fn,依赖最先完成任务的结果,有返回值 public <U> CompletableFuture<U> applyToEither(CompletionStage<? extends T> other, Function<? super T, U> fn) public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn) public <U> CompletableFuture<U> applyToEitherAsync(CompletionStage<? extends T> other, Function<? super T, U> fn, Executor executor) 使用示例 //第一个异步任务,休眠1秒,保证最晚执行晚 CompletableFuture<String> first = CompletableFuture.supplyAsync(()->{ try{ Thread.sleep(1000); }catch (Exception e){} return "hello world"; }); ExecutorService executor = Executors.newSingleThreadExecutor(); CompletableFuture<String> future = CompletableFuture //第二个异步任务 .supplyAsync(() -> "hello siting", executor) // data -> System.out.println(data) 是第三个任务 .applyToEitherAsync(first, data -> { System.out.println(data); return "OK"; } , executor); System.out.println(future); executor.shutdown(); --------输出结果-------- hello siting OK 5 、处理任务结果或者异常 exceptionally-处理异常 public CompletableFuture<T> exceptionally(Function<Throwable, ? extends T> fn) 如果之前的处理环节有异常问题,则会触发exceptionally的调用相当于 try...catch 使用示例 CompletableFuture<Integer> first = CompletableFuture .supplyAsync(() -> { if (true) { throw new RuntimeException("main error!"); } return "hello world"; }) .thenApply(data -> 1) .exceptionally(e -> { e.printStackTrace(); // 异常捕捉处理,前面两个处理环节的日常都能捕获 return 0; }); handle-任务完成或者异常时运行fn,返回值为fn的返回 相比exceptionally而言,即可处理上一环节的异常也可以处理其正常返回值 public <U> CompletableFuture<U> handle(BiFunction<? super T, Throwable, ? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn) public <U> CompletableFuture<U> handleAsync(BiFunction<? super T, Throwable, ? extends U> fn, Executor executor) 使用示例 CompletableFuture<Integer> first = CompletableFuture .supplyAsync(() -> { if (true) { throw new RuntimeException("main error!"); } return "hello world"; }) .thenApply(data -> 1) .handleAsync((data,e) -> { e.printStackTrace(); // 异常捕捉处理 return data; }); System.out.println(first.join()); --------输出结果-------- java.util.concurrent.CompletionException: java.lang.RuntimeException: main error! ... 5 more null whenComplete-任务完成或者异常时运行action,有返回值 whenComplete与handle的区别在于,它不参与返回结果的处理,把它当成监听器即可 即使异常被处理,在CompletableFuture外层,异常也会再次复现 使用whenCompleteAsync时,返回结果则需要考虑多线程操作问题,毕竟会出现两个线程同时操作一个结果 public CompletableFuture<T> whenComplete(BiConsumer<? super T, ? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action) public CompletableFuture<T> whenCompleteAsync(BiConsumer<? super T, ? super Throwable> action, Executor executor) 使用示例 CompletableFuture<AtomicBoolean> first = CompletableFuture .supplyAsync(() -> { if (true) { throw new RuntimeException("main error!"); } return "hello world"; }) .thenApply(data -> new AtomicBoolean(false)) .whenCompleteAsync((data,e) -> { //异常捕捉处理, 但是异常还是会在外层复现 System.out.println(e.getMessage()); }); first.join(); --------输出结果-------- java.lang.RuntimeException: main error! Exception in thread "main" java.util.concurrent.CompletionException: java.lang.RuntimeException: main error! ... 5 more 6 、多个任务的简单组合 public static CompletableFuture<Void> allOf(CompletableFuture<?>... cfs) public static CompletableFuture<Object> anyOf(CompletableFuture<?>... cfs) 使用示例 CompletableFuture<Void> future = CompletableFuture .allOf(CompletableFuture.completedFuture("A"), CompletableFuture.completedFuture("B")); //全部任务都需要执行完 future.join(); CompletableFuture<Object> future2 = CompletableFuture .anyOf(CompletableFuture.completedFuture("C"), CompletableFuture.completedFuture("D")); //其中一个任务行完即可 future2.join(); 7、取消执行线程任务 // mayInterruptIfRunning 无影响;如果任务未完成,则返回异常 public boolean cancel(boolean mayInterruptIfRunning) //任务是否取消 public boolean isCancelled() 使用示例 CompletableFuture<Integer> future = CompletableFuture .supplyAsync(() -> { try { Thread.sleep(1000); } catch (Exception e) { } return "hello world"; }) .thenApply(data -> 1); System.out.println("任务取消前:" + future.isCancelled()); // 如果任务未完成,则返回异常,需要对使用exceptionally,handle 对结果处理 future.cancel(true); System.out.println("任务取消后:" + future.isCancelled()); future = future.exceptionally(e -> { e.printStackTrace(); return 0; }); System.out.println(future.join()); --------输出结果-------- 任务取消前:false 任务取消后:true java.util.concurrent.CancellationException at java.util.concurrent.CompletableFuture.cancel(CompletableFuture.java:2276) at Test.main(Test.java:25) 0 8、任务的获取和完成与否判断 // 任务是否执行完成 public boolean isDone() //阻塞等待 获取返回值 public T join() // 阻塞等待 获取返回值,区别是get需要返回受检异常 public T get() //等待阻塞一段时间,并获取返回值 public T get(long timeout, TimeUnit unit) //未完成则返回指定value public T getNow(T valueIfAbsent) //未完成,使用value作为任务执行的结果,任务结束。需要future.get获取 public boolean complete(T value) //未完成,则是异常调用,返回异常结果,任务结束 public boolean completeExceptionally(Throwable ex) //判断任务是否因发生异常结束的 public boolean isCompletedExceptionally() //强制地将返回值设置为value,无论该之前任务是否完成;类似complete public void obtrudeValue(T value) //强制地让异常抛出,异常返回,无论该之前任务是否完成;类似completeExceptionally public void obtrudeException(Throwable ex) 使用示例 CompletableFuture<Integer> future = CompletableFuture .supplyAsync(() -> { try { Thread.sleep(1000); } catch (Exception e) { } return "hello world"; }) .thenApply(data -> 1); System.out.println("任务完成前:" + future.isDone()); future.complete(10); System.out.println("任务完成后:" + future.join()); --------输出结果-------- 任务完成前:false 任务完成后:10 总结 Java 多线程一直是面试时候的重点,也是能力提升的重要体现,如何做到波澜不惊,从容面对,需要我们对其中的内容融汇贯通,小编这里也对应总结了一份多线程-并发编程的思维导图,需要的朋友可以看看,关注公众号:麒麟改bug,还可以领取一份包含了Java基础、Java集合容器、Java异常、并发编程、JVM、Spring、Spring MVC、Spring Boot、Spring Cloud、MyBatis、Redis、MySQL数据库、消息中间件MQ与RabbitMQ、Dubbo、Linux、Tomcat、ZooKeeper、Netty、 架构设计&分布式&数据结构与算法等等,都是互联网大厂的面试真题,已经有粉丝靠这份PDF拿下众多大厂的offer。 欢迎大家一起交流,喜欢文章记得关注我点赞哟,感谢支持!

优秀的个人博客,低调大师

Day3 鸿蒙,用XML创建布局来开发是不是就简单一点

上文档: HarmonyOS提供了Ability和AbilitySlice两个基础类。有界面的Ability绑定了系统的Window进行UI展示,且具有生命周期。AbilitySlice主要用于承载Ability的具体逻辑实现和界面UI,是应用显示、运行和跳转的最小单元。AbilitySlice通过setUIContent()为界面设置布局。 组件需要进行组合,并添加到界面的布局中。在Java UI框架中,提供了两种编写布局的方式: 在代码中创建布局:用代码创建Component和ComponentContainer对象,为这些对象设置合适的布局参数和属性值,并将Component添加到ComponentContainer中,从而创建出完整界面。 在XML中声明UI布局:按层级结构来描述Component和ComponentContainer的关系,给组件节点设定合适的布局参数和属性值,代码中可直接加载生成此布局。 这一次为大家带来的就是“在XML中创建布局”。 涵盖核心知识点包括: 1、创建步骤: (1)加载XML布局作为根布局 super.setUIContent(ResourceTable.Layout_first_layout); (2)查找布局中组件 Button button = (Button) findComponentById(ResourceTable.Id_button); (3)设置组件的属性 ShapeElement background = new ShapeElement(); background.setRgbColor(new RgbColor(0,125,255)); background.setCornerRadius(25); button.setBackground(background); button.setClickedListener(new Component.ClickedListener() { @Override // 在组件中增加对点击事件的检测 public void onClick(Component Component) { // 此处添加按钮被点击需要执行的操作 } }); 2、XML中创建布局与代码创建布局差异点: (1)代码创建布局,需要首先初始化布局,设置布局的属性,这个过程在XML创建布局中是通过创建修改XML来实现; (2)代码创建布局,其组件都来源于XML,声明组件只要将在布局中根据ID寻找组件即可; 本文由GZH程序员小小叶发布!

优秀的个人博客,低调大师

Spring注解驱动开发之六——@Bean指定初始化、销毁方法、BeanPostProcessor后置处理器

本文包含以下内容: 新、旧方法指定初始化和销毁方法 InitializingBean、DisposableBean指定生命周期方法 @PostConstruct、@PreDestroy调用Bean 创建前后的方法 BeanPostProcessor 后置处理器,进行初始化 前后的工作 上述3种的联合调试 1.新、旧方法指定初始化和销毁方法 在之前,指定一个Bean 的初始化方法和销毁方法可以在 bean.xml 文件中<bean> 标签中指定 init-method和 destroy-method 进行初始函数、销毁函数的指定。要求没有任何参数、可以报任何异常 在注解中也可以通过在 @Bean (initMethod= "init" ,destroyMethod= "detory" ) 注解中指定 ,初始化方法和销毁方法。下面开始测试 1.建立实体类 @Componentpublic class Car { public Car(){ System.out.println("car constructor..."); } public void init(){ System.out.println("car ... init..."); } public void detory(){ System.out.println("car ... detory..."); }} 2.通过注解标记需要 创建的类 @Bean(initMethod="init",destroyMethod="detory") public Car car(){ return new Car(); } 3.编写测试方法进行 测试 @Test public void test01(){ //1、创建ioc容器 AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class); System.out.println("容器创建完成..."); //applicationContext.getBean("car"); //关闭容器 applicationContext.close(); } 4.获得结果 5.测试, 非单例的情况加上 @Scope ( "prototype" ) 注解,获得结果,表示,非单例的组件在容器创建后就不在进行管理,不再进行销毁操作了 2.InitializingBean、DisposableBean指定生命周期方法 除了通过指定指定初始化方法、销毁方法外,还可以通过继承 InitializingBean 、 DisposableBean 接口分别实现其afterPropertiesSet 、destroy 方法实现,在Bean创建前后执行函数操作,下面进行测试: 1)建立实体类 @Componentpublic class Cat implements InitializingBean,DisposableBean { public Cat(){ System.out.println("cat constructor..."); } @Override public void destroy() throws Exception { // TODO Auto-generated method stub System.out.println("cat...destroy..."); } @Override public void afterPropertiesSet() throws Exception { // TODO Auto-generated method stub System.out.println("cat...afterPropertiesSet..."); }} 2)运行获得结果 3.@PostConstruct、@PreDestroy调用Bean 创建前后的方法 在springboot中可以通过@PostConstruct、@PreDestroy,分别制定在Bean装配前后执行的函数。下面开始测试: 1)建立实体类 @Componentpublic class Dog implements ApplicationContextAware { //@Autowired private ApplicationContext applicationContext; public Dog(){ System.out.println("dog constructor..."); } //对象创建并赋值之后调用 @PostConstruct public void init(){ System.out.println("Dog....@PostConstruct..."); } //容器移除对象之前 @PreDestroy public void detory(){ System.out.println("Dog....@PreDestroy..."); }} 2)运行测试方法,在Bean创建完成之后、容器创建完成前 调用的 @PostConstruct 在Bean销毁前调用了 @PreDestroy的方法 4.BeanPostProcessor 后置处理器,进行初始化 前后的工作 可以通过建立BeanPostProcessor 实现类,通过其 postProcessBeforeInitialization 、postProcessAfterInitialization方法可以在Bean 完成创建之后、Bean 销毁之前,执行指定方法。下面开始测试: 1.创建自定义 BeanPostProcessor 实现类 /** * 后置处理器:初始化前后进行处理工作 * 将后置处理器加入到容器中 * @author lfy */@Componentpublic class MyBeanPostProcessor implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessBeforeInitialization..."+beanName+"=>"+bean); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { // TODO Auto-generated method stub System.out.println("postProcessAfterInitialization..."+beanName+"=>"+bean); return bean; }} 2.运行测试,得到结果,在Bean创建完成之后,调用了 postProcessBeforeInitialization方法 ,在创建Bean完成之后调用 postProcessAfterInitialization 。结果如下: 5.上述3种的联合调试 综合上述内容,同时加入(1)InitializingBean、DisposableBean (2)@PostConstruct、@PreDestroy (3)BeanPostProcessor查看执行方法的先后顺序 1.)首先是创建,测试结果如下图所示:执行顺序是,现执行 postProcessBeforeInitialization而后是构造方法 、而后是 postProcessAfterInitialization而后是 InitializingBean的 afterPropertiesSet -END- 可以关注我的公众号,免费获取价值1980元学习资料 点击“在看”,学多少都不会忘~ 本文分享自微信公众号 - 阿聪的全栈之路(gh_ffab7c84fb0c)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

【Spring注解驱动开发】使用@Autowired@Qualifier@Primary三大注解自动装配组件,你会了吗?

点击上方蓝色“冰河技术”,关注并选择“设为星标” 持之以恒,贵在坚持,每天进步一点点! 作者个人研发的在高并发场景下,提供的简单、稳定、可扩展的延迟消息队列框架,具有精准的定时任务和延迟队列处理功能。自开源半年多以来,已成功为十几家中小型企业提供了精准定时调度方案,经受住了生产环境的考验。为使更多童鞋受益,现给出开源框架地址: https://github.com/sunshinelyz/mykit-delay PS: 欢迎各位Star源码,也可以pr你牛逼哄哄的代码。 写在前面 【Spring专题】停更一个多月,期间在更新其他专题的内容,不少小伙伴纷纷留言说:冰河,你【Spring专题】是不是停更了啊!其实并没有停更,只是中途有很多小伙伴留言说急需学习一些知识技能,以便于跳槽,哈哈,大家都懂得!所以,中途停更了一段时间,写了一些其他专题的文章。现在,继续更新【String专题】。 关注 冰河技术 微信公众号,订阅更多技术干货!如果文章对你有所帮助,请不要吝惜你的点赞、在看、留言和转发,你的支持是我持续创作的最大动力! 项目工程源码已经提交到GitHub:https://github.com/sunshinelyz/spring-annotation 注解说明 @Autowired注解 @Autowired 注解,可以对类成员变量、方法和构造函数进行标注,完成自动装配的工作。@Autowired 注解可以放在类,接口以及方法上。在使用@Autowired之前,我们对一个bean配置属性时,是用如下xml文件的形式进行配置的。 <propertyname="属性名"value="属性值"/> @Autowired 注解的源码如下所示。 packageorg.springframework.beans.factory.annotation;importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Target({ElementType.CONSTRUCTOR,ElementType.METHOD,ElementType.PARAMETER,ElementType.FIELD,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfaceAutowired{booleanrequired()defaulttrue;} @Autowired 注解说明: (1)默认优先按照类型去容器中找对应的组件,找到就赋值; (2)如果找到多个相同类型的组件,再将属性名称作为组件的id,到 IOC 容器中进行查找。 @Qualifier注解 @Autowired是根据类型进行自动装配的,如果需要按名称进行装配,则需要配合@Qualifier 注解使用。 @Qualifier注解源码如下所示。 packageorg.springframework.beans.factory.annotation;importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Inherited;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Target({ElementType.FIELD,ElementType.METHOD,ElementType.PARAMETER,ElementType.TYPE,ElementType.ANNOTATION_TYPE})@Retention(RetentionPolicy.RUNTIME)@Inherited@Documentedpublic@interfaceQualifier{Stringvalue()default"";} @Primary注解 在Spring 中使用注解,常使用@Autowired, 默认是根据类型Type来自动注入的。但有些特殊情况,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种实现的情况下, 就可以使用@Primary注解来标注优先使用哪一个实现类。 @Primary注解的源码如下所示。 packageorg.springframework.context.annotation;importjava.lang.annotation.Documented;importjava.lang.annotation.ElementType;importjava.lang.annotation.Retention;importjava.lang.annotation.RetentionPolicy;importjava.lang.annotation.Target;@Target({ElementType.TYPE,ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME)@Documentedpublic@interfacePrimary{} 自动装配 在进行项目实战之前,我们先来说说什么是Spring组件的自动装配。Spring组件的自动装配就是:Spring利用依赖注入,也就是我们通常所说的DI,完成对IOC容器中各个组件的依赖关系赋值。 项目实战 测试@Autowired注解 这里,我们以之前项目中创建的dao、service和controller为例进行说明。dao、service和controller的初始代码分别如下所示。 dao packageio.mykit.spring.plugins.register.dao;importorg.springframework.stereotype.Repository;/***@authorbinghe*@version1.0.0*@description测试的dao*/@RepositorypublicclassPersonDao{} service packageio.mykit.spring.plugins.register.service;importio.mykit.spring.plugins.register.dao.PersonDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/***@authorbinghe*@version1.0.0*@description测试的Service*/@ServicepublicclassPersonService{@AutowiredprivatePersonDaopersonDao;} controller packageio.mykit.spring.plugins.register.controller;importorg.springframework.stereotype.Controller;/***@authorbinghe*@version1.0.0*@description测试的controller*/@ControllerpublicclassPersonController{@AutowiredprivatePersonServicepersonService;} 可以看到,我们在Service中使用@Autowired注解注入了Dao,在Controller中使用@Autowired注解注入了Service。为了方便测试,我们在PersonService类中生成一个toString()方法,如下所示。 packageio.mykit.spring.plugins.register.service;importio.mykit.spring.plugins.register.dao.PersonDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/***@authorbinghe*@version1.0.0*@description测试的Service*/@ServicepublicclassPersonService{@AutowiredprivatePersonDaopersonDao;@OverridepublicStringtoString(){returnpersonDao.toString();}} 这里,我们在PersonService类的toString()方法中直接调用personDao的toString()方法并返回。为了更好的演示效果,我们在项目的 io.mykit.spring.plugins.register.config 包下创建AutowiredConfig类,如下所示。 packageio.mykit.spring.plugins.register.config;importorg.springframework.context.annotation.ComponentScan;importorg.springframework.context.annotation.Configuration;/***@authorbinghe*@version1.0.0*@description测试自动装配组件的Config配置类*/@Configuration@ComponentScan(value={"io.mykit.spring.plugins.register.dao","io.mykit.spring.plugins.register.service","io.mykit.spring.plugins.register.controller"})publicclassAutowiredConfig{} 接下来,我们来测试一下上面的程序,我们在项目的src/test/java目录下的 io.mykit.spring.test 包下创建AutowiredTest类,如下所示。 packageio.mykit.spring.test;importio.mykit.spring.plugins.register.config.AutowiredConfig;importio.mykit.spring.plugins.register.service.PersonService;importorg.junit.Test;importorg.springframework.context.annotation.AnnotationConfigApplicationContext;/***@authorbinghe*@version1.0.0*@description测试自动装配*/publicclassAutowiredTest{@TestpublicvoidtestAutowired01(){//创建IOC容器AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(AutowiredConfig.class);PersonServicepersonService=context.getBean(PersonService.class);System.out.println(personService);context.close();}} 测试方法比较简单,这里,我就不做过多说明了。接下来,我们运行AutowiredTest类的testAutowired01()方法,得出的输出结果信息如下所示。 io.mykit.spring.plugins.register.dao.PersonDao@10e92f8f 可以看到,输出了PersonDao信息。 那么问题来了:我们在PersonService类中输出的PersonDao,和我们直接在Spring IOC容器中获取的PersonDao是不是同一个对象呢? 我们可以在AutowiredTest类的testAutowired01()方法中添加获取PersonDao对象的方法,并输出获取到的PersonDao对象,如下所示。 @TestpublicvoidtestAutowired01(){//创建IOC容器AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(AutowiredConfig.class);PersonServicepersonService=context.getBean(PersonService.class);System.out.println(personService);PersonDaopersonDao=context.getBean(PersonDao.class);System.out.println(personDao);context.close();} 我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 io.mykit.spring.plugins.register.dao.PersonDao@10e92f8fio.mykit.spring.plugins.register.dao.PersonDao@10e92f8f 可以看到,我们在PersonService类中输出的PersonDao对象和直接从IOC容器中获取的PersonDao对象是同一个对象。 如果在Spring容器中存在对多个PersonDao对象该如何处理呢? 首先,为了更加直观的看到我们使用@Autowired注解装配的是哪个PersonDao对象,我们对PersonDao类进行改造,为其加上一个remark字段,为其赋一个默认值,如下所示。 packageio.mykit.spring.plugins.register.dao;importlombok.AllArgsConstructor;importlombok.Data;importlombok.NoArgsConstructor;importorg.springframework.stereotype.Repository;/***@authorbinghe*@version1.0.0*@description测试的dao*/@Data@NoArgsConstructor@AllArgsConstructor@RepositorypublicclassPersonDao{privateStringremark="1";} 接下来,我们就在AutowiredConfig类中注入一个PersonDao对象,并且显示指定PersonDao对象在IOC容器中的bean的名称为personDao2,并为PersonDao对象的remark字段赋值为2,如下所示。 @Bean("personDao2")publicPersonDaopersonDao(){returnnewPersonDao("2");} 目前,在我们的IOC容器中就会注入两个PersonDao对象。那此时,@Autowired注解装配的是哪个PersonDao对象呢? 接下来,我们运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 PersonDao{remark='1'} 可以看到,结果信息输出了1,说明:@Autowired注解默认优先按照类型去容器中找对应的组件,找到就赋值;如果找到多个相同类型的组件,再将属性名称作为组件的id,到 IOC 容器中进行查找。 那我们如何让@Autowired装配personDao2呢? 这个问题问的好,其实很简单,我们将PersonService类中的personDao全部修改为personDao2,如下所示。 packageio.mykit.spring.plugins.register.service;importio.mykit.spring.plugins.register.dao.PersonDao;importorg.springframework.beans.factory.annotation.Autowired;importorg.springframework.stereotype.Service;/***@authorbinghe*@version1.0.0*@description测试的Service*/@ServicepublicclassPersonService{@AutowiredprivatePersonDaopersonDao2;@OverridepublicStringtoString(){returnpersonDao2.toString();}} 此时,我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 PersonDao{remark='2'} 可以看到,此时命令行输出了personDao2的信息。 测试@Qualifier注解 从测试@Autowired注解的结果来看:@Autowired注解默认优先按照类型去容器中找对应的组件,找到就赋值;如果找到多个相同类型的组件,再将属性名称作为组件的id,到 IOC 容器中进行查找。 如果IOC容器中存在多个相同类型的组件时,我们可不可以显示指定@Autowired注解装配哪个组件呢?有些小伙伴肯定会说:废话!你都这么问了,那肯定可以啊!没错,确实可以啊!此时,@Qualifier注解就派上用场了! 在之前的测试案例中,命令行输出了 PersonDao{remark='2'} 说明@Autowired注解装配了personDao2,那我们如何显示的让@Autowired注解装配personDao呢? 比较简单,我们只需要在PersonService类上personDao2字段上添加@Qualifier注解,显示指定@Autowired注解装配personDao,如下所示。 @Qualifier("personDao")@AutowiredprivatePersonDaopersonDao2; 此时,我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 PersonDao{remark='1'} 可以看到,此时尽管字段的名称为personDao2,但是我们使用了@Qualifier注解显示指定@Autowired注解装配personDao对象,所以,最终的结果输出了personDao对象的信息。 测试容器中无组件的情况 如果IOC容器中无相应的组件,会发生什么情况呢?此时,我们删除PersonDao类上的@Repository注解,并且删除AutowiredConfig类中的personDao()方法上的@Bean注解,如下所示。 packageio.mykit.spring.plugins.register.dao;/***@authorbinghe*@version1.0.0*@description测试的dao*/publicclassPersonDao{privateStringremark="1";publicStringgetRemark(){returnremark;}publicvoidsetRemark(Stringremark){this.remark=remark;}@OverridepublicStringtoString(){return"PersonDao{"+"remark='"+remark+'\''+'}';}} packageio.mykit.spring.plugins.register.config;importio.mykit.spring.plugins.register.dao.PersonDao;importorg.springframework.context.annotation.ComponentScan;importorg.springframework.context.annotation.Configuration;/***@authorbinghe*@version1.0.0*@description测试自动装配组件的Config配置类*/@Configuration@ComponentScan(value={"io.mykit.spring.plugins.register.dao","io.mykit.spring.plugins.register.service","io.mykit.spring.plugins.register.controller"})publicclassAutowiredConfig{publicPersonDaopersonDao(){PersonDaopersonDao=newPersonDao();personDao.setRemark("2");returnpersonDao;}} 此时IOC容器中不再有personDao,我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 Causedby:org.springframework.beans.factory.NoSuchBeanDefinitionException:Noqualifyingbeanoftype'io.mykit.spring.plugins.register.dao.PersonDao'available:expectedatleast1beanwhichqualifiesasautowirecandidate.Dependencyannotations:{@org.springframework.beans.factory.annotation.Qualifier(value=personDao),@org.springframework.beans.factory.annotation.Autowired(required=true)} 可以看到,Spring抛出了异常,未找到相应的bean对象,我们能不能让Spring不报错呢? 那肯定可以啊!Spring的异常信息中都给出了相应的提示。 {@org.springframework.beans.factory.annotation.Qualifier(value=personDao),@org.springframework.beans.factory.annotation.Autowired(required=true)} 解决方案就是在PersonService类的@Autowired添加一个属性required=false,如下所示。 @Qualifier("personDao")@Autowired(required=false)privatePersonDaopersonDao2; 并且我们修改下PersonService的toString()方法,如下所示。 @OverridepublicStringtoString(){return"PersonService{"+"personDao2="+personDao2+'}';} 此时,还需要将AutowiredTest类的testAutowired01()方法中直接从IOC容器中获取personDao的代码删除,如下所示。 @TestpublicvoidtestAutowired01(){//创建IOC容器AnnotationConfigApplicationContextcontext=newAnnotationConfigApplicationContext(AutowiredConfig.class);PersonServicepersonService=context.getBean(PersonService.class);System.out.println(personService);context.close();} 此时,我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 PersonService{personDao2=null} 可以看到,当为@Autowired添加属性required=false后,即使IOC容器中没有对应的对象,Spring也不会抛出异常。此时,装配的对象就为null。 测试完成后,我们再次为PersonDao类添加@Repository注解,并且为AutowiredConfig类中的personDao()方法添加@Bean注解。 测试@Primary注解 在Spring中,对同一个接口,可能会有几种不同的实现类,而默认只会采取其中一种实现的情况下, 就可以使用@Primary注解来标注优先使用哪一个实现类。 首先,我们在AutowiredConfig类的personDao()方法上添加@Primary注解,此时,我们需要删除PersonService类中personDao字段上的@Qualifier注解,这是因为@Qualifier注解为显示指定装配哪个组件,如果使用了@Qualifier注解,无论是否使用了@Primary注解,都会装配@Qualifier注解标注的对象。 设置完成后,我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 PersonService{personDao2=PersonDao{remark='2'}} 可以看到,此时remark的值为2,装配了AutowiredConfig类中注入的personDao。 接下来,我们为PersonService类中personDao字段再次添加@Qualifier注解,如下所示。 @Qualifier("personDao")@Autowired(required=false)privatePersonDaopersonDao; 此时,我们再次运行AutowiredTest类的testAutowired01()方法,输出的结果信息如下所示。 PersonService{personDao=PersonDao{remark='1'}} 可以看到,此时,Spring装配了使用@Qualifier标注的personDao。 重磅福利 关注「 冰河技术 」微信公众号,后台回复 “设计模式” 关键字领取《深入浅出Java 23种设计模式》PDF文档。回复“Java8”关键字领取《Java8新特性教程》PDF文档。回复“限流”关键字获取《亿级流量下的分布式限流解决方案》PDF文档,三本PDF均是由冰河原创并整理的超硬核教程,面试必备!! 好了,今天就聊到这儿吧!别忘了点个赞,给个在看和转发,让更多的人看到,一起学习,一起进步!! 写在最后 如果你觉得冰河写的还不错,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发、分布式、微服务、大数据、互联网和云原生技术,「 冰河技术 」微信公众号更新了大量技术专题,每一篇技术文章干货满满!不少读者已经通过阅读「 冰河技术 」微信公众号文章,吊打面试官,成功跳槽到大厂;也有不少读者实现了技术上的飞跃,成为公司的技术骨干!如果你也想像他们一样提升自己的能力,实现技术能力的飞跃,进大厂,升职加薪,那就关注「 冰河技术 」微信公众号吧,每天更新超硬核技术干货,让你对如何提升技术能力不再迷茫! 后记: 记住:你比别人强的地方,不是你做过多少年的CRUD工作,而是你比别人掌握了更多深入的技能。不要总停留在CRUD的表面工作,理解并掌握底层原理并熟悉源码实现,并形成自己的抽象思维能力,做到灵活运用,才是你突破瓶颈,脱颖而出的重要方向! 你在刷抖音,玩游戏的时候,别人都在这里学习,成长,提升,人与人最大的差距其实就是思维。你可能不信,优秀的人,总是在一起。。 扫一扫或长按二维码 关注冰河技术微信公众号 如果你喜欢这篇文章,欢迎点赞和转发。 生活很美好,明天见(。・ω・。)ノ♡ 写留言 本文分享自微信公众号 - 冰河技术(hacker-binghe)。如有侵权,请联系 support@oschina.cn 删除。本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一起分享。

优秀的个人博客,低调大师

【漫画】最近,老王又双叒get了CDN的新技能—可编程化敏捷开发

来全民加速节,听课程,购买CDN更划算 本次全民加速节,阿里云CDN诚意满满,推出《最省心CDN选购指南》《技术专家带你快速上手CDN》《阿里云CDN产品解读及全站加速在游戏行业的最佳实践》等丰富的在线课程 点击预约本周三的直播 时间: 8月12日15:00-16:00 讲师: 寒丰 阿里云CDN产品专家 研一 阿里云CDN解决方案架构师 演讲议题: 《阿里云CDN产品解读》 《DCDN全站加速在游戏行业的最佳实践》 点击进入活动页面,观看其他直播回放 产品优惠 同时全民加速节期间,阿里云CDN也为用户设置了多重优惠。 第一重:活动期间,用户可以领取活动专属的9850元代金券,当订单金额满足条件即可享受满减。第二重:爆款产品,CDN/全站加速流量包50TB,活动期间仅需4999元每年。第三重:新客户专项优惠,500GB 49.9元/年,1TB 99元/年,5TB 499元/年和10TB 999元/年。第四重,除了上述优惠,其他规格的产品套餐,在全民加速节期间也提供特惠,下行流量包购买有效期1年9折特惠,有效期1个月8.5折特惠。另外,静态https请求数包和动态请求数包9折特惠。 点击进入全民加速节页面,观看直播回放+领取优惠+查看活动详情

资源下载

更多资源
Mario

Mario

马里奥是站在游戏界顶峰的超人气多面角色。马里奥靠吃蘑菇成长,特征是大鼻子、头戴帽子、身穿背带裤,还留着胡子。与他的双胞胎兄弟路易基一起,长年担任任天堂的招牌角色。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册