[Spring+SpringMVC+Mybatis]框架学习笔记(二):Spring-IOC-DI

上一章:[Spring+SpringMVC+Mybatis]框架学习笔记(一):SpringIOC概述
下一章:[Spring+SpringMVC+Mybatis]框架学习笔记(三):Spring实现JDBC

第2章 Spring-IOC-DI

Spirng的IOC(控制反转)是通过依赖注入(dependency injection)来实现的。

优点:

  • 大量减少了对象的创建和管理,使代码层次更加清晰;
  • Spring的IOC容器是一个轻量级的容器,没有入侵性(不依赖容器的API),不需要实现一些特殊的接口,这是一个合理设计的基本要求;
  • 鼓励我们面向接口编程;
  • 减少代码的耦合,将耦合的部分推到了配置文件中,如果他们的关系发生了改变,只需要修改配置文件;
  • 提供了aop声明式的服务能力。

2.1 基于xml配置文件的注入

基于xml文件配置的注入:

  • 构造函数注入
  • setter方法注入
  • 特定接口注入(用的少,省略)

前两种详见第1章.

2.1.1 常见pojo类属性的注入

pojo类:没有实现任何接口和继承任何父类的简单的java类

1)bean

package com.steven.spring.sysmgr.entity;

import java.util.List;
import java.util.Map;
import java.util.Set;

/**
 * 用于测试pojo对象常见属性的注入
 * @author chenyang
 *
 */
public class User {
	private String strValue;
	private int intValue;
	private List listValue;
	private Set setValue;
	private String[] strArrayValue;
	private Map mapValue;
	public String getStrValue() {
		return strValue;
	}
	public void setStrValue(String strValue) {
		this.strValue = strValue;
	}
	public int getIntValue() {
		return intValue;
	}
	public void setIntValue(int intValue) {
		this.intValue = intValue;
	}
	public List getListValue() {
		return listValue;
	}
	public void setListValue(List listValue) {
		this.listValue = listValue;
	}
	public Set getSetValue() {
		return setValue;
	}
	public void setSetValue(Set setValue) {
		this.setValue = setValue;
	}
	public String[] getStrArrayValue() {
		return strArrayValue;
	}
	public void setStrArrayValue(String[] strArrayValue) {
		this.strArrayValue = strArrayValue;
	}
	public Map getMapValue() {
		return mapValue;
	}
	public void setMapValue(Map mapValue) {
		this.mapValue = mapValue;
	}
}

2)配置文件applicationContext-property.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="user" class="com.steven.spring.sysmgr.entity.User">
		<property name="strValue" value="abc"/>
		<property name="intValue" value="123"/>
		<property name="listValue">
			<list>
				<value>list1</value>
				<value>list2</value>
				<value>list3</value>
			</list>
		</property>
		
		<property name="setValue">
			<set>
				<value>set1</value>
				<value>set2</value>
				<value>set3</value>
			</set>
		</property>
		
		<property name="strArrayValue">
			<list>
				<value>strArray1</value>
				<value>strArray2</value>
				<value>strArray3</value>
			</list>
		</property>
		
		<property name="mapValue">
			<map>
				<entry key="key1" value="map1"></entry>
				<entry key="key2" value="map2"></entry>
				<entry key="key3" value="map3"></entry>
			</map>
		</property>
		
	</bean>
</beans>

3)测试类

package com.steven.spring.sysmgr.action;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.steven.spring.sysmgr.entity.User;
/**
 * 测试bean的属性的注入
 * @author chenyang
 *
 */
public class PropertyDITest {
	@Test
	public void testPropertyDI(){
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-property.xml");
		User user = (User) ac.getBean("user");
		
		System.out.println("strValue : " + user.getStrValue());
		System.out.println("intValue : " + user.getIntValue());
		System.out.println("listValue : " + user.getListValue());
		System.out.println("setValue : " + user.getSetValue());
		System.out.println("strArrayValue : " + user.getStrArrayValue());
		System.out.println("mapValue : " + user.getMapValue());
	}
}

测试结果:

strValue : abc
intValue : 123
listValue : [list1, list2, list3]
setValue : [set1, set2, set3]
strArrayValue : [Ljava.lang.String;@71def8f8
mapValue : {key1=map1, key2=map2, key3=map3}

2.1.2 bean的scope属性

bean的scope属性代表bean对象的作用域,scope=“singleton/prototype”

  • singleton 仅初始化一次,创建一个实例 A a = new A() 相当于单例模式
  • prototype 每次对bean的访问都会重新创建一个新的实例 相当于多例模式

1)配置文件applicationContext-scope.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">
	
	<!-- scope属性表示单例/多例,属性值singleton(默认)/prototype-->
	<bean id="userOther" class="com.steven.spring.sysmgr.entity.UserOther" scope="prototype"></bean>
</beans>

2)测试类(实体类UserOther略)

package com.steven.spring.sysmgr.action;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.steven.spring.sysmgr.entity.UserOther;
/**
 * 测试bean的scope属性
 * @author chenyang
 *
 */
public class ScopeDITest {
	private ApplicationContext ac;
	
	@Before
	public void init(){
		ac = new ClassPathXmlApplicationContext("applicationContext-scope.xml");
	}
	
	@Test
	public void testScopeDI(){
		
		UserOther userOther1 = (UserOther) ac.getBean("userOther");
		UserOther userOther2 = (UserOther) ac.getBean("userOther");
		
		System.out.println(userOther1.toString());
		System.out.println(userOther2.toString());
		System.out.println(userOther1 == userOther2);
		
	}
	
}

2.1.3 bean的延迟加载

  • 在bean标签里面写入lazy-init=”false/true”,当为false时,容器启动时加载;当为true时,启动时不加载,使用时加载;
  • 在bean的头文件里面写入default-lazy-init=”true” 代表整个配置文件的对象都是延迟加载的。

1)配置文件applicationContext-lazyInit.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">
	
	<!-- 延迟加载lazy-init属性,默认值false -->	
	<!-- 这里为了模拟出延迟加载的效果,故意将class值写错 -->	
	<bean id="userAnOther" class="com.steven.spring.sysmgr.entity.UserAnotherError" lazy-init="true"></bean>
</beans>

2)测试类

package com.steven.spring.sysmgr.action;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
 * 测试bean的scope属性
 * @author chenyang
 *
 */
public class LazyInitTest {
	private ApplicationContext ac;
	
	@Before
	public void init(){
		ac = new ClassPathXmlApplicationContext("applicationContext-lazyInit.xml");
	}
	
	@Test
	public void testLazyInit(){
		//若lazy-init为false,这句不会打印,直接报错;若为true,这句打印后报错
		System.out.println("---------容器已启动--------");
		ac.getBean("userAnOther");
		
	}
	
}

2.1.4 bean的自动装配(autowire)

spring可以自动的向bean中注入依赖 ----> 自动装配(autowire),其底层都是通过setter方式注入:

  • byName 定义的依赖的bean名称需要与类中引用的名称一致,就会匹配依赖关系;
  • byType 通过定义的依赖bean 的类型来进行匹配.

ps:建议不要在配置文件里面用自动装配,虽然可以减少配置文件,但是不利于维护。这里讲主要是后面注解部分要用到。

1)服务类UserService和Dao类IUserDao、UserDaoImplJdbc和UserDaoImplOther与第1章中例子中相同,这里略

2)配置文件applicationContext-autowire.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">
	
	<!-- 自动装配autowire,它有两个属性值:byName/byType -->
	
	<bean id="userDao" class="com.steven.spring.sysmgr.dao.impl.UserDaoImplJdbc"></bean>
	
	<bean id="userDaoImplOther" class="com.steven.spring.sysmgr.dao.impl.UserDaoImplOther" autowire-candidate="false"></bean>
	
	<!-- byName: 它是根据类UserService中所依赖的类的引用的名称(userDao),在本配置文件中去寻找对应的bean的id,这里即为userDao -->
	<bean id="userServiceByName" class="com.steven.spring.sysmgr.service.UserService" autowire="byName"></bean>
	
	<!-- byType: 它是根据类UserService中所依赖的类的类型来匹配,只要类型一致即可(包含实现或子类),那么这里就会匹配两个,就会报错,因此就需要在不需匹配的bean标签中加上	autowire-condidate="false",该属性默认值为true	-->
	<bean id="userServiceByType" class="com.steven.spring.sysmgr.service.UserService" autowire="byType"></bean>
</beans>

3) 测试类

package com.steven.spring.sysmgr.action;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.steven.spring.sysmgr.service.UserService;

public class AutoWireTest {
	ApplicationContext ac;
	
	@Before
	public void init(){
		ac = new ClassPathXmlApplicationContext("applicationContext-autowire.xml");
	}
	
	//测试自动装配byName方式
	@Test
	public void testAutoWireByName(){
		UserService userService = (UserService)ac.getBean("userServiceByName");
		userService.loginUser("abc", "123");
	}
	
	//测试自动装配byType方式
	@Test
	public void testAutoWireByType(){
		UserService userService = (UserService)ac.getBean("userServiceByType");
		userService.loginUser("abc", "123");
	}
}

2.2 配置文件的加载方式

2.2.1 单个配置文件

  • 根据类的相对路径加载:
    ApplicationContext ac = new FileSystemXmlApplicationContext("applicationContext.xml");
  • 根据文件的绝对路径加载:
    ApplicationContext ac = new FileSystemXmlApplicationContext("E:\\spring02-IOC-DI\\\src\\\applicationContext.xml");

2.2.2 多个配置文件

1)测试类:

package com.steven.spring.sysmgr.action;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * 测试不同方式加载多个spring配置文件
 * @author chenyang
 *
 */
public class ConfigFilesLoadTest {
	@Test
	public void testConfigFilesLoad(){
		//方式1: 按照单个配置文件逐个加载,此处略
		
		//方式2:罗列或数组
		//罗列的方式
		/*ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml",
				"applicationContext-property.xml",
				"applicationContext-scope.xml");*/
		
		//数组的方式
		/*String[] configFiles = new String[]{
				"applicationContext.xml",
				"applicationContext-property.xml",
				"applicationContext-scope.xml"};
		ApplicationContext ac = new ClassPathXmlApplicationContext(configFiles);*/
		
		//方式3:字符串匹配的方式
		/*ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext*.xml");*/
		
		//方式4:总配置文件的方式
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext-all.xml");
		
	}
}

2)总配置文件applicationContext-all.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">
	
	<import resource="applicationContext.xml"/>
	<import resource="applicationContext-property.xml"/>
	<import resource="applicationContext-scope.xml"/>
</beans>

2.3 基于注解的注入

  • 引入jar包:spring-aop-4.2.1.RELEASE.jar
  • 在xml配置文件里加入约束
  • 在xml配置文件里定义扫描需要用到的包的路径
  • 在需要注解的bean对象前面加入注解标识符
    • @Component("") 通用型
    • @Service("") 一般用于声明服务类
    • @Repository("") 一般用于声明DAO类
    • @Controller("") 一般用于声明控制类(springMVC/struts2 action/controller)
    • @Scope(""singleton/prototype") 声明单例/多例
  • 声明依赖关系
    • @Resource 默认是用byName的方式注入,实在找不到,就用byType方式
    • @Autowired 默认是用byType的方式注入,但如果遇到多个,然后用byName的方式
    • @Qualifier("") 指定需要的对象的名称,需与Autowired配合使用,不能单独使用

注意(面试):

注解方式一般原则上不需要设置setter 方法,但一般都写上。因为当我们通过配置文件的方式进行了部分DI,若没有setter 方法,就会报异常。

这里借用第1章中的例子。

1)配置文件applicationContext.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"
    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">
	
	<context:component-scan base-package="com.steven.spring.sysmanage"></context:component-scan>
</beans>

2)服务类

package com.steven.spring.sysmanage.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.steven.spring.sysmanage.dao.IUserDao;

/**
 * 用于用户登录、增删改查的服务类
 * @author Administrator
 *
 */

//说明当前类是一个组件,相当于在配置文件中加入的对应的bean标签
//@Component("userService")
@Service("userService")
public class UserService {
	@Autowired //默认是用byType的方式注入,但如果遇到多个,然后用byName的方式
	//@Qualifier("userDaoOther") //指定需要的类的名称,需与Autowired配合使用,不能单独使用
	//@Resource	 //默认是用byName的方式注入,实在找不到,就用byType方式
	IUserDao userDao;
	
	public void setUserDao(IUserDao userDao) {
		this.userDao = userDao;
	}
	
	/**
	 * 通过调用与数据库交互的UserDao里面的loginUser方法,判断是否验证成功
	 * @param userName
	 * @param password
	 * @return
	 */
	public boolean loginUser(String userName, String password){
		boolean flag = false;
		flag = userDao.loginUser(userName, password);
		return flag;
	}
	
}

3)DAO类

package com.steven.spring.sysmanage.dao.impl;

import org.springframework.stereotype.Repository;

import com.steven.spring.sysmanage.dao.IUserDao;

//@Component("userDao")
@Repository("userDao")
public class UserDao implements IUserDao {
	public boolean loginUser(String userName, String password) {
		System.out.println("这是通过JDBC进行登录验证的DAO方法");
		return true;
	}

}

package com.steven.spring.sysmanage.dao.impl;

import org.springframework.stereotype.Repository;

import com.steven.spring.sysmanage.dao.IUserDao;

//@Component("userDaoOther")
@Repository("userDaoOther")
public class UserDaoOther implements IUserDao {
	public boolean loginUser(String userName, String password) {
		System.out.println("这是通过其它方式进行登录验证的DAO方法");
		return true;
	}

}

4)测试类

package com.steven.spring.sysmanage.test;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.steven.spring.sysmanage.service.UserService;
/**
 * 测试使用注解的方式实现DI
 * @author Administrator
 *
 */
public class AnnotationTest {
	@Test
	public void testDI(){
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService userService = (UserService) ac.getBean("userService");
		userService.loginUser("abc", "123");
		
	}
	
}

上一章:[Spring+SpringMVC+Mybatis]框架学习笔记(一):SpringIOC概述
下一章:[Spring+SpringMVC+Mybatis]框架学习笔记(三):Spring实现JDBC

posted @ 2019-07-08 15:35  Steven0325  阅读(281)  评论(0编辑  收藏  举报