spring复习 -day1:Spring简介 、 Spring容器 、 Spring IOC

Spring创建对象方法

 

创建对象过程:
第一步:添加SpringIOC环境
(1)在WebRoot/WEB-INT/lib文件夹下,引入SpringIOC配置环境的jar包
如图:

(2)在src文件下引入applicationContext.xml文件,如图:


第二步:定义bean(相当于在容器中创建好对象了,只要等着在
注意:该步骤是在applicationContext.xml文件中完成的
 
定义bean有三种方式(以创建一个GregorianCalendar对象为例):
 
(1)创建对象机制:new 类名()
例:
<!--采用new GregorianCalendar(),构造方法-->
<beanid="c1" class="java.util.GregorianCalendar">
</bean>
注:
1、id标识了这个bean
2、class属性的值表示:包名.类名,一定要写完整,因为执行的时候是从class来看new的是什么对象
3、创建这个bean相当于代码:
Calendar c = new GregorianCalendar();
 
(2)创建对象机制:类名.方法()
例:
<!--采用Calendar.getInstance(),静态工厂方法 -->
<beanid="c2" class="java.util.Calendar"
factory-method="getInstance">
</bean>
注:
1、创建这个bean相当于代码:
Calendar c1 =Calendar.getInstance();
 
(3)创建对象机制:对象.方法()
例:
<!--采用c2.getTime(),对象工厂方法 -->
<beanid="date" factory-bean="c2" 
factory-method="getTime">
</bean>
注:
1、创建这个bean相当于代码:
Calendar c2 = date.getTime();
 
第三步:实例化Spring容器
 
注意:该步骤实例化出一个ApplicationContext对象,该对象会实现applicationContext.xml文件,该文件又会将其中的bean都创建出来
 
结果:applicationContext.xml文件中创建了多少bean,在其他java类中你就省写了多少个个new对象的代码!
 
(1)创建一个java类,在与applicationContext.xml文件相同的src下
如图:

(2)在这个TestBean类的main方法中实例化Spring容器
代码为:
publicclass TestBean {
publicstatic void main(String[]args){
//创建spring容器对象:配置文件+容器对象(ApplicationContext)
 
//添加配置文件
Stringconf = "applicationContext.xml";
注:
1、new ClassPathXmlApplicationContext(conf)相当于实例化了spring容器
2、传入配置文件conf就是说ac这个对象是根据配置文件定义,按照定义实例化出来的一个容器对象
 
ApplicationContext ac =new
ClassPathXmlApplicationContext(conf);
}
}
 
第四步:使用bean对象
1、获取bean对象的方法:
Spring容器对象.getBean("id值");
 
2、getBean()方法返回的是一个Object对象,所以需要转型
例:
Calendarc1 = (Calendar)ac.getBean("c1");
 
3、getBean()方法中支持传入两个参数,第二个参数是强转的类型,这样就不用在方法外写了
注:第二个参数应写为:类名.class
例:
Calendarc1 = ac.getBean("c1",Calendar.class);
 
例:对于applicationContext.xml中创建的bean,我们在TestBean类的main方法中获取她,然后使用(就输出)
 
packageorg.tarena.test;
 
importjava.util.Calendar;
importjava.util.Date;
importjava.util.GregorianCalendar;
 
importorg.springframework.context.ApplicationContext;
importorg.springframework.context.support.ClassPathXmlApplicationContext;
 
 
publicclass TestBean {
publicstatic void main(String[]args){
//创建spring容器对象
//添加配置文件
Stringconf = "applicationContext.xml";
//这句代码相当于实例化了spring容器,传入配置文件就是说ac这个对象是根据
//配置文件定义,然后实例化出来的一个容器对象
ApplicationContext ac = newClassPathXmlApplicationContext(conf);
//从spring容器中获取c1
Calendarc1 = ac.getBean("c1",Calendar.class);
//从spring容器中取出c2
Calendarc2 = ac.getBean("c2",Calendar.class);
//使用c1、c2,这里就把她输出来
System.out.println(c1);
System.out.println(c2);
//用对象工厂方法,获取一个date对象,并输出(即输出当前系统时间)
Date date= ac.getBean("date",Date.class);
System.out.println(date);
}
}
执行后程序输出:
//第一个日期
java.util.GregorianCalendar[time=1490187619219,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=4,DAY_OF_MONTH=22,DAY_OF_YEAR=81,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=0,SECOND=19,MILLISECOND=219,ZONE_OFFSET=28800000,DST_OFFSET=0]
//第二个日期
java.util.GregorianCalendar[time=1490187619284,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2017,MONTH=2,WEEK_OF_YEAR=12,WEEK_OF_MONTH=4,DAY_OF_MONTH=22,DAY_OF_YEAR=81,DAY_OF_WEEK=4,DAY_OF_WEEK_IN_MONTH=4,AM_PM=1,HOUR=9,HOUR_OF_DAY=21,MINUTE=0,SECOND=19,MILLISECOND=284,ZONE_OFFSET=28800000,DST_OFFSET=0]
//第三个日期
Wed Mar 2221:00:19 CST 2017

 

 
 

scope="singleton/prototype"属性

知识点1:scope属性
1、该属性写在bean中,用于控制对象创建方式(使用范围)
2、在<bean>元素中使用scope属性控制可以支持singleton或prototype
注:默认值是singleton,就是说在bean中没有写就等同于scope="singleton"
 
知识点2:singleton/prototype的作用
1、<beanscope="singleton">表示该组件在Spring
容器里只有一个bean对象
例:在java类中,多次使用ac.getBean("id");返回的都是同一个对象
 
2、<beanscope="prototype">表示该组件每次
ac.getBean("id");都返回一个新的对象.
 
知识点3:使用场合
1、类的属性定义在方法外,不要使用singleton属性,必须使用prototype属性
解释:考虑这么一个例子:
public class User{
 
private Useruser;
 
public void f1(){
对user操作...
}
}
 
问题就在于--User这个类是单例(singleton)了,那么User这个属性也是单例,而你在用f1方法操作user时,别人也在用f1方法操作user,这时,f1操作的user到底以谁为准呢?
 
2、如下情况就用singleton和prototype都可以了
public class User{
public void f1(){
 
User user =xxx;
 
对user操作...
}
}

解释:因为f1方法操作的对象可以是相同或不同的,所以就不存在上面的问题了。
 
 

init-method属性

总:配置对象的初始环境有两种方式--
 
一、在该对象的类的构造器中写配置初始环境的代码
代码:public 类名(){配置初始环境}
 
二、在该对象的类的其他方法中写配置初始环境的代码
代码:
1、public 返回值类型 方法名 类名(){配置参数环境}
2、在applicationContext.xml文件中,在该对象的bean标签中指定init-method="方法名"
注:如果只是在该类中定义了一个初始化方法,而在Spring容器中,没有用init-method指定该方法的----创建一个容器对象,获取该对象,但初始化方法并没有执行
 
知识点1:init-method属性--指定对象的初始化方法
结合下面的例子,我们可得出该属性的如下特点:
 
1、如果scope="singleton"(单例对象)则,创建ExampleBean对象时
只会调用一次init方法
 
2、如果scope="prototype"则,每创建一个ExampleBean对象就
调用一次init方法 
 
3、我们把代码写在init-method指定的方法、构造器中,这两者
有什么不同呢?
答:使用init-method时,是创建完对象后才调用init方法中写的代码
,使用构造器时,是在创建对象的同时也调用写在里面的代码了
 
一、创建一个ExampleBean类
 
packageorg.tarena.bean;
 
publicclass ExampleBean {
 
//在创建ExampleBean对象的同时,配置初始环境
注:构造方法永远都是一创建对象就会被调用的
publicExampleBean(){
System.out.println("在构造方法中解析其他配置文件");
}
//不用构造方法,在其他方法中配置初始环境
注:该方法需要init-method属性来指定,否则创建该对象后,并不会调用该方法
publicvoid init(){
System.out.println("在自定义方法中解析其他配置文件");
}
publicvoid execute(){
System.out.println("调用execute方法");
}
}
 
二、在applicationContext.xml文件中配置
<?xmlversion="1.0" encoding="UTF-8"?>
<beans......>
 
<beanid="e1" class="org.tarena.bean.ExampleBean"
scope="prototype" init-method="init">
</bean>
 
//注:
1、prototype表明每次用getBean("id")获得的都是不一样的ExampleBean对象
2、init-method="init"使得在创建出一个ExampleBean对象后,会自动调用init方法
 
</beans>
 
三、写个测试类,看看效果
packageorg.tarena.test;
......
publicclass testExampleBean {
publicstatic void main(String[]args){
//声明配置文件
Stringconf = "applicationContext.xml";
//创建Spring容器,同时传入配置文件
//容器中已经帮我们创建好了对象
ApplicationContext ac= 
newClassPathXmlApplicationContext(conf);
//获取e1创建的对象,她是以new()方式创建的ExampleBean对象
ExampleBean e1 =ac.getBean("e1",ExampleBean.class);
  //再次获取一个ExampleBean对象,看取出来的是不是同一个
ExampleBean e2 =ac.getBean("e1",ExampleBean.class);
 
//输出e1/e2的地址,也看看这两个对象是否相等
System.out.println(e1);
System.out.println(e2);
System.out.println(e1==e2);
}
}
点击启动,程序输出:
在构造方法中解析其他配置文件
在自定义方法中解析其他配置文件
在构造方法中解析其他配置文件
在自定义方法中解析其他配置文件
org.tarena.bean.ExampleBean@36527f
org.tarena.bean.ExampleBean@6210fb
false
 
要点:
1、不管什么情况,创建出一个对象,该对象的构造方法就会被调用
 
2、我们自定义的方法在创建对象之后,确实也被调用了
 
3、也测试了以下不写init-method="init"的情况,该情况下程序将输出:
在构造方法中解析其他配置文件
在构造方法中解析其他配置文件
org.tarena.bean.ExampleBean@36527f
org.tarena.bean.ExampleBean@6210fb
false
 

destroy-method属性

前言:该属性用于释放对象占用的资源,虽然不用destroy属性,也有垃圾回收器会来处理,不过最好还是写好,靠垃圾回收器处理有时会出现各种问题。

 
知识点1:destroy-method属性
1、该属性的触发需要满足如下条件:
 
条件一:组件对象为单例模式,即scope="singleton"时才可触发销毁方法,
若scope="prototype",则destroy-method指定的方法永远不会被调用
 
条件二:在释放Spring容器对象时,才会一个个的调用单例的destroy方法,
即,在执行ac.close();时,才会启动destroy-method指定的方法
 
注:要使容器对象ac能调用close方法,需要修改声明:将
ApplicationContext ac = newClassPathXmlApplicationContext(conf);
改为:
AbstractApplicationContext ac = newClassPathXmlApplicationContext(conf);
 
案例:
一、就上篇的ExampleBean类,向其中加入一个方法
packageorg.tarena.bean;
 
publicclass ExampleBean {
//定义一个对象销毁方法(方法名随便写)
public void mydestroy(){
System.out.println("对象资源释放...");
}
//在创建ExampleBean对象的同时,配置初始环境
publicExampleBean(){
System.out.println("在构造方法中解析其他配置文件");
}
//不用构造方法,在其他方法中配置初始环境
publicvoid init(){
System.out.println("在自定义方法中解析其他配置文件");
}
publicvoid execute(){
System.out.println("调用execute方法");
}
}
 
二、在applicationContext.xml文件中指定mydestroy方法为该对象的销毁方法
<?xmlversion="1.0" encoding="UTF-8"?>
<beans......>
 
<beanid="e1" class="org.tarena.bean.ExampleBean"
scope="prototype"init-method="init" 
destroy-method="mydestroy">
 
</beans>
 
注:虽然用destroy-method属性指定了mydestroy为销毁方法,但ExampleBean类还是prototype的(非单例)
结果:我们在前面已经说了,非单例对象永远也执行不了销毁方法
 
三、在测试类中试试
packageorg.tarena.test;
......
importorg.tarena.bean.ExampleBean;
 
publicclass testExampleBean {
publicstatic void main(String[]args){
//声明配置文件
Stringconf = "applicationContext.xml";
//创建Spring容器,同时传入配置文件
注:要关闭容器对象ac,这里必须要用AbstractApplicationContext
AbstractApplicationContext ac= 
newClassPathXmlApplicationContext(conf);
//获取e1创建的对象,她是以new()方式创建的ExampleBean对象
ExampleBean e1 =ac.getBean("e1",ExampleBean.class);
  //再次获取一个ExampleBean对象,看取出来的是不是同一个
ExampleBean e2 =ac.getBean("e1",ExampleBean.class);
//输出e1/e2的地址,也看看这两个对象是否相等
System.out.println(e1);
System.out.println(e2);
System.out.println(e1==e2);
 
//关闭容器对象ac,启动ExampleBean类的销毁方法
ac.close();
}
}
 
执行程序,输出结果为:
 
在构造方法中解析其他配置文件
在自定义方法中解析其他配置文件
在构造方法中解析其他配置文件
在自定义方法中解析其他配置文件
org.tarena.bean.ExampleBean@1963b3e
org.tarena.bean.ExampleBean@b6548
false
......
信息: Destroyingsingletons inorg.springframework.beans.factory.support.DefaultListableBeanFactory@15c07d8:defining beans [c1,c2,date,e1,p1,p2,s1]; root of factoryhierarchy
 
可见,非单例情况下,对象的销毁方法是无法被调用的。
 
将scope="prototype"该为scope="singleton"后,执行程序,输出:
在构造方法中解析其他配置文件
在自定义方法中解析其他配置文件
org.tarena.bean.ExampleBean@1ee2c2c
org.tarena.bean.ExampleBean@1ee2c2c
true
2017-3-2317:09:53org.springframework.context.support.AbstractApplicationContextdoClose
信息:Closingorg.springframework.context.support.ClassPathXmlApplicationContext@19a0c7c:startup date [Thu Mar 23 17:09:52 CST 2017]; root of contexthierarchy
2017-3-2317:09:53org.springframework.beans.factory.support.DefaultSingletonBeanRegistrydestroySingletons
信息:Destroying singletons inorg.springframework.beans.factory.support.DefaultListableBeanFactory@2f0df1:defining beans [c1,c2,date,e1,p1,p2,s1]; root of factoryhierarchy
对象资源释放...  
 //这里调用的正是ExampleBean类中的mydestroy方法
 

lazy-init属性

前言:lazy-init属性有什么用?
答:
1、ApplicationContext实现的默认行为就是在启动时将所有singletonbean提前进行实例化,通常情况下这是件好事,因为这样在配置中的任何错误就会即刻被发现(否则的话可能要花几个小时甚至几天)。
 
2、有时候这种默认处理可能并不是你想要的。如果你不想让一个singletonbean在ApplicationContext实现在初始化时被提前实例化,那么可以将bean设置为延迟实例化。这就要用到lazy-init="true"的设置了
 
知识点1:lazy-init属性
1、该属性用于控制单例对象创建时机(注意了,该属性是用在单例对象上的)
 
2、非单例的对象是在ac.getBean("id")时创建的(注意了,也就是说,用不用该属性都是在ac.getBean("id")时创建的)
 
3、在默认情况下(即,在bean中没有设置lazy-init属性,等同于lazy-init="false")----
当Spring容器创建时,单例对象也会跟着被实例化
 
4、lazy-init="true"属性可以将单例对象的实例化推迟到getBean方法被调用时(即,ac.getBean("id")来获取这个对象了,她才会被实例化)

set信息注入解释:为什么叫set注入?

因为在该形式下,传入的属性值,相当于在该对象中为一个属性赋值,即--对象.setXXX(属性值)
 
知识点:在bean标签的标签体中,使用property标签----
相当于传入了setXX()方法:其中的name代表对象的属性,value代表该属性对应的值
 
注:使用property的这种方式又叫"set信息注入"
 
 
一、我们以一个案例来先说明我们用set注入相当于做了什么事情----
 
//创建一个computer类对象(对应bean中class属性)
computer c = newcomputer();
 
//调用computer对象的set方法,为各个属性赋值
(对应bean下,property标签中的name(属性名)和value(属性值))
c.setCpu("ie7");
c.setHdd("希捷");
c.setMainbord("华硕");
 
二、创建一个java类
packageorg.tarena.bean;
 
publicclass computer {
//这个java类有三个属性
privateString cpu;
privateString hdd;
privateString mainbord;
//显示配置信息
publicvoid show(){
System.out.println("cpu:"+cpu);
System.out.println("hdd:"+hdd);
System.out.println("mainbord:"+mainbord);
}
 
//设置了属性对象的get/set方法,该java类才是一个javabean
publicString getCpu() {
returncpu;
}
publicvoid setCpu(String cpu) {
this.cpu =cpu;
}
publicString getHdd() {
returnhdd;
}
publicvoid setHdd(String hdd) {
this.hdd =hdd;
}
publicString getMainbord() {
returnmainbord;
}
publicvoid setMainbord(String mainbord) {
this.mainbord =mainbord;
}
}
 
三、在配置文件中进行set注入,为computer对象设置属性值
<?xmlversion="1.0" encoding="UTF-8"?>
<beans.......>
 
<beanid="p1"class="org.tarena.bean.computer">
<!-- 信息注入(set注入)-->
 
//设置computer对象的cpu属性的值为:ie7
<propertyname="cpu" value="ie7">
</property>
 
//设置computer对象的hdd属性的值为:希捷
<propertyname="hdd" value="希捷">
</property>
 
//设置computer对象的mainbord属性的值为:华硕
<propertyname="mainbord" value="华硕">
</property>
 
 
</bean>
</beans>
 
四、创建一个测试类,试试效果
packageorg.tarena.test;
。。。。。。
importorg.tarena.bean.computer;
 
publicclass testComputer {
publicstatic void main(String[]args){
//computerc = new computer();
//c.setCpu("ie7");
//c.setHdd("希捷");
//c.setMainbord("华硕");
//注:这么写显然使得几个型号与testComputer这个类的
//耦合度过大,以后要该个型号(比如说该cpu),这时,就要
//到这个java类中该代码
//解决方法:通过Spring容器,我们可以降低几个型号书写与该类中耦合度
//声明配置文件
Stringconf = "applicationContext.xml";
 
//创建一个Spring容器对象,需要将配置文件传入
ApplicationContext ac= 
newClassPathXmlApplicationContext(conf);
 
//从容器中获取computer对象,我们已经用set注入的方式给该对象的各个属性赋值了
computer c= ac.getBean("p1",computer.class);
 
//调用computer对象的show()方法,看是不是真的为属性赋值了
c.show();
}
}
 
五、程序执行后输出:
cpu:ie7
hdd:希捷
mainbord:华硕
 
set注入方式大成功!
 

构造器注入

总:和set注入相同的目的,使用构造器注入也是为了向对象的属性赋值,不过在实际工作中,很少会使用该方法(就是说用的多的还是set注入)

 
知识点1:
1、bean标签的标签体中的constructor-arg标签用于向该对象的构造器中传入参数
 
2、constructor-arg标签中:
index属性--表示第几个参数(从左向右,0表示最左边)
value属性--表示传入的参数值
 
3、例:index="0" value="高通"
表示构造器中,第一个参数的值为:高通
即:phone("高通",){
this.cpu="高通";
this.ram=null;
}
//第二该参数还没有,显然是不行的(会报空指针)
 
 
 
一、创建一个
packageorg.tarena.bean;
 
publicclass phone {
 
//该类有两个属性
privateString cpu;
privateString ram;
//在构造器中设置两个属性的值(传入的两个参数在等式的左边,右边表示该对象的属性)
publicphone(String cpu,String ram){
this.cpu=cpu;
this.ram=ram;
}
 
//调用该方法将显示出该对象的属性值
publicvoid show(){
System.out.println("---手机配置---");
System.out.println("cpu:"+cpu);
System.out.println("ram:"+ram);
}
}
 
二、在Spring容器中配置构造器注入,设置phone对象的各个属性值
<?xmlversion="1.0" encoding="UTF-8"?>
<beans......>
 
//相当于:newphone();
<beanid="p2" class="org.tarena.bean.phone">
 
//相当于:newphone("高通",);
<constructor-argindex="0" value="高通">
</constructor-arg>
 
//相当于:new phone("高通","2G");
<constructor-argindex="1"value="2G">
</constructor-arg>
 
</bean>
</beans>
 
三、在测试类中试试效果
packageorg.tarena.test;
。。。。。。
importorg.tarena.bean.phone;
 
publicclass TestPhone {
publicstatic void main(String[]args){
//声明配置文件
Stringconf = "applicationContext.xml";
//创建一个Spring容器,同时将配置文件传进去
ApplicationContext ac=
newClassPathXmlApplicationContext(conf);
//从容器对象中获取phone,已经通过构造器注入的方式给该对象的各个属性赋值了
phone p =ac.getBean("p2",phone.class);
//调用phone对象中的方法,看看赋值是否成功
p.show();
}
}
 
四、执行程序,输出为:
---手机配置---
cpu:高通
ram:2G
 
 

set注入传入对象

前言:之前我们都是向对象的属性传入一个属性值,Spring解耦合的功能还不是很明显,这次我们之前几篇的继承上用set注入传入对象

 
还需说明:set注入方法本来就可以传入对象
 
零、先说一下做了什么。
还是用property标签,其中的name指定属性,赋值的内容由字符串变为对象后,是用ref="id"来实现的,其中,id是要引入的对象的bean的id
 
一、创建一个
packageorg.tarena.bean;
 
publicclass student {
 
//该student类有两个属性
 
注意:这两个属性都是之前我们创建的类,即student的两个属性:
是之前创建的computer类,该"属性"中又包含三个属性
是之前创建的phone类,该"属性"中又包含两个属性
privatecomputer c;
privatephone p;
//调用student类的show()方法,注意computer类和phone类中也有它们对应的show()方法
publicvoid show(){
c.show();
p.show();
}
 
//设置了属性对应的get/set方法后,该java类才是一个javabean
publiccomputer getC() {
returnc;
}
publicvoid setC(computer c) {
this.c =c;
}
publicphone getP() {
returnp;
}
publicvoid setP(phone p) {
this.p =p;
}
}
 
二、在Spring容器中用set注入为该student对象的属性赋值
<?xmlversion="1.0" encoding="UTF-8"?>
<beans......>
 
//相当于:student s = new student();
<beanid="s1"class="org.tarena.bean.student">
 
<!--通过set注入computer-->
 
//在此处,我们通过ref(表示引用)引入computer对象,将她填到student类的c属性中
 
//name指定了属性c,ref的p1指定了computer对象
<propertyname="c"ref="p1">
</property>
注:computer对应的bean中,id值设为"p1"
 
<!--通过set注入phone -->
 
//同过ref属性引入phone属性,将她填到student类的p属性中
 
//name指定了属性p,ref的p1指定了phone对象
<propertyname="p"ref="p2">
</property>
</bean>
注:phone对应的bean中,id值设为"p2"
 
</beans>
 
三、在测试类中,试试效果
packageorg.tarena.test;
......
importorg.tarena.bean.student;
 
publicclass testStudent {
publicstatic void main(String[]args){
//声明配置文件
Stringconf = "applicationContext.xml";
//创建一个Spring容器对象,同时将配置文件传入
ApplicationContext ac=
newClassPathXmlApplicationContext(conf);
//从容器对象中获取student对象,我们通过set注入为该对象的属性赋值了(只不过这次赋的是对象)
student s= ac.getBean("s1",student.class);
//调用student类的方法
s.show();
}
}
 
四、执行程序,输出结果为:
cpu:ie7
hdd:希捷
mainbord:华硕
---手机配置---
cpu:高通
ram:2G

springIOC的概念

IOC概念:Inversion of Control控制反转或反向控制
 
控制反转:改变了对象获取方式.
 
之前编码----方式采用new构造器方式获取对象;
ioc中采用由容器创建对象之后注入进来使用。
 
只要修改配置就可以改变对象关系
 
A-->B
setB

一张图小结springIOC

 

 

 

posted @ 2017-04-27 23:08  Faron  阅读(286)  评论(0编辑  收藏  举报