昆明网站关键词优化,苏州新区保洁公司,wordpress多梦主题,招标网免费1、Spring概述
1.1、Spring是什么?
Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题Spring最根本的使命是解决企业级应用开发的复杂性#xff0c;即简化Java开发。这些功能的底层都依赖于它的两个核心特性#xff0c;也就是…1、Spring概述
1.1、Spring是什么?
Spring是一个轻量级Java开发框架,目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题Spring最根本的使命是解决企业级应用开发的复杂性即简化Java开发。这些功能的底层都依赖于它的两个核心特性也就是依赖注入dependency injectionDI和面向切面编程aspect-oriented programmingAOP。
1.2 Spring的优缺点
优点
方便解耦简化开发 Spring就是一个大工厂可以将所有对象的创建和依赖关系的维护交给Spring管理。AOP编程的支持 Spring提供面向切面编程可以方便的实现对程序进行权限拦截、运行监控等功能。声明式事务的支持只需要通过配置就可以完成对事务的管理而无需手动编程。方便程序的测试 Spring对Junit4支持可以通过注解方便的测试Spring程序。方便集成各种优秀框架 Spring不排斥各种优秀的开源框架其内部提供了对各种优秀框架的直接支持如Struts、Hibernate、MyBatis等。降低JavaEE API的使用难度Spring对JavaEE开发中非常难用的一些APIJDBC、JavaMail、远程调用等都提供了封装使这些API应用难度大大降低
缺点
Spring明明一个很轻量级的框架却给人感觉大而全Spring依赖反射反射影响性能使用门槛升高入门Spring需要较长时间
1.3 Spring的组成模块
Spring 总共大约有 20 个模块 由 1300 多个不同的文件构成。 而这些组件被分别整合在核心容器Core Container 、 AOPAspect Oriented Programming和设备支持Instrmentation 、数据访问与集成Data Access/Integeration 、 Web、 消息Messaging 、 Test等 6 个模块中。 以下是 Spring 5 的模块结构图
**spring core**提供了框架的基本组成部分包括控制反转Inversion of ControlIOC和依赖注入Dependency InjectionDI功能。**spring beans**提供了BeanFactory是工厂模式的一个经典实现Spring将管理对象称为Bean。**spring context**构建于 core 封装包基础上的 context 封装包提供了一种框架式的对象访问方法。**spring jdbc**提供了一个JDBC的抽象层消除了烦琐的JDBC编码和数据库厂商特有的错误代码解析 用于简化JDBC。**spring aop**提供了面向切面的编程实现让你可以自定义拦截器、切点等。**spring Web**提供了针对 Web 开发的集成特性例如文件上传利用 servlet listeners 进行 ioc 容器初始化和针对 Web 的 ApplicationContext。**spring test**主要为测试提供支持的支持使用JUnit或TestNG对Spring组件进行单元测试和集成测试。
1.4 Spring中应用的设计模式
工厂模式BeanFactory就是简单工厂模式的体现用来创建对象的实例单例模式Bean默认为单例模式。代理模式Spring的AOP功能用到了JDK的动态代理和CGLIB字节码生成技术模板方法用来解决代码重复的问题。比如. RestTemplate, JmsTemplate, JpaTemplate。观察者模式定义对象键一种一对多的依赖关系当一个对象的状态发生改变时所有依赖于它的对象都会得到通知被制动更新如Spring中listener的实现–ApplicationListener。
2、Spring IOC
2.1、什么是IOC
IOCInversion of Control控制反转指将对象的控制权转移给Spring框架由 Spring 来负责控制对象的生命周期比如创建、销毁和对象间的依赖关系。 最直观的表达就是以前创建对象的时机和主动权都是由自己把控的如果在一个对象中使用另外的对象就必须主动通过new指令去创建依赖对象使用完后还需要销毁比如Connection等对象始终会和其他接口或类耦合起来。而 IOC 则是由专门的容器来帮忙创建对象将所有的类都在 Spring 容器中登记当需要某个对象时不再需要自己主动去 new 了只需告诉 Spring 容器然后 Spring 就会在系统运行到适当的时机把你想要的对象主动给你。也就是说对于某个具体的对象而言以前是由自己控制它所引用对象的生命周期而在IOC中所有的对象都被 Spring 控制控制对象生命周期的不再是引用它的对象而是Spring容器由 Spring 容器帮我们创建、查找及注入依赖对象而引用对象只是被动的接受依赖对象所以这叫控制反转。 看不懂没有关系帅哥公主请看代码
一般情况
public class Test {public static void main(String[] args) {A a new A();//a.b new B(); ! 如果我们没有这句代码运行是会报错的。a.b.shout();}
}
class A{B b; //这相当于是一个指针但是没有指向任何对象
}
class B{void shout(){System.out.println(我是B);}
}Spring情况
Component
public class A {Autowiredpublic B b;
}
Component
public class B {public void shout(){System.out.println(我是B);}
}
public class MyTest {Testpublic void test1(){ApplicationContext context new ClassPathXmlApplicationContext(beans.xml);A a context.getBean(a, A.class);//这个地方我们 new B() 了吗没有吧但是可以直接使用//因为 spring 容器帮我们创建了也处理了二者之间的依赖关系。我们直接用就行了a.b.shout();}
}2.2、什么是DI
IoC 的一个重点就是在程序运行时动态的向某个对象提供它所需要的其他对象这一点是通过DIDependency Injection依赖注入来实现的即应用程序在运行时依赖 IoC 容器来动态注入对象所需要的外部依赖。而 Spring 的 DI 具体就是通过反射实现注入的反射允许程序在运行的时候动态的生成对象、执行对象的方法、改变对象的属性
能注入的数据类型
基本类型和String。其他bean类型在配置文件中或者注解配置过的bean。复杂类型/集合类型。
注入的方式
使用构造函数提供。使用set方法提供。使用注解提供。接口注入不推荐
2.2.1 构造函数注入
顾名思义就是使用类中的构造函数给成员变量赋值。注意赋值的操作不是我们自己做的而是通过配置的方式让 spring 框架来为我们注入。 标签中的属性 type用于指定要注入的数据的数据类型该数据类型也是构造函数中某个或某些参数的类型。 index用于指定要注入的数据给构造函数中指定索引位置的参数赋值。索引的位置是从0开始。 name用于指定给构造函数中指定名称的参数赋值。 value用于提供基本类型和String类型的数据 ref用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象 优势 在获取bean对象时注入数据是必须的操作否则对象无法创建成功。 弊端 改变了bean对象的实例化方式使我们在创建对象时如果用不到这些数据也必须提供。
!--默认构造器方式--
bean iduser classcom.kuang.pojo.Userproperty namename value张三/
/bean!--通过有参构造创建对象。方式一下标赋值--
bean iduser classcom.kuang.pojo.Userconstructor-arg index0 valuejerry/
/bean!--通过有参构造创建对象。方式二类型创建不建议使用--
bean iduser classcom.kuang.pojo.Userconstructor-arg typejava.lang.String valuejarry/
/bean!--通过有参构造创建对象。方式三通过参数名推荐使用--
bean iduser classcom.kuang.pojo.Userconstructor-arg namename valuejarry/constructor-arg namebirthday refnow/
/bean
!-- 配置一个日期对象 --
bean idnow classjava.util.Date/bean2.2.2 set方法注入
顾名思义就是在类中提供需要注入成员的 set 方法。
set方法注入【常用】 标签的属性 name用于指定注入时所调用的set方法名称 value用于提供基本类型和String类型的数据 ref用于指定其他的bean类型数据。它指的就是在spring的Ioc核心容器中出现过的bean对象。 优势 创建对象时没有明确的限制可以直接使用默认构造函数。 弊端 如果有某个成员必须有值则获取对象是有可能set方法没有执行。
bean idaccountService2 classcom.itheima.service.impl.AccountServiceImpl2property namename valuetom /propertyproperty nameage value23/propertyproperty namebirthday refnow/property
/bean2.2.3 注解注入
//等价于 bean iduser classcom.x.pojo.User/
Component //组件
Scope(prototype)
public class User {//等价于 property namename value嘿嘿/
// Value(嘿嘿)public String name;//放set方法也行Value(嘿嘿)public void setName(String name) {this.name name;}
}Component注解用来将类声明为容器的组件当容器启动后通过扫描会生成对应的对象交给容器托管。
Value注解用来给属性赋值可以直接放在属性上也可以放在对应的set方法上
Scope注解声明 类的作用域
2.3 自动装配
自动装配针对的是引入型对象 自动装配和依赖注入有什么联系与区别 让我们来看看 Gpt 的回答 自动装配Autowired和依赖注入Dependency Injection在Spring框架中是相关概念它们实现了相同的目标即将一个对象的依赖关系注入到另一个对象中。然而它们之间存在一些联系和区别 联系 实现方式 自动装配是一种依赖注入的方式它可以通过Autowired注解基于注解的自动装配或者在XML配置文件中使用property元素基于XML的自动装配来实现。目标 自动装配和依赖注入的共同目标是减少硬编码提高代码的灵活性和可维护性同时降低了对象之间的耦合度。由Spring容器管理 在两种情况下Spring容器负责管理Bean之间的关系确保依赖的Bean在需要的时候能够被正确注入到目标Bean中。 区别 显式配置 vs 隐式配置 依赖注入可以通过显式配置在XML配置文件或Java配置类中明确指定依赖关系或者隐式配置使用Autowired注解进行自动装配来实现。自动装配是一种依赖注入的方式它是一种自动化的、隐式的依赖注入。灵活性 自动装配提供了更大的灵活性因为它不需要在配置文件中显式地指定每个Bean的依赖关系。相反Spring容器会根据自动装配规则自动地完成依赖注入这样就减少了配置的工作量同时也更容易适应变化。粒度 依赖注入的粒度更细可以对一个类的特定属性进行注入也可以对构造方法进行注入而自动装配通常是针对整个类进行的它会自动装配类中所有符合条件的属性。 综上所述自动装配是依赖注入的一种实现方式它通过隐式的方式完成依赖注入提高了配置的便利性和灵活性。依赖注入是一种更广泛的概念它包括了显式配置和隐式配置两种方式而自动装配则是依赖注入的一种特殊形式。 public class People {private Dog dog; //用来测试 xml 文件private Cat cat; // 用来测试 注解
}
public class Cat {String name;
}
public class Dog {private String name;
}放在此处下面要用到
2.3.1 基于XML配置文件
自动装配是spring满足bean依赖的一种方式。spring会通过在上下文寻找匹配实现bean的自动装配。
测试文件
public void test1(){ApplicationContext context new ClassPathXmlApplicationContext(beans.xml);People people context.getBean(people, People.class);System.out.println(people.getDog().getName());
}ByName
使用 byName 它会根据 people对应的属性名去匹配 id 名与之相同的 bean
注意它匹配id之后会进行一个类型的检查总不能给 cat属性装配一个 dog 对象吧
bean iddog classcom.x.pojo.Dogproperty namename valuedog/
/bean
bean iddog2 classcom.x.pojo.Dogproperty namename valuedong2/
/bean
bean idpeople classcom.x.pojo.People autowirebyName/输出结果为 dog
总结 byName 根据 name 去匹配 如果 没有匹配到 就会报错
ByType
使用 byType 它会根据 people对应的属性的类型名去匹配 class属性对应与之对应的 bean
bean iddog1 classcom.x.pojo.Dogproperty namename valuedog/
/bean
bean idpeople classcom.x.pojo.People autowirebyType/输出结果 dog
总结byType的根据属性去匹配如果有多个 匹配 的 bean 则会报错
2.3.2 基于注解
xml文件的形式 是在 标签使用 autowired属性
注解则有两种常用的 Autowired 和Resource
使用方法很简单我们直接再对应的属性上或者 set方法上添加注解就行
public class People {Autowired //注意 Resource 不能用在 构造方法上People(){}Autowired//Resourceprivate Cat cat;private String name;public Cat getCat() {return cat;}//ResourceAutowiredpublic void setCat(Cat cat) {this.cat cat;}二者还是有区别的
1、来源不同
Autowired 是 Spring 定义的注解Resource 是 Java 定义的注解记得导入相应的依赖
2、依赖查找顺序不同
Autowired 是先根据类型byType查找如果存在多个 Bean 再根据名称byName进行查找Resource 是先根据名称查找如果根据名称查找不到再根据类型进行查找
3、参数不同
Autowired 只支持设置一个 required 的参数 可以搭配Qualifier注解但是一旦指定名字且没有对应的id也不会进行type匹配了 Resource支持很多个
4、用法不同
Autowired 可以用在 属性set方法构造器方法Resource不能用在构造方法
2.4 使用注解实现bean的托管
我们发现前面我们每声明一个类都要在 xml 文件中添加一个对应的标签当类比较多时这个工作量就会很大。有没有别的方法呢
这就要提到我们的Component注解了。使用这个注解我们要在配置文件中进行声明
!--指定要扫描的包这个包下的注解就会生效--
context:component-scan base-packagecom.x/
!--开启注解配置--
context:annotation-config/ context:component-scan base-packagecom.x/当spring容器启动后他就会自动去扫描 com.x包下所有添加了
Component、controller、service、repository注解的类将其看成bean交给容器托管。
我们在类上添加一个 Component 注解就可以将类标记为Spring中的bean了
Component //组件
public class User {//等价于 property namename value嘿嘿/public String name;Value(嘿嘿)public void setName(String name) {this.name name;}
}基于 Component 扩展的注解还有三个 controller、service、repository
1、controller controller控制器层注入服务
2、service service服务层注入dao
3、repository dao持久层实现dao访问
4、component 标注一个类为Spring容器的Bean把普通pojo实例化到spring容器中相当于配置文件中的bean id class/这里不具体讲四者有什么区别只是希望大家简单的知道 service 标记的类说说这个类是service层的实现就够了
2.5 配置类实现
JavaConfig是Spring的一个子项目 在Spring4之后它变成了核心功能SpringBoot中推荐使用这种方法
配置类
myConfig2.class
Configuration
ComponentScan(basePackages com.x.pojo)
public class myConfig2 {}myConfig.class
package com.x.config;
Configuration //它也会被spring托管它的本质也是一个组件
// Configuration 代表他是一个配置类就和beans.xml一样
Import(myConfig2.class)
public class myConfig {//注册一个bean就相当于之前写的一个bean标签//这个方法的名字就相当于id//返回值就相当于 class属性BeanUser getUser(){return new User();//返回要注入bean的对象}
}Configration 注解声明当前类是一个配置类相当于 Spring 中的一个 XML 文件 Bean 注解作用在方法上声明当前方法的返回值是一个 Bean 配置类的加载
我们发现此处用到了一个新标签 Bean我们对比最开始用到的
Component
2.4.1 Bean 和 Component 的区别 Component和Service和Repository用于自动检测和使用类路径扫描自动配置bean。注释类和bean之间存在隐式的一对一映射即每个类一个bean。 Bean用于显式声明单个bean而不是让Spring像上面那样自动执行它。它将bean的声明与类定义分离并允许您精确地创建和配置bean 该怎么理解这段话呢 首先 我们知道 Component 是放在类上的这样就可以将这个类标记为bean并交给spring托管。我们完完全全就是在 这个类的 “源码”上进行的配置。那如果我们引入的是第三方的类不知道源码那我们怎么用Component注释呢这个时候就要用 Bean了。也就是所谓的声明与类定义分离。我们不需要知道类是怎么定义的。我们只需要声明一个这个类的对象 那可能又有疑问了既然是声明对象为什么不这样写呢 Configuration
public class AppConfig {BeanUser user;
}现在我们去看Bean的源码:这是源码的第一句话
Indicates that a method produces a bean to be managed by the Spring container.中文意思是指示方法生成要由 Spring 容器管理的 Bean。这样就应该能明白为啥放在方法上了吧。
3、Spring Bean
3.1、Spring Bean是什么
Bean作为Spring框架面试中不可或缺的概念其本质上是指代任何被Spring加载生成出来的对象。本质上区别于Java BeanJava Bean是对于Java类的一种规范定义。Spring Bean代表着Spring中最小的执行单位其加载、作用域、生命周期的管理都由Spring操作。可见Spring Bean在整个Spring框架中的重要地位。 什么是Java Bean 并非所有的类都是 Java Bean其是一种特殊的类具有以下特征 提供一个默认的无参构造函数。需要被序列化并且实现了 Serializable 接口。可能有一系列可读写属性并且一般是 private 的。可能有一系列的 getter 或 setter 方法。 3.2、Spring Bean的作用域
1、singleton默认作用域单例bean每个容器中只有一个bean的实例。
2、prototype为每一个bean请求创建一个实例。
3、request为每一个request请求创建一个实例在请求完成以后bean会失效并被垃圾回收器回收。
4、session与request范围类似同一个session会话共享一个实例不同会话使用不同的实例。
5、global-session全局作用域所有会话共享一个实例。如果想要声明让所有会话共享的存储变量的话那么这全局变量需要存储在global-session中。
3.3、Spring 的容器启动流程
详细见此文章 Spring Ioc 容器启动流程-CSDN博客
3.4、Spring Bean的生命周期
简单来说Spring Bean的生命周期只有四个阶段实例化 Instantiation -- 属性赋值 Populate -- 初始化 Initialization -- 销毁 Destruction
但具体来说Spring Bean的生命周期包含下图的流程 1实例化Bean
对于BeanFactory容器当客户向容器请求一个尚未初始化的bean时或初始化bean的时候需要注入另一个尚未初始化的依赖时容器就会调用createBean进行实例化。
对于ApplicationContext容器当容器启动结束后通过获取BeanDefinition对象中的信息实例化所有的bean。
2设置对象属性依赖注入实例化后的对象被封装在BeanWrapper对象中紧接着Spring根据BeanDefinition中的信息 以及 通过BeanWrapper提供的设置属性的接口完成属性设置与依赖注入。
3处理Aware接口Spring会检测该对象是否实现了xxxAware接口通过Aware类型的接口可以让我们拿到Spring容器的一些资源
①如果这个Bean实现了BeanNameAware接口会调用它实现的setBeanName(String beanId)方法传入Bean的名字 ②如果这个Bean实现了BeanClassLoaderAware接口调用setBeanClassLoader()方法传入ClassLoader对象的实例。 ②如果这个Bean实现了BeanFactoryAware接口会调用它实现的setBeanFactory()方法传递的是Spring工厂自身。 ③如果这个Bean实现了ApplicationContextAware接口会调用setApplicationContext(ApplicationContext)方法传入Spring上下文 4BeanPostProcessor前置处理如果想对Bean进行一些自定义的前置处理那么可以让Bean实现了BeanPostProcessor接口那将会调用postProcessBeforeInitialization(Object obj, String s)方法。
5InitializingBean如果Bean实现了InitializingBean接口执行afeterPropertiesSet()方法。
6init-method如果Bean在Spring配置文件中配置了 init-method 属性则会自动调用其配置的初始化方法。
7BeanPostProcessor后置处理如果这个Bean实现了BeanPostProcessor接口将会调用postProcessAfterInitialization(Object obj, String s)方法由于这个方法是在Bean初始化结束时调用的所以可以被应用于内存或缓存技术
以上几个步骤完成后Bean就已经被正确创建了之后就可以使用这个Bean了。
8DisposableBean当Bean不再需要时会经过清理阶段如果Bean实现了DisposableBean这个接口会调用其实现的destroy()方法
9destroy-method最后如果这个Bean的Spring配置中配置了destroy-method属性会自动调用其配置的销毁方法。
如果对bean详细加载流程的感兴趣的读者可以阅读这篇文章自己跟源码跟了两天看到最后有点崩溃放弃了大家一起看看大佬的文章吧还是太菜了后续如果有能力和事件的话一定会再次尝试读取源码SpringBean的生命周期
3.5、Spring Bean的线程安全问题
Spring容器本身并没有提供Bean的线程安全策略因此可以说Spring容器中的Bean本身不具备线程安全的特性但是具体情况还是要结合Bean的作用域来讨论。
1对于prototype作用域的Bean每次都创建一个新对象也就是线程之间不存在Bean共享因此不会有线程安全问题。
2对于singleton作用域的Bean所有的线程都共享一个单例实例的Bean因此是存在线程安全问题的。但是如果单例Bean是一个无状态Bean也就是线程中的操作不会对Bean的成员执行查询以外的操作那么这个单例Bean是线程安全的。比如Controller类、Service类和Dao等这些Bean大多是无状态的只关注于方法本身。 有状态Bean(Stateful Bean) 就是有实例变量的对象可以保存数据是非线程安全的。 无状态Bean(Stateless Bean)就是没有实例变量的对象不能保存数据是不变类是线程安全的。 对于有状态的bean比如Model和View就需要自行保证线程安全最浅显的解决办法就是将有状态的bean的作用域由“singleton”改为“prototype”。
也可以采用ThreadLocal解决线程安全问题为每个线程提供一个独立的变量副本不同线程只操作自己线程的副本变量。 ThreadLocal和线程同步机制都是为了解决多线程中相同变量的访问冲突问题。同步机制采用了“时间换空间”的方式仅提供一份变量不同的线程在访问前需要获取锁没获得锁的线程则需要排队。而ThreadLocal采用了“空间换时间”的方式。ThreadLocal会为每一个线程提供一个独立的变量副本从而隔离了多个线程对数据的访问冲突。因为每一个线程都拥有自己的变量副本从而也就没有必要对该变量进行同步了。 3.6、Spring Bean的循环依赖问题
循环依赖问题在Spring中主要有三种情况
1通过构造方法进行依赖注入时产生的循环依赖问题。 2通过setter方法进行依赖注入且是在多例原型模式下产生的循环依赖问题。 3通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。 在Spring中只有第3种方式的循环依赖问题被解决了其他两种方式在遇到循环依赖问题时都会产生异常。这是因为
第一种构造方法注入的情况下在new对象的时候就会堵塞住了其实也就是”先有鸡还是先有蛋“的历史难题。 第二种setter方法多例的情况下每一次getBean()时都会产生一个新的Bean如此反复下去就会有无穷无尽的Bean产生了最终就会导致OOM问题的出现。 Spring在单例模式下的setter方法依赖注入引起的循环依赖问题主要是通过二级缓存和三级缓存来解决的其中三级缓存是主要功臣。解决的核心原理就是在对象实例化之后依赖注入之前Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。
大家一起来膜拜大佬的文章吧写不动了脑袋不够用 Spring循环依赖问题
3.7、BeanFactory 和 ApplicationContext 区别
BeanFactory是Spring里面最底层的接口是IoC的核心定义了IoC的基本功能包含了各种Bean的定义、加载、实例化依赖注入和生命周期管理。
ApplicationContext接口作为BeanFactory的子类除了提供BeanFactory所具有的功能外还提供了更完整的框架功能
继承MessageSource因此支持国际化。资源文件访问如URL和文件ResourceLoader。载入多个有继承关系上下文即同时加载多个配置文件 使得每一个上下文都专注于一个特定的层次比如应用的web层。提供在监听器中注册bean的事件。
区别
1、 BeanFactroy采用的是延迟加载形式来注入Bean的只有在使用到某个Bean时(调用getBean())才对该Bean进行加载实例化。这样我们就不能提前发现一些存在的Spring的配置问题。如果Bean的某一个属性没有注入BeanFacotry加载后直至第一次使用调用getBean方法才会抛出异常。 ApplicationContext它是在容器启动时一次性创建了所有的Bean。这样在容器启动时我们就可以发现Spring中存在的配置错误这样有利于检查所依赖属性是否注入。 ApplicationContext启动后预载入所有的单实例Bean所以在运行的时候速度比较快因为它们已经创建好了。相对于BeanFactoryApplicationContext 唯一的不足是占用内存空间当应用程序配置Bean较多时程序启动较慢。 2、 BeanFactory和ApplicationContext都支持BeanPostProcessor、BeanFactoryPostProcessor的使用但两者之间的区别是BeanFactory需要手动注册而ApplicationContext则是自动注册。 3、 BeanFactory通常以编程的方式被创建ApplicationContext还能以声明的方式创建如使用ContextLoader。 4、Spring Aop
4.1、什么是 Aop
OOP面向对象允许开发者定义纵向的关系但并不适用于定义横向的关系会导致大量代码的重复而不利于各个模块的重用。
AOP一般称为面向切面作为面向对象的一种补充用于将那些与业务无关但却对多个对象产生影响的公共行为和逻辑抽取并封装为一个可重用的模块这个模块被命名为“切面”Aspect减少系统中的重复代码降低了模块间的耦合度提高系统的可维护性。可用于权限认证、日志、事务处理。
4.2、Spring AOP里面的几个名词的概念
1连接点Join point指程序运行过程中所执行的方法。在Spring AOP中一个连接点总代表一个方法的执行。
2切面Aspect被抽取出来的公共模块可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合一个切面可以由多个切点和通知组成。 在Spring AOP中切面可以在类上使用 AspectJ 注解来实现。 3切点Pointcut切点用于定义 要对哪些Join point进行拦截。 切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。 4通知Advice指要在连接点Join Point上执行的动作即增强的逻辑比如权限校验和、日志记录等。通知有各种类型包括Around、Before、After、After returning、After throwing。
5目标对象Target包含连接点的对象也称作被通知Advice的对象。 由于Spring AOP是通过动态代理实现的所以这个对象永远是一个代理对象。
6织入Weaving通过动态代理在目标对象Target的方法即连接点Join point中执行增强逻辑Advice的过程。
7引入Introduction添加额外的方法或者字段到被通知的类。Spring允许引入新的接口以及对应的实现到任何被代理的对象。例如你可以使用一个引入来使bean实现 IsModified 接口以便简化缓存机制。
几个概念的关系图可以参考下图
1连接点Join point指程序运行过程中所执行的方法。在Spring AOP中一个连接点总代表一个方法的执行。
2切面Aspect被抽取出来的公共模块可以用来会横切多个对象。Aspect切面可以看成 Pointcut切点 和 Advice通知 的结合一个切面可以由多个切点和通知组成。
在Spring AOP中切面可以在类上使用 AspectJ 注解来实现。
3切点Pointcut切点用于定义 要对哪些Join point进行拦截。
切点分为execution方式和annotation方式。execution方式可以用路径表达式指定对哪些方法拦截比如指定拦截add*、search*。annotation方式可以指定被哪些注解修饰的代码进行拦截。
4通知Advice指要在连接点Join Point上执行的动作即增强的逻辑比如权限校验和、日志记录等。通知有各种类型包括Around、Before、After、After returning、After throwing。
5目标对象Target包含连接点的对象也称作被通知Advice的对象。 由于Spring AOP是通过动态代理实现的所以这个对象永远是一个代理对象。
6织入Weaving通过动态代理在目标对象Target的方法即连接点Join point中执行增强逻辑Advice的过程。
7引入Introduction添加额外的方法或者字段到被通知的类。Spring允许引入新的接口以及对应的实现到任何被代理的对象。例如你可以使用一个引入来使bean实现 IsModified 接口以便简化缓存机制。
几个概念的关系图可以参考下图 4.3 通知
Advice的类型
1前置通知Before Advice在连接点Join point之前执行的通知。
2后置通知After Advice当连接点退出的时候执行的通知不论是正常返回还是异常退出。
3环绕通知Around Advice包围一个连接点的通知这是最强大的一种通知类型。 环绕通知可以在方法调用前后完成自定义的行为。它也可以选择是否继续执行连接点或直接返回它们自己的返回值或抛出异常来结束执行。
4返回后通知AfterReturning Advice在连接点正常完成后执行的通知如果连接点抛出异常则不执行
5抛出异常后通知AfterThrowing advice在方法抛出异常退出时执行的通知
Advice的执行顺序 没有异常情况下的执行顺序 around before advice before advice target method 执行 after advice around after advice after Returning advice 出现异常情况下的执行顺序 around before advice before advice target method 执行 after advice around after advice afterThrowing advice java.lang.RuntimeException异常发生 4.4、使用方法
public void test(){ApplicationContext context new ClassPathXmlApplicationContext(applicationContext.xml);//动态代理代理的是接口UserService userService context.getBean(userService, UserService.class);userService.add();
}用来测试的代码
第一种原生的AOP
bean iduserService classcom.x.service.UserServiceImpl/
bean idlog classcom.x.Log.Log/
bean idafterLog classcom.x.Log.AfterLog/
!--配置AOP:需要导入AOP的约束--
aop:config!--切入点我们需要在哪执行我们的方法--aop:pointcut idpointcut expressionexecution(* com.x.service.UserServiceImpl.*(..))/!--执行环绕增加--aop:advisor advice-reflog pointcut-refpointcut/aop:advisor advice-refafterLog pointcut-refpointcut/
/aop:config自定义的log类要实现相应的接口
public class AfterLog implements AfterReturningAdvice {//返回后的//returnValue 返回值Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println(执行了method.getName()方法,返回结果为:returnValue);}
}
public class Log implements MethodBeforeAdvice {//method要执行对象的目标方法//objects:参数//target目标对象Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println(target.getClass().getName()的method.getName()被执行了);}
}输出结果为 第二种自定义方法 bean iddiy classcom.x.diy.DiyPointCut/aop:config!--自定义切面,ref要引用的类--aop:aspect refdiy!--切入点--aop:pointcut idpoint expressionexecution(* com.x.service.UserServiceImpl.*(..))/!--通知--aop:before methodbefore pointcut-refpoint/aop:after methodafter pointcut-refpoint//aop:aspect/aop:configpublic class DiyPointCut {public void before(){System.out.println(方法执行前);}public void after(){System.out.println(方法执行后);}
}执行结果 第三种使用注解 !--方式三--bean idapc classcom.x.diy.AnnotationPointCut/!--开启注解支持 JDK(默认 proxy-target-classfalse) cglib(proxy-target-classtrue)--aop:aspectj-autoproxy /Aspect//标注这个类是一个切面
public class AnnotationPointCut {Before(execution(* com.x.service.UserServiceImpl.*(..)))public void before(){System.out.println(方法执行前);}After(execution(* com.x.service.UserServiceImpl.*(..)))public void after(){System.out.println(方法执行后);}//在就环绕增强中,我们可以给定一个参数,待定我们要获取的处理切入的点Around(execution(* com.x.service.UserServiceImpl.*(..)))public void around(ProceedingJoinPoint jp) throws Throwable {System.out.println(环绕前);Signature signature jp.getSignature();//获得签名System.out.println(signature:signature);//执行方法jp.proceed();//方法执行System.out.println(环绕后);}
}
执行结果 5、Spring 事务
Spring事务的本质其实就是数据库对事务的支持没有数据库的事务支持spring是无法提供事务功能的。Spring只提供统一事务管理接口具体实现都是由各数据库自己实现数据库事务的提交和回滚是通过 redo log 和 undo log实现的。Spring会在事务开始时根据当前环境中设置的隔离级别调整数据库隔离级别由此保持一致。
5.1、Spring事务的种类
spring支持编程式事务管理和声明式事务管理两种方式
①编程式事务管理使用TransactionTemplate。
②声明式事务管理建立在AOP之上的。其本质是通过AOP功能对方法前后进行拦截将事务处理的功能编织到拦截的方法中也就是在目标方法开始之前启动一个事务在执行完目标方法之后根据执行情况提交或者回滚事务。 声明式事务最大的优点就是不需要在业务逻辑代码中掺杂事务管理的代码只需在配置文件中做相关的事务规则声明或通过Transactional注解的方式便可以将事务规则应用到业务逻辑中减少业务代码的污染。唯一不足地方是最细粒度只能作用到方法级别无法做到像编程式事务那样可以作用到代码块级别。 5.2、spring的事务传播机制
pring事务的传播机制说的是当多个事务同时存在的时候spring如何处理这些事务的行为。事务传播机制实际上是使用简单的ThreadLocal实现的所以如果调用的方法是在新线程调用的事务传播实际上是会失效的。 PROPAGATION_REQUIRED默认传播行为如果当前没有事务就创建一个新事务如果当前存在事务就加入该事务。 ② PROPAGATION_REQUIRES_NEW无论当前存不存在事务都创建新事务进行执行。 ③ PROPAGATION_SUPPORTS如果当前存在事务就加入该事务如果当前不存在事务就以非事务执行。‘ ④ PROPAGATION_NOT_SUPPORTED以非事务方式执行操作如果当前存在事务就把当前事务挂起。 ⑤ PROPAGATION_NESTED如果当前存在事务则在嵌套事务内执行如果当前没有事务则按REQUIRED属性执行。 ⑥ PROPAGATION_MANDATORY如果当前存在事务就加入该事务如果当前不存在事务就抛出异常。 ⑦ PROPAGATION_NEVER以非事务方式执行如果当前存在事务则抛出异常。 5.3、Spring中的隔离级别 ① ISOLATION_DEFAULT这是个 PlatfromTransactionManager 默认的隔离级别使用数据库默认的事务隔离级别。 ② ISOLATION_READ_UNCOMMITTED读未提交允许事务在执行过程中读取其他事务未提交的数据。 ③ ISOLATION_READ_COMMITTED读已提交允许事务在执行过程中读取其他事务已经提交的数据。 ④ ISOLATION_REPEATABLE_READ可重复读在同一个事务内任意时刻的查询结果都是一致的。 ⑤ ISOLATION_SERIALIZABLE所有事务逐个依次执行。