工厂模式解耦思路:
工厂模式解耦说明图
直接new对象高耦合
工厂接管创建对象来解耦
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vNfkVZQO-1592693871144)(Spring%E7%AC%94%E8%AE%B0.assets/image-20200618163946799.png)]
工厂类示例:
public class BeanFactory {
//定义一个Properties对象
private static Properties props;
//定义一个Map,用于存放我们要创建的对象。我们把它称之为容器
private static Map<String,Object> beans;
//使用静态代码块为Properties对象赋值
static {
try {
//实例化对象
props = new Properties();
//获取properties文件的流对象
InputStream in = BeanFactory.class.getClassLoader().getResourceAsStream("bean.properties");
props.load(in);
//实例化容器
beans = new HashMap<String,Object>();
//取出配置文件中所有的Key
Enumeration keys = props.keys();
//遍历枚举
while (keys.hasMoreElements()){
//取出每个Key
String key = keys.nextElement().toString();
//根据key获取value
String beanPath = props.getProperty(key);
//反射创建对象
Object value = Class.forName(beanPath).newInstance();
//把key和value存入容器中
beans.put(key,value);
}
}catch(Exception e){
throw new ExceptionInInitializerError("初始化properties失败!");
}
}
/**
* 根据bean的名称获取对象,单例
* @param beanName
* @return
*/
public static Object getBean(String beanName){
return beans.get(beanName);
}
/**
* 根据Bean的名称获取bean对象 多例模式
* @param beanName
* @return
*/
public static Object getBean(String beanName){
Object bean = null;
try {
String beanPath = props.getProperty(beanName);
bean = Class.forName(beanPath).newInstance();//每次都会调用默认构造函数创建对象
}catch (Exception e){
e.printStackTrace();
}
return bean;
}
}
把创建对象(new 对象)的权利交给了框架(或工厂),降低程序耦合性
Spring中的IOC:
DI(依赖注入):依赖关系的维护被称之为依赖注入
能注入的数据类型:
注入方式:
获取spring的Ioc核心容器,并根据id获取对象
核心容器的两个接口引发出的问题:
ApplicationContext:单例对象适用、采用此接口
它在构建核心容器时,创建对象采取的策略是采用立即加载的方式。也就是说,只要一读取完配置文件马上就创建配置文件中配置的对象。
BeanFactory:多例对象使用(了解)
它在构建核心容器时,创建对象采取的策略是采用延迟加载的方式。也就是说,什么时候根据id获取对象了,什么时候才真正的创建对象。
示例:
public static void main(String[] args) {
//1.获取核心容器对象
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
ApplicationContext ac = new FileSystemXmlApplicationContext("C:\\Users\\bean.xml");
//2.根据id获取Bean对象
IAccountService as = (IAccountService)ac.getBean("accountService");
IAccountDao adao = ac.getBean("accountDao",IAccountDao.class);
System.out.println(as);
System.out.println(adao);
//--------BeanFactory---------- 了解
Resource resource = new ClassPathResource("bean.xml");
BeanFactory factory = new XmlBeanFactory(resource);
IAccountService as = (IAccountService)factory.getBean("accountService");
System.out.println(as);
}
Spring中bean对象的创建:
<bean id="accountService" class="top.tdte.service.AccountServiceImpl" />
<bean id="instanceFactory" class="top.tdte.factory.InstanceFactory">bean>
<bean id="accountService" factory-bean="instanceFactory" factory-method="getAccountService">bean>
<bean id="accountService" class="top.tdte.factory.StaticFactory" factory-method="getAccountService">bean>
bean的作用范围调整
<bean id="accountService" class="com.xxx.AccountServiceImpl" scope="prototype">bean>
bean对象的生命周期
<bean id="accountService" class="com.xxx.AccountServiceImpl"
scope="prototype" init-method="init" destroy-method="destroy">bean>
配置文件约束:
<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
https://www.springframework.org/schema/beans/spring-beans.xsd">
beans>
标签:constructor-arg
标签出现的位置:bean标签的内部
标签中的属性:
优势:在获取bean对象时,注入数据是必须的操作,否则对象无法创建成功。
弊端:改变了bean对象的实例化方式,使我们在创建对象时,如果用不到这些数据,也必须提供。
<bean id="accountService" class="top.tdte.service.impl.AccountServiceImpl">
<constructor-arg name="name" value="tdte">constructor-arg>
<constructor-arg name="age" value="18">constructor-arg>
<constructor-arg name="birthday" ref="now">constructor-arg>
bean>
<bean id="now" class="java.util.Date"/>
<bean id="accountService2" class="com.itheima.service.impl.AccountServiceImpl2">
<property name="name" value="TEST" >property>
<property name="age" value="21">property>
<property name="birthday" ref="now">property>
bean>
list array set
map props
<bean id="accountService3" class="com.itheima.service.impl.AccountServiceImpl3">
<property name="strArray">
<array>
<value>AAAvalue>
<value>BBBvalue>
<value>CCCvalue>
array>
property>
<property name="myList">
<array>
<value>AAAvalue>
<value>BBBvalue>
<value>CCCvalue>
array>
property>
<property name="mySet">
<set>
<value>AAAvalue>
<value>BBBvalue>
<value>CCCvalue>
set>
property>
<property name="myMap">
<map>
<prop key="testC">cccprop>
<prop key="testD">dddprop>
map>
property>
<property name="myProps">
<props>
<entry key="testA" value="aaa">entry>
<entry key="testB">
<value>BBBvalue>
entry>
props>
property>
bean>
使用byName的时候,需要保证所有bean的id唯一,并且这个bean需要和自动注入的属性的set方法的值一致
<bean id="dog" class="top.tdte.domain.Dog"/>
<bean id="people" class="com.xxx.People" autowire="byName" />
byType的时候,需要保证所有bean的class唯一,并且这个bean需要和自动注入的属性的类型一致!
<bean id="dog" class="top.tdte.pojo.Dog"/>
<bean id="people" class="com.kuang.pojo.People" autowire="byType" />
<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
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="top.tdte"/>
<bean id="runner" class="org.apache.commons.dbutils.QueryRunner" scope="prototype">
<constructor-arg name="ds" ref="dataSource">constructor-arg>
bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<property name="driverClass" value="com.mysql.jdbc.Driver">property>
<property name="jdbcUrl" value="jdbc:mysql://localhost:3306/eesy">property>
<property name="user" value="root">property>
<property name="password" value="1234">property>
bean>
beans>
他们的作用就和在XML配置文件中编写一个标签实现的功能是一样的
他们的作用就和在xml配置文件中的bean标签中写一个标签的作用是一样的
@Autowired:自动按照类型注入。只要容器中有唯一的一个bean对象类型和要注入的变量类型匹配,就可以注入成功
@Qualifier:根据名称注入,它在给类成员变量注入时不能单独使用,需要与Autowired配合,但是在给方法参数注入时可以单独使用
@Resource:直接按照bean的id注入。它可以独立使用
以上三个注入都只能注入其他bean类型的数据,而基本类型和String类型无法使用上述注解实现,因此需要另一个注解@Value。
集合类型的注入只能通过XML来实现
@Value:用于注入基本类型和String类型的数据
他们的作用就和在bean标签中使用scope属性实现的功能是一样的
他们的作用就和在bean标签中使用init-method和destroy-methode的作用是一样的
注解示例:
@Service("accountService")
@Scope("prototype")
public class AccountServiceImpl implements AccountService {
// @Autowired
// @Qualifier("accountDao1")
@Resource(name = "accountDao2")
private IAccountDao accountDao = null;
@Value("Ken")
private String name;
@PostConstruct
public void init(){
System.out.println("初始化方法执行了");
}
@PreDestroy
public void destroy(){
System.out.println("销毁方法执行了");
}
public void saveAccount(){
accountDao.saveAccount();
}
}
@Configuration(类):标识当前类是一个配置类,可以被包扫描配置扫描到
@ComponentScan(类):用于通过注解指定spring在创建容器时要扫描的包
<context:component-scan base-package="com.tdte" />
@Bean(方法):用于把当前方法的返回值作为bean对象存入spring的ioc容器中
@Import(类):用于导入其他的配置类
@PropertySource(类):用于指定properties文件的位置 ,并联合value注解和spEl表达式进行配置文件注入
@PropertySource("classpath:top/tdte/jdbcConfig.properties")
@Scope(方法): 配置bean对象作用域
@Qualifier(参数):配置bean方法的参数引入那个bean
示例:
@Configuration
//扫描包路径
@ComponentScan("com.tdte")
//导入其他配置类,形成父子关系
@Import(JdbcConfig.class)
//配置文件路径 用于加载${key名称}EL表达式
@PropertySource("classpath:jdbcConfig.properties")
public class SpringConfiguration {
}
//和spring连接数据库相关的配置类
//如果不使用@Import(JdbcConfig.class)引入而是把下面这个类添加@Configuration注解,并被scan到,那么下面这个列和上面的配置类为兄弟关系
//由于是父子关系,引入配置文件的注解可以直接写在父配置文件中
public class JdbcConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
/**
* 用于创建一个QueryRunner对象
* @param dataSource
* @return
*/
@Bean(name="runner")
@Scope("prototype")
public QueryRunner createQueryRunner(@Qualifier("ds2") DataSource dataSource){
return new QueryRunner(dataSource);
}
/**
* 创建数据源对象
* @return
*/
@Bean(name="ds2")
public DataSource createDataSource(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl(url);
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
@Bean(name="ds1")
public DataSource createDataSource1(){
try {
ComboPooledDataSource ds = new ComboPooledDataSource();
ds.setDriverClass(driver);
ds.setJdbcUrl("jdbc:mysql://localhost:3306/eesy02");
ds.setUser(username);
ds.setPassword(password);
return ds;
}catch (Exception e){
throw new RuntimeException(e);
}
}
}
jdbcConfig.properties配置文件内容:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/eesy
jdbc.username=root
jdbc.password=1234
ApplicationContext context = new AnnotationConfigApplicationContext(KuangConfig.class);
Spring整合junit的配置
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-testartifactId>
<version>5.2.3.RELEASEversion>
dependency>
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:beans.xml")
// 使用spring中的测试类,替换原有的不能假装ioc容器的测试类
@RunWith(SpringJUnit4ClassRunner.class)
// 告诉测试类,我的配置文件或配置类在哪
@ContextConfiguration(classes = SpringConfiguration.class)
// @ContextConfiguration(locations = "classpath:beans.xml")
public class AccountServiceTest {
@Autowired
private IAccountService as = null;
@Test
public void testFindAll() {
//3.执行方法
List<Account> accounts = as.findAllAccount();
for(Account account : accounts){
System.out.println(account);
}
}
}
示例:
public static void main(String[] args) {
final Producer producer = new Producer();
IProducer proxyProducer = (IProducer) Proxy.newProxyInstance(producer.getClass().getClassLoader(),
producer.getClass().getInterfaces(),
new InvocationHandler() {
/**
* 作用:执行被代理对象的任何接口方法都会经过该方法
* 方法参数的含义
* @param proxy 代理对象的引用
* @param method 当前执行的方法
* @param args 当前执行方法所需的参数
* @return 和被代理对象方法有相同的返回值
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float) args[0];
//2.判断当前方法是不是销售
if ("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money * 0.8f);
}
return returnValue;
}
});
proxyProducer.saleProduct(10000f);
}
<dependency>
<groupId>cglibgroupId>
<artifactId>cglibartifactId>
<version>3.3.0version>
dependency>
public static void main(String[] args) {
final Producer producer = new Producer();
Producer cglibProducer = (Producer)Enhancer.create(producer.getClass(), new MethodInterceptor() {
/**
* 执行北地阿里对象的任何方法都会经过该方法
* @param proxy
* @param method
* @param args
* 以上三个参数和基于接口的动态代理中invoke方法的参数是一样的
* @param methodProxy 当前执行方法的代理对象
*/
@Override
public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
//提供增强的代码
Object returnValue = null;
//1.获取方法执行的参数
Float money = (Float)args[0];
//2.判断当前方法是不是销售
if("saleProduct".equals(method.getName())) {
returnValue = method.invoke(producer, money*0.8f);
}
return returnValue;
}
});
cglibProducer.saleProduct(12000f);
}
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-aopartifactId>
<version>5.2.3.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
dependency>
<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
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd"
xmlns:aop="http://www.springframework.org/schema/aop">
beans>
execution([权限修饰] 返回值(*) 方法所在的类的全路径.类名.方法名(方法参数类型) 方法抛出异常类型)
public void top.xxx.ServiceImpl.*()
* top.xxx.ServiceImpl.*()
* *.*.*.*.ServiceImpl.*())
* *..ServiceImpl.*()
* *..*.*()
* top.tdte.service.*.*(..)
示例(约束省略):
<aop:config>
<aop:pointcut id="pointcut" expression="execution(* top.tdte.service.impl.AccountServiceImpl.*(..) ) "/>
<aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
<aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
aop:config>
<bean id="accountService" class="top.springaop.service.AccountServiceImpl"/>
<bean id="logger" class="top.springaop.utils.Logger"/>
<aop:config>
<aop:aspect id="logAspect" ref="logger">
<aop:before method="printLog"
pointcut="execution(* top.springaop.service.AccountServiceImpl.saveAccount())"/>
aop:aspect>
aop:config>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* top.tdte.service.impl.*.*(..))"/>
<aop:aspect id="logAdvice" ref="logger">
<aop:before method="beforePrintLog" pointcut-ref="pt1" />
<aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt1"/>
<aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt1" />
<aop:after method="afterPrintLog" pointcut-ref="pt1" />
<aop:around method="aroundPringLog" pointcut-ref="pt1" />
<aop:pointcut id="pt1" expression="execution(* top.tdte.service.impl.*.*(..))"/>
aop:aspect>
aop:config>
Log通知类配置中的成员方法:
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("Logger类中的aroundPringLog方法开始记录日志了。。。最终");
}
}
使用注解前提:
<aop:aspectj-autoproxy/>
或者在配置类的类注解上添加注解
@EnableAspectJAutoProxy
开启aop注解支持
通知类示例:
//配置文件开启注解扫描
//把本类交给spring来管理,否则不能切入,或配置bean也可以
@Component
//表示当前类是一个切面类
@Aspect
public class Logger {
@Pointcut("execution(* com.itheima.service.impl.*.*(..))")
private void pt1(){}
// 前置通知
@Before("pt1()")
public void beforePrintLog(){
System.out.println("beforePrintLog方法开始记录日志了。。。");
}
// 后置通知
@AfterReturning("pt1()")
public void afterReturningPrintLog(){
System.out.println("afterReturningPrintLog方法开始记录日志了。。。");
}
// 异常通知
@AfterThrowing("pt1()")
public void afterThrowingPrintLog(){
System.out.println("afterThrowingPrintLog方法开始记录日志了。。。");
}
// 最终通知
@After("pt1()")
public void afterPrintLog(){
System.out.println("afterPrintLog方法开始记录日志了。。。");
}
// 环绕通知
@Around("pt1()")
public Object aroundPringLog(ProceedingJoinPoint pjp){
Object rtValue = null;
try{
Object[] args = pjp.getArgs();//得到方法执行所需的参数
System.out.println("aroundPringLog方法开始记录日志了。。。前置");
rtValue = pjp.proceed(args);//明确调用业务层方法(切入点方法)
System.out.println("aroundPringLog方法开始记录日志了。。。后置");
return rtValue;
}catch (Throwable t){
System.out.println("aroundPringLog方法开始记录日志了。。。异常");
throw new RuntimeException(t);
}finally {
System.out.println("aroundPringLog方法开始记录日志了。。。最终");
}
}
}
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.cj.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/xxx");
ds.setUsername("");
ds.setPassword("");
JdbcTemplate中crud的简单使用:
public class JdbcTemplateDemo3 {
public static void main(String[] args) {
ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
JdbcTemplate jt = ac.getBean("jdbcTemplate", JdbcTemplate.class);
// 保存
jt.update("insert into account(name,money)values(?,?)", "eee", 3333f);
// 更新
jt.update("update account set name=?,money=? where id=?", "test", 4567, 7);
// 删除
jt.update("delete from account where id=?", 8);
// 查询所有
// List accounts = jt.query("select * from account where money > ?",new AccountRowMapper(),1000f);
List<Account> accounts = jt.query("select * from account where money > ?", new BeanPropertyRowMapper<Account>(Account.class), 1000f);
for (Account account : accounts) {
System.out.println(account);
}
// 查询一个
List<Account> accounts1 = jt.query("select * from account where id = ?", new BeanPropertyRowMapper<Account>(Account.class), 1);
System.out.println(accounts1.isEmpty() ? "没有内容" : accounts1.get(0));
//查询返回一行一列(使用聚合函数,但不加group by子句)
Long count = jt.queryForObject("select count(*) from account where money > ?", Long.class, 1000f);
System.out.println(count);
}
}
//定义Account的封装策略
class AccountRowMapper implements RowMapper<Account> {
/**
* 把结果集中的数据封装到Account中,然后由spring把每个Account加到集合中
*/
@Override
public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
Account account = new Account();
account.setId(rs.getInt("id"));
account.setName(rs.getString("name"));
account.setMoney(rs.getFloat("money"));
return account;
}
}
由于dao层不同的实现类都需要显示的配置JdbcTemplate对象,为了简化代码,spring提供了一个类JdbcDaoSupport,他是 spring 框架为我们提供的一个类,该类中定义了一个 JdbcTemplate 对象,我们可以直接在dao的实现类继承JdbcDaoSupport,在实现类中实用getJdbcTemplate()方法来获取JdbcTemplate对象
由于JdbcDaoSupport是spring提供的,因此不能使用注解注入,只能通过配置类或配置xml文件进行对象的注入
// 先给JdbcDaoSupport注入数据源,再使用
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
@Override
public Account findAccountById(Integer accountId) {
List<Account> accounts = super.getJdbcTemplate().query("···");
return accounts.isEmpty()?null:accounts.get(0);
}
}
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-txartifactId>
<version>5.2.5.RELEASEversion>
dependency>
<dependency>
<groupId>org.aspectjgroupId>
<artifactId>aspectjweaverartifactId>
<version>1.9.5version>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>8.0.20version>
dependency>
<dependency>
<groupId>org.springframeworkgroupId>
<artifactId>spring-jdbcartifactId>
<version>5.2.5.RELEASEversion>
dependency>
配置文件约束:
<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"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/tx
https://www.springframework.org/schema/tx/spring-tx.xsd">
beans>
spring中基于XML的声明式事务控制配置步骤
在第5点中配置事务的属性说明(tx:attributes标签下tx:method标签的属性):
<beans>
<bean id="accountDao" class="...."/>
<bean id="dataSource" class="...."/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource">property>
bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="*" propagation="REQUIRED" read-only="false"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
tx:attributes>
tx:advice>
<aop:config>
<aop:pointcut id="pt1" expression="execution(* top.tdte.service.impl.*.*(..))">aop:pointcut>
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"/>
aop:config>
beans>
配置文件:
<beans>
<context:component-scan base-package="top.tdte"/>
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<property name="dataSource" ref="dataSource"/>
bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://tdte.top:3306/db1"/>
<property name="username" value="tdte"/>
<property name="password" value="311776"/>
bean>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
beans>
配置注解:
@Service("accountService")
//因为注解有默认属性,只要有@Transactional就会控制住事务问题
//也可以自定义配置,如果在类上就是全局配置,作用于整个方法
@Transactional(propagation = Propagation.SUPPORTS, readOnly = true)
public class AccountServiceImpl implements IAccountService {
@Autowired
private AccountDao accountDao;
@Override
public Account findAccountById(Integer accountId) {
return accountDao.findAccountById(accountId);
}
// 或者在方法上配置,作用域指定的方法
@Transactional(propagation = Propagation.REQUIRED, readOnly = false)
@Override
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney() - money);
//2.4转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5更新转出账户
accountDao.updateAccount(source);
//int i = 1 / 0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
@Configuration
@ComponentScan("top")
@PropertySource("db.properties")
//开启声明式事务注解
@EnableTransactionManagement
public class SpringConfig {
@Value("${jdbc.driver}")
private String driver;
@Value("${jdbc.url}")
private String url;
@Value("${jdbc.username}")
private String username;
@Value("${jdbc.password}")
private String password;
// 给dao层继承JdbcDaoSupport父类注入数据源
@Bean("accountDao")
IAccountDao template(DataSource dataSource) {
AccountDaoImpl accountDao = new AccountDaoImpl();
accountDao.setDataSource(dataSource);
return accountDao;
}
// 数据源
@Bean("dataSource")
DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driver);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
// 事务管理器
@Bean("tx")
PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
// 业务层声明式事务
@Service("accountService")
public class AccountServiceImpl implements IAccountService {
private IAccountDao accountDao;
@Autowired
public void setAccountDao(IAccountDao accountDao) {
this.accountDao = accountDao;
}
@Override
@Transactional
public void transfer(String sourceName, String targetName, Float money) {
System.out.println("transfer....");
//2.1根据名称查询转出账户
Account source = accountDao.findAccountByName(sourceName);
//2.2根据名称查询转入账户
Account target = accountDao.findAccountByName(targetName);
//2.3转出账户减钱
source.setMoney(source.getMoney() - money);
//2.4转入账户加钱
target.setMoney(target.getMoney() + money);
//2.5更新转出账户
accountDao.updateAccount(source);
int i = 1 / 0;
//2.6更新转入账户
accountDao.updateAccount(target);
}
}
MyBatis-Spring整合的事务控制详情请看官方文档