Java经典框架之Spring

Java经典框架之Spring

Java 是第一大编程语言和开发平台。它有助于企业降低成本、缩短开发周期、推动创新以及改善应用服务。如今全球有数百万开发人员运行着超过 51 亿个 Java 虚拟机,Java 仍是企业和开发人员的首选开发平台。
    

课程内容的介绍

1. Spring简介
2. Spring IoC
3. Spring AOP
4. Spring事务管理
     

一、Spring简介

1. Spring概览
官网地址 : http://www.spring.io
简化Java开发的。Spring不仅只是一个框架,Spring已经成为了一种生态。(与其说是Java开发,不如说是Spring开发)
一个Java应用层程序,是由许多个类组成的,这些类之间必然存在依赖关系,当项目越来越大,依赖关系越来越复杂,需要一个专业的框架来处理类之间的依赖关系,为了解决这个问题,SUN公司推出了EJB(重量级)专门用来解决类的依赖问题。
Spring(Spring Framework) 是一个开源框架,最早由Rod Johnson创建,并在《Expert One-on-One:J2EE Design and Development》这本著作中进行了介绍。Spring是为了解决企业级应用开发的复杂性而创建的,使用Spring可以让简单的JavaBean实现之前只有EJB才能完成的事情。但Spring不仅仅局限于服务器端开发,任何Java应用都能在简单性、可测试性和松耦合等方面从Spring中获益。
bean的各种名称……虽然Spring用bean或者JavaBean来表示应用组件,但并不意味着Spring组件必须要遵循JavaBean规范。一个Spring组件可以是任何形式的POJO(POJO(Plain Ordinary Java Object)简单的Java对象,实际就是普通JavaBeans,是为了避免和EJB混淆所创造的简称)。
Spring 可以做非常多的事情。但归根结底,支撑Spring的仅仅是少许的基本理念,所有的理念都可以追溯到Spring最根本的使命上:简化Java开发。这是一个郑重的承诺。许多框架都声称在某些方面做了简化,但Spring的目标是致力于全方位的简化Java开发。这势必引出更多的解释,Spring是如何简化Java开发的?
     
为了降低Java开发的复杂性,Spring采取了以下4种关键策略
基于POJO的轻量级和最小侵入式编程;
通过控制反转和依赖注入以及面向接口实现松耦合;
基于切面和惯例进行声明式编程;
通过切面和模板减少样板式代码。
  
2.环境准备
2.1 jar包下载
官网jar下载地址:https://repo.spring.io/libs-release-local/org/springframework/spring/

    
将下载的dist压缩包解压缩:

      
2.2 相关核心的模块说明
Spring核心模块

  
Spring切面编程模块

     
Spring之数据访问和集成模块

    
Spring值Web模块

    

Spring测试和通信报文模块

       
3.常见版本号说明
软件版本号:2.0.2.RELEASE --》X.Y.Z.RELEASE

     
常见的版本号

      
Spring版本命名规则

     
4. Spring的结构图

  
各个模块之间的相互依赖关系图

  

二、Spring IoC

1.IoC的概念
1. IoC控制反转(IoC,Inversion of Control),
是一个概念,是一种思想。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。把对象的创建、初始化、销毁等工作交给spring容器来做。由spring容器控制对象的生命周期。
2. DI依赖注入:Dependency Injection。
依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。依赖注入是目前最优秀的解耦方式。依赖注入让Spring的Bean之间以配置文件的方式组织在一起,而不是以硬编码的方式耦合在一起的。
3. IoC与DI的关系
IoC是一个概念,是一种思想,其实现方式多种多样。当前比较流行的实现方式之一是DI。
  
SpringIoC 类似于 包子铺, 对象就是包子。
IoC:控制反转,本质就是将对象new的过程交给了Spring容器去处理。
   
2.IoC基于XML配置的实现方式
2.1 基本实现步骤
演示第一个SpringIoC案例。
     
2.1.1 创建一个普通的Java项目

  
2.1.2 导入相关的Spring依赖
在项目根目录下创建一个bin文件夹

    
将我们需要使用到的相关的jar拷贝到bin目录下。

     
将bin目录设置为Library目录

    
2.1.3 创建Spring配置文件
在src目录下创建一个applicationContext.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>
     
2.1.4 添加对应的Java类型

     
2.1.5 配置文件中注册Bean
将User在配置文件中通过bean标签注册。
<?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">
    <!-- 通过Bean标签将User实例添加到IoC容器中去 -->
    <bean class="com.bobo.ioc.bean.User" name="user"/>
</beans>
   
2.1.6 Java代码实现
/**
* 通过IoC的方式获取User对象
* @return
*/
public static User getUserForIoC(){
    // IoC的初始化操作
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    // 从容器中获取User对象
    User user = (User)ac.getBean("user");
    return user;
}
      
注意第一次启动抛 ClassNotFoundException异常,因为缺少logging依赖,导入即可。
    

     
再去执行即可

     
2.2 getBean方法
我们从IoC容器中获取对应的对象是通过getBean方法实现的,默认是调用的无参构造方法,如果不存在该方法就会抛出如下异常。

  
<?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">
    <!-- 通过Bean标签将User实例添加到IoC容器中去 -->
    <bean class="com.bobo.ioc.bean.User" name="user"/>

    <bean class="com.bobo.ioc.bean.Student" name="stu" id="stuId" />
</beans>
  
根据name获取
// 可以通过bean 的name值获取
Student stu = (Student) ac.getBean("stu");
  
根据id获取
Student stu = (Student) ac.getBean("stuId");
  
根据class获取
Student stu = ac.getBean(Student.class);
      
如果我们根据类型去容器中获取对象,如果该类型在容器中有多个实例,那么会抛出异常信息。
<?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">
    <!-- 通过Bean标签将User实例添加到IoC容器中去 -->
    <bean class="com.bobo.ioc.bean.User" name="user"/>

    <bean class="com.bobo.ioc.bean.Student" name="stu" id="stuId" />
    <bean class="com.bobo.ioc.bean.Student" />
</beans>
    

    
那么这时我们可以通过组合的方式来查找。
  
组合的方式查找
Student stu = ac.getBean("stuId", Student.class);
  
2.3 id和name的区别
id="stuId1,stuId2,stuId3"
表示bean有一个名字,就这个名字就叫stuId1,stuId2,stuId3
name="stu1,stu2,stu3"
表示bean有多个名字,分别是 stu1、stu2以及 stu3
   
2.4 BeanFactory和ApplicationContext的区别
通过类图结构我们能够发现BeanFactory是ApplicationContext接口的一个父接口,ApplicationContext接口定义的功能肯定要比BeanFactory接口定义的功能要强大。

  
public static void main(String[] args) {
    // 创建IoC容器对象的时候就会初始化容器中管理的所有的Bean
    ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
    System.out.println("-----------");
    // 去IoC容器中查找满足条件的对象
    System.out.println(ac.getBean("stu1", Student.class));
    // 创建IoC容器对象,BeanFactory仅仅只会加载解析配置文件,并不会创建具体的实例对象
    System.out.println("*************************");
    BeanFactory bf = new XmlBeanFactory(new
    ClassPathResource("applicationContext.xml"));
    System.out.println("------------");
    // 去容器中根据条件获取对象,如果对象没有实例化那么就会创建该对象
    System.out.println(bf.getBean("stu1",Student.class));
}
   
2.5 IoC对象工厂注入
2.5.1 静态工厂注入
通过工厂类的静态方法获取对应的实例对象。
package com.bobo.ioc.factory;

import com.bobo.ioc.bean.User;

/**
 * User 工厂类
 */
public class UserFactory {

    /**
     * 创建User对象
     * 方法必须是static修饰
     * @return
     */
    public static User getInstance(){
        return new User();
    }
}
    
配置文件信息:
<?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">

    <!-- 静态工厂配置 -->
    <bean class="com.bobo.ioc.factory.UserFactory" factory-method="getInstance"
id="user666" />
</beans>
   
2.5.2 动态工厂注入
通过工厂类的普通方法获取实例对象。
package com.bobo.ioc.factory;

import com.bobo.ioc.bean.User;

/**
 * 动态工厂
 */
public class UserDynamicFactory {


    public User getInstance(){
        return new User();
    }
}
  
配置文件信息
<?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">

    <!-- 注册动态工厂Bean -->
    <bean class="com.bobo.ioc.factory.UserDynamicFactory" id="userDynamicFactory" />
    <!-- 从工厂对象获取User对象 -->
    <bean id="user" factory-bean="userDynamicFactory" factory-method="getInstance" />
</beans>
    
3. 基于配置方式的属性注入
属性注入:如何给对象中的属性设值。
    
3.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">
    <bean class="com.bobo.ioc.bean.User" >
        <constructor-arg name="id" value="666"/>
        <constructor-arg name="userName" value="bobo"/>
        <constructor-arg name="gender" value="男" />
    </bean>
</beans>
   
配置的简化方式: c 名称空间。

     
还可以通过参数的下标来传值。

     
3.2 设值注入
设值注入:就是通过Bean对象的setter和getter方法来设置或获取对象的属性信息,这样一来有参构造方法就不是必须的了。

      
配置文件信息
<?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">
    <bean class="com.bobo.ioc.bean.Student" >
        <!-- 设值注入 -->
        <property name="id" value="456"/>
        <property name="className" value="K1D" />
        <property name="stuName" value="bobo" />
    </bean>
</beans>
     
简化配置:p名称空间

  
3.3 对象注入
如果初始化的属性的类型是自定义的对象,那么注入的时候可以通过 ref 属性来从IoC容器中获取合适的对象注入。
package com.bobo.ioc.bean;

public class Cat {

    // 昵称
    private String nickName;

    // 颜色
    private String color;

    public String getNickName() {
        return nickName;
    }

    public void setNickName(String nickName) {
        this.nickName = nickName;
    }

    public String getColor() {
        return color;
    }

    public void setColor(String color) {
        this.color = color;
    }

    @Override
    public String toString() {
        return "Cat{" +
                "nickName='" + nickName + '\'' +
                ", color='" + color + '\'' +
                '}';
    }
}
package com.bobo.ioc.bean;

import java.util.List;
import java.util.Map;
import java.util.Properties;

public class UserBean {
    // 编号
    private Integer id;

    // 拥有的对象
    private Cat cat;

    // List集合
    private List<String> games;

    // 数组
    private Cat[] cats;


    // Map集合
    private Map<String,Object> scores;

    // Properties对象
    private Properties properties;

    public List<String> getGames() {
        return games;
    }

    public Map<String, Object> getScores() {
        return scores;
    }

    public void setScores(Map<String, Object> scores) {
        this.scores = scores;
    }

    public Properties getProperties() {
        return properties;
    }

    public void setProperties(Properties properties) {
        this.properties = properties;
    }

    public void setGames(List<String> games) {
        this.games = games;
    }

    public Cat[] getCats() {
        return cats;
    }

    public void setCats(Cat[] cats) {
        this.cats = cats;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

    @Override
    public String toString() {
        return "UserBean{" +
                "id=" + id +
                ", cat=" + cat +
                '}';
    }
}
   
注入的方式
<?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:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 注册一个Cat对象 -->
    <!--<bean class="com.bobo.ioc.bean.Cat" id="cat"
      p:color="黑色" p:nickName="花花"  />

    <bean class="com.bobo.ioc.bean.UserBean" >
        &lt;!&ndash; 设值注入 &ndash;&gt;
        <property name="id" value="666"/>
        <property name="cat" ref="cat"/>
    </bean>-->
    <!-- 等价的写法 -->


    <bean class="com.bobo.ioc.bean.UserBean" >
        <!-- 设值注入 -->
        <property name="id" value="666"/>
        <property name="cat" >
            <bean class="com.bobo.ioc.bean.Cat"
                  p:color="黑色" p:nickName="花花"  />
        </property>
    </bean>
</beans>

     
3.4 数组和集合注入

  
配置文件中的信息
<bean class="com.bobo.ioc.bean.UserBean" >
    <!-- 设值注入 -->
    <property name="id" value="666"/>
    <property name="cat" >
        <bean class="com.bobo.ioc.bean.Cat"
            p:color="黑色" p:nickName="花花" />
    </property>
    <!-- List 类型的注入 -->
    <property name="games" >
        <list>
            <value>LOL</value>
            <value>DNF</value>
            <value>WZRY</value>
        </list>
    </property>
    <!-- 数组 类型的注入 -->
    <property name="cats">
        <array>
            <bean class="com.bobo.ioc.bean.Cat" p:color="灰色1" p:nickName="小白"/>
            <bean class="com.bobo.ioc.bean.Cat" p:color="灰色2" p:nickName="小黑"/>
            <bean class="com.bobo.ioc.bean.Cat" p:color="灰色3" p:nickName="大毛"/>
        </array>
    </property>
</bean>
  
3.5 Map和Properties类型注入

  
配置文件信息
<property name="scores">
    <!-- Map 类型注入 -->
    <map >
        <description>用户成绩信息</description>
        <entry key="英语" value="100"></entry>
        <entry key="数学" value="98"/>
    </map>
</property>

<property name="properties" >
    <!-- Properties 类型注入 -->
    <props>
        <prop key="username">zhangsan</prop>
        <prop key="password">123456</prop>
    </props>
</property>
</bean>
   
4.综合小案例
将我们原来三层架构的案例改造为IoC实现的案例。

      
4.1 UserBean
package com.gupaoedu.ioc.bean;

public class User {

    private  Integer id;

    private String userName;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    @Override
    public String toString() {
        return "User{" +
                "id=" + id +
                ", userName='" + userName + '\'' +
                '}';
    }
}
  
4.2 持久层
接口
package com.gupaoedu.ioc.dao;

import com.gupaoedu.ioc.bean.User;

/**
 * User的Dao
 */
public interface IUserDao {

    /**
     * 获取User对象
     * @return
     */
    User getUser();
}
  
实现
package com.gupaoedu.ioc.dao.impl;

import com.gupaoedu.ioc.bean.User;
import com.gupaoedu.ioc.dao.IUserDao;

public class UserDaoImpl implements IUserDao {

    @Override
    public User getUser() {
        User user = new User();
        user.setId(666);
        user.setUserName("波波");
        System.out.println("持久层...");
        return user;
    }
}
  
4.3 业务层
接口
package com.gupaoedu.ioc.service;

import com.gupaoedu.ioc.bean.User;

/**
 * User Service
 */
public interface IUserService {

    /**
     * 获取User对象
     * @return
     */
    User getUser();
}
    
实现
package com.gupaoedu.ioc.service.impl;

import com.gupaoedu.ioc.bean.User;
import com.gupaoedu.ioc.dao.IUserDao;
import com.gupaoedu.ioc.dao.impl.UserDaoImpl;
import com.gupaoedu.ioc.service.IUserService;

/**
 * User 的Service实现
 */
public class UserServiceImpl implements IUserService {

    // 通过设值注入的方式做赋值  需要提供对应的setter方法
    private IUserDao dao ;

    @Override
    public User getUser() {
        System.out.println("业务逻辑层....");
        return dao.getUser();
    }

    /**
     * 设值注入必须要提供的方法
     * @param dao
     */
    public void setDao(IUserDao dao) {
        this.dao = dao;
    }
}
  
4.4 控制层
接收和响应请求
package com.gupaoedu.ioc.controller;

import com.gupaoedu.ioc.bean.User;
import com.gupaoedu.ioc.service.IUserService;

/**
 * 控制层
 */
public class UserController {

    private IUserService service;

    public User queryUser(){
        System.out.println("控制层...");
        return service.getUser();
    }

    public void setService(IUserService service) {
        this.service = service;
    }
}
    
4.5 配置文件
<?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">

    <!-- 将 控制层  业务层  持久层 的三个实例添加到IoC容器中 -->
    <bean class="com.gupaoedu.ioc.dao.impl.UserDaoImpl" id="userDao"></bean>

    <bean class="com.gupaoedu.ioc.service.impl.UserServiceImpl" id="userService" >
        <!-- 设值注入IUserDao实例 -->
        <property name="dao" ref="userDao"/>
    </bean>

    <bean class="com.gupaoedu.ioc.controller.UserController" id="userController" >
        <!-- 设值注入 IUserService实例 -->
        <property name="service" ref="userService" />
    </bean>
</beans>
     
4.6 测试代码
package com.gupaoedu.ioc;

import com.gupaoedu.ioc.controller.UserController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppStart {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 从IoC容器中获取UserController实例
        UserController u = ac.getBean(UserController.class);
        System.out.println(u.queryUser());
    }
}
  
输出结果

    
最后整体的目录结构

  
5.基于注解的形式
配置文件的实现方式在bean对象比较多的情况下,配置文件会越来越复杂,这时我们可以通过扫描加注解的方式来简化我们的操作。
    
5.1 添加AOP支持依赖
基于注解的方式需要用到AOP的支持,所以我们需要添加AOP的依赖。

      
5.2 添加扫描
通过context标签的component-scan属性指定我们的扫描路径。
<?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-4.3.xsd">

    <!--<bean class="com.gupaoedu.ioc.bean.UserBean" />-->
    <!-- 添加扫码路径 -->
    <context:component-scan base-package="com.gupaoedu.ioc.bean" />
</beans>
  
5.3 @Component注解
/**
* @Component 被该注解标识的Java会被SpringIoC容器加载
*/
@Component
    public class UserBean {
}
  
6. @Resource和@Autowired

在注解的场景下依赖注入我们可以通过 @Resource和@Autowired实现,他们的区别是@Resource 默认是根据name属性来查找的,而@Autowired注解默认是根据类型查找的,@Autowired需要和 @Qualifier配合实现基于name的查找。
package com.gupaoedu.ioc.bean;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;


/**
 * @Component 被该注解标识的Java会被SpringIoC容器加载
 */
@Component
public class UserBean {

    // 从IoC容器中获取一个Cat实例
    //@Resource(name = "cat")
    // 默认是根据类型查找的
    @Autowired
    @Qualifier("a111")
    private Cat cat;

    public Cat getCat() {
        return cat;
    }

    public void setCat(Cat cat) {
        this.cat = cat;
    }

}
   
7.注入的注解

    
设置特殊的扫描条件
<?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-4.3.xsd">

    <!-- 添加扫码 -->
    <!--<context:component-scan base-package="com.gupaoedu.ioc.controller">
</context:component-scan>
    <context:component-scan base-package="com.gupaoedu.ioc.service.impl">
</context:component-scan>
    <context:component-scan base-package="com.gupaoedu.ioc.dao.impl">
</context:component-scan>-->
    <!--<context:component-scan base-package="com.gupaoedu.ioc.*" >
</context:component-scan>-->
    <!-- 不使用 @Component @Controller @Service @Repository 特殊声明使用@Controller-->
    <context:component-scan base-package="com.gupaoedu.ioc.controller" usedefault filters="false">
    <!-- 包含哪个注解 -->
    <context:include-filter type="annotation"expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!-- 使用 @Component @Controller @Service @Repository 但是要排除掉Controller -->
    <context:component-scan base-package="com.gupaoedu.ioc.service.impl,com.gupaoedu.ioc.dao.impl" use-default-filters="true">
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
</beans>
      
在配置文件使用的基础上添加的有注解的使用方式,大大的简化了纯配置文件使用的方式。
   
8.Java配置类
在Spring3.x之后开始支持Java配置类的形式,从而舍弃配置文件的方式。
  
8.1 基本的使用
Java配置文件需要通过@Configuration注解标注,需要加载的实例需要通过@Bean注解标注。
/**
* Java配置类
* 被@Configuration注解标注的Java类就是一个配置类
* 作用和 applicationContext.xml 是一样的
*/

@Configuration
public class JavaConfig {
    /**
    * 被@Bean注解修饰的方法 等价于
    * <bean class="com.bobo.ioc.bean.UserBean" name='getUserBean'><bean/>
    * 默认的name为方法的名称
    * @return
    */
    @Bean("abc")
    public UserBean getUserBean(){
        return new UserBean();
    }
}
      
通过Java配置类的方式使用的话对应的加载类要更换为 AnnotationConfigApplicationContext
public class AppStart {
    public static void main(String[] args) {
        // 基于Java配置类的 IoC初始化
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        // 获取实例
        UserBean user = ac.getBean("abc", UserBean.class);
        System.out.println(user);
    }
}
   
8.2 @ComponentScan注解
基于Java配置类的实现方式中要结合@Component注解使用的话,添加扫描我们需要通过@ComponentScan或者@ComponentScans注解要实现。
package com.bobo.ioc.config;

import com.bobo.ioc.bean.DataSource;
import com.bobo.ioc.bean.UserBean;
import org.springframework.context.annotation.*;

/**
 * Java配置类
 *   被@Configuration注解标注的Java类就是一个配置类
 *   作用和 applicationContext.xml 是一样的
 */
@Configuration
// 设置扫描
//@ComponentScan(basePackages = {"com.bobo.ioc.bean"})
@ComponentScans(@ComponentScan(basePackages = "com.bobo.ioc.bean"))
public class JavaConfig {

    /**
     * 被@Bean注解修饰的方法 等价于
     * <bean class="com.bobo.ioc.bean.UserBean" name='getUserBean'><bean/>
     * 默认的name为方法的名称
     * @return
     */

    @Bean("abc")
    public UserBean getUserBean(){
        UserBean user = new UserBean();
        return user;
    }
}
   
复杂结构
@Configuration
@ComponentScans({
    @ComponentScan(basePackages = {"com.gupaoedu.ioc.controller"}
    ,useDefaultFilters = false
    ,includeFilters = {@ComponentScan.Filter(value ={Controller.class})}
    )
    , @ComponentScan(basePackages = 
    {"com.gupaoedu.ioc.service.impl","com.gupaoedu.ioc.dao.impl"}
    ,useDefaultFilters = true
    ,excludeFilters = {@ComponentScan.Filter(value = Controller.class)})
})
public class JavaConfig {
}
   
8.3 @Conditional注解
我们可以通过@Conditional注解控制Bean对象的载入。
package com.bobo.ioc.config;

import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;

public class ConditionOnClass implements Condition {
    /**
     *
     * @param conditionContext
     * @param annotatedTypeMetadata
     * @return
     *    true 表示条件成立
     *    false 表示条件不成立
     */
    @Override
    public boolean matches(ConditionContext conditionContext
            , AnnotatedTypeMetadata annotatedTypeMetadata) {
        try {
            Class<?> aClass = conditionContext.getClassLoader()
                    .loadClass("com.bobo.ioc.bean.Person");
            if(aClass != null){
                return true;
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        return false;
    }
}
  
使用
/**
     * 被@Bean注解修饰的方法 等价于
     * <bean class="com.bobo.ioc.bean.UserBean" name='getUserBean'><bean/>
     * 默认的name为方法的名称
     * @return
     */
    @Bean("abc")
    @Conditional(ConditionOnClass.class) // 条件注解  决定是否将UserBean添加到IoC容器中
    public UserBean getUserBean(){
        UserBean user = new UserBean();
        return user;
    }
   
8.4 @Profile注解
在实际开发环境中,我们的项目会经历各种不同的环境(开发环境,测试环境,生产环境)
package com.bobo.ioc.bean;

/**
 * 数据源对象
 */
public class DataSource {

    private String url;

    private String userName;

    private String password;

    public DataSource(String url, String userName, String password) {
        this.url = url;
        this.userName = userName;
        this.password = password;
    }

    @Override
    public String toString() {
        return "DataSource{" +
                "url='" + url + '\'' +
                ", userName='" + userName + '\'' +
                ", password='" + password + '\'' +
                '}';
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}
   
@Profile注解使用
 /**
     * 开发环境的数据源
     * @return
     */
    @Bean("ds")
    @Profile("dev")
    public DataSource devDataSource(){
        return new DataSource("http://dev1:8888/","admin","123456");
    }

    /**
     * 生产环境的数据源
     * @return
     */
    @Bean("ds")
    @Profile("pro")
    public DataSource proDataSource(){
        return new DataSource("http://pro1:8888/","root","123456");
    }
     
启动容器设置环境信息。
package com.bobo.ioc;

import com.bobo.ioc.bean.StudentBean;
import com.bobo.ioc.bean.UserBean;
import com.bobo.ioc.config.JavaConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class AppStart {

    public static void main(String[] args) {
        /*// 基于Java配置类的 IoC初始化
        ApplicationContext ac = new AnnotationConfigApplicationContext(JavaConfig.class);
        // 获取实例
        UserBean user = ac.getBean("abc", UserBean.class);
        System.out.println(user);

        System.out.println(ac.getBean(StudentBean.class));*/
        AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext();
        // 设置对应的环境信息
        ac.getEnvironment().setActiveProfiles("pro");
        ac.register(JavaConfig.class);
        ac.refresh();
        System.out.println(ac.getBean("ds"));
    }
}

        

9.Bean的作用域
配置文件
<?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">
    <!--
        scope:默认是 singleton 单例模式,每次获取的都是同一个对象
        prototype 每次从容器中获取的都是一个新的对象
    -->
<bean class="com.bobo.ioc.bean.UserBean" scope="prototype"/>
</beans>
      
Java配置类
@Configuration
    public class JavaConfig {
    @Bean
    @Scope(value = "prototype")
    //@Scope("singleton")
    public UserBean userBean(){
        return new UserBean();
    }
}
  
作业:有自己的语言描述什么是IoC(控制反转)
  

三、Spring AOP

AOP:面向切面编程
OOP:面向对象编程
AOP是OOP的一个有效补充
  
1.代理模式
AOP本身是基于动态代理模式实现的,所以掌握代理模式是我们学好AOP的一个重要的前提条件。

  
1.1 静态代理模式
若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理 ,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下, 静态代理中的代理类和目标类会实现同一接口或是派生自相同的父类。
   
1.1.1 创建公共接口
/**
* 代理模式中的 公共接口
*/
public interface SomeService {
    String doSome(String msg);
}
  
1.1.2 目标对象
/**
* 代理模式中的 目标对象
*/
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String msg) {
        System.out.println("目标对象执行了..." + msg);
        return "hello:" + msg;
    }
}
  
1.1.3 创建代理对象
package com.bobo.aop.proxy;

import com.bobo.aop.service.SomeService;

/**
 * 静态代理类
 */
public class SomeProxy implements SomeService {

    private SomeService target;

    public SomeProxy(SomeService target) {
        this.target = target;
    }

    /**
     * 代理对象实际执行的方法
     * @param msg
     * @return
     */
    @Override
    public String doSome(String msg) {
        System.out.println("目标方法执行之前....");
        // 让目标对象来执行核心业务
        String res = target.doSome(msg);
        System.out.println("目标对象执行之后");
        return res.toUpperCase();
    }
}
    
1.1.4 测试
package com.bobo.aop;

import com.bobo.aop.proxy.SomeProxy;
import com.bobo.aop.service.SomeService;
import com.bobo.aop.service.impl.SomeServiceImpl;

public class AppStart {

    public static void main(String[] args) {
        // 获取目标对象
        SomeService target = new SomeServiceImpl();
        // 获取代理对象
        SomeProxy proxy = new SomeProxy(target);
        // 通过代理对象执行目标对象的方法
        System.out.println(proxy.doSome("abc"));
    }
}
    

  
1.2 动态代理
代理类在程序运行时创建的代理方式被成为动态代理。 也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。

   
1.2.1 动态代理模式之JDK代理
声明接口
package com.bobo.aop.service;

/**
 * 代理模式中的 公共接口
 */
public interface SomeService {

    String doSome(String msg);
}
  
创建目标类
package com.bobo.aop.service.impl;

import com.bobo.aop.service.SomeService;

/**
 * 代理模式中的 目标对象
 */
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String msg) {
        System.out.println("目标对象执行了..." + msg);
        return "hello:" + msg;
    }
}
     
实现代理
package com.bobo.aop;

import com.bobo.aop.service.SomeService;
import com.bobo.aop.service.impl.SomeServiceImpl;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class AppStart {

    public static void main(String[] args) {
        // 获取目标对象
        SomeService target = new SomeServiceImpl();
        // 获取基于JDK的代理对象
        SomeService proxy = (SomeService) Proxy.newProxyInstance(target.getClass().getClassLoader() // 获取一个类加载器
                , target.getClass().getInterfaces() // 获取目标对象实现的所有的接口
                , new InvocationHandler() { // 获取一个InvocationHandler实例
                    /**
                     * 代理对象执行的方法
                     *     会在该方法中调用目标对象的方法
                     * @param proxy
                     * @param method
                     * @param args
                     * @return
                     * @throws Throwable
                     */
                    @Override
                    public Object invoke(Object proxy
                            , Method method, Object[] args) throws Throwable {
                        System.out.println("目标对象执行之前...");
                        // 目标对象方法的执行
                        Object res = method.invoke(target, args[0]);
                        System.out.println("目标对象执行之后...");
                        return res;
                    }
                }
        );
        // 调用代理对象中的相关方法
        System.out.println(proxy.doSome("123456"));
    }
}
   

1.2.3动态代理模式之CGLIB代理
如果目标对象没有实现任何的接口,那么我们是使用不了JDK代理模式的,这时我们只能通过CGLIB代理来实现。CGLIB的实现本质是继承。
    
创建目标对象
/**
* 目标对象
* 目标对象没有实现任何的接口
*/
public class SomeServiceImpl {
    public String doSome(String msg){
        System.out.println("目标对象执行了..." + msg);
        return "hello:" + msg;
    }
}
   
创建CGLIB工厂
package com.bobo.aop.proxy;

import com.bobo.aop.service.impl.SomeServiceImpl;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * CGLIB工厂
 */
public class CglibProxy implements MethodInterceptor {

    // 声明目标对象
    private SomeServiceImpl target;

    public CglibProxy(SomeServiceImpl target) {
        this.target = target;
    }

    /**
     * 对外提供用来创建代理对象的方法
     * @return
     */
    public SomeServiceImpl createProxy(){
        // 创建增强器
        Enhancer enhancer = new Enhancer();
        // 指定父类
        enhancer.setSuperclass(SomeServiceImpl.class);
        // 指定回调接口对象
        enhancer.setCallback(this);
        // 返回创建的cglib代理对象
        return (SomeServiceImpl) enhancer.create();
    }

    /**
     * 和之前的JDK动态代理中的invoke方法类似
     * @param o
     * @param method
     * @param objects
     * @param methodProxy
     * @return
     * @throws Throwable
     */
    @Override
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("目标对象执行之前...");
        // 目标对象方法的执行
        Object res = method.invoke(target, objects[0]);
        System.out.println("目标对象执行之后...");
        return res;
    }
}
   
代码运行
package com.bobo.aop;

import com.bobo.aop.proxy.CglibProxy;
import com.bobo.aop.service.impl.SomeServiceImpl;

public class AppStart {

    public static void main(String[] args) {
        // 获取目标对象
        SomeServiceImpl target = new SomeServiceImpl();
        // 获取CGLIB代理对象
        SomeServiceImpl proxy = new CglibProxy(target).createProxy();
        // 执行方法
        System.out.println(proxy.doSome("abcd"));
    }
}
     

   
2. AOP概念
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented
Programming,面向对象编程)的补充和完善。
面向切面是面向对象中的一种方式而已。在代码执行过程中,动态嵌入其他代码,叫做面向切面编程。常见的使用场景:
        Authentication 权限
        Caching 缓存
        Context passing 内容传递
        Error handling 错误处理
        Lazy loading 懒加载
        Debugging 调试
        logging, tracing, profiling and monitoring 记录跟踪 优化 校准
        Performance optimization 性能优化
        Persistence 持久化
        Resource pooling 资源池
        Synchronization 同步
        Transactions 事务
        ....
   
面向切面编程的几个核心概念

    
面向切面编程,就是将交叉业务逻辑封装成切面,利用AOP的功能将切面织入到主业务逻辑中。所谓交叉业务逻辑是指,通用的、与主业务逻辑无关的代码,如安全检查、事务、日志等。若不使用AOP,则会出现代码纠缠,即交叉业务逻辑与主业务逻辑混合在一起。这样,会使主业务逻辑变的混杂不清。
   
AOP术语介绍

  

   

    
3.AOP的具体实现
3.1 环境准备
在Spring中我们要使用AOP的话需要添加对应的依赖。

    
3.2 基于纯代理的AOP
基于纯代理方式实现的AOP中具有的几种通知类型。

    
3.2.1 前置通知
创建接口
package com.bobo.aop.service;

/**
 * 公共接口
 */
public interface SomeService {

    String doSome(String msg);

    void say();
}
   
创建实现类
package com.bobo.aop.service.impl;

import com.bobo.aop.service.SomeService;

/**
 * 目标对象
 */
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String msg) {
        System.out.println("目标对象doSome执行了..." + msg);
        return "Hello:" + msg;
    }

    @Override
    public void say() {
        System.out.println("目标对象say执行了..." + 1/0 );
    }
}
     
创建前置通知类
package com.bobo.aop.advice;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;
import java.util.Arrays;

/**
 * 前置通知类
 */
public class MyMethodBeforeAdvice implements MethodBeforeAdvice {
    /**
     * 前置通知  抽取的公共代码
     * @param method 目标对象的方法
     * @param objects 目标对象的参数列表
     * @param o 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(method);
        System.out.println(Arrays.toString(objects));
        System.out.println(o);
        System.out.println("前置通知执行了.....");
    }
}
    
配置文件
<?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"
    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/context
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop
    http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService">
</bean>
    <!-- 注册通知类 -->
    <bean class="com.bobo.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice">
</bean>

    <!-- 创建对应的代理类 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
    <!-- 指定目标对象 -->
    <property name="target" ref="someService"/>
    <!-- 指定目标对象实现的所有接口 -->
    <property name="interfaces" value="com.bobo.aop.service.SomeService"/>
    <!-- 指定切面 将通知和切入点关联 -->
    <property name="interceptorNames">
        <list>
            <value>beforeAdvice</value>
        </list>
    </property>
</bean>
</beans>
    
测试操作
package com.bobo.aop;

import com.bobo.aop.service.SomeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppStart {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        SomeService some = ac.getBean("proxyFactoryBean",SomeService.class);
        System.out.println(some);
        System.out.println("************");
        String obj = some.doSome("aaa");
        System.out.println(obj);
        System.out.println("----------");
        some.say();
    }
}
    

    
3.2.2 后置通知
在目标方法执行之后执行的通知。
     
创建后置通知类
package com.bobo.aop.advice;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * 后置通知类
 */
public class MyAfterReturningAdvice implements AfterReturningAdvice {

    /**
     * 后置通知方法
     * @param returnValue
     * @param method
     * @param args
     * @param target
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("后置方法执行了。。。" + returnValue );
    }
}
   
修改配置文件
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"></bean>
    <!-- 注册通知类 -->
    <!-- 前置通知 -->
    <bean class="com.bobo.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
    <!-- 后置通知 -->
    <bean class="com.bobo.aop.advice.MyAfterReturningAdvice" id="afterReturningAdvice"/>
   
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="someService"/>
        <!-- 指定目标对象实现的所有接口 -->
        <property name="interfaces" value="com.bobo.aop.service.SomeService"/>
        <!-- 指定切面 将通知和切入点关联 -->
        <property name="interceptorNames">
            <list>
                <!--<value>beforeAdvice</value>
                <value>afterReturningAdvice</value>-->
                <!--<value>methodInterceptor</value>-->
                <value>throwsAdvice</value>
            </list>
        </property>
    </bean>

</beans>
    
测试

  
3.2.3 环绕通知
环绕通知就是在切入点方法之前前后都会织入的方式,而且环绕通知相比于前置通知和后置通知来说可以修改返回结果。
  
创建通知类
package com.bobo.aop.advice;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 环绕通知类
 */
public class MyMethodInterceptor implements MethodInterceptor {

    /**
     * 环绕通知
     *    可以修改返回结果
     * @param invocation
     * @return
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("目标对象执行之前....");
        Object obj = invocation.proceed(); // 目标对象的调用
        System.out.println("目标对象执行之后....");
        if(obj != null && obj instanceof  String){
            return ((String) obj).toUpperCase();
        }
        return obj;
    }
}
   
修改配置文件
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"></bean>
    <!-- 注册通知类 -->
    <!-- 前置通知 -->
    <bean class="com.bobo.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
    <!-- 后置通知 -->
    <bean class="com.bobo.aop.advice.MyAfterReturningAdvice" id="afterReturningAdvice"/>
    <!-- 环绕通知 -->
    <bean class="com.bobo.aop.advice.MyMethodInterceptor" id="methodInterceptor"/>

  
    <!-- 创建对应的代理类 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="someService"/>
        <!-- 指定目标对象实现的所有接口 -->
        <property name="interfaces" value="com.bobo.aop.service.SomeService"/>
        <!-- 指定切面 将通知和切入点关联 -->
        <property name="interceptorNames">
            <list>
                <!--<value>beforeAdvice</value>
                <value>afterReturningAdvice</value>-->
                <!--<value>methodInterceptor</value>-->
                <value>throwsAdvice</value>
            </list>
        </property>
    </bean>

</beans>
  
测试

   
3.2.4 异常通知
当切入点方法执行抛异常的时候会触发的通知类型。
   
创建通知类
package com.bobo.aop.advice;

import org.springframework.aop.ThrowsAdvice;

/**
 * 异常通知类
 */
public class MyThrowsAdvice implements ThrowsAdvice {

    /**
     * 异常通知
     * @param ex
     */
    public void afterThrowing(Exception ex){
        System.out.println("异常通知执行了..." + ex.getStackTrace());
    }
}
  
修改配置文件
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">


    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"></bean>
    <!-- 注册通知类 -->
    <!-- 前置通知 -->
    <bean class="com.bobo.aop.advice.MyMethodBeforeAdvice" id="beforeAdvice"></bean>
    <!-- 后置通知 -->
    <bean class="com.bobo.aop.advice.MyAfterReturningAdvice" id="afterReturningAdvice"/>
    <!-- 环绕通知 -->
    <bean class="com.bobo.aop.advice.MyMethodInterceptor" id="methodInterceptor"/>

    <!-- 异常通知 -->
    <bean class="com.bobo.aop.advice.MyThrowsAdvice" id="throwsAdvice"/>
    <!-- 创建对应的代理类 -->
    <bean class="org.springframework.aop.framework.ProxyFactoryBean" id="proxyFactoryBean">
        <!-- 指定目标对象 -->
        <property name="target" ref="someService"/>
        <!-- 指定目标对象实现的所有接口 -->
        <property name="interfaces" value="com.bobo.aop.service.SomeService"/>
        <!-- 指定切面 将通知和切入点关联 -->
        <property name="interceptorNames">
            <list>
                <!--<value>beforeAdvice</value>
                <value>afterReturningAdvice</value>-->
                <!--<value>methodInterceptor</value>-->
                <value>throwsAdvice</value>
            </list>
        </property>
    </bean>

</beans>
  
添加异常信息

     
测试

   
3.3 基于AspectJ的AOP
对于AOP这种编程思想,很多框架都进行了实现。Spring就是其中之一,可以完成面向切面编程。然而,AspectJ也实现了AOP的功能,且其实现方式更为简捷,使用更为方便,而且还支持注解式开发。所以,Spring又将AspectJ的对于AOP的实现也引入到了自己的框架中。在Spring中使用AOP开发时,一般使用AspectJ的实现方式。
    
ActJ中的通知类型

  
AspectJ的切入点表达式
execution(
    [modifiers-pattern] 访问权限类型
    ret-type-pattern 返回值类型
    [declaring-type-pattern] 全限定性类名
    name-pattern(param-pattern) 方法名(参数名)
    [throws-pattern] 抛出异常类型
)
    
切入点表达式要匹配的对象就是目标方法的方法名。所以,execution表达式中明显就是方法的签名。注意,表达式中加 [ ] 的部分表示可省略部分,各部分间用空格分开。在其中可以使用以下符号。

  
举例:

  
3.3.1 基于注解的实现
3.3.1.1 前置通知
创建对应的接口和实现
package com.bobo.aop.service;

/**
 * 公共接口
 */
public interface SomeService {

    String doSome(String msg);

    void say();
}
package com.bobo.aop.service.impl;

import com.bobo.aop.service.SomeService;

/**
 * 目标对象
 */
public class SomeServiceImpl implements SomeService {
    @Override
    public String doSome(String msg) {
        System.out.println("目标对象doSome执行了..." + msg);
        return "Hello:" + msg;
    }

    @Override
    public void say() {
        System.out.println("目标对象say执行了..."  + 1 / 0);
    }
}
   
创建对应的切面类

/**
 * 切面类
 *   @Aspect 该注解标注的 Java类就是一个切面类
 */
@Aspect
public class MyAspect {

    /**
     * 前置通知
     *   通过切入点表达式来使 通知和切入点关联
     */
    //@Before(value = "execution(* com.bobo.aop.service.*.*(..))")
    public void beforeMethod(){
        System.out.println("前置通知");
    }
}
  
配置文件
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>
    <!-- 注册切面类 -->
    <bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>

    <!-- 注册自动代理 -->
    <aop:aspectj-autoproxy/>
</beans>
    
也可以使用Java配置类的方式
package com.bobo.aop.config;

import com.bobo.aop.aspect.MyAspect;
import com.bobo.aop.service.SomeService;
import com.bobo.aop.service.impl.SomeServiceImpl;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@EnableAspectJAutoProxy
public class JavaConfig {

    @Bean
    public SomeService someService(){
        return new SomeServiceImpl();
    }
    @Bean
    public MyAspect myAspect(){
        return new MyAspect();
    }
}
    
测试
package com.bobo.aop;

import com.bobo.aop.service.SomeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppStart {
    public static void main(String[] args) {
        ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
        SomeService service = ac.getBean(SomeService.class);
        System.out.println(service);
        service.doSome("aaaa");
        System.out.println("-------------");
        service.say();
    }
}
  

  
3.3.1.2 后置通知
在前面的基础上我们只需要在切面类中添加对应的后置通知即可。

/**
 * 切面类
 *   @Aspect 该注解标注的 Java类就是一个切面类
 */
@Aspect
public class MyAspect {

    /**
     * 前置通知
     *   通过切入点表达式来使 通知和切入点关联
     */
    //@Before(value = "execution(* com.bobo.aop.service.*.*(..))")
    public void beforeMethod(){
        System.out.println("前置通知");
    }

    /**
     * 后置通知
     */
    //@AfterReturning(value = "execution(* com.bobo.aop.service.*.*(..))",returning = "msg")
    public void afterReturningMethod(Object msg){
        System.out.println("后置通知..." + msg);
    }

}
  

  
3.3.1.3 环绕通知
切面类中添加对应的环绕通知,通过切入点表达式和切入点关联。
/**
     * 环绕通知
     * @return
     */
    //@Around(value = "execution(* com.bobo.aop.service.*.*(..))")
    public Object aroundMethod(ProceedingJoinPoint pjp){
        System.out.println("执行之前...");
        // 执行目标对象的方法
        Object proceed = null;
        try {
             proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("执行之后...");
        return proceed;
    }
   

   
3.3.1.4 异常通知
/**
     * 异常通知
     * @param ex
     */
    @AfterThrowing(value = "execution(* com.bobo.aop.service.*.*(..))",throwing = "ex")
    public void throwsMethod(Exception ex){
        System.out.println("异常通知..." + ex);
    }
   

   
3.3.1.5 最终通知
 /**
     * 最终通知
     *    只要切入点方法执行了,那么最终通知一定会执行
     *    我们可以用来做一些资源回收等操作
     */
    @After(value = "execution(* com.bobo.aop.service.*.*(..))")
    public void afterMethod(){
        System.out.println("最终通知...");
    }
   

   
3.3.2 基于配置文件的实现
在前一个案例基础上我们来实现。
    
3.3.2.1 前置通知
切面类
package com.bobo.aop.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;

/**
 * 切面类
 *   @Aspect 该注解标注的 Java类就是一个切面类
 */

public class MyAspect {

    /**
     * 前置通知
     *   通过切入点表达式来使 通知和切入点关联
     */
    public void beforeMethod(){
        System.out.println("前置通知");
    }

    /**
     * 后置通知
     */
    public void afterReturningMethod(Object msg){
        System.out.println("后置通知..." + msg);
    }

    /**
     * 环绕通知
     * @return
     */
    public Object aroundMethod(ProceedingJoinPoint pjp){
        System.out.println("执行之前...");
        // 执行目标对象的方法
        Object proceed = null;
        try {
             proceed = pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("执行之后...");
        return proceed;
    }

    /**
     * 异常通知
     * @param ex
     */
    public void throwsMethod(Exception ex){
        System.out.println("异常通知..." + ex);
    }

    /**
     * 最终通知
     *    只要切入点方法执行了,那么最终通知一定会执行
     *    我们可以用来做一些资源回收等操作
     */
    public void afterMethod(){
        System.out.println("最终通知...");
    }
}
    
配置文件
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>
    <!--  注册切面类 -->
    <bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>
    <!-- AspectJ AOP 配置 -->
    <aop:config>
        <!--<aop:pointcut id="" expression=""/>-->
        <!-- 配置切面类 -->
        <aop:aspect ref="myAspect">
            <!-- 通知和切入点关联  用到切入点表达式 -->
            <!--<aop:before method="beforeMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))"/>-->
        </aop:aspect>
    </aop:config>
</beans>
  

    
3.3.2.2 后置通知
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>
    <!--  注册切面类 -->
    <bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>
    <!-- AspectJ AOP 配置 -->
    <aop:config>
        <!--<aop:pointcut id="" expression=""/>-->
        <!-- 配置切面类 -->
        <aop:aspect ref="myAspect">
            <!-- 通知和切入点关联  用到切入点表达式 -->
            <!--<aop:before method="beforeMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))"/>-->
            <!-- 配置后置通知 -->
            <!--<aop:after-returning method="afterReturningMethod" returning="msg" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
        </aop:aspect>
    </aop:config>
</beans>
  

   
3.3.2.3 环绕通知
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>
    <!--  注册切面类 -->
    <bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>
    <!-- AspectJ AOP 配置 -->
    <aop:config>
        <!--<aop:pointcut id="" expression=""/>-->
        <!-- 配置切面类 -->
        <aop:aspect ref="myAspect">
            <!-- 通知和切入点关联  用到切入点表达式 -->
            <!--<aop:before method="beforeMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))"/>-->
            <!-- 配置后置通知 -->
            <!--<aop:after-returning method="afterReturningMethod" returning="msg" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
            <!--<aop:around method="aroundMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
        </aop:aspect>
    </aop:config>
</beans>
  

  
3.3.2.4 异常通知
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>
    <!--  注册切面类 -->
    <bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>
    <!-- AspectJ AOP 配置 -->
    <aop:config>
        <!--<aop:pointcut id="" expression=""/>-->
        <!-- 配置切面类 -->
        <aop:aspect ref="myAspect">
            <!-- 通知和切入点关联  用到切入点表达式 -->
            <!--<aop:before method="beforeMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))"/>-->
            <!-- 配置后置通知 -->
            <!--<aop:after-returning method="afterReturningMethod" returning="msg" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
            <!--<aop:around method="aroundMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
            <aop:after-throwing method="throwsMethod" throwing="ex" pointcut="execution(* com.bobo.aop.service.*.*(..))" />
        </aop:aspect>
    </aop:config>
</beans>
  

   
3.3.2.5 最终通知
<?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"
       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/context
       http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 注册目标类 -->
    <bean class="com.bobo.aop.service.impl.SomeServiceImpl" id="someService"/>
    <!--  注册切面类 -->
    <bean class="com.bobo.aop.aspect.MyAspect" id="myAspect"/>
    <!-- AspectJ AOP 配置 -->
    <aop:config>
        <!--<aop:pointcut id="" expression=""/>-->
        <!-- 配置切面类 -->
        <aop:aspect ref="myAspect">
            <!-- 通知和切入点关联  用到切入点表达式 -->
            <!--<aop:before method="beforeMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))"/>-->
            <!-- 配置后置通知 -->
            <!--<aop:after-returning method="afterReturningMethod" returning="msg" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
            <!--<aop:around method="aroundMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))" />-->
            <aop:after-throwing method="throwsMethod" throwing="ex" pointcut="execution(* com.bobo.aop.service.*.*(..))" />
            <aop:after method="afterMethod" pointcut="execution(* com.bobo.aop.service.*.*(..))" />
        </aop:aspect>
    </aop:config>
</beans>
   

   

四、Spring事务管理

1.JdbcTemplate
简化Jdbc操作的步骤、模板化数据库操作。
   
1.1 基本操作
导入相关的依赖

   
配置JdbcTemplate
在配置文件中配置JdbcTemplate相关的信息
<?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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/context
        http://www.springframework.org/schema/context/spring-context.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">

    <context:component-scan base-package="com.bobo.aop.dao.impl,com.bobo.aop.service.impl"/>

    <!-- 注册一个DataSource -->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <!-- 设置注入相关的数据库信息 -->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8" />
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- 注册一个JdbcTemplate对象 -->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!-- 构造注入关联数据源 -->
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>
</beans>
    
数据库操作
package com.bobo.aop.dao.impl;

import com.bobo.aop.bean.User;
import com.bobo.aop.dao.IUserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@Repository
public class UserDaoImpl implements IUserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private String sql;

    @Override
    public int addUser(User user) {
        sql = "insert into t_user(username,address,gender)values(?,?,?)";
        return jdbcTemplate.update(sql,user.getUsername(),user.getAddress(),user.getGender());
    }

    @Override
    public int deleteUserById(Integer id) {
        sql = "delete from t_user where id = ?";
        return jdbcTemplate.update(sql,id);
    }

    @Override
    public int updateUser(User user) {
        sql = "update t_user set username=?,address=?,gender=? where id = ?";
        return jdbcTemplate.update(sql,user.getUsername(),user.getAddress(),user.getGender(),user.getId());
    }

    @Override
    public List<User> queryList() {
        sql = "select * from t_user";
        return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(){
            /**
             * 每获取一行记录就会回调一次本方法
             * @param rs
             * @param rowNumber
             * @return
             * @throws SQLException
             */
            @Override
            public User mapRow(ResultSet rs, int rowNumber) throws SQLException {
                // 我们自己将查询出来的这行记录转换为我们期望的Java对象
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setAddress(rs.getString("address"));
                user.setGender(rs.getString("gender"));
                return user;
            }
        });

    }

    @Override
    public User queryUserById(Integer id) {
        sql = "select * from t_user where id = ?";
        Integer[] ids = {id};
        List<User> lists = jdbcTemplate.query(sql, ids, new BeanPropertyRowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNumber) throws SQLException {
                // 我们自己将查询出来的这行记录转换为我们期望的Java对象
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setAddress(rs.getString("address"));
                user.setGender(rs.getString("gender"));
                return user;
            }
        });
        if(lists != null && lists.size() == 1){
            return lists.get(0);
        }
        return null;
    }
}
    
2.Spring的事务管理
2.1 事务相关概念介绍
数据库事务(Database Transaction) ,是指作为单个逻辑工作单元执行的一系列操作,要么完全地执行,要么完全地不执行。 事务处理可以确保除非事务性单元内的所有操作都成功完成,否则不会永久更新面向数据的资源。通过将一组相关操作组合为一个要么全部成功要么全部失败的单元,可以简化错误恢复并使应用程序更加可靠。一个逻辑工作单元要成为事务,必须满足所谓的ACID(原子性、一致性、隔离性和持久性)属性。事务是数据库运行中的逻辑工作单位,由DBMS中的事务管理子系统负责事务的处理。

  
2.2 事务案例准备
准备一个产生非事务管理的问题案例
Dao中提供正常的数据库操作方法
package com.bobo.aop.dao.impl;

import com.bobo.aop.bean.User;
import com.bobo.aop.dao.IUserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;

@Repository
public class UserDaoImpl implements IUserDao {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    private String sql;

    @Override
    public int addUser(User user) {
        sql = "insert into t_user(username,address,gender)values(?,?,?)";
        return jdbcTemplate.update(sql,user.getUsername(),user.getAddress(),user.getGender());
    }

    @Override
    public int deleteUserById(Integer id) {
        sql = "delete from t_user where id = ?";
        return jdbcTemplate.update(sql,id);
    }

    @Override
    public int updateUser(User user) {
        sql = "update t_user set username=?,address=?,gender=? where id = ?";
        return jdbcTemplate.update(sql,user.getUsername(),user.getAddress(),user.getGender(),user.getId());
    }

    @Override
    public List<User> queryList() {
        sql = "select * from t_user";
        return jdbcTemplate.query(sql,new BeanPropertyRowMapper<User>(){
            /**
             * 每获取一行记录就会回调一次本方法
             * @param rs
             * @param rowNumber
             * @return
             * @throws SQLException
             */
            @Override
            public User mapRow(ResultSet rs, int rowNumber) throws SQLException {
                // 我们自己将查询出来的这行记录转换为我们期望的Java对象
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setAddress(rs.getString("address"));
                user.setGender(rs.getString("gender"));
                return user;
            }
        });

    }

    @Override
    public User queryUserById(Integer id) {
        sql = "select * from t_user where id = ?";
        Integer[] ids = {id};
        List<User> lists = jdbcTemplate.query(sql, ids, new BeanPropertyRowMapper<User>() {
            @Override
            public User mapRow(ResultSet rs, int rowNumber) throws SQLException {
                // 我们自己将查询出来的这行记录转换为我们期望的Java对象
                User user = new User();
                user.setId(rs.getInt("id"));
                user.setUsername(rs.getString("username"));
                user.setAddress(rs.getString("address"));
                user.setGender(rs.getString("gender"));
                return user;
            }
        });
        if(lists != null && lists.size() == 1){
            return lists.get(0);
        }
        return null;
    }
}
  
然后在service中我们提供复杂的业务处理方法,
package com.bobo.aop.service.impl;

import com.bobo.aop.bean.User;
import com.bobo.aop.dao.IUserDao;
import com.bobo.aop.service.IUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class UserServiceImpl implements IUserService {

    @Autowired
    private IUserDao dao;


    /**
     * @Transactional 表示开启事务
     * @return
     */
    @Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.DEFAULT)
    @Override
    public int fun1() {
        // 添加数据
        User user = new User();
        user.setUsername("王五256");
        user.setAddress("长沙256");
        user.setGender("男");
        // 添加用户信息
        dao.addUser(user);
        user.setId(5);
        // 更新用户信息
        dao.updateUser(user);
        return 0;
    }
}
  
就会出现一个成功了一个没有成功的问题。
  
2.3 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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/context
        http://www.springframework.org/schema/context/spring-context.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">

    <context:component-scan base-package="com.bobo.aop.dao.impl,com.bobo.aop.service.impl"/>

    <!-- 注册一个DataSource -->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <!-- 设置注入相关的数据库信息 -->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8" />
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- 注册一个JdbcTemplate对象 -->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!-- 构造注入关联数据源 -->
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <!--
        Spring中使用XML配置事务有三大步骤
        1、创建事务管理器
        2、配置事务方法
        3、配置AOP
     -->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <!-- 管理数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:advice id="interceptor" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="fun*" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置AOP -->
    <aop:config>
        <aop:pointcut id="tx" expression="execution(* com.bobo.aop.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="interceptor" pointcut-ref="tx"/>
    </aop:config>
</beans>
     
2.4 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:context="http://www.springframework.org/schema/context"
       xmlns:tx="http://www.springframework.org/schema/tx"
       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/context
        http://www.springframework.org/schema/context/spring-context.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">

    <context:component-scan base-package="com.bobo.aop.dao.impl,com.bobo.aop.service.impl"/>

    <!-- 注册一个DataSource -->
    <bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
        <!-- 设置注入相关的数据库信息 -->
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/shop?serverTimezone=GMT%2B8&amp;useUnicode=true&amp;characterEncoding=utf-8" />
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
    </bean>
    <!-- 注册一个JdbcTemplate对象 -->
    <bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
        <!-- 构造注入关联数据源 -->
        <constructor-arg name="dataSource" ref="dataSource"/>
    </bean>

    <!--
        Spring中使用XML配置事务有三大步骤
        1、创建事务管理器
        2、配置事务方法
        3、配置AOP
     -->
    <bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
        <!-- 管理数据源 -->
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:advice id="interceptor" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="fun*" propagation="REQUIRED" isolation="DEFAULT"/>
        </tx:attributes>
    </tx:advice>
    <!-- 配置AOP -->
    <aop:config>
        <aop:pointcut id="tx" expression="execution(* com.bobo.aop.service.impl.*.*(..))"/>
        <aop:advisor advice-ref="interceptor" pointcut-ref="tx"/>
    </aop:config>
    <!-- 基于注解的方式实现
    <tx:annotation-driven transaction-manager="transactionManager" />-->
</beans>
   
在需要被事务管理的方法的头部添加 @Transcational 即可。

  
3.事务的传播属性
Spring中的7个事务传播行为:

  
举例说明
案例代码
ServiceA
ServiceA {
    void methodA() {
        ServiceB.methodB();
    }
}
12345
  
ServiceB
ServiceB {
    void methodB() {
    }
}
1234
  
1.PROPAGATION_REQUIRED
假如当前正要运行的事务不在另外一个事务里,那么就起一个新的事务。比方说,ServiceB.methodB的事务级别定义PROPAGATION_REQUIRED, 那么因为执行ServiceA.methodA的时候,ServiceA.methodA已经起了事务。这时调用ServiceB.methodB,ServiceB.methodB看到自己已经执行在ServiceA.methodA的事务内部。就不再起新的事务。而假如ServiceA.methodA执行的时候发现自己没有在事务中,他就会为自己分配一个事务。这样,在ServiceA.methodA或者在ServiceB.methodB内的不论什么地方出现异常。事务都会被回滚。即使ServiceB.methodB的事务已经被提交,可是ServiceA.methodA在接下来fail要回滚,ServiceB.methodB也要回滚。

   
2.PROPAGATION_SUPPORTS
假设当前在事务中。即以事务的形式执行。假设当前不在一个事务中,那么就以非事务的形式执行。
   
3.PROPAGATION_MANDATORY
必须在一个事务中执行。也就是说,他仅仅能被一个父事务调用。否则,他就要抛出异常。
  

4.PROPAGATION_REQUIRES_NEW
这个就比较绕口了。 比方我们设计ServiceA.methodA的事务级别为PROPAGATION_REQUIRED,ServiceB.methodB的事务级别为PROPAGATION_REQUIRES_NEW。那么当运行到ServiceB.methodB 的时候,ServiceA.methodA所在的事务就会挂起。ServiceB.methodB会起一个新的事务。等待ServiceB.methodB的事务完毕以后,他才继续运行。
他与PROPAGATION_REQUIRED 的事务差别在于事务的回滚程度了。由于ServiceB.methodB是新起一个事务,那么就是存在两个不同的事务。假设ServiceB.methodB已经提交,那么ServiceA.methodA失败回滚。ServiceB.methodB是不会回滚的。假设ServiceB.methodB失败回滚,假设他抛出的异常被ServiceA.methodA捕获,ServiceA.methodA事务仍然可能提交。
  

   
5.PROPAGATION_NOT_SUPPORTED
当前不支持事务。比方ServiceA.methodA的事务级别是PROPAGATION_REQUIRED 。而ServiceB.methodB的事务级别是PROPAGATION_NOT_SUPPORTED ,那么当执行到ServiceB.methodB时。ServiceA.methodA的事务挂起。而他以非事务的状态执行完,再继续ServiceA.methodA的事务。
   
6.PROPAGATION_NEVER
不能在事务中执行。
如果ServiceA.methodA的事务级别是PROPAGATION_REQUIRED。 而ServiceB.methodB的事务级别是PROPAGATION_NEVER ,那么ServiceB.methodB就要抛出异常了。
   
7.PROPAGATION_NESTED
如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与PROPAGATION_REQUIRED类似的操作。
   
Spring中事务的配置
配置文件的方式
<tx:advice id="txAdvice" transaction-manager="txManager">
    <tx:attributes>
    <!--设置所有匹配的方法,然后设置传播级别和事务隔离-->
        <tx:method name="save*" propagation="REQUIRED" />
        <tx:method name="add*" propagation="REQUIRED" />
        <tx:method name="create*" propagation="REQUIRED" />
        <tx:method name="insert*" propagation="REQUIRED" />
        <tx:method name="update*" propagation="REQUIRED" />
        <tx:method name="merge*" propagation="REQUIRED" />
        <tx:method name="del*" propagation="REQUIRED" />
        <tx:method name="remove*" propagation="REQUIRED" />
        <tx:method name="put*" propagation="REQUIRED" />
        <tx:method name="get*" propagation="SUPPORTS" read-only="true" />
        <tx:method name="count*" propagation="SUPPORTS" read-only="true" />
        <tx:method name="find*" propagation="SUPPORTS" read-only="true" />
        <tx:method name="list*" propagation="SUPPORTS" read-only="true" />
        <tx:method name="*" propagation="SUPPORTS" read-only="true" />
    </tx:attributes>
</tx:advice>
12345678910111213141516171819
  
注解的方式
<!--开启注解的方式-->
<tx:annotation-driven transaction-manager="transactioManager" />
12
   
@Transactional(propagation=Propagation.REQUIRED)
如果有事务,那么加入事务,没有的话新建一个(默认情况下)。
@Transactional(propagation=Propagation.NOT_SUPPORTED)
容器不为这个方法开启事务。
@Transactional(propagation=Propagation.REQUIRES_NEW)
不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务。
@Transactional(propagation=Propagation.MANDATORY)
必须在一个已有的事务中执行,否则抛出异常。
@Transactional(propagation=Propagation.NEVER)
必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)。
@Transactional(propagation=Propagation.SUPPORTS)
如果其他bean调用这个方法,在其他bean中声明事务,那就用事务。如果其他bean没有声明事务,那就不用事务。
  
4.事务的隔离级别
事务隔离级别指的是一个事务对数据的修改与另一个并行的事务的隔离程度,当多个事务同时访问相同数据时,如果没有采取必要的隔离机制,就可能发生以下问题:

   
Spring支持的隔离级别

  

  
再必须强调一遍,不是事务隔离级别设置得越高越好,事务隔离级别设置得越高,意味着势必要花手段去加锁用以保证事务的正确性,那么效率就要降低,因此实际开发中往往要在效率和并发正确性之间做一个取舍,一般情况下会设置为READ_COMMITED,此时避免了脏读,并发性也还不错,之后再通过一些别的手段去解决不可重复读和幻读的问题就好了。
  
Spring设置事务隔离级别
配置文件的方式
<tx:advice id="advice" transaction-manager="transactionManager">
    <tx:attributes>
        <tx:method name="fun*" propagation="REQUIRED" isolation="DEFAULT"/>
    </tx:attributes>
</tx:advice>
12345
  
注解的方式
@Transactional(isolation=Isolation.DEFAULT)
public void fun(){
    dao.add();
    dao.udpate();
}
12345
  

小结

Spring建议的是使用DEFAULT,就是数据库本身的隔离级别,配置好数据库本身的隔离级别,无论在哪个框架中读写数据都不用操心了。而且万一Spring没有把这几种隔离级别实现的很完善,出了问题就麻烦了。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/266071.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞

20231220将NanoPC-T4(RK3399)开发板的Android10的SDK按照Rockchip官方挖掘机开发板编译打包刷机之后启动跑飞 2023/12/20 17:19 简略步骤&#xff1a;rootrootrootroot-X99-Turbo:~/3TB$ tar --use-compress-programpigz -xvpf rk3399-android-10.git-20210201.tgz rootrootro…

HarmonyOS4.0系统性深入开发02 UIAbility组件详解(上)

UIAbility组件概述 概述 UIAbility组件是一种包含UI界面的应用组件&#xff0c;主要用于和用户交互。 UIAbility组件是系统调度的基本单元&#xff0c;为应用提供绘制界面的窗口&#xff1b;一个UIAbility组件中可以通过多个页面来实现一个功能模块。每一个UIAbility组件实例…

智能优化算法应用:基于爬行动物算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于爬行动物算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于爬行动物算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.爬行动物算法4.实验参数设定5.算法结果6.…

Android Canvas画布saveLayer与对应restoreToCount,Kotlin

Android Canvas画布saveLayer与对应restoreToCount&#xff0c;Kotlin private fun mydraw() {val originBmp BitmapFactory.decodeResource(resources, R.mipmap.pic).copy(Bitmap.Config.ARGB_8888, true)val newBmp Bitmap.createBitmap(originBmp.width, originBmp.heigh…

【数据结构一】初始Java集合框架(前置知识)

Java中的数据结构 Java语言在设计之初有一个非常重要的理念便是&#xff1a;write once&#xff0c;run anywhere&#xff01;所以Java中的数据结构是已经被设计者封装好的了&#xff0c;我们只需要实例化出想使用的对象&#xff0c;便可以操作相应的数据结构了&#xff0c;本篇…

数值分析期末复习

第一章 科学计算 误差 解题步骤 先求绝对误差: ∣ x − x ∗ ∣ |x - x^*| ∣x−x∗∣求相对误差限: ∣ x − x ∗ ∣ x ∗ \frac{|x\,\,-\,\,x^*|}{x^*} x∗∣x−x∗∣​求有效数字 ∣ x − x ∗ ∣ 需要小于它自身的半个单位 |x-x^*|\text{需要小于它自身的半个单位} ∣…

qt简单连接摄像头

要使用摄像头&#xff0c;就需要链接多媒体模块以及多媒体工具模块 需要在.pro文件中添加QT multimedia multimediawidgets 是用的库文件 QCamera 类用于打开系统的摄像头设备&#xff0c; QCameraViewfinder 用于显示捕获的视频&#xff0c; QCameraImageCapt…

3D 纹理贴图基础知识

在线工具推荐&#xff1a; 3D数字孪生场景编辑器 - GLTF/GLB材质纹理编辑器 - 3D模型在线转换 - Three.js AI自动纹理开发包 - YOLO 虚幻合成数据生成器 - 三维模型预览图生成器 - 3D模型语义搜索引擎 介绍 纹理贴图是创建模型时离不开的最后一块拼图。同样&#xff0c;…

陈可之|三峡|《河水不犯井水的游戏》

《河水不犯井水的游戏》 尺寸&#xff1a;130x90cm 陈可之2007年绘 油画《河水不犯井水的游戏》是陈可之先生三峡系列作品之一&#xff0c;巧借古代传说&#xff0c;并具象化为螃蟹、乌龟、长江水&#xff0c;描绘了一幅和谐共生的画面。 画面右侧&#xff0c;蜿蜒宽阔的长江水…

内存函数的学习

额外知识点 第一个 假设c为int类型&#xff0c;&#xff08;char&#xff09;c之后&#xff0c;之后如果还用变量c的话&#xff0c;c依然为int类型。&#xff08;&#xff09;强制转换操作符并不会永久改变原本的变量类型。 第二个 \0在打印时不会显示出来 第三个 void …

Latex生成的PDF中加入书签/Navigation/导航

本文参考&#xff1a;【Latex学习】在生成pdf中加入书签/目录/提纲_latex 书签-CSDN博客 &#xff08;这篇文章写的真的太棒了&#xff01;非常推荐&#xff09; 题外话&#xff0c;我的碎碎念&#xff0c;这也是我如何提高搜索能力的办法&#xff1a;想在Latex生成的PDF中加入…

解决用Fiddler抓包,网页显示你的连接不是专用/私密连接

关键&#xff1a;重置fiddler的证书 在Fiddler重置证书 1、Actions --> Reset All Certificates --> 弹窗一路yes 2、关掉Fiddler&#xff0c;重新打开 3、手机删掉证书&#xff0c;重新下载安装。 &#xff08;如果还不行&#xff0c;重新试一遍&#xff0c;先把浏览器…

用python对航空公司客户价值进行聚类分析

1.实验目的 1.会用Python创建KMeans聚类分析模型&#xff1b; 2.使用KMeans模型对航空公司客户价值进行聚类分析&#xff1b; 3.会对聚类结果进行分析 2.实验设备 Jupyter notebook 3.实验原理 4.实验内容 使用sklearn.cluester的KMeans类对航空公司客户数据进行聚类分析&…

Springboot+vue的装饰工程管理系统(有报告),Javaee项目,springboot vue前后端分离项目

演示视频&#xff1a; Springbootvue的装饰工程管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的装饰工程管理系统&#xff0c;采用M&#xff08…

TSINGSEE青犀可视化视频云平台JT/T1078接入能力在智慧物流中的应用

一、引言 随着科技的快速发展和全球贸易的蓬勃发展&#xff0c;智慧物流成为了现代物流业的重要发展方向。智慧物流通过引入先进的信息技术&#xff0c;实现了物流过程的自动化、智能化和信息化&#xff0c;从而提高了物流效率和准确性。在这个过程中&#xff0c;JT/T1078接入…

天文观测与计算机技术:粉丝的视角

天文观测与计算机技术&#xff1a;粉丝的视角 在浩瀚无垠的宇宙中&#xff0c;每一颗星星都像是神秘的密码&#xff0c;等待我们去破译。作为天文观测的粉丝&#xff0c;我深知每一个美丽的星图背后都离不开计算机技术的支持。在这篇文章中&#xff0c;我将分享我对天文观测与…

蓝桥杯第二场小白入门赛(1~5)(对不起,我线段树太菜了)

1.模拟 2.贪心 3.二分 4.数论 5.数论 6.线段树&#xff08;线段树还是练少了...&#xff09; 1. 蓝桥小课堂-平方和 直接模拟&#xff0c;注意数据范围 #include <bits/stdc.h> using namespace std; #define LL long long #define pb push_back #define x first …

华清远见嵌入式学习——ARM——作业2

目录 作业要求&#xff1a; 现象&#xff1a; 代码&#xff1a; 思维导图&#xff1a; 模拟面试题&#xff1a; 作业要求&#xff1a; GPIO实验——3颗LED灯的流水灯实现 现象&#xff1a; 代码&#xff1a; .text .global _start _start: 设置GPIOEF时钟使能 0X50000…

【Linux驱动】字符设备驱动程序框架 | LED驱动

&#x1f431;作者&#xff1a;一只大喵咪1201 &#x1f431;专栏&#xff1a;《RTOS学习》 &#x1f525;格言&#xff1a;你只管努力&#xff0c;剩下的交给时间&#xff01; 目录 &#x1f3c0;Hello驱动程序⚽驱动程序框架⚽编程 &#x1f3c0;LED驱动⚽配置GPIO⚽编程驱动…

【Linux C | 文件I/O】文件的读写 | read、write、lseek 函数

&#x1f601;博客主页&#x1f601;&#xff1a;&#x1f680;https://blog.csdn.net/wkd_007&#x1f680; &#x1f911;博客内容&#x1f911;&#xff1a;&#x1f36d;嵌入式开发、Linux、C语言、C、数据结构、音视频&#x1f36d; &#x1f923;本文内容&#x1f923;&a…