所谓控制反转就是应用本身不负责依赖对象的创建及维护,依赖对象的创建及维护是由外部容器负责的。这样控制权就由应用转移到了外部容器,控制权的转移就是所谓反转。
依赖注入(Dependency Injection)
所谓依赖注入就是指:在运行期,由外部容器动态地将依赖对象注入到组件中
spring的配置文件模版
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd"> <bean id="userId"class="biz.impl.UserBizImpl"/> </beans>
实例化spring容器
ApplicationContextapp=new ClassPathXmlApplicationContext("bean.xml");
从spring容器中得到bean
当spring容器启动后,因为spring容器可以管理bean对象的创建,销毁等生命周期,所以我们只需从容器直接获取Bean对象就行,而不用编写一句代码来创建bean对象。从容器获取bean对象的代码如下:
ApplicationContextapp=new ClassPathXmlApplicationContext("bean.xml");
UserBizuserBiz=app.getBean("userId",UserBiz.class);
Bean的作用域
.singleton
在每个SpringIoC容器中一个bean定义只有一个对象实例。默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean会才初始化bean。如:
<bean id="xxx"class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
<beans default-lazy-init="true“...>
.prototype
每次从容器获取bean都是新的对象。<bean id=“” class=“” scope=“prototype”/>
每次从容器获取bean都是新的对象。
根据经验,对有状态的bean应该使用prototype作用域,
而对无状态的bean则应该使用singleton作用域
.request
表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTPrequest内有效
.session
表示该针对每一次HTTP请求都会产生一个新的bean,同时该bean仅在当前HTTPsession内有效
.globalSession
不过它仅仅在基于portlet的web应用中才有意义。Portlet规范定义了全Session
的概念,它被所有构成某个portletweb应用的各种不同的portlet所共享。
以上3种均基于web的SpringApplicationContext情形下有效
延迟初始化bean
默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,这时候,只有第一次获取bean时才初始化bean。如:
<bean id="xxx"class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,如下:
<beans default-lazy-init="true“...>
Bean的初始化方法和销毁方法
<bean id="xxx"class=“biz.OrderServiceBean" init-method="init" destroy-method="close"/> //init-method初始化方法 destroy-method销毁方法 public void init() { System.out.println(“已经初始化了”); } public void close() { System.out.println(“被销毁了”); } //ApplicationContext不具有close()方法来调用销毁函数,需要使用ApplicationContext 才可以调用close() AbstractApplicationContext ctx = newClassPathXmlApplicationContext("beans.xml"); PersonService person =(PersonService)ctx.getBean("personServiceBean"); ctx.close();
注入依赖对象
public class UserBizImpl implements UserBiz { private UserDao userDao; //需要注入的对象 } 方式一 <bean id="userDaoId "class="cn.itcast.service.OrderDaoBean"/> <bean id="orderService"class="biz.impl.UserBizImpl"> <propertyname="userDao " ref="userDaoId "/> </bean> //这里使用id定义的userDaoId可以被其他bean使用 方式二(使用内部bean,但该bean不能被其他bean引用) <bean id="orderService"class=" biz.impl.UserBizImpl"> <property name="userDao "> <!--没有id(name)属性,所以不能被其它引用--> <bean class=“dao.impl.userDaoImpl”/> </property> </bean> 方法三 也可以通过构造函数注入和set方法注入 <bean id="userDaoId "class="cn.itcast.service.OrderDaoBean"/> <bean id="orderService"class="cn.itcast.service.OrderServiceBean"> <constructor-arg index=“0” ref=”userDaoId”/>//构造器注入 (构造函数注入,则构造函数的为这个对象赋值,index表示参数的索引,可以省略的) <property name=“userDao” ref=” userDaoId”/>//属性setter方法注入 (使用set方法注入当然得为对象设置set方法) </bean>
集合类型的装配
<bean id="order"class="cn.itcast.service.OrderServiceBean"> <property name="lists"> <list> <value>pkbest</value> </list> </property> <property name="sets"> <set> <value>pkbest</value> </set> </property> <property name="maps"> <map> <entry key=“pkbest" value="28"/> </map> </property> <property name="properties"> <props> <propkey=“pk">best</prop> </props> </property> </bean>
依赖注入--手工装配
第一种方法:同上面依赖注入对象的第三种是一样的
第二种方法:
在java代码中使用@Autowired或@Resource注解方式进行装配。但我们需要在xml配置文件中配置以下信息:
<beansxmlns="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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:annotation-config/> </beans>
这个配置隐式注册了多个对注释进行解析处理的处理器:AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor
注: @Resource注解在spring安装目录的lib\j2ee\common-annotations.jar
在java代码中使用@Autowired或@Resource注解方式进行装配,这两个注解的区别是:@Autowired 默认按类型装配,@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
@Autowired
private PersonDao personDao;//用于字段上
@Autowired
public void setOrderDao(OrderDao orderDao){//用于setter方法上
this.orderDao = orderDao;
}
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它required属性为false。如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。如下:
@Autowired @Qualifier("personDaoBean")
private PersonDao personDao;
@Resource注解和@Autowired一样,也可以标注在字段或setter方法上,但它默认按名称装配。名称可以通过@Resource的name属性指定,如果没有指定name属性,当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象,当注解标注在setter方法上,即默认取bean名称寻找依赖对象。
@Resource(name=“personDaoBean”)
private PersonDao personDao;//用于字段上
注意:如果没有指定name属性,并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配。但一旦指定了name属性,就只能按名称装配了。
通过在classpath自动扫描
前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,如果这些这组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。spring为我们引入了组件自动扫描机制,他可以在类路径底下寻找标注了@Component、@Service、@Controller、@Repository注解的类,并把这些类纳入进spring容器中管理。它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
<beansxmlns="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-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd"> <context:component-scanbase-package="cn.itcast"/> // base-package指定需要扫描的路径 </beans>
其中base-package为需要扫描的包(含子包)。
@Service用于标注业务层组件、 @Controller用于标注控制层组件(如struts中的action)、@Repository用于标注数据访问组件,即DAO组件。而@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
@Scope用于指定scope作用域的(用在类上) @PostConstruct用于指定初始化方法(用在方法上)
@PreDestory用于指定销毁方法(用在方法上)
使用自动扫描以后在beam.xml中的beans中不需要再定义bean了,要使用时路径下的bean对象时只需要通过类名首字母小写获取即可,比如要获取UserBizImpl的对象
UserBiz biz = app.getBean("userBizImpl", UserBiz.class);
如果依赖的注入对象的接口有多个实现类的情况下需要,在@Resource中的name的值也类名首字母小写,如果只有一个实现类的话可以不需要指明@Resource的name属性
@Resource(name=“userDaoImpl”)