一:为什么出现三大框架?
Struts:以前写项目,可以用jsp+servlet做,但是servlet中的代码复杂,且包含大量的jdbc的语句,为了便于以后的维护,出现struts框架,分层结构更加清晰,各层做各层应该做的事情,表现层,业务逻辑层,数据访问层,持久化层;
Hibernate:为了解决对象模型跟关系模型的阻抗问题而产生的,用面向对象的方式去处理在关系数据库中的数据;是一个ORM框架
Spring:没有出现Spring之前,各层之间的访问,需要在调用层中创建被调用层的对象,使用该对象去调用相应的方法,这样层与层之间的耦合性(紧密度)就会加强,日后在修改代码的时候,会牵一发而动全身;出现了Spring,可以自动创建需要被调用的对象以及进行属性注入,也可以维护这些bean(具体的java类)之间的关系;
二:Spring相当于武林盟主,管理着web层,业务逻辑层,dao层,持久化层,除了维护我们自己写的bean,也有它自己定义的bean,也有第三方的bean
三:学习Spring
(1) jars
(2) applicationContext.xml
(3) 对象的使用
第一部分:Spring自动创建bean对象及属性注入
第二部分:Spring维护bean之间的关系
第三部分:IoC(控制反转)跟DI(依赖注入)
第一部分:Spring容器自动创建bean对象及属性注入
四:入门案例
(1)//Spring容器在加载的时候,先去查看applicationContext.xml有没有配置某个bean,如果配置了,就将该bean放进Spring容器中,并实例化,然后装入内存中
<beans>
<bean id=”userService” class=”com.test.UserService”>
//上面的配置就等价于 UserServiceuserService=new UserService(); --àid对应对象名
//class属性指定该bean(UserService这个类)在项目的位置
</bean>
</beans>
如果出现这样的配置
就相当于创建了两个对象
(2)如果UserService这个类中有一个成员属性
那么通过上面的配置文件是给UserService这个类创建对象的;对象创建完之后,得用对象去调用属性,现在得name还没有赋值,现在默认是null;现在要是想给name属性赋值,那么就得有set方法,或者是构造函数,【Spring容器给bean赋值的方式有两种,一种set,一种是通过构造函数,set常用】
更改UserService类
package c.test;
publicclass UserService {
private String name;
publicvoid setName(String name) {
this.name = name;
}
publicvoid testSpring(){
System.out.println("spring给name赋的值"+name);
}
}
(3)UserSerive中有了set方法,(不需要写get方法,但是PO类要写get方法),接着修改配置文件,给name赋值
现在只要加载Spring容器,那么UserService这个对象就有了,不再需要我们new出来,而且name的值也有了,现在就是如何从Spring容器中取出这个对象,并去调用
(4)新建一个Test类
运行Test类
(5) 关于以上案例的几点说明
●(a):在第三步(3)中,使用<value>给成员属性name赋值,但在SSH结合的时候,配置文件如下
<bean id="userService" class="test.service.impl.UserServiceImpl">
<property name="genericDao"ref="userDao" />
</bean>
<bean id="userAction"class="test.action.UserAction">
<property name="userService"ref="userService" />
</bean>
<property>节点下没有<value>属性:原因
这在第二部分讲
● (b):依赖注入(dependencyinjection)的两种方式:setter方法注入(设值注入),构造注入
上面的例子是set注入,也是Spring官方推荐的做法
构造注入举例:(分四种:按照构造函数参数顺序赋值,按类型匹配入参,按索引匹配入参,上面两种方式联合使用)
先看第一种方式:按照构造函数参数顺序赋值的方式,对上面的例子进行两个地方的修改:一个是UserService类,一个是配置文件的修改
UserService类中多增加一个成员属性age;
----à多个构造函数,下面再修该配置文件
属性注入值得顺序要跟构造函数参数的顺序一致;如果顺序不一致,将会报异常;参数类型不匹配--à引出构造注入的第二种方式:按照类型匹配入参:
修改配置文件:将20 跟张三调换顺序
第三种注入方式:按照索引匹配入参:出现在UserService中出现多个带参构造函数;
修改UserService类
修改配置文件
运行结果
现在给school赋值
结果:
第四种注入方式:
联合使用第二种跟第三种方式:出现的情况是:例如:出现多个String类型的属性,以及出现多个重载的构造方法,配置文件修改如下
● (c):上面的Test类是通过ApplicationContext(应用上下文,是个接口)的getBean()方法获取Spring容器中已经创建的bean对象,除了另一种方法:通过BeanFactory获取bean;上面提到Spring容器,Spring提供了两种容器:一种是BeanFactory,一种是ApplicationContext,两个都是接口,第二个是建立在第一个基础之上的;而这两种方式创建出来的bean的生命周期也是不一样的;这部分的在后面再说;
第二部分: Spring容器维护bean的关系
上面的入门案例说的是Spring容器如何帮我们自动创建bean的对象,下面看第二部分:Spring如何去维护这些bean的关系;
(1) 在数据库中,想维护表与表之间的关系,就得通过主外键的关系来维护,在DDL语句中需要指定的;但是维护bean的关系,则不需要,它是Spring容器自己完成的;
(2) 在上面的项目中,增加一个BookService类,另外修改UserService类,增加一个成员属性;
UserService类代码
BookService类代码
配置文件:
Test类代码不变;
结果:
代码说明:
(1) UserService中的bookService属性跟name属性不同,bookService是个对象的引用,所以在配置文件中用ref,而不用value;而且在类中也得给bookService进行set设置
(2) 这些bean都是由Spring容器创建跟维护的,那么Spring容器什么时候加载?Test类中第一句代码:ApplicationContextapplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
其中applicationContext称为Spring容器对象,该对象是通过ClassPath~(“applicationContext.xml”)来创建的,在创建容器对象的同时也创建了配置文件中定义过的bean对象,并装进内存;此时也能维护它们的关系;现在在UserService中要是有BookService类的对象了,之前都是自己new出来的,现在不再需要了,在Spring容器中,它们之间有联系了;
Java中的内存有堆栈之分:
上面就是Spring维护bean之间的关系,通过ref来完成
第三部分:IoC(控制反转)跟DI(依赖注入)
(1) 其实IoC跟DI就是同一个概念,只是后来Spring的设计者觉得叫做DI更好;第二部分的代码中,bean与bean的关系是在applicationContext.xml文件中配置的,它们的关系通过ref来指定,UserService中的bookService要依赖于id=”bookService”这个bean,并通过setBookService()对其进行注入;
(2) 在Test类中,使用UserService对象,完全没有用到new这个关键字,此时创建对象的权利由程序员手中转移到了Spring容器中;控制反转:就是将创建对象以及维护对象之间的关系转换到Spring容器中;
(3) 上面UserService跟BookService都是具体的java类,在实际项目中,它们都应该是接口,它们应该作为一个接口类型的成员属性存在于调用它们的某个实现类中;面向接口编程
(4) 控制反转的好处:实现层与层之间的松散耦合,便于项目的开发及维护
第四部分:BeanFactory跟ApplicationContext
一:
(1):如何验证new ClassPathXmlApplicationContext(“applicationContext.xml”);这句话一执行,对象就被创建?在构造函数中写个打印语句;-à虽然还没有getBean(),但是该bean的对象已经创建(只是在singleton=”true”)的情况下
(2):beanFactory上面的代码只是实例化容器对象,并没有创建bean的对象,当在getBean()的时候才创建,类似延时加载,使用BeanFactory可以节省内存,但是大多数情况下,还是使用ApplicationContext
(3):ApllicationContext获取bean,bean定义时候有个参数singleton=”true”,单例的,创建的是同一个对象;
Test类
运行结果:地址一样,只初始化一次
现在将singleton改为false
运行结果:地址不一样,初始化两次
ApplicationContext获取bean跟BeanFactory获取bean的区别,BeanFactory只有在getBean()的时候才会去创建bean的对象,A~~~是在new Class~~~(“applicationContext.xml”);的时候创建bean对象,且是单例的,但是这是在没有指定lazy-init的情况下;
现在设置lazy-init=“true“,那么A~~~也会跟BeanFactory一样,在getBean的时候才会创建bean的对象;当singlton=”false”的时候,若没有getBean(),也是跟lazy-init=”true”是一样的效果
二:ApplicationContext中的bean的生命周期
(1) newClasspathXmlApplicationContext(“applicationContext.xml”);调用无参构造函数实例化
(2) 调用set()方法,注入值
(3) 一个bean实现了BeanNameAware接口,则可以调用BeanNameAware的setBeanName()方法,获取该实例对象的名字,就是id指定的名字
(4) 一个bean实现BeanFactoryAware接口
(5) 一个bean实现ApplicationContextAware接口
(6) 自己写个后置处理器MyBeanPostProcessor,实现BeanPostProcessor接口,并实现该接口的两个方法;然后在配置文件中定义该bean就行<bean id=””class=””/>
【类似Filter,EncodingFilter要实现Filter接口,并实现其三个方法init(),destroy(),detory(),任何请求都要经过该过滤器;
后置处理器过滤指定的IP地址等
先调用的是该后置处理器的postProcessBeforeInitialization();
它的postProcessAfterInitialization()方法需要在第九步执行
(7):一个bean实现InitializingBean接口,实现afterPropertiesSet()方法;
(8):在xml中的<bean>节点中配置初始化方法<bean init-method=”myInit()”…..>
在bean中自己定义一个myInit()方法
(9):调用后置处理器的postProcessAfterInitialization()方法
(10):使用bean对象
(11):容器关闭,实现DisposableBean接口,实现destory()方法;
(12):在xml文件的<bean>节点下自定义destory-method方法,跟第八步类似
第十一步跟第十二步的代码无法打印出来,原因是,此时的容器以及关闭了,destory中应该是释放资源的代码,虽然没有打印,但是肯定是执行了
三:BeanFactory中bean的生命周期
上面的是ApplicationContext的bean生命周期,下面用BeanFactory获取bean,修改代码
其他代码不修改,执行结果:
少了第五步,第六步跟第九步
第五部分:对bean中不同数据类型的装配方式
一:数组类型-à
新建Valuation类
配置bean
Test类中使用bean
打印结果:
应用-à
在applicationContext.xml文件中给LocalSessionFactoryBean的mappingResources属性注入值,值为对象关系映射文件的文件名
二:List集合注入值
新建一个NameService类
配置bean
修改Valuation类
配置id=”valuation”这个bean
Test使用bean
三:set集合注入值
在Valuation中增加新的成员属性
配置文件中注入值
Test中使用bean
可以看出来,给Set集合注入6个值,但是只出现两个值:原因是:set集合中的元素是无须的,不可重复的,若是有相同元素,则会覆盖,List集合有序且可以重复
打印结果:
四:map集合注入值
Valuation类
配置文件:
Test类:
结果:
五:给Properties属性集合注入值,在配置Hibernate的属性用到
六:继承的配置
新建两个类Son跟Father
Father
配置文件:
Test使用bean
结果:
如果子类要想覆盖父类中某个属性的值,重新定义即可
第六部分:自动装配bean及Spring特殊bean
一:bean的自动装配
有5种:byName,byType,constructor,autodetect,defalut
Default型的跟其他四种不同,它必须依赖<beans>几点的default-autowire指定的值;
1. byName是根据其他bean的id的值来匹配:autowire=”byName”
2. byType是根据其他bean的class的类名来匹配 autowire=”byType”
3. constructor是根据构造函数的方式进行匹配 autowire=”constructor”
4. autodetect是在第二种跟第三种选择一种 autowire=”autodetect”
5. default类型
<beans
Default-autowire=”no”;不启动默认装配
>
二:Spring特殊bean
1. 后置处理器:BeanPostProcessor,在叙述ApplicationContext的bean生命周期时讲过,处于生命周期的第六步跟第九步
2. 配置信息分离(分散配置)PropertyPlaceholderConfigurer;
主要作用:从主xml文件中分离出部分的配置信息,例如数据库的Url,用户名跟密码
好处:无需修改主xml文件,只需要修改属性文件即可
引入属性文件方式
<context:property-placeholderlocation="classpath:位置 " ignore-unresolvable="true" />
在主xml文件中,要使用占位符变量去获取值:${variable}-à对应着properties中的key
在applicationContext.xml中dataSource跟sessionFactory用到