1.IOC概述
spring的核心技术就是IOC和aop,先来介绍一下IOC,inversion of control的缩写,即控制反转。那么什么叫控制反转呢?先来了解一下程序耦合的概念。在软件工程中,程序的耦合指的是对象之间的依赖关系,依赖关系越强那么耦合度就越高,那么程序的独立性就很差,维护成本就越大。因此,我们要尽量减低程序之间的耦合。
那么我们怎么减低耦合呢?例如我们通过三层架构模式实现对数据库中学生信息表的增删查改,我们知道控制层会调用service业务逻辑层,而业务逻辑层又会调用持久层,这时候我们的程序耦合度就比较高,因为我们创建一个对象必须依赖例外一个对象,所以我们可以使用工厂模式解耦:即我们在创建对象时并不通过具体的new一个对象,而是先在配置文件中以key=value的形式配置,key即获取bean对象的id,value即对应的完整类名。然后通过一个工厂类加载配置文件,拿到key,value通过反射技术创建对象。这时候我们再需要创建对象只要通过id拿就可以,减低了程序之间的耦合。
代码如下:
bean.property文件:
StudentDao=dao.StudentDao
StudentService=service.StudentService
工厂类:
public class BeanFactory {
private static Properties prop;
//创建bean容器用来存取bean对象
private static Map<String,Object> beans;
static{
beans = new HashMap<String,Object>();
//创建Properties对象
prop = new Properties();
//输入流读取配置文件
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
try {
prop.load(in);
//获取所有的bean
Enumeration<Object> keys = prop.keys();
//遍历keys
while(keys.hasMoreElements()){
String key = keys.nextElement().toString();
String beanPath = prop.getProperty(key);
//通过反射构造对象
Object bean = Class.forName(beanPath).newInstance();
//存到容器中
beans.put(key, bean);
}
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
throw new RuntimeException(e);
}
}
/*使用单例模式进行改造
public static Object getBean(String beanName){
Object bean=null;
String beanPath = prop.getProperty(beanName);
System.out.println(beanPath);
//通过反射构造bean对象
try {
bean = Class.forName(beanPath).newInstance();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return bean;
}*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
}
这时候就可以直接拿bean对象:
package service;
import dao.StudentDao;
import factory.BeanFactory;
public class StudentService {
//直接通过id获取到studentDao对象
private StudentDao studentDao = (StudentDao) BeanFactory.getBean("StudentDao");
public void save(){
studentDao.save();
}
}
理解上面解耦的过程,可以说说IOC了,简单的说IOC就是为了减低程序之间耦合性的,把我们随意创建对象的控制权交给spring来管理,因此叫做控制反转。
2. IOC入门案例
2.1 首先到入核心jar包(这里使用的是spring3.2.5版本):
及日志支持包:
2.2 准备目标类:之前创建该对象都是通过new,待会通过配置直接向IOC容器拿就行了。
package entity;
public class Player {
private String name;
private int age;
public Player(){
System.out.println("player对象创建了");
}
public void init(){
System.out.println("执行了init方法");
}
public void destroy(){
System.out.println("执行了destroy方法");
}
}
2.3 配置XML文件:
- 路径:可以任意,建议放在src下
- 名称:随意
- 添加约束:如果记不住可以收藏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.xsd">
<bean id="Player" class="entity.Player" scope="singleton" init-method="init" destroy-method="destroy"></bean>
</beans>
我们来测试一下
package test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import entity.Player;
import entity.Team;
/*
* 测试spring环境搭建
*/
public class Test {
public static void main(String[] args) {
//获取IOC容器对象
//使用ClassPathXmlApplicationContext创建容器,在创建时就会实例化bean对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
Team team = (Team) ac.getBean("Team");
Player player = (Player) ac.getBean("Player");
System.out.println(team);
System.out.println(player);
}
}
这里说一下spring创建bean对象的几种方式:
第一种:使用bean的无参构造函数创建
<bean id="Team" class="entity.Team"></bean>
<bean id="Player" class="entity.Player"></bean>
第二种:通过普通工厂中的方法创建对象(通过一个类的方法创建另一个类的实例)
<bean id="PlayerFactory" class="factory.PlayerFactory"></bean>
<bean id="Player" factory-bean="PlayerFactory" factory-method="getPlayer"></bean>
第三种:通过普通工厂的静态方法创建对象( 通过一个类的静态方法创建另一个类的实例)
<bean id="Team" class="factory.PlayerFactory" factory-method="getTeam"></bean>
bean对象的作用域:
通过scope属性设置:
singleton:表示bean是单例(默认)
prototype:表示bean是多例
request:表示作用域web请求
session:表示作用域websession会话
global-sesssion:表示作用于集群环境下的session
bean的生命周期
bean对象的生命周期
单例:单例bean跟IOC容器同生共死
多例:使用时创建,长时间不用时有垃圾回收器回收
3. 依赖注入入门案例
依赖注入:把有依赖关系的类放到容器中,解析出这些类的实例,就是依赖注入。spring中提供三种方式依赖注入:通过构造方法,set方法,注解
3.1基于xml通过构造方法或set依赖注入
-
导入jar包,跟上面的包一样
-
准备目标类:用于测试注入基本类型数据和复杂类型数据(集合)
package entity;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
public class Student {
private int id;
private String name;
private Date birthday;
private String[] myArray;
private List<String> myList;
private Map<String,String> myMap;
private Set<String> mySet;
private Properties myProp ;
public Student(int id, String name, Date birthday, String[] myArray,
List<String> myList, Map<String, String> myMap, Set<String> mySet,
Properties myProp) {
super();
this.id = id;
this.name = name;
this.birthday = birthday;
this.myArray = myArray;
this.myList = myList;
this.myMap = myMap;
this.mySet = mySet;
this.myProp = myProp;
}
public void setName(String name) {
this.name = name;
}
public void setId(int id) {
this.id = id;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}
public void setMyArray(String[] myArray) {
this.myArray = myArray;
}
public void setMyList(List<String> myList) {
this.myList = myList;
}
public void setMyMap(Map<String, String> myMap) {
this.myMap = myMap;
}
public void setMySet(Set<String> mySet) {
this.mySet = mySet;
}
public void setMyProp(Properties myProp) {
this.myProp = myProp;
}
@Override
public String toString() {
return "Student [id=" + id + ", name=" + name + ", birthday="
+ birthday + ", myArray=" + Arrays.toString(myArray)
+ ", myList=" + myList + ", myMap=" + myMap + ", mySet="
+ mySet + ", myProp=" + myProp + "]";
}
}
- 配置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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 依赖注入:通过构造函数注入, -->
<!--
<bean id="Student" class="entity.Student">
<constructor-arg name="id" value="1101"></constructor-arg>
<constructor-arg name="name" value="狗娃"></constructor-arg>
<constructor-arg name="birthday" ref="Date"></constructor-arg>
</bean>
-->
<bean name="Date" class="java.util.Date"></bean>
<!-- 依赖注入:通过set方法注入 -->
<bean id="Student" class="entity.Student">
<property name="id" value="1102"></property>
<property name="name" value="狗蛋"></property>
<property name="birthday" ref="Date"></property>
<!-- 复杂类型的注入(集合) -->
<property name="mySet">
<set>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>
<property name="myMap">
<map>
<entry key="a" value="aaa"></entry>
<entry key="b" value="bbb"></entry>
<entry key="c" value="ccc"></entry>
</map>
</property>
</bean>
</beans>
- 测试:
package test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import entity.Student;
/*
* 依赖注入测试类
*/
public class Test {
public static void main(String[] args) {
//获取到IOC容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
Student student = (Student) ac.getBean("Student");
System.out.println(student);
}
}
3.2 基于注解的依赖注入
这里通过模拟对数据库student的增删查改说明
- 导包:在前面包的基础上导入spring-context-3.2.5.RELEASE.jar
- 准备类
实体类:
package entity;
import org.springframework.stereotype.Component;
@Component
public class Student {
private int id;
private String name;
public Student(){
}
}
持久层dao:
package dao;
import org.springframework.stereotype.Repository;
/*
* 持久层
*/
@Repository(value="dao")
public class StudentDao implements IStudentDao{
public void save(){
System.out.println("保存了1111111");
}
}
业务逻辑层service:
@Service
public class StudentService {
//@Autowired
//@Qualifier(value="dao2")
@Resource(name="dao2")
private IStudentDao dao;
public void save(){
dao.save();
}
}
- 通过xml文件开开启注解扫描(需要先导入context约束)
<?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.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 使用IOC注解步骤:
1.导入context命名空间
2.开启注解扫描
3.使用注解
-->
<context:component-scan base-package="service"></context:component-scan>
<context:component-scan base-package="dao"></context:component-scan>
</beans>
- 测试:
public class App {
private static StudentService service;
public static void main(String[] args) {
//创建容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
service = (StudentService) ac.getBean("studentService");
service.save();
}
}
解释一下注解的意思:
业务逻辑层
用于生成对象的注解:
-
Component:实例化当前类,并将对象放入IOC容器中。默认id是第一个字母小写的该类名
下面三个注解与Component注解作用一样,只是为了使三层结构更加清晰 -
Controler:表现层
-
Service:业务逻辑层
-
Repository:持久层
用于注入数据的注解: -
Autowired:自动按照类型注入,在IOC容器中只有唯一的bean对象类型和要注入的对象类型一致时,注入成功。
-
当容器中有多个bean对象匹配时则按照bean的id(key)与要注入的对象变量名匹配
-
Qualifier:与类注入注解Autowired配合使用,可以指定注入bean的id(key)
-
Resource:直接按照bean的id注入,可以独立使用
上面三个注解只能住bean类型的,复杂(集合)类型的只能通过xml配置注入。
- value:用于注入基本类型,String类型。在value属性中可以写spel(sprin中的EL表达式)
用于作用域的注解:
- scope:跟xml配置一样,有singleton(单例),prototype(多例)