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注解給測試類中的變量注入數據

示例代碼如下:

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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM