SSM框架-Spring


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());
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值