网站建设服务器是什么意思,网页游戏制作软件,长春网站建设产品展示,企业网站选择第三方还是自己做什么是Spring
Spring是一个轻量级Java开发框架#xff0c;目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题#xff0c;以提高开发效率。它是一个分层的JavaSE/JavaEE full-stack#xff08;一站式#xff09;轻量级开源框架#xff0c;为开发Java应用程序…什么是Spring
Spring是一个轻量级Java开发框架目的是为了解决企业级应用开发的业务逻辑层和其他各层的耦合问题以提高开发效率。它是一个分层的JavaSE/JavaEE full-stack一站式轻量级开源框架为开发Java应用程序提供全面的基础架构支持。Spring负责基础架构因此Java开发者可以专注于应用程序的开发。 Spring最根本的使命是解决企业级应用开发的复杂性即简化Java开发提高下发效率。
为什么要使用Spring
根据Spring官网所述Spring可以实现更快更轻松更安全地编程。 Spring对速度简单性和生产力的关注使其成为世界上最受欢迎的Java 开发框架。具体来说主要体现在以下六个方面 (1) Spring无处不在。Spring受到全世界开发人员的信任。各个领域各个企业都在广泛使用。 (2) Spring灵活性强。Spring提供的扩展能力和启动的粘合第三方库的能力可以使开发人员轻松构建几乎任何可以想象的应用程序。Spring框架的控制反转IoC能力为广泛的功能集奠定了基础。 (3) Spring可以提高生产力。Spring Boot改变了Java编程的方式从根本上简化了编程体验。Spring Cloud使微服务开发变得轻松。 (4) Spring快速。Spring可以快速启动快速关闭并支持执行的优化。Spring项目也越来越多地支持反应式非阻塞编程模型以提高效率。 (5) Spring注重安全。Spring代码提交者与安全专家合作修补和测试所有报告的漏洞。Spring密切关注关联的第三方类库并定期发布更新以帮助确保数据和应用程序尽可能安全。此外Spring Security可以更轻松地与行业标准安全方案集成。 (6) Spring社区支持较好。Spring社区是一个庞大全球化多元的社区涵盖了从初学者到经验丰富的职业人士的各个阶段需要内容拥有带入新高度所需的支持和资源快速入门指南和教程视频聚会支持等等。
Spring 框架简介 (1) Spring Core提供 Spring 框架的基本功能。核心容器的主要组件是BeanFactory它是工厂模式的实现。BeanFactory 使用控制反转IOC模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。 (2) Spring AOP提供 Spring 框架面向切面的编程能力将面向切面的编程功能集成到 Spring 框架中可以将 Spring 框架管理的任何对象实现 AOP提供了事务管理服务通过使用 Spring AOP不用依赖 EJB 组件就可以将声明性事务管理集成到应用程序中。 (3) Spring Context向 Spring 框架提供上下文信息。Spring 上下文支持消息源和观察者模式。Spring 上下文包括企业服务例如 JNDI、EJB、电子邮件、国际化、校验和调度功能。 (4) Spring ORMSpring 框架整合了若干个 ORM 框架从而提供了 ORM 的对象关系工具其中包括 JDO、Hibernate 和 iBatis SQL Map。所有这些都遵从 Spring 的通用事务和 DAO 异常层次结构。 (5) Spring DAO提供数据访问能力访问数据库提供了有意义的异常层次结构可用该结构来管理异常处理和不同数据库供应商抛出的错误消息。异常层次结构简化了错误处理并且极大地降低了需要编写的异常代码数量例如打开和关闭连接。 (6) Spring WebWeb 上下文模块建立在应用程序上下文模块之上为基于 Web 的应用程序提供了上下文。所以Spring 框架支持与 Jakarta Struts 的集成Web 模块还简化了处理多部分请求以及将请求参数绑定到域对象的工作。 (7) Spring MVCMVC 框架是一个全功能的构建 Web 应用程序的 MVC 实现。通过策略接口MVC 框架变成为高度可配置的MVC 容纳了大量视图技术其中包括 JSP、Velocity、Tiles、iText 和 POI。
Spring Bean
Bean创建的容器类
Spring 有两个容器类 BeanFactory 和 ApplicationContext。 BeanFactory这是一个最简单的容器它主要的功能是为依赖注入DI提供支持。 ApplicationContextApplication Context 是 Spring 中的高级容器。和 BeanFactory 类似它可以加载和管理配置文件中定义的 Bean。 另外它还增加了企业所需要的功能比如从属性文件中解析文本信息和将事件传递给所指定的监听器。
BeanFactory 和 ApplicationContext 的区别
二者都是 Spring 框架的两大核心接口都可以当做 Spring 的容器。其中 ApplicationContext 是 BeanFactory 的子接口。 BeanFactory 是 Spring 里面最底层的接口包含了各种 Bean 的定义读取配置文档管理 Bean 的加载、实例化控制 Bean 的生命周期维护对象之间的依赖关系等功能。 ApplicationContext 接口作为 BeanFactory 的派生除了提供 BeanFactory 所具有的功能外还提供了更完整的框架功能 (1) 继承 MessageSource支持国际化。 (2) 统一的资源文件访问方式。 (3) 提供在监听器中注册 Bean 的事件。 (4) 支持同时加载多个配置文件。 (5) 载入多个有继承关系上下文使得每一个上下文都专注于一个特定的层次如应用的 Web 层。 具体区别体现在以下三个方面 (a) 加载方式不同 BeanFactroy 采用的懒加载方式注入 Bean即只有在使用到某个 Bean 时才对该 Bean 实例化。这样我们就不能在程序启动时发现一些存在的 Spring 的配置问题。 ApplicationContext 是在启动时一次性创建了所有的 Bean。 (b) 创建方式不同 BeanFactory 通常以编程的方式被创建ApplicationContext 还能以声明的方式创建如使用 ContextLoader。 (c) 注册方式不同 二者都支持 BeanPostProcessor、BeanFactoryPostProcessor 的使用但 BeanFactory 需要手动注册而 ApplicationContext 则是自动注册。
Bean的创建过程
(1) Spring Bean的容器类通过解析注解类或者以其他方式定义的类将Bean标签解析成BeanDefinitionBean定义信息 (2) BeanFactory通过获取到的BeanDefinition利用反射创建Bean对象。 (3) 通过populateBean()方法对Bean对象进行属性填充。 (4) 通过invokeAwareMethods()方法对Bean对象进行赋值。 (5) 调用BeanPostProcessor的初始化前置方法。 (6) 调用init-method方法进行初始化操作。 (7) 调用BeanPostProcessor的初始化后置方法AOP在此处进行。 (8) 将创建好的Bean对象放入BeanDefinitionMap容器中。 (9) 通过Context.getBean()方法获得Bean对象并使用。–正常是通过依赖注入的方式使用 (10) spring容器关闭时会调用DisposableBean的destory()方法销毁Bean对象如果配置了destory-method属性spring会自动调用指定的销毁方法。 单例bean的初始化以及依赖注入一般都在容器初始化阶段进行只有懒加载lazy-init为true的单例bean是在应用第一次调用getBean()时进行初始化和依赖注入。 多例bean 在容器启动时不实例化即使设置 lazy-init 为 false 也没用只有调用了getBean()才进行实例化。
Spring Bean 作用域
Spring 提供以下五种 Bean 的作用域 (1) Singleton: Bean 在每个 Spring Ioc 容器中只有一个实例也是 Spring 的默认配置。 (2) Prototype一个 Bean 的定义可以有多个实例。 (3) Request每次 Http 请求都会创建一个 Bean故该作用域仅在基于 Web 的 Spring ApplicationContext情形下有效。 (4) Session在一个 Http Session 中一个 Bean 对应一个实例。该作用域同样仅在基于 Web 的 Spring ApplicationContext 情形下有效。 (5) Global-session在一个全局的 Http Session 中一个 Bean 定义对应一个实例。 值的注意的是使用 Prototype 作用域时需要慎重的思考因为频繁创建和销毁 Bean 会带来很大的性能开销。
Spring 自动装配
Spring的自动装配有三种模式byType(根据类型)byName(根据名称)、constructor(根据构造函数)。
Autowired是按类型匹配的(byType)如果需要按名称(byName)匹配的话可以使用Qualifier注解与Autowired结合。 Resource默认按 byName模式自动注入。Resource有两个中重要的属性name和type。Spring容器对于Resource注解的name属性解析为bean的名字type属性则解析为bean的类型。因此使用name属性则按byName模式的自动注入策略如果使用type属性则按 byType模式自动注入策略。倘若既不指定name也不指定type属性Spring容器将通过反射技术默认按byName模式注入。 上述两种自动装配的依赖注入并不适合简单值类型如int、boolean、long、String以及Enum等对于这些类型Spring容器也提供了Value注入的方式。Value接收一个String的值该值指定了将要被注入到内置的java类型属性值Spring 容器会做好类型转换。一般情况下Value会与properties文件结合使用。
Spring 的单例是否线程安全
Spring 中的单例 Bean 并不是线程安全的。 但我们日常使用时往往并未做多线程并发处理那又是如何保证线程安全的呢 实际上大部分时候我们定义的 Bean 是无状态的如 dao 类所有某种程度上来说 Bean 也是安全的但如果 Bean 有状态的话比如 model 对象那就要开发者自己去保证线程安全了。 其中有状态就是有数据存储功能无状态就是不会。
对于有状态的Bean如何保证线程安全
使用ThreadLocal来保证。也可使用多例的模式但是不推荐(在高并发场景下)。
Spring Bean 生命周期
Bean 在 Spring 容器中从创建到销毁经历了若干阶段每一阶段都可以进行个性化定制。 (1实例化Spring 对 Bean 进行实例化 (2属性注入Spring 将配置和 Bean 的引用注入到对应的属性中 (3如果 Bean 实现了 BeanNameAware 接口Spring 将 Bean 的 ID 传递给 setBeanName() 方法 (4如果 Bean 实现了 BeanFactoryAware 接口Spring 将调用 setBeanFactory() 方法将 BeanFactory 容器实例传入 (5如果 Bean 实现了 ApplicationContextAware 接口Spring 将调用 setApplicationContext() 方法将 Bean 所在的应用上下文的引用传入进来 (6BeanPostProcessor前置处理如果 Bean 实现了 BeanPostProcessor 接口Spring 将调用它们的 postProcessBeforeInitialization() 方法 (7初始化如果 Bean 实现了 InitializingBean 接口Spring 将调用它们的 afterPropertiesSet() 方法。类似地如果 Bean 使用 initmethod 声明了初始化方法该方法也会被调用 (8BeanPostProcessor后置处理如果 Bean 实现了 BeanPostProcessor 接口Spring 将调用它们的postProcessAfterInitialization()方法 (9使用此时Bean 已经准备就绪可以被应用程序使用了它们将一直驻留在应用上下文中直到该应用上下文被销毁 (10销毁如果 Bean 实现了 DisposableBean 接口Spring 将调用它的 destroy() 接口方法。同样如果使用 destroymethod 声明了销毁方法该方法也会被调用。
Spring 常用的Bean注解
(1) Configuration、Component、Service、Repository、Controller用于标识Bean的角色。 (2) Autowired、Qualifier、Resource、Value用于自动装配Bean。 (3) PostConstruct、PreDestroy用于指定初始化和销毁方法。 (4) Transactional用于声明式事务管理。 (5) Scope用于指定Bean作用域。 (6) Async用于实现异步编程。
Bean和Component有什么区别
都是使用注解定义 Bean。Bean 是使用 Java 代码装配 BeanComponent 是自动装配 Bean。 Component 注解用在类上表明一个类会作为组件类并告知Spring要为这个类创建bean每个类对应一个 Bean。 Bean 注解用在方法上表示这个方法会返回一个 Bean。Bean 需要在配置类中使用即类上需要加上Configuration注解。 Bean 注解更加灵活。当需要将第三方类装配到 Spring 容器中因为没办法源代码上添加Component注解只能使用Bean 注解的方式。
Spring Bean 循环依赖及解决办法
循环依赖指两个或多个Bean之间相互引用形成了一个无限循环调用的情况。在Spring容器中如果存在循环依赖但是都是单例模式的Bean则可以通过Spring容器提前暴露正在创建的Bean从而避免循环依赖问题。如果存在循环依赖且其中一方是原型模式的Bean则Spring无法处理这种情况。 临时规避显式配置参数。
循环依赖问题在Spring中主要有三种情况 1通过构造方法进行依赖注入时产生的循环依赖问题。 2通过setter方法进行依赖注入且是在多例原型模式下产生的循环依赖问题。 3通过setter方法进行依赖注入且是在单例模式下产生的循环依赖问题。 在Spring中只有第3种方式的循环依赖问题被解决了其他两种方式在遇到循环依赖问题时都会产生异常。这是因为 第一种构造方法注入的情况下在new对象的时候就会堵塞住了其实也就是”先有鸡还是先有蛋“的历史难题。 第二种setter方法多例的情况下每一次getBean()时都会产生一个新的Bean如此反复下去就会有无穷无尽的Bean产生了最终就会导致OOM问题的出现。 Spring在单例模式下的setter方法依赖注入引起的循环依赖问题主要是通过二级缓存和三级缓存来解决的其中三级缓存是主要功臣。解决的核心原理就是在对象实例化之后依赖注入之前Spring提前暴露的Bean实例的引用在第三级缓存中进行存储。
解释一下 Spring IoC
IoC简介
IoC(Inversion of Control即控制反转)不是一种技术而是一种设计思想。传统程序设计直接在对象内部通过new的方式创建对象。而这种方式会导致程序间的耦合。假如类A依赖于接口IB且在其内部创建了类B实现接口IB的一个实例。这种方式会将类B的实例的创建耦合到了类A中当需要使用接口IB的另一个实现时就需要更改代码。而IoC则反转依赖对象的获取方式。IoC提供一个容器来创建这些对象即由Ioc容器来控制对象的创建。这样当类A需要依赖接口IB时仅需声明对IB的引用具体的实例化由IoC容器负责。
DI简介
DI(Dependency Injection依赖注入)就是让容器去决定依赖关系即容器全权负责的组件的装配它会把符合依赖关系的对象通过属性或者构造函数传递给需要的对象。常见的依赖注入实现有三种构造方法注入、setter方法注入、接口注入(从 Spring4 开始已被废弃)等。 (1) 构造方法注入即被注入对象可以通过在其构造方法中声明依赖对象的参数列表让外部通常是IoC容器知道它需要哪些依赖对象然后外部会检查被注入对象的构造方法取得其所需要的依赖对象列表进而为其注入相应对象。 (2) setter方法注入即当前对象只需要为其依赖对象所对应的属性添加setter方法IoC容器通过此setter方法将相应的依赖对象设置到被注入对象的方式即setter方法注入。 (3) 接口注入被注入对象如果想要IoC容器为其注入依赖对象就必须实现某个接口这个接口提供一个方法用来为被注入对象注入依赖对象IoC容器通过接口方法将依赖对象注入到被注入对象中去。相对于前两种注入方式接口注入比繁琐和死板被注入对象必须声明和实现另外的接口。
解释一下AOP
AOPAspect-Oriented Programming面向切面编程可以看成OOPObject-Oriented Programing面向对象编程的补充和完善。面向对象编程时建立了一种对象层次结构可以定义从上到下的关系但不适合定义从左到右的关系。如日志功能。日志代码水平地散布在所有的对象层次中也就是说多个对象使用相似或相同的日志代码。对于其他类型的代码如安全性、异常处理等也是如此。这种散布在各个对象中的业务逻辑无关的代码被称为横切cross-cutting代码在面向对象设计中这种代码导致了大量的代码重复不利于模块的重用。 简单来说面向对象编程的自上而下的关系不适合自左向右的关系会带来横切代码的重复。而AOP技术主要针对这个问题进行优化。 AOP技术可以将那些影响了多个类的公共行为封装到一个可重用模块并将其名为“Aspect”即切面。所谓“切面”就是将那些与业务无关但业务模块所共同调用的逻辑或责任封装起来从而减少系统的重复代码降低模块间的耦合度。 AOP把软件系统分为两个部分核心关注点和横切关注点。核心关注点主要指业务处理的主要流程横切关注点则指与业务处理的主要流程关系不大的部分。横切关注点的一个特点是他们经常发生在核心关注点的多处而各处都基本相似。比如权限认证、日志、事务处理。AOP的作用在于分离系统中的各种关注点将核心关注点和横切关注点分离。正如Avanade公司的高级方案构架师Adam Magee所说AOP的核心思想就是“将应用程序中的商业逻辑同对其提供支持的通用服务进行分离。” (1) 切面Aspect切面是通知和切入点的结合。通知和切入点共同定义了切面的全部内容。 (2) 连接点Join point指方法在Spring AOP中一个连接点总是代表一个方法的执行。连接点是在应用执行过程中能够插入切面的一个点。这个点可以是调用方法时、抛出异常时、甚至修改一个字段时。切面代码可以利用这些点插入到应用的正常流程之中并添加新的行为。 (3) 通知Advice在AOP术语中切面的工作被称为通知。 (4) 切入点Pointcut切入点的定义会匹配通知所要织入的一个或多个连接点。通常使用明确的类和方法名称或是利用正则表达式定义所匹配的类和方法名称来指定这些切点最经典的使用方式是使用注解。 (5) 引入Introduction引入允许我们向现有类添加新方法或属性。 (6目标对象Target Object 被一个或者多个切面aspect所通知advise的对象。它通常是一个代理对象。 (7) 织入Weaving织入是把切面应用到目标对象并创建新的代理对象的过程。在目标对象的生命周期里有以下时间点可以进行织入 (a) 编译期切面在目标类编译时被织入。AspectJ的织入编译器是以这种方式织入切面的。 (b) 类加载期切面在目标类加载到JVM时被织入。需要特殊的类加载器它可以在目标类被引入应用之前增强该目标类的字节码。AspectJ5的加载时织入就支持以这种方式织入切面。 (c) 运行期切面在应用运行的某个时刻被织入。一般情况下在织入切面时AOP容器会为目标对象动态地创建一个代理对象。Spring AOP就是以这种方式织入切面。
Spring AOP应用场景
AOP常见于以下场景 (1) 日志记录 (2) 权限校验和管理 (3) 缓存 (4) 事务
Spring AOP 实现原理
Spring AOP是基于动态代理实现。通过动态代理可以对被代理对象的方法进行增强。Spring AOP用到了两种动态代理技术JDK动态代理、CGLIB库。 JDK动态代理只能对实现了接口的类生成代理而不能针对类。 CGLIB(Code Generation Library) 是针对类实现代理主要是对指定的类生成一个子类覆盖其中的方法继承。 Spring AOP默认策略是如果目标类是接口则使用 JDK 动态代理技术否则使用 Cglib 来生成代理。 更多动态代理的介绍可参考笔者之前的文章。
Spring AOP 使用方式
Spring Boot简化了AOP的使用。这里仅介绍如何在Spring Boot中使用AOP。 (1) 引入Maven依赖
!--引用AOP注解功能开始--
dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-aop/artifactId
/dependency
!--引用AOP注解功能结束--(2) 自定义注解
/*** Author: courage007* Date: 2021/02/05/下午3:54* Description:*/
Target({ElementType.METHOD})
Retention(RetentionPolicy.RUNTIME)
Documented
public interface CustomAnnotation {/**** return 指定值*/String value() default ;/**** return 指定名称*/String name() default ;}(3) 自定义切面并声明切入点并实现AOP通知
/*** Author: courage007* Date: 2021/02/05/下午3:47* Description:*/
Aspect
Component
public class CustomAspect implements Ordered {/*** * 切入点*/Pointcut(annotation(com.github.courage007.springdemo.annotation.CustomAnnotation))public void pointCutCustomAnnotation() {// 无需内容System.out.println(Test Pointcut Method);}Before(pointCutCustomAnnotation())public void before(JoinPoint joinPoint) {try {Signature signature joinPoint.getSignature();//获取方法参数Object[] arguments joinPoint.getArgs();//获取目标类Class? aClass joinPoint.getTarget().getClass();//获取类名String targetName aClass.getName();//获取当前方法名insertString methodName signature.getName();//获取方法数组Method[] methods aClass.getMethods();String operation ;//获取当前方法1Class?[] argTypes new Class[arguments.length];for (int i 0; i arguments.length; i) {argTypes[i] arguments[i] null ? null : arguments[i].getClass();}Method method null;try {method aClass.getMethod(methodName, argTypes);operation method.getAnnotation(CustomAnnotation.class).value();} catch (NoSuchMethodException e) {}//获取当前方法2MethodSignature signature1 (MethodSignature)joinPoint.getSignature();Method method1 signature1.getMethod();//获取方法参数类型数组Class?[] parameterTypes method1.getParameterTypes();//获取当前方法3for (Method mm : methods) {if (mm.getName().equals(methodName)) {Class?[] clazzs mm.getParameterTypes();if (clazzs.length arguments.length) {//获取代理方法上注解的operation参数的值operation mm.getAnnotation(CustomAnnotation.class).value();break;}}}//通过方法获取注解参数值CustomAnnotation serviceLog method.getAnnotation(CustomAnnotation.class);String item serviceLog.value();String biz serviceLog.name();StringBuilder paramsBuf new StringBuilder();for (Object arg : arguments) {paramsBuf.append(arg);paramsBuf.append();}} catch (Throwable e) {}}After(pointCutCustomAnnotation())public void after(JoinPoint joinPoint) {try {String targetName joinPoint.getTarget().getClass().getName();String methodName joinPoint.getSignature().getName();Object[] arguments joinPoint.getArgs();Class? targetClass Class.forName(targetName);Method[] methods targetClass.getMethods();String operation ;for (Method method : methods) {if (method.getName().equals(methodName)) {Class?[] clazzs method.getParameterTypes();if (clazzs.length arguments.length) {operation method.getAnnotation(CustomAnnotation.class).value();break;}}}StringBuilder paramsBuf new StringBuilder();for (Object arg : arguments) {paramsBuf.append(arg);paramsBuf.append();}} catch (Throwable e) {}}/*** 环绕通知处理处理** param* throws Throwable*/Around(pointCutCustomAnnotation())public Object around(ProceedingJoinPoint point) throws Throwable {// 先执行业务,注意:业务这样写业务发生异常不会拦截日志。Object result point.proceed();try {handleAround(point);// 处理日志} catch (Exception e) {}return result;}AfterReturning(pointcut pointCutCustomAnnotation())public void doAfterReturning(JoinPoint joinPoint) {System.out.println(TTEESSTT);}/*** around日志记录** param point* throws SecurityException* throws NoSuchMethodException*/public void handleAround(ProceedingJoinPoint point) throws Exception {Signature sig point.getSignature();MethodSignature msig null;if (!(sig instanceof MethodSignature)) {throw new IllegalArgumentException(该注解只能用于方法);}msig (MethodSignature) sig;Object target point.getTarget();Method currentMethod target.getClass().getMethod(msig.getName(), msig.getParameterTypes());// 方法名称String methodName currentMethod.getName();// 获取注解对象CustomAnnotation aLog currentMethod.getAnnotation(CustomAnnotation.class);// 类名String className point.getTarget().getClass().getName();// 方法的参数Object[] params point.getArgs();StringBuilder paramsBuf new StringBuilder();for (Object arg : params) {paramsBuf.append(arg);paramsBuf.append();}}AfterThrowing(pointcut pointCutCustomAnnotation(), throwing e)public void doAfterThrowing(JoinPoint joinPoint, Throwable e) {try {String targetName joinPoint.getTarget().getClass().getName();String className joinPoint.getTarget().getClass().getName();String methodName joinPoint.getSignature().getName();Object[] arguments joinPoint.getArgs();Class? targetClass Class.forName(targetName);Method[] methods targetClass.getMethods();String operation ;for (Method method : methods) {if (method.getName().equals(methodName)) {Class?[] clazzs method.getParameterTypes();if (clazzs.length arguments.length) {operation method.getAnnotation(CustomAnnotation.class).value();break;}}}StringBuilder paramsBuf new StringBuilder();for (Object arg : arguments) {paramsBuf.append(arg);paramsBuf.append();}} catch (Exception ex) {}}
}(4) 使用自定义注解
/*** Author: courage007* Date: 2021/02/05/下午3:15*/
public class CustomController {Autowiredprivate ICustomService customService;GetMapping(/xxx)CustomAnnotation(value 测试, name test)public Integer getXXX() {return customService.getXXX();}
}Spring AOP通知顺序
AOP支持的通知类型有Before、After、Around、AfterReturning、AfterThrowing等。接下来详细说明下 (1) Before在目标方法被调用之前做增强处理Before只需要指定切入点表达式即可。 (2) AfterReturning在目标方法正常完成之后做增强AfterReturning除了指定切入点表达式后还可以指定一个返回值形参名returning代表目标方法的返回值。 (3) AfterThrowing主要用来处理程序中未处理的异常AfterThrowing除了指定切入点表达式后还可以指定一个throwing的返回值形参名可以通过该形参名来访问目标方法中所抛出的异常对象。 (4) After在目标方法完成之后做增强无论目标方法时候成功完成。After可以指定一个切入点表达式。 (5) Around环绕通知在目标方法完成前后做增强处理环绕通知是最重要的通知类型像事务、日志等都是环绕通知,注意编程中核心是一个ProceedingJoinPoint。 注意spring aop在方法正常返回和抛出异常时各个通知的执行顺序不同。此外Spring版本不一样通知执行顺序也会存在差异。 (1) Spring4.0 正常情况环绕前置Before目标方法执行环绕返回环绕最终AfterAfterReturning 异常情况环绕前置Before目标方法执行环绕异常环绕最终AfterAfterThrowing (2) Spring5.28 正常情况环绕前置Before目标方法执行AfterReturningAfter环绕返回环绕最终 异常情况环绕前置Before目标方法执行AfterThrowingAfter环绕异常环绕最终 可见Spring 5 提前了AfterXXX的执行位置。
多个切面的执行顺序
上面的例子仅描述单个切面的执行顺序如果在同一个方法有多个AOP其执行不会存在任何顺序。也就是说这些代码会随机生成。如果需要按照指定的顺序运行还需手动设置优先级。 Spring aop就是一个同心圆要执行的方法为圆心最外层的order最小。从最外层按照AOP1、AOP2的顺序依次执行doAround方法doBefore方法。然后执行method方法最后按照AOP2、AOP1的顺序依次执行doAfterReturn、doAfter方法。也就是说对多个AOP来说先before的一定后after。
配置多个切面的执行顺序的方式
Spring支持多种方式配置AOP执行顺序如实现实现org.springframework.core.Ordered接口、使用Ordered注解、配置文件添加配置等。 (1) 实现org.springframework.core.Ordered接口
Aspect
Component
public class CustomAspectWithOrder implements Ordered {Override public int getOrder() { return 1; } } (2) 使用Ordered注解
Aspect
Component
Order(1)
public class CustomAspectWithOrder {} (3) 配置文件添加配置
aop:config expose-proxytrue aop:aspect refaopBean order0 aop:pointcut idtestPointcut expressionannotation(xxx.xxx.xxx.annotation.xxx)/ aop:around pointcut-reftestPointcut methoddoAround / /aop:aspect
/aop:config Pointcut注解
Pointcut注解用于声明切入点。在Pointcut注解中通过书写切入点表达式来描述需要处理的连接点集合。Pointcut注解定义如下 /*** Pointcut declaration** author Alexandre Vasseur*/
Retention(RetentionPolicy.RUNTIME)
Target(ElementType.METHOD)
public interface Pointcut {/*** return the pointcut expression* We allow as default for abstract pointcut*/String value() default ;/*** When compiling without debug info, or when interpreting pointcuts at runtime,* the names of any arguments used in the pointcut are not available.* Under these circumstances only, it is necessary to provide the arg names in * the annotation - these MUST duplicate the names used in the annotated method.* Format is a simple comma-separated list.* * return argNames the argument names (should match those in the annotated method)*/String argNames() default ;
}DeclareParents注解
在面向切面的编程中对方法的增强叫做Wearing织入而对类的增强叫introduction引入。Introduction Advice引入增强就是对已有类添加方法它也是面向切面编程提供的一种特殊增强。 在Spring AOP中使用DeclareParents注解实现引入增强。 (1) 定义增强方法及对应类
/*** Author: courage007* Date: 2021/02/05/下午8:49* Description:*/
public interface ICustomServiceProxy {Integer getYYY();
}/*** Author: courage007* Date: 2021/02/05/下午8:50* Description:*/
public class CustomServiceProxyImpl implements ICustomServiceProxy {Overridepublic Integer getYYY() {return 222;}
}(2) 切面中声明DeclareParents注解
/*** Author: courage007* Date: 2021/02/05/下午3:47* Description:*/
Order(1)
Aspect
Component
public class CustomAspect {DeclareParents(value com.github.courage007.springdemo.service.impl.CustomService*, defaultImpl CustomServiceProxyImpl.class)private ICustomServiceProxy customServiceProxy;
}(3) 使用引入增强的方法
/*** Author: courage007* Date: 2021/02/05/下午3:15* Description:*/
RestController
RequestMapping(custom)
public class CustomController {Autowiredprivate ICustomService customService;GetMapping(/xxx)CustomAnnotation(value 测试, name test)public Integer getXXX() {// 使用强制类型转换ICustomServiceProxy customServiceProxy (ICustomServiceProxy) customService;customServiceProxy.getYYY();return customService.getXXX();}
}
Spring 事务
Spring事务的本质就是数据库对事务的支持。Spring框架提供统一的事务抽象通过统一的编程模型使得应用程序可以很容易地在不同的事务框架之间进行切换。 无论是JTA、JDBC、Hibernate/JPA、Mybatis/Mybatis-PlusSpring都使用统一的编程模型使得应用程序可以很容易地在不同的事务框架之间进行切换。这也符合面向接口编程思想。Spring事务框架的代码在org.springframework:spring-tx中。Spring事务抽象的核心类图如下 Spring事务管理的核心接口是PlatformTransactionManager。接口PlatformTransactionManager定义事务操作的行为PlatformTransactionManager依赖TransactionDefinition和TransactionStatus接口。TransactionDefinition接口定义与Spring兼容的事务属性如隔离级别、事务传播行为等。TransactionStatus接口则定义事务的状态如是否回滚、是否完成、是否包含安全点Save Point、将基础会话刷新到数据存储区如果适用等。
PlatformTransactionManager简介
PlatformTransactionManager是Spring事务框架的核心接口。应用程序可以直接使用PlatformTransactionManager但它并不是主要用于API应用程序将借助事务模板TransactionTemplate或声明式事务Declarative Transaction。 对于需要实现PlatformTransactionManager接口的应用程序可通过继承AbstractPlatformTransactionManager抽象类的方式实现。AbstractPlatformTransactionManager类已实现事务传播行为和事务同步处理。子类需要实现针对事务特定状态如beginsuspendresumecommit的模板方法。Spring事务框架已经实现了JtaTransactionManagerJPA和DataSourceTransactionManagerJDBC。应用程序可以参考以上方法实现事务管理器。PlatformTransactionManager事务继承示例如下
Spring事务隔离级别和传播级别
TransactionDefinition接口中定义了Spring事务隔离级别和Spring事务传播级别。隔离级别主要控制事务并发访问时隔离程度。Spring支持的隔离级别如下 除了使用ISOLATION_DEFAULT表示使用数据库默认的隔离级别外其余四个隔离级别与数据库规范的隔离级别一致。 需要注意的是隔离级别越高意味着数据库事务并发执行性能越差。JDBC规范虽然定义了事务支持的以上行为但是各个JDBC驱动、数据库厂商对事务的支持程度可能各不相同。出于性能的考虑我们一般设置READ_COMMITTED级别。针对READ_COMMITTED隔离级别无法避免的脏读通常使用数据库的锁来处理。 传播级别主要控制含事务方法的调用如一个事务方法调用另一个事务方法时Spring对事务的处理方式。Spring事务传播级别共七类。它们是 (1)PROPAGATION_REQUIRED支持当前事务如果当前有事务则加入如果当前没有事务则新建一个。这种方式是默认的事务传播方式。(一般直接使用不需要调整) (2)PROPAGATION_SUPPORTS支持当前事务如果当前有事务则加入如果当前没有事务则以非事务方式执行。 (3)PROPAGATION_MANDATORY支持当前事务如果当前有事务则加入如果当前没有事务则抛出异常。当前必须有事务 (4)PROPAGATION_REQUIRES_NEW不支持当前事务如果当前有事务则挂起当前事务然后新创建一个事务如果当前没有事务则自己创建一个事务。 (5)PROPAGATION_NOT_SUPPORTED不支持当前事务如果当前有事务则把当前事务挂起执行完后恢复事务忽略当前事务。 (6)PROPAGATION_NEVER不支持当前事务如果当前存在事务则抛出异常。当前必须不能有事务 (7)PROPAGATION_NESTED如果当前存在事务则嵌套在当前事务中。如果当前没有事务则新建一个事务自己执行。对嵌套事务来说内部事务回滚时不会影响外部事务的提交但是外部事务回滚会把内部事务一起回滚。这个和新建一个事务的区别
Spring事务失效
常见的事务失效场景有 (1) 异常类型不对默认支持回滚的是 Runtime 异常或异常被业务捕获。 (2) 数据源不支持事务如 MySQL 未开启事务或使用 MyISAM 存储引擎。 (3) 非 Public 方法不支持事务。 (4) Spring传播类型不支持事务。 (5) 事务未被 Spring 接管。
Spring事务有优势
(1) 为不同的事务API 如 JTAJDBCHibernateJPA 和JDO提供一个不变的编程模式。 (2) 为编程式事务管理提供了一套简单的API而不是一些复杂的事务API (3) 支持声明式事务管理。 (4) 和Spring各种数据访问抽象层很好得集成。
参考
https://blog.csdn.net/adminpd/article/details/123016872 常见Java后端面试题系列——Spring篇 https://blog.csdn.net/qq_57434877/article/details/123714044 spring创建Bean的流程以及Bean的生命周期 https://thinkwon.blog.csdn.net/article/details/104397516 Spring面试题 https://blog.csdn.net/adminpd/article/details/123016872 Java后端面试题系列——Spring篇 https://zhuanlan.zhihu.com/p/623502268 Spring面试题详解—从基础到进阶 https://zhuanlan.zhihu.com/p/493343355 Spring面试题 https://blog.csdn.net/q982151756/article/details/80513340 细说Spring——AOP详解AOP概览 https://blog.51cto.com/5914679/2092253#h10 Spring AOP 切点(pointcut)表达式 https://blog.csdn.net/weixin_46009162/article/details/113333311 Spring boot AOP结合注解的使用 https://cloud.tencent.com/developer/article/1441626 spring aop概念、使用、动态代理原理 https://www.jianshu.com/p/5b9a0d77f95f spring aop 及实现方式 https://www.cnblogs.com/orzjiangxiaoyu/p/13869747.html Spring-AOP-基于注解的AOP通知执行顺序 https://blog.csdn.net/hxpjava1/article/details/55504513 spring多个AOP执行先后顺序 https://www.jianshu.com/p/f7238613c877 Spring AOP注解DeclareParents的使用 https://www.cnblogs.com/chihirotan/p/7365890.html Spring AOP中引入增强 https://www.cnblogs.com/wangshen31/p/9383828.html 用注解DelcareParents实现引用增强 https://blog.csdn.net/a745233700/article/details/80959716 Spring常见面试题总结