1.概述
1.1 什么是Spring
Spring是一个分层的Java SE/EE full-stack(一站式)轻量级开源框架,它以IOC和AOP为内核,使用基本的JavaBean来完成以前只可能由EJB完成的工作,取代了EJB臃肿、低效的开发模式。
Spring致力于Java EE应用各层的解决方案,在表现层(它提供了Spring MVC以及与Struts框架的整合功能;在业务逻辑层可以管理事务、记录日志等;在持久层可以整合MyBatis、Hibernate、JdbcTemplate等技术。
1.2 优点
- 非侵入式设计
- 方便解耦、简化开发
- 支持AOP
- 支持声明式事务处理
- 方便事务的测试(提供了对Junit的支持,额可以通过注解@Test方便地测试Spring程序)
- 方便集成各种优秀框架
- 降低Java EE API的使用难度
1.3 Web项目ApplicationContext容器的实例化
Web项目ApplicationContext容器的实例化由web服务器万册灰姑娘。在web.xml中添加如下配置
<!--指定Spring配置文件的位置,有多个配置文件时,以逗号分隔-->
<context-param>
<param-name>contextConfigLocation</param-name>
<!--Spring将加载spring目录下的applicationCOntext.xml文件-->
<param-value>
classpath:spring/applicationContext.xml
</param-value>
</context-param>
<!--指定以ContextLoaderListener方式启动Spring容器-->
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
2.IOC(控制反转)
2.1 什么是IOC
在传统模式下,一个对象(调用者)需要调用另一个对象(被调用者,即被依赖对象)时,使用“new 被调用者”这种方式来创建对象。但是这种方式会导致调用者与被调用者之间的耦合性增加,不利于后期项目的升级和维护。
在IOC的思想下,对象的实例不再由调用者来创建,而是由容器(如Spring容器)来创建,容器会负责控制程序之间的关系,而不是由调用者的程序代码直接控制。这样,控制权由代码转移到容器,控制权发生了反转,这就是IOC(控制反转)
2.2 IOC与DI(依赖注入)
IOC是一种设计思想,而实现这种思想的两种方式包括依赖注入和依赖查找。
依赖注入:容器负责将被依赖对象赋值给调用者的成员变量,这相当于为调用者注入了它以来的实例,这就是依赖注入。
2.3 DI的实现方式
- 1.属性setter方法注入
- 2.构造方法注入
2.4 Bean
2.4.1 什么是Bean
Bean被实例的、组装的(及被Spring管理的)Java对象
2.4.2 Bean的配置
一般通过XML文件来注册并管理Bean之间的关系。XML配置文件的根元素是<beans>,<beans>中可包含多个 <bean>元素。
<?xml version="1.0" encoding="UTF-8"?>
<!-Spring XML配置文件的根元素是<beans>-->
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--一个<beans>元素可包含多个<bean>子元素-->
<!--
属性含义:
1.id:Bean的唯一标识符,Spring容器对Bean的配置、管理都通过该属性来完成
2.name:Spring容器同样可以通过此属性对容器中的Bean进行配置和管理,可以为Bean指定多个名称,中间用 逗号或分号分隔
如果在Bean中未指定id和name属性,则Spring会将class指当作id使用
3.class:该属性指定Bean的具体实现类,它必须是一个完整的类名(类全限定名=包名+类名)
4.scope:该属性用来设定Bean实例的作用域。默认值为"singleton"。后续会由详细解释
-->
<bean id="duke" name="duke" class="com.springinaction.springidol.Juggler" scope="singleton">
<!-- constructor-arg:通过构造方法设置属性值 -->
<constructor-arg value="15"></constructor-arg>
</bean>
<bean id="saxphone" class="com.springinaction.springidol.saxphone"></bean>
<bean id="piano" class="com.springinaction.springidol.piano"></bean>
<!-- 为集合配置bean -->
<bean id="hank" class="com.springinaction.springidol.OneManBand">
<!--<property>:用于调用Bean中的setter方法完成属性赋值,从而完成以来注入。
name属性:指定Bean实例中的相应属性名
ref或value属性:指定参数值
-->
<property name="instruments">
<list>
<ref bean="piano" />
<ref bean="saxphone" />
</list>
</property>
<property name="instruments2">
<map>
<!--entry:用于设置map的一个键值对-->
<entry key="piano" value-ref="piano"></entry>
<entry key="saxphone" value-ref="saxphone"></entry>
</map>
</property>
</bean>
</beans>
实例化Bean有三种方式:构造器实例化(通常情况下使用该中方式)、静态工厂实例化和实例工厂实例化。
2.4.3 Bean的作用域
使用scope可以指定Bean的作用域。
- singleton:单例,该类型的Bean在容器中只有一个实例,无论有多少个Bean引用它,始终指向同一个对象。这是Spring容器默认的作用域。
- prototype:原型,每次通过Spring容器获取的prototype定义的Bean时,容器都将创建一个新的Bean实例。
以上为常用的作用域,还有其他5种作用域:request、session、globalSession、application、websocket
2.4.4 Bean的声明周期
Spring容器可以管理singleton作用域的Bean的生命周期。而prototype作用域的Bean,Spring只负责创建,当创建了实例后,Bean的实例就交给客户端代码来管理,容器不再追踪其声明周期。
Bean的生命周期流程:
- 1.实例化Bean
- 2.设置属性值:利用依赖注入完成Bean中所有属性值的配置注入
- 3.调用BeanNameAware的setBeanName()方法
- 4.调用BeanFactoryAware的setBeanFactory()方法
- 5.调用ApplicationContextAware的setApplicationContext()方法
- 6.调用BeanPostProcessor的预初始化方法
- 7.调用InitializingBean的afterPropertiesSet()方法
- 8.调用订制的初始化方法
- 9.调用BeanPostProcessor的后初始化方法
- 10.Spring缓存池中准备就绪(singleton),将准备就绪的Bean交给调用者(prototype)
- 11.Spring中Bean的销毁
2.2.5 Bean的装配方式
Bean的装配方式(Bean依赖注入的方式)有基于XML的装配、基于注解的装配和自动装配等。
基于Annotation(注解)的装配
注解 | 作用 |
---|---|
@Componnent | 一个泛化的概念,仅仅代表一个组件(Bean),可以作用在任何层次 |
@Repository | 用于将数据访问层的类表示为Spring的Bean |
@Service | 用于将业务层的类表示为Spirng的Bean |
@Controller | 用于将控制层的类表示为Spring的Bean |
@Autowired | 通常用于标注在变量上,完成Bean的自动装配。默认按照Bean的类型进行装配 |
@Resource | 与Autowired类似,但默认按照Bean的实例名称进行装配 |
@Qualifier | 配合@Autowired使用,改变装配方式 |
//配置文件需要加入以下几句
<!--使用context命名空间,并在配置文件中开启相应的注解处理器-->
<context:annotation-config />
<!--对包路径下的所有Bean文件进行扫描-->
<context:component-scan base-package="需要装配的Bean所在包路径" />
3. AOP
3.1 什么是AOP
AOP(Aspect-Oriented Programming)表示面向切面编程,是对OOP的一种补充。
在传统的使用OOP思想进行事务处理、日志记录等操作时,一般通过组合或者继承的方法来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分担到各个方法中。这样,如果想要关闭或者修改某个功能,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。
AOP思想就是解决这类问题,AOP采取横向抽取机制,嫁给你分散在各个方法中的重复代码提取出来,然后再册灰姑娘徐编译或运行时,再将这些提取出来的代码应用到需要执行的地方。
3.2 AOP相关术语
- Aspect(切面):指封装的用于横向插入系统功能(如事务、日志等)。该类要被Spring容器识别为切面,需要在配置文件中通过<bean>元素指定。
- Joinpoint(连接点):指对象的一个操作,例如方法的调用或异常的抛出。在Spirng AOP容器中,连接点就是指方法的调用。
- Pointcut(切入点):切面与程序流程的交叉点,即那些需要处理的连接点。通常在程序中,切入点指的是类或者方法名。
- Advice(通知/增强处理):在定义好的切入点处所有执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。
- Target Object(目标对象):所有被通知的对象,也称为被增强对象。
- Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。
- Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。
3.3 AOP框架
3.3.1 Spring AOP
动态代理指由AOP框架动态生成一个对象,该对象可以作为目标对象使用。Spring中的AOP代理,由JDK动态代理和CGLIB代理两种方式。
通知类型
- 环绕通知:在目标方法执行前后实施增强,可以用于日志、事务管理等功能
- 前置通知:在目标方法执行前前实施增强,可以用于权限管理等功能
- 后置通知:在目标方法执行后实施增强,可以用于关闭流、上传文件、删除临时文件等功能
- 异常通知:在目标方法抛出异常后实施增强,可以用于异常记录日志等功能
- 引介通知:在目标类中加入一些新的方法和属性。
3.3.2 AspectJ
AspectJ是一个基于JAVA语言的AOP框架,Spring在2.0后引入了对它的支持,并建议使用AspectJ开发AOP。
使用AspectJ实现AOP有两种方式,基于XML的声明式AspectJ和基于注解的声明式AspectJ。
//基于XML
<?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:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
<!--目标类 -->
<bean id="customerDao" class="com.mengma.dao.CustomerDaoImpl" />
<!--切面类 -->
<bean id="myAspect" class="com.mengma.aspectj.xml.MyAspect"></bean>
<!--AOP 编程 -->
<aop:config>
<aop:aspect ref="myAspect">
<!-- 配置切入点,通知最后增强哪些方法 -->
<aop:pointcut expression="execution ( * com.mengma.dao.*.* (..))"
id="myPointCut" />
<!--前置通知,关联通知 Advice和切入点PointCut -->
<aop:before method="myBefore" pointeut-ref="myPointCut" />
<!--后置通知,在方法返回之后执行,就可以获得返回值returning 属性 -->
<aop:after-returning method="myAfterReturning"
pointcut-ref="myPointCut" returning="returnVal" />
<!--环绕通知 -->
<aop:around method="myAround" pointcut-ref="myPointCut" />
<!--抛出通知:用于处理程序发生异常,可以接收当前方法产生的异常 -->
<!-- *注意:如果程序没有异常,则不会执行增强 -->
<!-- * throwing属性:用于设置通知第二个参数的名称,类型Throwable -->
<aop:after-throwing method="myAfterThrowing"
pointcut-ref="myPointCut" throwing="e" />
<!--最终通知:无论程序发生任何事情,都将执行 -->
<aop:after method="myAfter" pointcut-ref="myPointCut" />
</aop:aspect>
</aop:config>
</beans>
基于注解的声明式AspectJ
注解 | 描述 |
---|---|
@Aspect | 定义一个切面 |
@Pointcut | 用于定义一个切入点表达式。表达式格式为execution(* com.*.*(…)),excution(返回值 包名.类名.方法名(参数)),*代表统配。在使用时还需定义一个返回指为void,且方法体为空的普通的方法。 |
@Before | 定义前置通知。 |
@AfterReturning | 定义后置通知 |
@Around | 定义环绕通知 |
@AfterThrowing | 定义异常通知来处理程序中未处理的异常 |
@After | 定义最终final通知,不管是否异常,该通知都会执行 |
//案例
package com.itheima.aspectj.annotation;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
/**
* 切面类,在此类中编写通知
*/
@Aspect
@Component
public class MyAspect {
// 定义切入点表达式
@Pointcut("execution(* com.itheima.jdk.*.*(..))")//对象类所在位置
// 使用一个返回值为void、方法体为空的方法来命名切入点
private void myPointCut() {
}
// 前置通知
@Before("myPointCut()")
public void myBefore(JoinPoint joinPoint) {
System.out.println("前置通知 : 模拟执行权限检查...,");
System.out.println("目标类是:" + joinPoint.getTarget());
System.out.println(",被植入增强处理的目标方法为:" + joinPoint.getSignature().getName());
}
//后置通知
@AfterReturning("myPointCut()")
public void myAfterReturning(JoinPoint joinPoint) {
System.out.println("后置通知:模拟记录日志...,");
System.out.println("被植入增强处理的方法为:");
}
//环绕通知
@Around("myPointCut()")
public Object myAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕开始:执行目标方法之前,模拟开启事务...");
// 执行当前目标方法
Object obj = proceedingJoinPoint.proceed();
// 结束
System.out.println("环绕结束:执行目标方法之后,模拟关闭事务...");
return obj;
}
// 异常通知
@AfterThrowing(value="myPointCut()",throwing="e")
public void myAfterThrowing(JoinPoint joinPoint, Throwable e) {
System.out.println("异常通知:" + "出错了" + e.getMessage());
}
// 最终通知
@After("myPointCut()")
public void myAfter() {
System.out.println("最终通知:模拟方法结束后的释放资源...");
}
}
同时需要在spring核心配置文件中开启注解扫描,启动注解声明式AspectJ支持
<!--启动基于注解的声明式AspectJ支持-->
<aop:aspectj-autoproxy />
4.Spring的数据库开发(包含事务管理)
针对数据库的操作,Spring框架提供了JdbcTemplate类,但是数据库操作一般交由MyBatis来做,Spring只进行数据源的配置和事务管理。
4.1 配置数据源等
<!--配置数据源-->
<bean id="dataSource" class="org.apache.commons.dbcp2.BasicDataSource">
<!--数据库驱动-->
<property name="driverClassName" value=""/>
<!--连接数据库的url-->
<property name="rul" value=""/>
<!--连接数据库的用户名-->
<property name="username" value=""/>
<!--连接数据库的密码-->
<property name="password" value=""/>
<!--最大连接数-->
<property name="maxTotal" value=""/>
<!--最大空闲连接-->
<property name="maxIdle" value=""/>
<!--初始化来凝结数-->
<property name="initialSize" value=""/>
</bean>
<!--配置事务管理器,依赖于数据源-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager" />
4.2 事务管理
4.2.1 事务管理的核心接口
- PlatformTransactionManager:Spring提供的平台事务管理器,主要用于管理事务。提供了如下方法
- getTransaction(TransactionDefinition definition):获取事务状态信息
- commit(TransactionStatus status):提交事务
- rollback(TransactionStatus status):回滚事务
- TransactionDefinition:事务定于(描述)的对象,该对象中定义了事务规则,并提供了获取事务相关信息的方法
- String getName():获取事务对象名称
- int getIsolationLevel():获取事务的隔离级别
- int getPropagationBehavior():获取事务的传播行为
- int getTimeout():获取事务的超时时间
- boolean isReadOnly():获取事务是否只读
- TransactionStatus:事务的状态,它描述了某一时间点上事务的状态信息。
- void flush():刷新事务
- boolean hasSavepoint():获取是否存在保存点
- boolean isCompleted():获取事务是否完成
- boolean isNewTransaction():获取是否是新事务
- boolean isRollbackOnly():获取是否回滚
- void setRollbackOnly():设置事务回滚
4.2.2 事务管理的方式
Spring中的事务管理分为编程式事务管理和声明式事务管理。声明式事务管理是通过AOP技术实现的事务管理,其主要思想是将事务管理作为一个“切面”代码单独编写,然后AOP技术将事务管理的“切面”代码织入到业务目标类中。
4.2.3 声明式事务管理
声明式事务管理由两种实现方式,基于XML的方式和基于Annotation的方式。
基于注解的方式
注解为**@Transactional**,可以标注在类或者方法上,标注在类上表示对类中所有方法起作。注解可配置参数如下:
参数 | 描述 |
---|---|
value | 指定需要使用的事务管理器,默认为"",别名为"transactionManager" |
transactionManager | 指定事务的限定字符,默认为"" |
isolation | 指定事务的隔离级别,默认为底层事务的隔离级别 |
noRollbackFor | 指定遇到特定异常时强制不回滚事务 |
noRollbackForClassName | 指定遇到特定的多个异常时强制不回滚事务。属性值可以指定多个异常类名 |
propagation | 指定事务的传播行为,默认为Propagation.REQUIRED |
read-only | 指定事务的是否只读,默认为false |
rollbackFor | 指定遇到特定异常时强制回滚事务 |
rollbackForClassName | 指定遇到特定的多个异常时强制回滚事务。属性值可以指定多个异常类名 |
timeout | 指定事务的超时时长 |
基于注解的方式进行事务管理需要在配置文件中注册事务驱动,可见上面配置文件。