1. 程式人生 > >Spring基於純註解方式的使用

Spring基於純註解方式的使用

經過上篇xml與註解混合方式,對註解有了簡單額瞭解,上篇的配置方式極大地簡化了xml中配置,但仍有部分配置在xml中進行,接下來我們就通過註解的方式將xml中的配置用註解的方式實現,並最終去掉xml配置。

一、xml中遺留配置

註解掃描

<!-- 開啟註解並掃描指定包中帶有註解的類 -->
<context:component-scan base-package="com.kkb.spring.service"/>

非自定義bean,如sqlsessionFactory

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" value="dataSource"></property>
</bean>

下面將用註解的方式進行替代。

二、元件註冊

首先是@Configuration註解,被其修飾的類可看做xml配置檔案,通過此類進行元件註冊。而上下文容器可通過下面方式進行建立:

ApplicationContext context = new    AnnotationConfigApplicationContext(SpringConfiguration.class);

其中,SpringConfiguration是被@Configuration修飾的配置類。

1、@Bean註解

@Bean是Spring提供的註解,它在配置類中使用,可以將Java類交給Spring進行管理,@Bean有如下特徵:

  • 預設時,以@Bean修飾的bean物件對應的關鍵字是【方法名】
  • 如果在@Bean指定bean物件對應關鍵@Bean(value={"stu1","stu2"}),此時bean物件在Spring容器對應關鍵字就不是【方法名】,是stu1或則stu2
  • 所有通過@Bean修飾生成的bean物件預設的情況下都是單例。
  • 對於單例的bean物件,可以通過@Lazy延緩變物件被建立時機。
@Configuration//相當於配置檔案
public class ApplicationConfig {
    
    @Bean(value={"stu1"})
    public Student student2(){
        return new Student();
    }
    
    //這個註解專用於單例模式bean物件,此時bean物件不會在,spring容器啟動時被建立的,只有在一個使用者來訪時才會被建立
    @Lazy
    @Bean
    public Teacher teacher(){
        return new Teacher();
    }
}

2、@ComponentScan註解

@ComponentScan是Spring中的註解,在配置類中使用,用於配置掃描的類,它可以將某些類排除在Spring容器之外,也可以將某些類新增到Spring容器之內 。相當於context:component-scan標籤

@ComponentScan(value="com.kkb.beans")
@Configuration//相當於配置檔案
public class ApplicationConfig {}

可以在@ComponentScan中配置FilterType進行掃描檔案的過濾,FilterType提供五種掃描策略:

  • ANNOTATION 根據註解進行過濾(@Controller,@Service,@Resposity,@Compoent)
  • ASSIGNABLE_TYPE 根據指定型別
  • ASPECTJ表示式過濾
  • REGEX根據正則表示式過濾
  • CUSTOM,根據開發人員自行定義過濾規則

2.1、將指定類新增到Spring容器中

將指定的類新增Spring容器使用includeFilters

@ComponentScan(value="com.luis.beans",useDefaultFilters=false,
             includeFilters={
                      @Filter(type=FilterType.ANNOTATION,
                               value={Controller.class,Service.class}),
                       
                      @Filter(type=FilterType.ASSIGNABLE_TYPE,
                              value={DeptDao.class})       
               })
@Configuration//相當於配置檔案
public class ApplicationConfig {

}

上面的程式碼採用了1,2兩個策略將指定類新增到Spring容器中。

自定義掃描規則的配置如下:

在@ComponentScan(value="包路徑",
              excludeFilters={
                        @Filter(type=FilterType.CUSTOM,value=自定義過濾規則類.class)
              })
@Configuration//相當於配置檔案
public class ApplicationConfig {

}

此外還需配置過濾規則類。

2.2、將指定類排除Spring容器外

將指定類排除Spring容器外使用excludeFilters

@ComponentScan(value="com.luis.beans",
               excludeFilters={
                       @Filter(type=FilterType.ANNOTATION,
                               value={Controller.class,Service.class})  
               })
@Configuration//相當於配置檔案
public class ApplicationConfig {

}

3、@Conditionnal註解

@Conditionnal是Spring提供的註解,用在配置類中,用於動態決定是否新增進Spring容器中。

現有兩個類:Student和Teacher

//如果當前工程執行在Windows系統下,就註冊Student
public class Student {}

//如果當前工程執行在Linux系統下,就註冊Teacher
public class Teacher {}

定義兩個判斷類:LinuxCondition和WindowsCondition

public class LinuxCondition implements Condition {
    /*
     * ConditionContext context:spring容器上下文環境
     * AnnotatedTypeMetadata metadata :@Conditional修飾型別資訊
     */
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
          String systemName = context.getEnvironment().getProperty("os.name");
          if(systemName.contains("Linux")){
              return true;
          }
          return false;
    }
}
public class WindowsCondition implements Condition {
    
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { 
           String systemName = context.getEnvironment().getProperty("os.name");
           if(systemName.contains("Windows")){
               return true;
           }
        return false;
    }
}

配置類程式碼:

@Configuration//相當於配置檔案
public class ApplicationConfig {

    @Conditional({LinuxCondition.class})
    @Bean
    public Teacher teacher(){
        return new Teacher();
    }
    
    @Conditional({WindowsCondition.class})
    @Bean
    public Student student(){
        return new Student();
    }
}

4、@Import註解

用來組合多個配置類, 相當於spring配置檔案中的import標籤,在引入其他配置類時,可以不用再寫@Configuration 註解。也可以指定將指定bean導到SpringIOC容器中

@import(value="com.luis.Student")
@Configuration//相當於配置檔案
public class ApplicationConfig {

}

此外,可通過自定義的選擇器將bean新增到IOC容器中。

public class MyImportSelector implements ImportSelector {

    public String[] selectImports(AnnotationMetadata importingClassMetadata) {
        String classNames[]={"com.luis.Student"};
        return classNames;
    }
}
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {

    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        //1.將Java類註冊到Spring
        BeanDefinitionBuilder builer  = BeanDefinitionBuilder.genericBeanDefinition(Teacher.class);
        //2.建立當前Java類的例項物件
        BeanDefinition obj= builer.getBeanDefinition();
        
        //3.通過Spring的bean註冊器,將當前Java類的例項物件新增到Spring容器
        registry.registerBeanDefinition("luis", obj);
    }
}

配置類程式碼如下:

@Import(value={MyImportSelector.class,MyImportBeanDefinitionRegistrar.class})
@Configuration//相當於配置檔案
public class ApplicationConfig {
    //兩種新增方式
}

5、FactoryBean介面

FactoryBean是Spring容器提供的一種註冊bean的方式,通過它來獲得元件的bean的物件以及bean物件的單例或則多例的形態。

public class MyFactoryBean implements FactoryBean<Student> {

    //通知Spring容器,當前Student類的例項物件建立方式
    public Student getObject() throws Exception {
        return new Student();
    }

    //通知Spring容器,被管理的bean物件在spring容易對應的型別
    public Class<?> getObjectType() {
        return Student.class;
    }

    /*
     *   true 單例
     *   false prototype
     * */
    public boolean isSingleton() {
        return true;
    }
}

配置類程式碼如下:

@Configuration//相當於配置檔案
public class ApplicationConfig {
    @Bean
     public FactoryBean factoryBean(){
         return new MyFactoryBean();
     }
}

6、@PropertySource註解

寫在配置類中,用於 載入properties配置檔案,相當於context:property-placeholder標籤

properties檔案:

jdbc.driver=com.mysql.jdbc.Driver 
jdbc.url=jdbc:mysql:///spring
jdbc.username=root 
jdbc.password=root

配置類:

@Configuration
@PropertySource("classpath:jdbc.properties")
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    /**
     * 建立一個數據源,並存入 spring 容器中
     */
    @Bean(name = "dataSource")
    public DataSource createDataSource() {
        try {
            ComboPooledDataSource ds = new ComboPooledDataSource();
            ds.setDriverClass(driver);
            ds.setJdbcUrl(url);
            ds.setUser(username);
            ds.setPassword(password);
            return ds;
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

二、bean物件賦值

1、@Value註解

@Value可在當前類中指定屬性上賦值,其有以下幾種用法:

public class Student {

    @Value("luis")//使用基本資料為屬性賦值
    private String sname;
    @Value("#{28-2}")//使用SPEL為屬性賦值
    private int age;
    @Value("${student.home}")//讀取來自於外部的properties屬性檔案內容
    private String home;
}

2、BeanPostProcessor

為所有的類的指定屬性賦值BeanPostProcessor(後置處理器),BeanPostProcessor介面的自定義直接實現類可以在當前Spring容器的 所有bean物件初始化前後被呼叫。

BeanPostProcessor是一個介面,主要用於在bean物件初始化前後,做一些輔助功能

其中,postProcessBeforeInitialization:bean被初始化之前工作,postProcessAfterInitialization:被初始化之後工作

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {

    //在bean物件初始化之前被呼叫 bean是Spring容器管理一個物件,beanName就是當前物件在Spring容器關聯關鍵字
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        
        if(bean.getClass()==Dog.class){
          try{
              Field field = bean.getClass().getDeclaredField("age");
              field.setAccessible(true);
              field.set(bean, 15);
          }catch(Exception ex){
              ex.printStackTrace();
          }
        }else if(bean.getClass()==Bird.class){
             try{
                  Field field = bean.getClass().getDeclaredField("age");
                  field.setAccessible(true);
                  field.set(bean, 9);
              }catch(Exception ex){
                  ex.printStackTrace();
              }
        }
        return bean;
    }

    //在bean物件初始化之後被呼叫 bean是Spring容器管理一個物件,beanName就是當前物件在Spring容器關聯關鍵字
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }   
}

配置類:

@ComponentScan(value={"com.luis.beans"})
@Configuration//相當於配置檔案
public class ApplicationConfig {}

BeanPostProcessor的原理是:遍歷Spring容器中所有的BeanPostProcessor,執行每一個BeanPostProcessor中的 postProcessBeforeInitialization方法,如果某一次執行返回null.則立刻結束執行。

三、Bean的生命週期

1、@Bean註解處理Bean

可以在類檔案中定義初始化或銷燬方法,通過@Bean註解進行方法的指定。

public class Student{
    public void init() {
        System.out.println("物件被建立");
    }

    public void destory() {
        System.out.println("物件被回收");
    }
}
@Configuration//相當於配置檔案
public class ApplicationConfig {
     @Bean(initMethod="init",destoryMethod="destory")
    public Student student(){
        return new Student();
    }
}

2、InitializingBean&DisposableBean

我們可以讓類檔案同時實現InitializingBean介面與DisposableBean介面,根據這兩個介面提供的監聽方法來監聽當前類的bean的例項化時機和銷燬時機。

public class Student implements InitializingBean, DisposableBean {

    public Student() {
        System.out.println("Student構造方法被呼叫");
    }

    public void destroy() throws Exception {
        System.out.println("Student物件被銷燬");
    }

    public void afterPropertiesSet() throws Exception {
        System.out.println("Student物件被初始化");
    }
}

3、@PreDestroy& @PostConstruct

可以在類中指定方法上新增這兩個註解,來指定監聽bean物件被例項化和銷燬的時機。這兩個註解不是由Spring提供的。

public class Student{

    public Student() {
        System.out.println("Student構造方法被呼叫");
    }
    
    @PreDestroy
    public void destroy() throws Exception {
        System.out.println("Student物件被銷燬");
    }

    @PostConstruct
    public void afterPropertiesSet() throws Exception {
        System.out.println("Student物件被初始化");
    }
}

四、Junit的使用

在進行單元測試的過程中,每個方法都需要建立上下文容器,他們是不可或缺的,但又與我們的只寫業務程式碼的理念相違背,不過好在Junit 給我們暴露了一個註解(@RunWith),可以讓我們替換掉它的執行器。這時,我們需要依靠 spring 框架,因為它提供了一個執行器,可以讀取配置檔案(或註解)來建立容器,使用時只要指出配置檔案的位置。其步驟如下:

  • 新增依賴包:spring-test
  • 通過@RunWith註解,指定spring的執行器,Spring的執行器為:SpringJunit4ClassRunner
  • 過@ContextConfiguration註解,指定spring執行器需要的配置檔案路徑
  • 通過@Autowired註解給測試類中的變數注入資料

示例程式碼如下:

示例程式碼如下:

```java
@RunWith(SpringJunit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public calss TestStudentService{
@Autowired
private StudentMapper studentMapper;

@Test
public void getStudent(){
    studentMapper.getStudent();
}

}