Spring第一天——入门与IOC
大致内容
spring基本概念
IOC入门
【17.6.9更新】,如何学习spring?
掌握用法
深入理解
不断实践
反复总结
再次深入理解与实践
一、Spring相关概念
1.概述:
Spring是一个开源框架,Spring是于2003 年兴起的一个轻量级的容器(container)
无论从大小和开销还是来说,都足以称得上是轻量级的框架
由Rod Johnson创建。
简单来说,Spring是一个分层的JavaSE/EEfull-stack(一站式) 轻量级开源框架
可以说,spring带来了复杂JavaEE开发的春天!
【更新】spring是一个解决方案级别的整合框架,主要是用来作解耦
2.核心有两部分:
IOC:控制反转
对象创建不是通过new,而是通过配置交给Spring管理
AOP:面向切面编程
所以AOP的本质是在一系列纵向的控制流程中,把那些相同的子流程提取成一个横向的面
是一种容器,它会管理由他创建的对象,包括生命周期的管理等。
3.一站式框架
在JavaEE三层架构中,每一层都提供了不同的解决方案
web层:SpringMVC
service层:IOC
dao层:jdbcTemplate
对应JavaWeb阶段的三层架构:
4.使用的版本
Spring4.x的版本
5.Spring包的下载方式
http://www.open-open.com/news/view/1eb1613
将文章大致内容提取出来就是:
找到 spring framework
找到All avaible features and modules are described in the Modules section of the reference documentation .
Their maven/gradle coordinates are also described there
这个就是各个spring版本的下载地址: http://repo.spring.io/release/org/springframework/spring 而且很清楚的告诉你下载哪个文件。
(等待maven依赖管理的更新)
二、IOC(Inverse of Control)控制反转
【更新】:面向接口编程 每一层只向外提供接口,各层之间只依赖接口而不依赖具体的实现,用于隐藏具体实现并实现多态的组件
什么被反转了?获得依赖对象的过程被反转了
IOC主要的观点就是借助第三方(也就是IOC容器)实现依赖关系的对象之间的解耦
更多详细的IOC与DI的阐述,请参见:http://www.cnblogs.com/xdp-gacl/p/3707631.html
1.对象创建交给Spring管理
【更新】:应用程序本身并不负责依赖对象的创建和维护,由IOC容器负责创建和维护
(IOC容器初始化的时候会创建一系列的bean,并存在spring的上下文中,ApplicationContext)
2.Ioc管理分为两部分:
【更新,初始化IOC容器的方法(WEB应用中) listen可以通过context-param】 主要是指定 contextConfigLocation
1)Ioc配置文件
2)Ioc注解
(1)==Ioc的底层原理:
主要用到的技术:
xml配置文件
dom4j解析配置文件
工厂设计模式
反射
之前代码: 一个User类: public class User{ public void add(){ ... } }
想调用add方法,需要构建类的对象,再调用:
User user = new User();user.add();
如果想在servlet中使用,需要将上述代码写在servlet中,但如果原来的类名、方法名发生了修改
则servlet也要同步修改,耦合度太高,不方便!
针对这种情况,提出第一阶段的解决方案:
工厂模式,使用此模式进行解耦操作
public class UserService{ public void add(){ ... } } 做法是建立一个工厂类: public class UserFactory{ //提供静态方法返回 public static UserService getService(){ return new UserService(); } } 在servlet中调用可以使用工厂: UserFactory.getService().add(); 这样就相对降低了和类的耦合度 但缺陷是和工厂类又有耦合度(高内聚,低耦合)
IOC的解决的大概过程:
要在servlet得到UserService类的实例
步骤:
第一步:创建xml配置文件,配置需要创建的对象的类;
<bean id="userService" class="cn.UserService">
第二步:创建工厂类,使用dom4j解析配置文件+反射
解析xml得到指定id值得class属性值:类路径
采用反射创建类的对象
得到类对象过程还是通过工厂:Factory.getUserService();
此时类名称发生变化时,只需要变更xml配置文件即可
(2)IOC入门案例:
第一步:
导包:jar包里每个都有三个jar包,分别是jar包、doc、源码
spring核心组件有四个(如图)
分别是:beans core context spEL(表达式的,导expression的包)
再导入支持日志输出的jar包(spring本身并不提供日志jar包)【log4j的使用在后面作补充】
log4j logging 两个jar包
也就是如图六个jar包
第二步:
创建类和方法
例如创建User类和add()方法
package cn.ioc; public class User { public void add(){ System.out.println("spring_ioc_add"); } }
第三步:
创建spring配置文件,配置创建的类
前面两个框架的名称和位置固定,而spring不是,但一般都建议放在src下,【更新】:一般放在与src同级的 source folder 下。
建议名字为 applicationContext.xml
自定义命名请参考:http://huihai.iteye.com/blog/1019281
但完全可以自定义名字(位置建议src!)
在web.xml中指定spring路径:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/beans.xml</param-value>
</context-param>
比如这里创建一个 bean1.xml
创建完xml文件,首先是引入约束(这里是schema约束!)
进入包里的docs->spring framework reference->html->找最后一个文件 (见图!)
【更新】:现在由 springIDE来做这件事
引入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-4.0.xsd"> <!--ioc入门--> <bean id="user" class="cn.ioc.User"></bean> </beans>
注意:id建议与类同名,首字母改为小写(这也是spring的默认命名策略)
第四步:
简单测试
步骤:
加载核心配置文件
得到对象
具体不再赘述,见testIoC
package cn.ioc; import org.junit.Test; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class TestIoC { //只是测试这样写,实际开发并不这样操作 @Test public void test(){ //加载spring核心配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml"); //得到配置创建的对象 User user =(User)context.getBean("user"); user.add(); } }
//配置文件没有提示的问题(无法上网,类比之前dtd引入,但稍有区别)此处不再赘述,全部在另外一篇同分类引入约束的博客介绍
(3)spring的bean管理(基于配置文件,day02注解方式)
==bean实例化的三种方式:(常用一种方式)
1)使用类的无参构造器(常用)
上面的入门案例
<bean id="user01" class="cn.ioc.User"></bean>
会自动找无参的构造器
2)使用静态工厂创建
在类里使用静态方法返回实例对象
<bean id="user02" class="cn.factory.BeanFactory" factory-method="getBean"></bean>
依旧使用context.getBean()方法得到bean
3)使用实例工厂创建
创建一个不是静态的方法返回类的对象,使用此方式先要创建工厂的对象
<bean id="bean3Factory" class="cn.factory.Bean3Factory"></bean>
<bean id="user03" factory-bean="bean3Factory" factory-method="getBean"></bean>
2、3仅作了解,实际开发用的一般是方式1
==bean标签常用属性的介绍:
id : 表示起的名称(一般不要包含特殊符号) ,可以根据id值得到配置的对象
class : 要创建对象所在类的全路径
name : (一般已经不用了) 功能和id类似,也可以得到bean对象(id值不能得到特殊符号),
属于遗留问题,为了整合原来的struts1的
scope : bean的作用范围:一般掌握两个值:
singleton:单例 (默认)
prototype:多例(应用于struts2的多实例的action)
可以分别拿到两个对象比较对象地址
(4)属性注入:创建对象的时候,也可以向类的属性设置值
属性注入的方式:支持级联属性的注入(但是必须初始化(不会像struts2一样自动创建,当然,很多时候我们都用不着))
原始方式:
有参构造注入
set()方法注入【用的最多】
接口注入(用的少)
spring只支持前两种方式的注入:
spring有参构造注入:
建立类
package cn.propertiry;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class PropertiryDemo01 {
private String username;
public PropertiryDemo01(String username) {
this.username = username;
}
public void print(){
System.out.println("有参构造属性构造:"+username);
}
}
配置文件修改:
<!-- 使用有参构造器属性注入 -->
<bean id="prop01" class="cn.propertiry.PropertiryDemo01">
<constructor-arg name="username" value="jiangbei"></constructor-arg>
</bean>
(不出现name属性时,把约束后面的spring-beans-4.0补上)
<!-- 有参构造注入 -->
<bean id="id" class="class">
<!-- name必须和构造器的参数名一致,ref为需要注入的引用 -->
<constructor-arg name="" ref=""/>
</bean>
spring的set方式注入【最常用】:
依旧是:创建类 配置文件配置 简单测试
package cn.propertiry;
public class Book {
private String BookName;//属性
//set()方法
public void setBookName(String bookName) {
BookName = bookName;
}
public void print(){
System.out.println("set属性注入:"+BookName);
}
}
修改配置文件
<!-- set()方法属性注入 -->
<bean id="book" class="cn.propertiry.Book">
<property name="BookName" value="从入门到放弃"></property>
</bean>
【实用】真正开发中不止是注入字符串,更多的是对象的注入等!
例如service中依赖dao,需要dao的属性,可以进行属性注入(只是注入的是对象!)
将new的过程交给spring实现!
先建立两个类 Service Dao
package cn.propertiry;
public class Service {
//定义dao成员变量
private Dao dao;
public void setDao(Dao dao) {
this.dao = dao;
}
public void add(){
//service中要调用dao的add()方法需要得到其对象
dao.add();
}
}
package cn.propertiry;
public class Dao {
public void add(){
System.out.println("dao.add");
}
}
在Service中依赖dao对象,定义变量,生成set()方法
进行配置(ref):
<!-- 属性注入,注入对象 -->
<bean id="dao" class="cn.propertiry.Dao"></bean>
<!-- 注入dao对象 -->
<bean id="service" class="cn.propertiry.Service">
<!-- name为成员变量名,ref为dao的bean的id -->
<property name="dao" ref="dao"></property>
</bean>
当然是可以通过多行注入多个属性的。
名称空间注入:(也可以实现属性注入)只引出概念,不作演示赘述(用的太少)
===spring注入复杂数据(会用):
例如注入list 注入数组 注入map 注入propertites
都在案例:Prop类里
定义成员
生成set
package cn.propertiry;
import java.util.List;
import java.util.Map;
import java.util.Properties;
public class Prop {
//注入复杂数据类型
private String[] strs;
private List<String> list;
private Map<String,String> map;
private Properties props;
public void setStrs(String[] strs) {
this.strs = strs;
}
public void setList(List<String> list) {
this.list = list;
}
public void setMap(Map<String, String> map) {
this.map = map;
}
public void setProps(Properties props) {
this.props = props;
}
public void print2(){
System.out.println("array:"+strs);
System.out.println("list:"+list);
System.out.println("map:"+map);
System.out.println("props:"+props);
}
}
配置注入(所有的注入都必须先有对象(相关数据)生成)
如果是配置引用,可以使用 value-ref等(按alt /即可见名知意)
也可以配置独立的bean(导入util的命名空间),以供多出引用
<!-- 注入复杂属性 -->
<bean id="p" class="cn.propertiry.Prop">
<!-- 进行属性注入 -->
<!-- 数组 -->
<property name="strs">
<list>
<value>sx</value>
<value>sy</value>
<value>sz</value>
</list>
</property>
<!-- list -->
<property name="list">
<list>
<value>lx</value>
<value>ly</value>
<value>lz</value>
</list>
</property>
<!-- map -->
<property name="map">
<map>
<entry key="mx" value="mvx"></entry>
<entry key="my" value="mvy"></entry>
<entry key="mz" value="mvz"></entry>
</map>
</property>
<!-- props -->
<property name="props">
<props>
<!-- props的value值写在标签里 -->
<prop key="px">pvx</prop>
<prop key="py">pvy</prop>
<prop key="pz">pvz</prop>
</props>
</property>
</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-4.0.xsd">
<!-- bean definitions here ioc入门-->
<bean id="user" class="cn.ioc.User"></bean>
<!-- 使用有参构造器属性注入 -->
<bean id="prop01" class="cn.propertiry.PropertiryDemo01">
<constructor-arg name="username" value="jiangbei"></constructor-arg>
</bean>
<!-- set()方法属性注入 -->
<bean id="book" class="cn.propertiry.Book">
<property name="BookName" value="从入门到放弃"></property>
</bean>
<!-- 属性注入,注入对象 -->
<bean id="dao" class="cn.propertiry.Dao"></bean>
<!-- 注入dao对象 -->
<bean id="service" class="cn.propertiry.Service">
<!-- name为成员变量名,ref为dao的bean的id -->
<property name="dao" ref="dao"></property>
</bean>
<!-- 注入复杂属性 -->
<bean id="p" class="cn.propertiry.Prop">
<!-- 进行属性注入 -->
<!-- 数组 -->
<property name="strs">
<list>
<value>sx</value>
<value>sy</value>
<value>sz</value>
</list>
</property>
<!-- list -->
<property name="list">
<list>
<value>lx</value>
<value>ly</value>
<value>lz</value>
</list>
</property>
<!-- map -->
<property name="map">
<map>
<entry key="mx" value="mvx"></entry>
<entry key="my" value="mvy"></entry>
<entry key="mz" value="mvz"></entry>
</map>
</property>
<!-- props -->
<property name="props">
<props>
<!-- props的value值写在标签里 -->
<prop key="px">pvx</prop>
<prop key="py">pvy</prop>
<prop key="pz">pvz</prop>
</props>
</property>
</bean>
</beans>
【更新】:使用外部属性配置文件(场景就是读取数据源等信息)
之前可以使用此种形式:缺点很明显,如果需要修改数据库的配置信息,将比较复杂,需要拿出spring的配置文件
<!-- 数据库连接池 (此时使用的是dbcp,效率不高,仅作演示使用)-->
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxActive" value="10" />
<property name="maxIdle" value="5" />
</bean>
我们将这些属性抽取到db.properties:(一般而言,我们都是给Key这里起类似 jdbc.username这样的以.分割的形式,因为有时候直接写username非常容易和其他的(如系统的用户名)相冲突)
jdbc.user=root
jdbc.password=1230
jdbc.driverClass=com.mysql.jdbc.Driver
jdbc.jdbcUrl=jdbc:mysql:///test
jdbc.initPoolSize=5
jdbc.maxPoolSize=10
我们加入context的命名空间(使用springIDE,更简单,更强大!),在配置文件中进行属性文件位置的配置:
<!-- 导入属性文件 -->
<context:property-placeholder location="classpath:db.properties"/>
如何在配置文件中取值我们之前已经提过,使用类似EL的形式:${val}
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<property name="maxActive" value="10"/>
<property name="maxIdle" value="5"/>
</bean>
//当然,这些配置都可以通过编码方式来代替,实现真正的全注解无配置开发
【更新】 spring的SpEL:为配置文件动态赋值提供了可能
使用#{...},可以动态引用其它对象,动态引用其它对象的属性值(字面值这里不做阐述)
value="#{car.price}",注解形式:
@Value("#{testConstant.STR}")
private String str;
并且还支持一些基本的运算,以及三目判断运算符等其他的待补充
【更新】 spring容器的生命周期方法:init-method 和 destroy-method
重要概念:
IoC和DI的区别:
IoC:控制反转,将对象的创建交给spring管理
DI:依赖注入,向类的属性中设置值
DI需要在IoC的基础上进行操作(需要在对象创建的基础上进行)
【更新】:DI是IOC的一种更简单的诠释,也就是说:通过引入IOC容器,利用依赖注入的方式,实现对象之间的解耦
【更新】bean的作用域(与struts2整合时使用到)
prototype(每次请求都创建新的实例,使用hashCode判断) singleton request session
bean的生命周期
定义
初始化
(比较简便的配置是bean中配置init-method destroy-method的配置)
当然不使用配置的时候可以通过bean实现 initlizaingBean disposiableBean两个接口,覆盖方法
当然,也可以配置全局的初始化和销毁方法(就近原则 全局的最后执行)
使用
销毁
第一天的最后引出整合的一些基本概述(后面day会做补充)
===spring整合web项目
Hibernate时有一个遗留问题:sessionFactory的创建会比较慢,可以交给服务器来创建
还有上面的bean1.xml每次都要加载spring核心配置文件,会影响性能
以上的类似问题,解决的实现思想都是:
把加载配置文件和创建对象在服务器启动时就创建,把压力给服务器
spring中封装了相关的处理类,这里简单介绍原理:
web阶段中有一个与天地同寿的对象 ServletContext
它的创建可以由监听器进行监听
在服务器启动的时候,服务器会为每个项目创建一个独一无二的对象:ServletContext
可以使用监听器对ServletContext进行监听,可以知道对象的创建时间
于是可以在监听器监听到ServletContext创建后
加载spring配置文件,把配置文件中配置的对象进行创建
对象创建后将创建的对象放在ServletContext中(它也是一个域对象)
域对象的存取数据直接使用get/setAttribute()即可