程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> JAVA編程 >> JAVA綜合教程 >> Java 的配置元數據

Java 的配置元數據

編輯:JAVA綜合教程

Java 的配置元數據


基於 Java 的配置元數據

Spring 新功能 Java-cofiguration 支持@Configuration 類注解和@Bean 方法注解@Bean 注解用於表明一個方法將會實例化、配置、初始化一個新對象,該對象由Spring IoC 容器管理。大家都熟悉 Spring 的< beans/>XML 配置, @Bean 注解方
法和它一樣。可以在任何 Spring @Component 中使用@Bean 注解方法,當然了,大多數情況下,@Bean 是配合@Configuration 使用的。@Configuration 注解的類表明該類的主要目的是作為 bean 定義的源。此外,@Configuration 類允許依賴本類中使用@Bean 定義的 bean。

@Configuration
public class AppConfig {

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

}

The AppConfig class above would be equivalent to the following Spring < beans/> XML:
我覺得直接使用XML挺好的,配合注解使用


    

使用 AnnotationConfigApplicationContext 實例化 Spring IoC 容器

Spring 的 AnnotationConfigApplicationContext 部分,是 Spring3.0 中新增的。這是一個強大的(譯注原文中是多才多藝的 versatile)ApplicationContext 實現,不僅能解析@Configuration 注解類,也能解析@Componnet 注解的類和使用 JSR-330
注解的類。使用@Configuration 注解的類作為配置元數據的時候, @Configuration 類本身也
會注冊為一個 bean 定義,類內所有的@Bean 注解的方法也會注冊為 bean 定義。使用@Component 和 JSR-330 注解類作為配置元數據時,他們本身被注冊為bean 定義,並假設 DI(依賴注入)元數據,像類內使用的@Autowired 或者@Inject都是

簡單結構

Spring 以 XML 作為配置元數據實例化一個 ClassPathXmlApplicationContext,以@Configuration 類作為配置元數據時, Spring 以差不多的方式,實例化一個AnnotationConfigApplicationContext。因此, Spring 容器可以實現零 XML 配
置。

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

AnnotationConfigApplicationContext 不是僅能與@Configuration 注解類配合使用。任何@Component 或者 JSR-330 注解的類都可以作為其構造函數的參數:

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。
我們就得使用編程注入依賴~

使用 register(Class…)編程式構造 Spring 容器

AnnotationConfigApplicationContext 也可以通過無參構造函數實例化,然後調用 registor()方法配置。此法應用於編程式構

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();
}

這些好麻煩的感覺~xml挺好的為什麼要基於java配置呢?

開啟組件掃描

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


    

上面的栗子中,會掃描 com.acme package 包,檢索出所有@Component-annotated類, Spring 容器將會注冊這些類為 Spring bean 定義。
在上面的示例中,com.acme包將被掃描,尋找任何@Component帶注釋的類,這些類將注冊為Spring 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);
}

Note:Remember that @Configuration classes are meta-annotated with @Component,(他的元注解是@Component) so they are candidates for component-scanning! In the example above, assuming that AppConfig is declared within the com.acme package (or any package underneath), it will be picked up during the call to scan(), and upon refresh() all its @Bean methods will be processed and registered as bean definitions within the container.

使用 AnnotationConfigWebApplicationContext 支持 WEB 應用

WebApplicationContext 接口一個實現 AnnotationConfigWebApplicationContext,是AnnotationConfigApplicationContext 的一個變體。在配置ContextLoaderListener、 Spring MVCDispatcherServlet 等等時,使用此實現類。下面這段 web.xml 片段,是典型 Spring MVC 的 Web 應用的配置。注意contextClass 類的 context-param 和 init-param。


    
    
        contextClass
        
            org.springframework.web.context.support.AnnotationConfigWebApplicationContext
        
    

    
    
        contextConfigLocation
        com.acme.AppConfig
    

    
    
        org.springframework.web.context.ContextLoaderListener
    

    
    
        dispatcher
        org.springframework.web.servlet.DispatcherServlet
        
        
            contextClass
            
                org.springframework.web.context.support.AnnotationConfigWebApplicationContext
            
        
        
        
            contextConfigLocation
            com.acme.web.MvcConfig
        
    

    
    
        dispatcher
        /app/*
    

這些和我們在學習servlet的時候差不多吧~比如監聽器,攔截器~進行處理!

使用@Bean 注解

@Bean 是方法注解,和 XML 中的元素十分相似。該注解支持的一些屬性,比如 init-method, destroy-method,autowiring 和 name

聲明Bean

要聲明 bean 非常簡單,只需要在方法上使用@Bean 注解。使用此方法,將會在ApplicationContext 內注冊一個 bean, bean 的類型是方法的返回值類型

@Configuration
public class AppConfig {

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

}

上面的配置和下面的 XML 配置等價:


    

上面兩種配置,都會在 ApplicationContext 內產生一個 bean 定義,名稱為transferService,該 Spring bean 綁定到一個類型為 TransferServiceImpl 的實例:
transferService -> com.acme.TransferServiceImpl

Receiving lifecycle callbacks 接受生命期的回調

使用@Bean 注解的 bean 定義,都支持常規生命周期回調,能使用 JSR-250 中的@PostConstruct 和@PreDestroy 注解
The regular Spring lifecycle callbacks are fully supported as well(常規的回調周期,使用編碼的方式注入的也是支持的). If a bean implements InitializingBean, DisposableBean, or Lifecycle, their respective methods are called by the container.(這些都會被容器調用的哦)
The standard set of Aware interfaces such a**s BeanFactoryAware, BeanNameAware, MessageSourceAware, ApplicationContextAware*, and so on are also fully supported.

下面這個是我剛剛用MyExclipse查看的她可以使用的屬性!相信大家都是知道這些到底是啥子意思吧!

@Bean(initMethod=”dd”,destroyMethod=”XX”,name=”ddd”,autowire=Autowire.BY_NAME)

public class Foo {
    public void init() {
        // initialization logic
    }
}

public class Bar {
    public void cleanup() {
        // destruction logic
    }
}

@Configuration
public class AppConfig {

    @Bean(initMethod = "init")
    public Foo foo() {
        return new Foo();
    }

    @Bean(destroyMethod = "cleanup")
    public Bar bar() {
        return new Bar();
    }

}

Of course, in the case of Foo above, it would be equally as valid to call the init() method directly during construction:
自己主動的去調用也是可以得!當你直接在Java中,你可以做任何你喜歡的,做你的對象 並不總是需要依靠容器生命周期!

@Configuration
public class AppConfig {
    @Bean
    public Foo foo() {
        Foo foo = new Foo();
        foo.init();
    return foo;
    }

    // ...

}

使用@Scope 注解

The default scope is singleton, but you can override this with the @Scope annotation:

@Configuration
public class MyConfiguration {

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

}

@Scope and scoped-proxy 兩個作用域不一樣的之間的依賴關系

作用域代理完成作用域bean 依賴。若使用 XML 配置,最簡單的方式是使用元素創建一個代理。若是在 Java 代碼中配置 bean,有一種等價的做法,使用@Scope注解並配置其 proxyMOde 屬性.默認配置是沒有代理 ScopedProxyMode.NO,但是你可以設置 ScopedProxyMode.TARGET_CLASS 或者 ScopedProxyMode.INTERFACES。 如
果將 XML 格式的作用域代理示例轉換成 Java 中使用@Bean

// an HTTP Session-scoped bean exposed as a proxy
@Bean
@Scope(value = "session", proxyMode = ScopedProxyMode.TARGET_CLASS)
public UserPreferences userPreferences() {
    return new UserPreferences();
}

@Bean
public Service userService() {
    UserService service = new SimpleUserService();
    // a reference to the proxied userPreferences bean
    service.setUserPreferences(userPreferences());
    return service;
}

默認情況下,配置類中,使用@Bean 的方法名作為返回 bean 的名字。通過配置可以覆蓋此設置,使用 name 屬性 即可。

@Configuration
public class AppConfig {

    @Bean(name = "myFoo")
    public Foo foo() {
        return new Foo();
    }

}

默認為foo

bean 別名在之前討論過的 bean 別名“Naming beans”,有時候需要給一個bean 指定多個 name。 @Bean 注解的 name 屬性就是干這個用,該屬性接收一個字串數組。

Bean的描述,我們這個就可以使用到很多地方,File,Method,Type

挺有用的,查看使用的時候,非常的方便!

Configuration
public class AppConfig {

    @Bean
    @Desciption("Provides a basic example of a bean")
    public Foo foo() {
        return new Foo();
    }

}

Lookup method injection 之前我們提到過吧,使用XML方式處理

兩個不同生命周期之間的依賴關系!
我們之前有兩種不同的處理方式
每次,我們調用process方法的內部,自己去主動的調用新的createCommand()

// a class that uses a stateful Command-style class to perform some processing
package fiona.apple;

// Spring-API imports
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class CommandManager implements ApplicationContextAware {

    private ApplicationContext applicationContext;

    public Object process(Map commandState) {
        // grab a new instance of the appropriate Command
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    protected Command createCommand() {
        // notice the Spring API dependency!
        return this.applicationContext.getBean("command", Command.class);
    }

    public void setApplicationContext(
            ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }
}

第二種方法:使用CGLIB的動態調用(我也不是很了解這個玩意)

和上面的思路是一樣的,每次改變的時候我們自己動態的調用新的東西!

package fiona.apple;

// no more Spring imports!

public abstract class CommandManager {

    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();
        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
        return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}



    




    

Using Java-configuration support , you can create a subclass of CommandManager where the abstract createCommand() method is overridden in such a way that it looks up a new (prototype) command object:

public abstract class CommandManager {
    public Object process(Object commandState) {
        // grab a new instance of the appropriate Command interface
        Command command = createCommand();

        // set the state on the (hopefully brand new) Command instance
        command.setState(commandState);
    return command.execute();
    }

    // okay... but where is the implementation of this method?
    protected abstract Command createCommand();
}
Bean
@Scope("prototype")
public AsyncCommand asyncCommand() {
    AsyncCommand command = new AsyncCommand();
    // inject dependencies here as required
    return command;
}
 這個配置文件,從寫了,父類的,抽象方法!
@Bean
public CommandManager commandManager() {
    // return new anonymous implementation of CommandManager with command() overridden
    // to return a new prototype Command object
    return new CommandManager() {
        protected Command createCommand() {
            return asyncCommand();
        }
    }
}

這裡返回的對象其實是個無名的子類,然後調用子類的方法,去進行操作!

進一步基於java的配置是如何工作的內部信息

Configuration
public class AppConfig {

    @Bean
    public ClientService clientService1() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientService clientService2() {
        ClientServiceImpl clientService = new ClientServiceImpl();
        clientService.setClientDao(clientDao());
        return clientService;
    }

    @Bean
    public ClientDao clientDao() {
        return new ClientDaoImpl();
    }

}

clientDao()被 clientService1()調用了一次,被 clientService2()調用了一次。因為這個方法會創建一個 ClientDaoImpl 類的實例並返回,也許你以為會有 2 個實例(分別返回給各個 service)。這個定義會有問題:在 Spring 中,實例化bean 默認的作用域是單例。這就是它的神奇之處:所有的@Configuration 類在啟動時,都是通過 CGLIB 創建一個子類。在調用父類的方法並創建一個新的實例之前,子類中的方法首先檢查是否緩存過。 僅僅會有一個實例!這裡沒有采用New而是調用的方法。被限制為單例

組裝 java 配置元數據 Composing Java-based configurations

很多個java的配置文件,我們要把他們何在一起的塞!讓後掃描的時候比較的方便,加入到容器中。這個和我們不同的XML文件的時候我們也需要把他們合並在一起一個意思的!
在 Spring XML 配置中使用< import/>元素,意在模塊化配置, @Import 注解也允許從其他配置類中加載@Bean 定義。
例子:使用一個當主的,其他的加入/導入進去就行了。就是這麼的簡單!

@Configuration
public class ConfigA {

     @Bean
    public A a() {
        return new A();
    }

}

@Configuration
@Import(ConfigA.class)
public class ConfigB {

    @Bean
    public B b() {
        return new B();
    }

}

現在,實例化 context 時,不需要同時指定 ConfigA.class 和 ConfigB.class,而是僅需要提供 ConfigB 即可:

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

    // now both beans A and B will be available...
    A a = ctx.getBean(A.class);
    B b = ctx.getBean(B.class);
}

該方式簡化了容器實例化,只需要一個類去處理,而不是需要開發者在構造期間記著大量的@Configuration。能讓每個開發者自己做自己的事情!

Injecting dependencies on imported @Bean definitions

在大部分實際場景中, bean 都會跨配置依賴。若使用 XML,這不是問題,因為不包含編譯器,開發者簡單的聲明ref=somBean 並相信 Spring 在容器實例化期間會正常運行。但是,使用@Configuration 類,配置模型替換為 java 編譯器,為了引用另一個 bean, Java編譯器會校驗該引用必須是有效的合法 Java 語法。非常幸運,解決這個這個問題非常簡單。還記得不, @Configuration 類在容器中本身就是一個 bean,這意味著他們能使用高級@Autowired 注入元數據,就像其他 bean 一樣。
構造的時候,依賴別的哦~

@Configuration
public class ServiceConfig {

    @Autowired
    private AccountRepository accountRepository;

    @Bean
    public TransferService transferService() {
        return new TransferServiceImpl(accountRepository);//構造函數的時候注入依賴關系!
    }

}

@Configuration
public class RepositoryConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

}

@Configuration
@Import({ServiceConfig.class, RepositoryConfig.class})
public class SystemTestConfig {

    @Bean
    public DataSource dataSource() {
        // return new DataSource
    }

}

public static void main(String[] args) {
    ApplicationContext ctx = new AnnotationConfigApplicationContext(SystemTestConfig.class);
    // everything wires up across configuration classes...
    TransferService transferService = ctx.getBean(TransferService.class);
    transferService.transfer(100.00, "A123", "C456");
}

@Autowired 可以很好的工作,使設計更具模塊化,但是自動注入的是哪個 bean 依然有些模糊不清。Spring Tool Suite 提供了可視化工具,用來展示 bean 之間是如何裝配的,也許這就是你需要的。(這個可視化工具呢?)

混合 java 和 xml 配置

Spring 的@Configuration 類並非是為了完全替換 Spring XML。有些工具,比如XML 命名空間就是一種理想的配置方式。如果 XML 更方便或是必須的,你就得選擇:或者選擇基於 XML 的配置方式實例化容器,比如使用ClassPathXmlApplicationContext,或者選擇基於 Java 配置風格使用AnnotationConfigApplcationContext 加上@ImportResource 注解導入必須的XML。

基於 XML 混合使用@Configuration 類

已存在大量的使用了 SPringXML 的代碼,有需求需要使用@Configuration 類,這些配置類需要引入到現存的 XML 文件中,此種做法也許更容易。接下來看看此場景。
@Configuration 類本身在容器內就是一個 bean。下面的樣例中,創建了一個@Configuration 類,類名是 AppConfig,引入一個配置文件 system-testconfig.xml。由於< context:annotation-config/>打開,容器會識別@Configuration 注解,並處理 AppConfig 類內聲明的@Bean 注解的方法。

@Configuration
public class AppConfig {

    @Autowired
    private DataSource dataSource;

    @Bean
    public AccountRepository accountRepository() {
        return new JdbcAccountRepository(dataSource);
    }

    @Bean
    public TransferService transferService() {
        return new TransferService(accountRepository());
    }

}
system-test-config.xml

    
    
    

    //本身就是一個bean,識別裡面的注解!加入到容器中去~

    
        
        
        
    
jdbc.properties
jdbc.url=jdbc:hsqldb:hsql://localhost/xdb
jdbc.username=sa
jdbc.password=
public static void main(String[] args) {
    ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath:/com/acme/system-test-config.xml");
    TransferService transferService = ctx.getBean(TransferService.class);
    // ...
}


system.test-config.xml 中, AppConfig< bean/>沒有 id 屬性,因為沒有其他bean 引用,也不會根據 name 從容器獲取,所以 id 不是必須指定的,同樣,DataSourcebean,它只會根據類型自動裝配,所以明確的 id 也不是必須的。因為@Configuration 是@Component 的元數據注解,@Configuration 注解類也會自動作為掃描組件的候選者。

我們能重新定義 system-testconfig.xml,使之能啟用高級掃描組件。注意,在此場景中,我們不需要明確的
聲明< context:annotation-config/>,因為< context:component-scan/>會開啟所有相同的功能。

system-test-config.xml

     這種方式掃描組件也是可以的。自己的多注意這些之間的關系和區別!
    
    

    
        
        
        
    

基於@Configuration 混合使用 xml 配置 誰是誰的主導呢

在應用中, @Configuration 類是主要的容器配置機制,但是仍然可能會需要一些 XML。在這些場景中, 使用@ImportResource,即可引用 XML 配置。這樣配置可是實現此效果,基於 java 配置,盡可能少的使用 XML。
XML的唯一問題是你要等到運行時的時候來發現Bean裡面的錯誤或者其他愚蠢的問題。

@Configuration
@ImportResource("classpath:/com/acme/properties-config.xml")
public class AppConfig {

    @Value("${jdbc.url}")
    private String url;

    @Value("${jdbc.username}")
    private String username;

    @Value("${jdbc.password}")
    private String password;

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

}
properties-config.xml

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

`

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved