zoukankan      html  css  js  c++  java
  • Spring--基于Java配置的容器配置

    Spring的非侵入性–基于Java的容器配置介绍

    一、Spring框架的非侵入性

    1.前言

    在上一篇博文中讲到,Spring允许以非侵入方式使用注解,无需接触目标组件的源代码。 要想真正理解这句话,我们就有必要先弄清楚非侵入式设计的概念了。

    2.侵入式与非侵入式

    • 侵入式:代码结构要与所使用的技术产生依赖。
    • 非侵入式:使用一个新的技术不会或者基本不改变原有代码结构,原有代码不作任何修改即可。

    3.侵入式框架与非侵入式框架

    1. 侵入式框架:引入了框架,对现有的类的结构有影响,需要实现框架某些接口或者基础某些特定的类。

      • 优点:侵入式可以使得用户的代码与框架更好的结合,充分利用框架提供的功能。
      • 缺点:侵入式让用户的代码对框架产生了依赖,不利于代码的复用,当去除框架的时候,程序就无法运行。
      • 例子:Struts1框架, Struts1代码严重依赖于Struts1 API,属于侵入性框架。
    2. 非侵入式框架:引入了框架,对现有的类结构没有影响,不需要实现框架某些接口或者特定的类。

      • 优点:代码对框架没有过多的依赖,允许所开发出来的应用系统能够在不用的环境中自由移植,不需要修改应用系统中的核心功能实现的代码
      • 缺点:无法复用框架提供的代码和功能
      • 例子:Spring框架,通过配置完成依赖注入就可以使用,当我们想换个框架,只需要修改相应的配置,程序仍然可以运行。

    二、在Java代码中使用注解来配置容器

    1.基础概念:@Bean@Configuration

    @Configuration修饰的类和由@Bean修饰的方法组成了Spring Java配置的主要构件。

    @Bean注解用于方法实例化,配置和初始化要由Spring IoC容器管理的对象。 使用的时候,@Bean注解的方法,所在的类需要用@Configuration来修饰,表示该类是作为Bean定义的来源,容器初始化时,需要扫描该类,进行自动装配。

    @Configuration注解的类,表示该类的主要目的是作为Bean定义的来源。 此外,@Configuration类允许通过调用同一类中的其他@Bean方法来定义Bean间的依赖关系。 最简单的@Configuration类的内容如下:

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

    2. @Component@Bean的区别

    @Component和@Bean都可以定义bean,但实际上@Component@Bean是做两个完全不同的事情,不应该混为一谈,具体比较如下:

    • @Component(和@Service@Repository)用于自动检测和使用类路径扫描自动配置bean。 注释类和bean之间存在隐式的一对一映射(即每个类一个bean)。这种方法对需要进行逻辑处理的控制非常有限,因为它纯粹是声明性的。
    • @Bean用于显式声明单个bean,而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离,并允许您精确地创建和配置bean。

    那我们在什么时候会使用@Bean?假设下,某天你想创建一个第三方的组件,但是你没有源代码,也就没办法使用@Component进行自动配置,这种时候使用@Bean就比较合适了。 又比如,具体采用哪个实现是依赖于某些参数的,在这种情况下@Bean无疑是更合适的选择。 示例如下:

    @Bean
    @Scope("prototype")
    public SomeService someService(String state) {
        switch (state) {
        case 1:
            return new Impl1();
        case 2:
            return new Impl2();
        case 3:
            return new Impl3();
        default:
            return new Impl();
        }
    }
    

    3.使用AnnotationConfigApplicationContext实例化Spring容器

    以下各节介绍了Spring 3.0中引入的AnnotationConfigApplicationContext。 这种通用的ApplicationContext实现,不仅能够接受@Configuration类作为输入,而且还可以接受普通的@Component类和带有JSR-330元数据注释的类。

    • 当提供@Configuration类作为输入时,@Configuration类本身将注册为Bean定义,并且该类中所有已声明的@Bean方法也将注册为Bean定义。
    • 提供@Component和JSR-330类时,它们将注册为bean定义,并且假定在必要时在这些类中使用了诸如@Autowired@Inject之类的DI元数据。

    (1)作为AnnotationConfigApplicationContext构造函数的参数

    @Configuration类用作输入,实例化AnnotationConfigApplicationContext,使得Spring容器的使用,完全不依赖XML。 如下面的示例所示:

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(AppConfig.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }
    

    前面还提到,同样可以将任何@Component或JSR-330带注释的类作为输入,提供给AnnotationConfigApplicationContext的构造函数进行实例化,如以下示例所示:

    public static void main(String[] args) {
        ApplicationContext ctx = new AnnotationConfigApplicationContext(MyServiceImpl.class, Dependency1.class, Dependency2.class);
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }
    

    上面的示例假定MyServiceImpl,Dependency1和Dependency2使用了Spring依赖项注入注解,例如@Autowired

    (2)使用register(Class<?>…)的编程方式构建容器

    可以使用无参构造函数来实例化AnnotationConfigApplicationContext,然后使用register()方法对其进行配置。 以编程方式构建AnnotationConfigApplicationContext时,此方法特别有用。 以下示例展示了如何执行此操作:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.register(AppConfig.class, OtherConfig.class);
        ctx.register(AdditionalConfig.class);
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
        myService.doStuff();
    }
    

    (3)使用scan(String ...)启动组件扫描(常见)

    要启用组件扫描,可以按如下方式使用@Configuration注解类:

    @Configuration
    @ComponentScan(basePackages = "com.acme") 
    public class AppConfig  {
        ...
    }
    

    在上面的示例中,com.acme包进行了扫描,以查找任何@Component注解的类,并将这些类注册为容器中的bean。AnnotationConfigApplicationContext公开了scan(String ...)方法,实现相同的组件扫描功能,如以下示例所示:

    public static void main(String[] args) {
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("com.acme");
        ctx.refresh();
        MyService myService = ctx.getBean(MyService.class);
    }
    

    另外需要注意,@Configuration类使用了@Component进行元注解( meta-annotated with @Component ),因此它们也是组件扫描的候选对象。 在前面的示例中,假定AppConfig在com.acme包(或下面的任何包)中声明,则在调用scan()时也会将其扫描。 根据refresh(),AppConfig的所有@Bean方法都将在容器中进行处理并注册为Bean。

    4.使用@Bean注解

    @Bean是方法级别(method-level )的的注解,是对XML配置的<bean/> 节点的直接模拟。@Bean注解同样支持<bean/> 提供的一些属性,如:init-methoddestroy-methodautowiringname可以在@Configuration注解的类或@Component注解的类中使用@Bean注释。

    (1)声明一个bean

    要声明bean,可以使用@Bean注解,对类方法进行注释,这样子Spring就会在以方法返回值为指定类型的ApplicationContext中定义一个bean。**默认情况下,bean名称与方法名称相同。**如以下示例:

    @Configuration
    public class AppConfig {
    
        @Bean
        public TransferServiceImpl transferService() {
            return new TransferServiceImpl();
        }
    }
    

    上面示例的配置等价于下面的XML配置

    <beans>
        <bean id="transferService" class="com.acme.TransferServiceImpl"/>
    </beans>
    

    以上两种方式的声明都使一个名为transferService的bean在ApplicationContext中可用,并绑定到类型为TransferServiceImpl的对象实例,即transferService -> com.acme.TransferServiceImpl

    还可以使用接口(或基类)作为返回类型来声明@Bean方法,如以下示例所示:

    @Configuration
    public class AppConfig {
    
        @Bean
        public TransferService transferService() {
            return new TransferServiceImpl();
        }
    }
    

    mark

    如果您通过声明的服务接口来一致地引用类型,则@Bean返回类型可以安全地加入该设计决策。 但是,对于实现多个接口的组件或存在其实现类型潜在引用的组件,声明尽可能具体的返回类型(至少与引用您的bean的注入点所要求的具体类型一样)更为安全。

    (2)bean依赖

    @Bean注解的方法可以具有任意数量的参数,这些参数描述构建该bean所需的依赖关系。 例如,如果我们的TransferService需要一个AccountRepository,则可以使用方法参数来实现该依赖关系,如以下示例所示:

    @Configuration
    public class AppConfig {
    
        @Bean
        public TransferService transferService(AccountRepository accountRepository) {
            return new TransferServiceImpl(accountRepository);
        }
    }
    

    (3)接收生命周期回调

    任何使用@Bean 注解定义的类都支持常规的生命周期回调,并且可以使用JSR-250中的@PostConstruct@PreDestroy注解。

    常规Spring生命周期回调也得到完全支持。 如果bean实现了InitializingBeanDisposableBeanLifecycle 等接口,则容器将调用它们各自的方法。

    还完全支持标准的* Aware接口集(例如BeanFactoryAwareBeanNameAwareMessageSourceAwareApplicationContextAware等)。

    @Bean注解还支持指定任意的初始化和销毁的回调方法,类似于XML配置中bean元素的init-methoddestroy-method属性,如以下示例所示:

    public class BeanOne {
    
        public void init() {
            // initialization logic
        }
    }
    
    public class BeanTwo {
    
        public void cleanup() {
            // destruction logic
        }
    }
    
    @Configuration
    public class AppConfig {
    
        @Bean(initMethod = "init")
        public BeanOne beanOne() {
            return new BeanOne();
        }
    
        @Bean(destroyMethod = "cleanup")
        public BeanTwo beanTwo() {
            return new BeanTwo();
        }
    }
    

    在bean的构造期间直接调用初始化方法也是可行的,不一定需要依赖容器的生命周期回调。 如以下示例所示:

    @Configuration
    public class AppConfig {
    
        @Bean
        public BeanOne beanOne() {
            BeanOne beanOne = new BeanOne();
            beanOne.init();
            return beanOne;
        }
    
        // ...
    }
    

    (4)指定Bean的作用域(Scope)

    在用@Bean定义bean时,可以指定bean的作用域,标准的作用域有:singleton、prototype、request、session、application、websocket,其中request、session、application、websocket,这四个作用域只有在web应用中才能够被使用到。

    Scope Description
    singleton (默认) 将每个Spring IoC容器的单个bean定义的作用域限定为单个对象实例(单例模式)。
    prototype 将单个bean定义的作用域限定为任意数量的对象实例。
    request 将单个bean定义的作用域限定为单个HTTP请求(request)的生命周期内。 也就是说,每个HTTP请求都会有一个单独的bean。仅在web应用中能使用到。
    session 将单个bean定义的作用域限定为HTTP会话(Session)的生命周期内。仅在web应用中能使用到。
    application 将单个bean定义的作用域限定为ServletContext的生命周期内。仅在web应用中能使用到。
    websocket 将单个bean定义的作用域限定为WebSocket的生命周期内。. 仅在web应用中能使用到。
    • 使用@Sopce注解

      bean默认的作用域是singleton,但是可以使用@Scope 注解进行自定义,以下示例,将bean的作用域指定为prototype:

      @Configuration
      public class MyConfiguration {
      
          @Bean
          @Scope("prototype")
          public Encryptor encryptor() {
              // ...
          }
      }
      

    (5)自定义bean名称

    默认情况下,配置类使用@Bean方法的名称作为bean的名称。 但是可以使用name属性进行自定义,以下示例将bean名称指定为myThing,默认为thing:

    @Configuration
    public class AppConfig {
    
        @Bean(name = "myThing")
        public Thing thing() {
            return new Thing();
        }
    }
    

    (6)bean别名

    有时希望为单个Bean设置多个名称,为此,@Bean注解的name属性接受一个String数组。 以下示例显示了如何为bean设置多个别名:

    @Configuration
    public class AppConfig {
    
        @Bean({"dataSource", "subsystemA-dataSource", "subsystemB-dataSource"})
        public DataSource dataSource() {
            // instantiate, configure and return DataSource bean...
        }
    }
    

    5.使用@Configuration注解

    @Configuration是一个类级别(class-level )的注解,表示其对象是Bean定义的源。

    (1)注入bean之间的依赖关系

    当bean彼此依赖时,表示依赖关系就像让一个bean方法调用另一个一样简单,如以下示例所示:

    @Configuration
    public class AppConfig {
    
        @Bean
        public BeanOne beanOne() {
            return new BeanOne(beanTwo());
        }
    
        @Bean
        public BeanTwo beanTwo() {
            return new BeanTwo();
        }
    }
    

    在上面的示例中,beanOne通过构造函数注入接收到beanTwo的引用。

    再次强调一遍:仅当在@Configuration类中声明@Bean方法时,此声明bean间依赖性的方法才有效。 您能使用普通的@Component类声明Bean间的依赖关系。

    (2)查找 方法注入

    不常用到,占个位置留着以后用到再补充。

    (3)有关基于Java的配置内部中是如何工作的更多信息

    不常用到,占个位置留着以后用到再补充。

    6.组合基于java的配置

    不常用到,占个位置留着以后用到再补充。

    参考资料

    [1]:Spring学习(1):侵入式与非侵入式,轻量级与重量级

    [2]: Spring:@Component 对比 @Bean

    [3]:SpringFrameworkReference

  • 相关阅读:
    (转载)VS2010/MFC编程入门之十九(对话框:颜色对话框)
    (转载)VS2010/MFC编程入门之十八(对话框:字体对话框)
    (转载)VS2010/MFC编程入门之十七(对话框:文件对话框)
    (转载)VS2010/MFC编程入门之十六(对话框:消息对话框)
    VS2010/MFC编程入门之十四(对话框:向导对话框的创建及显示)
    (转载)VS2010/MFC编程入门之十五(对话框:一般属性页对话框的创建及显示)
    (转载)VS2010/MFC编程入门之十三(对话框:属性页对话框及相关类的介绍)
    Android笔记之AsyncTask
    Android笔记之线程使用
    Android笔记之活动指示器使用
  • 原文地址:https://www.cnblogs.com/blog567/p/12374652.html
Copyright © 2011-2022 走看看