Spring入门之IOC

Spring是JavaEE的必学的框架之一,之前只稍微接触过,并没有系统性的深入了解和学习,最近打算开始系统的学习Spring的。今天写写一下关于Spring的IOC。

Spring的两大特性即为IOC和AOP,一个叫控制反转一个叫面向切面的编程。关于IOC,其实还有个描述的更为透彻的名词叫注入(DI)。这两个名词都是描述的Spring的统一特性。因为这是我的学习记录,所以此处只记录下我的理解。IOC和DI的意思,就是以前我们在编写代码的时候,要获取对象时是通过自己去new对象来获得,我们需要自己去处理去控制这个对象。而利用IOC,Spring框架则可以帮助我们去获得对象,我们将把对象的控制权交给Spring框架,我们需要用的时候直接拿来用即可,不用过多的去关注对象的生成啊周期之类的。

而如何去利用IOC特性,如果去实现这个功能,是我们使用Spring框架的一大知识点。详细点说,我们是需要去配置这个对象注入的过程的。常见的过程是,我们利用一个配置文档,在配置文档中配置编写我们需要注入的Bean(Spring中的对象都想bean),在需要获取对象时,spring读取配置文档,给我们一个对象即可。

实例:

public class SampleDAOImpl implements SampleDAO {

	public void sayByName(String word) {
		System.out.println("autowiringDao:" + word);
	}
	
	
	@Override
	public void insertSample(String s) {
		// TODO Auto-generated method stub
		System.out.println("保存数据" + s);
	}

}

我们自己定义了一个实现了SampleDao的SampleDAOImpl对象,我们正常在其他位置处使用这个对象的方式是。SampleDAOImpl sampleDaoImple = new SampleDAOImpl(),再去使用。而利用在配置文档中配置,我们可以采用下面方式:

@Test
public void testGetBean() {
	String springXmlpath = "classpath*:applicationContext.xml";
	ClassPathXmlApplicationContext context = 
			new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
	SampleDAOImpl sampleDAOImpl = (SampleDAOImpl) context.getBean("sampleDAOImp1");
}

上面代码的意思就是,获取配置文档信息,利用配置文档中的配置信息来返回对象。而配置文档中的配置信息为:

<?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:aop="http://www.springframework.org/schema/aop"
    xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
   http://www.springframework.org/schema/beans 
   http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
   http://www.springframework.org/schema/aop 
   http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
   http://www.springframework.org/schema/tx 
   http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
   http://www.springframework.org/schema/context      
   http://www.springframework.org/schema/context/spring-context-3.0.xsd"   
   >

   
   <bean id="sampleDAOImp1l" class="parafeel.dao.SampleDAOImpl">	
   </bean>
 
</beans>
我们只在配置文档处,配置了一个bean,设置了它的id和class属性,既可利用IOC来获得对应的对象。这个流程就相当是一个简单的依赖注入过程。而这个过程中我们有几点是需要去特别了解和学习的,不然的话在使用过程中就知其然而不知其所以然。

一、利用配置文件的方式来实现IOC

1、Spring的注入方式(即在IOC容器中注入Bean的方式)

常见的方式有两种1)设值注入,2)构造注入。

1)设值注入就是在对我们想要注入的属性,设置其set方法,并且在配置文档中配置到即可,

例如:我们有一个SampleServiceImpl类,里面有一个属性是SampleDAOImpl,那么利用Spring,我们在使用时Spring会给我们自动注入这个daoImpl对象,我们只需要配置即可。配置方式为:

public class SampleServiceImpl implements SampleService {
	
	private SampleDAOImpl sampleDAOImpl;	
	
	public void setSampleDAOImpl(SampleDAOImpl sampleDAOImpl) {
		this.sampleDAOImpl = sampleDAOImpl;
	}
}
我们可以看到SampleServiceImpl类里有个SampleDAOImpl属性,我们定义了这个属性的set方法,我们在配置文档中配置:

   <bean id="sampleDAOImpl" class="parafeel.dao.SampleDAOImpl">	
   </bean>
   
   <bean id="sampleServiceImpl" class="parafeel.service.SampleServiceImpl">
   		<property name="sampleDAOImpl" ref="sampleDAOImpl"></property>
   </bean>

既有set方法,有配置了property,既可以实现设值注入。

2)构造注入,同理,构造注入就是在SampleServiceImpl类里有个SampleDAOImpl属性,我们定义把这个属性定义为SampleServiceImpl类构造方法中的一个变量,再配置配置文档即可:

public class SampleServiceImpl implements SampleService {
	
	private SampleDAOImpl sampleDAOImpl;	
	
	public SampleServiceImpl(SampleDAOImpl sampleDAOImpl) {
		super();
		this.sampleDAOImpl = sampleDAOImpl;
	}
}

配置文档:

   <bean id="sampleDAOImpl" class="parafeel.dao.SampleDAOImpl">	
   </bean>
   
   <bean id="sampleServiceImpl" class="parafeel.service.SampleServiceImpl">
   		<constructor-arg index="0" ref="sampleDAOImpl"></constructor-arg>
   </bean>

上面两种设值注入及构造注入,就是spring的IOC提供的两种注入方式。而利用以上注入的方式,我们获取对象的方式变为:

@Test
public void testGetBean() {
	String springXmlpath = "classpath*:applicationContext.xml";
	ClassPathXmlApplicationContext context = 
			new ClassPathXmlApplicationContext(springXmlpath.split("[,\\s]+"));
	SampleServiceImpl s = (SampleServiceImpl) context.getBean("sampleServiceImpl");
	s.insertSample("test!");
}
此时,我们便将对对象的控制交予了Spring的IOC容器。我们只需要使用就行了!


    上面对象的注入过程可以视为对Bean的装配,在装配过程中,我们需要了解许多其他的内容,包括:配置文档的配置项及其作用、Bean的作用域、Bean的生命周期、Aware接口、和Bean的自动装配

2、Bean的配置项、作用域、生命周期及Aware接口

再重复一遍,在Spring的IOC容器里,每个类都是Bean。我们在注入类的时候,都是在配置Bean。在下面的配置文档中:

   <bean id="sampleDAOImpl" class="parafeel.dao.SampleDAOImpl" scope="singleton">	
   </bean>
   
   <bean id="sampleServiceImpl" class="parafeel.service.SampleServiceImpl">
   		<property name=""></property>
   		<constructor-arg index="0" ref="sampleDAOImpl"></constructor-arg>
   </bean>
   
1) 我们可以看到的配置项就用,id、class、scope。以及property、constructor-arg,这些都是用来配置bean的各项属性的。例如id就是指bean的名字,class就是指这个bean对应的类名,scope就是这个bean在IOC容器的作用域。property指这个bean的属性、constructor-arg则为这个bean的构造函数的参数。正式这些配置项,使得IOC能够利用配置文档来操作管理每个bean。这块的话,用到什么配置项才会更加了解,此处就写这些最常用的配置项,其他的还有很多配置项。

2)作用域,则是用来限定一个Bean在IOC容器中的作用域,常见的作用域范围有:单例(singleton)在IOC容器中是单例的,prototype每次请求都创建一个新的实例、request、session、global session,等。这些作用域对应的内容就是一个Bean在被IOC创造后,这个Bean能活多久。

3)除了作用域,第三个需要关注的配置细节是生命周期,一个Bean的生命周期有定义、初始化、使用、销毁,我们面对这四个部分,定义和使用是必然需要的,而有时候我们还需要配置一下初始化和销毁方法。

例如:常见的初始化(针对某个bean)方法有两种,一种是让这个bean实现org.springframework.beans.factory.initinlizingBean接口,同时重写其afterPropertiesSet方法。另一种方法是在XML中配置Bean时,在init-method字段加入其初始化所对应的方法,并在类里定义好这个方法。

第一种示例:实现了此接口

<bean id="sampleDAO" class="parafeel.dao.SampleDAO">
</bean>

public class SampleDAO implements InitializingBean{
	public void daoSay(String s){
		System.out.println("SampleDAO : " + s);
	}

	@Override
	public void afterPropertiesSet() throws Exception {
		System.out.println("init");
	}
}

在实现接口后,我们不需要在XML中配置初始化方法,只需配置一个Bean即可。

第二种示例:下面分别是XML配置文档写法,和类中的方法。那么在获取这个类时,就会自动运行init方法。

<bean id="sampleDAO" class="parafeel.dao.SampleDAO" init-method="init">
</bean>

public class SampleDAO {
	public void init() {
		System.out.println("init");
	}
	
	public void daoSay(String s){
		System.out.println("SampleDAO : " + s);
	}
}
以上便是配置bean的生命周期初始化部分的两个方法。而销毁的时候与此类似,也是两个方法。分别是,一实现org.springframework.beans.factory.DisposableBean接口实现其destroy方法。二是在XML配置文档中配置其destroy-method字段对应的方法,同时在bean中定义好对应方法。与初始化的步骤和内容相似,此处就不重复了。此外需要提一点的是,我们还可以在XML中配置所有bean的默认初始化和销毁方法,只需要设置default-init-method字段和default-destroy-method字段即可。

当然,我们可能分别实现了,设置默认的初始化或销毁方法,实现了初始化或销毁接口,定义了init-method与destroy-method字段对应的方法,这三种设置方式我们都实现了。那么运行的情况是,先开始初始化接口的方法,再进行init-method,先开始销毁接口的方法,再进行destroy-method,同时default的初始化或销毁方法不执行。
4)Aware。这块就我的了解,大概就是利用Spring下的各种Aware接口,辅助Spring Bean编程访问Spring容器。之前的几项,我们发现只要配置好之后,我们是关注不了Sping的IOC容器里是怎么运行的,我们直接面对的是返回给我们的结果。但是有时候我们需要访问其容器内部,此时就需要Aware接口。例如,我们在一个类中,实现ApplicationAware接口,并实现其setApplication方法,如下:

public class SampleDAO implements ApplicationContextAware{
	ApplicationContext applicationContext;
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) 
			throws BeansException {
		// TODO Auto-generated method stub
		this.applicationContext = applicationContext;
	}
我们可以看到,当一个类 实现 ApplicationAware接口,并实现其 setApplication方法,我们可以把

ApplicationContext注入到此类的属性中,这就相当于把Spring容器内部的资源打开了,我们利用此接口,就可以操作容器内部的重要资源。这块的内容应该是比较复杂和多的,但是基本的应用方向应该就是这样。


3、Bean的自动装配

上面的这些内容就是Spring的IOC方面的主要内容了,但是实现这些内容的方式除了配置的方式之外

我们还可以采用注解的方式实现自动装配(Autowiring)

1)之前我们说过IOC容器注入Bean的两种方式,设置注入和构造注入,这两种都需要在XML中配置,而利用自动装配,我们可以减少甚至不用XML来配置。

常见的有设置@Autowiring的属性的方法,默认我们不设置自动装配属性时,就是@Autowiring(NO),此时我们在配置一个Bean的属性为另一个Bean时,就需要配置其preperties字段或constructir-arg字段,而利用@Autowiring(byName)或者@Autowiring(byType)和@Autowiring(constructor)则可以不需要在一个bean内部配置其各种属性,在这个bean对应的类的内部各属性只要也是配置了的Bean时,就会发生自动装配。而不需要我们手动配置。

示例:我们在XML中配置如下:

default-autowire="byName"
   >
  	<bean id="sampleDAO" class="parafeel.dao.SampleDAO">
  	</bean>
   	<bean id="sampleService" class="parafeel.service.SampleService">
   	</bean>

在对应的SampleService的Bean里设置SampleDAO属性,并设置其set方法。

public class SampleDAO {
	public void daoSay(String s){
		System.out.println("SampleDAO : " + s);
	}
}
public class SampleService implements serviceInter{
	private SampleDAO sampleDAO;
	public void setSampleDAO(SampleDAO sampleDAO) {
		this.sampleDAO = sampleDAO;
	}
	@Override
	public void saySampleService(String s) {
		sampleDAO.daoSay(s);
		System.out.println("SampleService:" + s);
	}
}
而在下面的测试方法中,我们直接获取sampleService的过程中,sampleDAO已经被注入到这个示例里了,这就是自动装配的实现。

@Test 
public void testS() {
	SampleService sampleService = super.getBean("sampleService"); 
	sampleService.saySampleService("test");
}

这个就是byName来实现自动装配,此外还有byType。而constructor则是需要在属性对应的类中设置其构造函数而不是set方法,两种的区别主要就是这一点。大概可以理解成,byName和byType是用来简化设值注入,而constructor是用来简化构造注入的。此外我们还要知道在使用 byName、byType和constructor,我们配置不正确则会爆出异常,假如配置正确而在对应内中没有set方法或构造方法,则不会报异常且不会运行。
4、Resources

Resources是针对资源文件的统一接口。利用这个接口,可以读取各个位置存放的资源文件。


二、利用注解的方式来实现IOC

在上面,我们其实基本也就介绍了IOC是干什么,一般怎么用,有什么功能。我们在使用过程中发现,在IOC的应用过程中,配置文件属于重要的一环,什么都需要手动去配置,所谓的自动装配也看上去没那么自动,这时,利用注解(annotation)来实现IOC,会使得配置更加简单。

1、classpath扫描与组件管理

我们可以利用java的注解来配置Bean,而不利用XML来配置。常用的注解有,@Component(通用注解)、@Repository(注解持久层)、@Service(注解服务层)、@Controller(注解控制层)。即把上面的这些注解,注解到各个包的类中,进而来表示之前XML文件中需要分别配置的Bean。在注解上去之后,我们还是需要在XML中,配置一下扫描范围

<context:component-scan base-package="parafeel"></context:component-scan>
<context:property-placeholder location="classpath:/config.properties"/>
     
通过配置扫描范围,来确定对应的注解已经开始启用了。上面的是典型的扫描,范围比较大。我们还可以利用在XML中配置过滤器来修改范围。

2、如何用注解定义一个Bean

在XML中,我们一般是:

 <bean id="sampleDAO" class="parafeel.dao.SampleDAO">
 </bean>
 <bean id="sampleService" class="parafeel.service.SampleService" scope="singleton">
   	<property name="sampleDAO" ref="sampleDAO"></property>
 </bean>
而,利用注解,我们可以:

@Repository
public class SampleDAO {
	public void daoSay(String s){
		System.out.println("SampleDAO : " + s);
	}
}

@Service
@Scope("singleton")
public class SampleService implements serviceInter{
	@Autowired
	private SampleDAO sampleDAO;

	@Override
	public void saySampleService(String s) {
		sampleDAO.daoSay(s);
		System.out.println("SampleService:" + s);
	}
}
而在配置文件中,我们只要顶一下扫描的范围:

	<context:component-scan base-package="parafeel"></context:component-scan>
   	<context:property-placeholder location="classpath:/config.properties"/>
在使用注解的过程中,我们还要主要,例如

1)@Component是一个比@Repository、@Service、@Controller等更加广泛的注解。
2)在使用@Component放在类名前时,默认的Bean的名字就是类名首字母小写,我们也可以自己定义bean名字,例如@Component("test")。即可以定义名字,不定义有默认名字。

同时,关于名字,我们还可以利用实现BeanNameGenerator接口的类A,在XML中配置 

<context:component-scan base-package="parafeel" name-generator="A"></context:component-scan>

来实现自定义的默认命名策略。

3)我们可以在Bean的上面通过@scope("singleton"),利用设置其值,来定义一个Bean的作用域。

同时关于作用域,我们也可以实现ScopeMetadataResolver接口的类A,在XML中配置scope-resolver="A",如上,来实现自定义的作用域。

4)设置scoped-proxy,来设置代理方式,指定代理。

最后,我们在设置类A中有一属性b,以及类B。面对这样情况,我们既可以在属性b上@Autowired,也可以在属性b对应的setter方法上@Autowired,还可以在有属性b的A内构造方法上@Autowired。其实与XML配置的道理类似,只是方式不一样。


3、详解@Autowired注解

我们在上面利用注解来定义一个Bean的解释中,可以发现@Autowired是一个很常用的接口。关于@Autowired这个接口,我们应该知道一下几点:

1)@Autowired可以直接注解众所周知的常用的解析依赖接口,例如

@Service
@Scope("singleton")
public class SampleService implements serviceInter{
	@Autowired
	private ApplicationContext applicationContext;
	@Autowired
	private SampleDAO sampleDAO;

在之前,我们有所过,要想获得ApplicationContext这个对象,来深度试用Spring容器的内部,一般可以使类继承相应的Aware接口。而利用 @Autowired,我们可以直接自动往类里自动装配这个属性。
2) @Autowired可以用来注解泛型(在3.1上实验为成功),如下:

@Service
@Scope("singleton")
public class SampleService implements serviceInter{
	@Autowired
	private daoInter<Integer> integerDAO;
	@Autowired
	private SampleDAO sampleDAO;

还可以注解在集合Set或者List容器上,用来获取对应泛型的所有Bean:

public class SampleService implements serviceInter{
	@Autowired
	private Set<daoInter> integerDAO;
	@Autowired
	private SampleDAO sampleDAO;

3)@Autowired可以用来注解接口,当一个接口只有一个属性类时,我们可以在此接口上注解,在自动装配时,就会自动装配此接口的唯一实现类,当有多个实现类的时候会报异常。

4)@Autowired是有Spring下的BeanPostProcessor处理的,所有不能再BeanPostProcessor上应用这个注解。

4)@Qualifier,当有多个Bean实例或者多个方法时,可以利用@Qualifier来缩小扫描的指定范围。


3、详解@Bean注解

@Bean注解类似于XML配置文档中的<bean/>,我们的常用方式为:

@Configuration
public class SampleService implements serviceInter{
	
	@Bean
	public SampleDAO mySampleDAO() {
		return new SampleDAO();
	}
等价于:

<bean id="mySampleDAO" class="....">
</bean>
当然,我们也可以在每个 @ Bean注解上配置其名称,生命周期的初始化和销毁方法等。@Bean(name=" " initmethod=" " destroymethod=" ")等。还能配置@Bean的scope作用域,默认作用域是singleton。

此外,我们还可以在@Bean上通过@ImportResource注解来获取对应位置的资源文件,@Value来给对应的属性进行赋值。此处可以用在例如数据库的配置处,我们只需要在Bean对应的类上@ImportResource对应位置的资源文件,再在类里各属性处利用@Value来进行赋值即可。

这就是IOC的基本内容。









  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值