二、Spring的核心之IoC(Inversion of Control 控制反转)
1、IoC是什么?
回顾:连接池原理中的使用。
注意:我们在连接池原理中是使用构造函数注入的,当然还可以通过提供set方法实现注入。
2、Spring容器
Spring容器简单理解就是用于存放对象的。
3、Spring的IoC入门
3.1、建立一个java工程
3.2、导入Spring的核心jar包
3.3、建立一个XML配置文件
<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">
</beans>
注意:现在xml文件用什么名字无所谓。
关于spring的xml文件没有提示的原因和解决办法,与struts2和hibernate中相同。只是引入的约束前两个是dtd,而spring引入的schema约束。
3.4、资源交给Spring容器管理
<!-- 把持久层和业务层的创建都交给spring来管理
bean元素用于配置程序中可重用的组件。包括持久层,业务层以及表现层动作类
id属性指定的是spring容器中的key
class属性指定的就是类的全名称
spring容器在创建时,会读取配置文件,创建一个容器(map),把id作为key,把class属性创建的对象作为value,存入容器中 在加载配置文件时,会反射创建类对象。调用的默认无参的构造函数
-->
<bean id="userDao" class="cn.itcast.dao.impl.UserDaoForMySQLImpl"></bean> <bean id="userService" class="cn.itcast.service.impl.UserServiceImpl">
<!-- 在service中,注入dao -->
<property name="userDao" ref="userDao"></property>
</bean>
3.5、Spring容器的初始化及资源的获取
public static void main(String[] args) {
/*
* 1.加载配置文件,用于创建spring的容器(map)
* ClassPathXmlApplicationContext:表示配置文件在类路径下
* FileSystemXmlApplicationContext():当配置文件不在类路径下,而是在磁盘上时,使用此类加载。
*/
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
//从容器中获取需要的对象:根据key取value
// IUserDao userDao = (IUserDao) ac.getBean("userDao");
// System.out.println(userDao);
IUserService userService = (IUserService) ac.getBean("userService");
System.out.println(userService);
userService.saveUser();
}
4、Spring中API的体系结构
4.1、核心接口和类
BeanFactory:它使用的是延迟加载思想。当我们需要用bean时,才去创建对象。
ApplicationContext:它继承自BeanFactory接口,还继承了很多其他接口。功能比BeanFactory强大。它用的不是延迟加载思想,而是在加载配置文件时就创建了。(推荐)
FileSystemXmlApplicationContext:配置文件在本地磁盘上,使用该类加载。
ClassPathXmlApplicationContext :配置文件在classpath中,使用此类加载。
5、基于XML的spring的IoC配置
5.1、Spring实例化bean的方式
调用默认的构造方法 (推荐)
<!-- 把持久层和业务层的创建都交给spring来管理
bean元素用于配置程序中可重用的组件。包括持久层,业务层以及表现层动作类
id属性指定的是spring容器中的key
class属性指定的就是类的全名称
spring容器在创建时,会读取配置文件,创建一个容器(map),把id作为key,把class属性创建的对象作为value,存入容器中 需要掌握:在加载配置文件时,会反射创建类对象。调用的默认无参的构造函数。如果没有提供无参构造的情况下,是不能实例化bean对象的
-->
<bean id="userDao" class="cn.itcast.dao.impl.UserDaoForMySQLImpl"></bean> <bean id="userService" class="cn.itcast.service.impl.UserServiceImpl"></bean>
5.2、Bean的作用范围
<!-- Bean的作用范围
scope属性:
取值:
singleton:单例(默认值)
prototype:多例
request:请求范围
session:会话范围
global-session:集群的会话范围。
-->
<bean id="userBean" class="cn.itcast.bean.UserBean1" scope="prototype">
</bean>
5.3、Bean的生命周期方法
/**
* 模拟一个Bean
* @author zhy
*
*/
public class UserBean { //实例化
public UserBean(){
System.out.println("实例化:大爷来也!");
}
//初始化
public void init(){
System.out.println("初始化:大爷准备好了");
}
//销毁
public void destroy(){
System.out.println("销毁:老夫休矣!");
} public void saveUser(){
System.out.println("业务方法:老夫只劫财不劫色");
}
}
<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的生命周期
bean默认情况下是单例的,但是没有执行销毁方法。
bean:出生 活着 死亡
出生:在使用立即加载方式,spring容器一创建,bean对象就已经都创建好了。实例化bean和初始化bean就都执行了。
活着:spring容器还在,bean对象就一直在。
死亡:spring容器如果没了,bean对象也就消亡了。
-->
<bean id="userBean" class="cn.itcast.bean.UserBean" init-method="init" destroy-method="destroy"></bean>
</beans>
注意:当非单例时,初始化方法正常执行,但是销毁方法就不会执行了。
5.4、依赖注入
a、构造器注入:通过传参数
<!-- 第一种方式:是通过构造函数注入
使用construtor-arg
常用属性:
index:指定的是参数出现在构造函数中的索引
type:指定参数在构造函数中的数据类型
name:指定参数在构造函数中的名称 *****
以上三个都是在指定:给谁注入 value:注入基本数据类型或者String类型的值
ref:注入的是其他bean类型的值
以上两个都是在指定:注入的值 当在定义bean时,使用了constructor-arg元素,则在bean元素中有几个constructor-arg,就需要提供对应参数列表的构造函数
-->
<bean id="userBean" class="cn.itcast.bean.UserBean1">
<constructor-arg name="name" value="test" ></constructor-arg>
<constructor-arg name="age" value="18"></constructor-arg>
<constructor-arg name="birthday" ref="now" ></constructor-arg>
</bean>
b、属性注入:推荐
<!-- 第二种方式:是通过属性的set方法注入
需要使用的元素是:property
属性:
name:写的是set方法后面的部分,并且首字母转小写。
ref:注入的其他Bean类型的值
value:注入的是基本类型或者String类型的值
-->
<bean id="userBean2" class="cn.itcast.bean.UserBean2">
<property name="name" value="test" ></property>
<property name="age" value="18"></property>
<property name="birthday" ref="now"></property>
</bean>
c、p名称空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:p="http://www.springframework.org/schema/p"
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">
<!-- 使用p名称空间注入
1、导入P名称空间:xmlns:p="http://www.springframework.org/schema/p"
2、在bean元素中使用p名称空间
p:属性名称。
属性名称指的是set方法后面的部分。
每个属性都有两个赋值方式:
p:属性名称
p:属性名称-ref
-->
<bean id="userBean4" class="cn.itcast.bean.UserBean4" p:name="test" p:age="20" p:birthday-ref="now"></bean> <bean id="now" class="java.util.Date"></bean>
d、SpEL(Spring Expression Lanaguage)
<!-- 使用Spring中EL表达式。SpringEL 目前定位:了解
${now}就是SPring的EL表达式
-->
<bean id="userBean3" class="cn.itcast.bean.UserBean3">
<property name="name" value="test" ></property>
<property name="age" value="18"></property>
<property name="birthday" value="#{now}"></property>
</bean>
e、注入集合数据
<!-- 注入集合数据
如果是对象集合数据,list结构的,可以使用如下三个元素:
array
list
set
如果是map结构的,可以使用如下四个元素
map:
map的子元素是entry
props
props的子元素是prop
只要结构相同,元素可以互换。
-->
<bean id="userBean5" class="cn.itcast.bean.UserBean5">
<!-- 给数组注入值 -->
<property name="myStrs">
<list>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</list>
</property>
<!-- 给List集合注入值 -->
<property name="myList">
<set>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</set>
</property>
<!-- 给Set集合注入值 -->
<property name="mySet">
<array>
<value>AAA</value>
<value>BBB</value>
<value>CCC</value>
</array>
</property>
<!-- 给map注入数据 -->
<property name="myMap">
<props>
<prop key="testA">AAA</prop>
<prop key="testB">BBB</prop>
<prop key="testC">CCC</prop>
</props>
</property>
<!-- 给properties注入数据 -->
<property name="myProps">
<map>
<entry key="AAA">
<value>testA</value>
</entry>
<entry key="BBB" value="testB"></entry>
<entry key="CCC">
<value>testC</value>
</entry>
</map>
</property>
5.5、团队开发:多个Spring配置文件
a、一次加载多个配置文件
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean1.xml","bean2.xml");
//获取bean对象
UserBean1 bean1 = (UserBean1)ac.getBean("userBean1");
bean1.saveUser(); UserBean2 bean2 = (UserBean2)ac.getBean("userBean2");
bean2.saveUser();
}
b、基于主从配置文件
<!-- Spring的多配置文件
引入其他的配置
-->
<import resource="bean1.xml"/>
<import resource="bean2.xml"/>
c、注意事项
同一个配置文件中不能出现bean元素id属性取值相同的:
不同文件中bean元素id属性取值相同时,有加载顺序
使用bean的name属性获取bean对象(了解就行)
6、基于注解的spring的IoC配置
6.1、注解的使用前提
a、引入context的名称空间
b、指定要扫描的包
c、在Bean上面加入@Component注解
@Component("userBean")
//就相当于在配置文件中配置<bean id="userBean" class="cn.itcast.bean.UserBean" />
public class UserBean { @Value(value="test")
private String name; public void saveUser(){
System.out.println("业务方法:老夫只劫财不劫色");
System.out.println(name);
}
}
b、@Component的衍生注解@Controller@Service@Repository
作用:和@Component作用一样,让Spring容器管理(实例化)当前的bean。
属性: value:指定bean的名称。默认值是当前类的简单类名首字母小写
特点:在三成架构中,每个注解对应一层,使语义更加明确。
@Controller:一般用在表现层,比如struts2的action上
@Service:一般用在业务层,比如Service实现
@Repository:一般用在持久层,比如Dao实现
@Repository("userDaoForMySQL")
public class UserDaoForMySQLImpl implements IUserDao { public void saveUser() {
System.out.println("MySQL:保存了用户");
} public void deleteUser() {
System.out.println("MySQL:删除了用户");
} }
@Service("userService")
public class UserServiceImpl implements IUserService { public void saveUser() {
System.out.println("UserServiceImpl的savaUser方法执行了");
userDao.saveUser();
} public void deleteUser() {
userDao.deleteUser();
} }
@Controller("userAction")
public class UserAction { public String saveUser(){
System.out.println("UserAction的savaUser方法执行了");
userService.saveUser();
return null;
}
}
c、@Autowired
作用:自动按类型注入需要的对象。当使用了该注解时,setter就不是必须的了。
属性:
required:是否必须注入成功。默认值是true。
true:必须注入成功,如果出现注入失败,抛出异常。
false:不一定注入成功,不抛异常。
注意事项:
一个Service接口:IBookService
两个Service实现:BookServiceImpl1 BookServiceImpl2
由于@Autowired是自动按类型注入,当使用接口类型时,就看变量的名称,如果变量名称是bookServiceImpl1,则使用BookServiceImp1这个实现类,
如果变量名是bookServiceImpl2,则使用BookServiceImpl2这个实现类。如果没有符合规范的名称(类名首字母小写),则报错。
到底注入哪个实现类:
@Autowried
private BookService bookServiceImpl1;//注入BookServiceImpl1
@Autowried
private BookService bookServiceImpl2;//注入BookServiceImpl2
@Autowried
private BookService bookService;//注入失败
d、@Value
作用:注入基本类型和String。
属性:value:SpEL表达式,要注入的值
e、@Qualifier
作用:要配合@Autowried来一起使用,通过它指定要注入的bean的名称。按类型注入失效了。
属性:value:要注入的bean的名称
f、@Resource
作用:如同@Autowire和@Qualifier,是规范JSR-250中定义的(JCP)。通过指定bean的名称注入对象。
属性: name:要注入的bean的名称
g、@PostConstruct(了解)
作用:用在初始化方法上。生命周期有关
h、@PreDestroy(了解)
作用:用在销毁方法上。生命周期有关
i、@Configuration和@Bean(了解)
作用:@Configuration指定当前类为配置类,用于加载@Bean的定义。@Bean用于定义bean的名称,用法是@Bean(name="beanName")
注意:该类要被设置在注解自动扫描对应的包下。
6.3、Spring中使用单元测试
a、导入jar包:
spring-test-3.2.0.RELEASE.jar
b、设置Junit运行器和Spring配置文件
/**
* Spring整合Junit
* 1、导入junit的jar包。导入spring整合junit的jar包
* 2、要替换运行器(其实就是测试用的main函数),换成spring框架提供的,因为只有spring框架提供的才能支持spring。
* 3、告知新运行器,spring的配置文件所在的位置
* @author zhy
*
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations={"classpath:bean.xml"})
public class UserBeanTest { @Autowired
private UserBean userBean; @Test
public void testSaveUser(){
userBean.saveUser();
}
}