SSM框架-----Spring学习记录

一.Spring是什么?

spring是一个轻量级的IOC和AOP容器框架。是为Java应用程序提供基础性服务的一套框架,目的是用于简化企业应用程序的开发,它使得开发者只需要关心业务需求。

1.Spring的优点

  • 方便解耦,简化开发 (高内聚低耦合)
    Spring就是一个大工厂(容器),可以将所有对象创建和依赖关系维护交给Spring管理
  • AOP编程的支持
    Spring提供面向切面编程,支持将一些通用任务,如安全、事务、日志、权限等进行集中式管理,从而提供更好的复用
  • 声明式事务的支持
    只需要通过配置就可以完成对事务的管理,而无需手动编程方便程序的测试
  • Spring对Junit4支持
    可以通过注解方便的测试Spring程序方便集成各种优秀框架
  • Spring不排斥各种优秀的开源框架
    其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
  • 降低JavaEE API的使用难度
    Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低

二.Spring框架—IOC(控制反转)

1.什么是IOC?

把创建对象交给spring框架来完成

IOC(控制反转)理解:
(1) IOC就是控制反转,是指创建对象的控制权的转移,以前创建对象的主动权和时机是由自己把控的,而现在这种权力转移到Spring容器中,并由容器根据配置文件去创建实例和管理各个实例之间的依赖关系,对象与对象之间松散耦合,也利于功能的复用
(2) 最直观的表达就是,IOC让对象的创建不用去new了,可以由spring自动生产,使用java的反射机制,根据配置文件在运行时动态的去创建对象以及管理对象,并调用对象的方法的。
(3) Spring的IOC有三种注入方式 :构造器注入、setter方法注入、根据注解注入。

2.什么是DI(Dependency Injection 依赖注入)?

DI(Dependency Injection 依赖注入)理解:
spring这个容器中,替你管理着一系列的类,前提是你需要将这些类交给spring容器进行管理,然后在你需要的时候,不是自己去定义,而是直接向spring容器索取,当spring容器知道你的需求之后,就会去它所管理的组件中进行查找,然后直接给你所需要的组件.
实现IOC思想需要DI做支持

3.IOC案列(xml配置)

3.1 使用IOC创建对象的3种方式

3.1.1 使用默认构造函数创建(常用)

在spring配置文件中使用bean标签,并配以id,class属性,且没有其他标签和属性时,采用的就是默认构造函数创建bean对象,如果类中没有默认构造函数,则无法创建

<bean id="accountService" class="com.baidu.service.impl.AccountServiceImpl"></bean>

在这里插入图片描述
3.1.2 使用普通工厂中的方法创建

我们需要的类可能存在于jar包中,无法使用默认构造函数方法创建对象时,可以使用普通工厂的方法创建(使用某个类中的方法创建对象,并保存到spring容器中)

<bean id="instanceFactory" class="com.baidu.factory.instanceFactory"></bean>
<bean id="accountService" factory-bean="inatanceFactory" factory-method="getAccountService"></bean>

3.1.3 使用工厂中的静态方法创建

使用类中的静态方法创建对象,并存入spring容器

<bean id="accountService" class="com.baidu.factory.staticFactory" factory-method="getAccountService"></bean>

3.2 依赖注入的3种方式

3.2.1 构造函数注入
实现类中要有构造函数:
SSM框架-----Spring学习记录_第1张图片
spring配置文件:
SSM框架-----Spring学习记录_第2张图片

3.2.2 set方法注入
实现类中要有对应对象或属性的set方法:
SSM框架-----Spring学习记录_第3张图片
spring配置文件:
SSM框架-----Spring学习记录_第4张图片
关于集合数据的注入:
程序类中:SSM框架-----Spring学习记录_第5张图片
spring配置文件:

<!-- 注入集合数据 -->
    <bean id="accountService3" class="com.baidu.service.impl.AccountServiceImpl3">
        <property name="str">
           <array>
               <value>aaa</value>
               <value>bbb</value>
               <value>ccc</value>
           </array>
        </property>
        <property name="map">
          <map>
              <entry key="name" value="xh"></entry>
              <entry key="age" value="15"></entry>
              <entry key="sex" value="男"></entry>
          </map>
        </property>
        <property name="set">
           <set>
               <value>aa</value>
               <value>bb</value>
               <value>cc</value>
           </set>
        </property>
        <property name="list">
            <list>
                <value>q</value>
                <value>w</value>
                <value>e</value>
                <value>r</value>
            </list>
        </property>
        <property name="pro">
           <props>
               <prop key="A">aaa</prop>
               <prop key="B">bbb</prop>
           </props>
        </property>
    </bean>

3.2.3 注解注入
使用注解需要在spring配置文件中加:

 <!-- 容器创建时扫描该包下所有的注解-->
    <context:component-scan base-package="com.baidu"></context:component-scan>

Component:
作用:把当前类对象存入spring容器
属性:value:用于指定bean的id,默认是该类名首字母小写
Controller:一般用在表现层
Service:一般用在业务层
Repository:一般用在持久层
这三个注解和Component作用属性一模一样
他们是spring为我们提供的明确三层使用的注解,使我们对三层对象更加清晰

1.@Autowired
* 自动注入数据 根据数据类型找已存入spring容器中的bean对象
* 如果存在多个相同的对象,那就按照变量名找
* 2.@Autowired
* @Qualifier(“accountDao1”)
* @Qualifier(“accountDao1”)指定注入数据到哪儿个对象,但在类对象上不能单独使用,需要和@Autowired同时使用,他可以在传入方法参数时单独使用
* 3.@Resource(name = “accountDao1”)
* @Resource(name = “accountDao1”)可以指定注入数据到哪儿个对象,并且可以单独使用,使用name属性
* 4.@Value
* 用于注入基本类型和String类型
* 5.集合类型不能通过注解方式注入,只能通过xml方式注入

3.3 bean元素进阶(scope属性 生命周期属性)

3.3.1 scope属性

  • singleton:
    默认,每个容器中只有一个bean的实例,单例的模式由BeanFactory自身来维护。
  • prototype:
    为每一个bean请求提供一个实例。
  • request:
    Web环境下,对象与request生命周期一致
  • session:
    Web环境下,对象与session生命周期一致
  • global-session:全局作用域
    global-session和Portlet应用相关。当你的应用部署在Portlet容器中工作时,它包含很多portlet。如果你想要声明让所有的portlet共用全局的存储变量的话,那么这全局变量需要存储在global-session中。全局作用域与Servlet中的session作用域效果相同。

3.3.2 生命周期属性-----初始化和销毁
(1)配置一个方法作为生命周期初始化方法,spring会在对象创建之后立刻调用 init-method
(2)配置一个方法作为生命周期的销毁方法,spring容器在关闭并销毁所有容器中的对象之前调用destory-method

Spring整合Junit单元测试

/**
 * 使用Junit单元测试:测试我们的配置
 * Spring整合junit的配置
 *      1、导入spring整合junit的jar(坐标)
 *      2、使用Junit提供的一个注解把原有的main方法替换了,替换成spring提供的
 *             @Runwith
 *      3、告知spring的运行器,spring和ioc创建是基于xml还是注解的,并且说明位置
 *          @ContextConfiguration
 *                  locations:指定xml文件的位置,加上classpath关键字,表示在类路径下
 *                  classes:指定注解类所在地位置
 *
 *   当我们使用spring 5.x版本的时候,要求junit的jar必须是4.12及以上
 */

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountTest {

    @Autowired
    private AccountService service;

    @Test
    public void testFindAll(){
        List<Account> accounts = service.findAll();
        for(Account account : accounts){
            System.out.println(account);
        }
    }

三.Spring框架—AOP(面向切面编程)

1.什么是AOP?

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP(面向对象编程)的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。

  • 利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码
  • 经典应用:事务管理、性能监视、安全检查、缓存 、日志等

2.AOP的术语

  • JoinPoint(连接点):目标对象中,所有可以增强的方法
  • Pointcut(切入点):目标对象中,已经被增强的方法
  • Advice(通知/增强) :增强方法的代码、想要的功能
  • Target(目标对象):被代理对象,被通知的对象,被增强的类对象
  • Weaving(织入):是指把增强advice应用到目标对象target来创建新的代理对象proxy的过程.
  • Proxy(代理):将通知织入到目标对象之后形成的代理对象
  • aspect(切面):是切入点pointcut和通知advice的结合

3.AOP实现小案例(打印日志)

service层(业务层):相当于 Target(目标对象)
而这里面的所有方法都叫 JoinPoint(连接点)

public class AccountServiceDaoImpl implements AccountServiceDao {
    public void saveAccount() {
        System.out.println("保存账户成功");
        /*int i = 3/0;*/
    }

    public void updateAccount(int i) {
        System.out.println("更新账户成功");

    }

    public int deleteAccount() {
        System.out.println("删除账户成功");
        return 0;
    }
}

logger类:里面的所有方法就是Advice(通知/增强)

public class Logger {

    /**
     * 用于打印日志:计划让其在切入点方法执行之前执行(切入点方法就是业务层方法)
     */

    /**
     * 前置通知
     */
    public void beforePrintLog() {
        System.out.println("前置通知Logger类中的beforePrintLog方法开始记录日志了。。。");
    }

    /**
     * 后置通知
     */
    public void afterReturningPrintLog() {
        System.out.println("后置通知Logger类中的afterReturningPrintLog方法开始记录日志了。。。");
    }

    /**
     * 异常通知
     */
    public void afterThrowingPrintLog() {
        System.out.println("异常通知Logger类中的afterThrowingPrintLog方法开始记录日志了。。。");
    }

    /**
     * 最终通知
     */
    public void afterPrintLog() {
        System.out.println("最终通知Logger类中的afterPrintLog方法开始记录日志了。。。");
    }

spring的配置文件:
先把两个对象在spring容器中创建出来

    <bean id="accountService" class="com.baidu.service.impl.AccountServiceDaoImpl"></bean>

    <bean id="logger" class="com.baidu.logger.Logger"></bean>

然后进行AOP配置

 <!--配置AOP-->
    <aop:config>
        <!--配置切入点表达式-->
        <aop:pointcut id="pt" expression="execution(* com.baidu.service.impl.*.*(..))"/>
      <!-- 配置切面 -->
        <aop:aspect id="logAdvice" ref="logger">
            <!-- 配置通知的类型,并且建立通知方法和切入点方法的关联-->
            <!-- 配置前置通知:在切入点方法执行之前执行-->
             <aop:before method="beforePrintLog" pointcut-ref="pt" ></aop:before>

            <!-- 配置后置通知:在切入点方法正常执行之后值。它和异常通知永远只能执行一个-->
            <aop:after-returning method="afterReturningPrintLog" pointcut-ref="pt"></aop:after-returning>

            <!-- 配置异常通知:在切入点方法执行产生异常之后执行。它和后置通知永远只能执行一个-->
            <aop:after-throwing method="afterThrowingPrintLog" pointcut-ref="pt"></aop:after-throwing>

            <!-- 配置最终通知:无论切入点方法是否正常执行它都会在其后面执行-->
            <aop:after method="afterPrintLog" pointcut-ref="pt"></aop:after>

            <!-- 配置环绕通知 详细的注释请看Logger类中
            <aop:around method="aroundPringLog" pointcut-ref="pt"></aop:around>-->
        </aop:aspect>
    </aop:config>

切入点表达式的写法:
关键字:execution(表达式)
表达式:
访问修饰符 返回值 包名.包名.包名…类名.方法名(参数列表)

测试类: accountService中的saveAccount()方法相当于Pointcut(切入点)

public class AOPTest {

    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("bean.xml");
        AccountServiceDao accountService = ac.getBean("accountService",AccountServiceDao.class);

        accountService.saveAccount();
    }
}

SSM框架-----Spring学习记录_第6张图片

四.Spring中的事务控制

1.什么是事务

事务是应用程序中一系列严密的操作,所有操作必须成功完成,否则在每个操作中所作的所有更改都会被撤消。也就是事务具有原子性,一个事务中的一系列的操作要么全部成功,要么一个都不做。

2.事务的四大特性

⑴ 原子性(Atomicity)
  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

⑵ 一致性(Consistency)
  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。

拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

⑶ 隔离性(Isolation)
  隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。

即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。

关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。

⑷ 持久性(Durability)
  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。

例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

3.spring中的事务控制案例(转账)

案例准备:
pom.xml:

<dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-tx</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.7</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.6</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
    </dependencies>

dao层(持久层):

/**
 * 账户的持久层实现类
 */
public class AccountDaoImpl extends JdbcDaoSupport implements IAccountDao {
    @Override
    public Account findAccountById(Integer accountId) {
        List<Account> accounts = super.getJdbcTemplate().query("select * from account where id = ?",new BeanPropertyRowMapper<Account>(Account.class),accountId);
        return accounts.isEmpty()?null:accounts.get(0);
    }

    @Override
    public Account findAccountByName(String accountName) {
        List<Account> accounts = super.getJdbcTemplate().query("select * from account where name = ?",new BeanPropertyRowMapper<Account>(Account.class),accountName);
        if(accounts.isEmpty()){
            return null;
        }
        if(accounts.size()>1){
            throw new RuntimeException("结果集不唯一");
        }
        return accounts.get(0);
    }

    @Override
    public void updateAccount(Account account) {
        super.getJdbcTemplate().update("update account set name=?,money=? where id=?",account.getName(),account.getMoney(),account.getId());
    }
}

service层(业务层):

/**
 * 账户的业务层实现类
 *
 * 事务控制应该都是在业务层
 */
public class AccountServiceImpl implements IAccountService{

    private IAccountDao accountDao;

    public void setAccountDao(IAccountDao accountDao) {
        this.accountDao = accountDao;
    }

    @Override
    public Account findAccountById(Integer accountId) {
        return accountDao.findAccountById(accountId);

    }
    @Override
    public void transfer(String sourceName, String targetName, Float money) {
        System.out.println("转账了。。。。");
            //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);
    }
}

bean.xml(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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 配置业务层-->
    <bean id="accountService" class="com.baidu.service.impl.AccountServiceImpl">
        <property name="accountDao" ref="accountDao"></property>
    </bean>

    <!-- 配置账户的持久层-->
    <bean id="accountDao" class="com.baidu.dao.impl.AccountDaoImpl">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <!-- 配置数据源-->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/spring"></property>
        <property name="username" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>
    <!--配置事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--配置事务的通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!-- 配置事务的属性
                isolation:用于指定事务的隔离级别。默认值是DEFAULT,表示使用数据库的默认隔离级别。
                propagation:用于指定事务的传播行为。默认值是REQUIRED,表示一定会有事务,增删改的选择。查询方法可以选择SUPPORTS。
                read-only:用于指定事务是否只读。只有查询方法才能设置为true。默认值是false,表示读写。
                timeout:用于指定事务的超时时间,默认值是-1,表示永不超时。如果指定了数值,以秒为单位。
                rollback-for:用于指定一个异常,当产生该异常时,事务回滚,产生其他异常时,事务不回滚。没有默认值。表示任何异常都回滚。
                no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时事务回滚。没有默认值。表示任何异常都回滚。
        -->
        <tx:attributes>
            <tx:method name="*" propagation="REQUIRED" read-only="false"/>
            <tx:method name="find*" propagation="SUPPORTS" read-only="true"></tx:method>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="pt" expression="execution(* com.baidu.service.impl.*.*(..))"/>
        <!--建立切入点表达式和事务通知的对应关系 -->
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pt"></aop:advisor>
    </aop:config>
</beans>

测试:

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:bean.xml")
public class AccountServiceTest {

    @Autowired
    private  IAccountService as;

    @Test
    public  void testTransfer(){
        as.transfer("aaa","bbb",100f);
    }
}

没有异常的结果:转账成功
在这里插入图片描述
在service实现类中打开int=3/0的注解,就模拟了转账异常,再看运行结果:
在这里插入图片描述

由此可见,如果没有事务控制,那么就会发生错误,也就是aaa账户出了100,但是bbb账户没有收到100。

你可能感兴趣的:(SSM)