Spring 学习 (一)
目录
1.默认构造方法(最常用的初始化bean方式, 必须提供默认构造方法)
什么是Spring
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的Java 开发框架,由Rod Johnson 在其著作Expert One-On-One J2EE Development and Design中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。Spring使用基本的JavaBean来完成以前只可能由EJB完成的事情。然而,Spring的用途不仅限于服务器端的开发。从简单性、可测试性和松耦合的角度而言,任何Java应用都可以从Spring中受益。Spring的核心是控制反转(IoC)和面向切面(AOP)。简单来说,Spring是一个分层的JavaSE/EE full-stack(一站式) 轻量级开源框架。
Spring的优点
Spring 核心之 IoC(控制反转)
首先要分享的是Iteye的开涛这位技术牛人对Spring框架的IOC的理解,写得非常通俗易懂,以下内容全部来自原文,原文地址:http://jinnianshilongnian.iteye.com/blog/1413846
什么是IOC(控制反转)?
Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了),哪些方面反转了”,那我们来深入分析一下:
●谁控制谁,控制什么:传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对 象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)。
●为何是反转,哪些方面反转了:有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了。
用图例说明一下,传统程序设计如图2-1,都是主动去创建相关对象然后再组合起来:
图1-1 传统应用程序示意图
当有了IoC/DI的容器后,在客户端类中不再主动去创建这些对象了,如图2-2所示:
图1-2有IoC/DI容器后程序结构示意图
以下分享其它博客的通俗讲解:
所谓IoC,对于spring框架来说,就是由spring来负责控制对象的生命周期和对象间的关系。这是什么意思呢,举个简单的例子,我们是如何找女朋友的?常见的情况是,我们到处去看哪里有长得漂亮身材又好的mm,然后打听她们的兴趣爱好、qq号、电话号、ip号、iq号………,想办法认识她们,投其所好送其所要,然后嘿嘿……这个过程是复杂深奥的,我们必须自己设计和面对每个环节。传统的程序开发也是如此,在一个对象中,如果要使用另外的对象,就必须得到它(自己new一个,或者从JNDI中查询一个),使用完之后还要将对象销毁(比如Connection等),对象始终会和其他的接口或类藕合起来。
那么IoC是如何做的呢?有点像通过婚介找女朋友,在我和女朋友之间引入了一个第三者:婚姻介绍所。婚介管理了很多男男女女的资料,我可以向婚介提出一个列表,告诉它我想找个什么样的女朋友,比如长得像李嘉欣,身材像林熙雷,唱歌像周杰伦,速度像卡洛斯,技术像齐达内之类的,然后婚介就会按照我们的要求,提供一个mm,我们只需要去和她谈恋爱、结婚就行了。简单明了,如果婚介给我们的人选不符合要求,我们就会抛出异常。整个过程不再由我自己控制,而是有婚介这样一个类似容器的机构来控制。Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。
什么是DI(依赖注入)
IoC的一个重点是在系统运行中,动态的向某个对象提供它所需要的其他对象。这一点是通过DI(Dependency Injection,依赖注入)来实现的。
依赖注入:前提必须有IOC的环境,Spring管理这个类的时候将类的依赖的属性注入(设置)进来。
<!--Spring入门的配置 -->
<bean id="userDao" class="com.ysx.spring.demo1.UserDaoImpl">
</bean>这是正常情况下,将class对应的类交给Spring管理,那DI就是下面这种引入class类中属性的情况
<!--Spring入门的配置 -->
<bean id="userDao" class="com.ysx.spring.demo1.UserDaoImpl">
<property name="name" value="李东"></property>
</bean>当Spring帮我们创建这个UserDaoImpl类的对象时发现这个类中有一个属性name,这叫依赖。通过这里<property>帮我们把属性设置进来这就叫依赖注入也就是DI
依赖:下面代码块中,B类运行需要调用A类对象作为参数,我们就可以说B依赖A
Class A{
}
Class B{
public void xxx(A a){
}
}
IOC和DI区别
IOC:控制反转,将类的对象创建权交给Spring管理
DI:在Spring管理这个类的过程当中将这个类的属性设置进来
Spring工厂类
我们还是通过实例来讲解怎么使用:
首先设置接口
/**
* -用户管理业务层的接口
* @author hp
*
*/
public interface UserDao {
public void save();
}
然后是接口实现类
/**
* -用户管理业务层的接口实现类
* @author hp
*
*/
public class UserDaoImpl implements UserDao {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public void save() {
// TODO Auto-generated method stub
System.out.println("UserService执行了。。。" + name);
}
}
然后我们创建一个配置文件applicationContext.xml(Spring的默认名字,你叫什么都行)
这个 xml 文件的 beans 的 schema
<?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">
</beans>
然后我们在里面写入<bean>标签
<?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">
<!--Spring入门的配置 -->
id 是bean标签的唯一标识名字,自己起的
class 中是要让Spring管理的类的全限定名
property 是DI给类的属性赋值
name 是类中属性的属性名
value 是属性的值
<bean id="userDao" class="com.ysx.spring.demo1.UserDaoImpl">
<property name="name" value="李东"></property>
</bean>
</beans>
最后写一个测试类
public class SpringDemo1 {
@Test
public void demo2() {
//创建Spring的工厂 applicationContext
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
userDao.save();
}
}
我们可以看到工厂类的名字是ApplicationContext
Spring工厂类的结构图
我们可以从图中看到 ApplicationContext 继承 BeanFactory
BeanFactory:老版本的工厂类
在上例中如果工厂类是 BeanFactory 只有调用getBean的时候,才会生成类的实例
ApplicationContext:新版本的工厂类
ApplicationContext工厂类在加载配置文件的时候,就会将Spring管理的类都实例化。(它是BeanFactory的子类,意味着它的功能更加强大)
ApplicationContext有两个实现类
ClassPathXmlApplicationContext :加载类路径下的配置文件
FileSystemXmlApplicationContext :加载文件系统下的配置文件
Bean的相关的配置
<bean>标签的id和name的配置
id : 使用了约束中的唯一约束。里面不能出现特殊字符的。
name : 没有使用约束中的唯一约束(理论上可以出现重复的,但是实际开发不能出现的)。里面可以出现特殊字符。Spring和Struts1框架整合的时候会用到name,平常就用 id就可以了
Bean的生命周期的配置(了解)
init-method :Bean被初始化的时候执行的方法
destroy-method :Bean被销毁的时候执行的方法(Bean是单例创建,工厂关闭)
<!-- Spring生命周期的配置 -->
<bean id="customerDAO" class="com.ysx.spring.demo2.CustomerDAOImpl" scope="singleton"
init-method="setup" destroy-method="destroy">
</bean>
两个里面都直接写class对应类的方法名即可
这里我们还看到有一个scope的属性,这就涉及到我们的下一个知识点也是最重要的一个重点
Bean的作用范围的配置(重点)
scope :Bean的作用范围
范围包括:
- singleton :默认的,Spring会采用单例模式创建这个对象。
- prototype :多例模式。(Struts2和Spring整合一定会用到)
- request :应用在web项目中,Spring创建这个类以后,将这个类存入到request范围中。
- session :应用在web项目中,Spring创建这个类以后,将这个类存入到session范围中。
- globalsession :应用在web项目中,必须在porlet环境下使用。但是如果没有这种环境,相当于session。
porlet环境什么? 就是联合网站免登陆,比如当你登录百度官网,再到其子网站比如百度文库,百度贴吧都不用再进行登录操作,这种就用到了globalsession属性。
Spring实例化bean的三种方式
默认构造方法
静态工厂方法
实例工厂方法
1.默认构造方法(最常用的初始化bean
方式, 必须提供默认构造方法)
public class Person {
private String name;
private Integer age;
public Person() {
System.out.println("这是一个无参构造函数");
}
public Person(String name) {
this.name = name;
System.out.println(name+"参数的构造函数");
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
System.out.println("带有名字="+name+"和年龄="+age+"参数的构造函数");
}
}
bean.xml
<bean id="person" class="com.maple.Person"></bean>
<bean id="personWithParam" class="com.maple.Person">
<constructor-arg name="name" value="枫叶"/>
</bean>
<bean id="personWirhParams" class="com.maple.Person">
<constructor-arg name="name" value="枫叶"/>
<constructor-arg name="age" value="23"/>
</bean>
2.静态工厂初始化bean
当采用静态工厂方法创建bean时,除了需要指定class属性外,还需要通过factory-method
属性来指定创建bean
实例的工厂方法。Spring将调用此方法返回实例对象,就此而言,跟通过普通构造器创建类实例没什么两样。
public class MyBeanFactory {
/**
* 创建实例
* @return
*/
public static UserService createService(){
return new UserServiceImpl();
}
public static UserService createService(String name){
return new UserServiceImpl(name);
}
public static UserService createService(String name,int age){
return new UserServiceImpl(name,age);
}
}
bean.xml
<bean id="userServiceId" class="com.maple.MyBeanFactory" factory-method="createService"></bean>
<bean id="userServiceId" class="com.maple.MyBeanFactory" factory-method="createService">
<constructor-arg name="name" value="枫叶"/>
</bean>
<bean id="userServiceId" class="com.maple.MyBeanFactory" factory-method="createService">
<constructor-arg name="name" value="枫叶"/>
<constructor-arg name="age" value="23"/>
</bean>
3.实例工厂初始化bean
与使用静态工厂方法实例化类似,用来进行实例化的非静态实例工厂方法位于另外一个bean中(即下面的MyBeanFactory .java),容器将调用该bean的工厂方法来创建一个新的bean实例。
为使用此机制,class属性必须为空,而factory-bean 属性必须指定为当前(或其祖先)容器中包含工厂方法的bean的名称,而该工厂bean的工厂方法本身必须通过factory-method属性来设定。
必须先有工厂实例对象,通过实例对象创建对象。提供所有的方法都是“非静态”的。
/**
* 实例工厂,所有方法非静态
*
*/
public class MyBeanFactory {
/**
* 实例化工厂
*/
public UserService createService(){
return new UserServiceImpl();
}
/**
* 实例化工厂1
*/
public static UserService createService(String name){
return new UserServiceImpl(name);
}
/**
* 实例化工厂2
*/
public static UserService createService(String name,int age){
return new UserServiceImpl(name,age);
}
}
bean.xml
<!-- 创建工厂实例 -->
<bean id="myBeanFactoryId" class="com.maple.MyBeanFactory"></bean>
<!-- 获得userservice
* factory-bean 确定工厂实例
* factory-method 确定普通方法
-->
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService"></bean>
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService">
<constructor-arg name="name" value="枫叶"/>
</bean>
<bean id="userServiceId" factory-bean="myBeanFactoryId" factory-method="createService">
<constructor-arg name="name" value="枫叶"/>
<constructor-arg name="age" value="23"/>
</bean>
Spring的属性注入
构造方法的方式的属性注入
Set方法的方式的属性注入
Set方法的属性注入
Set方法设置对象类型的属性
这里的 ref 中的 car2 指向上面的 id为 car2 的<bean>标签然后调用该标签对Car2类对象进行属性注入再返还给下面的ref
P名称空间的属性注入(Spring2.5以后)
通过引入p名称空间完成属性的注入:
写法:
- 普通属性 p:属性名=”值”
- 对象属性 p:属性名-ref=”值”
P名称空间的引入
使用p名称空间
SpEL的属性注入(Spring3.0以后)
SpEL:Spring Expression Language,Spring的表达式语言。
语法 : #{SpEL}
SpEL 字面量:
整数 :#{8}
小数 :#{8.8}
科学计数法:#{1e4}
String :可以使用单引号或者双引号作为字符串的定界符号。
Boolean :#{true}
SpEL引用bean , 属性和方法:引用其他对象:#{car}
引用其他对象的属性:#{car.brand}
调用其它方法 , 还可以链式操作:#{car.toString()}
调用静态方法静态属性:#{T(java.lang.Math).PI}
SpEL支持的运算符号:算术运算符:+,-,*,/,%,^(加号还可以用作字符串连接)
比较运算符:< , > , == , >= , <= , lt , gt , eg , le , ge
逻辑运算符:and , or , not , |
if-else 运算符(类似三目运算符):?:(temary), ?:(Elvis)
正则表达式:#{admin.email matches '[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,4}'}
集合类型属性注入(了解)
当里面是普通类型,就用<value>标签
当里面是对象类型,就用<ref>标签
<!-- Spring的集合属性的注入============================ -->
<!-- 注入数组类型 -->
<bean id="collectionBean" class="com.itheima.spring.demo5.CollectionBean">
<!-- 数组类型 -->
<property name="arrs">
<list>
<value>王东</value>
<value>赵洪</value>
<value>李冠希</value>
</list>
</property>
<!-- 注入list集合 -->
<property name="list">
<list>
<value>李兵</value>
<value>赵如何</value>
<value>邓凤</value>
</list>
</property>
<!-- 注入set集合 -->
<property name="set">
<set>
<value>aaa</value>
<value>bbb</value>
<value>ccc</value>
</set>
</property>
<!-- 注入Map集合 -->
<property name="map">
<map>
<entry key="aaa" value="111"/>
<entry key="bbb" value="222"/>
<entry key="ccc" value="333"/>
</map>
</property>
</bean>
分模块配置(怎么使用多个配置文件)
两种方法:
1.在加载配置文件的时候,加载多个
2.在一个配置文件中引入多个配置文件