• Spring 系列之Spring常用注解总结


      传统的Spring做法是使用.xml文件来对bean进行注入或者是配置aop、实物,这么做有两个缺点:

    1、如果所有的内容都配置在.xml文件中,那么.xml文件将会十分庞大;如果按需求分开.xml文件,那么.xml文件又会非常多。总之这将导致配置文件的可读性与可维护性变得很低。

    2、在开发中.java文件和.xml文件之间不断切换,是一件麻烦的事,同时这种思维上的不连贯也会降低开发的效率。

      为了解决这两个问题,Spring引入了注解,通过“@XXX”的方  式,让注解与Java Bean紧密结合,既大大减少了配置文件的体积,又增加了Java Bean的可读性与内聚性。

      不使用注解:

      先看一个不使用注解的Spring示例,在这个示例的基础上,改成注解版本的,这样也能看出使用与不使用注解之间的区别,先定义一个老虎:

    package com.spring.model;
    public class Tiger{
      private String  tigerName="TigerKing";
      public String toString(){
        return "TigerName:"+tigerName;
      }
    }

      再定义一个猴子:

    package com.spring.model;
    public class Monkey{
        private String monkeyName="MonkeyKing";
        public String toString(){
           return "MonkeyName:"+monkeyName;   
      }
    }    

      定义一个动物园:

    package com.spring.model;
    public class Zoo{
        private  Tiger tiger;
        private Monkey monkey;
        
        public Tiger getTiger(){
           return tiger;
         } 
        public void setTiger(Tiger tiger){
            this.tiger=tiger;
         }
        public Monkey getMonkey(){
            return monkey;
         }
        public void setMonkey(Monkey monkey){
            this.monkey=monkey;
        }
         public String toString(){
             return tiger+"
    "+monkey;
        }
    }

      spring的配置文件这么写:

    <?xml version="1.0" encoding="UTF-8"?>
    <beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:p="http://www.springframework.org/schema/p"
        xmlns:context="http://www.springframework.org/schema/context"
        xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-3.0.xsd
        ">
        
         <bean id="zoo" class="com.spring.model.Zoo" >
            <property name="tiger" ref="tiger" />
            <property name="monkey" ref="monkey" />
        </bean>
        
        <bean id="tiger" class="com.spring.model.Tiger" />
        <bean id="monkey" class="com.spring.model.Monkey" />
    
    </beans>

      测试方法:

    public class TestAnnotation{
      /**
        *不使用注解
        */  
        @Test
       public void test(){
          //读取配置文件
          ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext2.xml");
          Zoo zoo=(Zoo) ctx.getBean("zoo");
         System.out.print(zoo.toString());
      }
    }

      都很熟悉,权当复习一遍了。

      1、@AutoWired

      @Autowired顾名思义,就是自动装配,其作用是为了消除Java代码里面的getter/setter与bean属性中的property。当然,getter看个人需求,如果私有属性需要对外提供的话,应当予以保留。@Autowirred默认按类型匹配的方式,在容器查找匹配的Bean,当且仅有一个匹配的Bean时,Spring将其注入@AutoWired标注的变量中。

      因此,引入@AutoWired注解,先看下Spring配置文件怎么写:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans
     3     xmlns="http://www.springframework.org/schema/beans"
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5     xmlns:p="http://www.springframework.org/schema/p"
     6     xmlns:context="http://www.springframework.org/schema/context"
     7     xsi:schemaLocation="http://www.springframework.org/schema/beans
     8     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     9     http://www.springframework.org/schema/context
    10     http://www.springframework.org/schema/context/spring-context-3.0.xsd
    11     ">
    12     
    13     <context:component-scan base-package="com.spring"/>
    14     
    15     <bean id="zoo" class="com.spring.model.Zoo" />
    16     <bean id="tiger" class="com.spring.model.Tiger" />
    17     <bean id="monkey" class="com.spring.model.Monkey" />
    18 
    19 </beans>

      注意第13行,使用必须告诉spring一下我要使用注解了,<context:component-scan base-package="XXX"/>是一种简单的,spring会自动扫描xxx路径下的注解。

      看到第15行,原来zoo里面应当注入两个属性tigger、monkey,现在不需要注入了。再看下,Zoo.java也很方便,把getter/setter都可以去掉:

    package com.spring.model;
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class Zoo{
        @Autowired
        private Tiger tiger;
    @Autowired
    private Monkey monkey;
    public String toString(){ return tiger+" "+monkey; } }

      这里@Autowired注解的意思就是,当Spring发现@Autowired注解时,将自动在代码上下文中找到和其匹配(默认是类型匹配)的Bean,并自动注入到相应的地方去。

      有一个细节性的问题是,假如bean里面有两个property,Zoo.java里面又去掉了属性的getter/setter并使用@Autowired注解标注这两个属性那会怎么样?答案是Spring会按照xml优先的原则去Zoo.java中寻找这两个属性的getter/setter,导致的结果就是初始化bean报错。

      OK,假设此时我把.xml文件的16行、17行两行给去掉,再运行,会抛出异常:

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'zoo': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.spring.model.Tiger com.spring.model.Zoo.tiger; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.spring.model.Tiger] 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:285)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
        at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
        at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
        at com.spring.test.TestAnnotation.test(TestAnnotation.java:16)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.spring.model.Tiger com.spring.model.Zoo.tiger; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.spring.model.Tiger] 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:502)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:282)
        ... 36 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No matching bean of type [com.spring.model.Tiger] 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:920)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:789)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:703)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:474)
        ... 38 more

      因为,@Autowired注解要去寻找的是一个Bean,Tiger和Monkey的Bean定义都给去掉了,自然就不是一个Bean了,Spring容器找不到也很好理解。那么,如果属性找不到我不想让Spring容器抛出异常,而就是显示null,可以吗?可以的,其实异常信息里面也给出了提示了,就是将@Autowired注解的required属性设置为false即可:

    package com.spring.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    public class Zoo {
        
        @Autowired(required=false)
        private Tiger tiger;
        
        @Autowired(required=false)
        private Monkey monkey;
        
        public String toString(){
            return tiger + "
    " + monkey;
        }
        
    }

    此时,找不到tiger、monkey两个属性,Spring容器不再抛出异常而是认为这两个属性为null。

    2、Qualifier(指定注入Bean的名称)

      如果容器中有一个以上匹配的Bean,则可以通过@Qualifier注解限定Bean的名称,看下面的例子:

    定义一个Car接口:

    package com.spring.service;
    
    public interface ICar {
        
        public String getCarName();
    }

      两个实现类BMWCar和BenzCar:

    package com.spring.service.impl;
    
    import com.spring.service.ICar;
    
    public class BMWCar implements ICar{
        
        public String getCarName(){
            return "BMW car";
        }
    }
    package com.spring.service.impl;
    
    import com.spring.service.ICar;
    
    public class BenzCar implements ICar{
        
        public String getCarName(){
            return "Benz car";
        }
    }

      再写一个CarFactory,引用car(这里先不用@Qualifier注解):

    package com.spring.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    
    import com.spring.service.ICar;
    
    public class CarFactory {
        
        @Autowired
        private ICar car;
        
        public String toString(){
            return car.getCarName();
        }
        
    }

      配置文件:

     1 <?xml version="1.0" encoding="UTF-8"?>
     2 <beans
     3     xmlns="http://www.springframework.org/schema/beans"
     4     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     5     xmlns:p="http://www.springframework.org/schema/p"
     6     xmlns:context="http://www.springframework.org/schema/context"
     7     xsi:schemaLocation="http://www.springframework.org/schema/beans
     8     http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
     9     http://www.springframework.org/schema/context
    10     http://www.springframework.org/schema/context/spring-context-3.0.xsd
    11     ">
    12     
    13     <context:component-scan base-package="com.spring" />
    14     
    15     <!-- Autowired注解配合Qualifier注解 -->
    16     <bean id="carFactory" class="com.spring.model.CarFactory" />
    17     <bean id="bmwCar" class="com.spring.service.impl.BMWCar" />
    18     <bean id="benz" class="com.spring.service.impl.BenzCar" />
    19     
    20 </beans>

      测试方法:

    /**
     * Autowired注解配合Qualifier注解
     */
    @Test
    public void test1(){
        //读取配置文件
        ApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext2.xml");
        CarFactory carFactory=(CarFactory) ctx.getBean("carFactory");
        System.out.println(carFactory.toString());
    }

      运行一下,不用说,一定是报错的,Car接口有两个实现类,Spring并不知道应当引用哪个实现类。

    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'carFactory': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: 
    Could not autowire field: private com.spring.service.ICar com.spring.model.CarFactory.car; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException:
    No unique bean of type [com.spring.service.ICar] is defined: expected single matching bean but found 2: [bmwCar, benz]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:285)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1074)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:291)
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:288)
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:190)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:580)
        at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:895)
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:425)
        at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
        at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
        at com.spring.test.TestAnnotation.test1(TestAnnotation.java:25)
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
        at java.lang.reflect.Method.invoke(Method.java:597)
        at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:45)
        at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:15)
        at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:42)
        at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:20)
        at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:263)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:68)
        at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:47)
        at org.junit.runners.ParentRunner$3.run(ParentRunner.java:231)
        at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:60)
        at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:229)
        at org.junit.runners.ParentRunner.access$000(ParentRunner.java:50)
        at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:222)
        at org.junit.runners.ParentRunner.run(ParentRunner.java:300)
        at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:45)
        at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
        at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
    Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.spring.service.ICar com.spring.model.CarFactory.car; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.spring.service.ICar] is defined: expected single matching bean but found 2: [bmwCar, benz]
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:502)
        at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:84)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:282)
        ... 36 more
    Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.spring.service.ICar] is defined: expected single matching bean but found 2: [bmwCar, benz]
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:796)
        at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:703)
        at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:474)
        ... 38 more

    出现这种情况通常有两种解决办法:
    (1)、在配置文件中删除其中一个实现类,Spring会自动去base-package下寻找Car接口的实现类,发现Car接口只有一个实现类,便会直接引用这个实现类。
    (2)、实现类就是有多个该怎么办?此时可以使用@Qualifier注解来指定Bean的名称:

    package com.spring.model;
    
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Qualifier;
    
    import com.spring.service.ICar;
    
    public class CarFactory {
        
        @Autowired
        @Qualifier("bmwCar")
        private ICar car;
        
        public String toString(){
            return car.getCarName();
        }
        
    }

    此处会注入名为"bmwCar"的Bean。

    3、Resource

    @Resource注解与@Autowired注解作用非常相似,这个就简单说了,看例子:

    package com.spring.model;
    
    import javax.annotation.Resource;
    
    public class Zoo1 {
        
        @Resource(name="tiger")
        private Tiger tiger;
        
        @Resource(type=Monkey.class)
        private Monkey monkey;
        
        public String toString(){
            return tiger + "
    " + monkey;
        }
    }

    这是详细一些的用法,说一下@Resource的装配顺序:
    (1)、@Resource后面没有任何内容,默认通过name属性去匹配bean,找不到再按type去匹配
    (2)、指定了name或者type则根据指定的类型去匹配bean
    (3)、指定了name和type则根据指定的name和type去匹配bean,任何一个不匹配都将报错

    然后,区分一下@Autowired和@Resource两个注解的区别:
    (1)、@Autowired默认按照byType方式进行bean匹配,@Resource默认按照byName方式进行bean匹配
    (2)、@Autowired是Spring的注解,@Resource是J2EE的注解,这个看一下导入注解的时候这两个注解的包名就一清二楚了
    Spring属于第三方的,J2EE是Java自己的东西,因此,建议使用@Resource注解,以减少代码和Spring之间的耦合。

  • 相关阅读:
    P1026 统计单词个数
    常见的DP优化类型
    中国空气质量在线监測分析平台
    Linux Kernel系列一:开篇和Kernel启动概要
    Android入门第八篇之GridView(九宫图)
    eclipse+webservice开发实例
    【java读书笔记】——java开篇宏观把控 + HelloWorld
    选择排序
    The encryption certificate of the relying party trust identified by thumbprint is not valid
    【传递正能量】2014年感动我的十大微视频
  • 原文地址:https://www.cnblogs.com/xianfengzhike/p/9096824.html
Copyright © 2020-2023  润新知