Spring学习总结(一)
Spring
Spring基本功能详解:
Spring的控制反转:把对象的创建、初始化、销毁等工作交给spring容器来做。
由spring容器控制对象的生命周期。
步骤:
启动spring容器
在类路径下寻找配置文件来实例化容器
ApplicationContext ctx = new ClassPathXmlApplicationContext(new String[]{"beans.xml"});可以在整个类路径中寻找xml文件
* 通过这种方式加载。需要将spring的配置文件放到当前项目的classpath路径下
* classpath路径指的是当前项目的src目录,该目录是java源文件的存放位置。
在文件系统路径下寻找配置文件来实例化容器
ApplicationContext ctx = new FileSystemXmlApplicationContext(new String[]{“d:\\beans.xml“});Spring的配置文件可以指定多个,可以通过String数组传入。
注:经常用第一种方法启动容器
从spring容器中提取对象。
别名:
<beans>
<alias name="person" alias="p"/>
<bean name="person" class="cn.itcast.aliasspring.Person"/>
</beans>
通过这样的配置可以达到在一个地方命名,在多个不同的地方使用不同的名字的效果。
Spring创建对象的方式:
1.无参构造函数
<bean id="personService" class="cn.itheima.bean.PersonServiceImpl"/>
2.静态工厂:
<bean id="personService" class ="cn.itcast.factory.PersonServiceFactory"
factory-method="createPersonService"/>
在类中提供createPersonService方法即可。
3.实例工厂:
关于对象的scope:
1.singleton(默认值)
在每个Spring IoC容器中一个bean定义只有一个对象实例(共享)(单例)。
默认情况下会在容器启动时初始化bean,但我们可以指定Bean节点的lazy-init=“true”来延迟初始化bean,
这时候,只有第一次获取bean会才初始化bean。
如:
<bean id="xxx" class="cn.itcast.OrderServiceBean" lazy-init="true"/>
如果想对所有bean都应用延迟初始化,可以在根节点beans设置default-lazy-init=“true“,
如下:
<beans default-lazy-init="true“ ...>
2.prototype
允许bean可以被多次实例化(使用一次就创建一个实例) . Spring不能对一个prototype bean的整个生命周期负责.
这就意味着清楚prototype作用域的对象并释放任何prototype bean所持有的昂贵资源都是客户端的责任。
3.Request
在一次HTTP请求中,一个bean定义对应一个实例:即每次HTTP请求将会有各自bean实例,他们依据某个bean定义创建而成。
该作用域仅在基于Web的Spring applicationContext情形下有效。
4.session
在一次HTTPsession中,一个bean定义对应一个实例:即每次HTTP请求将会有各自bean实例,他们依据某个bean定义创建而成。
该作用域仅在基于Web的Spring applicationContext情形下有效。
5.Global session
在一个全局HTTP session中,一个bean定义对应一个实例:即每次HTTP请求将会有各自bean实例,他们依据某个bean定义创建而成。
该作用域仅在基于Web的Spring applicationContext情形下有效。
spring初始化bean的时机:
Spring默认在启动时将所有singleton bean提前进行实例化。提前实例化意味着作为初始化的一部分,ApplicationContext会自动创建并配置所有的singleton bean.通常情况下这是件好事。因为这样在配置中有任何错误能立即发现。
Lazy-init=”true or false”
Lazy-init 为false,spring容器将在启动的时候报错(比较好的一种方式)
Lazy-init 为true,spring容器将在调用该类的时候出错。
bean的init、destroy(初始化和销毁)
Spring初始化bean或销毁bean时,有时需要作一些处理工作,因此spring可以在创建和拆卸bean的时候调用bean的两个生命周期方法。
<bean id=“foo” class=“...Foo”
init-method=“setup”
destory-method=“teardown”/>
当foo被载入到Spring容器中时调用init-method方法。当foo从容器中删除时调用destory-method(scope = singleton有效)。
Spring的DI(依赖注入)
xml形式:
使用构造器注入:
使用xml的注入方式:
通过参数的顺序
<constructor-arg index="0">
<value>张三</value>
</constructor-arg>
<constructor-arg index="1">
<value>56</value>
</constructor-arg>
通过参数的类型
<constructor-arg type="java.lang.Integer">
<value>56</value>
</constructor-arg>
<constructor-arg type="java.lang.String">
<value>张三</value>
</constructor-arg>
使用属性setting方法进行注入:
使用xml的注入方式:
简单Bean的注入
简单Bean包括两种类型:包装类型和String
<bean id="personService" class="com.itcast.bean.impl.PersonServiceImpl">
<!-- 基本类型,string类型 -->
<property name="age" value="20"></property>
<property name="name" value="张无忌"></property>
</bean>
引用其他Bean
<bean id="person" class="com.itcast.bean.Person" />
<bean id="personService" class="com.itcast.bean.impl.PersonServiceImpl">
<property name="person" ref="person" />
</bean>
装配list集合
<property name="lists">
<list>
<value>list1</value>
<value>list2</value>
<ref bean="person"/>
<list>
</property>
装配set集合
<property name="sets">
<set>
<value>set1</value>
<value>set2</value>
<ref bean="person"/>
<set>
</property>
装配map
<property name="maps">
<map>
<entry key="01">
<value>map01</value>
</entry>
<entry key="02">
<value>map02</value>
</entry>
</map>
</property>
map中的<entry>的数值和<list>以及<set>的一样,可以使任何有效的属性元素,需要注意的是key值必须是String的。
装配Properties:
<property name="props">
<props>
<prop key="01">prop1</prop>
<prop key="02">prop2</prop>
</props>
</property>
注解形式:
步骤:
在配置文件中,引入context命名空间
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring- beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
在配置文件中加入context:annotation-config标签
<context:annotation-config/>
这个配置隐式注册了多个对注释进行解析处理的处理器
AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,
PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor
注: @Resource注解在spring安装目录的lib\j2ee\common-annotations.jar
1、 这两个注解的区别是:
@Autowired 默认按类型装配。
@Autowired注解是按类型装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,
可以设置它required属性为false。
例: @Autowired(required=false)
如果我们想使用按名称装配,可以结合@Qualifier注解一起使用。
如下:
例: @Autowired@Qualifier("personDao")
@Resource默认按名称装配,当找不到与名称匹配的bean才会按类型装配。
1. @Resource 注解和 @Autowired一样,也可以标注在字段或属性的setter方法上.
2. @Resource 注解默认按名称装配。
3. 名称可以通过 @Resource的name属性指定,如果没有指定name属性:
1)当注解标注在字段上,即默认取字段的名称作为bean名称寻找依赖对象.
2)当注解标注在属性的setter方法上,即默认取属性名作为bean名称寻找依赖对象。
注意:如果没有指定name属性,并且按照默认的名称找不到依赖对象时, @Resource注解会回退到按类型装配。
但一旦指定了name属性,就只能按名称装配了。
@PostConstruct :指定Bean的初始化方法.
@PreDestroy : 指定Bean的销毁方法.
扫描机制:
前面的例子我们都是使用XML的bean定义来配置组件。在一个稍大的项目中,通常会有上百个组件,
如果这些组件采用xml的bean定义来配置,显然会增加配置文件的体积,查找及维护起来也不太方便。
spring2.5为我们引入了组件自动扫描机制,它可以在类路径底下寻找标注了:
@Component 、 @Service 、 @Controller、 @Repository注解的类,并把这些类纳入进spring容器中管理。
它的作用和在xml文件中使用bean节点配置组件是一样的。要使用自动扫描机制,我们需要打开以下配置信息:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="cn.itcast"/>
</beans>
在配置文件中添加context:component-scan标签。
<context:component-scan base-package="cn.itcast"/>
其中base-package为需要扫描的包(含含其下所有子包)。
注:
1、在使用组件扫描元素时,AutowiredAnnotationBeanPostProcessor和
CommonAnnotationBeanPostProcessor会隐式地被包括进来。 也就是说,连个组件都会被自动检测并织入
- 所有这一切都不需要在XML中提供任何bean配置元数据。
2、功能介绍
@Service用于标注业务层组件、
@Controller用于标注控制层组件(如struts中的action)、
@Repository用于标注数据访问组件,即DAO组件。
而 @Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
Spring中的继承:
类的继承关系在spring中的配置文件中要声明出来,否则是不行的
范例:
<bean id="student" class="cn.itcast.spring.extend.Student" parent="person" />
配置文件中,parent为student在容器中继承person.如果去掉person是不行的。
IOC和DI的意义:
核心:面向切面编程。
1.代理模式:
代理模式的英文叫做Proxy或Surrogate,中文都可译为”代理“,所谓代理,
就是一个人或者一个机构代表另一个人或者另一个机构采取行动。
在一些情况下,一个客户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用.
抽象主题角色
声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以是使用代理主题
代理主题(Proxy)角色:
代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;
代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题控制对真实主题的引用,
负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实的主题之前或之后,
都要执行某个操作,而不是单纯地将调用传递给真实主题对象。
真实主题角色
定义了代理角色所代表地真实对象.
JDK的动态代理必须具备四个条件:
1.. 目标接口
2.. 目标类
3.. 拦截器
4.. 代理类
总结:1、因为利用JDKProxy生成的代理类实现了接口,所以目标类中所有的方法在代理类中都有。
2、生成的代理类的所有的方法都拦截了目标类的所有的方法。
而拦截器中invoke方法的内容正好就是代理类的各个方法的组成体。
3、利用JDKProxy方式必须有接口的存在。
4、invoke方法中的三个参数可以访问目标类的被调用方法的API、被调用方法的参数、被调用方法的返回类型。
CGLIB做代理:
1.CGlib是一个强大的,高性能,高质量的Code生成类库。它可以在运行期扩展Java类与实现Java接口。
2.用CGlib生成代理类是目标类的子类。
3.用CGlib生成 代理类不需要接口
4.用CGLib生成的代理类重写了父类的各个方法。
5.拦截器中的intercept方法内容正好就是代理类中的方法体
spring有两种代理方式:
1.若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
优点:因为有接口,所以使系统更加松耦合
缺点:为每一个目标类创建接口
2.若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。
优点:因为代理类与目标类是继承关系,所以不需要有接口的存在。
缺点:因为没有使用接口,所以系统的耦合性没有使用JDK的动态代理好。
AOP编程:
Aspect(切面)
比如说事务、权限等,与业务逻辑没有关系的部分
joinpoint(连接点)
目标类的目标方法。(由客户端在调用的时候决定)
Pointcut(切入点)
所谓切入点是指我们要对那些拦截的方法的定义.
被纳入spring aop中的目标类的方法。
Advice(通知)
所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,
环绕通知(切面要完成的功能)
Target(目标对象):
代理的目标对象
Weaving(织入)
是指把切面应用到目标对象来创建新的代理对象的过程.切面在指定的连接点织入到目标对象 。
1、概念
面向切面编程
2、意义
场景
在编程的过程中,会遇到这样的情况:
在一个类中,有很多方法,这些方法有一个共性,就是在进行数据库操作的时候,需要开启事务和提交事务,
这样每个方法中都得写开启事务和事务提交的代码,我们能不能把这些代码只写一次呢?
实现
切面:
我们可以把上述的这种场景的事务的开启和提交提取出来,写在一个类中,这样的类为切面
通知:
把该类中的两个方法:事务的开启和提交为通知
目标类:
我们可以把在方法中对业务逻辑(数据库中数据的操作)的操作所在的类为目标类
目标方法:
实现业务逻辑的方法为目标方法,例如:在student表中插入一行数据
AOP代理:
利用动态代理模式生成的代理对象
代理对象的方法
代理对象的方法把通知与目标方法结合在了一起
连接点:
代理对象调用的方法为连接点,需要说明的是代理对象的机制有两种
1、代理对象和目标对象实现了共同的接口
2、代理对象是目标对象的子类
所以连接点也可以说是目标类中的一个方法
通知
前置通知
1、在目标方法执行之前执行
2、方法中可以有一个参数:PointJoint,从这个参数中可以获取目标方法的信息
3、前置通知的应用场合:
1、可以执行日志
2、如果目标方法涉及到数据库的操作,前置通知可以做开启事务的操作
3、如果要统计一个方法被访问的次数,可以通过前置通知设计计数器来实现
后置通知
1、在目标方法执行之后执行
2、可以获取目标方法的返回值
3、如果目标方法遇到异常,后置通知将不再执行
4、应用场合:
1、可以在后置通知中提交事务
2、在完成业务逻辑操作以后,可以执行一些额外的操作:发email等
最终通知
1、在目标方法执行之后执行
2、无论目标方法是否遇到异常,都执行
3、相当于代码中的finally
异常通知
1、可以获取目标方法抛出的异常
2、应用场合:
在mvc结构中,如果把service层的所有的类当做目标类,该通知可以成为异常处理的统一接口
环绕通知
1、可以控制目标方法的执行
3、说明
1、在spring容器中,spring容器会自动根据目标类是否实现接口,选择采用哪种动态代理实现代理对象
1、目标类实现了接口,则采用jdk动态代理
代理对象和目标对象实现了相同的接口
2、目标类没有实现接口,则采用cglib动态代理
代理类是目标类的子类
2、我们可以利用springAOP很好的把hibernate中的重复性代码抽取出来
1、写一个事务类 切面
两个方法:
开启事务 前置通知
提交事务 后置通知
2、目标类
3、代理类
生成的代理类的方法:
1、开启事务
2、执行数据库的操作
3、事务的提交
3、我们可以利用springAOP的特点,给目标类创建异常处理切面
4、在目标类的上下文可以建立很多个切面,这些切面可以处理:事务、日志、计数器、缓存等
4、扩展
AOP在企业中做到了:
日志可以由专门的人处理,因为现在日志的数量和种类越来越多了
事务由专门的人处理,现在的系统,事务变得很复杂
缓存由专门的人处理:有些人估计更擅长于缓存的操作
业务逻辑由专门的人处理:有些人估计对需要把握特别准确
在客户端生成的代理对象的方法把这些内容全部整合在了一起,但是上面的各个内容是送耦合的
JDKProxy代理 SpringAop
目标对象 目标对象
拦截器类 切面
拦截器类中的方法 通知
被拦截到的目标类中方法的集合 切入点
在客户端调用的方法(目标类目标方法) 连接点
代理类 AOP代理
代理类的代理方法生成的过程 织入
通知根据拦截目标类中的目标方法的位置不一样可以分为:前置通知、后置通知、最终通知、环绕通知、异常通知。
AOP实现的两种模式:
1.xml形式:
2.注解形式:
(待补全)
spring的事务管理:
1、原理
1、基于的原理是aop
2、标题分析
这个标题可以分解为这几个词:
声明式
程序员对目标方法进行声明
事务处理
spring容器负责处理事务
3、说明:
1、目的
这个技术的目的是让程序员不再关注事务,而只关注做自己的事情
2、利用AOP可以达到这个目的
1、程序员做数据库的操作
2、让spring容器做事务
2、步骤:
1、写java类,dao层和service层
2、写spring的配置文件
1、引入sessionFactory
2、引入事务管理器
3、引入AOP的配置
4、配置AOP
5、配置事务通知
3、spring中处理事务的框架
1、顶级接口
PlatformTransactionManager
getTransaction
commit
rollback
2、抽象类
AbstractPlatformTransactionManager
对commit、rollback方法进行了实现
3、具体类
DataSourceTransactionManager,HibernateTransactionManager等
4、说明:
在spring内部,都是面向接口编程(这点从HbiernateTemplate中可以体会),而接口的实现类是需要通过spring配置文件注入进去的,所以任何一个数据库
操作框架只要知道spring的结构,很容易整合到spring容器中
4、spring声明式事务处理的缺点
从原理上可以看出,如果一个类的方法中有数据库的操作,如果该类的事务处理想交给spring容器来做,那么必须满足:
1、把该类放入到spring容器中
2、为该类生成代理对象
如果存在这样的情况:
例如在jbpm工作流中,有些类是由jbpm内部调用的,但是这些类的方法中如果需要事务呢?答案是用spring容器做事务做不到
5、关于声明式事务处理的session
由于必须保证数据库操作的session和事务的session是同一个session,所以session必须由当前线程产生