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 事务管理器的支持。 使用PROPAGATION_NESTED时,外层事务的回滚可以引起内层事务的回滚。而内层事务的异常并不会导致外层事务的回滚,它是一个真正的嵌套事务。

全XML及半Annotation配置方式

DataSource、SqlSessionFactoryBean、MapperScan、TransactionManager

配置数据源

业界中包括很多非常著名的数据源,例如C3P0、DBCP、Hikari等,而Druid作为alibaba中非常强大的数据连接池,由于其较强的性能及可监控性,也是受到了业界的追捧。

首先导入坐标

        <!--数据连接池-->
        <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;
    }
}
posted @ 2020-10-27 22:09  大明湖畔的闰土  阅读(193)  评论(0编辑  收藏  举报