Spring依赖注入
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring依赖注入相关的知识,希望对你有一定的参考价值。
- 具体内容
Spring中对于依赖注入形式实际上有两个名词描述:IOC(控制反转)、DI(依赖注入),这两个的概念本质都是相同的,因为所有对象产生控制都要求通过applicationContext.xml文件实现。 必须启动容器,而后才可以进行该配置文件的内部的加载操作。
- Bean的基本配置
实际上对于反射进行类对象的产生那么最简化的做法就是使用无参构造完成,但是在Spring里面考虑到了大部分人的使用习惯,所以对于构造方法如果该类中的确不想提供有无参构造,则可以使用有参构造的形式完成。
范例:定义一个D ept.java类
package cn.wnh.vo; import java.io.Serializable; public class Dept implements Serializable { private Integer deptno; private String dname; private String loc;
public Dept(Integer deptno, String dname, String loc) { super(); this.deptno = deptno; this.dname = dname; this.loc = loc; } public String getLoc() { return loc; } public void setLoc(String loc) { this.loc = loc; } public Integer getDeptno() { return deptno; } public void setDeptno(Integer deptno) { this.deptno = deptno; } public String getDname() { return dname; } public void setDname(String dname) { this.dname = dname; } @Override public String toString() { return "Dept [deptno=" + deptno + ", dname=" + dname + ", loc=" + loc + "]"; } } |
范例:修改applicationContext.xml文件,进行Dept类对象的定义
<bean id="dept" class="cn.wnh.vo.Dept"> <constructor-arg index="0" value="12"/> <constructor-arg index="1" value="开发部"/> <constructor-arg index="2" value="北京"/> </bean> |
随后当通过"dept"这个id取得了Dept类对象的时候将自动调用构造方法,将以上的参数进行传递控制。
public class TestMessage { ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); IMessageService msgService = ctx.getBean("msg",IMessageService.class) ; @Test public void testDeptConstructor(){ Dept dept=ctx.getBean("dept",Dept.class); Logger.getLogger(TestMessage.class).info(dept); } |
现在可以发现,的确可以直接进行有参构造的调用,但是综合来讲,没有人真的这么去做,因为在使用对象的时候更多的习惯是利用setter方法设置的。
"<properties>"调用的是类中的属性,此时明确描述要为指定的属性设置内容("value"),也就是说会自动调用setter方法。 但是你千万要记住一点,最为强大的操作并不是说这个只能够实例化一个对象,它还可以直接通过配置文件的模式来定义不同类之间的引用关系,例如,现在有一个Emp的程序类,一个雇员属于一个部门,那么最初的实现做法是分别定义部门和雇员的类,而后再明确调用setter方法来设置关系,但是这一切的配置现在都不在需要通过硬编码的方式完成了,只需要通过配置文件即可实现。
public class Emp implements Serializable { private Integer empno; private String ename; private Double sal; private Date hiredate; private Dept dept; //setter与getter略; } |
在applicationContext.xml中配置如下关系:
<bean id="emp" class="cn.wnh.vo.Emp"> <property name="empno" value="1001"/> <property name="ename" value="SMITH"/> <property name="sal" value="8556.00"/ <property name="dept" ref="dept"/> </bean> <bean id="dept" class="cn.wnh.vo.Dept"> <property name="deptno" value="10"/> <property name="dname" value="开发部"/> <property name="loc" value="北京"/> </bean> |
本次的操作是通过Emp找到Dept的关系。
@Test public void testEmpAndDept(){ Emp emp=ctx.getBean("emp",Emp.class); Logger.getLogger(TestMessage.class).info(emp); } |
以后开发者对于所有的对象内容的配置都不再需要通过硬编码的方式完成了,直接通过配置文件就可以轻松的设置类对象彼此的引用关系。
- p命名空间操作
在Spring长期的发展过程之中,同一种功能可能有各种各样的实现。所以来讲对于之前的配置可以通过p命名空间进行完全的简化处理,它的简化的核心本质:只需要通过一行配置就可以解决多行问题。
需要在applicationContext.xml中配置如下文件:
xmlns:p="http://www.springframework.org/schema/p" |
随后如果要进行内容的引用,则采用的形式为"p:普通属性="内容""、"p:普通属性-ref="引用名称"";
<bean id="dept" class="cn.wnh.vo.Dept" p:deptno="10" p:dname="开发部" p:loc="北京"/> <bean id="emp" class="cn.wnh.vo.Emp" p:empno="7369" p:ename="SMITH" p:sal="800.00" p:dept-ref="dept"/> |
这种做法属于更加简单的做法,但是这种重叠的做法在整个Spring的设计过程之中很常见。
- 集合注入
几乎所有的开发必然要使用Array、List、Set、Map等处理操作,这些操作的注入在后面的开发之中会大量的出现,所以Spring对这些数据的注入操作都提供了非常完整的支持;
a、实现普通数组的注入
例如:一个部门可能有多个项目小组,要求保存每一个项目小组的名称信息;
范例:测试list集合:在Dept.java类中增加一个属性:private List<String> groups ;
在applicationContext.xml中增加以下内容:
<property name="groups"> <array value-type="java.lang.String"> <value>ERP项目组</value> <value>CRM项目组</value> </array> </property> |
查看输出:INFO [cn.wnh.test.TestMessage] - Dept [deptno=10, dname=开发部, loc=北京, groups=[ERP项目组, CRM项目组]] |
现在明确表示出在Dept类中存在有一个List集合(在Spring里面把数组和List作为了同等的对待);
测试Map集合,修改Dept.java类,增加Map集合:
private Map<Integer,String> groups ; |
配置applicationContext.xml:
<property name="groups"> <map> <entry key="110" value="ERP项目组"/> <entry key="110" value="ERP项目组2"/> </map> </property> |
那么现在在整个的处理过程之中最大的直观感受就是,Spring帮助用户自己控制对象的转型操作,用户所关心的只是用字符串实现相关的配置操作即可。
在以后进行项目开发的过程之中还会牵扯到一种属性的配置,这个会使用Properties类保存。
范例:使用Properties进行配置
在Dept.java中增加属性:
private Properties groups ; |
配置applicationContext.xml:
<property name="groups"> <props> <prop key="110">ERP项目组</prop> <prop key="120">CRM项目组</prop> </props> </property> |
4、那么在整个的配置之中最为重要的部分就是数组或集合可以引用其它对象。
例如,在开发之中会存在有这样的关系:一个部门有多个雇员,这种情况一定要在Dept类中存放有一个List集合,保存所有的雇员信息。
在Dept.java中增加属性:
private List<Emp> emps ; |
配置applicationContext.xml:
<property name="emps"> <list> <!-- <ref bean="emp"/>描述的是一个内部的Bean,这个Bean只能够被这个Dept类定义使用 --> <bean class="cn.wnh.vo.Emp"> <property name="empno" value="7566"/> <property name="ename" value="ALLEN"/> <property name="sal" value="2450.00"/> </bean> </list> </property> |
查看输出:INFO [cn.wnh.test.TestMessage] - Dept [deptno=10, dname=开发部, loc=北京, emps=[Emp [empno=7566, ename=ALLEN, sal=2450.0, dept=null]]] |
整个的处理操作过程之中,实际上没有必要过于担心,因为实际开发之中,这些配置不会非常的复杂,而且Spring框架的设计的好处是可以帮助用户更简化的进行代码的编写。
5、基于Annotation的依赖注入配置
在之前发现,Spring对于所有类的操作管理实际上都是基于配置文件完成的,那么大家思考一下,假设说现在做一个业务层,再做一个数据层,那么很明显业务层需要去调用数据层,如果都将所有的配置写在了applicationContext.xml文件里面,一个大型的项目会有几百个DAO实现类,也会有几百个业务实现类,那么这个文件就别看了。
所以在Spring里面针对于这些Bean定义以及关系的引用提供了一组Annotation,并且提供了自动注入操作配置。 为了能够更加显示出本配置的优点,所以建议将项目回归到最初状态。
5.1、 定义Dept.java的VO类
public class Dept implements Serializable { private Integer deptno; private String dname; private String loc; //getter,setter略 } |
5.2、 定义一个IDeptDAO的操作接口(习惯,可以不定义),这个接口里面只提供有一个增加数据方法:
package cn.wnh.dao; import cn.wnh.vo.Dept; public interface IDepotDAO { public boolean doCreate(Dept vo); } |
5. 3、 定义业务层的处理类:
package cn.wnh.service; import cn.wnh.vo.Dept; public interface IDeptService { public boolean add(Dept vo); } |
5. 4、 如果此时按照传统的做法,那么需要编写工厂类,而后要通过工厂类取得接口对象实例,最后再调用方法,可是现在有了Spring,那么这一切的对象产生就可以基于上下文扫描包的配置形式,必须在applicationContext.xml文件里面追加上下文扫描配置包:
xmlns:context="http://www.springframework.org/schema/context" http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd" <context:annotation-config/> <context:component-scan base-package="cn.mldn"/> |
5. 5、 随后在整个的Spring里面提供有如下的几种注解(除了名字不一样之外,都一样):
· 【数据层】仓库配置类:@Repository(org.springframework.stereotype.Repository)
· 【业务层】业务配置类:@Service(org.springframework.stereotype.Service)
· 【工具组件】工具类配置:@Component(org.springframework.stereotype.Component
· 【控制层】控制层配置:@Controller(org.springframework.stereotype.Controller)
package cn.wnh.dao.impl; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Repository; import cn.wnh.dao.IDeptDAO; import cn.wnh.vo.Dept; //写上此注解之后就相当于这个bean对象已经在applicationContext.xml文件里面配置上了 // 而它的引用名字就是首字母小写,即:deptDAOImpl @Repository public class DeptDAOImpl implements IDeptDAO { @Override public boolean doCreate(Dept vo) { LoggerFactory.getLogger(DeptDAOImpl.class).info(vo.toString()); return true; } } |
package cn.wnh.service.impl; import javax.annotation.Resource; import org.springframework.stereotype.Service; import cn.wnh.dao.IDeptDAO; import cn.wnh.service.IDeptService; import cn.wnh.vo.Dept; @Service public class DeptSeriverImpl implements IDeptService { @Resource private IDeptDAO deptDAO;//dao对象注入完成 @Override public boolean add(Dept vo) { return this.deptDAO.doCreate(vo); } } |
5. 6、 对代码进行测试,但是测试的话现在依然比较麻烦,需要通过手工创建上下文取得对象。
@Test public void testDeptConstructor(){ Dept vo=new Dept(); vo.setDeptno(12); vo.setDname("开发部"); vo.setLoc("北京"); IDeptService deptService=ctx.getBean("deptServiceImpl", IDeptService.class); Logger.getLogger(TestMessage.class).info(deptService.add(vo)); } |
总结 :现在使用Annotation的处理和在applicationContext.xml文件中的处理是完全一样的。
虽然现在的配置还不足以揭开Spring的完整面纱,但是从整个的开发代码结构来看,避免了复杂的引用处理关系,避免不同层之间的工厂设计的尴尬局面,整个的开发都变得非常的透明。用户要做的事情就是进行核心业务的实现。