Spring IOC/DI
来源:互联网 发布:初音手办淘宝 编辑:程序博客网 时间:2024/05/19 00:40
简介
SpringIOC可以帮我们完成对象的创建,以及给对象的属性赋值。
SpringIOC的设计出发点是最大化降低对象之间的关系(耦合度),提高代码的可重用性和可移植性。
IOC(Inversion of Control)控制反转的简称。以前我们经常用new来创建对象,并用setter方法设置对象与对象之间的依赖关系(例如一个学生对象student和一个老师对象teacher,我们通过student.setTeacher(teacher)绑定老师和学生对象之间的关系),坏处:对象和类(接口)、对象和对象之间太强的耦合关系。而IOC的本质就是存放那些对象创建、赋值放到IOC容器,在IOC容器中进行对象之间的依赖关系注入(DI)。IOC容器负责管理对象之间的依赖管理。
IOC容器获取
new出来的对象依赖于具体类,通过工厂模式获取对象依赖工厂。
实体类:
public interface ICourse{ public abstract void learn();}public class JavaCourse implements ICourse{ @Override public void learn() { System.out.println("学习JAVA课程"); }}public class OracleCourse implements ICourse{ @Override public void learn() { System.out.println("学习Oracle课程"); }}
一、对象的创建放到Spring配置文件(applicatoinContext.xml)中进行。如果需要使用对象,只需要现在Spring配置此对象(SpringIOC容器会帮助我们自动创建对象),然后直接从SpringIOC容器获取.
例如:
applicatoinContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans …> … <bean id="javaCourse" class="org.lzy.iocinstance.JavaCourse"> </bean> <bean id="oracleCourse" class="org.lzy.iocinstance.OracleCourse"> </bean> </beans>
配置后,SpringIOC容器会替我们自动创建这两个对象(根据class值所代表的类型,创建相应的对象,并用id来标识该对象)。以后要使用两个对象时,可以通过ApplicationContext对象的getBean()方法,根据标识符的id,直接从SpringIOC容器获取。
如:
Student.java
public class Student{ // 通过参数给getBean()方法传入不同的id值 来获取不同的课程对象 public void learnCourse() { ApplicationContext context = new ClassPathXmlApplicationContext("applicatoinContext.xml"); ICourse course =(ICourse)context.getBean("oracleCourse"); course.learn(); }}
SpringIOC容器的本质就是一个大工厂,交给Spring去维护,我们需要做就是Spring配置文件进行对象配置,然后通过getBean()方法从SpringIOC容器中获取。
控制反转:“控制”(对象创建)从Student类转移到SpringIOC容器。
依赖注入
DI:将SpringIOC容器中的资源,注入到某些对象之中。
实体类:
public class Teacher{ private String name ; private int age ; //省略setter、getter}public class Course{ private String courseName; //课程名 private int courseHours; //课时 private Teacher teacher; //授课老师 //省略setter、getter public void showInfo() { System.out.println("课程名:"+courseName+"\t课时:"+courseHours+"\t\t授课老师:"+teacher.getName()); }}
applicatoinContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans …> <bean id="teacher" class="org.lzy.dicinstance.Teacher"> <property name="name" value="李四"></property> <property name="age" value="20"></property> </bean> <bean id="course" class="org.lzy.diinstance.Course"> <property name="courseName" value="JAVA"></property> <property name="courseHours" value="100"></property> <property name="teacher" ref="teacher"></property> </bean> </beans>
<bean>
的id值,从而实现多个<bean>
之间相互引用、相互依赖的关系DI:SpringIOC容器不仅通过<property>
的value属性,给String courseName、 int courseHours这些类型的属性赋值,而且还可以通过<property>
的ref属性,给Teacher teacher这种类型对象也赋值。对象的值是通过SpringIOC容器注入进去。即依赖注入.
依赖注入的方式
常见的三种依赖注入方式:setter注入、构造器注入、p命名空间注入
(1)setter注入
本质通过反射机制,调用对象的setter方法,对属性进行赋值操作:
<bean id="course" class="org.lzy.diinstance.Course"> <property name="courseName" value="JAVA"></property> <property name="teacher" ref="teacher"></property> …</bean>
在Course对象中寻找setCourseName()方法,如果存在,则将<property>
中的value的值“java”传入参数中,即调用对象的setter方法进行赋值。如果不存在该方法,则产生异常。
采用value或ref 这种setter方式给属性赋值,需有相应的setter方法。
(2)构造器注入
注意:手工编写有参构造方法后,JVM不在提供默认的无参构造方法,需编写。
applicationContext.xml
<bean id="teacher" class="org.lzy.diinstance.Teacher"> <!—-给构造方法的第0个参数赋值--> <constructor-arg value="zy"></constructor-arg> <!—-给构造方法的第1个参数赋值--> <constructor-arg value="22"></constructor-arg> </bean>
<constructor-arg>
顺序和构造方法中参数的顺序一致。
不一致,可用index或name属性指定
①使用index来指定参数的位置索引
<bean id="teacher" class="org.lzy.diinstance.Teacher"> <!—-通过index属性,指定给构造方法中的第1个参数赋值--> <constructor-arg value="22" index="1"></constructor-arg> <!—-通过index属性,指定给构造方法中的第0个参数赋值--> <constructor-arg value="zy" index="0"></constructor-arg></bean>
②使用name属性来指定参数的属性名
<bean id="teacher" class="org.lzy.diinstance.Teacher"> <!—-通过name属性,指定给构造方法中的“age”参数赋值--> <constructor-arg value="22" name="age"></constructor-arg> <!—-通过name属性,指定给构造方法中的“name”参数赋值--> <constructor-arg value="zy" name="name"></constructor-arg></bean>
如果A类有String str和int num两个属性,并且只有以下两个构造方法,public A(String str){…}和public A(int num) {…},该如何通过构造方法赋值?
例子:
<bean id="a" class="A"> <constructor-arg value="123" ></constructor-arg></bean>
说明:Spring无法知道”123”是String类型还是int类型(Spiring将所有“简单类型”的变量值,都写在value值的双引号中)。
解决方法<constructor-arg>
标签中的name或type属性解决。
①使用name属性来指定参数的属性名。
②使用type属性来指定参数值的类型。如下,
bean id="a" class="A"> <constructor-arg value="123" type="java.lang.String"></constructor-arg></bean>
(3)p命名空间注入
必须在spring配置文件中引入“p命名空间”即xmlns:p=”http://www.springframework.org/schema/p”
例子
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="…" …></beans>
p命名空间注入的特点:直接使用“p”给对象属性赋值,简化配置代码。
<bean id="teacher" class="org.lzy.diinstance.Teacher" p:name="zy" p:age="22"></bean><bean id="course" class="org.lzy.diinstance.Course" p:courseName="JAVA" p:courseHours="680" p:teacher-ref="teacher"> </bean>
注入各种数据类型的属性
Spring提供了不同的标签来实现不同类型参数的注入
(1)使用setter设置注入方式,注入各种类型的属性
①注入“简单类型”的属性值
<bean id="teacher" class="org.lzy.dinstance.Teacher"> <!-- 使用子元素<value>注入 --> <property name="name"> <value type="java.lang.String">lzy</value> </property> <!-- 使用value属性注入 --> <property name="age" value="22"></property></bean>
两种注入参数值方式的区别
<value>
注入<value></value>
)的中间(不加双引号) 写在value的属性值中(必须加双引号) type属性 有(可选) 可以通过type属性指定数据类型 无 参数值包含特殊字符(<, &)时的处理方法 两种处理方法。 一、使用<![CDATA[ ]]>
标记 二、使用XML预定义的实体引用 一种处理方法。即使用XML预定义的实体引用其中,XML预定义的实体引用,如下:
示例:
<bean id="student" class="org.lzy.entity.Student"> <property name="stuName"> <value><![CDATA[张&三]]></value> </property> </bean> 或 <bean id="student" class="org.lzy.entity.Student"> <property name="stuName"> <value>张&三</value> </property> </bean> 或 <bean id="student" class="org.lzy.entity.Student"> <property name="stuName" value="张&三"></property> </bean>
给Student对象的stuName属性赋值为”张&;三”
②输入对象类型的属性值
使用<ref>
属性以外,还可以使用<ref>
子元素:
<bean id="course" class="org.lzy.diinstance.Course"> … <property name="teacher" > <ref bean="teacher"/> </property> </bean>
通过<ref>
子元素的bean属性,来指定需要引用的<bean>
的id值(即需要引用的对象)
特殊情况:如果需要引用的对象仅仅只需要使用一次,则可以使用“内部Bean”的方式来引用(类似于JAVA的“内部类”),如下:
<bean id="course" class="org.lzy.diinstance.Course"> … <property name="teacher" > <bean class="org.lzy.diinstance.Teacher"> <property name="name" value="lzy"></property> <property name="age" value="22" ></property> </bean> </property> </bean>
以上,就是使用“内部Bean”的方式,给Course对象注入了一个Teacher类型的属性。ps:“内部Bean”Teacher类所在的中并没有“id”属性。
③注入集合类型的属性值
对于集合或数组类型的属性,我们可以使用如下标签来注入属性值
<list>
:内层用<value>或<ref>
Set 外层用<set>
:内层用<value>或<ref>
Map 外层用<map>
:中间层用<entry>
;内层中键用<key><value>…</value></key>
,值用<value>…</value>
或<ref>…</ref>
Properties 外层用<props>
;内层中键写在<prop key=”..”>..</prop>
的key值中,值写在<prop>..</prop>
中间。Properties中的键和值通常都是字符串类型。具体示例如下:
AllConnectionType.java 包含各种集合类型的属性
public class AllConnectionType { private List<String> list; private String[] array; private Set<String> set; private Map<String, String> map; private Properties props;//省略setter、getter //输出所有属性值public void showInfo() { System.out.println("List属性:" + this.list); System.out.print("数组属性:"); for (String arr : this.array) { System.out.print(arr+"\t"); } System.out.println("\nSet属性:" + this.set); System.out.println("Map属性:" + this.map); System.out.println("Properties属性:" + this.props); }}
通过配置文件,注入全部的属性值,如下
applicationContext.xml
<bean id="connType" class="org.lanqiao.test.AllConnectionType" > <!-- 注入List类型 --> <property name="list"> <list> <!-- 定义List中的元素 --> <value>苹果</value> <value>橘子</value> </list> </property> <!-- 注入数组类型 --> <property name="array"> <list> <!-- 定义数组中的元素 --> <value>苹果</value> <value>橘子</value> </list> </property> <!-- 注入Set类型 --> <property name="set"> <list> <!-- 定义Set或数组中的元素 --> <value>苹果</value> <value>橘子</value> </list> </property> <!-- 注入Map类型 --> <property name="map"> <map> <!-- 定义Map中的键值对 --> <entry> <key> <value>apple</value> </key> <value>苹果</value> </entry> <entry> <key> <value>orange</value> </key> <value>橘子</value> </entry> </map> </property> <!-- 注入Properties类型 --> <property name="props"> <props> <!-- 定义Properties中的键值对 --> <prop key="apple">苹果</prop> <prop key="orange">橘子</prop> </props> </property> </bean>如果集合的属性值包含对象类型,只需要把<value>改成<ref bean=""/>.
④注入null和空字符串
<value></value>
,即在<value>
标签中不写任何值 null(如Teacher =null) <null>
表示给Course对象的courseName属性赋值为空字符串,给teacher属性赋值为null
<bean id="course" class="org.lzy.diinstance.Course"> <property name="courseName"> <value></value> </property> <property name="teacher" ><null/></property> </bean>
使用“构造器注入”方式,注入各种类型的属性
与 “setter设置注入”方式类似,只需要把上述<list>、<map>、< props >
等标签放入<constructor-age>和</constructor-age>
中间即可。
自动装配
使用IOC/DI后,对象与对象之间的关系是通过配置文件(ref属性)组织在一起,而不再是通过硬编码的方式耦合在一起了。弊端:需要额外编写大量配置文件。为了简化配置,可以使用Mybatis中“约定优于配置”原则。“自动装配”,适用对象类型(引用类型)的属性(即通过ref属性注入的<Bean>与<Bean>
之间的关系),而不适用简单类型(基本类型和String类型)。
applicationContext.xml
setter设值注入
<bean id="teacher" class="org.lzy.diinstance.Teacher" > … </bean> <bean id="course" class="org.lzy.diinstance.Course"> … <property name="courseHours" value="680"></property> <property name="teacher" ref="teacher"></property> </bean>
具体就是通过value为“简单类型”赋值,通过ref为对象类型赋值。而如果事先遵循一定的“约定”,就可以省略id为“course”的<bean>
中,使用<property>
为对象类型(Teacher对象)赋值的过程。
根据属性名自动装配
<bean id="teacher" class="org.lzy.diinstance.Teacher" > … </bean> <bean id="course" class="org.lzy.diinstance.Course" autowire="byName" />
给id=”course”的<bean>
中,加上 autowire=”byName”,就是为了告诉Spring这个<bean>
符合一定的“约定”,可以自动为对象类型的属性(即teacher)赋值。之后,Spring就会自动在其他<bean>
中,寻找id值与属性名“teacher”一致的<bean>
。如果找到,就会将找到的<bean>
注入到teacher属性之中,这就是根据“属性名”自动装配的约定。
通过autowire属性值来指定具体方式
<property>
的ref属性来指定对象之间的依赖关系。 byName 根据属性名自动装配。如果某一个<bean>
的id值,与当前<bean>
的某一个属性名相同,则自动注入;如果没有找到,则什么也不做。(本质是寻找属性名的setter方法) byType 根据属性类型自动装配。如果某一个<bean>
的类型,恰好与当前<bean>
的某一个属性的类型相同,则主动注入;如果有多个 <bean>
的类型都与当前<bean>
的某一个属性的类型相同,则Spring将无法决定注入哪一个<bean>
,就会抛出一个异常;如果没有找到,则什么也不做。 constructor 根据构造器自动装配。与byType类似,区别是它需要使用构造方法。如果Spring没有找到与构造方法参数列表一致的<bean>
,则会抛出异常。根据属性类型自动装配
<bean id="teacher" class="org.lzy.diinstance.Teacher" > … </bean> <bean id="course" class="org.lzy.diinstance.Course" autowire="byType" />
autowire设置为“byType”以后,Spring就会在其他所有<bean>
中,寻找与Course中的teacher属性类型相同的<bean>
(即找Teacher类型的<bean>
),找到之后就会自动注入给teacher属性。
根据构造器自动装配
<bean id="teacher" class="org.lzy.diinstance.Teacher" > … </bean> <bean id="course" class="org.lzy.diinstance.Course" autowire="constructor" />
使用构造器自动装配,必须在Course类中先提供相应的构造方法。比如,本例是想通过构造方法,给Course类中的teacher属性赋值,则就必须在Course类中提供以下构造方法,
Course.java
public class Course{ … private Teacher teacher; public Course(Teacher teacher) { this.teacher = teacher; }…}
说明:
1.如果配置文件中所有的<bean>
都要使用自动装配,则除了在每一个<bean>
中设置autowire属性以外,还可以设置一个全局的“default-autowire”,用于给所有的<bean>
都注册一个默认的自动装配类型。设置方法是在配置文件里,<beans>
的属性中加入“default-autowire”属性,如下,
<beans xmlns="…" xmlns:xsi="…" xmlns:p="…" xsi:schemaLocation="…" default-autowire="byName"> <bean …> …</bean> … </beans>
表示给所有的<bean>
都设置成了“根据属性名自动装配”。当然,设置全局的“default-autowire”以后,还可以在单独的<bean>
中再次设置自己的“autowire”用来覆盖全局设置。
2.我们虽然可以通过自动装配,来减少Spring的配置编码。但是过多的自动装配,会降低程序的可读性。因此,对于大型的项目来说,并不鼓励使用自动装配。
三种自动装配的可读性为:byName>byType>constructor
基于注解形式IoC配置
对于Dao层、Service层、Contoller层中的类,还可以通过注解的形式来实现Spring IoC。
使用注解定义Bean
StudentDaoImpl.java
import org.springframework.stereotype.Component; @Component("studentDao") public class StudentDaoImpl implements IStudentDao { @Override public void addStudent(Student student) { System.out.println("模拟增加学生操作..."); } }
以上通过@Component定义了一个名为studentDao的Bean,@Component(“studentDao”)的作用等价于XML形式的<bean id="studentDao" class="org.lanqiao.dao.StudentDaoImpl" />。
@Component可以作用在DAO层、Service层、Controller层等任一层的类中,范围较广。此外,还可以使用以下3个细化的注解:
使用注解实现自动装配
可以使用@Autowrited注解实现多个Bean之间的自动装配:
@Service("studentService") public class StudentServiceImpl implements IStudentService { //@Autowired标识的属性,默认会按“属性类型”自动装配 @Autowired private IStudentDao studentDao ; public void setStudentDao(IStudentDao studentDao) { this.studentDao = studentDao; } @Override public void addStudent(Student student) { studentDao.addStudent(student); } }
通过@Service标识了一个业务Bean,并且使用Autowired为studentDao属性自动装配。@Autowired默认采用按“属性类型”自动装配,可以通过@Qualifier设置为按“属性名”自动装配,如下:
@Service("studentService") public class StudentServiceImpl implements IStudentService { //指定@Autowired标识的属性,按“属性名”自动装配 @Autowired @Qualifier("studentDao") private IStudentDao studentDao ; … }
@Autowired除了可以对属性标识以外,还可以对setter方法进行标识,作用与对属性标识是相同的,如下:
@Service("studentService") public class StudentServiceImpl implements IStudentService { private IStudentDao studentDao ; @Autowired public void setStudentDao(IStudentDao studentDao) { this.studentDao = studentDao; } … }
扫描注解定义的Bean
使用@Controller、@Service、@Repository、@Component等标识完类(Bean)以后,还需要将这些类所在的包通过component-scan扫描后才能加载到Spring IoC容器之中,如下:
<beans…> <context:component-scan base-package="org.lzy.dao, org.lzy.service"> </context:component-scan> </beans>
通过base-package属性指定需要扫描的基准包是org.lzy.dao和org.lzy.service,之后Spring就会扫描这两个包中的所有类(含子包中的类),将其中用@Service等标识的类加入到SpringIoC容器之中。此处在定义扫描包时用到了context,所以在使用前需要导入context命名空间
使用注解实现事务
@Transactional注解在Spring中配置声明式事务,需导入一下jar包:
并在Sring配置文件中配置数据源、事务管理类,以及添加对注解配置事务的支持:
<beans…> … <!-- 配置数据源 --> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.OracleDriver"/> <property name="url" value="jdbc:oracle:thin:@127.0.0.1:1521:XE"/> <property name="username" value="system"/> <property name="password" value="123"/> <property name="maxActive" value="10"/> <property name="maxIdle" value="5"/> </bean> <!-- 配置事务管理类 --> <bean id="txManager" class="org.springframework.jdbc.datasource .DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!-- 增加对注解配置事务的支持 --> <tx:annotation-driven transaction-manager="txManager" /> </beans>
程序中使用@Transactional来配置事务
@Service("studentService")public class StudentServiceImpl implements IStudentService{ … @Transactional(readOnly=false,propagation=Propagation.REQUIRES_NEW) @Override public void addStudent(Student student) { studentDao.addStudent(student); }}
@Transactional的常用属性:
- Spring模拟(DI,IOC)
- Spring ioc Di概念
- spring IoC与DI
- Spring--IoC和DI
- spring IOC /DI 详解
- spring IOC ,DI原理
- Spring IOC(DI)模拟
- spring--IoC和DI
- Spring 的IoC DI
- Spring IOC/DI 配置
- spring IOC和DI
- 模拟Spring IOC DI
- Spring IoC与DI
- spring di ioc
- Spring IOC DI原理
- Spring IOC DI 简介
- Spring核心 IOC DI
- Spring 核心 Ioc(DI)
- linux环境变量文件补充
- get 方式传递中文
- Java作业
- 使用Mybatis Generator 工具逆向
- 请不要滥用SharedPreference
- Spring IOC/DI
- Codeforces 888D
- HDU1226 超级密码
- 驱动程序无法使用安全套接字层(SSL)加密与 SQL Server 建立安全连接。错误: RSA premaster secret error
- Being a Hero HDU
- 自传--2017.11.10
- 树莓派U盘挂载位置
- pinpoint插件开发之一:牛刀小试,调整gson插件
- 基于数据库的分布式锁实现