Springday03
使用注解实现IoC以及DI依赖注入
在配置文件中开启包扫描机制
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" 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 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"> <!--开启包扫描--> <context:component-scan base-package="com.csi" /> </beans>
注解实现IoC
- @Component:将当前的组件,以纯组件的形式加载到IoC容器
- @Reponsitory:将当前组件,以dao形式加载到IoC容器中
- @Service:将当前组件,以service形式加载到IoC容器中
- @Controller:将当前组件,以controller形式加载到IoC容器中
使用以上注解时,能够给每一个组件进行重命名。默认情况下,以首字母小写为bean的id,存储到IoC容器中。
使用Autowried或者是Resource来完成DI依赖注入
@Autowired:按照类型实现自动装配。也就是意味着在容器中必须拥有此类型的bean。如果出现了多个要注入的类型,此时,IoC容器会不知道该注入哪个依赖,这时可以通过@Qualifier("bean-id")来指明要注入具体哪个bean。
@RunWith(SpringJUnit4ClassRunner.class) // 使用注解形式加载spring配置文件->applicationContext.xml @ContextConfiguration({"classpath:spring/applicationContext.xml"}) public class TestSpring { @Autowired @Qualifier("userServiceImpl") private UserService userService ; @Test public void test1() { userService.save(new UserInfo()) ; 在 } }
@Resource:按照名称装配(bean的id)。在Spring中,加入注解后,会将对应类型的名称的首字母小写,存储到IoC容器中,Resource通过name在IoC容器中进行匹配。
@Resource(name = "userServiceImpl") private UserService userService ;
使用annotation注解形式实现AOP
自定义一个全注解的AOP类
@Aspect public class TransactionManager { /** * 定义连接点 */ @Pointcut("execution(* com.csi.service..*.*(..))") public void pointcut(){ } @Before("pointcut()") //前置 public void before(JoinPoint joinPoint) { System.out.println("前置"); } @AfterReturning(value = "pointcut()",returning = "result") //后置 public void afterReturning(JoinPoint joinPoint,Object result) { System.out.println("后置"); } @AfterThrowing(value = "pointcut()",throwing = "e") //异常 public void afterThrowing(JoinPoint joinPoint,RuntimeException e) { System.out.println("异常"); } @After(value = "pointcut()") //最终 public void after(JoinPoint joinPoint) { System.out.println("最终"); } @Around(value = "pointcut()") //环绕 public void around(ProceedingJoinPoint pjp) { System.out.println("环绕前置"); try { System.out.println("环绕"); Object result = pjp.proceed() ; System.out.println("提交事务"); } catch (Throwable throwable) { throwable.printStackTrace(); System.out.println("回滚事务"); } finally { System.out.println("关闭资源"); } System.out.println("环绕后置"); } }
在配置文件里,要开启自动代理,同时要告诉容器哪一个类是增强类。
<!--开启自动代理(Annotataion)--> <aop:aspectj-autoproxy /> <!--通知Spring容器,哪一个类是AOP的增强类--> <bean id="transactionManager" class="com.csi.aop.TransactionManager" />
Spring整合MyBatis
Spring事务管理
事务回顾
@Override public void transform() { String sql = "UPDATE table SET money = money - 500 WHERE userId = 1" ; update(sql); int num = 3 / 0 ; // 抛出异常信息 sql = "UPDATE table SET money = money + 500 WHERE userId = 2" ; update(sql) ; }
当程序执行3/0会出现异常,导致程序会结束运行,由于前面的update方法已经执行,导致最终总的钱数发生了变化!
事务四大特性(acid)
- 原子性(atomicity):事务是不可再分割的最小逻辑单元,一旦执行了,那么最终的结果不会因为出现各种问题而导致数据不统一。 通俗来说:一个事务中的操作,要么都成功,要么都失败。
- 一致性(consistency):事务执行前以及执行后,最终结果不应该发生变化。 通俗来说:一个事务必须使数据库从一个一致性状态转换到另一个一致性状态。
- 隔离性(isolation):事务之间应该保持一定的隔离性。 防止脏读、幻读、不可重复读。
- 持久性(durability):事务一旦提交了,就无法再次回退了。
事务可能出现的问题
脏读:读到了没有提交的另一个事务。
张三开启了一个事务,需要进行转账,此时,李四读到了张三未提交的事务中的金额值,并进了修改,导致最后一致性出问题。
幻读:读到了已经提交的数据,数据可能新增了,也可能减少了。主要是针对于数据的增加及删除操作。
张三准备打印流水,在查询后,发现一共30条数据,但是打印后产生了31条数据,导致其感觉出现了幻觉。
不可重复读:读到了已经提交的数据,但是数据的内容发生了变化。
张三读取了一条数据,结果是30,但是打印之后,结果变成了28.
针对于以上的可能出现的问题,数据库进行隔离机制的设计
Oracle的默认隔离级别是已提交读。MySQL默认的是可重复读。
Spring的事务传播机制
https://blog.csdn.net/x_h_xx/article/details/106388440
如果在一个类中,所有的方法上都需要设计对应事务支持方式,那么,如果A方法调用了B方法,此时如何设计?
1) REQUIRED(默认属性) 如果存在一个事务,则支持当前事务。如果没有事务则开启一个新的事务。 被设置成这个级别时,会为每一个被调用的方法创建一个逻辑事务域。如果前面的方法已经创建了事务,那么后面的方法支持当前的事务,如果当前没有事务会重新建立事务。
2) MANDATORY 支持当前事务,如果当前没有事务,就抛出异常。
3) NEVER 以非事务方式执行,如果当前存在事务,则抛出异常。
4) NOT_SUPPORTED 以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
5) REQUIRES_NEW 新建事务,如果当前存在事务,把当前事务挂起。
6) SUPPORTS 支持当前事务,如果当前没有事务,就以非事务方式执行。
7) NESTED 支持当前事务,新增Savepoint点,与当前事务同步提交或回滚。 嵌套事务一个非常重要的概念就是内层事务依赖于外层事务。外层事务失败时,会回滚内层事务所做的动作。而内层事务操作失败并不会引起外层事务的回滚。
PROPAGATION_NESTED 与 PROPAGATION_REQUIRES_NEW的区别: 它们非常类似,都像一个嵌套事务,如果不存在一个活动的事务,都会开启一个新的事务。
使用PROPAGATION_REQUIRES_NEW时,内层事务与外层事务就像两个独立的事务一样,一旦内层事务进行了提交后,外层事务不能对其进行回滚。两个事务互不影响。两个事务不是一个真正的嵌套事务。同时它需要JTA 事务管理器的支持。
全XML及半Annotation配置方式
配置数据源
首先导入坐标
<!--数据连接池--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.16</version> </dependency>
由于应用到了MyBatis及数据库,所以一并导入坐标
<!--mybatis包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.6</version> </dependency> <!--mybatis-spring整合包--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.5</version> </dependency> <!--mysql的驱动包--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.15</version> </dependency>
在xml添加配置
<!--读取配置文件--> <context:property-placeholder location="classpath:jdbc.properties" /> <!--配置数据源--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close"> <property name="url" value="${mysql_jdbc_url}" /> <property name="username" value="${mysql_jdbc_user}" /> <property name="password" value="${mysql_jdbc_password}" /> </bean>
jdbc.properties
#####MySQL数据源配置####
mysql_jdbc_url=jdbc:mysql://192.168.20.251:3306/test?serverTimezone=Asia/Shanghai
mysql_jdbc_className=com.mysql.jdbc.Driver
mysql_jdbc_user=root
mysql_jdbc_password=root123
maxActive=80
整合MyBatis
<!--完成MyBatis以及Spring的整合--> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <!--将数据源配置给mybatis--> <property name="dataSource" ref="dataSource" /> <!--配置MyBatis加载的对象的别名:默认情况下,将类名的首字母小写--> <property name="typeAliasesPackage" value="com.csi.domain" /> </bean> <!--配置映射的接口--> <bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.csi.dao" /> </bean>
<!--事务配置--> <!--1.配置事务管理器--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean> <!--配置增强及相关的一些功能--> <tx:advice id="txAdvice"> <tx:attributes> <!--配置切入点。*:代表所有的方法,默认事务传播机制为required,readonly是false值--> <tx:method name="*"/> <!--根据方法名称,只要find开头的方法全部进行拦截,并且进行事务增强,Supports代表着根据调用者的事务决定自身的事务--> <tx:method name="find*" read-only="true" propagation="SUPPORTS" /> </tx:attributes> </tx:advice> <!--配置切面--> <aop:config> <aop:pointcut id="pointcut" expression="execution(* com.csi.service..*.*(..))"/> <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut" /> </aop:config>
至此,Spring及MyBatis整合完毕
半XML及半Annotation配置方式
该种方式,利用Spring的@Transactional实现,在Transactional注解中的配置方式
@Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Inherited @Documented public @interface Transactional { @AliasFor("transactionManager") // 获取事务名称 String value() default ""; @AliasFor("value") String transactionManager() default ""; Propagation propagation() default Propagation.REQUIRED; //事务传播机制 Isolation isolation() default Isolation.DEFAULT; //事务隔离级别,默认为数据库设定 int timeout() default -1; // 超时时长,-1代表永不超时。 boolean readOnly() default false; //是否为只读 Class<? extends Throwable>[] rollbackFor() default {}; //遇到异常就回滚 String[] rollbackForClassName() default {}; //设置遇到那些类回滚 Class<? extends Throwable>[] noRollbackFor() default {}; // 遇到哪些异常不回滚 String[] noRollbackForClassName() default {}; }
在核心配置文件中,增加TransactionManager事务管理器
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource" /> </bean>
之后开启事务注解支持
<tx:annotation-driven/>
在业务逻辑层添加事务支持
@Service @Transactional public class UserInfoServiceImpl implements UserInfoService { @Autowired private UserInfoDao userInfoDao ; @Override public void save(UserInfo userInfo) { userInfoDao.save(userInfo) ; } @Override public List<UserInfo> list(int grade_id) { return userInfoDao.list(grade_id); } }
全Annotation配置方式
核心配置SpringConfig.java
@Configuration @Import({DataSourceConfig.class,MybatisConfig.class,TransactionManagerConfig.class}) @ComponentScan("com.csi") @EnableAspectJAutoProxy @EnableTransactionManagement public class SpringConfig { }
数据源配置DataSourceConfig.java
@PropertySource({"jdbc.properties"}) public class DataSourceConfig { @Value("${mysql_jdbc_url}") private String url; @Value("${mysql_jdbc_user}") private String user; @Value("${mysql_jdbc_password}") private String password; @Bean public DruidDataSource getDataSource(){ DruidDataSource dataSource = new DruidDataSource(); dataSource.setUrl(url); dataSource.setUsername(user); dataSource.setPassword(password); return dataSource; } }
Mybatis配置MybatisConfig.java
public class MybatisConfig { /** * 构建factoryBean对象 * @param dataSource 数据源 * @return factoryBean对象 */ @Bean public SqlSessionFactoryBean getSqlSessionFactory(DataSource dataSource){ SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); sessionFactoryBean.setDataSource(dataSource); sessionFactoryBean.setTypeAliasesPackage("com.csi.domain"); return sessionFactoryBean; } /** * 构件mapper扫描对象 * @return mapper扫描对象 */ @Bean public MapperScannerConfigurer getMapperScanner(){ MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer(); mapperScannerConfigurer.setBasePackage("com.csi.dao"); return mapperScannerConfigurer; } }
事务管理器配置TransactionManagerConfig.java
public class TransactionManagerConfig { @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){ DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(); dataSourceTransactionManager.setDataSource(dataSource); return dataSourceTransactionManager; } }