一、Spring模块
Spring可以分为6大模块:
Spring Core spring的核心功能: IOC容器, 解决对象创建及依赖关系
Spring Web Spring对web模块的支持。
---可以与struts整合,让struts的action创建交给spring
---spring mvc模式
Spring DAO Spring 对jdbc操作的支持 【JdbcTemplate模板工具类】
Spring ORM spring对orm的支持:
---既可以与hibernate整合,【session】
---也可以使用spring的对hibernate操作的封装
Spring AOP 切面编程
SpringEE spring 对javaEE其他模块的支持
1.Core模块[kɔː(r)]快速入门
搭建配置环境 引入jar包:
涉及到Spring core的开发jar包有五个:
commons-logging-1.1.3.jar 日志
spring-beans-3.2.5.RELEASE.jar bean节点
spring-context-3.2.5.RELEASE.jar spring上下文节点
spring-core-3.2.5.RELEASE.jar spring核心功能
spring-expression-3.2.5.RELEASE.jar spring表达式相关表
编写配置文件:
Spring核心的配置文件applicationContext.xml或者叫bean.xml
前面在介绍Spring模块的时候已经说了,Core模块是:IOC容器,解决对象和创建之间的依赖关系。
因此Core模块主要是学习如何得到IOC容器,通过IOC容器来创建对象
2.得到Spring容器对象【IOC容器】
Spring容器不单单只有一个,可以归为两种类型
• Bean工厂,BeanFactory(功能简单)
• 应用上下文,ApplicationContext(功能强大,一般我们使用这个)
通过Resource获取BeanFactory
加载Spring配置文件
通过XmlBeanFactory+配置文件来创建IOC容器
类路径下XML获取ApplicationContext
直接通过ClassPathXmlApplicationContext对象来获取
3.在Spring中总体来看可以通过三种方式来配置对象:
使用XML文件配置
使用注解来配置
使用JavaConfig来配置
A. XML配置方式
在上面我们已经可以得到IOC容器对象了。接下来就是在在applicationContext.xml文件中配置信息 (让IOC容器根据applicationContext.xml文件来创建对象)
创建对象的方式:
无参构造函数创建对象
带参数的构造函数创建对象
工厂创建对象
----静态方法创建对象
----非静态方法创建对象
带参数的构造函数创建对象:
首先,JavaBean就要提供带参数的构造函数:
配置applicationContext.xml文件
在constructor上如果构造函数的值是一个对象,而不是一个普通类型的值,我们就需要用到ref属性了,而不是value属性
c名称空间
我们在使用XML配置创建Bean的时候,如果该Bean有构造器,那么我们使用<constructor-arg>这个节点来对构造器的参数进行赋值,为了简化配置,Spring来提供了c名称空间
要想c名称空间是需要导入
B. 注解方式
通过注解来配置信息就是为了简化IOC容器的配置,注解可以把对象添加到IOC容器中、处理对象依赖关系。
使用注解步骤:
1)先引入context名称空间
xmlns:context="http://www.springframework.org/schema/context"
2)开启注解扫描器
<context:component-scan base-package=""></context:component-scan>
创建对象以及处理对象依赖关系,相关的注解:
@ComponentScan 扫描器
@Configuration 表明该类是配置类
@Component 指定把一个对象加入IOC容器--->@Name也可以实现相同的效果【一般少用】
@Repository 作用同@Component; 在持久层使用
@Service 作用同@Component; 在业务逻辑层使用
@Controller 作用同@Component; 在控制层使用
@Resource 依赖关系
如果@Resource不指定值,那么就根据类型来找,相同的类型在IOC容器中不能有两个
如果@Resource指定了值,那么就根据名字来找
C. 通过Java方式
怎么通过java代码来配置Bean呢?
编写一个java类,使用@Configuration修饰该类
被@Configuration修饰的类就是配置类
bean对象创建细节
• scope属性
指定scope属性,IOC容器就知道创建对象的时候是单例还是多例的
• lazy-init属性
lazy-init属性只对singleton【单例】的对象有效,lazy-init默认为false。
当我们想要对象在使用的时候才创建,那么将lazy-init设置为ture就行了
• init-method和destroy-method属性
如果我们想要对象在创建后,执行某个方法,我们指定为init-method属性就行了
如果我们想要IOC容器销毁后,执行某个方法,我们指定destroy-method属性就行了
二、Spring依赖注入
在上节中主要讲解了Spring Core模块的使用IOC容器创建对象的问题,Spring Core模块主要是解决对象的创建和对象之间的依赖关系,因此这节主要讲解如何使用IOC容器来解决对象之间的依赖关系。
对象之间的依赖关系,其实就是给对象上的属性赋值,Spring提供了好几种的方式来给属性赋值:
• 通过构造函数
• 通过set方法给属性注入值
• p名称空间
• 自动装配(了解)
• 注解
p 名称空间注入属性值
p名称空间这种方式其实就是set方法的一种优化,优化了配置而已,p名称空间这个内容需要在Spring3版本以上才能使用
applicationContext.xml配置文件:使用p名称空间
自动装配
Spring还提供了自动装配的功能,能够非常简化我们的配置,自动装载默认是不打开的,自动装配常用的可分为两种:
根据名字来装配
根据类型类装配
applicationContext.xml配置文件:使用自动装配,根据名字
applicationContext.xml配置文件:使用自动装配,根据类型
值得注意的是:如果使用了根据类型来自动装配,那么在IOC容器中只能有一个这样的类型,否则就会报错
使用注解
@Autowired注解来实现自动装配:
可以在构造器上修饰
也可以在setter方法上修饰
来自java的@Inject的和@AutoWired有相同的功能
三、Spring AOP
前面已经简单学习了Spring的Core模块,本节我们就开启了Spring的AOP模块,在讲解AOP模块之前,首先我们来讲解一下cglib代理
cglib代理
由于静态代理需要实现目标对象的相同接口,那么可能会导致代理类会非常多维护---->因此出现了动态代理。
动态代理也有个约束:目标对象一定是要有接口,没有接口就不能实现动态代理。----->因此出现了cglib代理
cglib代理也叫子类代理,从内存中构建出一个子类来扩展目标对象的功能。
AOP的概述
Aop: aspect object programming 面向切面编程
功能: 让关注点代码与业务代码分离
面向切面编程就是指: 对很多功能都有的重复的代码抽取,再在运行的时候往业务方法上动态植入切面类代码
使用Spring AOP开发步骤
引入aop相关jar包:
spring-aop-3.2.5.RELEASE.jar 【spring3.2源码】
aopalliance.jar 【spring2.5源码/lib/aopalliance】
aspectjweaver.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
aspectjrt.jar 【spring2.5源码/lib/aspectj】或【aspectj-1.8.2\lib】
引入名称空间:
注解方式实现AOP编程:
手动的实现AOP编程是需要自己来编写代理工厂的,现在有了Spring,就不需要我们自己写代理工厂了。Spring内部会帮我们创建代理工厂。也就是说,不用我们自己写代理对象了。因此,我们只要关心切面类、切入点、编写切入表达式指定拦截什么方法就可以了
• 在配置文件中开启AOP注解方式
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<context:component-scan base-package="aa"/>
<!-- 开启aop注解方式 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
• AOP注解API
@Aspect 指定一个类为切面类
@Pointcut("execution(* cn.itcast.e_aop_anno..(..))") 指定切入点表达式
@Before("pointCut_( )") 前置通知: 目标方法之前执行
@After("pointCut_( )") 后置通知:目标方法之后执行(始终执行)
@AfterReturning("pointCut_( )") 返回后通知: 执行方法结束前执行(异常不执行)
@AfterThrowing("pointCut_( )") 异常通知: 出现异常时候执行
@Around("pointCut_( )") 环绕通知: 环绕目标方法执行
• XML方式实现AOP编程
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--对象实例-->
<bean id="userDao" class="aa.UserDao"/>
<bean id="orderDao" class="aa.OrderDao"/>
<!--切面类-->
<bean id="aop" class="aa.AOP"/>
<!--AOP配置-->
<aop:config >
<!--定义切入表达式,拦截哪些方法-->
<aop:pointcut id="pointCut" expression="execution(* aa.*.*(..))"/>
<!--指定切面类是哪个-->
<aop:aspect ref="aop">
<!--指定来拦截的时候执行切面类的哪些方法-->
<aop:before method="begin" pointcut-ref="pointCut"/>
<aop:after method="close" pointcut-ref="pointCut"/>
</aop:aspect>
</aop:config>
</beans>
• 切入点表达式
切入点表达式主要就是来配置拦截哪些类的哪些方法
四、Spring DAO
上节主要讲解了如何使用Spring来实现AOP编程,本节主要讲解Spring的DAO模块对JDBC的支持,以及Spring对事务的控制
首先来看一下我们原生的JDBC:需要手动去数据库的驱动从而拿到对应的连接
1.使用Spring的JDBC
首先,想要使用Spring的JDBC模块,就必须引入两个jar文件:
spring-jdbc-3.2.5.RELEASE.jar
spring-tx-3.2.5.RELEASE.jar
JDBC对C3P0数据库连接池是有很好的支持的。因此我们直接可以使用Spring的依赖注入,在配置文件中配置dataSource就行了,创建dataSource,创建jdbcTemplate对象
<?xml version="1.0" encoding="UTF-8"?>
<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"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver"></property>
<property name="jdbcUrl" value="jdbc:mysql:///zhongfucheng"></property>
<property name="user" value="root"></property>
<property name="password" value="root"></property>
<property name="initialPoolSize" value="3"></property>
<property name="maxPoolSize" value="10"></property>
<property name="maxStatements" value="100"></property>
<property name="acquireIncrement" value="2"></property>
</bean>
<!--扫描注解-->
<context:component-scan base-package="bb"/>
<!-- 2. 创建JdbcTemplate对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"></property>
</bean>
</beans>
2.事务控制概述
Spring的事务控制是属于Spring Dao模块的
一般地,我们事务控制都是在service层做的。。为什么是在service层而不是在dao层呢?
service层是业务逻辑层,service的方法一旦执行成功,那么说明该功能没有出错。一个service方法可能要调用dao层的多个方法…如果在dao层做事务控制的话,一个dao方法出错了,仅仅把事务回滚到当前dao的功能,这样是不合适的(因为我们的业务由多个dao方法组成)。如果没有出错,调用完dao方法就commit了事务,这也是不合适的(导致太多的commit操作)。
事务控制分为两种:
编程式事务控制
声明式事务控制
编程式事务控制:
自己手动控制事务,就叫做编程式事务控制。比较灵活,但开发起来比较繁琐: 每次都要开启、提交、回滚
Jdbc代码:
Conn.setAutoCommite(false); // 设置手动控制事务
Hibernate代码:
Session.beginTransaction(); // 开启一个事务
声明式事务控制:
Spring提供对事务的控制管理就叫做声明式事务控制。Spring的事务控制是基于AOP实现的。因此它的耦合度是非常低的
Spring给我们提供了事务的管理器类DataSourceTransactionManager
搭建配置环境:
AOP相关的jar包【因为Spring的声明式事务控制是基于AOP的,那么就需要引入AOP的jar包。】
引入tx名称空间
引入AOP名称空间
引入jdbcjar包【jdbc.jar包和tx.jar包】
• XML方式实现声明式事务控制
首先,我们要配置事务的管理器类
配置事务管理器类如何管理事务
最后,配置拦截哪些方法
• 使用注解的方法实现事务控制
第一步和XML的是一样的,必须配置事务管理器类:
第二步:开启以注解的方式来实现事务控制
最后,想要控制哪个方法事务,在其前面添加@Transactional这个注解就行了!如果想要控制整个类的事务,那么在类上面添加就行了
事务属性
其实我们在XML配置管理器类如何管理事务,就是在指定事务的属性!我们来看一下事务的属性有什么:
Spring事务基于Spring AOP,Spring AOP底层用的动态代理,动态代理有两种方式:
基于接口代理(JDK代理)
基于接口代理,凡是类的方法非public修饰,或者用了static关键字修饰,那这些方法都不能被Spring AOP增强
基于CGLib代理(子类代理)
基于子类代理,凡是类的方法使用了private、static、final修饰,那这些方法都不能被Spring AOP增强
Spring事务几个重要的接口
Spring事务可以分为两种:
编程式事务(通过代码的方式来实现事务)
声明式事务(通过配置的方式来实现事务)
编程式事务在Spring实现相对简单一些,而声明式事务因为封装了大量的东西(一般我们使用简单,里头都非常复杂),所以声明式事务实现要难得多
在编程式事务中有以下几个重要的了接口:
TransactionDefinition:定义了Spring兼容的事务属性(比如事务隔离级别、事务传播、事务超时、是否只读状态)
TransactionStatus:代表了事务的具体运行状态(获取事务运行状态的信息,也可以通过该接口间接回滚事务等操作)
PlatformTransactionManager:事务管理器接口(定义了一组行为,具体实现交由不同的持久化框架来完成---类比JDBC)
在声明式事务中,除了TransactionStatus和PlatformTransactionManager接口,还有几个重要的接口:
TransactionProxyFactoryBean:生成代理对象
TransactionInterceptor:实现对象的拦截
TransactionAttrubute:事务配置的数据
五、Spring IOC全面认知
在《精通Spring4.x 企业应用开发实战》中对IOC的定义是这样的:
IoC(Inversion of Control)控制反转,包含了两个方面:一、控制,二、反转
我们可以简单认为:
控制指的是:当前对象对内部成员的控制权。
反转指的是:这种控制权不由当前对象管理了,由其他(类,第三方容器)来管理
IOC不够开门见山,于是Martin Fowler提出了DI(dependency injection)来替代IoC,即让调用类对某一接口实现类的依赖关系由第三方(容器或协作类)注入,以移除调用类对某一接口实现类的依赖
使用IOC的好处:
不用自己组装,拿来就用。
享受单例的好处,效率高,不浪费空间。
便于单元测试,方便切换mock组件。
便于进行AOP操作,对于使用者是透明的。
统一配置,便于修改
1.2 IOC容器的原理
从上面就已经说了:IOC容器其实就是一个大工厂,它用来管理我们所有的对象以及依赖关系
原理就是通过Java的反射技术来实现的!通过反射我们可以获取类的所有信息(成员变量、类名等等等)!
再通过配置文件(xml)或者注解来描述类与类之间的关系
我们就可以通过这些配置信息和反射技术来构建出对应的对象和依赖关系了
**Spring容器(Bean工厂)**可简单分成两种:
BeanFactory
这是最基础、面向Spring的
ApplicationContext
这是在BeanFactory基础之上,面向使用Spring框架的开发者。提供了一系列的功能
1.3 IOC容器装配Bean
1.3.1 装配Bean方式
Spring4.x开始IOC容器装配Bean有4种方式:
XML配置
注解
JavaConfig
基于Groovy DSL配置(这种很少见)
总的来说:我们以XML配置+注解来装配Bean的多
1.3.2 依赖注入方式
依赖注入的方式有3种方式:
属性注入-->通过setter()方法注入
构造函数注入
工厂方法注入
总的来说使用属性注入是比较灵活和方便的,这是大多数人的选择
1.3.3 对象之间关系
<bean>对象之间有三种关系:
依赖-->挺少用的(使用depends-on就是依赖关系了-->前置依赖【依赖的Bean需要初始化之后,当前Bean才会初始化】)
继承-->可能会用到(指定abstract和parent来实现继承关系)
引用-->最常见(使用ref就是引用关系了)
1.3.4 Bean的作用域
Bean的作用域:
单例Singleton
多例prototype
与Web应用环境相关的Bean作用域
--reqeust
--session
六、Spring AOP全面认知
我们可以先看看下面这段代码:
我们学Java面向对象的时候,如果代码重复了怎么办?
AOP的理念:就是将分散在各个业务逻辑代码中相同的代码通过横向切割的方式抽取到一个独立的模块中
将这些被我们横切出来的逻辑代码融合到业务逻辑中,来完成和之前(没抽取前)一样的功能!这就是AOP首要解决的问题了
其实,Spring AOP的底层原理就是动态代理!在Java中动态代理有两种方式:
JDK动态代理
CGLib动态代理
那么JDK代理和CGLib代理我们该用哪个呢??在《精通Spring4.x 企业应用开发实战》给出了建议:
如果是单例的我们最好使用CGLib代理,如果是多例的我们最好使用JDK代理
原因:
JDK在创建代理对象时的性能要高于CGLib代理,而生成代理对象的运行性能却比CGLib的低。
看到这里我们就应该知道什么是Spring AOP(面向切面编程)了:将相同逻辑的重复代码横向抽取出来,使用动态代理技术将这些重复代码织入到目标对象方法中,实现和原来一样的功能。这样一来,我们就在写业务时只关心业务代码,而不用关心与业务无关的代码
1.3 AOP的实现者
AOP除了有Spring AOP实现外,还有著名的AOP实现者:AspectJ
AspectJ是语言级别的AOP实现,扩展了Java语言,定义了AOP语法,能够在编译期提供横切代码的织入,所以它有专门的编译器用来生成遵守Java字节码规范的Class文件
1.4 AOP的术语
连接点(Join point):
能够被拦截的地方:Spring AOP是基于动态代理的,所以是方法拦截的。每个成员方法都可以称之为连接点~
切点(Poincut):
具体定位的连接点:上面也说了,每个方法都可以称之为连接点,我们具体定位到某一个方法就成为切点。
增强/通知(Advice):
表示添加到切点的一段逻辑代码,并定位连接点的方位信息。
简单来说就定义了是干什么的,具体是在哪干
Spring AOP提供了5种Advice类型给我们:前置、后置、返回、异常、环绕给我们使用!
织入(Weaving):
将增强/通知添加到目标类的具体连接点上的过程。
引入/引介(Introduction):
引入/引介允许我们向现有的类添加新方法或属性。是一种特殊的增强!
切面(Aspect):
切面由切点和增强/通知组成,它既包括了横切逻辑的定义、也包括了连接点的定义。
1.5 Spring对AOP的支持
Spring提供了3种类型的AOP支持:
基于代理的经典SpringAOP
需要实现接口,手动创建代理
纯POJO切面
使用XML配置,aop命名空间
@AspectJ注解驱动的切面
使用注解的方式,这是最简洁和最方便的
1.6 总结
• AOP的底层实际上是动态代理,动态代理分成了JDK动态代理和CGLib动态代理。如果被代理对象没有接口,那么就使用的是CGLIB代理(也可以直接配置使用CBLib代理)
• 如果是单例的话,那我们最好使用CGLib代理,因为CGLib代理对象运行速度要比JDK的代理对象要快