Spring框架
Spring是一个轻量级的IOC(控制反转)和AOP(面向切面编程)的容器框架,能够为企业级开发提供一站式服务。核心模块是Spring Core,其他模块基于这个核心模块。
Spring的优势
①、解耦合,简化开发
通过Spring提供的IoC容器,我们可以将对象之间的依赖关系交由Spring进行控制,避免硬编码所造成的过度
程序耦合。有了Spring,用户不必再为单实例模式类、属性文件解析等这些很底层的需求编写代码,可以更专注于
上层的应用。
②、AOP编程的支持
通过Spring提供的AOP功能,方便进行面向切面的编程,许多不容易用传统OOP实现的功能可以通过AOP轻
松应付。
③、声明式事务的支持
在Spring中,我们可以从单调烦闷的事务管理代码中解脱出来,通过声明式方式灵活地进行事务的管理,提高
开发效率和质量。
④、方便程序的测试
可以用非容器依赖的编程方式进行几乎所有的测试工作,在Spring里,测试不再是昂贵的操作,而是随手可做
的事情。例如:Spring对Junit4支持,可以通过注解方便的测试Spring程序。
⑤、方便集成各种优秀框架
Spring不排斥各种优秀的开源框架,相反,Spring可以降低各种框架的使用难度,Spring提供了对各种优秀
框架(如Struts、Hibernate、Hessian、Quartz)等的直接支持。
⑥、降低Java EE API的使用难度
Spring对很多难用的Java EE API(如JDBC,JavaMail,远程调用等)提供了一个薄薄的封装层,通过Spring
的简易封装,这些Java EE API的使用难度大为降低。
⑦、Java 源码是经典学习范例
Spring的源码设计精妙、结构清晰、匠心独运,处处体现着大师对Java设计模式灵活运用以及对Java技术的高
深造诣。Spring框架源码无疑是Java技术的最佳实践范例。
Spring的基础概念
Spring的相关依赖
<properties>
<spring.version>5.2.9.RELEASE</spring.version>
</properties>
<!-- spring支持 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.1.10.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
bean对象的三种创建方式
<bean id=”objService” class=”com.zxm.service.ObjServiceImpl”/>
<!--表示根据com.zxm.service.ObjServiceImpl类创建bean对象-->
<bean id=”objService” class=”com.zxm.factory.instanceFactory” factory-method=”getObjService”/>
<!--表示根据com.zxm.factory.instanceFactory 类中的静态方法getObjService的返回值获取bean对象-->
<bean id=” instanceFactory” class=”con.zxm.factory.instanceFactory” />
<bean id=”objService” factory-bean=”instanceFactory” factory-method=”getObjService”/>
<!--表示根据instanceFactory这个类中的getObjService方法的返回值创建bean对象-->
bean的作用范围
Spring框架中默认为单例设计(singleton)
所谓Bean的作用范围其实就是指Spring给我们创建出的对象的存活范围。
在配置文件中通过bean的scope属性指定,基本有五个取值(不同版本spring,作用域略有不同):
singleton(默认): 创建出的实例为单例模式, IOC只创建一次,然后一直存在
- prototype: 创建出的实例为多例模式, 每次获取bean的时候,IOC都给我们重新创建新对象
- request(web) :web项目中,Spring 创建一个 Bean 的对象,将对象存入到 request 域中.
- session (web): web项目中,Spring 创建一个 Bean 的对象,将对象存入到 session 域中.
- globalSession :(用于分布式web开发) 创建的实例绑定全局session对象 .
注意:
- 单例设计时,spring容器加载完成时,对象已经创建,随时准备给用户使用,
- 多例设计时,是在用户使用对象时,spring容器进行创建,
bean对象的生命周期
单例模式下 singleton
创建:容器创建的时候,就创建对象;销毁:容器销毁的时候才销毁对象。
多例模式下 prototype
创建:每一次getBean()调用的时候创建(在使用时创建) 销毁:这个对象的销毁与Spring无关,而是由垃圾回收器处理。
Spring常用的注解
// 用于创建bean对象,的注解 => <bean>
1) @Component:一般都在spring组件上使用
2) @Service:一般都在Service上使用;
3) @Repository:一般都在DAO上使用;
4) @Controller: 用于Action控制器
// 用于注入属性,的注解 => <property>
1) @Autowired: 【自动按照类型注入】,只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,将可以注入成功,当Ioc容器中有两个对象匹配时,此时会再根据变量名进行匹配,如果还无法匹配时,则会报错
2) @Resource: [直接按照bean的id注入,如果找不到,则按照类型注入]
a) name:用于指定bean的id,根据id注入
b) type:用于指定bean的类型,根据类型注入
3) @Qualifier:在按照类中注入的基础之上再按照名称注入,它给类成员注入时不能单独使用,但是在给方法参数注入时可以
a) value: 用于指定注入bean的id
4) @Value: 用于简单数据类型的注入,相当于< property name="" value="" > 。
a) value:用于指定数据的值,它可以使用spring中SpEL表达式
b) SpEL的写法:${ 表达式 }
// 用于改变作用域范围的 =><scope>
1) @Scope:
a) value: 指定范围的取值,常用取值:singleton 单例 prototype 多例
用于指定bean的作用范围,相当于配置文件中的< bean scope="">。
// 和生命周期有关的 =><init-method> 或者 <destory-method>
1) @PreDestroy: 用于指定销毁方法
2) @PostConstruct: 用于指定初始化方法
// 这两个注解标注方法分别在对象的创建之后和销毁之前执行。相当于< bean init-method="init" destroy-method="destory" />
Spring新注解
// 使用@Configuration可以替换spring.xml文件
1) @Configuration:指定当前类为配置类
2) @ComponentScan:用于通过注解指定spring在创建容器时要扫描的包
a) value:指定创建容器时要扫描的包,相当于<context: component- scan base-package= "cn. mLdn"/>
3) @Bean:用于把当前方法的返回值作为bean对象,交由spring容器管理
a) name:用于指定bean的id,默认值为当前方法的名称
b) 细节:当我们使用注解配置方法时,如果方法有参数,spring框架会去容器中查找有没有可用的bean对象,查找方式和@Autowired注解作用一样
4) @Import(类全名): 用于导入其他(相当于副配置类)的配置类
a) value: 用于指定其他配置类,当我们使用Impoer的注解后,导入的都是子配置类
5) @PropertySource(“classpath: …..”):用于指定properties文件的位置
a) value:指定文件的名称和路径
b) 关键字:classpath 表示类路径下
6) @EnableTransactionManagement 开启spring声明式事务支持
自定义类配置文件
@Configuration
@ComponentScan(value = "cn.mldn.lxh",
excludeFilters = @ComponentScan.Filter(type = FilterType.ANNOTATION,
value = Controller.class))
@EnableTransactionManagement
@Import({JdbcConfig.class,MybatisConfig.class})
@Configuration
@PropertySource("classpath:jdbc.properties")
public class SpringConfig {
注解与xml配置总结
• 注解和xml是Spring提供的两种配置形式,所实现的功能完全一样。
• 注解的好处是配置简单,xml的好处是修改配置不用改动源码,企业开发中两种方式灵活使用。
• 注意在注解使用前不要忘记添加包扫描< context:component-scan base-package="" />
• 注解和xml配置对应关系如下表格:
Ioc扫描并注册组件总结:
- 在spring配置文件中添加
< context:component-scan base-package="" />
开启包扫描,扫描包中是否有@Component
等注解,如果有则将标注了此注解的类注册进Ioc容器中 - 如果扫描到有
@Autowired
等注解,则根据类型,或者bean id 将注册进Ioc容器中的组件注入,标注了该注解的属性或对象中、
Spring相关API
BeanFactory
- BeanFactory是 Spring 的"心脏"。
- BeanFactory是 IOC 容器的核心接口,它定义了IOC的基本功能。
- Spring使用它来配置文档,管理bean的加载,实例化并维护bean之间的依赖关系,负责bean的生命周期。
ApplicationContext
- ApplicationContext由BeanFactory派生而来,可以比喻为Spring的躯体。
- ApplicationContext在BeanFactory的基础上添加了很多功能:
(1) 支持了aop功能和web应用
(2) MessageSource, 提供国际化的消息访问
(3) 通过配置来实现BeanFactory中很多编码才能实现的功能
两者区别: - beanFactory主要是面向Spring框架的基础设施,也就是供spring自身内部调用,而Applicationcontext 主要面向Spring的使用者。
- BeanFactroy在第一次使用到某个Bean时(调用getBean()),才对该Bean进行加载实例化,而ApplicationContext是在容器启动时,一次性创建并加载了所有的Bean。
三个重要实现类
三个实现类都是用来读取配置文件,然后初始化Spring的容器的,区别在于读取配置文件的位置不同。
- ClassPathXmlApplicationContext: 从classpath目录读取配置文件
- FileSystemXmlApplicationContext: 从文件系统或者url中读取配置文件
- AnnotationConfigApplicationContext:当我们使用注解配置容器对象时,需要使用此类来创建 spring 容器,它用来读取注解。
// 根据classpath获取spring.xml配置文件
ClassPathXmlApplicationContext cs = new ClassPathXmlApplicationContext("spring.xml");
// 从容器中获取指定的bean对象
TestService testService = (TestService) cs.getBean("testService");
// 使用bean对象
System.out.println(testService);
Spring整合Junit4进行测试
1、引入坐标
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.4.RELEASE</version>
<scope>test</scope>
</dependency>
2、在测试类上指定运行器
@RunWith(SpringJUnit4ClassRunner.class) // 指定测试运行器
public class AccountServiceTest {
xxxx
}
3、指定配置文件
@RunWith(SpringJUnit4ClassRunner.class) // 指定测试运行器
@ContextConfiguration("classpath:applicationContext.xml") // 指定配置文件
public class AccountServiceTest {
xxxx
}
4、使用测试
使用spring整合Junit4进行测试
@RunWith(SpringJUnit4ClassRunner.class)
加载spring配置文件
@ContextConfiguration(classes = SpringConfigration.class)
public class AccountServiceTest2 {
@Autowired
private AccountService accountService;
@Test
public void testSave() {
Account a = new Account();
a.setAname("王五");
a.setBalance(456.67);
System.out.println("save.result=" + accountService.save(a));
}
}
IoC:控制反转
DI:依赖注入(重点)
DI依赖注入和IOC控制反转是相同的概念,Java类中依赖的对象,容器进行创建,注入到依赖中,提高程序的维护性
构造方法注入
1、创建一个对象,和一个有参构造
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private String tittle;
private Double price;
public void bookDy(){
System.out.println(this.hashCode());
}
}
2、在spring.xml文件中创建bean对象
<!-- 指定的类对象创建对象,默认使用无参构造方法-->
<bean id="book" class="cn.mldn.lxh.Book">
<!-- 指定使用类对象的有参构造,创建对象-->
<constructor-arg name="price" value="200"/>
<constructor-arg name="tittle" value="西游记"/>
</bean>
3、使用创建的bean对象
@Test
public void test01(){
ClassPathXmlApplicationContext context =
new ClassPathXmlApplicationContext("spring.xml");
Book book = (Book) context.getBean("book");
System.out.println(book.toString());
}
属性注入
- 普通属性:
<property name=”属性名” value=”属性值”/>
- 引用属性:
<property name=”属性名” ref=”引用的bean ID”/>
Property标签是通过类中定义的set()方法进行赋值的,类中必须为字段定义set()和get()方法
1、创建两个类对象
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Book {
private String tittle;
private Double price;
public void bookDy(){
System.out.println(this.hashCode());
}
}
@Data
@Accessors
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String userName;
private String passWord;
private Book book;
private String[] str;
private List<String> list;
private Set<String> set;
private Map<String,String> map;
private Properties properties;
public void userDy(){
System.out.println(this.toString());
}
}
2、在spring.xml文件中配置bean的属性注入
!-- 指定的类对象创建对象,默认使用无参构造方法-->
<bean id="book" class="cn.mldn.lxh.Book">
<!-- 指定使用类对象的有参构造,创建对象-->
<constructor-arg name="price" value="200"/>
<constructor-arg name="tittle" value="西游记"/>
</bean>
<bean id="user" class="cn.mldn.lxh.User">
<property name="id" value="1"/>
<property name="userName" value="张三"/>
<property name="passWord" value="123456"/>
<property name="book" ref="book"/>
<property name="str">
<array>
<value>a</value>
<value>b</value>
</array>
</property>
<property name="set">
<set>
<value>AA</value>
<value>BB</value>
</set>
</property>
<property name="map">
<map>
<entry key="GG" value="gg"/>
<entry key="HH" value="hh"/>
</map>
</property>
<property name="properties">
<props>
<prop key="II">ii</prop>
<prop key="JJ">jj</prop>
</props>
</property>
</bean>
开启Ioc 的Annotation支持
- 在applicationContext.xml文件中为Annotation配置添加命名空间
- 配置Annotation的使用范围
< context: annotation- config/>
表示启动Annotation配置支持
2)<context: component- scan base-package= "cn. mldn"/>
表示在Spring容器启动时扫描“cn.mldn” 包下的所有程序类,
Annotation的三种注解
功能都一样,只是单词不同
@Component
:主要用于定义组件,一般都在Action上使用@Service
:主要用于定义组件,一般都在Service.上使用;@Repository
:主要用于定义组件,一般都在DAO上使用;
注意:这三种注解都表示将,注解的类注册进spring容器中相当如在applicationContext.xml文件中配置标签
现在如果使用了注解定义组件,那么名称默认情况下就是类名称的结构形式:。
- AdminDAOImpl, 则访问此组件的名称就是“adminDAOmpl"; 。
- RoleDAOImpl, 则访问此组件的名称就是“roleDAOImpl”
AOP:事务处理
AOP概念
- AOP能够为多个没有相互关联的类,提供通用服务,能够实现代码解耦,方便维护,让每个类只关注自己的核心业务,AOP是一种编程技术,
不是Spring特有的 - AOP基于动态代理技术,静态代理只能代理一种业务,动态代理可以代理多种业务
- 动态代理有JDK动态代理和CGLib动态代理两种,JDK动态代理中的被代理对象必须实现过接口,CGLib动态代理通过继承完成,类不能被final修饰
- 前置通知(Before Advice): 在某一操作执行之前负责处理;。
- 后置通知(After Advice): 在某一 操作执行之后负责处理,但是后置通知需要考虑以下几种子类:
(1) 后置返回通知(After Returning Advice): 负责处理返回结果的时候进行拦截;。
(2) 后置异常通知(After Throwing Advice):当出现异常之后进行拦截;
(3) 后置最终通知(After Finally Advice):不管执行到最后是否出现异常,都会执行拦截;。 - 环绕通知(Around Adivce):可以在具体的执行操作之前、之后、异常出现处理等地方任意编写,可以说是。包含了如上的几种通知的综合体.
AOP => 环绕通知
AOP主要用于业务层,在业务层调用数据层前执行数据库连接操作和在调用后关闭数据库连接等其他的操作
步骤
- 在applicationContext.xml文件中配置AOP支持,添加AOP命名空间
- 配置事务处理类,TxManger(主要用于事务的提交和回滚)
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@Component
public class TxManger {
// 声明一个本地线程属性变量
private ThreadLocal<Connection> th = new ThreadLocal<>();
@Autowired
private DataSource dataSource;
//获取连接
public Connection getConnection() {
Connection connection = th.get();
try {
if (connection == null) {
connection = dataSource.getConnection();
th.set(connection);
}
return connection;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
//开启
public void begin() {
try {
//关闭自动提交,相当于开启了事务
getConnection().setAutoCommit(false);
} catch (SQLException e) {
e.printStackTrace();
}
}
//提交
public void commit() {
try {
getConnection().commit();
} catch (SQLException e) {
e.printStackTrace();
}
}
//回滚
public void rollback() {
try {
getConnection().rollback();
} catch (SQLException e) {
e.printStackTrace();
}
}
//关闭
public void close() {
try {
getConnection().close();
//要从集合中移除连接对象
th.remove();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
- 定义切面处理类(日志增强类):主要用于定义在指定切入点方法前后执行的方法,并将此类对象加入Ioc容器中,这样在applicationContext.xml文件中才可以引用此类
注意:在切面处理类中定义环绕处理方法时,方法参数只能定义为ProceedingJoinPoint类型 , 通过此类型可以取得全部的提交参数信息。
import cn.mldn.lxh.config.TxManger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
public class Logger {
// 获取事务管理对象
@Autowired
private TxManger txManger;
// 定义环绕通知处理
public Object serviceAround(ProceedingJoinPoint point){
// 开启事务
txManger.begin();
try {
System.out.println("执行的方法是:"+point.getSignature().getName());
System.out.println("执行方法的参数是:"+ this.getString(point.getArgs()));
// 执行业务,并获取方法的返回值
Object proceed = point.proceed();
// 提交事务
txManger.commit();
// 将返回值进行返回,可以进行修改后进行返回
return proceed;
}catch (Throwable t){
System.out.println("出现异常");
t.printStackTrace();
// 事务回滚
txManger.rollback();
}finally {
// 关闭事务
txManger.close();
}
return null;
}
// 用来处理环绕通知中的,方法参数处理
private String getString(Object[] args) {
String result = "[";
for(Object o : args) {
if(o instanceof Integer) {
result += (", " + ((Integer) o));
} else if (o instanceof String) {
result += (", " + ((String) o));
} else if(o instanceof Double) {
result += (", " + ((Double) o));
}
}
result += "]";
return result;
}
}
环绕通知理解
- 定义引用切面:配置AOP切入点和切入面之间的关系:配置切入点,并指定切入点的前后执行的切入面方法
<bean id="logger" class="cn.mldn.lxh.log.Logger"/>
<aop:config>
<aop:pointcut id="pt" expression="execution(* cn.mldn.lxh.service.impl.BookServiceImpl.*(..))"/>
<aop:aspect ref="logger">
<aop:around method="serviceAround" pointcut-ref="pt"/>
</aop:aspect>
</aop:config>
<!-- proxy-target-class属性值决定是基于接口的还是基于类的代理被创建。
如果proxy-target-class 属性值被设置为true,那么基于类的代理将起作用(这时需要cglib库)。
如果proxy-target-class属值被设置为false或者这个属性被省略,那么标准的JDK 基于接口的代理-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
定义切入点表达式
切入点表达式:主要用来匹配需要切入的具体类中的具体方法,这样就可以在applicationContext.xml文件中配置这个方法的AOP处理(在执行执行这个方法之前执行什么方法,执行之后执行上面方法)
execution(修饰符匹配 返回值匹配 操作类匹配.方法名匹配(参数匹配) 抛出异常匹配)
例子:execution(* cn.mldn.*.*(..))
- 修饰符:public | private(可以省略)
- *:表示任意的返回类型
- cn.mldn:表示在cn.mldn这个包中,cn.mldn…表示在任意子包下的
*.*
:表示任意类的任意方法- (…):表示任意参数类型和个数
开启AOP的Annotation支持
- 在ApplactionContext.xml文件中添加,开启AOP的Annotation配置
- Annotation配置AOP注解
(1)@Aspect
使用此注解标记的类为切面处理类
(2)@Before(value=" execution(切入点表达式)")
使用此注解标记的切面类方法为指定的切入点方法前执行方法
(3)@Before(value=" execution(切入点表达式) and args(param)" , argNames(param))
注解带参数的切面处理类方法
注意:在使用注解版的AOP,最终通知,可能会在后置通知,和异常通知之前执行,因此在使用注解版的AOP最好使用环绕通知
@Around
环绕通知
@Before
前置通知 => 开启事务
@AfterReturning
后置通知 => 提交事务
@AfterThrowing
异常通知 => 事务回滚
@After
最终通知 => 关闭连接
声明式事务
基于xml配置
<!-- (1)配置druid数据源-->
<bean id="druid_DataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://192.168.56.106:3306/spring_day03_aop?characterEncoding=UTF-8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
</bean>
<!-- (2)实例化spring提供的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druid_DataSource"/>
</bean>
<!-- (3)配置事务的通知-->
<tx:advice id="tx" transaction-manager="transactionManager">
<tx:attributes>
<!-- 配置事务作用的对象:以find开头的方法,是以只读的方式打开数据库
REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到这个事务中。一般的选择(默认值)
SUPPORTS: 支持当前事务,如果当前没有事务,就以非事务方式执行(没有事务)-->
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<!-- (4)配置切面-->
<aop:config>
<!-- 配置切入点-->
<aop:pointcut id="pt" expression="execution(* cn.mldn.lxh.service.*(..))"/>
<!-- 切面组装:建立切入点和事务通知的对应关系-->
<aop:advisor advice-ref="tx" pointcut-ref="pt"/>
</aop:config>
tx:method配置说明
基于Annotation的声明式事务
<!-- (1)配置druid数据源-->
<bean id="druid_DataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url"
value="jdbc:mysql://192.168.56.106:3306/spring_day03_aop?characterEncoding=UTF-8&useSSL=false"/>
<property name="username" value="root"/>
<property name="password" value="123456"/>
<property name="defaultAutoCommit" value="true" />
</bean>
<!-- (2)实例化spring提供的事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="druid_DataSource"/>
</bean>
<!-- (3)添加事务管理器驱动,开启注解模式的事务管理-->
<tx:annotation-driven transaction-manager="transactionManager"/>
@Transactional(timeout=30) //默认是30秒
- 在类上使用@Transactional注解后,标示类中所有方法都进行事物处理
- 在方法上使用@Transactional(propagation =Propagation.NOT_SUPPORTED)注解后,表示此方法不使用事务
@Service
public class BookServiceImpl implements Iservice<BookInfo> {
@Autowired
RoomServiceImpl roomService;
// 开启事务,当发生异常或者手动抛出异常时进行回滚
@Transactional(rollbackFor = Exception.class)
@Override
public boolean insert(Test test) throws Exception {
roomService.insert(test);
int i = 10 / 0; // 发生异常,自动进行回滚
return false;
}
注意的几点:
@Transactional
只能被应用到public方法上, 对于其它非public的方法,如果标记了@Transactional也不会报错,但方法没有事务功能.- 用 spring 事务管理器,由spring来负责数据库的打开,提交,回滚.默认遇到运行期例外(throw new RuntimeException(“注释”);)会回滚,即遇到不受检查(unchecked)的例外时回滚;而遇到需要捕获的例外(throw new Exception(“注释”);)不会回滚,即遇到受检查的例外(就是非运行时抛出的异常,编译器会检查到的异常叫受检查例外或说受检查异常)时,需我们指定方式来让事务回滚要想所有异常都回滚,要加上
@Transactional(rollbackFor={Exception.class,其它异常})
如果让unchecked例外不回滚:@Transactional(notRollbackFor=RunTimeException.class)
事务注意事项
注意:除零异常可以触发回滚,手工抛出的异常不能触发。
spring aop 异常捕获原理:被拦截的方法需显式抛出异常,并不能经任何处理,这样aop代理才能捕获到方法的异常,才能进行回滚,默认情况下aop只捕获runtimeexception的异常,但可以通过
<tx:method name="upd*" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
配置来捕获特定的异常并回滚,换句话说在service的方法中不使用try catch 或者在catch中最后加上throw new runtimeexcetpion(),这样程序异常时才能被aop捕获进而回滚
解决方案:
方案1.例如service层处理事务,那么service中的方法中不做异常捕获,或者在catch语句中最后增加throw new RuntimeException()语句,以便让aop捕获异常再去回滚,并且在service上层(webservice客户端,view层action)要继续捕获这个异常并处理
方案2.在service层方法的catch语句中增加:TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
语句,手动回滚,这样上层就无需去处理异常(现在项目的做法)
<tx:method name="transfer*" read-only="false" propagation="REQUIRED" rollback-for="java.lang.Exception"/>
怎么理解Ioc,aop
Ioc(控制反转)
理解:当Spring的初始化的时候,会读取配置文件中的诸多bean,根据bean的class的值寻找对应的Class字节码文件,然后通过反射技术,创建出一个个对象,创建的对象会被存放到Ioc容器内部的一个Map结构中,等待被使用,当我们需要使用具体的对象时就无须自己创建,而是直接从Spring的IOC容器中取。
IOC(Inversion of Control,控制反转)不是什么技术,而是一种设计思想。它的目的是指导我们设计出更加松耦合的程序。
- 控制:指的是控制权,在java中可以简单理解为 对象的控制权限(比如对象的创建、销毁等权限) 。
- 反转:指的是将对象的控制权由原来的 程序员在类中主动控制 反转到 由Spring容器来控制。
举个例子:找对象 - 传统方式:自己找,想要啥样的自己去大街上找(new);
- IOC方式 :婚介所,首先人们将自己的信息注册到婚介所。然后,等你想要对象的时候,婚介所就会帮你找 到,然后给你送来。
IoC(控制反转):把用户创建对象的权限赋予spring容器,spring创建对象,用户使用对象这就是IoC
注意:一个接口,只要一个实现类,在业务层进行使用时,按照接口进行注入,当想换一个子类时,只需要将子类进行替换即可,而业务层的接口注入不用进行修改这个就是Ioc
Aop(事务管理)
Aop面向,切面编程的思想将,原本OOP面向对象的思想的耦合现象,进行了更深的优化,将原本复杂的业务逻辑,以及事务管理,等进行了分离,利用切入点,与切入面的思想,将原本公共代码进行了剥离,使其业务层,只关注与其具体的业务逻辑,解决了oop思想编程的耦合现象。
怎么理解Ioc(控制反转):
主要作用:解耦合
例子:
(1)以前,是我们自己在类中new对象进行使用,是用户自己控制对象的生命周期,而从Ioc容器中获取对象,是Ioc容器进行管理,而用户只需要进行使用即可,而不需要关注对象本身
(2)当一个controller类,需要调用一个service时,需要自己在类中new具体的service一个对象,这个方式创建的对象,以后想要换一个service进行使用,这时就需要进行修改代码,而从Ioc容器中获取对象,则直接面向接口进行注入,不需要修改代码,这样就实现了解耦合
怎么理解aop:(事务管理,日志管理)
主要作用:事务管理
(1)以前:在编写业务时,在写业务代码后,还需要考虑事务,以及日志的管理,会编写大量的重复代码,增大了编码难度,
(2)使用aop:业务层只需要关注具体的业务,aop在业务的关注点上切入事务管理,以及日志的管理,大大减少了编码难度