zoukankan      html  css  js  c++  java
  • Spring实战——通过Java代码装配bean

      上篇说的是无需半行xml配置完成bean的自动化注入。这篇仍然不要任何xml配置,通过Java代码也能达到同样的效果。

      这么说,是要把上篇的料拿出来再煮一遍? 当然不是,上篇我们几乎都在用注解的方式如@ComponentScan @Component等就完成了自动化注入,但是这些注解不是无所不能的,有些地方它也是望尘莫及的,比如第三方类库,你总不能跑到人家jar包或者库里面悄悄的加上这些注解再伪装成原来的样子用吧,太丑了! 所以,Spring可以通过显示配置的方式来解决,第一种前面有介绍过,就是通过xml来显示声明bean,第二种就是这里要介绍的基于Java代码方式装配bean。

      基于注解的自动化注入固然优雅,但是它也有鞭长莫及的时候,这时候就来看看Java代码如何装配bean的。

      还记的上篇的配置类CDPlayerConfig是长这样的

    @Configuration
    @ComponentScan
    public class CDPlayerConfig { 
    }
    

      有了这个万能的组件扫描注解,一切都是那么只能,只需要在bean类上加上如@Component注解,Spring就会自动为该类创建相应的bean类。

      但是现在因为我们有一些第三方类库,我们没办法去深入类中加上这些标记了,所以@ComponentScan就失去了威力和意义。

      这篇的CDPlayerConfig应该长这样

    @Configuration
    public class CDPlayerConfig { 
    }
    

      @Configuration注解表示该类是一个配置类。显然要创建的bean的信息是要放到这个类中的。

      基于其他类以及类中的方法不变比如CompactDisc, CDPlayer, SgtPeppers等。编写测试方法如下

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes=CDPlayerConfig.class)
    public class CDPlayerTest {
    
      @Rule
      public final StandardOutputStreamLog log = new StandardOutputStreamLog();
    
      @Autowired
      private MediaPlayer player;
      
      @Autowired
      private CompactDisc cd;
      
      @Test
      public void cdShouldNotBeNull() {
        assertNotNull(cd);
      }
    
      @Test
      public void play() {
        player.play();
        assertEquals(
            "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
    ",
            log.getLog());
      }
    
    }
    

      首先从代码来看就会出现如下的注入错误

      这里显示MediaPlayer无法注入。同时运行程序得到结果如下

    Testing started at 0:20 ...
    0:20:03: Executing external tasks 'cleanTest test'...
    :cleanTest
    :compileJava
    :processResources UP-TO-DATE
    :classes
    :compileTestJava
    :processTestResources UP-TO-DATE
    :testClasses
    :test
    一月 15, 2017 12:20:06 上午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.web.ServletTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [javax/servlet/ServletContext]
    一月 15, 2017 12:20:06 上午 org.springframework.test.context.TestContextManager retrieveTestExecutionListeners
    信息: Could not instantiate TestExecutionListener [org.springframework.test.context.transaction.TransactionalTestExecutionListener]. Specify custom listener classes or make the default listener classes (and their required dependencies) available. Offending class: [org/springframework/transaction/interceptor/TransactionAttributeSource]
    一月 15, 2017 12:20:06 上午 org.springframework.context.support.GenericApplicationContext prepareRefresh
    信息: Refreshing org.springframework.context.support.GenericApplicationContext@6adf0b01: startup date [Sun Jan 15 00:20:06 CST 2017]; root of context hierarchy
    一月 15, 2017 12:20:06 上午 org.springframework.test.context.TestContextManager prepareTestInstance
    严重: Caught exception while allowing TestExecutionListener [org.springframework.test.context.support.DependencyInjectionTestExecutionListener@61c715e8] to prepare test instance [soundsystem.CDPlayerTest@1081b08c]
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'soundsystem.CDPlayerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private soundsystem.MediaPlayer soundsystem.CDPlayerTest.player; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:293)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1186)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:384)
    	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
    	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:331)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:213)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:290)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:292)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:105)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:56)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:64)
    	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:497)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    	at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    	at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:106)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:497)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    	at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    	at java.lang.Thread.run(Thread.java:745)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private soundsystem.MediaPlayer soundsystem.CDPlayerTest.player; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:509)
    	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:290)
    	... 46 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1118)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:967)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862)
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:481)
    	... 48 more
    
    
    Error creating bean with name 'soundsystem.CDPlayerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private soundsystem.MediaPlayer soundsystem.CDPlayerTest.player; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'soundsystem.CDPlayerTest': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private soundsystem.MediaPlayer soundsystem.CDPlayerTest.player; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:293)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1186)
    	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.autowireBeanProperties(AbstractAutowireCapableBeanFactory.java:384)
    	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.injectDependencies(DependencyInjectionTestExecutionListener.java:110)
    	at org.springframework.test.context.support.DependencyInjectionTestExecutionListener.prepareTestInstance(DependencyInjectionTestExecutionListener.java:75)
    	at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:331)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.createTest(SpringJUnit4ClassRunner.java:213)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner$1.runReflectiveCall(SpringJUnit4ClassRunner.java:290)
    	at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.methodBlock(SpringJUnit4ClassRunner.java:292)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:233)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:87)
    	at org.junit.runners.ParentRunner$3.run(ParentRunner.java:238)
    	at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:63)
    	at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:236)
    	at org.junit.runners.ParentRunner.access$000(ParentRunner.java:53)
    	at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:229)
    	at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    	at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:71)
    	at org.junit.runners.ParentRunner.run(ParentRunner.java:309)
    	at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:176)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.runTestClass(JUnitTestClassExecuter.java:105)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassExecuter.execute(JUnitTestClassExecuter.java:56)
    	at org.gradle.api.internal.tasks.testing.junit.JUnitTestClassProcessor.processTestClass(JUnitTestClassProcessor.java:64)
    	at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.processTestClass(SuiteTestClassProcessor.java:50)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:497)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.messaging.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:32)
    	at org.gradle.messaging.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:93)
    	at com.sun.proxy.$Proxy2.processTestClass(Unknown Source)
    	at org.gradle.api.internal.tasks.testing.worker.TestWorker.processTestClass(TestWorker.java:106)
    	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    	at java.lang.reflect.Method.invoke(Method.java:497)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:35)
    	at org.gradle.messaging.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
    	at org.gradle.messaging.remote.internal.hub.MessageHub$Handler.run(MessageHub.java:360)
    	at org.gradle.internal.concurrent.ExecutorPolicy$CatchAndRecordFailures.onExecute(ExecutorPolicy.java:54)
    	at org.gradle.internal.concurrent.StoppableExecutorImpl$1.run(StoppableExecutorImpl.java:40)
    	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
    	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
    	at java.lang.Thread.run(Thread.java:745)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private soundsystem.MediaPlayer soundsystem.CDPlayerTest.player; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:509)
    	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:290)
    	... 46 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [soundsystem.MediaPlayer] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1118)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:967)
    	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:862)
    	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:481)
    	... 48 more
    
    soundsystem.CDPlayerTest > play FAILED
        org.springframework.beans.factory.BeanCreationException
            Caused by: org.springframework.beans.factory.BeanCreationException
                Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException
    1 test completed, 1 failed
    :test FAILED
    FAILURE: Build failed with an exception.
    * What went wrong:
    Execution failed for task ':test'.
    > There were failing tests. See the report at: file:///C:/Users/Administrator/Desktop/spring_video/SpringiA4_SourceCode/Chapter_02/stereo-javaconfig/build/reports/tests/index.html
    * Try:
    Run with --stacktrace option to get the stack trace. Run with --info or --debug option to get more log output.
    BUILD FAILED
    Total time: 4.59 secs
    There were failing tests. See the report at: file:///C:/Users/Administrator/Desktop/spring_video/SpringiA4_SourceCode/Chapter_02/stereo-javaconfig/build/reports/tests/index.html
    

      究其原因,主要是既没有在xml中没有声明相应的bean,也没有添加@ComponentScan启动自动扫描组件机制。

      所以这里还是需要在CDPlayerConfig配置类中做文章。

      声明bean

      通过注解@Bean声明一个bean对象。

    @Bean
      public CompactDisc compactDisc() {
        return new SgtPeppers();
      }
    

      该声明好比在xml中添加

    <bean id="sgtPeppers" class="soundsystem.SgtPeppers"/>

      通过这种方式,Spring会默认为SgtPeppers创建一个名为sgtPeppers的bean,如果你想换个名字,只需要在@Bean后面加上类似(name="lonelyHeartClubBand")这样的属性即可。

      如果你的野心远不止满足于通过Java代码创建一个bean的话,其实你还试试如何在CDPlayer中播放CompactDisc,那就需要在CDPlayer中注入CompactDisc的bean了。于是我们可以这样声明

    @Bean
      public CDPlayer cdPlayer(CompactDisc compactDisc) {
        return new CDPlayer(compactDisc);
      }
    

      加上@Bean注解,表明cdPlayer()方法会创建一个bean实例并将其注册到Spring的应用上下文中,显然bean的名字默认就是cdPlayer。在执行这个方法的时候,Spring会拦截所有对方法的调用,当然这里会传入Spring注册好的CompactDisc的实例bean给cdPlayer方法来确保返回的是创建好的cdPlayer的bean。注意这里的bean是单例的,其实在xml中配置的bean如果没有特殊声明,默认也是单例的。也就是说即使这里创建了多个类似cdPlayer的方法,得到的仍然是同一个cdPlayer的bean。

      这时候运行测试类CDPlayerTest

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes=CDPlayerConfig.class)
    public class CDPlayerTest {
    
      @Rule
      public final StandardOutputStreamLog log = new StandardOutputStreamLog();
    
      @Autowired
      private MediaPlayer player;
    
      @Test
      public void play() {
        player.play();
        assertEquals(
            "Playing Sgt. Pepper's Lonely Hearts Club Band by The Beatles
    ",
            log.getLog());
      }
    
    }
    

      

      测试通过。

      表明CDPlayer注入成功。前面通过Java代码注册的Bean有效。

    如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!如果您想持续关注我的文章,请扫描二维码,关注JackieZheng的微信公众号,我会将我的文章推送给您,并和您一起分享我日常阅读过的优质文章。

  • 相关阅读:
    MariaDB · 版本特性 · MariaDB 的 GTID 介绍
    stm8s 中断重复进入
    PCB积累
    链表的创建、增加、删除、改数据、遍历
    百度文库文字下载工具指引
    防倒灌的开关电路
    AD快速从原理图查找pcb中元件
    三目运算符填坑
    嵌入式结构化分层思想
    原码,反码,补码
  • 原文地址:https://www.cnblogs.com/bigdataZJ/p/SpringInAction2.html
Copyright © 2011-2022 走看看