美文网首页
SSM框架-Spring

SSM框架-Spring

作者: 青丶空 | 来源:发表于2020-03-10 11:33 被阅读0次

    Spring


    1. Spring概述

        Spring是应用full-stack轻量级框架,以IoC(Inverse Of Control : 控制反转)和AOP(Aspect Oriented Programming : 面向切面编程)为内核,提供了表现层SpringMVC和持久层Spring JDBC以及业务层事务管理等众多的企业级应用技术,还能整合开源世界中众多的第三方框架和类库。

    2. Spring的优势

    1. 方便解耦,简化开发
      • 通过Spring提供的IoC容器,可以将对象间的依赖关系交由Spring进行控制,编码硬编码所造成的过度程序耦合。
    2. AOP编程支持
      • 通过Spring的AOP功能,方便进行面向切面编程,许多不容易用传荣OOP实现的功能可以通过AOP轻松实现。
    3. 声明式事务的支持
      • 可以简化在事务管理方面的代码,通过声明的方式进行灵活的事务管理,提高开发效率和质量
    4. 方便程序的测试
      • 可以用非容器依赖的编程方式进行几乎所有的测试工作,测试的难度被降低了。
    5. 方便集成各种优秀框架
      • Spring提供了对各种邮箱框架的直接支持。
    6. 降低JavaEE API的使用难度
      • Spring对JavaEE API中的代码(JDBC、JavaMail、远程调用等)进行了封装,降低了使用API的难度。

    3. Spring的IoC应用

    1. 在Maven中导入Spring的坐标

        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.0.2.RELEASE</version>
            </dependency>
        </dependencies>
    

    2. Spring的主配置文件:applicationContext.xml

    1. 主配置的约束头信息
    <?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">
    
    </beans>
    
    1. 简单的Spring配置文件
    <!-- 配置service 
            id : 唯一标识
            class : 指定类的全类名
    -->
    <bean id="accountService" class="com.hsh.study.service.impl.AccountServiceImpl"></bean>
    
    <!-- 配置dao -->
    <bean id="accountDao" class="com.hsh.study.dao.impl.AccountDaoImpl"></bean>
    
    1. 常见属性标签
      1. bean标签:用于配置对象让Spring来创建。默认情况下Spring调用类中的无参构造函数,没有无参构造函数则不能创建成功
        • 属性:
          • id:给对象在容器总提供一个唯一标识。用于获取对象。
          • class:指定类的全限定类名。用于反射创建对象,默认情况下调用无参构造函数
          • scope:指定对象的作用范围。
            1. singleton:默认值,单例对象
            2. prototype:多例的
            3. request:在WEB中,Spring创建一个Bean对象,将对象存入request域中。
            4. session:在WEB中,Spring创建一个Bean对象,将对象存入session域中。
            5. global session:在WEB中,应用在Porlet环境,如果没有Porlet环境那么globalSession相当于Session。
          • init-method:指定类中的初始化方法名称。
          • destory-method:指定类中销毁方法的名称
          • factory-method:指定生产对象的静态方法(使用静态工厂初始化Bean时)
          • factory-bean:用于指定实例工厂bean的id(使用实例工厂初始化Bean时)
          • factory-method:用于指定实例工厂中创建对象的方法(使用实例工厂初始化Bean时)

    3. Spring的执行方法

    1. 使用ApplicaionContext接口获取Spring的容器:ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    2. 根据配置文件获取对象:AccountService accountService = ac.getBean("accountService",AccountService.class);
    3. 使用对象:accountService.saveAccount();

    ApplicationContext和BeanFactory的区别:

    1. BeanFactory是Spring容器中的顶层接口。
    2. ApplicationContext是BeanFactory的子接口。
    3. BeanFactory和ApplicationContext创建对象的时间点不同;
      • BeanFactory:什么时候使用什么时候创建对象
      • ApplicationContext:只要读取完配置文件,默认情况下就会创建对象。

    4. Spring的依赖注入

    1. 依赖注入概念:在编写程序时,通过IoC(控制反转)将对象的创建交给了Spring,但是代码中不可能出现没有依赖的情况。IoC解耦只是降低了代码间的依赖关系,但不会完全消除。
    2. 构造函数的注入(通过类中的构造函数给成员变量完成赋值的操作)
      1. 编写需要进行赋值的实体类
      public class User {
          private String username;
          private Integer age;
          private Date birthday;
      
          public User(String username, Integer age, Date birthday) {
              this.username = username;
              this.age = age;
              this.birthday = birthday;
          }
      
          @Override
          public String toString() {
              return "User{" +
                      "username='" + username + '\'' +
                      ", age=" + age +
                      ", birthday=" + birthday +
                      '}';
          }
      }
      
      
      1. 用配置文件通过构造函数对属性进行赋值
      <!-- 通过构造器进行属性注入 -->
      <bean id="user" class="com.hsh.study.domain.User">
          <!-- 通过<constructor-arg>标签对构造函数中的属性进行赋值
                  index:指定参数在构造函数参数列表的索引位置
                  type:指定参数在构造函数中的数据类型
                  name:指定参数在构造函数中的名称
                  ==--- 上面用于指定给谁赋值,下面指定赋什么值 ---==
                  value:能赋值的类型为基本数据类型和String类型
                  ref:能赋值的类型是其他bean类型,必须通过配置文件配置bean
          -->
          <constructor-arg name="username" value="张三"></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>
      
    3. set方法的注入(常用)
      1. 编写需要进行赋值的实体类
      public class User {
         private String username;
         private Integer age;
         private Date birthday;
         
         public String getUsername() {
             return username;
         }
      
         public void setUsername(String username) {
             this.username = username;
         }
      
         public Integer getAge() {
             return age;
         }
      
         public void setAge(Integer age) {
             this.age = age;
         }
      
         public Date getBirthday() {
             return birthday;
         }
      
         public void setBirthday(Date birthday) {
             this.birthday = birthday;
         }
         @Override
         public String toString() {
             return "User{" +
                     "username='" + username + '\'' +
                     ", age=" + age +
                     ", birthday=" + birthday +
                     '}';
         }
      }
      
      
      1. 用配置文件通过构造函数对属性进行赋值
       <!-- 通过set方法注入 -->
      <bean id="user" class="com.hsh.study.domain.User">
          <!-- 通过<property>标签对属性进行赋值
                  name:找类中set方法后面的部分
                  ref:给属性赋值是其他bean类型
                  value:给属性赋值的是基本数据类型或者string类型
           -->
          <property name="username" value="李四"></property>
          <property name="age" value="16"></property>
          <property name="birthday" ref="now"></property>
      </bean>
      
      <!-- 引入日期 -->
      <bean id="now" class="java.util.Date"></bean>
      
    4. 使用p名称空间注入(本质还是调用set方法)
      1. 编写需要进行赋值的实体类
      public class User {
         private String username;
         private Integer age;
         private Date birthday;
         
         public String getUsername() {
             return username;
         }
      
         public void setUsername(String username) {
             this.username = username;
         }
      
         public Integer getAge() {
             return age;
         }
      
         public void setAge(Integer age) {
             this.age = age;
         }
      
         public Date getBirthday() {
             return birthday;
         }
      
         public void setBirthday(Date birthday) {
             this.birthday = birthday;
         }
         @Override
         public String toString() {
             return "User{" +
                     "username='" + username + '\'' +
                     ", age=" + age +
                     ", birthday=" + birthday +
                     '}';
         }
      }
      
      
      1. 用配置文件通过构造函数对属性进行赋值
      <!-- 导入p标签的约束文件 -->
      xmlns:p="http://www.springframework.org/schema/p"
      <!-- 通过p标签进行注入 -->
      <bean id="user" class="com.hsh.study.domain.User"
            p:username="王五" p:age="18" p:birthday-ref="now"
      ></bean>
      
      <!-- 引入日期 -->
      <bean id="now" class="java.util.Date"
      ></bean>
      
    5. 注入集合属性
      1. 编写需要进行赋值的实体类
      public class Collection {
          private String[] myStrs;
          private List<String> myList;
          private Set<String> mySet;
          private Map<String,String> myMap;
          private Properties myProps;
      
          public void setMyStrs(String[] myStrs) {
              this.myStrs = myStrs;
          }
      
          public void setMyList(List<String> myList) {
              this.myList = myList;
          }
      
          public void setMySet(Set<String> mySet) {
              this.mySet = mySet;
          }
      
          public void setMyMap(Map<String, String> myMap) {
              this.myMap = myMap;
          }
      
          public void setMyProps(Properties myProps) {
              this.myProps = myProps;
          }
      
          public String[] getMyStrs() {
              return myStrs;
          }
      
          public List<String> getMyList() {
              return myList;
          }
      
          public Set<String> getMySet() {
              return mySet;
          }
      
          public Map<String, String> getMyMap() {
              return myMap;
          }
      
          public Properties getMyProps() {
              return myProps;
          }
      }
      
      1. 用配置文件通过构造函数对属性进行赋值
      <!-- 集合的注入 -->
      <bean id="collection" class="com.hsh.study.domain.Collection">
          <!-- 注入集合数据
                  List结构:array、List、Set
                  Map结构:map、properties、entry
           -->
          <!-- 数组注入 -->
          <property name="myStrs">
              <list>
                  <value>AAA</value>
                  <value>BBB</value>
                  <value>CCC</value>
              </list>
          </property>
          <!-- list集合注入 -->
          <property name="myList">
              <array>
                  <value>AAA</value>
                  <value>CCC</value>
                  <value>BBB</value>
              </array>
          </property>
          <!-- set集合注入 -->
          <property name="mySet">
              <set>
                  <value>BBB</value>
                  <value>AAA</value>
                  <value>CCC</value>
              </set>
          </property>
          <!-- map集合注入 -->
          <property name="myMap">
              <map>
                  <entry key="111" value="AAA"></entry>
                  <entry key="222" value="BBB"></entry>
                  <entry key="333" value="CCC"></entry>
              </map>
          </property>
          <!-- Props注入 -->
          <property name="myProps">
              <props>
                  <prop key="333">AAA</prop>
                  <prop key="222">CCC</prop>
                  <prop key="111">BBB</prop>
              </props>
          </property>
      </bean>
      

    5. Spring基于注解的使用

    1. 在Maven中导入Spring的坐标
    2. 配置Spring的配置文件使得支持注解
      1. 配置文件约束信息中引入注解的约束
      <?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
              http://www.springframework.org/schema/context/spring-context.xsd">
              
      </bean>
      
      1. 在配置文件中开启对注解的支持
      <?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
              http://www.springframework.org/schema/context/spring-context.xsd">
          <!-- 告知spring创建容器时要扫描的包 -->
          <context:component-scan base-package="com.hsh.study"></context:component-scan>
      </beans>
      
    3. Spring的常用注解
      • 创建对象的:<bean id="" class="" ></bean>
        1. @Component()
          • 将资源交由Spring进行管理,相当于在xml中配置<bean>标签
          • 默认属性为value,value即是hean的id,如果不指定value属性则默认id为当前类名,首字母小写
        2. @Controller @Service @Repository
          • 这三个注释的作用与@Component的作用一致,只不过提供了更加明确的语义化
            • @Controller:一般用于表现层
            • @Service:一般用于业务层
            • @Repository:一般用于持久层
      • 用于注入数据的:<property name="" ref="" /> <property name="" value="" />
        1. @Autowired
          • 自动按照类型注入。当使用注解注入属性时,可以不用声明set方法。只能注入其他bean类型。当有多个类型匹配时,使用要注入的对象变量名称作为bean的id。
        2. @Qualifier
          • 在自动按照类型的基础上,再按照Bean的id注入。在给字段注入时不能独立使用,必须和@Autowired一起使用;但是给方法参数注入时可以独立使用。
          • value:指定bean的id
        3. @Resource
          • 直接按照Bean的id进行注入。也是只能注入其他Bean类型
          • name:指定bean的id
        4. @Value
          • 注入基本数据类型和String类型的数据
          • value:用于指定值
      • 用于改变作用范围的:<bean id="" class="" ==scope=""==> </bean>
        1. @Scope
          • 指定bean的作用范围
          • value:指定范围的值(==singleton==、==prototype==、request、session、globalsession)
      • 和生命周期相关的:<bean id="" class="" ==init-method=""== ==destory-method=""== />
        1. @PostConstruct
          • 用于指定初始化方法
        2. @PreDestroy
          • 用于指定销毁方法
      • 用于纯注解配置的注解
        1. @Configuration
          • 作用:用于指定当前类是一个spring的配置类,当创建容器时会从该类上加载注解。获取容器时需要使用AnnotationApplicationContext(有@Configuration注解的类.class)
          • value:用于指定配置类的字节码
          • 注:当配置类作为AnnotationConfigApplicationContext对象创建的参数时,该注解可以不写。
        2. @ComponentScan
          • 作用:用于指定Spring再初始化容器时要扫描的包。作用和在spring的xml配置文件中的<context:component-scan base-package="com.hsh.study" />的作用相同
          • basePackages:用于指定要扫描的包,和注解中的value属性作用一致。
        3. @Bean
          • 作用:该注解只能写在方法上,表明使用此方法创建一个对象并放入Spring的容器中
          • name:给当前的@Bean注解方法创建的对象指定一个名称
        4. @PropertySource
          • 作用:用于加载.properties文件中的配置。例如当配置数据源时,可以把连接数据库的信息写道properties配置文件中,使用注解指定配置文件的位置。
          • value[]:用于指定properties文件位置。如果是在类路径下,需要写上==classpath:==
        5. @Import
          • 作用:用于导入其他配置类,在引入其他配置时可以不用再写@Configuration注解。
          • value[]:用于指定其他配置类的字节码
    4. Spring使用XML和使用注解配置的优劣
      1. 使用注解的优势:配置简单,维护方便,适用于Bean的是实现类由用户自己开发
      2. 使用XML的优势:修改时不需要改源码,不涉及重新编译和部署。适用于Bean来自第三方,使用其他的Bean。

    6. Spring中使用纯注解配置

    1. 创建一个主配置类
    //@Configuration
    @ComponentScan("com.hsh.study")
    @Import(JdbcConfig.class)
    @PropertySource("classpath:jdbcConfig.properties")
    public class SpringConfiguration {
    
    }
    
    1. 创建JDBC配置类
    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("singleton")
        public QueryRunner createQueryRunner(@Qualifier("ds1") DataSource dataSource){
            return new QueryRunner(dataSource);
        }
        /**
         * 创建数据库连接对象
         * @return
         */
        @Bean("ds1")
        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);
            }
        }
    }
    
    
    1. 使用注释完成创建对象和注入
    //1. 业务层
    @Service("accountService")
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        @Override
        public List<Account> findAll() {
            return accountDao.findAll();
        }
    }
    //2. 持久层
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Autowired
        private QueryRunner runner;
    
        @Override
        public List<Account> findAll() {
            try {
                return runner.query("select * from account",new BeanListHandler<Account>(Account.class));
            } catch (SQLException e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    1. 测试
     ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            // 基于注解创建applicationContext
            AccountService accountService = applicationContext.getBean("accountService", AccountService.class);
            List<Account> accounts = accountService.findAll();
            for (Account account1 : accounts){
                System.out.println(account1);
            }
    

    4. Spring整合Junit

    1. 导入整合junit的jar包

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>5.0.2.RELEASE</version>
        </dependency>
    

    2. 使用注解@RunWith代替原有运行器

    @RunWith(SpringJUnit4ClassRunner.class)
    public class JdbcTest2 {
        @Test
        public void test() {
        }
    }
    

    3. 使用@ContextConfiguration指定spring配置文件位置

    1. @ContextConfiguration属性:
      • locations属性:用于指定配置文件的位置。位于类路径下需使用classpath:表示。
      • calsses属性:用于指定注解的类。当不使用xml时,需要用此属性指定注解类的位置。
    2. 应用
    @RunWith(SpringJUnit4ClassRunner.class)
    // 使用XML配置文件时
    @ContextConfiguration(locations = {"classpath:applicationContext.xml"})
    // 使用注解时
    @ContextConfiguration(classes = {config.SpringConfiguration.class})
    public class JdbcTest2 {
        @Test
        public void test() {
        }
    }
    

    4. 使用@Autowired给变量注入数据

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = {config.SpringConfiguration.class})
    public class JdbcTest2 {
        @Autowired
        private AccountService accountService;
        @Test
        public void test() {
            ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringConfiguration.class);
            accountService = applicationContext.getBean("accountService", AccountService.class);
            List<Account> accounts = accountService.findAll();
            for (Account account1 : accounts){
                System.out.println(account1);
            }
        }
    }
    

    5. Spring的AOP

    1. 概念:

    AOP(Aspect Oriented Programming)面向切面编程。利用AOP对业务逻辑的各个部分进行隔离,从而降低业务逻辑各部分之间的耦合度
    

    2. AOP的作用及优势

    1. 作用:在程序运行期间,在不修改源码的基础上对已有方法进行增强。
    2. 优势:①减少重复代码②提高开发效率③维护方便

    3. AOP中相关的术语

    1. JoinPoint(连接点):连接点指的是哪些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。
    2. Pointcut(切入点):切入点指的是对哪些连接点进行拦截的定义。
    3. Advice(通知/增强):通知是指拦截到JoinPoint后所做的事情便是通知。
      • 通知的类型:前置通知,后置通知,异常通知,最终通知和环绕通知。
    4. Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,Introduction可以在运行期间为类动态的添加一些方法或者Field。
    5. Target(目标对象):代理的目标对象。
    6. Weaving(织入):指把增强应用到目标对象来创建新的代理对象的过程。
    7. Proxy(代理):一个类被AOP织入增强后,就产生了一个结果代理类。
    8. Aspect(切面):是切入点和通知(引介)的结合。

    3. AOP的使用

    1. 在Maven中引入Spring的AOP坐标
    <!-- AOP坐标 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    
    1. 在spring的配置文件中导入约束
      1. spring-AOP的约束头信息
      <?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"
             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">
          
      </beans>
      
      1. spring-AOP的配置
        • execution表达式
          1. 在AOP中使用execution表达式首先要导入表达式的坐标
          <!-- AOP使用execution表达式坐标 -->
          <dependency>
              <groupId>org.aspectj</groupId>
              <artifactId>aspectjweaver</artifactId>
              <version>1.8.7</version>
          </dependency>
          
          1. execution表达式的使用方法
            • 普通的表达式:权限修饰符 返回值类型 包名.包名...类名.方法名(参数列表)
            • 标准表达式写法:public void com.hsh.study.service.impl.AccountServiceImpl.findAll()
            • 权限修饰符可以省略,返回值类型使用通配符代替:* com.hsh.study.service.impl.AccountServiceImpl.findAll()
            • 包名使用通配符代替,几个类几个通配符:* *.*.*.*.*.AccountServiceImpl.findAll()
            • 包名可以使用..表示当前包及其子包:* *..AccountServiceImpl.findAll()
            • 方法名和类名都可以使用通配符:* *..*.*()
            • 参数列表:
              1. 基本数据类型直接写名称:int
              2. 引用数据类型写包名.类名的方式 java.lang.String
              3. 使用..表示有无参数均可,有参数可以为任意类型
            • 全通配写法:execution(* *..*.*(..))
        • AOP的具体配置
          1. 基本配置
          <!-- 首先导入对应的bean -->
          <bean id="accountService" class="..."></bean>
          <!-- 导入配置方法的bean -->
          <bean id="logger" class="..." ></bean>
          <!-- 配置aop -->
          <aop:config>
              <!-- 配置切入点表达式<aop:pointcut />
                      id:指定表达式唯一标识
                      expression:指定表达式内容
                   此标签写在<aop:aspect>内部表示当前切面可用
                   此标签写在<aop:aspect>外部表示所有切面可用
              -->
              <aop:pointcut id="pt1" expression="execution(* *..*.*(..))"/>
          <!-- 配置切面 -->
              <aop:aspect id="logAdvice" ref="transactionManager">
                  <!-- 配置前置通知" -->
                  <aop:before method="beforePringLog" pointcut-ref="pt1" />
                  <!-- 配置后置通知 -->
                  <aop:after-returning method="afterReturningPringLog" pointcut-ref="pt1" />
                  <!-- 配置异常通知 -->
                  <aop:after-throwing method="afterThrowingPringLog" pointcut-ref="pt1" />
                  <!-- 配置最终通知 -->
                  <aop:after method="afterPringLog" pointcut-ref="pt1" />
                  <!-- 配置环绕通知 -->
                  <!-- <aop:around method="aroundPrintLog" pointcut-ref="pt1" />-->
              </aop:aspect>
          </aop:config>
          
          1. 环绕通知配置
            • 类中
            public Object aroundPrintLog(ProceedingJoinPoint pjp){
            Object resultValue = null;
            try{
                // 得到方法执行所需的参数
                Object[] args = pjp.getArgs();
                // 执行前置通知
                System.out.println("前置通知");
                // 执行切入点方法
                resultValue = pjp.proceed(args);
                // 执行后置通知
                System.out.println("后置通知");
                return resultValue;
            }catch (Throwable e){
                System.out.println("异常通知");
                throw new RuntimeException(e);
            }finally {
                System.out.println("最终通知");
            }
            
          }
          * 配置文件中

          <bean id="accountService" class="..."></bean>

          <bean id="logger" class="..." ></bean>

          <aop:config>

          <aop:pointcut id="pt1" expression="execution(* ...*(..))"/>

          <aop:aspect id="logAdvice" ref="transactionManager">


          </aop:aspect>
          </aop:config>
          ```
    2. 基于注解的Spring-AOP配置
      1. 在配置文件中导入AOP相关的约束头信息
      <?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: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/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">
      </bean>
      
      1. 指定Spring注解所需扫描的包,并开启对注解AOP的支持
      <!-- 需要扫描的包 -->
      <context:component-scan base-package="com.hsh.study"></context:component-scan>
      <!-- 开启对注解AOP的支持 -->
      <aop:aspectj-autoproxy/>
      
      1. 针对各实体类进行注解配置
      2. 在通知类中使用注解进行通知
        • 在通知类上使用@Aspect注释表明这是一个切面类
        @Component("logger")
        @Aspect
        public class Logger {
        
        }
        
        • 切入点表达式注解
          1. @Aspect
            • 作用:指定切入点表达式
            • value:指定表达式的内容
        • 针对AOP的四种状态对应的注释
          1. @Before
            • 作用:将当前方法看作前置通知
            • value:用于指定切入点表达式,还可以指定切入点表达式的引用
          2. @After-Returning
            • 作用:将当前方法看作后置通知
            • value:用于指定切入点表达式,还可以指定切入点表达式的引用
          3. @AfterThrowing
            • 作用:将当前方法看作异常通知
            • value:同上
          4. @After
            • 作用:将当前方法看作最终通知
            • value:同上
          5. @Around
            • 作用:将当前方法看作环绕通知
            • value:同上 @Around("pt1()") //引入切入点时括号要加上
      3. 使用纯注解配置时
        • 在配置类上使用注解:@EnableAspectJAutoProxy即可

    6. Spring中的JdbcTemplate

    1. 概念

    JdbcTemplate是Spring框架中提供的一个对象,是对原始JDBC API的简单封装。其中提供了许多的操作模板类。

    1. 操作关系数据库:JdbcTemplate、HibernateTemplate
    2. 操作nosql数据:RedisTemplate
    3. 操作消息队列:JmsTemplate

    2. 基于Spring的JdbcTemplate所必须的jar包或坐标

    <!-- JdbcTemplate坐标 -->
     <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- 事务控制坐标 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    

    3. Spring-JdbcTemplate的使用

    1. 编写Spring的配置文件

    1. 导入spring-xml的约束头信息
    <?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">
    
    </beans>
    
    1. 配置相应的数据源
        <!-- 使用c3p0数据库连接池时 -->
        <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
            <property name="driverClass" value="com.mysql.cj.jdbc.Driver" />
            <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/spring?useSSL=false" />
            <property name="user" value="root" />
            <property name="password" value="123" />
        </bean>
        <!-- 使用Spring内置数据源 -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${jdbc.driverClass}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
        <!-- 使用外部配置文件的Spring内置数据源时 -->
        <!-- 引入外部配置文件 -->
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location" value="classpath:jdbc.properties"></property>
        </bean>
        <!-- 使用注解引入外部文件时 -->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        
    

    2. 使用JdbcTemplate实现增删改查操作

    1. 在配置文件中配置JdbcTemplate
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>
    
    1. 可以直接使用JdbcTemplate实现增删改查操作

    3. 在Dao中使用JdbcTemplate

    1. 不通过继承JdbcDaoSupport来简化获取JdbcTemplate的操作(用于xml配置和注解配置中)
      1. 在Dao实现类中添加jdbcTemplate属性
      private JdbcTemplate jdbcTemplate;
      
      // 添加注入
      public void setJdbcTemplate(JdbcTemplate jdbcTemplate){
          this.jdbcTemplate = jdbcTemplate;
      }
      
      1. 在配置文件中添加Dao实现类的bean
      <bean id="accountDao" class="...AccountDaoImpl">
          <property name="jdbcTemplate" ref="jdbcTemplate" />
      </bean>
      
      1. 使用jdbcTemplate属性执行数据库操作
    2. 通过继承JdbcDaoSupport来简化获取JdbcTemplate的操作(仅限于xml配置中)
      1. 在Dao实体类中继承JdbcDaoSupport,不用添加jdbcTemplate属性
      public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao { }
      
      1. 在配置文件中添加Dao实体类的Bean
      <bean id="accountDao" class="..AccountDaoImpl">
          <!-- 可以配置JdbcTemplate也可以配置DataSource 配置一个即可-->
          <!-- 配置JdbcTempldate -->
          <property name="jdbcTemplate" ref="jdbcTemplate" />
          <!-- 配置DataSource -->
          <property name="dataSource" ref="dataSource" />
      </bean>
      
      1. 调用父类的方法super.getJdbcTemplate()来执行相应的数据库操作

    7. Spring中的事务控制

    • Spring中关于事务控制的API

    1. PlatformTransactionManager(Spring中的事务管理器)

    PlatformTransactionManager接口提供了事务操作的方法,其中包含3各具体的操作:

    1. 获取事务状态信息TransactionStatus getTransaction(TransactionDefinition definition)
    2. 提交事务void commit(TransactionStatus status)
    3. 回滚事务void rollback(TransactionStatus status)
      真正使用的是PlatformTransactionManager接口的实体类:
    4. org.springframework.jdbc.datasource.==DataSourceTransactionManager== 使用Spring JDBC或iBatis进行持久化数据时使用
    5. org.springframework.orm.hibernate5.==HibernateTransactionManager== 使用Hibernate版本进行持久化数据时使用

    2. TransactionDefinition

    TransactionDefinition是事务的信息定义对象,方法如下:

    1. 获取事务对象名称:String getName()
    2. 获取事务隔离级别:int getIsolationLevel()
      • 默认级别,归属以下某一种:ISOLATION_DEFAULT
      • 可以读取未提交数据:ISOLATION_READ_UNCOMMITTED
      • 只能读取已提交数据,解决脏读问题(Oracle默认级别):ISOLATION_READ_COMMITTED
      • 是否读取其他事务提交修改后的数据,解决不可重复读问题(MySql默认级别):ISOLATION_REPEATABLE_READ
      • 是否读取其他事务提交添加后的数据,解决幻影读的问题:ISOLATION_SERIALIZABLE
    3. 获取事务传播途径:int getPropagationBehavior()
      • REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,加入到该事务中。(默认值)
      • SUPPORTS:支持当前事务,如果当前没有事务,就通过非事务方式执行
      • MANDATORY:使用当前的事务,如果当前没有事务则抛异常
      • REQUERS_NEW:新建事务,如果当前在事务中,则将当前事务挂起
      • NOT_SUPPORTED:以非事务的方式执行操作,如果当前存在事务,则将当前事务挂起
      • NEVER:以非事务的方式运行,如果当前存在事务,抛出异常
      • NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行REQUIRED类似的操作
    4. 获取事务超时时间:int getTimeout()
      • 默认值为-1,没有超时限制。如果有则以秒为单位进行设置
    5. 获取事务是否只读:boolean isReadOnly()
      • 只读事务:查询
      • 读写型事务:增删改

    3. TransactionStatus

    TransactionStatus接口提供某个时间点上事务对象的状态信息,其中有6个具体的操作

    1. 刷新事务:void flush()
    2. 获取是否存在存储点:boolean hasSavepoint()
    3. 获取事务是否完成:boolean isCompleted()
    4. 获取事务是否为新的事务:boolean isNewTransaction()
    5. 获取事务是否回滚:boolean isRollbackOnly()
    6. 设置事务回滚:void setRollbackOnly()
    • Spring中事务控制的具体操作

    1. 导入Spring事务控制的相关坐标

    <!-- JDBC相关坐标 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    <!-- 事务控制相关坐标 -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>5.0.2.RELEASE</version>
    </dependency>
    

    2. 在配置文件中导入aop和tx约束

    <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-3.1.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
    
    </beans>
    

    3. 配置Bean、数据源、事务控制、AOP

    <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-3.1.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop-3.1.xsd">
    
        <!-- 配置service -->
        <bean id="accountService" class="com.hsh.study.service.impl.AccountServiceImpl">
            <property name="accountDao" ref="accountDao"></property>
        </bean>
    
        <!-- 配置DAO -->
        <bean id="accountDao" class="com.hsh.study.dao.impl.AccountDaoImpl">
            <property name="dataSource" ref="dataSource"></property>
        </bean>
    
        <!-- 配置数据源 -->
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
            <property name="driverClassName" value="${jdbc.classDriver}" />
            <property name="url" value="${jdbc.url}" />
            <property name="username" value="${jdbc.username}" />
            <property name="password" value="${jdbc.password}" />
        </bean>
    
        <!-- 配置数据库配置文件 -->
        <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
            <property name="location" value="classpath:jdbc.properties" />
        </bean>
    
        <!-- 配置事务管理器 -->
        <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
            <!-- 导入数据源 -->
            <property name="dataSource" ref="dataSource" />
        </bean>
    
        <!-- 事务的配置 -->
        <tx:advice id="txAdvice" transaction-manager="transactionManager" >
            <!-- 配置事务的属性 -->
            <tx:attributes>
                <tx:method name="*" read-only="false" propagation="REQUIRED"/>
                <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
            </tx:attributes>
        </tx:advice>
        <!-- AOP的配置 -->
        <aop:config>
            <!-- 配置切入点表达式 -->
            <aop:pointcut id="pt1" expression="execution(* com.hsh.study.service.impl.*.*(..))"/>
            <!-- 配置AOP -->
            <aop:advisor advice-ref="txAdvice" pointcut-ref="pt1"></aop:advisor>
        </aop:config>
    </beans>
    
    • Spring中事务控制的具体操作(使用注解开发)

    1. 主注解类的配置
    // 声明式配置类
    @Configuration
    // 告诉Spring要扫描的包
    @ComponentScan("com.hsh.study")
    // 导入的另一个配置类
    @Import(JdbcConfig.class)
    // 配置文件的声明
    @PropertySource("classpath:jdbc.properties")
    // 开启注解AOP支持
    @EnableAspectJAutoProxy
    // 开启注解事务支持
    @EnableTransactionManagement
    public class SpringConfig {
    
    }
    
    1. Jdbc注解类的配置
    public class JdbcConfig {
        // @Value:表示引入配置文件中的值
        @Value("${jdbc.classDriver}")
        private String classDriver;
        @Value("${jdbc.url}")
        private String url;
        @Value("${jdbc.username}")
        private String username;
        @Value("${jdbc.password}")
        private String password;
        // 声明jdbcTemplate
        @Bean("jdbcTemplate")
        public JdbcTemplate createJdbcTemplate(DataSource dataSource){
            return new JdbcTemplate(dataSource);
        }
        /**
         * 创建事务管理器
         * Bean注解不配值时,默认类名首字母小写如DataSourceTransactionManager变成dataSourceTransactionManager
         */
        @Bean
        public DataSourceTransactionManager transactionManager(DataSource dataSource){
            return new DataSourceTransactionManager(dataSource);
        }
        // 声明数据源
        @Bean("dataSource")
        public DataSource createDataSource(){
            try {
                DriverManagerDataSource manager = new DriverManagerDataSource();
                manager.setDriverClassName(classDriver);
                manager.setUrl(url);
                manager.setUsername(username);
                manager.setPassword(password);
                return manager;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }
    
    1. 业务层实现
    @Service("accountService")
    @Transactional(readOnly = true,propagation = Propagation.SUPPORTS)
    public class AccountServiceImpl implements AccountService {
        @Autowired
        private AccountDao accountDao;
    
        @Override
        @Transactional(readOnly = false,propagation = Propagation.REQUIRED)
        public void transfer(String sourceName, String targetName, Double money) {
            // 获取两个用户
            Account source = accountDao.findByName(sourceName);
            Account target = accountDao.findByName(targetName);
            // 执行转账操作
            source.setMoney(source.getMoney()-money);
            target.setMoney(target.getMoney()+money);
            // 执行更新操作
            accountDao.update(source);
            accountDao.update(target);
        }
    
    1. 持久层实现
    @Repository("accountDao")
    public class AccountDaoImpl implements AccountDao {
        @Autowired
        private JdbcTemplate jdbcTemplate;
        
        @Override
        public Account findById(Integer accountId) {
            List<Account> accounts = jdbcTemplate.query("select * from account where id = ?",
                    new BeanPropertyRowMapper<Account>(Account.class), accountId);
            return accounts.isEmpty()?null:accounts.get(0);
        }
        @Override
        public Account findByName(String accountName) {
            List<Account> accounts = jdbcTemplate.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 update(Account account) {
            jdbcTemplate.update("update account set money = ? where id = ?",
                    account.getMoney(),account.getId());
        }
    }
    
    

    相关文章

      网友评论

          本文标题:SSM框架-Spring

          本文链接:https://www.haomeiwen.com/subject/pogudhtx.html