当前位置:   article > 正文

Spring框架的全面详解(个人学习总结)

spring框架

1、什么是Spring框架

Spring框架是一个容器,它是用来整合其他框架的框架。

它的核心是IOC(控制反转)和AOP(面向切面编程)。

它是一个大的家族,由20多个模块构成,在很多领域都提供了非常优秀的支持。

它的最终目标其实就是解耦合。

2、Spring框架的优点

(1)轻量级

它的核心jar包很小,总共3M多,对代码零污染。

(2)面向接口编程

Spring底层是面向接口的开发.
面向接口开发的规范:
    1.类中的成员变量设计为接口
    2.方法的参数设计为接口
    3.方法的返回值设计为接口
    4.调用时接口指向实现类

总结一句话,上接口就是为了更加灵活。

(3)AOP面向切面编程

1.切面就是程序中那些公共的,通用的,重复的功能。例如 :日志,事务,权限验证等。
2.面向切面编程AOP就是将切面单独拿出来开发,在需要的业务功能上自动反织回去。

(4)方便的整合其它框架

3、什么是IOC(控制反转)

控制反转IOC(Inversion of Control)它是一种概念,同时也是一种思想,就是让Spring容器干活的思想。

问题:
1.控制什么?

是创建对象和依赖注入的控制权

2.反转什么?

是由程序员控制权反转给Spring容器控制权

正转:由程序员创建对象和依赖注入

  1. Student s = new Student(); //程序员在创建对象
  2. s.setName("吃切糕的斯派克"); // 程序员依赖注入值
  3. s.setAge(25); // 程序员依赖注入值

反转:Spring容器创建对象和依赖注入

  1. <bean id="stu" class="com.spike.pojo.Student"> ===>Spring容器在创建对象
  2. <property name="name" value="吃切糕的斯派克"></property> ===>Spring容器依赖注入值
  3. <property name="age" value="25"></property> ===>Spring容器依赖注入值
  4. </bean>

4、Spring的IOC实现

主要有两种方式

1.基于xml的实现形式
2.基于注解的实现形式

5、基于xml的IOC的实现

先在pom.xml文件中指定 Java 编译版本,可以是jdk8,jdk11或者jdk17,本处采用jdk17。

  1. <properties>
  2. <maven.compiler.source>17</maven.compiler.source>
  3. <maven.compiler.target>17</maven.compiler.target>
  4. <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  5. </properties>
(1)创建对象

注意点:1.一定要有无参的构造方法

              2.所有对象的创建和依赖注入是在容器启动时发生

              3.使用<bean>标签创建对象

代码示例

  1. <!--创建Student对象
  2. 参数:
  3. id:创建的对象的名称
  4. class:创建的对象的类型
  5. -->
  6. <bean id="stu" class="com.spike.pojo.Student"></bean>
(2)依赖注入
1.按注入值分为简单类型注入和引用类型注入

简单类型(8种基本类型(封装类型)+String)使用value注入值。
引用类型使用ref注入值。

2.按注入的方式分为setter注入和构造方法注入
A.   setter注入法

注意点:一定要有setXXX()方法
              使用<property>标签赋值

代码示例

  1. <!--创建School对象-->
  2. <bean id="school" class="com.spike.pojo.School">
  3. <property name="name" value="天津大学"></property>
  4. <property name="address" value="天津市南开区"></property>
  5. </bean>
  6. <!--创建Student-->
  7. <bean id="stu" class="com.spike.pojo.Student">
  8. <property name="name" value="吃切糕的斯派克"></property>
  9. <property name="age" value="25"></property>
  10. <property name="school" ref="school"></property> ===>引用类型注入值
  11. </bean>
B.  构造方法注入

使用<constructor-arg>标签赋值。

a.使用构造方法参数名称注入值

  1. <!--创建School对象,使用构造方法参数名称注入值
  2. public School(String name1, String address1) {...} //构造方法
  3. -->
  4. <bean id="school" class="com.spike.pojo.School">
  5. <constructor-arg name="name1" value="天津大学"></constructor-arg>
  6. <constructor-arg name="address1" value="天津市南开区"></constructor-arg>
  7. </bean>

b.使用构造方法参数下标注入值

  1. <!--创建Student,使用构造方法参数下标注入值,默认下标从0开始
  2. public Student(String name, int age, School school){...}
  3. -->
  4. <bean id="stuIndex" class="com.spike.pojo.Student">
  5. <constructor-arg index="1" value="25"></constructor-arg>
  6. <constructor-arg index="0" value="吃切糕的斯派克"></constructor-arg>
  7. <constructor-arg index="2" ref="school"></constructor-arg>
  8. </bean>

c.使用构造方法参数默认顺序注入值

  1. <!--创建Student,使用构造方法参数默认顺序注入值
  2. public Student(String name, int age, School school){...}
  3. -->
  4. <bean id="stuSequence" class="com.spike.pojo.Student">
  5. <constructor-arg value="吃切糕的斯派克"></constructor-arg>
  6. <constructor-arg value="25"></constructor-arg>
  7. <constructor-arg ref="school"></constructor-arg>
  8. </bean>

6、创建系统类型的对象

Spring的IOC的功能就是创建对象并依赖注入值,并不在乎是系统类型还是自定义类型。

  1. <!--例如创建Date类型的对象-->
  2. <bean id="myDate" class="java.util.Date">
  3. <property name="time" value="1234567891011"></property>
  4. </bean>
  5. <!--注意setTime()方法不一定给time的成员变量赋值,只是调用setTime()的方法-->

7、Spring创建对象的作用域

Spring创建对象默认的作用域范围是单例的(singleton)。可以设置为非单例(prototype)。

1.默认单例(因为创建的过程很复杂):
  1. <bean id="myDate" class="java.util.Date" scope="singleton"></bean>
  2. <bean id="myDate" class="java.util.Date" ></bean>
  3. <!--每次取都返回同一个第一次创建好的对象-->
2.设置为非单例
  1. <bean id="myDate" class="java.util.Date" scope="prototype"></bean>
  2. <!--每次取都返回一个新创建的对象-->

8、引用类型的属性自动注入

前提:引用类型才可以实现自动注入。
           自动注入有两种方式:按类型自动注入 ,按名称自动注入。

  1. <bean id="school" class="com.bjpowernode.s02.School">
  2. <property name="name" value="天津大学"></property>
  3. <property name="address" value="天津市南开区"></property>
  4. </bean>
  5. <!--创建Student-->
  6. <!--按名称自动注入-->
  7. <bean id="stu" class="com.spike.pojo.Student" autowire="byName">
  8. <property name="name" value="吃切糕的斯派克"></property>
  9. <property name="age" value="25"></property>
  10. </bean>
  11. <!--按类型自动注入-->
  12. <bean id="stu" class="com.spike.pojo.Student" autowire="byType">
  13. <property name="name" value="吃切糕的斯派克"></property>
  14. <property name="age" value="25"></property>
  15. </bean>

 9、基于注解的IOC实现

依赖注入:DI(Dependency Injection)是IOC的实现技术,所以说DI就是IOC。两种不同的说法而已,就是Spring去创建对象并依赖注入。

(1)创建对象的注解

    @Comconpent:创建任意对象,默认对象的名称是类名的驼峰命名法,也可以自定义对象名称
    @Controller:专门用于创建界面层的对象
    @Service:专门用来创建业务逻辑层的对象
    @Repository:专门用来数据访问层的对象

(2)依赖注入的注解

    @Value:简单类型注入
    @Autowired:引用类型按类型注入

    注意:

    @Autowired
    @Qualifier("名称")

    俩注解一起用是引用类型的按名称注入

代码示例

  1. @Component
  2. public class Student {
  3. @Value("吃切糕的斯派克")
  4. private String name;
  5. @Value("25")
  6. private int age;
  7. @Autowired //===>单独使用是按类型注入
  8. @Qualifier("school") //===>俩注解一起使用是按名称注入
  9. private School school;
  10. }

切记!!!!一定要在applicationContext.xml配置文件中添加包扫描。

<context:component-scan base-package="com.spike.pojo"></context:component-scan>
(3)同源类型

 1.被注入的对象的类型(Student中的School对象)与注入的对象(Bean工厂中创建好的对象)的类型完全一致。
 2.被注入的对象的类型(Student中的School对象--父)与注入的对象(Bean工厂中创建好的对象--子)的类型是父子类类型。
               按类型注入时如果有多个可被注入的对象,再次按名称进行筛选注入
               按名称注入时如果有多个可被注入的对象,直接按名称指定注入
 3.被注入的对象的类型(Student中的School对象--接口)与注入的对象(Bean工厂中创建好的对象--实现类)的类型是接口和实现类。

10、添加包扫描的方式

(1)单个包扫描(推荐使用)
  1. <context:component-scan base-package="com.spike.mapper"></context:component-scan>
  2. <context:component-scan base-package="com.spike.service.impl"></context:component-scan>
  3. <context:component-scan base-package="com.spike.controller"></context:component-scan>
(2)多个包扫描,多个包之间以逗号或分号或空格分隔
<context:component-scan base-package="com.spike.mapper;com.spike.service.impl,com.spike.controller">
(3)扫描根包(不推荐使用,因为多扫无用包,降低容器启动效率)
<context:component-scan base-package="com.spike"></context:component-scan>

11、Spring配置文件的拆分

因为项目越来越大,多人协作开发,要进行配置文件拆分。

(1)按层拆(推荐使用)
  1. <!--applicationContext_mapper.xml-->
  2. <bean id="uMapper" class="com.spike.mapper.UsersMapperImpl"></bean>
  3. <bean id="bMapper" class="com.spike.mapper.BookMapperImpl"></bean>
  4. <!--applicationContext_service.xml-->
  5. <bean id="uService" class="com.spike.service.impl.UsersServiceImpl">
  6. <property name="usersMapper" ref="uMapper"></property>
  7. </bean>
  8. <bean id="bService" class="com.spike.service.impl.BookServiceImpl">
  9. <property name="bookMapper" ref="bMapper"></property>
  10. </bean>
  11. <!--applicationContext_controller.xml-->
  12. <bean id="usersController" class="com.spike.controller.UsersController">
  13. <property name="usersService" ref="uService"></property>
  14. </bean>
  15. <bean id="bookController" class="com.spike.controller.BookController">
  16. <property name="bookService" ref="bService"></property>
  17. </bean>
(2)按功能拆
  1. <!--applicationContext_users.xml-->
  2. <bean id="uMapper" class="com.spike.mapper.UsersMapperImpl"></bean>
  3. <bean id="uService" class="com.spike.service.impl.UsersServiceImpl">
  4. <property name="usersMapper" ref="uMapper"></property>
  5. </bean>
  6. <bean id="usersController" class="com.spike.controller.UsersController">
  7. <property name="usersService" ref="uService"></property>
  8. </bean>
  9. <!--applicationContext_book.xml-->
  10. <bean id="bMapper" class="com.spike.mapper.BookMapperImpl"></bean>
  11. <bean id="bService" class="com.spike.service.impl.BookServiceImpl">
  12. <property name="bookMapper" ref="bMapper"></property>
  13. </bean>
  14. <bean id="bookController" class="com.spike.controller.BookController">
  15. <property name="bookService" ref="bService"></property>
  16. </bean>

12、配置文件的整合

(1)单个文件导入
  1. <import resource="applicationContext_mapper.xml">
  2. <import resource="applicationContext_service.xml">
  3. <import resource="applicationContext_controller.xml">
(2)批量导入
<import resource="applicationContext_*.xml">

13、IOC实现之基于xml与注解的区别

注解优点:
                     方便
                     直观
                     高效(代码少,没有配置文件的书写那么复杂)。
注解弊端:   以硬编码的方式写入到 Java 代码中,修改是需要重新编译代码的。


XML方式优点:
                         配置和代码是分离的。
                         在 xml 中做修改,无需编译代码,只需重启服务器即可将新的配置加载。
XML方式缺点:编写麻烦,效率低,大型项目过于复杂。

14、什么是AOP

AOP(Aspect Orient Programming),面向切面编程。
切面就是程序中那些公共的通用的,重复的代码和功能称为切面。例如:日志,事务 ,权限等。
面向切面编程:将切面单拎出来开发,在需要的方法中自动反织回去.
通过运行期动态代理实现程序功能的统一维护的一种技术。
面向切面编程,就是将交叉业务逻辑封装成切面,利用 AOP 容器的功能将切面织入到主业务逻辑中。提高程序的可重用性,同时提高了开发的效率。

15、手写AOP框架

目的:完成业务和切面功能的解耦合
业务:图书或商品购买业务功能
切面:事务或日志或权限

代理设计模式是23种设计模式之一。目标对象不可访问,通过代理对象增强功能访问就是代理设计模式。
代理模式分为静态代理和动态代理。动态代理分为JDK动态代理和CGLib动态代理。

静态代理的设计规范:
                                (1)必须有业务接口
                                (2)代理对象和目标(业务)对象实现同一个业务接口
                                (3)静态代理对象在程序运行前就已经存在
                                (4)静态代理负责业务和切面的整合

JDK动态代理的设计规范:
                                (1)必须有业务接口
                                (2)动态代理对象在程序运行时动态的在内存中构建
                                (3)动态代理通过方法传参进行业务和切面的整合
                                (4)动态代理对象不能代理业务接口外的方法

JDK动态代理涉及到的类和接口
                                (1)Proxy类   java.lang.reflect包下的类
                                         用来生成动态代理对象
                                        public static Object newProxyInstance(ClassLoader loader,
                                                                                                      Class<?>[] interfaces,
                                                                                                      InvocationHandler h)
                                (2)InvocationHandler接口
                                         代理处理器,用来进行切面和业务整合
                     public Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
                                (3)Method类
                                         用来传递测试类中正在调用的目标方法buy(),one()
                                         method.invoke();  ===>进行手工调用目标方法

代码示例       手写AOP-------->循序渐进

第一个版本:业务和切面耦合在一起

  1. /**
  2. * 第一个版本:业务和切面耦合在一起
  3. */
  4. public class BookServiceImpl {
  5. public void buy(){
  6. try {
  7. //切面
  8. System.out.println("事务开启............");
  9. //业务
  10. System.out.println("图书购买业务功能实现 ...........");
  11. //切面
  12. System.out.println("事务提交............");
  13. } catch (Exception e) {
  14. System.out.println("事务回滚...........");
  15. }
  16. }
  17. }

第二个版本:使用静态子类代理拆分业务和切面

  1. /**
  2. * 第二个版本:通过子类实现切面功能,父类只做业务实现
  3. */
  4. public class BookServiceImpl {
  5. public void buy(){
  6. //只完成主业务功能
  7. System.out.println("图书购买功能实现..............");
  8. }
  9. }
  10. /**
  11. * 第二个版本:子类完成切面功能(事务)的处理,整合父类的业务功能
  12. */
  13. public class SubBookServiceImpl extends BookServiceImpl {
  14. @Override
  15. public void buy() {
  16. //子类中提供事务的切面
  17. try {
  18. System.out.println("事务开启...........");
  19. super.buy();//父类中主业务功能实现
  20. System.out.println("事务提交...........");
  21. } catch (Exception e) {
  22. System.out.println("事务回滚...........");
  23. }
  24. }
  25. }

 第三个版本:使用静态代理,完成业务和切面的解耦合

  1. /**
  2. * 第三个版本:接口规定业务功能
  3. */
  4. public interface Service {
  5. //购买的业务
  6. void buy();
  7. }
  8. public class BookServiceImpl implements Service {
  9. @Override
  10. public void buy() {
  11. System.out.println("图书购买业务功能实现 ..............");
  12. }
  13. }
  14. public class ProductServiceImpl implements Service {
  15. @Override
  16. public void buy() {
  17. System.out.println("商品业务功能实现.........");
  18. }
  19. }
  20. /**
  21. * 第三个版本的静态代理类,在本类中完成业务和切面的整合
  22. * 灵活目标对象的切换
  23. */
  24. public class Agent implements Service{
  25. //类中的成员变量设计为接口
  26. Service target;
  27. //通过构造方法传入目标对象,方法的参数设计为接口
  28. public Agent(Service target){
  29. this.target = target;
  30. }
  31. @Override
  32. public void buy() {
  33. try {
  34. //事务切面
  35. System.out.println("事务开启.............");
  36. //调用目标对象的业务功能
  37. // BookServiceImpl bookService = new BookServiceImpl();
  38. // bookService.buy();
  39. // ProductServiceImpl productService = new ProductServiceImpl();
  40. // productService.buy();
  41. target.buy();//图书对象来了,就是图书功能实现 ,商品对象来了,就是商品功能实现
  42. //事务切面
  43. System.out.println("事务提交............");
  44. } catch (Exception e) {
  45. System.out.println("事务回滚............");
  46. }
  47. }
  48. }
  49. @Test
  50. public void test02(){
  51. //调用时接口指向实现类
  52. Service agent = new Agent(new ProductServiceImpl());
  53. agent.buy();
  54. }

  第四个版本   使用静态代理,以面向接口的方式进行业务和切面的各种拆分整合

  1. /**
  2. * 切面接口定义
  3. */
  4. public interface AOP {
  5. //JDK8以后的新特性,默认接口实现default
  6. default void before(){}
  7. default void after(){}
  8. default void exception(){}
  9. }
  10. /**
  11. * 事务的切面实现
  12. */
  13. public class TransAop implements AOP {
  14. @Override
  15. public void before() {
  16. System.out.println("事务开启............");
  17. }
  18. @Override
  19. public void after() {
  20. System.out.println("事务提交............");
  21. }
  22. @Override
  23. public void exception() {
  24. System.out.println("事务回滚............");
  25. }
  26. }
  27. /**
  28. * 日志的切面实现
  29. */
  30. public class LogAop implements AOP {
  31. @Override
  32. public void before() {
  33. System.out.println("前置日志输出...........");
  34. }
  35. }
  36. /**
  37. * 静态代理对象,完成业务和切面的整合
  38. * 通过接口灵活的进行业务功能切换和切面功能切换
  39. */
  40. public class Agent implements Service {
  41. //成员变量设计为接口(面向接口编程)
  42. Service target; //灵活切换目标对象
  43. AOP aop; //灵活切换切面对象
  44. //构造方法传入参数(方法的参数设计为接口--->面向接口编程)
  45. public Agent(Service target,AOP aop){
  46. this.target = target;
  47. this.aop = aop;
  48. }
  49. @Override
  50. public void buy() {
  51. try {
  52. //切面
  53. aop.before(); //不同的切面功能
  54. //业务
  55. target.buy(); //不同的业务功能
  56. //切面
  57. aop.after();
  58. } catch (Exception e) {
  59. aop.exception();
  60. }
  61. }
  62. }
  63. @Test
  64. public void test02(){
  65. //通过静态代理对象完成功能调用
  66. Service agent = new Agent(new ProductServiceImpl(),new LogAop());
  67. agent.buy();
  68. }

  第五个版本  使用动态代理,解耦合业务和切面

  1. public class ProxyFactory {
  2. public static Object getAgent(Service yewu,AOP aop){
  3. return Proxy.newProxyInstance(
  4. yewu.getClass().getClassLoader(), // ClassLoader loader,类加载器,将目标对象加载进JVM被解析执行
  5. yewu.getClass().getInterfaces(), //Class<?>[] interfaces, 目标对象实现的所有业务接口
  6. // InvocationHandler h 代理处理器,进行业务和切面整合
  7. new InvocationHandler() {
  8. @Override
  9. public Object invoke(
  10. //生成的动态代理对象(本次没用)
  11. Object proxy,
  12. //外部测试类中正在调用的目标方法通过method对象传进来,可以手工在此调用 method.invoke();===>buy()
  13. Method method,
  14. //目标方法的参数
  15. Object[] args) throws Throwable {
  16. //进行业务和切面整合
  17. Object obj = null;
  18. try {
  19. //切面
  20. aop.before();
  21. //业务方法 buy() 通过反射调用目标方法 method.invoke(yewu,args)===> buy();
  22. obj = method.invoke(yewu,args);
  23. //切面
  24. aop.after();
  25. } catch (Exception e) {
  26. //切面
  27. aop.exception();
  28. }
  29. return obj; //目标方法的返回值
  30. }
  31. }
  32. );
  33. }
  34. }

16、Spring原生AOP的实现

Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice; 
After通知:在目标方法被调用后调用,涉及接口org.springframework.aop.AfterReturningAdvice; 
Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.ThrowsAdvice; 
Around通知:拦截对目标方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor;

17、AspectJ框架

它是专门针对AOP的非常优秀的框架,易学易用。
它是基于java语言开发的,可以无缝扩展功能。
AspetJ 是 Eclipse 的开源项目。

(1)AspectJ常见通知类型

(1)@Before:前置通知
(2)@AfterReturning:后置通知
(3)@Around:环绕通知
(4)@After:最终通知
(5)@Pointcut:给切入点表达式起别名

(2)AspectJ 的切入点表达式(重点

用来指定切入切面的位置
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
简化版公式:
execution(方法返回值 方法声明(参数))
常见符号解析:
    *    代表任意字符(通配符)
    ..   出现在方法的参数中,代表任意参数
         出现在路径中,代表当前路径及其子路径及其所有的类


  代码示例

  1. execution(public * *(..)) //所有公共访问权限的方法
  2. execution(* set*(..)) //以set开头的任意方法
  3. execution(* com.spike.service.impl.*.*(..)) //com.spike.service.impl下的所有类中的所有方法
  4. execution(* com.spike.service..*.*(..)) //com.spike.service及其子包和所有类
  5. execution(* *..service.*.*(..)) //以service结尾的包,前面可以有多级包
  6. execution(* *.service.*.*(..)) //service前面只能有一级包
(3)@Before前置通知 

    在目标方法前切入切面功能,不能影响目标方法的返回值。

  1. @Aspect //交给AspectJ框架去处理切面功能
  2. @Component
  3. public class MyAspect {
  4. /**
  5. * 实现切面功能要依靠切面方法
  6. * 前置通知切面方法的规范:
  7. * 1)访问权限是public
  8. * 2)切面方法没有返回值void
  9. * 3)切面方法名称自定义
  10. * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
  11. * 5)使用@Before注解声明是前置通知
  12. * 参数:
  13. * value:指定切入点表达式
  14. * 目标方法:
  15. * public String doSome(String name, int age)
  16. */
  17. // @Before(value = "execution(public String com.spike.service.SomeServiceImpl.doSome(String,int))")
  18. //@Before(value = "execution(public String com.spike.service.SomeServiceImpl.*(String,int))")
  19. //@Before(value = "execution(public String com.spike.service.SomeServiceImpl.*(..))")
  20. //@Before(value = "execution(public * com.spike.service.SomeServiceImpl.*(..))")
  21. //@Before(value = "execution(public * com.spike.service.*.*(..))")
  22. //@Before(value = "execution(public * com.spike.service..*.*(..))")
  23. @Before(value = "execution(public * *..service..*.*(..))")
  24. public void myBefore(){
  25. System.out.println("前置通知功能实现");
  26. }
  27. }
(4)@AfterReturning后置通知

    在目标方法执行后切入切面功能,在切面方法中可以得到目标方法的返回值。
    目标方法的返回值是通过切面方法的参数传入,所以受传参的值传递和引用传递的影响,值传递      (String+8种基本类型)不能影响目标方法的返回值,但引用传递可以改变目标方法的返回值。

  1. @Aspect
  2. @Component
  3. public class MyAspect {
  4. /**
  5. * 后置通知切面方法的规范
  6. * 1)访问权限是public
  7. * 2)切面方法没有返回值void
  8. * 3)切面方法名称自定义
  9. * 4)切面方法可以没有参数,如果有参数就是目标方法的返回值
  10. * 5)使用@AfterReturning注解声明是后置通知
  11. * 参数
  12. * value:指定切入点表达式
  13. * returning:指定返回的目标方法的返回值的名称,此名称与切面方法参数名称一致
  14. */
  15. @AfterReturning(value = "execution(* com.spike.service.*.*(..))",returning ="obj" )
  16. public void myAfterReturning(Object obj){
  17. //先判断是否有返回值
  18. if(obj != null){
  19. //再判断是否是String类型
  20. if(obj instanceof String){
  21. String s = (String) obj;
  22. System.out.println("在切面方法中目标方法的返回值是:"+s.toUpperCase());
  23. }
  24. //再判断是否是Student类型
  25. if(obj instanceof Student){
  26. Student stu = (Student) obj;
  27. stu.setName("吃切糕的斯派克");
  28. System.out.println("在切面方法中目标方法的返回值是:"+stu);
  29. }
  30. }
  31. System.out.println("后置通知功能实现");
  32. }
  33. }
(5)@Around环绕通知

  是通过拦截目标方法,在其前后切入切面功能,它是功能最强大的通知,事务就是使用的环绕通知
  在切面方法中可以随意改变目标方法的返回值。
  切面方法的参数就是目标方法本身。
  切面方法的返回值就是目标方法的返回值。
  切面方法中可以控制目标方法的访问。

  1. //需求:根据目标方法的第一个参数是吃切糕的斯派克,则可访问目标方法,否则拒绝访问目标方法.
  2. @Aspect
  3. @Component
  4. public class MyAspect {
  5. /**
  6. * 环绕通知切面方法规范
  7. * 1)访问权限是public
  8. * 2)切面方法有返回值,其返回值就是目标方法的返回值
  9. * 3)切面方法名称自定义
  10. * 4)切面 法有参数,参数就是目标方法本身
  11. * 5)要回避异常Throwable
  12. * 6)使用@Around注解声明是环绕通知
  13. * 参数:
  14. * value:指定切入点表达式
  15. */
  16. @Around(value = "execution(* com.spike.service.*.*(..))")
  17. public Object myAround(ProceedingJoinPoint pjp) throws Throwable {
  18. //取出目标方法的参数
  19. Object[] params = pjp.getArgs();
  20. if (params.length > 1) {
  21. String name = (String) params[0];
  22. if ("吃切糕的斯派克".equals(name)) {
  23. //前切功能
  24. System.out.println("环绕通知中的前置功能");
  25. //调用目标方法
  26. Object obj = pjp.proceed();
  27. //后切功能
  28. System.out.println("环绕通知中的后置功能");
  29. return obj.toString().toUpperCase();
  30. }
  31. }
  32. System.out.println("您无权访问目标方法");
  33. return null;
  34. }
  35. }
(6)@After最终通知

是在目标方法执行后切入切面,不管目标方法是否正常执行,切面方法中的功能都会执行,它有点类似于finally,是程序的一个出口,用来进行善后处理,关闭流,清空对象等。

  1. @Aspect
  2. @Component
  3. public class MyAspect {
  4. /**
  5. * 实现切面功能要依靠切面方法
  6. * 最终通知切面方法的规范:
  7. * 1)访问权限是public
  8. * 2)切面方法没有返回值void
  9. * 3)切面方法名称自定义
  10. * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
  11. * 5)使用@After注解声明是最终通知
  12. * 参数:
  13. * value:指定切入点表达式
  14. *
  15. */
  16. @After(value = "execution(* com.spike.service.*.*(..))")
  17. public void myAfter(){
  18. System.out.println("最终通知功能实现");
  19. }
  20. }
(7)@Pointcut切入点表达式起别名

如果多个切面切入到同一个切入点,则可以使用@Pointcut注解起别名,使用一个无参无返空实现的方法名称作为别名。

  1. @Aspect
  2. @Component
  3. public class MyAspect {
  4. /**
  5. * 实现切面功能要依靠切面方法
  6. * 最终通知切面方法的规范:
  7. * 1)访问权限是public
  8. * 2)切面方法没有返回值void
  9. * 3)切面方法名称自定义
  10. * 4)切面可以没有参数,如果有参数只能是JoinPoint类型
  11. * 5)使用@After注解声明是最终通知
  12. * 参数:
  13. * value:指定切入点表达式
  14. *
  15. */
  16. @After(value = "mycut()")
  17. public void myAfter(){
  18. System.out.println("最终通知功能实现");
  19. }
  20. @Before(value = "mycut()")
  21. public void myBefore1(){
  22. System.out.println("前置通知功能实现1");
  23. }
  24. @Before(value = "mycut()")
  25. public void myBefore2(){
  26. System.out.println("前置通知功能实现2");
  27. }
  28. @AfterReturning(value = "mycut()")
  29. public void myAfterReturning(){
  30. System.out.println("后置通知功能实现");
  31. }
  32. @Pointcut(value = "execution(* com.spike.service.*.*(..))")
  33. public void mycut(){}
  34. }
(8)AspectJ框架切换动态代理模式

     AspectJ框架默认使用的是JDK动态代理,可以设置为CGLib动态代理。
  (1)JDK动态代理必须实现接口

<aop:aspectj-autoproxy></aop:aspectj-autoproxy>    ===>JDK动态代理

    代理对象必须使用接口来接。
    SomeService service = (SomeService) ac.getBean("someServiceImpl");
    动态代理对象的类型是:class jdk.proxy2.$Proxy19
    如果使用实现类来接动态代理对象则报错:class jdk.proxy2.$Proxy19 cannot be cast to class        com.spike.service.SomeServiceImpl 

  (2)设置为CGLib动态代理,可以不实现接口,它是通过动态生成子类代理重写父类中业务方法来扩展功能。

<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> ===>CGLib动态代理

    可以使用接口或实现类来接动态代理对象
    SomeServiceImpl service = (SomeServiceImpl) ac.getBean("someServiceImpl");
    SomeService service = (SomeService) ac.getBean("someServiceImpl");

18、Spring的事务

事务的处理是由Spring来控制,Spring事务控制的方式有两种:编程式事务和声明式事务


  (1)编程式事务(注解式事务)

1.在配置文件中添加事务管理器

  1. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  2. <!--必须配置数据源-->
  3. <property name="dataSource" ref="dataSource"></property>
  4. </bean>

2.在配置文件中添加事务的注解驱动

<tx:annotation-driven></tx:annotation-driven>

3.在类上或方法上添加@Transactional注解声明事务

  1. @Transactional
  2. public Integer saveAccounts(Accounts accounts) {
  3. Integer num = accountsMapper.saveAccounts(accounts);
  4. System.out.println(accounts.getaName()+"帐户增加成功!num="+num);
  5. //手工抛出异常
  6. System.out.println(1/0);
  7. return num;
  8. }

4.@Transactional注解参数详解

  1. @Transactional(
  2. noRollbackForClassName = "ArithmeticException", //发生指定的异常不回滚事务,使用异常的名称
  3. noRollbackFor = ArithmeticException.class, //发生指定的异常不回滚事务,使用异常的类型
  4. rollbackForClassName = "NullPointerException", //发生指定的异常必须回滚事务,使用异常的名称
  5. rollbackFor = NullPointerException.class, //发生指定的异常必须回滚事务,使用异常的类型
  6. isolation = Isolation.DEFAULT , //使用数据库默认的事务隔离级别
  7. timeout = -1, //设置连接超时时间,默认-1,永不超时
  8. readOnly = false, //查询时必须设置只读属性为true,默认是false
  9. propagation = Propagation.REQUIRED //事务的传播特性
  10. )

(2)事物的传播特性

事务的传播特性可以决定多个事务之间的互斥,归并,加入等。
  常用
  PROPAGATION_REQUIRED:必被包含事务
  PROPAGATION_REQUIRES_NEW:自己新开事务,不管之前是否有事务
  PROPAGATION_SUPPORTS:支持事务,如果加入的方法有事务,则支持事务,如果没有,不    单开事务
  PROPAGATION_NEVER:不能运行中事务中,如果包在事务中,抛异常
  PROPAGATION_NOT_SUPPORTED:不支持事务,运行在非事务的环境
  不常用
  PROPAGATION_MANDATORY:必须包在事务中,没有事务则抛异常
  PROPAGATION_NESTED:嵌套事务

(3)声明式事务(一般推荐使用)

只需要在配置文件中声明,不需要硬编码实现事务管理。
  需要方法有命名规范:
  增加:  insert   save   add   create
  更新:  update   change  modify   set  
  删除:  delete   drop   remove  clear
  查询:  select   search  find  query get

代码示例 

  1. <!-- 基于注解的开发添加包扫描-->
  2. <context:component-scan base-package="com.spike.service.impl"></context:component-scan>
  3. <!-- 添加事务管理器-->
  4. <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  5. <property name="dataSource" ref="dataSource"></property>
  6. </bean>
  7. <!-- 添加事务的切面-->
  8. <tx:advice id="myadive" transaction-manager="transactionManager">
  9. <tx:attributes>
  10. <tx:method name="*select*" read-only="true"/>
  11. <tx:method name="*find*" read-only="true"/>
  12. <tx:method name="*get*" read-only="true"/>
  13. <tx:method name="*search*" read-only="true"/>
  14. <tx:method name="*query*" read-only="true"/>
  15. <tx:method name="*insert*" propagation="REQUIRED" />
  16. <tx:method name="*add*" propagation="REQUIRED"/>
  17. <tx:method name="*save*" propagation="REQUIRED" />
  18. <tx:method name="*create*" propagation="REQUIRED"/>
  19. <tx:method name="*update*" propagation="REQUIRED"/>
  20. <tx:method name="*change*" propagation="REQUIRED"/>
  21. <tx:method name="*modify*" propagation="REQUIRED"/>
  22. <tx:method name="*set*" propagation="REQUIRED"/>
  23. <tx:method name="*delete*" propagation="REQUIRED"/>
  24. <tx:method name="*drop*" propagation="REQUIRED"/>
  25. <tx:method name="*remove*" propagation="REQUIRED"/>
  26. <tx:method name="*clear*" propagation="REQUIRED"/>
  27. <tx:method name="*" propagation="SUPPORTS"/>
  28. </tx:attributes>
  29. </tx:advice>
  30. <!-- 切面和切入点绑定-->
  31. <aop:config>
  32. <aop:pointcut id="mycut" expression="execution(* com.spike.service.impl.*.*(..))"/>
  33. <aop:advisor advice-ref="myadive" pointcut-ref="mycut"></aop:advisor>
  34. </aop:config>
(4)事务管理器 

但凡要进行事务的处理,必须添加事务管理器。
事务管理器是根据不同的技术或框架来生成提交和回滚的对象。
  JDBC:       Connection       con.commit();     con.rollback();
  MyBatis:    SqlSession     sqlSession.commit();     sqlSession.rollback();
  Hibernate:  Session          session.commit();      session.rollback();

  MyBatis框架的事务管理器是:DataSourceTransactionManager

19、Spring中常见的设计模式

(1)工厂模式:Spring通过工厂模式BeanFactory,ApplicationContext创建Bean对象。
(2)代理设计模式:SpringAOP的实现,底层使用了动态代理模式。
(3)单例模式:Spring中的Bean默认都是单例的。
(4)模板方法模式:Spring中jdbcTemplate,hibernateTemplate等以Template结尾的类都用到了模板模式。
(5)装饰模式:我们的项目需要连接多个数据库,而不同的客户在访问时可能会访问不同的数据库,这种模式可以让我们根据用户的需求动态的切换数据库。
(6)观察者模式:Spring的事件驱动是观察者模式的应用。
(7)适配器模式:SpringAOP的增强功能使用到了适配器模式。

笔者的话

写到这里已经2w字了,可能还有些知识点未列出,笔者在后续会进行更新。同时也恭喜你成功的学到了最后

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/寸_铁/article/detail/767720
推荐阅读
相关标签