使用常用的注解配置Spring容器


注解配置 spring与基于 xml配置 spring那种方式好,其实是要看情况的,各有各的好

  • 注解配置:使配置更简短,简洁

  • XML配置:擅长连接组件而不需要接触源代码和重新编译。

:注解配置spring 会在基于XML 配置 spring 之前执行的,所以你的上下文中即使用了注解配置和XML配置,如果两个都配置了同一个Bean,那么基于XML配置的会覆盖前面注解配置的数据。

在使用注解配置spring前需要先注册相应注解的类,例如,如果需要使用Autowired注解注入属性值,先得把处理这个注解的类org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor注册到IOC容器中才能使用Autowired的功能,注册这些处理类的方式有两种,一种是显示的注入的IOC容器中,另一种就是在XML中配置<context:annotation-config/>隐式的注入容器中。

XML中配置<context:annotation-config/>隐式注入注解的配置类

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <context:annotation-config/>
</beans>

XML中配置<context:annotation-config/>显示的注入注解的配置类

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <!--<context:annotation-config/>-->
        <bean id="required"     class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
</beans>

以上方式其中在XML中添加<context:annotation-config/>会隐式的注册一些BeanPostProcessor 如下:

  • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
  • org.springframework.context.annotation.CommonAnnotationBeanPostProcessor
  • org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor
  • org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor

:<context:annotation-config/>仅仅会检测与它定义的同一个上下文中的bean,不会检测到其他上下文中的注解,如果你在WebApplication上下文中加入<context:annotation-config/>配置,那么就只能解析在web容器中Bean的注解。

1.9.1 @Required

这个 @Required 注解一般是使用在Bean属性中的setXXX()方法上面,用来标注这个属性需要显示注入的或容器依赖注入方式给一个值。

代码使用方式如下:

public class TestBean {

    private String name;

    @Required
    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }
}

此注释指示必须在配置时通过bean定义中的显式属性值或通过自动装配来填充受影响的bean属性。

:从Spring Framework 5.1开始,@ Required批注已正式弃用,转而使用构造函数注入进行必需的设置(或InitializingBean.afterPropertiesSet()的自定义实现以及bean属性setter方法)

1.9.2 @Autowired

**注 ** : 一般有使用@Autowired注释的地方都可以使用JSR 330@Inject注释代替

@Autowired的几种使用方式如下:

  • 在构造函数上添加@Autowired注入依赖

可以在一个Bean中的构造函数中使用@Autowired来注入依赖,并且从Spring Framework 4.3开始,如果一个bean中只定义了一个构造函数,则不需要在构造函数上使用@Autowired`注解也能注入依赖,但是,如果有几个构造函数可用,则至少在一个构造函数上使用@Autowired注解,用来告诉容器使用哪个。

​ 一个构造函数注入情况@Autowired可加可不加:

public class TestBean {
    private TestBeanB testBeanB;
    
    //这里可加可不加都能完成注入
    @Autowired
    public TestBean(TestBeanB testBeanB) {
        this.testBeanB = testBeanB;
    }

    // get/set...
}

​ 多个构造函数注入情况必须要在一个构造函数上加@Autowired,不然容器会抛出一个异常找不到构造函数:

public class TestBean {
    private TestBeanB testBeanB;
    private TestBeanA testBeanA;

	//两个构造函数其中一个必须要使用@Autowired,如果不加会抛异常
    public TestBean(TestBeanB testBeanB,TestBeanA testBeanA) {
        this.testBeanB = testBeanB;
        this.testBeanA = testBeanA;
    }

    @Autowired
    public TestBean(TestBeanB testBeanB) {
        this.testBeanB = testBeanB;
    }

    // get/set...
}
  • setXXX方法上添加@Autowired注入依赖

    public class TestBean {
        private TestBeanB testBeanB;
    
        public TestBeanB getTestBeanB() {
            return testBeanB;
        }
    
        @Autowired
        public void setTestBeanB(TestBeanB testBeanB) {
            this.testBeanB = testBeanB;
        }
    }
    
  • 在普通方法上添加@Autowired注入依赖

    public class TestBean {
        private TestBeanB testBeanB;
    
        @Autowired
        public void fillAttribute(TestBeanB testBeanB){
            this.testBeanB = testBeanB;
        }
    
        public TestBeanB getTestBeanB() {
            return testBeanB;
        }
    
        public void setTestBeanB(TestBeanB testBeanB) {
            this.testBeanB = testBeanB;
        }
    }
    
  • 在属性上添加@Autowired注入依赖

    public class TestBean {
        @Autowired
        private TestBeanB testBeanB;
    
        // get/set...
    }
    
  • 在属性和构造函数上组合添加@Autowired注入依赖

    public class TestBean {
        @Autowired
        private TestBeanB testBeanB;
        private TestBeanA testBeanA;
    
        @Autowired
        public TestBean(TestBeanA testBeanA) {
            this.testBeanA = testBeanA;
        }
    
        // get/set...
    }
    
  • 还可以在数组,集合,Map等容器中添加@Autowired注入依赖,同样可以支持,属性,构造函数,方法这些方式,跟上述一致。

    public class TestBean {
        //这里可以是数组,集合,Map等容器,同样可以支持,属性,构造函数,方法这些方式注入
        //这里只展示了属性注入
        @Autowired
        private TestBeanB[] testBeanB;
    
        // get/set...
    }
    
  • 可以通过设置@Autowired的属性required为false,可以指示容器跳过这个注入点操作,不注入对应的值,required默认值为true

    public class TestBean {
        @Autowired(required = false)
        private TestBeanB testBeanB;
    
        // get/set...
    }
    
  • 使用Java8中的java.util.Optional或者是JSR-305中的@Nullable注解组合@Autowired也可以达到跳过莫个注入点操作,不注入对应的值。

    public class TestBean {
        @Autowired
        public void setTestBeanB(Optional<TestBeanB> testBeanB) {
            //...
        }
        
        @Autowired
        public void setTestBeanB(@Nullable TestBeanB testBeanB) {
            //...
        }
    }
    
  • 也可以将@Autowired注解放在(BeanFactory,ApplicationContext,Environment
    ,ResourceLoader,ApplicationEventPublisher,MessageSource)这些接口以及它们的实现类中(例如ConfigurableApplicationContext或ResourcePatternResolver) 将自动解析而无需进行特殊设置

    public class TestBean {
    
        @Autowired
        private ApplicationContext applicationContext;
    
        public String getName(){
            //这里可以直接使用 上面注入的ApplicationContext 容器
            return this.applicationContext.getBean("testBeanA", TestBeanA.class).getName();
        }
    }
    

@Autowired,@Inject,@Value@Resource批注由Spring BeanPostProcessor实现处理。
这意味着您不能在自己的BeanPostProcessorBeanFactoryPostProcessor类型(如果有)
中应用这些注释。必须使用XMLSpring @Bean方法显式使用这些类型。

1.9.3 @Primary@Autowired注入不纠结

由于在定义Bean的时候可以有多个不同名称对应同一个Class路径类的情况,所以通常系统会出现同中类型有多个Bean,那么@Autowired在按类型注入Bean的时候就不指定到底要注入那个一个对象了,而@Primary正好可以表示多个Bean中可以指明容器在注入的时候优先注入添加了@Primary注解的对象,也就是同类型的多个对象中添加了@Primary注解的被注入的优先级最高。

​ 使用java代码定义Bean

@Configuration
public class JavaConfig {

    @Primary
    @Bean
    public TestBeanB firstTestBean() {
        TestBeanB testBeanB = new TestBeanB();
        testBeanB.setName("张三");
        return testBeanB;
    }

    @Bean
    public TestBeanB secondTestBean() {
        TestBeanB testBeanB = new TestBeanB();
        testBeanB.setName("张三");
        return testBeanB;
    }

    @Bean
    public TestBean testBean() {
        return new TestBean();
    }
}

​ 使用XML形式定义Bean

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <context:annotation-config/>

        <bean id="testBean" 
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
        </bean>
        <bean id="testBeanB1" 
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB"
              primary="true">
            <property name="name" value="李四"/>
        </bean>
        <bean id="testBeanB2" 
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <property name="name" value="李四"/>
        </bean>
</beans>

Bean中使用@Autowired注入@primary选择好的对象

public class TestBean {

    @Autowired
    private TestBeanB testBeanB;

    // get/set ....
}
1.9.4 @Qualifiers@Autowired注入指定名称的Bean

在定义同类型的多个Bean的时候还可以使用Qualifier标注别名来区分对象,然后在目标类中使用@Autowired@Qualifier注解组合注入对应别名的对象。

​ 定义Bean可以使用java配置定义也可以使用XML定义,这里就不展示java配置定义了,直接展示XML怎么使用<qualifier>标签顶一个对象的别名。

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <context:annotation-config/>

        <bean id="testBean"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
        </bean>
        <bean id="testBeanB1"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier value="main"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>
        <bean id="testBeanB2"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier value="action"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>
</beans>

上述利用<qualifier>定义好Bean就可以在目标类中使用@Autowired@Qualifier注解来注入指定名称的对象了,同样的@Qualifier注解也可以使用在构造方法,属性,普通方法,方法参数等@Autowired注解可以使用的地方。

public class TestBean {

    @Autowired
    @Qualifier(value = "main")
    private TestBeanB testBeanB;

    // get/set ...
}

还可以使用自定义的注解来定义对象的别名,使用方法基本和Qualifier一致,只是自定义的注解需要指定Type类型。

​ 如何定义@Qualifier类型的自定义注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD,ElementType.FIELD,ElementType.CONSTRUCTOR,ElementType.PARAMETER})
@Qualifier
public @interface Genre {
    String value() default "";
}

使用自定义的Qualifier类型的注解定义对象,注意自定义的注解需要设置Type属性为自定义注解的完全限定名称。

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <context:annotation-config/>

        <bean id="testBean"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
        </bean>
        <bean id="testBeanB1"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="main"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>
        <bean id="testBeanB2"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="action"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>
</beans>

上述使用自定义注解给Bean名称的时候也可以使用Bean标签的元数据标签<meta/>来代替<qualifier>标签把value值用<meta/>表示前提是在没有<qualifier>标签,因为<qualifier>标签的优先级远高于<meta/>标签。

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <context:annotation-config/>

        <bean id="testBean"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
        </bean>
        <bean id="testBeanB1"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="main"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>
        <bean id="testBeanB2"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <!--<qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="action"></qualifier>-->
            <!-- 使用meta 替换 qualifier -->
            <meta key="value" value="action"/>
            <property name="name" value="qualifier"/>
        </bean>
</beans>

使用自定义的注解结合@Autowired注解注入对应的依赖

public class TestBean {
    private TestBeanB testBeanB;
    @Autowired
    @Genre(value = "main")
    private TestBeanB testBeanB2;

    public TestBeanB getTestBeanB() {
        return testBeanB;
    }
    @Autowired
    public void setTestBeanB(@Genre(value = "action") TestBeanB testBeanB) {
        this.testBeanB = testBeanB;
    }
}
1.9.5 可以使用泛型来限定对象

​ 有时候使用泛型<>也可以达到qualifier一致的作用来区分不同的对象通过容器判断注入到目标对象。

​ 像下面这种情况就只会注入父接口是Store,且泛型类型是Integer的对象存到集合中,其他泛型的不会注入。

public class TestBeanA {
    private List<Store<Integer>> storeList;
}
1.9.6 可以使用CustomAutowireConfigurer添加自定义的qualifier类型的注解

在介绍怎么自定义qualifier类型的注解的时候,我们的做法是直接在自定义的注解里面引入@Qualifier注解可以查看上述是怎么自定义的,其实可以不在自定义中引入@Qualifier注解转而在org.springframework.beans.factory.annotation.CustomAutowireConfigurer类中添加自定义的Qualifier类型的注解也可以达到注解@Qualifier一样的效果,因为这个类最终会把自定义的注解添加到和@qualifier注解同一个处理类中。

<?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:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
        <context:annotation-config/>

        <bean id="testBean"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBean">
        </bean>
        <bean id="testBeanB1"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="main"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>
        <bean id="testBeanB2"
              class="springioc.coretechnologic.ioc.annotationbasecontainerconfig.TestBeanB">
            <qualifier type="springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre" value="action"></qualifier>
            <property name="name" value="qualifier"/>
        </bean>

        <bean id="customQualifier"
               class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
            <property name="customQualifierTypes">
                <set>
                    <value>springioc.coretechnologic.ioc.annotationbasecontainerconfig.Genre</value>
                </set>
            </property>
        </bean>
</beans>
1.9.7 使用@Resource注入

Spring还通过在字段或bean``setXXX方法上使用JSR-250 @Resource批(javax.annotation.Resource)支持注入@Resource具有名称属性。默认情况下,Spring将注入Bean名称为name属性值的对象,如果没有指定名称,则默认名称是从字段名称或setter方法派生的。如果是字段,则采用字段名称。在使用setter方法的情况下,它采用参数bean属性名称。@Resource注解会先根据属性名称去查找Bean名称为属性名称的对象,如果没有找到,再根据属性类型去查找对应的Bean@Resource也可以跟@Autowired一样直接注入下面这些接口以及他们的实现类:BeanFactoryApplicationContextResourceLoaderApplicationEventPublisherMessageSource等。

public class TestBean {
    //1. 使用在属性上
    @Resource(name = "testBeanBResource")
    private TestBeanB testBeanB;
    
    //也可以跟@Autowired一样直接注入ApplicationContext
    @Resource
    private ApplicationContext applicationContext;

    public TestBeanB getTestBeanB() {
        return testBeanB;
    }
	//2. 使用在set方法上
    @Resource
    public void setTestBeanB(TestBeanB testBeanB) {
        this.testBeanB = testBeanB;
    }
}
1.9.8 使用 @Value注入

@Value通常用于注入外部属性,一般都是使用application.properties文件中定义的值,也可以使用SPEL表达式,也还有其他外部的值。

public class TestBean {
    //如果没有找到值,就会把ceshi.name这个字符串赋值给name
    //如果不想没有值的时候注入ceshi.name字符串,
    //就需要声明一个PropertySourcesPlaceholderConfigurer ,并且这个方法必须是static的
    @Value("${ceshi.name}")
    private String name;
    //引入SPEL表达式获取系统属性
    @Value("#{systemProperties['os.name']}")
    private String other;

   	//....
}

新建一个ioc.properties文件

ceshi.name=王五

如果没有找到值,就会把ceshi.name这个字符串赋值给name,如果不想没有值的时候注入ceshi.name字符串,
就需要声明一个PropertySourcesPlaceholderConfigurer ,并且这个方法必须是static的。

@Configuration
@PropertySource("classpath:ioc.properties")
public class JavaConfig {
	//...

    @Bean
    public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer(){
        return new PropertySourcesPlaceholderConfigurer();
    }
}
1.9.9 PostContructPreDestory的使用

CommonAnnotationBeanPostProcessor不仅可以识别@Resource注解,还可以识别JSR-250生命周期注解:javax.annotation.PostConstructjavax.annotation.PreDestroy。在Spring 2.5中引入的对这些注解的支持为初始化回调销毁回调中描述的生命周期回调机制提供了一种替代方法。PostConstruct的执行时机为构造器执行完成后立马执行,PreDestroy的执行时机为对象销毁前一步执行。假设CommonAnnotationBeanPostProcessorSpring ApplicationContext中注册,则在生命周期中与相应的Spring生命周期接口方法或显式声明的回调方法在同一点调用带有这些注解之一的方法。在以下示例中,演示了初始化,销毁用法:

public class TestBean {

    public TestBean() {
        System.out.println("构造器方法执行");
    }

    @PostConstruct
    public void postConstruct(){
        System.out.println("初始话回掉方法执行");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("销毁回掉方法执行");
    }
}

​ 与@Resource一样,@PostConstruct@PreDestroy批注类型是JDK 6到8的标准Java库的一部分。但是,整个javax.annotation包与JDK 9中的核心Java模块分开,并最终在JDK 11中删除了。如果需要,现在需要通过Maven Central获取javax.annotation-api工件,只需像其他任何库一样将其添加到应用程序的类路径中即可。

参考文献

【https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-postconstruct-and-predestroy-annotations】
【1.9. Annotation-based Container Configuration】

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值