Spring
IoC
一 Spring概述
1.什么是Spring?
- Spring是为了解决企业应用开发的复杂性而创建的,优秀的轻量级企业应用解决方案,开放源代码,主要特点是采用分层结构,允许单独使用某一模块,也可以将多个模块组合使用。
- 企业应用:为了满足企业运行需要开发的软件系统,现在的企业应用大多运行在开放性平台(存在信息交互的可能性),不再是孤立的系统,而是在相互联系中构成了一个系统群。
- 轻量级:轻量级框架如Struts、Spring,侧重于降低开发的复杂度,体积小,消耗小,相应地处理能力有所减弱。
- JavaBean:使用java语言编写的可重用组件,主要用作数据的载体。
- EJB:Enterprise JavaBean,服务器端组件模型,定义了一个用于开发基于组件的企业应用的规范,本身复杂而繁琐。
2.Spring的技术基础
- IoC:Inversion of Control,控制翻转。
- AOP:Aspect Oriented Program,面向切面编程。
3.Spring的构成
- Spring core:Spring框架的核心,提供了Spring框架的基础功能,实现了IoC,包含重要的类BeanFactory。BeanFactory是IoC的容器,负责bean的实例化、初始化、使用与销毁。
- Spring context:继承了BeanFactory,添加了许多功能,如国际化、数据校验等。
- Spring AOP:继承AOP的所用功能,通过事务可以将Spring管理的任意对象AOP化。
- Spring DAO:提供了JDBC的抽象层,简化了数据库厂商的异常错误,大幅度减少了代码,并且支持声明式事务与编程式事务。
- Spring ORM:兼容所有流行的ORM框架,完美地整合了Hibernate\Mybatis。
- Spring Web:提供了Servlet监听器上下文与Web上下文,集成了现有的Web框架,如JSP、Struts。
- Spring MVC:建立在Spring的核心功能之上,具有Spring框架的所有特性。
二 BeanFactory
1.BeanFactory
一个容器,通过读取bean的定义文件获取所有的bean对象,实现对bean的创建、使用与管理。
2.ApplicationContext
继承了BeanFactory,添加了许多功能,如国际化、数据验证等,有三个比较重要的实现类:
- ClassPathXmlApplicationContext:从类路径下加载配置文件,一般常用此类加载applicationContext.xml。
- FileSystemXmlApplicationContext:从文件路径加载文件,适用性范围广。
- WebApplicationContext:Spring与Web整合后在bean在Web中的容器。
三 控制反转
1.什么是控制反转?
一种思想,依赖注入是该思想的一种实现,将创建实例的任务交给IoC容器,需要实例时向容器发送请求即可,这样降低了引用类A对被引用类B的依赖,即B发生改变,A需要修改很少一部分,甚至不需要修改。
2.降低耦合度的实现过程
在传统开发中,A类调用B类,就必须在内部创建B类,B b= new B(),当B类被删除,就需要修改A类的代码,这样A类就受到B类变化的拖累。如果A类调用的是B类的接口,并且对象由外部创建,当外部实现类发生改变时,只要提供一个满足要求的实现类,A依然可以正常运行,不需要修改A中的代码,B的变化没有波及A。
3.依赖注入
⑴setter注入
底层调用setter方法实现注入:
<bean id="引用变量"class="全限定性类名"> <property name="属性名"> <value>属性值</value> </property> </bean>
⑵构造器注入
底层调用构造方法实现注入:
1 <bean id="引用变量"class="全限定性类名"> 2 <constructor-arg> 3 <value>参数值</value> 4 </constructor-arg> 5 </bean>
配置文件中标签的赋值顺序必须与构造方法形参定义顺序一致,否则抛出出错。为了避免顺序不一致导致的错误,在配置文件中为每一个赋值标签设定一个index值,对应其要赋值的形参在构造器中的顺序:
1 <constructor-arg index="0"> 2 <value>参数值</value> 3 <constructor-arg>、
⑶集合注入
数组与List集合:
1 <list> 2 <value>value01</value> 4 <value>value02</value> 5 </list>
数组还有一种更简单的赋值方式,值写在一块,用逗号隔开:
1 <property name=""value="value01,value02"/>
Set集合:
1 <set> 2 <value>value01</value> 3 <value>value02</value> 4 </set>
Map集合:
<map> 2 <entry key=""value=""/> 3 <entry key=""value=""/> 4 </map>
Properties对象:
1 <props> 3 <prop key="xxx"></prop> 5 <prop key="xxx"></prop> 7 </props>
⑷引用bean
<property name=""> <ref local="id"/> </property>
⑸自动装配
byName:通过名称装配,将IoC容器中与属性名同名的bean注入。
<bean autowire="byName"id=""class=""/>
- 按名称装配的不足:如果IoC容器中存在多个同名而类型不同的bean,注入后就会发生错误。
byType:将与属性数据相同的bean注入。
<bean autowire="byType"id=""class=""/>
- 按类型转配的不足:如果IoC容器中存在多个类型相同的bean,IoC容器会因为无法识别需要的bean而报错。
⑹内部bean
如果希望内部bean只能通过外部bean访问,那么将内部bean的定义放在外部bean内部:
<bean id="xxx"class="OuterClass"> <property name="innerClassAttrName"> <bean class="InnerClass">//因为不支持外部对象引用,所以不需要定义id <property name="">xxxxx</property> </bean> </property> </bean>
四 同类抽象bean与异类抽象bean
1.背景
在配置文件中,如果多个bean拥有共同的属性,分别为每一个bean配置,数据冗余,为了消除冗余,将这些共同的属性提取出来,定义在一个抽象bean中,其他bean通过引用该抽象bean为属性赋值。
2.同类抽象bean
为同类的bean赋值:
<bean id="abstractBean"class="ClassA"abstract="true"> <property name=""value=""/> ........为共同属性赋值.......... </bean> <bean id="xxx"parent="abstractBean"> <property name=""value=""/> ........为特有属性赋值.......... </bean>
- abstract="true":将该bean定义为抽象bean,防止通过getBean直接访问该bean。
3.异类抽象bean
为不同类型的bean赋值:
<bean id="abstractBean"abstract="true"> <property name=""value=""/> ........为共同属性赋值.......... </bean> <bean id="xxx"class=""parent="abstractBean"> <property name=""value=""/> ........为特有属性赋值.......... </bean>
五 bean作用域
1.生命周期的设定
<bean id=""class=""/>//采用默认值singleton <bean id=""class=""singleton="true/false"/> <bean id=""class=""scope="singleton/prototype"/>
2.singleton与prototype
- singleton:bean采用单例模式,生命周期与IoC容器相同,由IoC容器管理,每次请求返回的是同一bean对象,。
- prototype:采用多例模式,IoC容器将对象交给使用者,由使用者管理,每一次请求获取的都是不同对象,。
六 SpEL
1.SpEL是什么?
Spring Expression Language,用于在配置文件中动态地为属性赋值,该值可以来源于容器中其他bean的属性,调用bean方法的返回值,也可是调用容器以外其他类静态方法的返回值。
2.基本语法
#{expression}
- 调用容器中其他bean的属性:#{beanName.attrName}
- 调用容器中bean的方法:#{beanId.method(arg)}
- 调用容器以外其他类的静态方法:#{T(全限定性类名).method(arg)}
七 bean的生命周期
1.11个阶段
bean的生命周期可分为11个阶段,即总共有11时机可以用来改变bean,除自定义的BeanPostProcessor适用于所有bean而单独创建外,其他的都通过bean实现相应的接口实现,如BeanNameAware\BeanFactoryAware\InitializingBean\DisposableBean等。
2.相关的接口或者类
⑴在bean内部自定义控制生命周期的方法
- 定义在setter方法执行完毕后调用的方法,该方法必须无参:
-
<bean init-method="方法名">
- 定义在IoC容器关闭以后调用的方法,必须无参:
-
<bean destory-method="方法名"/>
⑵BeanPostProcessor
一个接口,其实现类只有在配置文件中配置之后才可以使用,由底层调用,配置时无需设定id。容器中所有的bean在初始化阶段都会调用其中的两个方法:
- postProcessBeforeInitialization:在bean初始化完毕之前由容器调用;
- postProcessAfterInitialization:在bean初始化完毕之后由容器调用。
八 配置文件的分散编写
1.平等关系
两个配置文件各自单独存在,不存在包含关系。在java代码中读取平等关系的配置文件需要使用指定类的可变参数的形式:
new ClassPathXmlApplicationContext(String...)
2.包含关系
一个配置文件在内容上包含另一个配置文件:
<import resources="classpath:xxxxx.xml"/>
- 配置文件路径前一定要加classpath,为底层指定加载方式,因为配置文件的加载方法有多种:以ClassPath开头的类从类路径加载,以FileSystem开头的类从文件路径加载。在java中加载时使用ClassPath开头的类实际上已经设定了文件的加载方法,程序员只负责加载外层文件,内层文件的加载是由底层负责的,如果不指明加载方式,底层无法加载。底层执行过程大致是:提取classpath,判断,根据判断结果选择加载方式,如果classpath不存在,底层就无法进行判断,无法运行,就会抛出错误。
九 注解式开发
1.注册扫描器
在配置文件中注册注册扫描器:
<context:component-scan base-package="xxx"/>
Spring不同于Hibernate与Struts,没有默认的配置文件,被动加载,必须显式指明配置文件,因此当采用注解开始时也必须显式指明组件位置。
- 如果包名为xxx,表示扫描该包及其子包。
- 如果包名为xxx.*,表示只扫描子包。
2.常用注释:
- @Component(value="相当于配置文件中的id"):表明该类是一个组件,扫描时容器会创建该类的实例,这是bean注解的第一步,先让容器把该类当做需要创建bean的类。作用相同的注解有@Repository(注解在Dao接口的实现类上)\@Service(注解在Service的实现类上)\@Controller(注解在Controller类上)。
- @Scope(value="singleton/prototype"):设置作用域。
- @Value(value=""):为一般属性赋值。
- @Resource(name=""):为域属性赋值。域属性就是自定义类的对象。@Autowired::Spring提供的依照类型的域属性注入注解。
- @PostConstruct:在bean内部设定在初始化完成之后调用的方法。初始化完成的标识是InitializingBean实现类的方法afterPropertiesSet调用完毕。
- @PreDestroy:在bean内部设定bean销毁前调用的方法。
十 SpringJUnit4
SpringJUnit4提供了对bean的简化测试方案,进行简单设置之后,测试时就不需要再创建容器,可以直接使用bean:
- 在类上加@RunWith(SpringJUnit4ClassRunner.class):表示采用SpringJUnit4进行测试。
- 在类上加@ContextConfiguration(locations="classpath:xxxx.xml"):指明配置文件。
- 在域属性加@Autowired:按照类型注入,也可以采用其他方式注入
AOP
一概述
1.产生背景
在AOP产生以前,OOP是编程的基本原则,所有对象执行需要的代码都必须写在类中,如果多个类拥有一部分相同的代码,那么这些代码就必须在每个类中都编写,不仅代码大量重复,而且不便于维护,这时就产生了将那些重复出现、与业务无关的代码从业务中分离出来的思想,就是AOP。AOP将重复的、与业务无关的代码从业务中分离处理,业务执行时切入业务中,形式上与业务分离,执行时与业务结合,不仅便于维护,而且降低了业务逻辑部分与非业务逻辑部分的耦合度。
2.原理
Aspect Oriented Program,面向切面编程,建立在动态代理机制之上。
3.重要的概念
- 切面:对象执行过程中切入流程的、与业务逻辑无关的代码。在程序中,切面是一个集中了通知的类。
- 切入点:切面切入流程的点,Spring只支持方法类型的切入点,即切面只能在对象方法执行前后切入,不能切入方法内部。
- 通知:切入点被横切时,所采取的业务逻辑,通知是切面中的方法。
- 目标对象:切面切入的对象。
- 织入:将切面功能应用于目标对象的过程。
- 引用:动态地向目标对象添加属性与方法的过程。
二 Spring自身对AOP的实现
1.原理
Spring自身提供了对AOP思想的实现,主要借助内置接口的实现类与配置文件完成。
2.Advice
通知,切入点被横切时采取的处理逻辑,即在切入点前后执行的方法。在Spring中,Advice是一个层级比较高的接口,具体使用的通知都间接实现了该接口。缺点是,一旦定义就对所有方法都起作用。
- 前置通知:在切点执行前执行,实现MethodBeforeAdvice实现。
- 后置通知:在切点成功执行后执行,排在环绕通知之后,方法无返回值,不可以修改返回值,实现AfterReturningAdvice。
- 环绕通知:在切点执行前与执行成功后执行,可以修改返回值,实现MethodInterceptor。
- 异常通知:在切点抛出异常时,执行,实现ThrowsAdvice。
3.Advisor
顾问,Advice的装饰者,可以对方法进行筛选,使通知只对选定的方法起作用。常用到的两个实现类:
- NameMatchMethodPointcutAdvisor:列举选定的方法,使通知只对选定的方法有效。
- RegexMethodPointccutAdvisor:通过正则表达式匹配方法。
⑴NameMatchMethodPointcutAdvisor
<bean id=""class="Advisor的两个实现类其中一个"> <property name="advice"ref="通知名"/>//装饰的通知 <property name="mappedNames"value="方法名"/>//即切点 </bean>
⑵RegexpMethodPointcutAdvisor
<property name="advice"ref="通知名" /> <property name="pattern" value="正则表达式,匹配对象时接口的方法" />
4.ProxyFactoryBean
⑴代理通过ProxyFactoryBean类创建,底层执行Proxy.newProxyInstance(),对比底层实现为该类属性赋值,动态地执行:
<bean id=""class="xxxxxx.ProxyFactoryBean"> <property name="targetName"name=""/> <property name="interceptorNames"value="通知名或顾问名"/> </bean>
5.基于顾问的自动代理生成器
当有多个目标对象时,使用ProxyFactoryBean创建代理对象时,需要为每一个代理对象编写创建过程,而使用DefaultAdvisorProxyCreator,可以自动为目标对象创建代理。因为DefaultAutoProxyCreator底层实现了BeanPostProcessor,由底层自动调用。
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
6.基于beanName的自动代理生成器
⑴该代理生成器弥补了基于顾问的代理生成器无法选择通知、无法选择目标对象的不足。
<bean id="proxy"class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator"> <property name="beanNames" value="target" />//选择要代理的对象 <property name="interceptorNames" value="methodInterceptor" />//选择切面 </bean>
7.获取代理
无论是基于顾问的,还是基于beanName的自动代理生成器,都可以同时代理多个目标对象,为了区分被代理的目标对象只能通过目标对象来获取代理对象,这样才能保证一个代理对象指向一个目标对象。
三 AspectJ
<aop:config proxy-target-class="true/false"> <aop:aspect ref="切面所在类的id"> ---将切点定义在切面内,对其他切面不可见。也可以将切点定义在切面外,对所有切面可见--- <aop:pointcut ref="切点所在类的id" expression="execution(切入点表达式)"/> <aop:before method="方法名"pointcut-ref="切点id"/> <aop:after method="方法名"pointcut-ref="切点id"/> </aop:aspect> </aop:config>
1.切入点表达式
用于限制切入点的表达式,与表达式匹配的方法都可以用作切入点,语法格式:
[访问权限] 返回值类型 [全限定性类名] 方法名(..)
- ():匹配不带参数的方法。
- (*):匹配带一个参数的方法。
- (..):匹配带任意形式参数或不带参数的方法。
- *:代表若干字符。
- ..:代表多级目录。
- +:代表该类及其子类。
2.其他标签
- <aop:config>:配置AOP的全部信息,包括切面、切点。proxy-target-class指明所用的代理机制,CGLIB,还是基于接口的代理JDK,默认为false,使用基于接口的代理。
- <aop:aspect>:配置切面,包含切点前执行的方法、切点后执行的方法、适用的切点等。
- <aop:pointcut>:定义切点,Spring只支持方法类型的切点,expression="execution(* 方法)"指明用作切点的方法。
- <aop:before>:指明在切点前执行的方法,pointcut-ref属性指明适用的切点。
- <aop:after>:指明在切点后执行的方法,无论切点是否成功执行都会执行,pointcut-ref属性指明适用的切点。
- <aop:after-throwing>:在抛出异常后执行。
- <aop:after-returning returning="返回值result">:在返回值之后执行,返回值名必须与方法形参名相同。
- <aop:around>:紧贴代理对象方法前后执行,方法必须有返回值,可以修改目标方法的返回值。当使用环绕通知时,目标对象的方法只在环绕方法内部被调用后才可以执行,因为环绕通知底层采用MethodInterceptor,,而拦截器内部必须显式地调用后面的方法,进程才能推进。目标对象的方法可能有返回值,因此环绕通知必须有返回值。
四 DAO
1.什么是DAO?
Data Access Object,数据访问对象,封装了一些对数据库进行持久化操作的方法,将持久化操作与一般的业务逻辑分开,便于维护与测试。
2.数据源
Spring提供的数据源DriverManagerDataSource,,在实际开发中很少用到,常用到第三方数据源C3P0、DBCP。
DriverManagerDataSource:
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource"> <property name="driverClassName"> <value>com.mysql.jdbc.Driver</value> </property> <property name="url"> <value>jdbc:mysql://localhost:3366/db_spring</value> </property> <property name="username"> <value>root</value> </property> <property name="password"> <value>123</value> </property> </bean>
C3P0:
<bean id="c3p0"class="com.mchange.v2.c3p0.ComboPooledDataSource"> <property name="driverClass" value="com.mysql.jdbc.Driver" /> <property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3366/db_spring"/> <property name="user" value="root" /> <property name="password" value="123" /> </bean>
DBCP:
<bean id="dbcp"class="org.apache.commons.dbcp2.BasicDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3366/db_spring"/> <property name="username" value="root" /> <property name="password" value="123" /> </bean>
3.DAO类
⑴JdbcDaoSupport
支持以基础的JDBC技术操作数据库的抽象类,开发者需要设置数据源,通过子类获得JdbcTemplate来访问数据库。利用框架提供的类DriverManagerDataSource创建与数据的连接,然后通过该类的实例获得Connection对象,后续操作同JDBC相同。Dao层实现继承JdbcDaoSupport,内部获取getJdbcTemplate进行底层数据库操作。
Dao的实现类通过JdbcDaoSupport.getJdbcTemplate获取模板对象,通过模板对象封装的方法操作数据库:
- update:在模板中增删改操作统一用updae方法。
- queryForObject(slq,requiredClass,arg):用于查询一个字段,并且查询结果唯一。requeredClass表示查询字段的类型,以便对查询结果转型。
- queryForList(slq,requiredClass,arg):用于查询一个字段,结果可能有多个。
- queryForObject(sql,rowMapper,arg):查询结果是一个对象,其中rowMapper用于将查询字段封装成一个对象,查询结果必须包含对象的全部属性。
- query(sql,rowMapper,arg):返回对个对象。
- 数据源注入时自动创建模板,所以不需要在Dao的实现层显式注入JdbcTemplate的,即注入数据源时自动创建模板。
- 系统自动在方法结束时销毁JdbcTemplate对象,因此每次在方法中使用时都需要重新创建。
- HibernateDaoSupport:支持在Hibernate中操作数据库的抽象类,开发者需要设置SessionFactory,然后获得Hibernate的实现,HibernatDaoSupport底层实现复杂,效率低,不建议使用。
4.继承
JdbcDaoSupport/HibernateDaoSupport层级都高于对应的模板类,内部都提供了获取相应模板类的方法,与数库的交互通过模板类来完成。应用Spring提供的DAO模块时,自定义类通常继承XXXDaoSupport类,在自定义方法内部获得响应模板类来具体操作数据库。
五 事务概述
1.原理
Spring事务管理是基于AOP实现的,而Spring的AOP是以方法为单位的,所以Spring的事务属性就是将事务应用到方法上的策略。
2.事务管理器
Spring提供了PlatformTransactionManager接口来管理事务,该类提供了用于事务管理的通知,可以将该类视作切面,两个重要的实现类:
- DataSourceTransactionManager:适用于JDBC、Mybatis。
- HibernateTransactionManager:适用于Hibernate。
3.异常
Spring在默认情况下,发生运行时异常回滚,编译时异常提交。程序员可以改变回滚方式。
4.事务属性
事务的四大属性分为传播行为、隔离级别、只读和超时。事务的传播行为用于确定是否将事务应用在方法上以及应用的策略,比如应用当前事务,新建事务。
六 基于Spring AOP的事务管理
1.四个要素
代理工厂、目标对象、切点、切面。
- 代理工厂:在SpringAOP事务管理中采用TransactionProxyFactoryBean充当代理工厂。
- 切面:在Spring事务管理中PlatformTransactionManager充当切面。
2.配置文件
<bean id=""class="xxxTransactionProxyFactoryBean">//代理工厂 <property name="target" ref=""/>//目标对象 <property name="transactionManager"ref=""/>//切面 //事务属性,就是事务应用到目标对象方法上的策略 <property name="transactionAttributes> <props> <prop key="目标对象方法">事务四大属性</prop> </props> </property> </bean>
3.事务的属性
因为目标对象中有多个方法,并不是所有方法都需要被事务管理,被事务管理的方法所需要的管理方式也不同,因此需要通过事务属性进行个性化定制,如ISOLATION_DEFAULT/PROPAGATION_REQUIRED/-Exception。
4.异常
Spring事务管理默认在发生编译时异常时提交,也可以设置成发生编译时异常回滚,在事务属性中设置:"-Excpetion",以负号开头,加异常,表示当发生该异常时事务回滚。
七 基于AspectJ的事务管理
1.配置文件的编写:
<tx:advice id="advice"tansaction-manager="transactionManger"> <tx:attributes> <tx:method name="buyStock" isolation="DEFAULT"propagation="REQUIRED"rollback-for="StockProcessException" /> </tx:attributes> </tx:advice> <aop:config> <aop:pointcut id="p01"expression="execution(xxxx)"/> <aop:advisor advice-ref="advice"pointcut-ref="p01"/> </aop:config>
八 注解式开发
以上两种事务管理是基于配置文件实现的,缺点是需要为每一个代理对象创建代理工厂,因此提供了基于注解的实现,可以方便地为多个对象创建代理:
在配置文件中添加:
<tx:annotation-driven transaction-manager="transactionManager"/>
在目标对象方法上添加以下注解,设定事务的属性:
@Transactional(isolation =Isolation.DEFAULT, propagation = Propagation.REQUIRED, rollbackFor=StockProcessException.class)