参考
https://www.processon.com/view/5bbac847e4b015327afb89dc?fromnew=1

一、Spring概述

Spring简介

Spring : 春天 —->给软件行业带来了春天 2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。 2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。 很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。 Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术

官网 : http://spring.io/
官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring/
GitHub : https://github.com/spring-projects

Spring优点

1、Spring是一个开源免费的框架 (容器 )
2、Spring是一个轻量级的框架 , 非侵入式的
3、控制反转 IOC
4、面向切面 Aop
5、对事物的支持 , 对其他框架的支持

一句话概括:
Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)

Spring体系结构

image.png
Spring 由 20 多个模块组成,它们可以分为:数据访问/集成(Data Access/Integration)、Web、面向切面编程(AOP, Aspects)、提供 JVM的代理(Instrumentation)、消息发送(Messaging)、核心容器(Core Container)和测试(Test)。

二、IOC容器

1、理解容器

控制反转(IOC,Inversion of Control),是一个概念,是一种思想。指将传统上由程序代码直接操控的对象调用权交给容器,通过容器来实现对象的装配和管理。控制反转就是对对象控制权的转移,从程序代码本身反转到了外部容器。通过容器实现对象的创建,属性赋值,依赖的管理。 IoC 是一个概念,是一种思想,其实现方式多种多样。当前比较流行 应用广泛的实现方式是依赖注入。
依赖:classA 类中含有 classB 的实例,在 classA 中调用 classB 的方法完成功能,即 classA对 classB 有依赖。
Ioc 的实现:
➢ 依赖注入:DI(Dependency Injection),程序代码不做定位查询,这些工作由容器自行完成。
依赖注入 DI 是指程序运行过程中,若需要调用另一个对象协助时,无须在代码中创建被调用者,而是依赖于外部容器,由外部容器创建后传递给程序。
Spring 的依赖注入对调用者与被调用者几乎没有任何要求,完全支持对象之间依赖关系的管理。
Spring 框架使用依赖注入(DI)实现 IoC。
Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依赖关系。使用 IoC 实现对象之间的解耦和。

2、快速入门-第一个Spring程序

1、创建 maven 项目

选择maven的 quickstart,创建一个标准的maven项目
image.png

2、pom.xml引入 maven 依赖

  1. <!--spring依赖-->
  2. <dependency>
  3. <groupId>org.springframework</groupId>
  4. <artifactId>spring-context</artifactId>
  5. <version>5.2.5.RELEASE</version>
  6. </dependency>
  7. <build>
  8. <plugins>
  9. <!--maven编译插件-->
  10. <plugin>
  11. <artifactId>maven-compiler-plugin</artifactId>
  12. <version>3.1</version>
  13. <configuration>
  14. <source>1.8</source>
  15. <target>1.8</target>
  16. </configuration>
  17. </plugin>
  18. </plugins>
  19. </build>

3、定义接口与实体类

  1. public interface SomeService {
  2. void doSome();
  3. }
  4. public class SomeServiceImpl implements SomeService {
  5. public SomeServiceImpl() {
  6. super();
  7. System.out.println("SomeServiceImpl无参数构造方法");
  8. }
  9. @Override
  10. public void doSome() {
  11. System.out.println("====业务方法doSome()===");
  12. }
  13. }

4、创建 Spring 配置文件

在 src/main/resources/目录现创建一个 xml 文件,文件名可以随意,但 Spring 建议的名称为 applicationContext.xml。
spring 配置中需要加入约束文件才能正常使用,约束文件是 xsd 扩展名。

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!--告诉spring创建对象
  7. 声明bean , 就是告诉spring要创建某个类的对象
  8. id:对象的自定义名称,唯一值。 spring通过这个名称找到对象
  9. class:类的全限定名称(不能是接口,因为spring是反射机制创建对象,必须使用类)
  10. spring就完成 SomeService someService = new SomeServiceImpl();
  11. spring是把创建好的对象放入到map中, spring框架有一个map存放对象的。
  12. springMap.put(id的值, 对象);
  13. 例如 springMap.put("someService", new SomeServiceImpl());
  14. 一个bean标签声明一个对象。
  15. -->
  16. <bean id="someService" class="com.bjpowernode.service.impl.SomeServiceImpl" />
  17. <bean id="someService1" class="com.bjpowernode.service.impl.SomeServiceImpl" scope="prototype"/>
  18. <!--
  19. spring能创建一个非自定义类的对象吗, 创建一个存在的某个类的对象。
  20. -->
  21. <bean id="mydate" class="java.util.Date" />
  22. </beans>
  23. <!--
  24. spring的配置文件
  25. 1.beans : 是根标签,spring把java对象成为bean。
  26. 2.spring-beans.xsd 是约束文件,和mybatis指定 dtd是一样的。
  27. -->

5、定义测试类

  1. String config="applicationContext.xml";
  2. ApplicationContext ac = new ClassPathXmlApplicationContext(config);
  3. //使用spring提供的方法, 获取容器中定义的对象的数量
  4. int nums = ac.getBeanDefinitionCount();
  5. System.out.println("容器中定义的对象数量:"+nums);
  6. //容器中每个定义的对象的名称
  7. String names [] = ac.getBeanDefinitionNames();
  8. for(String name:names){
  9. System.out.println(name);
  10. }
  11. //从容器中获取某个对象, 你要调用对象的方法
  12. //getBean("配置文件中的bean的id值")
  13. SomeService service = (SomeService) ac.getBean("someService");
  14. service.doSome();
  15. //获取一个非自定义的类对象
  16. Date my = (Date) ac.getBean("mydate");
  17. System.out.println("Date:"+my);

6、容器接口和实现类

ApplicationContext 用于加载 Spring 的配置文件,在程序中充当“容器”的角色
image.png
我们一般有两种方式获取容器对象,
1、ClassPathXmlApplicationContext(基于配置文件的)

新建一个配置文件(一般放在 resources/applicationContext.xml),在配置文件中通过标签(如 )向容器中注入实例对象

2、AnnotationConfigApplicationContext(基于配置类注解的)

新建一个配置类MyConfig.java(注解@Configuration),在配置类文件中通过注解向容器中注入实例对象

  1. //配置类
  2. @Configuration
  3. public class MyBeanConfig {
  4. @Bean
  5. public Object myDate(){
  6. return new Date();
  7. }
  8. }
  9. //测试类
  10. @Test
  11. public void test05(){
  12. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MyBeanConfig.class);
  13. Object myDate = context.getBean("myDate");
  14. System.out.println(myDate.getClass().getName()); //java.util.Date
  15. }

创建容器并注入对象原理
image.png
核心源码:
image.png

3、依赖注入DI-基于XML

DI(dependence injection)依赖注入
1-Spring基础 - 图6
bean 实例在调用无参构造器创建对象后,就要对 bean 对象的属性进行初始化。初始化是由容器自动完成的,称为注入。
根据注入方式的不同,常用的有三种方式:构造器注入、Set方式注入(重点)、扩展方式注入

1、构造器注入

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。即,使用构造器设置依赖关系。
举例 1:
image.png
标签中用于指定参数的属性有:
➢ name:指定参数名称。
➢ value:给参数注入的属性值
➢ index:指明该参数对应着构造器的第几个参数,从 0 开始。不过,该属性不要也行,但要注意,若参数类型相同,或之间有包含关系,则需要保证赋值顺序要与构造器中的参数顺序一致。

举例 2:
使用构造注入创建一个系统类 File 对象
image.png

2、Set方式注入

set 注入也叫设值注入,是指通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。
1、创建Bean实体类
注意:实体类必须要有get set 方法(因为Spring容器DI时依赖setter注入的)

  1. @Data
  2. @NoArgsConstructor
  3. @AllArgsConstructor
  4. public class Student {
  5. private String name;
  6. private Address address;
  7. private String[] books;
  8. private List<String> hobbys;
  9. private Map<String,String> score;
  10. private Set<String> games;
  11. private String wife;
  12. private Properties info;
  13. }
  14. @Data
  15. @NoArgsConstructor
  16. @AllArgsConstructor
  17. public class Address {
  18. private String addr;
  19. }

2、在xxx.xml中配置bean

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  5. <bean id="address" class="org.xjt.di.domain.Address">
  6. <property name="addr" value="shenzhen"></property>
  7. </bean>
  8. <bean id="student" class="org.xjt.di.domain.Student">
  9. <property name="name" value="xiong"></property>
  10. <property name="address" ref="address"></property>
  11. <property name="books">
  12. <array>
  13. <value>语文</value>
  14. <value>数学</value>
  15. <value>英语</value>
  16. </array>
  17. </property>
  18. <property name="hobbys">
  19. <list>
  20. <value>足球</value>
  21. <value>篮球</value>
  22. <value>羽毛球</value>
  23. </list>
  24. </property>
  25. <property name="score">
  26. <map>
  27. <entry key="chinese" value="86"/>
  28. <entry key="math" value="96"/>
  29. <entry key="english" value="88"/>
  30. </map>
  31. </property>
  32. <property name="games">
  33. <set>
  34. <value>三国杀</value>
  35. <value>街头霸王</value>
  36. <value>魂斗罗</value>
  37. </set>
  38. </property>
  39. <property name="wife">
  40. <null></null>
  41. </property>
  42. <property name="info">
  43. <props>
  44. <prop key="学号">2018112233</prop>
  45. <prop key="班级">机械3班</prop>
  46. <prop key="性别"></prop>
  47. </props>
  48. </property>
  49. </bean>
  50. </beans>

3、扩展方式注入

p-命名空间
需要先导入约束:xmlns:p="http://www.springframework.org/schema/p"

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:p="http://www.springframework.org/schema/p"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. https://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <!-- set方法注入-->
  7. <bean name="classic" class="com.example.ExampleBean">
  8. <property name="email" value="someone@somewhere.com"/>
  9. </bean>
  10. <!-- p-命名空间方法注入(必须先导入依赖)-->
  11. <bean name="p-namespace" class="com.example.ExampleBean"
  12. p:email="someone@somewhere.com"/>
  13. </beans>

c-命名空间
需要先导入约束:xmlns:c="http://www.springframework.org/schema/c"

  1. <beans xmlns="http://www.springframework.org/schema/beans"
  2. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  3. xmlns:c="http://www.springframework.org/schema/c"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. https://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="beanTwo" class="x.y.ThingTwo"/>
  7. <bean id="beanThree" class="x.y.ThingThree"/>
  8. <!-- traditional declaration with optional argument names -->
  9. <bean id="beanOne" class="x.y.ThingOne">
  10. <constructor-arg name="thingTwo" ref="beanTwo"/>
  11. <constructor-arg name="thingThree" ref="beanThree"/>
  12. <constructor-arg name="email" value="something@somewhere.com"/>
  13. </bean>
  14. <!-- 必须要有构造方法 -->
  15. <!-- c-namespace declaration with argument names -->
  16. <bean id="beanOne" class="x.y.ThingOne" c:thingTwo-ref="beanTwo"
  17. c:thingThree-ref="beanThree" c:email="something@somewhere.com"/>
  18. </beans>

4、依赖注入DI-基于注解(重要)

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解需要在原有Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解
指定多个包的三种方式:
(1)使用多个 context:component-scan 指定不同的包路径
image.png
(2)指定 base-package 的值使用分隔符
分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。
image.png
(3)base-package 是指定到父包名
base-package 的值表是基本包,容器启动会扫描包及其子包中的注解,当然也会扫描到子包下级的子包。所以 base-package 可以指定一个父包就可以。
image.png
但不建议使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。指定到目标包和合适的。也就是注解所在包全路径。例如注解的类在 com.bjpowernode.beans 包中
image.png

定义Bean的注解_@_Component

@Component有几个衍生的注解,分别为@_Controller 、@Service、@_Repository
这4个注解功能是一样的,都是代表将某个类注入到Spring容器中 装配Bean

需要在bean类上使用注解@Component,该注解的 value 属性用于指定该 bean 的 id 值
image.png
另外@Component有几个衍生的注解 分别为:
➢ @Repository 用于对 DAO 实现类进行注解
➢ @Service 用于对 Service 实现类进行注解
➢ @Controller 用于对 Controller 实现类进行注解
这4个注解功能是一样的,都是代表将某个类注入到Spring容器中 装配Bean
但这三个注解还有其他的含义:
@Service创建业务层对象,业务层对象可以加入事务功能,
@Controller 注解创建的对象可以作为处理器接收用户的请求。
@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久层对象,业务层对象,控制层对象。
image.png

简单类型属性注入_@_Value

需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。 使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加
到 setter 上。
举例:
image.png

自动注入-byType

使用 byType 方式自动注入,要求:配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用者 bean 类的某引用类型属性类型同源。即要么相同,要么有 is-a 关系(子类,或是实现类)。但这样的同源的被调用 bean 只能有一个。多于一个,容器就不知该匹配哪一个了。
举例:
image.png

byType 自动注入@Autowired

需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。 使用该注解完成属性注入时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。
举例:
image.png

自动注入-byName

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,让容器自动将被调用者 bean 注入给调用者 bean。容器是通过调用者的 bean类的属性名与配置文件的被调用者 bean 的 id 进行比较而实现自动注入的。
举例:
image.png
测试类:
image.png

byName自动注入@Autowired 与@Qualifier

需要在引用属性上联合使用注解@Autowired 与@Qualifier。@Qualifier 的 value 属性用于指定要匹配的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。
举例:
image.png

JDK自动注入注解-_@_Resource

Spring 提供了对 jdk 中@Resource 注解的支持。@Resource 注解既可以按名称匹配 Bean,也可以按类型匹配 Bean,默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性上,也可在 set 方法上。
(1)byType 注入引用类型属性
@Resource 注解若不带任何参数,采用默认按名称的方式注入,按名称不能注入 bean,则会按照类型进行 Bean 的匹配注入。
举例:
image.png
(2)byName 注入引用类型属性
@Resource 注解指定其 name 属性,则 name 的值即为按照名称进行匹配的 Bean 的 id。
举例:
image.png

注解与 XML 的对比

注解优点是:

  • 方便
  • 直观
  • 高效(代码少,没有配置文件的书写那么复杂)。


    其弊端也显而易见:以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。

XML 方式优点是:

  • 配置和代码是分离的
  • 在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。

xml 的缺点是:编写麻烦,效率低,大型项目过于复杂。

5、依赖注入DI-使用javaConfig

javaConfig,是在 Spring 3.0 开始从一个独立的项目并入到 Spring 中的。javaConfig 可以看成一个用于完成 Bean 装配的 Spring 配置文件,一个类中只要标注了@Configuration注解,这个类就可以为spring容器提供Bean定义的信息了。标注了@Configuration和标注了@Component的类一样是一个Bean,可以被Spring的 context:component-scan 标签扫描到。类中的每个标注了@Bean的方法都相当于提供了一个Bean的定义信息。

  1. @Configuration
  2. public class AppConfig {
  3. @Bean
  4. public UserDao userDao() {
  5. return new UserDaoImpl();
  6. }
  7. @Bean
  8. public UserService userService() {
  9. //这里不能声明接口类型
  10. UserServiceImpl userService = new UserServiceImpl();
  11. //配置依赖关系(需要set方法)
  12. userService.setUserDao(userDao());
  13. return userService;
  14. }
  15. }

测试类:

  1. //基于xml
  2. ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
  3. UserService service = (UserService)context.getBean("userService");
  4. User user = service.getUser();
  5. System.out.println(user);
  6. //基于javaConfig
  7. AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
  8. UserService service = (UserService)context.getBean("userService");
  9. User user = service.getUser();
  10. System.out.println(user);

6、为应用指定多个Spring配置文件

在实际应用里,随着应用规模的增加,系统中 Bean 数量也大量增加,导致配置文件变得非常庞大、臃肿。为了避免这种情况的产生,提高配置文件的可读性与可维护性,可以将Spring 配置文件分解成多个配置文件。
包含关系的配置文件:
多个配置文件中有一个总文件,总配置文件将各其它子文件通过引入。在 Java代码中只需要使用总配置文件对容器进行初始化即可。
举例:
image.png
image.png

7、Bean作用域(Scopes)

1-Spring基础 - 图25

  • 单例模式

1-Spring基础 - 图26

  1. <bean id="accountService" class="com.something.DefaultAccountService"/>
  2. <!-- the following is equivalent, though redundant (singleton scope is the default) -->
  3. <bean id="accountService" class="com.something.DefaultAccountService" scope="singleton"/>
  • 原型模式

1-Spring基础 - 图27

  1. <bean id="accountService" class="com.something.DefaultAccountService" scope="prototype"/>
  • Request, Session, Application, and WebSocket

    三、面向切面编程AOP

    1、不使用 AOP 的开发方式

    Step1:项目 aop_leadin1

    先定义好接口与一个实现类,该实现类中除了要实现接口中的方法外,还要再写两个非业务方法。非业务方法也称为交叉业务逻辑:
    ➢ doTransaction():用于事务处理
    ➢ doLog():用于日志处理
    然后,再使接口方法调用它们。接口方法也称为主业务逻辑。
    接口和实现类:
    image.png

    Step2:项目 aop_leadin2

    当然,也可以有另一种解决方案:将这些交叉业务逻辑代码放到专门的工具类或处理类中,由主业务逻辑调用。
    image.png

    Step3:项目 aop_leadin3

    以上的解决方案,还是存在弊端:交叉业务与主业务深度耦合在一起。当交叉业务逻辑较多时,在主业务代码中会出现大量的交叉业务逻辑代码调用语句,大大影响了主业务逻辑的可读性,降低了代码的可维护性,同时也增加了开发难度。 所以,可以采用动态代理方式。在不修改主业务逻辑的前提下,扩展和增强其功能。
    基于JDK的动态代理需要实现接口InvocationHandler 重写invoke方法
    image.png
    测试类:
    image.png

    2、代理模式

    http://c.biancheng.net/view/1359.html
    代理模式的作用:
  1. 功能增强:在原有功能上 增加额外的功能(不改变原有功能的代码)
  2. 控制访问:代理类不让你访问目标,例如商家不让顾客直接访问厂家

实现代理的方式有两种:

  1. 静态代理:代理类是自己手动实现的 自己创建一个Java代理类,同时所要代理的目标类是确定的(实现简单 容易理解)
  2. 动态代理:在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。换句话说: 动态代理是一种创
    建java对象的能力,让你不用创建TaoBao类,就能创建代理类对象。

动态代理(原理): 基于反射机制
1、什么是动态代理 ?
使用jdk的反射机制,创建对象的能力, 创建的是代理类的对象。 而不用你创建类文件。不用写java文件。
动态:在程序执行时,调用jdk提供的方法才能创建代理类的对象。
jdk动态代理,必须有接口,目标类必须实现接口
没有接口时,需要使用cglib动态代理

2、动态代理能做什么 ?
可以在不改变原来目标方法功能的前提下, 可以在代理中增强自己的功能代码。
程序开发中的意思。
比如:你所在的项目中,有一个功能是其他人(公司的其它部门,其它小组的人)写好的,你可以使用。
GoNong.class , GoNong gn = new GoNong(), gn.print();
你发现这个功能,现在还缺点, 不能完全满足我项目的需要。 我需要在gn.print()执行后,需要自己在增加代码。
用代理实现 gn.print()调用时, 增加自己代码, 而不用去改原来的 GoNong文件。


1.代理 代购, 中介,换ip,商家等等 比如有一家美国的大学, 可以对全世界招生。 留学中介(代理) 留学中介(代理): 帮助这家美国的学校招生, 中介是学校的代理, 中介是代替学校完成招生功能。 代理特点:

  1. 中介和代理他们要做的事情是一致的: 招生。
  2. 中介是学校代理, 学校是目标。
  3. 家长—-中介(学校介绍,办入学手续)——美国学校。
  4. 中介是代理,不能白干活,需要收取费用。
  5. 代理不让你访问到目标。
  1. 为什么要找中介
  2. 1. 中介是专业的, 方便
  3. 2. 家长现在不能自己去找学校。 家长没有能力访问学校。 或者美国学校不接收个人来访。买东西都是商家卖, 商家是某个商品的代理, 你个人买东西, 肯定不会让你接触到厂家的。
  1. 在开发中也会有这样的情况, 你有a类, 本来是调用c类的方法, 完成某个功能。 但是c不让a调用。
    a ——-不能调用 c的方法。
    在a 和 c 直接 创建一个 b 代理, c让b访问。
    a —访问b—-访问c
    实际的例子: 登录,注册有验证码, 验证码是手机短信。
    中国移动, 联通能发短信。
    中国移动, 联通能有子公司,或者关联公司,他们面向社会提供短信的发送功能
    张三项目发送短信——子公司,或者关联公司——-中国移动, 联通

3.使用代理模式的作用 1.功能增强: 在你原有的功能上,增加了额外的功能。 新增加的功能,叫做功能增强。 2.控制访问: 代理类不让你访问目标,例如商家不让用户访问厂家。 4.实现代理的方式 1.静态代理 : 1) 代理类是自己手工实现的,自己创建一个java类,表示代理类。 2)同时你所要代理的目标类是确定的。 特点: 1)实现简单 2)容易理解。 缺点: 当你的项目中,目标类和代理类很多时候,有以下的缺点: 1)当目标类增加了, 代理类可能也需要成倍的增加。 代理类数量过多。 2) 当你的接口中功能增加了, 或者修改了,会影响众多的实现类,厂家类,代理都需要修改。影响比较多。 模拟一个用户购买u盘的行为。 用户是客户端类 商家:代理,代理某个品牌的u盘。 厂家:目标类。 三者的关系: 用户(客户端)—-商家(代理)—-厂家(目标) 商家和厂家都是卖u盘的,他们完成的功能是一致的,都是卖u盘。 实现步骤:

  1. 创建一个接口,定义卖u盘的方法, 表示你的厂家和商家做的事情。
  2. 创建厂家类,实现1步骤的接口
  3. 创建商家,就是代理,也需要实现1步骤中的接口。
  4. 创建客户端类,调用商家的方法买一个u盘。 代理类完成的功能:
  5. 目标类中方法的调用
  6. 功能增强 2.动态代理 在静态代理中目标类很多时候,可以使用动态代理,避免静态代理的缺点。 动态代理中目标类即使很多, 1)代理类数量可以很少, 2)当你修改了接口中的方法时,不会影响代理类。 动态代理: 在程序执行过程中,使用jdk的反射机制,创建代理类对象, 并动态的指定要代理目标类。 换句话说: 动态代理是一种创建java对象的能力,让你不用创建TaoBao类,就能创建代理类对象。 在java中,要想创建对象: 1.创建类文件, java文件编译为class 2.使用构造方法,创建类的对象。 动态代理的实现:
  7. jdk动态代理(理解): 使用java反射包中的类和接口实现动态代理的功能。反射包 java.lang.reflect , 里面有三个类 :InvocationHandler , Method, Proxy.
  8. cglib动态代理(了解): cglib是第三方的工具库, 创建代理对象。 cglib的原理是继承, cglib通过继承目标类,创建它的子类,在子类中 重写父类中同名的方法, 实现功能的修改。
  1. 因为cglib是继承,重写方法,所以要求目标类不能是final的, 方法也不能是final的。
  2. cglib的要求目标类比较宽松, 只要能继承就可以了。cglib在很多的框架中使用,
  3. 比如 mybatis spring框架中都有使用。

jdk动态代理:

  1. 反射, Method类,表示方法。类中的方法。 通过Method可以执行某个方法。
  2. jdk动态代理的实现
    反射包 java.lang.reflect , 里面有三个类 : InvocationHandler , Method, Proxy.
    1)InvocationHandler 接口(调用处理器):就一个方法invoke()
    invoke():表示代理对象要执行的功能代码。你的代理类要完成的功能就写在
    invoke()方法中。

代理类完成的功能:

  1. 调用目标方法,执行目标方法的功能
  2. 功能增强,在目标方法调用时,增加功能。
  1. 方法原型:
  2. 参数: Object proxy:jdk创建的代理对象,无需赋值。
  3. Method method:目标类中的方法,jdk提供method对象的
  4. Object[] args:目标类中方法的参数, jdk提供的。
  5. public Object invoke(Object proxy, Method method, Object[] args)
  6. InvocationHandler 接口:表示你的代理要干什么。
  7. 怎么用: 1.创建类实现接口InvocationHandler
  8. 2.重写invoke()方法, 把原来静态代理中代理类要完成的功能,写在这。
  1. 2Method类:表示方法的, 确切的说就是目标类中的方法。
  2. 作用:通过Method可以执行某个目标类的方法,Method.invoke();
  3. method.invoke(目标对象,方法的参数)
  4. Object ret = method.invoke(service2, "李四");
  5. 说明: method.invoke()就是用来执行目标方法的,等同于静态代理中的
  6. //向厂家发送订单,告诉厂家,我买了u盘,厂家发货
  7. float price = factory.sell(amount); //厂家的价格。

3)Proxy类:核心的对象,创建代理对象。之前创建对象都是 new 类的构造方法() 现在我们是使用Proxy类的方法,代替new的使用。 方法: 静态方法 newProxyInstance() 作用是: 创建代理对象, 等同于静态代理中的TaoBao taoBao = new TaoBao();

  1. 参数:
  2. 1. ClassLoader loader 类加载器,负责向内存中加载对象的。 使用反射获取对象的ClassLoader
  3. a , a.getCalss().getClassLoader(), 目标对象的类加载器
  4. 2. Class<?>[] interfaces 接口, 目标对象实现的接口,也是反射获取的。
  5. 3. InvocationHandler h : 我们自己写的,代理类要完成的功能。
  6. 返回值:就是代理对象
  7. public static Object newProxyInstance(ClassLoader loader,
  8. Class<?>[] interfaces,
  9. InvocationHandler h)
  1. 实现动态代理的步骤:
    1. 创建接口,定义目标类要完成的功能
  2. 创建目标类实现接口
  3. 创建InvocationHandler接口的实现类,在invoke方法中完成代理类的功能
    1.调用目标方法
    2.增强功能

4.使用Proxy类的静态方法,创建代理对象。 并把返回值转为接口类型。

示例:
1-Spring基础 - 图32
com.xjt.handler.MyInvocationHandler.java
1-Spring基础 - 图33
目标类和接口
1-Spring基础 - 图34
1-Spring基础 - 图35
1-Spring基础 - 图36

基于Spring配置文件的aop

  • 方法一:

    使用原生是sping-api接口 在目标方法前置后置增加功能 该方法需要定义前置方法实现org.springframework.aop.MethodBeforeAdvice 定义后置方法实现org.springframework.aop.AfterReturningAdvice 优点:前后置方法中可获取到目标对象、执行的方法、参数,自定制程度化高

前置方法

  1. package org.example.log;
  2. import org.springframework.aop.AfterReturningAdvice;
  3. import java.lang.reflect.Method;
  4. public class AfterLog implements AfterReturningAdvice {
  5. @Override
  6. public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  7. System.out.println(target.getClass().getName()+"类的"+method.getName()+"方法被执行了,返回结果为:"+returnValue);
  8. }
  9. }

后置方法

  1. package org.example.log;
  2. import org.springframework.aop.MethodBeforeAdvice;
  3. import java.lang.reflect.Method;
  4. public class Log implements MethodBeforeAdvice {
  5. @Override
  6. public void before(Method method, Object[] args, Object target) throws Throwable {
  7. System.out.println(target.getClass().getName()+"类的"+method.getName()+"方法被执行了");
  8. }
  9. }

Spring的配置:applicationContext.xml

  1. <bean id="userService" class="org.example.service.UserServiceImpl"/>
  2. <bean id="log" class="org.example.log.Log"/>
  3. <bean id="afterLog" class="org.example.log.AfterLog"/>
  4. <aop:config>
  5. <!--切入点 表达式-->
  6. <aop:pointcut id="pointcut" expression="execution(* org.example.service.UserServiceImpl.*(..))"/>
  7. <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
  8. <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
  9. </aop:config>
  • 方法二:

前后置方法可以定义在一个类中

  1. package org.example.diy;
  2. public class Diy {
  3. void before(){
  4. System.out.println("before===============目标方法执行前");
  5. }
  6. void after(){
  7. System.out.println("after===============目标方法执行后");
  8. }
  9. }

Spring的配置:applicationContext.xml

  1. <bean id="userService" class="org.example.service.UserServiceImpl"/>
  2. <bean id="diy" class="org.example.diy.Diy"/>
  3. <aop:config>
  4. <aop:aspect ref="diy">
  5. <aop:pointcut id="pointcut" expression="execution(* org.example.service.UserServiceImpl.*(..))"/>
  6. <aop:before method="before" pointcut-ref="pointcut"/>
  7. <aop:after method="after" pointcut-ref="pointcut"/>
  8. </aop:aspect>
  9. </aop:config>

执行效果:

  1. public static void main( String[] args ){
  2. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  3. UserService userService = (UserService) applicationContext.getBean("userService");
  4. userService.add();
  5. }
  6. before===============目标方法执行前
  7. 执行了UserServiceImpl::add()
  8. after===============目标方法执行后

基于注解的aop

切面类

  1. package org.example.anno;
  2. import org.aspectj.lang.ProceedingJoinPoint;
  3. import org.aspectj.lang.annotation.After;
  4. import org.aspectj.lang.annotation.Around;
  5. import org.aspectj.lang.annotation.Aspect;
  6. import org.aspectj.lang.annotation.Before;
  7. @Aspect //声明当前类是切面类
  8. public class AnnotationAop {
  9. @Before("execution(* org.example.service.UserServiceImpl.*(..))")
  10. void doBefore(){
  11. System.out.println("==========方法执行前==========");
  12. }
  13. @After("execution(* org.example.service.UserServiceImpl.*(..))")
  14. void doAfter(){
  15. System.out.println("==========方法执行前==========");
  16. }
  17. @Around("execution(* org.example.service.UserServiceImpl.*(..))")
  18. void doAround(ProceedingJoinPoint joinPoint) throws Throwable {
  19. System.out.println("环绕前。。。");
  20. joinPoint.proceed();
  21. System.out.println("环绕后。。。");
  22. }
  23. }

applicationContext.xml配置

  1. <bean id="userService" class="org.example.service.UserServiceImpl"/>
  2. <!--方法二:基于注解的实现-->
  3. <bean id="annotationAop" class="org.example.anno.AnnotationAop"/>
  4. <!--proxy-target-class 默认false 使用jdk动态代理,true使用cglib-->
  5. <aop:aspectj-autoproxy proxy-target-class="false"/>

打印结果:

  1. public static void main( String[] args ){
  2. ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
  3. UserService userService = (UserService) applicationContext.getBean("userService");
  4. userService.add();
  5. }
  6. 环绕前。。。
  7. ==========方法执行前==========
  8. 执行了UserServiceImpl::add()
  9. ==========方法执行前==========
  10. 环绕后。。。

3、AOP 概述

AOP(Aspect Orient Programming),面向切面编程。面向切面编程是从动态角度考虑程序运行过程。
AOP 底层,就是采用动态代理模式实现的。采用了两种代理:JDK 的动态代理,与 CGLIB的动态代理。

AOP 为 Aspect Oriented Programming 的缩写,意为:面向切面编程,可通过运行期动态代理实现程序功能的统一维护的一种技术。AOP 是 Spring 框架中的一个重要内容。利用 AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

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

例如,转账,在真正转账业务逻辑前后,需要权限控制、日志记录、加载事务、结束事务等交叉业务逻辑,而这些业务逻辑与主业务逻辑间并无直接关系。但,它们的代码量所占比重能达到总代码量的一半甚至还多。它们的存在,不仅产生了大量的“冗余”代码,还大大干扰了主业务逻辑—-转账。

面向切面编程对有什么好处?

1.减少重复;
2.专注业务;
注意:面向切面编程只是面向对象编程的一种补充。
使用 AOP 减少重复代码,专注业务实现:
image.png

4、AOP 编程术语

(1)切面(Aspect)
切面泛指交叉业务逻辑。上例中的事务处理、日志处理就可以理解为切面。常用的切面是通知(Advice) 。实际就是对主业务逻辑的一种增强。
(2)连接点(JoinPoint)
连接点指可以被切面织入的具体方法。通常业务接口中的方法均为连接点。
(3)切入点(Pointcut)
切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。
(4)目标对象(Target)
目 标 对 象 指 将 要 被 增 强 的 对 象 。 即 包 含 主 业 务 逻 辑 的 类 的 对 象 。 上例中 的StudentServiceImpl 的对象若被增强,则该类称为目标类,该类对象称为目标对象。当然不被增强,也就无所谓目标不目标了。
(5)通知(Advice)
通知表示切面的执行时间,Advice 也叫增强。上例中的 MyInvocationHandler 就可以理解为是一种通知。换个角度来说,通知定义了增强代码切入到目标代码的时间点,是目标方法执行之前执行,还是之后执行等。通知类型不同,切入时间不同。 切入点定义切入的位置,通知定义切入的时间。

5、AspectJ 对AOP的实现

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

AspectJ 是一个优秀面向切面的框架,它扩展了 Java 语言,提供了强大的切面实现。官网地址:http://www.eclipse.org/aspectj/ AspetJ 是 Eclipse 的开源项目,官网介绍如下: a seamless aspect-oriented extension to the Javatm programming language(一种基于 Java 平台的面向切面编程的语言) Java platform compatible(兼容 Java 平台,可以无缝扩展) easy to learn and use(易学易用)

AspectJ 的通知类型

AspectJ 中常用的通知有五种类型:
(1)前置通知
(2)后置通知
(3)环绕通知
(4)异常通知
(5)最终通知

AspectJ 的切入点表达式

AspectJ 定义了专门的表达式用于指定切入点。表达式的原型是:

  1. execution(modifiers-pattern? ret-type-pattern declaring-type-pattern?name-pattern(param-pattern) throws-pattern?)

解释:
modifiers-pattern] 访问权限类型
ret-type-pattern 返回值类型
declaring-type-pattern 包名类名
name-pattern(param-pattern) 方法名(参数类型和参数个数)
throws-pattern 抛出异常类型
?表示可选的部分

以上表达式共 4 个部分。
execution(访问权限 方法返回值 方法声明(参数) 异常类型)

切入点表达式要匹配的对象就是目标方法的方法名。所以,execution 表达式中明显就是方法的签名。注意,表达式中黑色文字表示可省略部分,各部分间用空格分开。在其中可以使用以下符号:
image.png
举例:
execution(public (..))
指定切入点为:任意公共方法。
execution( set(..))
指定切入点为:任何一个以“set”开始的方法。
execution( com.xyz.service..*(..))
指定切入点为:定义在 service 包里的任意类的任意方法。
execution( com.xyz.service...*(..))
指定切入点为:定义在 service 包或者子包里的任意类的任意方法。“..”出现在类名中时,后面必须跟“”,表示包、子包下的所有类。
**execution(
..service..(..))
指定所有包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(
.service..(..))
指定只有一级包下的 serivce 子包下所有类(接口)中所有方法为切入点
execution(
.ISomeService.(..))
指定只有一级包下的 ISomeSerivce 接口中所有方法为切入点
execution( ..ISomeService.(..))
指定所有包下的 ISomeSerivce 接口中所有方法为切入点
execution(
com.xyz.service.IAccountService.(..))
指定切入点为:IAccountService 接口中的任意方法。
execution(
com.xyz.service.IAccountService+.(..))
指定切入点为:IAccountService 若为接口,则为接口中的任意方法及其所有实现类中的任意方法;若为类,则为该类及其子类中的任意方法。
execution(
joke(String,int)))
指定切入点为:所有的 joke(String,int)方法,且 joke()方法的第一个参数是 String,第二个参数是 int。如果方法中的参数类型是 java.lang 包下的类,可以直接使用类名,否则必须使用全限定类名,如 joke( java.util.List, int)。
execution( joke(String,)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,第二个参数可以是任意类型,如 joke(String s1,String s2)和 joke(String s1,double d2)都是,但joke(String s1,double d2,String s3)不是。
execution( joke(String,..)))
指定切入点为:所有的 joke()方法,该方法第一个参数为 String,后面可以有任意个参数且参数类型不限,如 joke(String s1)、joke(String s1,String s2)和 joke(String s1,double d2,String s3)都是。
execution(
joke(Object))
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型。joke(Object ob)是,但,joke(String s)与 joke(User u)均不是。
execution( joke(Object+))) *
指定切入点为:所有的 joke()方法,方法拥有一个参数,且参数是 Object 类型或该类的子类。不仅 joke(Object ob)是,joke(String s)和 joke(User u)也是。

6、AspectJ 基于注解的 AOP 实现

基于注解的 AOP 实现步骤

1、maven 依赖

  1. <dependency>
  2. <groupId>org.springframework</groupId>
  3. <artifactId>spring-context</artifactId>
  4. <version>5.2.5.RELEASE</version>
  5. </dependency>
  6. <dependency>
  7. <groupId>org.springframework</groupId>
  8. <artifactId>spring-aspects</artifactId>
  9. <version>5.2.5.RELEASE</version>
  10. </dependency>

2、引入 AOP 约束
在 AspectJ 实现 AOP 时,要引入 AOP 的约束。配置文件中使用的 AOP 约束中的标签,均是 AspectJ 框架使用的,而非 Spring 框架本身在实现 AOP 时使用的。
AspectJ 对于 AOP 的实现有注解和配置文件两种方式,常用是注解方式。

3、定义业务接口与实现类
image.png
4、定义切面类
类中定义了若干普通方法,将作为不同的通知方法,用来增强功能。
image.png
5、声明目标对象切面类对象
image.png
6、注册 AspectJ 的自动代理
在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。
image.png
<aop:aspectj-autoproxy/>的底层是由AnnotationAwareAspectJAutoProxyCreator实现的。
从其类名就可看出,是基于 AspectJ 的注解适配自动代理生成器。 其工作原理是,<aop:aspectj-autoproxy/>通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找到目标类的目标方法,再由通知类型找到切入的时间点。

7、测试类中使用目标对象的 id
image.png

@Before 前置通知-方法有 JoinPoint 参数

在目标方法执行之前执行。被注解为前置通知的方法,可以包含一个 JoinPoint 类型参数。该类型的对象本身就是切入点表达式。通过该参数,可获取切入点表达式、方法签名、目标对象等。 不光前置通知的方法,可以包含一个 JoinPoint 类型参数,所有的通知方法均可包含该参数。
image.png

@AfterReturning 后置通知-注解有 returning 属性

在目标方法执行之后执行。由于是目标方法之后执行,所以可以获取到目标方法的返回值。该注解的 returning 属性就是用于指定接收方法返回值的变量名的。所以 被注解为后置通知的方法,除了可以包含 JoinPoint 参数外,还可以包含用于接收返回值的变量。该变量最好为 Object 类型,因为目标方法的返回值可能是任何类型。

接口增加方法:
image.png
实现方法:
image.png
定义切面:
image.png

@Around 环绕通知-增强方法有 ProceedingJoinPoint参数

在目标方法执行之前之后执行。被注解为环绕增强的方法要有返回值,Object 类型。并且方法可以包含一个 ProceedingJoinPoint 类型的参数。接口 ProceedingJoinPoint 其有一个proceed()方法,用于执行目标方法。若目标方法有返回值,则该方法的返回值就是目标方法的返回值。最后,环绕增强方法将其返回值返回。该增强方法实际是拦截了目标方法的执行。

接口增加方法:
image.png
接口方法的实现:
image.png
定义切面:
image.png

@AfterThrowing 异常通知-注解中有 throwing 属性

在目标方法抛出异常后执行。该注解的 throwing 属性用于指定所发生的异常类对象。当然,被注解为异常通知的方法可以包含一个参数 Throwable,参数名称为 throwing 指定的名称,表示发生的异常对象。

增加业务方法:
image.png
方法实现:
image.png
定义切面:
image.png

@After 最终通知

无论目标方法是否抛出异常,该增强均会被执行。

增加方法:
image.png
方法实现:
image.png
定义切面:
image.png

@Pointcut 定义切入点

当较多的通知增强方法使用相同的 execution 切入点表达式时,编写、维护均较为麻烦。
AspectJ 提供了@Pointcut 注解,用于定义 execution 切入点表达式。 其用法是,将@Pointcut 注解在一个方法之上,以后所有的 execution 的 value 属性值均可使用该方法名作为切入点。代表的就是@Pointcut 定义的切入点。这个使用@Pointcut 注解的方法一般使用 private 的标识方法,即没有实际作用的方法。
image.png

四、Spring 集成 MyBatis

将 MyBatis 与 Spring 进行整合,主要解决的问题就是将 SqlSessionFactory 对象交由 Spring来管理。所以,该整合,只需要将 SqlSessionFactory 的对象生成器 SqlSessionFactoryBean 注册在 Spring 容器中,再将其注入给 Dao 的实现类即可完成整合。 实现 Spring 与 MyBatis 的整合常用的方式:扫描的 Mapper 动态代理

Spring 像插线板一样,mybatis 框架是插头,可以容易的组合到一起。插线板 spring 插上 mybatis,两个框架就是一个整体。
所谓的Spring集成MyBatis,就是由Spring给MyBatis注入需要的组件。比如:DataSource(数据源)、SqlSessionFactory、sqlSession、事务管理对象等。

1、添加集成MyBatis的依赖

  1. <dependencies>
  2. <!--spring核心 IOC容器-->
  3. <dependency>
  4. <groupId>org.springframework</groupId>
  5. <artifactId>spring-context</artifactId>
  6. <version>${spring.version}</version>
  7. </dependency>
  8. <!--spring事务-->
  9. <dependency>
  10. <groupId>org.springframework</groupId>
  11. <artifactId>spring-tx</artifactId>
  12. <version>5.2.5.RELEASE</version>
  13. </dependency>
  14. <!--spring对jdbc的支持依赖包(关联spring-tx包) -->
  15. <dependency>
  16. <groupId>org.springframework</groupId>
  17. <artifactId>spring-jdbc</artifactId>
  18. <version>${spring.version}</version>
  19. </dependency>
  20. <!-- mybatis-spring 依赖包 -->
  21. <dependency>
  22. <groupId>org.mybatis</groupId>
  23. <artifactId>mybatis-spring</artifactId>
  24. <version>2.0.0</version>
  25. </dependency>
  26. <!--mybatis 依赖包 -->
  27. <dependency>
  28. <groupId>org.mybatis</groupId>
  29. <artifactId>mybatis</artifactId>
  30. <version>3.5.1</version>
  31. </dependency>
  32. <!-- mysql驱动 依赖包 -->
  33. <dependency>
  34. <groupId>mysql</groupId>
  35. <artifactId>mysql-connector-java</artifactId>
  36. <version>5.1.6</version>
  37. </dependency>
  38. <!-- alibaba数据源 -->
  39. <dependency>
  40. <groupId>com.alibaba</groupId>
  41. <artifactId>druid</artifactId>
  42. <version>1.1.12</version>
  43. </dependency>
  44. </dependencies>

2、创建Mapper

xxxMapper.java 和xxxMapper.xml 一般放在同一个目录中
下面是采用注解的方式写的sql

  1. @Mapper
  2. public interface EmpMapper {
  3. @Select("select * from emp where empno=#{empno}")
  4. public Emp getEmpById(int empno);
  5. }

在Mapper接口上添加@Mapper注解,Spring容器扫描到此注解后,就会将SqlSession注入给Mapper组件。

3、添加db.properties文件

  1. jdbc.driver=com.mysql.jdbc.Driver
  2. jdbc.url=jdbc:mysql://localhost:3306/emp?characterEncoding=utf-8
  3. jdbc.username=root
  4. jdbc.password=123

4、Spring容器中添加组件

applicationContext.xml

  1. <!--开启注解扫描,设置需要扫描的包(用于扫描测试时用的Service组件) -->
  2. <context:component-scan base-package="com.neusoft.ms"/>
  3. <!-- 引入db配置文件 -->
  4. <context:property-placeholder location="classpath:db.properties" />
  5. <!-- 配置dataSource数据源 -->
  6. <bean id="dataSource"
  7. class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  8. <property name="driverClassName" value="${jdbc.driver}"></property>
  9. <property name="url" value="${jdbc.url}"></property>
  10. <property name="username" value="${jdbc.username}"></property>
  11. <property name="password" value="${jdbc.password}"></property>
  12. </bean>
  13. <!-- 创建SqlSessionFactory,并配置实体对象别名 -->
  14. <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  15. <property name="dataSource" ref="dataSource"></property>
  16. <property name="typeAliasesPackage" value="com.neusoft.ms.po" />
  17. </bean>
  18. <!-- 配置Mapper,自动扫描Mapper接口,并为其注入SqlSessionFactory -->
  19. <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  20. <property name="basePackage" value="com.neusoft.ms.mapper"></property>
  21. <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
  22. </bean>

这里,配置了sqlSessionFactory组件,并将sqlSessionFactory组件注入给Mapper组件。

5、测试

创建Service组件,然后测试

  1. @Service("empService")
  2. public class EmpServiceImpl implements EmpService{
  3. @Autowired
  4. private EmpMapper empMapper;
  5. @Override
  6. public Emp getEmpById(int empno) {
  7. return empMapper.getEmpById(empno);
  8. }
  9. }
  1. public class MsTest {
  2. public static void main(String[] args) {
  3. ApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
  4. EmpService service = (EmpService)context.getBean("empService");
  5. Emp emp = service.getEmpById(7369);
  6. System.out.println(emp);
  7. }
  8. }

6、添加事务管理

Spring为了支持事务管理,专门封装了事务管理对象。我们只要在Spring容器中配置这个对象,即可使用。
在Spring容器中添加事务管理的配置:

  1. <!-- 配置Spring提供的事务管理器 -->
  2. <bean id="transactionManager"
  3. class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  4. <property name="dataSource" ref="dataSource"></property>
  5. </bean>
  6. <!-- 开启注解事务 -->
  7. <tx:annotation-driven transaction-manager="transactionManager" />

在Service组件中,使用@Transactional注解,就可以给业务方法添加事务管理。

  1. @Transactional
  2. public int transferAccounts(Emp emp1,Emp emp2) {
  3. //需要事务管理的业务
  4. }

注意:

  1. 需要事务管理的service,在方法上加上@Transactional 注解即可。
  2. 必须为public方法才行,不要捕捉异常,要让异常自动抛出,否则不能进行事务回滚。

7、事务传播行为

@Transactional 注解中的 propagation 属性,可以设置事务传播行为。属性值为:

  1. REQUIRED:如果当前没有事务,就新建一个事务,如果已经存在一个事务中,就加入到这个事务中。这是最常见的选择。
  2. SUPPORTS:支持当前事务,如果当前没有事务,就以非事务方式执行。
  3. MANDATORY:使用当前的事务,如果当前没有事务,就抛出异常。
  4. REQUIRES_NEW:新建事务,如果当前存在事务,把当前事务挂起。
  5. NOT_SUPPORTED:以非事务方式执行操作,如果存在事务,就把当前事务挂起。
  6. NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。
    1. @Override
    2. @Transactional(propagation=Propagation.SUPPORTS)
    3. public Dept getDeptById(Integer deptno) {
    4. return deptMapper.getDeptById(deptno);
    5. }
    [

](https://null_688_6639.gitee.io/javase/23spring/)