1. 通过在classpath自动扫描方式把组件纳入spring容器中管理 ---------------------------------------------------------------------- 为什么我们要使用类路径扫描的方式把组件纳入Spring容器中管理呢?前面的例子我们都是使用XML的bean定义来配置组件。尽管我们使用了依赖注入的注解减少了属性的注入配置,但是还是避免不了在配置文件中定义大量的bean,在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,配置文件会很臃肿,查找及维护起来也不太方便。spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息: Xml代码
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd">
          <context:component-scan base-package="cn.itcast"/>
</beans>
  1. 首先要引入context命名空间,接着要打开主键扫描<context:component-scan/>这个配置项
    其中base-package为需要扫描的包(含子包),就是说我们要扫描哪个包下面的类,这里要注意它除了会扫描你指定包下的类,还会扫描这个包底下子包的类。
    那么扫描这些类,Spring怎么知道这些类要交给Spring管理呢?这时候,我们用到了注解。spring2.5为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。

    @Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
    这四个注解目前作用都是一样的,因为目前Spring还没对这几个注解进行特别的处理,随便标哪个注解在目前都是一样的。当然可能以后会有一些细微的变化,目前还没做到这一点。

    beans.xml
    Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"       
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
         
          <context:component-scan base-package="cn.itcast"/>
</beans>
  1. 在beans.xml配置文件里,发现很干净,只是打开了配置项

    PersonDaoBean.java
    Java代码
package cn.itcast.dao.impl;

import org.springframework.stereotype.Repository;

import cn.itcast.dao.PersonDao;

@Repository  //这个注解告诉Spring,PersonDaoBean这个类现在要交给Spring管理,Spring扫描到
             //这个类之后就知道它要把这个类纳入到Spring容器中管理
public class PersonDaoBean implements PersonDao {
	public void add(){
		System.out.println("执行了PersonDaoBean中的add()方法");
	}
}
  1. PersonServiceBean.java Java代码  
package cn.itcast.service.impl;

import org.springframework.stereotype.Service;

import cn.itcast.dao.PersonDao;
import cn.itcast.service.PersonService;

@Service   //当Spring扫描到这个类之后就知道它要把这个类纳入到Spring容器中管理,而且它是作为服务层组件的
public class PersonServiceBean implements PersonService {
	private PersonDao personDao;

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public void save(){
		personDao.add();
	}
}
  1. SpringTest.java Java代码
package junit.test;

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.itcast.service.PersonService;

public class SpringTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@Test public void instanceSpring(){
		AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		PersonService personService = (PersonService)ctx.getBean("personService");
		System.out.println(personService);
		ctx.close();

	}
}
  1. 如果输出为null,则代表没被管理。
    运行单元测试代码,控制台输出:
    cn.itcast.service.impl.PersonServiceBean@be0e27
    cn.itcast.dao.impl.PersonDaoBean@193385d
    对象已经获取到了,我们采用的是Spring自动扫描的方式把bean交给了Spring管理,采用这种方式,以后就不需要在beans.xml配置文件配置大量的bean,不需要了,这样就把开发人员从配置文件中解脱出来了,这个功能很有用哈,这也是为什么Spring2.5推出来的原因,它提供了一些更便于开发的新功能,这个注解采用扫描的方式就是他推出的一个新特点



    有同学现在就可能有些想法了,默认值是bean的名称,首字母小写,那么能改么?  Spring当然能提供这种修改途径
    PersonServiceBean.java
    Java代码
package cn.itcast.service.impl;

import org.springframework.stereotype.Service;

import cn.itcast.dao.PersonDao;
import cn.itcast.service.PersonService;

@Service("personService")
//当Spring扫描到这个类之后就知道它要把这个类纳入到Spring容器中管理,而且它是作为服务层组件的
//可以通过personService这个名称获取这个bean了
public class PersonServiceBean implements PersonService {
	private PersonDao personDao;

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public void save(){
		personDao.add();
	}
}
  1. 运行单元测试代码,输出
    cn.itcast.service.impl.PersonServiceBean@be0e27
    cn.itcast.dao.impl.PersonDaoBean@193385d
    就是说,可以通过personService这个名称得到这个bean,这里,大家就学会了怎样修改bean的名称。

    这时候同学又有疑问了,bean的作用域范围有好几种啊(单例,原型prototype),目前到底是属于哪种作用域范围呢?是单例。  如果想把它改为原型prototype怎么办? 就要通过另一个注解@Scope

    PersonServiceBean.java
    Java代码
package cn.itcast.service.impl;

import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import cn.itcast.dao.PersonDao;
import cn.itcast.service.PersonService;

@Service("personService")
//当Spring扫描到这个类之后就知道它要把这个类纳入到Spring容器中管理,而且它是作为服务层组件的
//可以通过personService这个名称获取这个bean了
@Scope("prototype")  //每调用一次getBean方法,都会返回一个新的实例
public class PersonServiceBean implements PersonService {
	private PersonDao personDao;

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public void save(){
		personDao.add();
	}
}
  1. SpringTest.java Java代码
package junit.test;

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.itcast.dao.impl.PersonDaoBean;
import cn.itcast.service.PersonService;

public class SpringTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@Test
	public void instanceSpring() {
		AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		PersonService personService1 = (PersonService) ctx.getBean("personService");
		// 关于bean的名称,这里有条默认的规则,就是类的名称,然后第一个字母小写
		PersonService personService2 = (PersonService) ctx.getBean("personService");
		System.out.println(personService1 == personService2);
		ctx.close();
	}
}
  1. 如果打印false,则证明配置是成功的
    运行单元测试代码,打印结果是false
    所以通过@Scope这个注解,就可以修改bean的作用域。

    这时候同学们有没有想到另一个问题? 原先我们在beans.xml里使用bean配置的时候,我们可以指定初始化init-method方法,和销毁destroy-method方法.这里怎么办呢?    我们可以使用注解的方式来指定初始化方法和销毁方法

    PersonServiceBean.java
    Java代码
package cn.itcast.service.impl;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

import org.springframework.stereotype.Service;

import cn.itcast.dao.PersonDao;
import cn.itcast.service.PersonService;

@Service("personService")
//当Spring扫描到这个类之后就知道它要把这个类纳入到Spring容器中管理,而且它是作为服务层组件的
//可以通过personService这个名称获取这个bean了
public class PersonServiceBean implements PersonService {
	private PersonDao personDao;

	@PostConstruct   //大家注意,这个注解不是Spring的
	//以后学习EJB3的时候就可以发现,这个注解也是EJB3里面用来初始化bean的注解。
	//这个注解用的范围很广泛,Spring对这个注解也做了支持
	//这个注解的工作呢,后面也有个处理器对它进行解析,这个处理器在哪里注册呢?
	public void init(){
		System.out.println("初始化");
	}

	@PreDestroy //在bean实例被摧毁之前,会执行这个方法
	public void destory(){
		System.out.println("关闭资源");
	}

	public void setPersonDao(PersonDao personDao) {
		this.personDao = personDao;
	}

	public void save(){
		personDao.add();
	}
}
  1. beans.xml Xml代码
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">

          <context:component-scan base-package="cn.itcast"/>
		  <!--
		  	这个配置在背后注册了很多处理器,这些处理器都会对一些注解进行解析,其中就包括我们原先采用的
			<context:annotation-config></context:annotation-config>这个配置项里面的注解里面使用的处理器
			就是说使用了<context:component-scan>配置的话,<context:annotation-config>配置就可以去掉
		  -->
</beans>
  1. SpringTest.java Java代码
package junit.test;

import org.junit.BeforeClass;
import org.junit.Test;
import org.springframework.context.support.AbstractApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import cn.itcast.service.PersonService;

public class SpringTest {

	@BeforeClass
	public static void setUpBeforeClass() throws Exception {
	}

	@Test
	public void instanceSpring() {
		AbstractApplicationContext ctx = new ClassPathXmlApplicationContext("beans.xml");
		PersonService personService = (PersonService) ctx.getBean("personService");
		// 关于bean的名称,这里有条默认的规则,就是类的名称,然后第一个字母小写
		ctx.close();
	}
}

  1. 运行单元测试代码,输出
    初始化
    关闭资源

    这里已经给大家介绍完了怎样采用扫描的方式来把组件交给Spring管理,这种方式是我们目前在新开发应用中大量使用的方式,以前的应用中这种方式是没有这种功能的,以前采用Spring2.0是没有这种功能的,这个功能是2.5推出的,这个功能一推出,确实是被很多的新开发的项目所采用,因为这种方式确实是太方便了,能够大大提高我们的开发效率