Spring 不排斥各种优秀的开源框架,相反 Spring 可以降低各种框架的使用难度,Spring提供了对各种优秀框架(如 Struts,Hibernate、MyBatis)等的直接支持。简化框架的使用。Spring 像插线板一样,其他框架是插头,可以容易的组合到一起。需要使用哪个框架,就把这个插头放入插线板。不需要可以轻易的移除。
<bean>
加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
创建类:接口,实现类,没有接口的类
public interface SomeService {
void doSome();
}
public class SomeServiceImpl implements SomeService{
@Override
public void doSome() {
System.out.println("SomeServiceImpl 启动");
}
}
创建spring的配置文件,使用
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--xsd 是约束文件的扩展名 xsd 的约束文件功能强,验证比较全面-->
<!--声明java对象交给Spring创建和管理
class:类的全限定名称,不能是接口(Spring使用反射创建对象)
id:自定义的对象名称,要求是唯一值。表示在Spring中的对象名称,
通过这个名称可以从Spring中找到对象。获取对象
对象是放入到Spring的容器(Map<id,对象>)
-->
<bean id="someService" class="com.test.service.SomeServiceImpl"></bean>
<!--<bean>等同于SomeService someService = new SomServiceImpl();-->
</beans>
我们使用容器中的对象,通过ApplicationContext接口和他的实现类ClassPathXmlApplcactionContext的方法getBean()
@Test
public void test(){
System.out.println( "Hello World!" );
//定义Spring的配置文件
String config="applicationContext.xml";
//创建Spring的容器对象,根据Spring配置文件的位置,使用接口的不同实现类
//如果Spring配置文件位置是在类路径下(classpath)
ApplicationContext ctx= new ClassPathXmlApplicationContext(config);
//根目录下 ApplicationContext ctx = new FileSystemXmlApplicationContext(config);
//创建容器对象时,就把配置文件的所有对象都创建出来,放到容器中,需要时再取出来
SomeService service= (SomeService) ctx.getBean("someService");
//调用业务的方法
service.doSome();
}
控制反转(IoC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。
描述:把对象的创建,赋值,管理工作都交给代码之外的容器实现,也就是对象的创建是有其它外部资源完成
控制: 创建对象,对象的属性赋值,对象之间的关系管理。
反转: 把原来的开发人员管理,创建对象的权限转移给代码之外的容器实现。 由容器代替开发人员管理对象。创建对象,给属性赋值。
正转:由开发人员在代码中,使用new 构造方法创建对象, 开发人员主动管理对象。
public static void main(String args[]){
Student student = new Student(); // 在代码中, 创建对象。--正转。
}
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。
开发人员在项目中只需要提供对象的名称, 对象的创建,查找,赋值都由容器内部自己实现
(spring使用di的技术, 底层使用的是反射机制)
为什么要使用 ioc : 目的就是减少对代码的改动, 也能实现不同的功能。 实现解耦合
简单类型的set注入<property name="属性名" value="属性的值" />
<bean id="student" class="com.text.pack01.Student" >
<!--通过set方法注入-->
<property name="name" value="lisi"/>
<property name="age" value="20"/>
引用类型的set注入<proper name="属性名" ref="bean的id" />
<property name="school" ref="school"/>
</bean>
<constructor-arg>
的name属性,name表示构造方法的形参名<constructor-arg>
的index属性,表示构造方法形参的位置,从0开始byName: 按名称注入, java类中引用类型的**【属性名】和spring容器中bean的【id】**一样,数据类型一样。这样的bean赋值给引用类型
<bean id="student" class="com.text.pack01.Student" autowire="byName">
byType:按类型注入,java类中引用类型的是**【数据类型】和spring容器中bean的【class】**是同源关系的,这样的bean能够赋值给引用类型
加入依赖 :spring-context, 间接加入spring-aop
在类中加入注解
//使用注解创建对象 默认类的第一个字母小写
@Component
public class Student {
//可以在属性上,也可以在set方法上
@Value("李四")
private String name;
@Value("20")
private int age;
//@Resource 设置byType @Resource(name=byType)
//@Autowired 默认byType 用byName加@Qualifier注解
//@Autowired(required=true) 默认true 注入失败报错,required=false 注入失败值为null。
@Autowired
@Qualifier("mySchool")//设置byName
private School school;
......
在spring的配置文件中,加入组件扫描器的标签 <context:component-scan base-package="包名">
ioc能够实现业务对象之间的解耦合, 例如service和dao对象之间的解耦合。
动态代理是指, 程序在整个运行过程中根本就不存在目标类的代理类,目标对象的代理
对象只是由代理生成工具(不是真实定义的类)在程序运行时由 JVM 根据反射等机制动态
生成的。代理对象与目标对象的代理关系在程序运行时才确立。
使用jdk反射包中的类实现创建代理对象的功能
要求:目标类必须实现接口
public class DaiLi implements InvocationHandler {
//目标对象
private Object target;
public DaiLi(Object target) {
this.target = target;
}
//代理类的业务方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
SqlSession session=null;
Object obj=null;
try {
session= SqlSessionUtil.getSession();
//处理业务逻辑
//真实对象的业务方法
obj=method.invoke(target,args);
//处理业务逻辑完毕
session.commit();
} catch (Exception e) {
//异常回滚
session.rollback();
e.printStackTrace();
}finally {
SqlSessionUtil.closeSession(session);
}
return obj;
}
//获取代理对象
public Object getProxy(){
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
}
使用第三方的工具库,实现代理对象的创建
要求:目标类必须能够继承,不能是final
原理:就是继承,子类就是代理
execution(访问修饰符 返回值
包名.类名.方法名称(方法的参数)
异常)
@Before 前置通知 在目标方法之前先执行切面的功能
@Aspect//定义切面类
public class Advice {
//增强功能的方法
@Before(value = "execution(public void com.test.service.pack01.OneServiceImpl.sayHi())")
//属性value表示切面执行的位置
public void Before(){
System.out.println("在目标方法执行之前,例如输出日志");
}
@AfterReturning 后置通知 在目标方法之后执行的,能够获取到目标方法的返回值
/* returning是目标方法的返回值,命名与通知方法的形参名一致
相当于 Object obj = sayHi(String,Integer)
AfterReturning(obj)
可以修改这个返回值,影响最后调用的结果
*/
@AfterReturning(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))",returning = "obj")
public void AfterReturning(JoinPoint jp,Object obj){
/*指定通知方法中的参数: JoinPoint
JoinPoint(连接点)代表业务方法,要加入切面功能的业务方法 如sayHi
作用是:可以在通知方法中获取方法执行时的信息,例如方法的名称,方法的实参。
如果你的切面功能中需要用到方法的信息,就加入JoinPoint。
这个JointPoint参数的值是由框架赋予,必须是第一个位置的参数
System.out.println("方法的签名(定义)="+jp.getSignature());
System.out.println("方法的名称 ="+jp.getSignature().getName());
//获取方法的实参
Object args[] = jp.getArgs();
for (Object arg:args
) {
//可以用来判断目标方法的实参是否是想要的,然后再执行功能
System.out.println("参数="+arg);
}*/
if(obj!=null){
System.out.println("见面时间"+new Date());
}
}
@Around 环绕通知 在目标方法前和后都能增强功能,控制目标方法的的访问,修改返回值
/*
@Around: 环绕通知
属性:value 切入点表达式
位置;在方法的定义上面
特点:
1.它是功能最强的通知
2.在目标方法的前和后都能增强功能
3.控制目标方法是否被调用执行
4.修改原来的目标方法的执行结果,影响最后调用的结果
环绕通知等同与jdk动态代理的,InvocationHandler接口
返回值就是目标方法的执行结果,可以被修改。
*/
@Around(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))")
public Object Around(ProceedingJoinPoint pjp) throws Throwable {
Object obj=null;
//目标方法前加入功能
System.out.println("见面时间"+new Date());
//通过ProceedingJoinPoint.proceed对目标方法的调用
pjp.proceed();//method.invoke();
//目标方法后加入功能
System.out.println("提交事务");
return obj;
}
@AfterThrowing 异常通知 在目标方法抛出异常后执行的通知
//throwing属性 用于指定发生的异常对象
//相当于try{目标方法}catch(){ myAfterThrowing(Throwable ex) }
@AfterThrowing(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))",throwing = "ex")
public void myAfterThrowing(Throwable ex){
//记录异常信息
System.out.println("异常通知:异常发生时执行"+ex.getMessage());
}
@After 最终通知 总是会被执行的代码
//相当于 try{}catch(){}finally{ myAfter() }
@After(value="execution(* *..OneServiceImpl.sayHi(String,Integer))")
public void myAfter(){
System.out.println("最终通知");
}
@Pointcut 定义和管理切入点的辅助注解
/*@Pointcut : 定义和管理切入点,如果你的项目中有多个切入点表达式是重复的,可以复用的。
可以使用@Pointcut,统一管理切入点表达式。
属性: value 切入点表达式
位置: 在自定义的方法上面
特点:
当使用@Pointcut定义在一个方法的上面,此时这个方法就是切入点表达式的别名
其他的通知中,value就可以使用这个方法代替切入点表达式。
*/
@Pointcut(value = "execution(* *..OneServiceImpl.sayHi(String,Integer))")
private void pointCut(){
//无需代码
}
@After(value="pointCut()")
public void myAfter(){
System.out.println("最终通知");
}
代理的使用方式
<!--
声明自动代理对象:使用aspectj框架的功能,创建目标对象的代理对象。
创建代理对象时在内存中实现的,修改目标对象的内存中的机构。
使用目标对象就是被修改后的代理对象
-->
<aop:aspectj-autoproxy/>
<!--
如果你期望目标类有接口,但使用cglib代理
proxy-target-class="true" 告诉框架使用cglib动态代理
<aop:aspectj-autoproxy proxy-target-class="true"/>
-->
maven依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
使用spring的ioc核心技术,把mybatis框架中使用的对象交给spring统一创建和管理。spring是容器,存放你项目中要使用的各种对象, 例如Service对象, Dao对象,工具类对象等等。
定义dao接口 ,StudentDao,以及service接口和实现类
public interface StudentDao {
int insertStudent(Student student);
List<Student> selectStudents();
}
//service.....
定义mapper文件 StudentDao.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.JInterest.dao.StudentDao">
<insert id="insertStudent">
insert into student values(#{id},#{name},#{email},#{age})
</insert>
<select id="selectStudents" resultType="Student">
select id,name,email,age from student order by id desc
</select>
</mapper>
定义mybatis的主配置文件 mybatis.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--settings:控制mybatis全局行为-->
<settings>
<!--设置mybatis输出日志-->
<setting name="logImpl" value="STDOUT_LOGGING"/>
</settings>
<!--设置别名-->
<typeAliases>
<!--name:实体类所在的包名
表示cn.JInterest.domain包中的类名就是别名
你可以使用Student表示cn.JInterest.domain.Student
-->
<package name="cn.JInterest.domain"/>
</typeAliases>
<!-- sql mapper(sql映射文件)的位置-->
<mappers>
<!--
name:是包名, 这个包中的所有mapper.xml一次都能加载
-->
<package name="cn.JInterest.dao"/>
</mappers>
</configuration>
创建dao的代理对象
StudentDao dao = SqlSession.getMapper(StudentDao.class);
List<Student> students = dao.selectStudents();
要使用dao对象,需要使用getMapper()方法,
怎么能使用getMapper()方法,需要哪些条件
1)获取SqlSession对象, 需要使用SqlSessionFactory的openSession()方法。
2)创建SqlSessionFactory对象。 通过读取mybatis的主配置文件,能创建SqlSessionFactory对象
使用Factory能获取SqlSession,有了SqlSession就能有dao ,目的就是获取dao对象,最后service层调用dao对象
spring整合mabatis后的applicationContext.xml文件
使用阿里公司的Druid连接池,配置文档:DruidDataSource配置
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!--
把数据库的配置信息,写在一个独立的文件,编译修改数据库的配置内容
让spring知道jdbc.properties文件的位置
-->
<context:property-placeholder location="classpath:jdbc.properties" />
<!--声明数据源DataSource, 作用是连接数据库的,代替了mybatis的连接方式-->
<bean id="myDataSource" class="com.alibaba.druid.pool.DruidDataSource"
init-method="init" destroy-method="close">
<!--set注入给DruidDataSource提供连接数据库信息 -->
<!--
使用属性配置文件中的数据,语法 ${key}
-->
<property name="url" value="${jdbc.url}" /><!--setUrl()-->
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.passwd}" />
<property name="maxActive" value="${jdbc.max}" />
</bean>
<!--声明的是mybatis中提供的SqlSessionFactoryBean类,这个类内部创建SqlSessionFactory的-->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!--set注入,把数据库连接池付给了dataSource属性-->
<property name="dataSource" ref="myDataSource" />
<!--mybatis主配置文件的位置
configLocation属性是Resource类型,读取配置文件
它的赋值,使用value,指定文件的路径,使用classpath:表示文件的位置
-->
<property name="configLocation" value="classpath:mybatis.xml" />
</bean>
<!--
创建dao对象,使用SqlSession的getMapper(StudentDao.class)
MapperScannerConfigurer:在内部调用getMapper()生成每个dao接口的代理对象 。
org.apache.ibatis.binding.MapperProxy@9cd25ff
-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!--指定SqlSessionFactory对象的id-->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />
<!--指定包名, 包名是dao接口所在的包名。
MapperScannerConfigurer会扫描这个包中的所有接口,把每个接口都执行
一次getMapper()方法,得到每个接口的dao对象。
创建好的dao对象放入到spring的容器中的。 dao对象的默认名称是 接口名首字母小写
-->
<property name="basePackage" value="cn.JInterest.dao"/>
</bean>
<!--声明service-->
<bean id="studentService" class="cn.JInterest.service.impl.StudentServiceImpl">
<property name="studentDao" ref="studentDao" />
</bean>
</beans>
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.JInterest</groupId>
<artifactId>Spring_04-mybatis</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>11</maven.compiler.source>
<maven.compiler.target>11</maven.compiler.target>
</properties>
<dependencies>
<!--单元测试-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<!--spring核心ioc-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--做spring事务用到的-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<!--mybatis依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--mybatis和spring集成的依赖-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<!--mysql驱动-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.13</version>
</dependency>
<!--阿里公司的数据库连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
</dependencies>
<build>
<!--目的是把src/main/java目录中的xml文件包含到输出结果中。输出到classes目录中-->
<resources>
<resource>
<directory>src/main/java</directory><!--所在的目录-->
<includes><!--包括目录下的.properties,.xml 文件都会扫描到-->
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
</project>
数据源DataSource,使用阿里公司的Druid连接池
SqlSessionFactory对象, 使用的SqlSessionFactoryBean在内部创建的SqlSessionFactory
Dao代理对象,使用的MapperScannConfigure,在这个类的内部,调用getMapper(),创建接口的Dao对象
当我的操作,涉及得到多个表,或者是多个sql语句的insert,update,delete。需要保证这些语句都是成功才能完成我的功能,
或者都失败,保证操作是符合要求的。
在java代码中写程序,控制事务,此时事务应该放在那里呢?
service类的业务方法上,因为业务方法会调用多个dao方法,执行多个sql语句
spring处理事务的模型,使用的步骤都是固定的。把事务使用的信息提供给spring就可以了
事务内部提交,回滚事务,使用的事务管理器对象,代替你完成commit,rollback事务管理器是一个接口和他的众多实现类。
接口:PlatformTransactionManager ,定义了事务重要方法 commit ,rollback
实现类:spring把每一种数据库访问技术对应的事务处理类都创建好了。
mybatis访问数据库---spring创建好的是DataSourceTransactionManager
hibernate访问数据库----spring创建的是HibernateTransactionManager
怎么使用:你需要告诉spring 你用是那种数据库的访问技术,怎么告诉spring呢?
声明数据库访问技术对于的事务管理器实现类, 在spring的配置文件中使用<bean>
声明就可以了
例如,你要使用mybatis访问数据库,你应该在xml配置文件中
<bean id=“xxx" class="...DataSourceTransactionManager">
你的业务方法需要什么样的事务,说明需要事务的类型。
说明方法需要的事务:
事务的隔离级别:有4个值。
DEFAULT:采用 DB 默认的事务隔离级别。MySql 的默认为 REPEATABLE_READ; Oracle默认为 READ_COMMITTED。
➢ READ_UNCOMMITTED:读未提交。未解决任何并发问题。
➢ READ_COMMITTED:读已提交。解决脏读,存在不可重复读与幻读。
➢ REPEATABLE_READ:可重复读。解决脏读、不可重复读,存在幻读
➢ SERIALIZABLE:串行化。不存在并发问题。
事务的超时时间: 表示一个方法最长的执行时间,如果方法执行时超过了时间,事务就回滚。
单位是秒, 整数值, 默认是 -1
. (涉及因素多,一般默认)
事务的传播行为 : 控制业务方法是不是有事务的, 是什么样的事务的。
7个传播行为,表示你的业务方法调用时,事务在方法之间是如果使用的。
掌握以下三个:
spring提交事务,回滚事务的时机
当你的业务方法,执行成功,没有异常抛出,当方法执行完毕,spring在方法执行后提交事务。事务管理器commit
当你的业务方法抛出运行时异常或ERROR, spring执行回滚,调用事务管理器的rollback
运行时异常的定义: RuntimeException 和他的子类都是运行时异常, 例如NullPointException , NumberFormatException
当你的业务方法抛出非运行时异常, 主要是受查异常时,提交事务
受查异常:在你写代码中,必须处理的异常。例如IOException, SQLException
适合中小项目使用的, 注解方案。
spring框架自己用aop实现给业务方法增加事务的功能, 使用**@Transactional注解增加事务。@Transactional注解是spring框架自己注解,放在public方法的上面,表示当前方法具有事务。
可以给注解的属性赋值,表示具体的隔离级别,传播行为,异常信息**等等
需要声明事务管理器对象
<bean id="xx" class="DataSourceTransactionManager">
开启事务注解驱动, 告诉spring框架,我要使用注解的方式管理事务。
<tx:annotation-driven transaction-manager="transactionManager" />
spring使用aop机制,创建**@Transactional**所在的类代理对象,给方法加入事务的功能。
在你的业务方法执行之前,先开启事务,在业务方法之后提交或回滚事务,使用aop的环绕通知
/*
rollbackFor:表示发生指定的异常一定回滚.
处理逻辑是:
1) spring框架会首先检查方法抛出的异常是不是在rollbackFor的属性值中
2) 如果你的抛出的异常不在rollbackFor列表中,spring会判断异常是不是RuntimeException,
如果是一定回滚。
@Transactional(
propagation = Propagation.REQUIRED,
isolation = Isolation.DEFAULT,
readOnly = false,
rollbackFor = {
NullPointerException.class, NotEnoughException.class
}
)*/
@Transactional
@Around("你要增加的事务功能的业务方法名称")
Object myAround(){
//开启事务,spring给你开启
try{
buy(1001,10);
//spring的事务管理器.commit();
}catch(Exception e){
//spring的事务管理器.rollback();
}
}
适合大型项目,有很多的类,方法,需要大量的配置事务,使用aspectj框架功能,在spring配置文件中声明类,方法需要的事务。这种方式业务方法和事务配置完全分离。
实现步骤: 都是在xml配置文件中实现。
1. 要使用的是aspectj框架,需要加入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
<bean id="xx" class="DataSourceTransactionManager">
声明方法需要的事务类型(配置方法的事务属性【隔离级别,传播行为,超时】)
配置aop:指定哪些哪类要创建代理。
spring配置:
<!--
数据源配置
.....
.....
-->
<!--声明式事务处理:和源代码完全分离的-->
<!--1.声明事务管理器对象-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="myDataSource" />
</bean>
<!--2.声明业务方法它的事务属性(隔离级别,传播行为,超时时间)
id:自定义名称,表示 <tx:advice> 和 </tx:advice>之间的配置内容的
transaction-manager:事务管理器对象的id
-->
<tx:advice id="myAdvice" transaction-manager="transactionManager">
<!--tx:attributes:配置事务属性-->
<tx:attributes>
<!--tx:method:给具体的方法配置事务属性,method可以有多个,分别给不同的方法设置事务属性
name:1)完整的方法名称,不带有包和类。
2)方法可以使用通配符,* 表示任意字符
propagation:传播行为,枚举值
isolation:隔离级别
rollback-for:你指定的异常类名,全限定类名。 发生异常一定回滚
-->
<tx:method name="buy" propagation="REQUIRED" isolation="DEFAULT"
rollback-for="java.lang.NullPointerException,cn.JInterest.except.NotEnoughException"/>
<!--使用通配符,指定很多的方法,从上往下匹配-->
<tx:method name="add*" propagation="REQUIRES_NEW" />
<!--指定修改方法-->
<tx:method name="modify*" />
<!--删除方法-->
<tx:method name="remove*" />
<!--查询方法,query,search,find-->
<tx:method name="*" propagation="SUPPORTS" read-only="true" />
</tx:attributes>
</tx:advice>
<!--配置aop-->
<aop:config>
<!--配置切入点表达式:指定哪些包中类,要使用事务
id:切入点表达式的名称,唯一值
expression:切入点表达式,指定哪些类要使用事务,aspectj会创建代理对象
com.crm.service
com.service
-->
<aop:pointcut id="servicePt" expression="execution(* *..service..*.*(..))"/>
<!--配置增强器:关联adivce和pointcut
advice-ref:通知,上面tx:advice哪里的配置
pointcut-ref:切入点表达式的id
-->
<aop:advisor advice-ref="myAdvice" pointcut-ref="servicePt" />
</aop:config>
</beans>
<bean>
web使用容器对象:
servlet接受请求调用service层做数据操作
//请求数据...
//创建spring的容器对象
String config= "spring.xml";
ApplicationContext ctx = new ClassPathXmlApplicationContext(config);
//获取service,处理业务
StudentService service = (StudentService) ctx.getBean("studentService");
service.addStudent(student);
需要的依赖
<!-- servlet依赖 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!-- jsp依赖 -->
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2.1-b03</version>
<scope>provided</scope>
</dependency>
因为web项目是在tomcat服务器上运行的。tomcat启动,项目就一直运行。如果用上面那种方式,获得一次请求就要创建一次ApplicationContext对象,然后把spring.xml文件中所有对象创建出来,同时发送100个请求就要创建100个,垃圾回收器还没来得及回收又创建了,这样很浪费内存
解决方案: 容器对象创建一次, 把容器对象放入到全局作用域ServletContext中。
实现: 使用监听器 当全局作用域对象被创建时 创建容器 存入ServletContext
1)配置web.xml
<!--注册监听器ContextLoaderListener
监听器被创建对象后,会读取/WEB-INF/spring.xml
为什么要读取文件:因为在监听器中要创建ApplicationContext对象,需要加载配置文件。
/WEB-INF/applicationContext.xml就是监听器默认读取的spring配置文件路径
可以修改默认的文件位置,使用context-param重新指定文件的位置
配置监听器:目的是创建容器对象,创建了容器对象, 就能把spring.xml配置文件中的所有对象都创建好。
用户发起请求就可以直接使用对象了。
-->
<context-param>
<!-- contextConfigLocation:表示配置文件的路径 -->
<param-name>contextConfigLocation</param-name>
<!--自定义配置文件的路径-->
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2)加入监听器依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>5.2.5.RELEASE</version>
</dependency>
</dependencies>
3)servlet接受请求调用service层做数据操作
//使用框架中的方法,获取容器对象
WebApplicationContext ctx = null;
ServletContext sc = getServletContext();
ctx = WebApplicationContextUtils.getRequiredWebApplicationContext( sc );
System.out.println("容器对象的信息========"+ctx);
//获取service,处理业务
StudentService service = (StudentService) ctx.getBean("studentService");
service.addStudent(student);
WebApplicationContext底层实现
查其源码,看其调用关系,就可看到其是从 ServletContext 中读取的属性值,即 Spring容器。
(容器对象在 ServletContext 的中存放的 key 为 WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE。)
本文由 阿俊 创作,如果您觉得本文不错,请随意赞赏
采用 知识共享署名4.0 国际许可协议进行许可
本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名
原文链接:https://jinterest.cn/archives/spring
最后更新:2020-11-19 13:21:20
Update your browser to view this website correctly. Update my browser now