热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

一文了解那些和SpringBean有关的那些注解!

△Hollis,一个对Coding有着独特追求的人△这是Hollis的第220篇原创分享作者lcxuan来源lHollis(ID:hollischuan

△Hollis, 一个对Coding有着独特追求的人△

640

这是Hollis的第 220 篇原创分享

作者 l cxuan

来源 l Hollis(ID:hollischuang)


随着Spring的流行,我们经历过基于XML-Based 的配置,随着SpringBoot的流行,我们逐渐使用基于注解的配置替换掉了基于XML-Based的配置,那么你知道基于注解的配置的基础组件都是什么吗?都包括哪些要素?那么本节就来探讨一下。注:本篇文章更多的是讨论Spring基于注解的配置一览,具体的技术可能没有那么深,请各位大佬见谅。

探讨主题:

  • 基础概念:@Bean 和 @Configuration

  • 使用AnnotationConfigApplicationContext 实例化Spring容器

  • 使用@Bean 注解

  • 使用@Configuration 注解

  • 编写基于Java的配置

  • Bean定义配置文件

  • PropertySource 抽象类

  • 使用@PropertySource

  • 占位符的声明

基础概念:@Bean 和 @Configuration

Spring中新的概念是支持@Bean注解 和 @Configuration 注解的类。@Bean 注解用来表明一个方法实例化,配置并且通过IOC容器初始化并管理一个新的对象。@Bean注解就等同于XML-Based中的标签,并且扮演了相同的作用。你可以使用基于注解的配置@Bean 和 @Component,然而他们都用在@Configuration配置类中。

使用@Configuration 注解的主要作用是作为bean定义的类,进一步来说,@Configuration注解的类允许通过调用同类中的其他@Bean标注的方法来定义bean之间依赖关系。如下所示:

新建一个maven项目(我一般都直接创建SpringBoot项目,比较省事),创建AppConfig,MyService,MyServiceImpl类,代码如下:

@Configuration
public class AppConfig {

    @Bean
    public MyService myService(){
        return new MyServiceImpl();
    }
}

public interface MyService {}
public class MyServiceImpl implements MyService {}

上述的依赖关系等同于XML-Based:

<beans>
    <bean id&#61;"myService",class&#61;"com.spring.annotation.service.impl.MyServiceImpl"/>
beans>

使用AnnotationConfigApplicationContext 实例化Spring容器

AnnotationConfigApplicationContext 基于注解的上下文是Spring3.0 新添加的注解&#xff0c;它是ApplicationContext的一个具体实现&#xff0c;它可以接收&#64;Configuration注解的类作为输入参数&#xff0c;还能接收使用JSR-330元注解的普通&#64;Component类。

当提供了&#64;Configuration 类作为输入参数时&#xff0c;&#64;Configuration类就会注册作为bean的定义信息并且所有声明&#64;Bean的方法也都会作为bean的定义信息。

当提供&#64;Component和JSR-330 声明的类时&#xff0c;他们都会注册作为bean的定义信息&#xff0c;并且假设在必要时在这些类中使用诸如&#64;Autowired或&#64;Inject之类的注解

简单的构造

在某些基于XML-Based的配置&#xff0c;我们想获取上下文容器使用ClassPathXmlApplicationContext&#xff0c;现在你能够使用&#64;Configuration 类来实例化AnnotationConfigApplicationContext。

在MyService中添加一个printMessage()方法&#xff0c;实现类实现对应的方法。新建测试类进行测试

public class ApplicationTests {

    public static void main(String[] args) {
        ApplicationContext context &#61; new AnnotationConfigApplicationContext(AppConfig.class);
        MyService service &#61; context.getBean(MyService.class);
          // printMessage() 输出something...
        service.printMessage();
    }
}

如前所述&#xff0c;AnnotationConfigApplicationContext不仅限于使用&#64;Configuration类。任何&#64;Component或JSR-330带注释的类都可以作为输入提供给构造函数&#xff0c;如下例所示

public class ApplicationTests {

    public static void main(String[] args) {
        ApplicationContext context &#61; new AnnotationConfigApplicationContext(MyServiceImpl.class,Dependency1.class,Dependency2.class);
        MyService myService &#61; context.getBean(MyService.class);
        myService.printMessage();
    }
}


使用register注册IOC容器

你可以实例化AnnotationConfigApplicationContext通过使用无参数的构造器并且使用register方法进行注册&#xff0c;它和AnnotationConfigApplicationContext带参数的构造器起到的效果相同。

public class ApplicationTests {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx &#61; new AnnotationConfigApplicationContext();
        ctx.register(AppConfig.class, OtherConfig.class);
        ctx.register(AdditionalConfig.class);
        ctx.refresh();
        MyService myService &#61; ctx.getBean(MyService.class);
        System.out.println(ctx.getBean(OtherConfig.class));
        System.out.println(ctx.getBean(AdditionalConfig.class));
        myService.printMessage();
    }
}

OtherConfig.class 和 AdditionalConfig.class 是使用&#64;Component 标注的类。


允许scan()方法进行组件扫描

为了允许组件进行扫描&#xff0c;需要在&#64;Configuration配置类添加&#64;ComponentScan()注解&#xff0c;改造之前的AdditionalConfig类&#xff0c;如下&#xff1a;

&#64;Configuration
&#64;ComponentScan(basePackages &#61; "com.spring.annotation.config")
public class AdditionalConfig {}

&#64;ComponentScan指定了基础扫描包位于com.spring.annotation.config下&#xff0c;所有位于该包范围内的bean都会被注册进来&#xff0c;交由Spring管理。它就等同于基于XML-Based的注解&#xff1a;


    package&#61;"com.spring.annotation.config/>

AnnotationConfigApplicationContext中的scan&#xff08;&#xff09;方法以允许相同的组件扫描功能&#xff0c;如以下示例所示&#xff1a;

public static void main(String[] args) {
  AnnotationConfigApplicationContext ctx &#61; new AnnotationConfigApplicationContext();
  ctx.scan("com.spring.annotation");
  ctx.refresh();
  MyService myService &#61; ctx.getBean(MyService.class);
}

为什么说&#64;Configuration用法和&#64;Component都能够标注配置类&#xff1f;因为&#64;Configuration的元注解就是&#64;Component。

&#64;Target({ElementType.TYPE})
&#64;Retention(RetentionPolicy.RUNTIME)
&#64;Documented
&#64;Component
public &#64;interface Configuration {
String value() default "";
}



使用AnnotationConfigWebApplicationContext支持web容器

AnnotationConfigApplicationContext的一个WebApplicationContext的变化是使用AnnotationConfigWebApplicationContext。配置Spring ContextLoaderListener的servlet监听器&#xff0c;Spring MVC的DispatcherServlet等时&#xff0c;可以使用此实现。以下web.xml代码段配置典型的Spring MVC Web应用程序&#xff08;请注意context-param和init-param的使用&#xff09;

<web-app>
      
    <context-param>
        <param-name>contextClassparam-name>

        <param-value>
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        param-value>
    context-param>

      
    <context-param>
        <param-name>contextConfigLocationparam-name>

        <param-value>com.spring.annotation.config.AdditionalConfigparam-value>
    context-param>

      
    <listener>
        <listener-class>org.springframework.web.context.ContextLoaderListenerlistener-class>
    listener>

      
    <servlet>
        <servlet-name>dispatcherservlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServletservlet-class>
        
        <init-param>
            <param-name>contextClassparam-name>

            <param-value>
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            param-value>
        init-param>
        
        <init-param>
            <param-name>contextConfigLocationparam-name>

            <param-value>com.spring.annotation.config.MvcConfigparam-value>
        init-param>
    servlet>

    
    <servlet-mapping>
        <servlet-name>dispatcherservlet-name>
        <url-pattern>/app/*url-pattern>
    servlet-mapping>
web-app>

使用&#64;Bean注解

&#64;Bean 注解是一个方法级别的注解&#xff0c;能够替换XML-Based中的标签&#xff0c;&#64;Bean注解同样支持标签支持的属性&#xff0c;像是 init-method, destroy-method, autowiring。

定义一个Bean

与基础概念中Bean的定义相同&#xff0c;读者可以参考基础概念部分进行了解&#xff0c;我们不在此再进行探讨。


Bean的依赖

&#64;Bean 注解可以有任意数量的参数来构建其依赖项&#xff0c;例如

public class MyService {
    private final MyRepository myRepository;
    public MyService(MyRepository myRepository) {
        this.myRepository &#61; myRepository;
    }
    public String generateSomeString() {
        return myRepository.findString() &#43; "-from-MyService";
    }
}

&#64;Configuration
class MyConfiguration {
    &#64;Bean
    public MyService myService() {
        return new MyService(myRepository());
    }
    &#64;Bean
    public MyRepository myRepository() {
        return new MyRepository();
    }
}

public class MyRepository {
    public String findString() {
        return "some-string";
    }
}


接受生命周期回调

任何使用&#64;Bean的注解都支持生命周期的回调&#xff0c;使用JSR-220提供的&#64;PostConstruct和&#64;PreDestory注解来实现。如果bean实现了InitializingBean,DisposableBean或者Lifecycle接口&#xff0c;他们的方法会由IOC容器回调。一些以Aware的实现接口(像是BeanFactoryAware,BeanNameAware, MessageSourceAware, ApplicationContextAware等)也支持回调。

&#64;Bean注解支持特定的初始化和销毁方法&#xff0c;就像XML-Based中的init-method和 destory-method中的bean属性&#xff0c;下面这个例子证实了这一点

&#64;Configuration
public class AppConfig {

    &#64;Bean(initMethod &#61; "init")
    public BeanOne beanOne(){
        return new BeanOne();
    }

    &#64;Bean(destroyMethod &#61; "cleanup")
    public BeanTwo beanTwo(){
        return new BeanTwo();
    }
}

class BeanOne {
    public void init(){}
}

class BeanTwo {
    public void cleanup(){}
}

对于上面的例子&#xff0c;也可以手动调用init()方法&#xff0c;与上面的initMethod 方法等效

&#64;Bean
public BeanOne beanOne(){
  BeanOne beanOne &#61; new BeanOne();
  beanOne.init();
  return beanOne;
}

当你直接使用Java开发时&#xff0c;你可以使用对象执行任何操作&#xff0c;并且不必总是依赖于容器生命周期。


Bean的作用范围

Spring包括&#64;Scope注解能够让你指定Bean的作用范围&#xff0c;Bean的Scope默认是单例的&#xff0c;也就是说&#64;Bean标注的对象在IOC的容器中只有一个。你可以重写&#64;Scope的作用范围&#xff0c;下面的例子说明了这一点&#xff0c;修改OtherConfig如下

&#64;Configuration
public class OtherConfig {

    &#64;Bean
    &#64;Scope("prototype")
    public Dependency1 dependency1(){
        return new Dependency1();
    }
}

每次尝试获取dependency1这个对象的时候都会重新生成一个新的对象实例。下面是Scope的作用范围和解释&#xff1a;

DescriptionnScope

singleton

默认单例的bean定义信息&#xff0c;对于每个IOC容器来说都是单例对象

prototype

bean对象的定义为任意数量的对象实例

request

bean对象的定义为一次HTTP请求的生命周期&#xff0c;也就是说&#xff0c;每个HTTP请求都有自己的bean实例&#xff0c;它是在单个bean定义的后面创建的。仅仅在web-aware的上下文中有效

session

bean对象的定义为一次HTTP会话的生命周期。仅仅在web-aware的上下文中有效

application

bean对象的定义范围在ServletContext生命周期内。仅仅在web-aware的上下文中有效

websocket

bean对象的定义为WebSocket的生命周期内。仅仅在web-aware的上下文中有效



&#64;Scope和Scoped-proxy

Spring提供了一种通过scoped proxies与scoped依赖一起作用的方式。最简单的在XML环境中创建代理的方式是通过标签。使用&#64;Scope注解为在Java中配置bean提供了与proxyMode属性相同的功能。默认是不需要代理的(ScopedProxyMode.NO),但是你需要指定ScopedProxyMode.TARGET_CLASS或者ScopedProxyMode.INTERFACES。


自定义Bean名称

默认的情况下&#xff0c;配置类通过&#64;Bean配置的默认名称(方法名第一个字母小写)进行注册和使用&#xff0c;但是你可以更换&#64;Bean的name为你想指定的名称。修改AdditionalConfig 类

&#64;Configuration
//&#64;ComponentScan(basePackages &#61; "com.spring.annotation.config")
public class AdditionalConfig {

    &#64;Bean(name &#61; "default")
    public Dependency2 dependency2(){
        return new Dependency2();
    }
}



Bean的别名

有时候需要为单例的bean提供多个名称&#xff0c;也叫做Bean的别名。Bean注解的name属性接收一个Array数组。下面这个例子证实了这一点&#xff1a;

&#64;Configuration
public class OtherConfig {

//    &#64;Bean
//    &#64;Scope("prototype")
//    public Dependency1 dependency1(){
//        return new Dependency1();
//    }

        &#64;Bean({"dataSource""dataSourceA""dataSourceB"})
    public DataSource dataSource(){
            return null;
    }
}



Bean的描述

有时&#xff0c;提供更详细的bean描述信息会很有帮助(但是开发很少使用到)。为了增加一个对&#64;Bean的描述&#xff0c;你需要使用到&#64;Description注解

&#64;Configuration
public class OtherConfig {

//    &#64;Bean
//    &#64;Scope("prototype")
//    public Dependency1 dependency1(){
//        return new Dependency1();
//    }

//    &#64;Bean({"dataSource", "dataSourceA", "dataSourceB"})
//    public DataSource dataSource(){
//        return null;
//    }

    &#64;Bean
    &#64;Description("此方法的bean名称为dependency1")
    public Dependency1 dependency1(){
        return new Dependency1();
    }
}

使用&#64;Configuration注解

更多关于&#64;Configuration 的详细说明&#xff0c;请你参考

已经把&#64;Configuration的注解说明的比较详细了。

组成Java-Based环境配置的条件

Spring基于注解的配置能够允许你自定义注解&#xff0c;同时能够降低配置的复杂性。

使用&#64;Import注解

就像在Spring XML文件中使用元素来帮助模块化配置一样&#xff0c;&#64;Import 注解允许从另一个配置类加载&#64;Bean定义&#xff0c;如下所示

&#64;Configuration
public class ConfigA {

    &#64;Bean
    public A a(){
        return new A();
    }
}

&#64;Configuration
&#64;Import(ConfigA.class)
public class ConfigB {

    &#64;Bean
    public B b(){
        return new B();
    }
}

现在&#xff0c;在实例化上下文时&#xff0c;不需要同时指定ConfigA.class 和 ConfigB.class &#xff0c;只需要显示提供ConfigB

public static void main(String[] args) {
    ApplicationContext ctx &#61; new AnnotationConfigApplicationContext(ConfigB.class);

    A a &#61; ctx.getBean(A.class);
    B b &#61; ctx.getBean(B.class);
}

这种方法简化了容器实例化&#xff0c;因为只需要处理一个类&#xff0c;而不是要求你在构造期间记住可能大量的&#64;Configuration类


有选择性的包含&#64;Configuration 类和&#64;Bean 方法

选择性的允许或者禁止&#64;Configuration注解的类和&#64;Bean注解的方法是很有用的&#xff0c;基于一些任意系统状态。一个常见的例子是只有在Spring环境中启用了特定的配置文件时才使用&#64;Profile注释激活bean。

&#64;Profile注解也实现了更灵活的注解&#64;Conditional&#xff0c;&#64;Conditional 注解表明在注册&#64;Bean 之前应参考特定的Condition实现。

实现Condition接口就会提供一个matched方法返回true或者false

更多关于&#64;Conditional 的示例&#xff0c;请参考

https://www.cnblogs.com/cxuanBlog/p/10960575.html


结合Java与XML配置

Spring &#64;Configuration类能够100%替换XML配置&#xff0c;但一些工具(如XML命名空间)仍旧是配置容器的首选方法&#xff0c;在这种背景下&#xff0c;使用XML使很方便的而且使刚需了。你有两个选择&#xff1a;使用以XML配置实例化容器为中心&#xff0c;例如&#xff1a;ClassPathXmlApplicationContext导入XML或者实例化以Java配置为中心的AnnotationConfigApplicationContext并提供ImportResource注解导入需要的XML配置。


将&#64;Configuration声明为普通的bean元素

请记住&#xff0c;&#64;Configuration类存放的是容器中的bean定义信息&#xff0c;下面的例子中&#xff0c;我们将会创建一个&#64;Configuration类并且加载了外部xml配置。下面展示了一个普通的Java配置类

&#64;Configuration
public class AppConfig {

    &#64;Autowired
    private DataSource dataSource;

    &#64;Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    &#64;Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }
}

下面是system-test-config.xml配置类的一部分

<beans>

      
    <context:annotation-config/>

         
      
    <context:property-placeholder location&#61;"classpath:/com/spring/annotation/jdbc.properties"/>

    <bean class&#61;"com.spring.annotation.config.AppConfig"/>

    <bean class&#61;"org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name&#61;"driverClassName" value&#61;"${jdbc.driverClassName}" />
        <property name&#61;"url" value&#61;"${jdbc.url}"/>
        <property name&#61;"username" value&#61;"${jdbc.username}"/>
        <property name&#61;"password" value&#61;"${jdbc.password}"/>
    bean>
beans>

引入jdbc.properties建立数据库连接

jdbc.driverClassName&#61;com.mysql.jdbc.Driver
jdbc.url&#61;jdbc:mysql://localhost:3306/sys
jdbc.username&#61;root
jdbc.password&#61;123456


public static void main(String[] args) {
    ApplicationContext ctx &#61; new ClassPathXmlApplicationContext("classpath:/com/spring/annotation/system-test-config.xml");
    TransferService transferService &#61; ctx.getBean(TransferService.class);
    // ...
}

在system-test-config.xml中&#xff0c;AppConfig 对应的标签没有声明id属性&#xff0c;虽然这样做是可以接受的&#xff0c;但是没有必要&#xff0c;因为没有其他bean引用它&#xff0c;并且不太可能通过名称从容器中获取它。同样的&#xff0c;DataSource bean只是按类型自动装配&#xff0c;因此不严格要求显式的bean id。


使用<> 挑选指定的&#64;Configuration类

因为&#64;Configuration的原注解是&#64;Component&#xff0c;所以&#64;Configuration注解的类也能用于组件扫描&#xff0c;使用与前一个示例中描述的相同的方案&#xff0c;我们可以重新定义system-test-config.xml以利用组件扫描。请注意&#xff0c;在这种情况下&#xff0c;我们不需要显式声明&#xff0c;因为启用相同的功能。

<beans>

    <context:component-scan base-package&#61;"com.spring.annotation"/>
    <context:property-placeholder location&#61;"classpath:/com/spring/annotation/jdbc.properties"/>

    <bean class&#61;"org.springframework.jdbc.datasource.DriverManagerDataSource">
          <property name&#61;"driverClassName" value&#61;"${jdbc.driverClassName}" />
        <property name&#61;"url" value&#61;"${jdbc.url}"/>
        <property name&#61;"username" value&#61;"${jdbc.username}"/>
        <property name&#61;"password" value&#61;"${jdbc.password}"/>
    bean>
beans>



&#64;Configuration 类使用&#64;ImportResource

在基于Java注解的配置类中&#xff0c;仍然可以使用少量的&#64;ImportResource导入外部配置&#xff0c;最好的方式就是两者结合&#xff0c;下面展示了一下Java注解结合XML配置的示例

&#64;Configuration
&#64;ImportResource("classpath:/com/spring/annotation/properties-config.xml")
public class AppConfig {

    &#64;Value("${jdbc.driverClassName}")
      private String driver;

    &#64;Value("${jdbc.url}")
    private String url;

    &#64;Value("${jdbc.username}")
    private String username;

    &#64;Value("${jdbc.password}")
    private String password;

    &#64;Bean
    public DataSource dataSource() {
        return new DriverManagerDataSource(url, username, password);
    }
}

Properties-config.xml

<beans>
    <context:property-placeholder location&#61;"classpath:/com/spring/annotation/jdbc.properties"/>
beans>

jdbc.properties

jdbc.driverClassName&#61;com.mysql.jdbc.Driver
jdbc.url&#61;jdbc:mysql://localhost:3306/sys
jdbc.username&#61;root
jdbc.password&#61;123456


public static void main(String[] args) {
    ApplicationContext ctx &#61; new AnnotationConfigApplicationContext(AppConfig.class);
    TransferService transferService &#61; ctx.getBean(TransferService.class);
    // ...
}

PS&#xff1a;本文来自作者投稿&#xff0c;原作者cxuan&#xff0c;以下是他的赞赏码&#xff0c;如果你喜欢本文&#xff0c;欢迎给作者赞赏。感谢&#xff01;同时&#xff0c;欢迎广大作者向Hollis投稿哦~

640?wx_fmt&#61;png


640?wx_fmt&#61;gif

在 GitHub 更新中&#xff0c;欢迎关注&#xff0c;欢迎star。

640?

直面Java第256期&#xff1a;jdk1.6对synchronized做了哪些优化&#xff1f;

成神之路第015期&#xff1a;设计模式&#xff1a;单例模式

深入并发第008期&#xff1a;到底什么是计算机内存模型&#xff1f;


- MORE | 更多精彩文章 -



如果你喜欢本文,

请长按二维码&#xff0c;关注 Hollis.

640?

转发至朋友圈&#xff0c;是对我最大的支持。


好文章&#xff0c;我在看❤️



推荐阅读
author-avatar
zhaiweibubu
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有