Spring @Order注解使用详解

目录
  • 前言
  • 例子一
  • 例子二
  • 例子三
  • 实例论证
  • 源码分析

前言

很长一段时间没有写博客了,今天一时心血来潮,突然写出这篇文章就很突兀。但是看到网上关于Spring的@Order注解的不是特别准确的结论,想着还是表达一下看法,当然也是通过写文章来让自己的思路更清晰一点,如果有不是很赞同的部分,希望可以一起讨论。

首先先说结论:Spring的@Order注解(或者实现Ordered接口、或者实现PriorityOrdered接口),不决定Bean的实例化顺序和执行顺序,更不会影响Spring的Bean的扫描顺序;它影响着Spring将扫描的多个Bean放入数组、集合(Map)时的排序。

下面我只验证@Order注解,至于实现接口的方式有兴趣的可以试一下。

例子一

@Slf4j(topic = "e")
@Order(1)
@Component
public class OrderTestService1 {
	public OrderTestService1(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}
@Slf4j(topic = "e")
@Order(2)
@Component
public class OrderTestService2 {
	public OrderTestService2(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}
@Slf4j(topic = "e")
@Order(3)
@Component
public class OrderTestService3 {
	public OrderTestService3(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}

打印顺序如下:
OrderTestService1.java 行数=13 10:56:20.756 [main] DEBUG e - order-1
OrderTestService2.java 行数=13 10:56:20.760 [main] DEBUG e - order-2
OrderTestService3.java 行数=13 10:56:20.761 [main] DEBUG e - order-3

例子二

改变OrderTestService三个类的注解序值

@Slf4j(topic = "e")
@Order(3)
@Component
public class OrderTestService1 {
	public OrderTestService1(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}

}
@Slf4j(topic = "e")
@Order(2)
@Component
public class OrderTestService2 {
	public OrderTestService2(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}
@Slf4j(topic = "e")
@Order(1)
@Component
public class OrderTestService3 {
	public OrderTestService3(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}

结果:当改变OrderTestService接口的三个类注解序值时,类的实例化顺序根本没有变化,即@Order注解不决定Bean的实例化顺序。

例子三

@Slf4j(topic = "e")
public class E {
	public void orderList(){
		int orderValue = 0;
		if(this.getClass().isAnnotationPresent(Order.class)){
			Order order = this.getClass().getAnnotation(Order.class);
			orderValue = order.value();
		}
		log.debug("List Order postProcessBeanFactory {} order={}",this.getClass().getSimpleName(),orderValue);
	}
}
@Slf4j(topic = "e")
@Order(3)
@Component
public class OrderTestService1 extends E{
	public OrderTestService1(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}
@Slf4j(topic = "e")
@Order(2)
@Component
public class OrderTestService2 extends E{
	public OrderTestService2(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}
@Slf4j(topic = "e")
@Order(1)
@Component
public class OrderTestService3 extends E{
	public OrderTestService3(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
}
@Component
public class OrderTestService {
	List<E> beanFactoryPostProcessor;
	public List<E> getBeanFactoryPostProcessor() {
		return beanFactoryPostProcessor;
	}
	@Autowired
	public void setBeanFactoryPostProcessor(List<E> beanFactoryPostProcessor) {
		this.beanFactoryPostProcessor = beanFactoryPostProcessor;
	}
}

打印顺序如下:
E.java 行数=15 11:01:47.756 [main] DEBUG e - List Order postProcessBeanFactory OrderTestService3 order=1
E.java 行数=15 11:01:47.756 [main] DEBUG e - List Order postProcessBeanFactory OrderTestService2 order=2
E.java 行数=15 11:01:47.756 [main] DEBUG e - List Order postProcessBeanFactory OrderTestService1 order=3

结论:当通过注入类型为集合或者数组(可以自行认证)时,@Order的注解值影响注入的顺序,而这并不代表着说@Order注解影响着Bean的执行顺序,接着往下看。

实例论证

不影响Bean的执行顺序

然上面的三个类全都实现BeanFactoryPostProcessor,然后观察postProcessBeanFactory方法的执行顺序,如下:

@Slf4j(topic = "e")
@Order(3)
@Component
public class OrderTestService1 extends E implements BeanFactoryPostProcessor {
	public OrderTestService1(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		int orderValue = 0;
		if(this.getClass().isAnnotationPresent(Order.class)){
			Order order = this.getClass().getAnnotation(Order.class);
			orderValue = order.value();
		}
		log.debug("execute postProcessBeanFactory a order={}",orderValue);
	}
}
@Slf4j(topic = "e")
@Order(2)
@Component
public class OrderTestService2 extends E implements BeanFactoryPostProcessor {
	public OrderTestService2(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		int orderValue = 0;
		if(this.getClass().isAnnotationPresent(Order.class)){
			Order order = this.getClass().getAnnotation(Order.class);
			orderValue = order.value();
		}
		log.debug("execute postProcessBeanFactory a order={}",orderValue);
	}
}
@Slf4j(topic = "e")
@Order(1)
@Component
public class OrderTestService3 extends E implements BeanFactoryPostProcessor {
	public OrderTestService3(){
		log.debug("order-{}",this.getClass().getAnnotation(Order.class).value());
	}
	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		int orderValue = 0;
		if(this.getClass().isAnnotationPresent(Order.class)){
			Order order = this.getClass().getAnnotation(Order.class);
			orderValue = order.value();
		}
		log.debug("execute postProcessBeanFactory a order={}",orderValue);
	}
}

结论:通过上面的打印结果,Spring的@Order注解并不影响Bean的执行顺序。

接下来主要分析为什么@Order注解在注入类型为集合时可以影响其顺序。

源码分析

源码位置:org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency方法中这一段

@Nullable
	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				return shortcut;
			}
			// 获取依赖的类型
			Class<?> type = descriptor.getDependencyType();
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			if (value != null) {
				if (value instanceof String) {
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ?
							getMergedBeanDefinition(beanName) : null);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				try {
					return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
				}
				catch (UnsupportedOperationException ex) {
					// A custom TypeConverter which does not support TypeDescriptor resolution...
					return (descriptor.getField() != null ?
							converter.convertIfNecessary(value, type, descriptor.getField()) :
							converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
				}
			}
			// resolveMultipleBeans方法是解析当前依赖项是否支持多个bean注入 比如list
			// 如果是能支持多个注入则在该方法内部就完成了bean的查找,否则下面完成查找
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}
			// 完成查找的功能,有可能会查找出来多个结果
			// 需要注意的是这里的多个结果和上面的支持的多个注入不是同一回事
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (matchingBeans.isEmpty()) {
				// 如果没有找到而且你又在依赖上面加上了必须的条件,则会出异常
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				// 如果没有加必须条件则返回null,意思就是不注入任何对象
				return null;
			}
			String autowiredBeanName;
			Object instanceCandidate;
			// 假设找出来多个
			if (matchingBeans.size() > 1) {
				// 通过descriptor 也就是依赖描述器来推断出来需要注入的这个对象的名字
				// 注意这里不是当前注入对象的名字而是需要注入的对象的名字
				// 假设A依赖B,这里推断的B这个对象应该叫什么名字
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				// 假设推断出来为null
				// 什么情况下为null?就是你提供的名字和任何找出来的对象的名字匹配不上
				if (autowiredBeanName == null) {
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
					}
					else {
						// In case of an optional Collection/Map, silently ignore a non-unique case:
						// possibly it was meant to be an empty collection of multiple regular beans
						// (before 4.3 in particular when we didn't even look for collection beans).
						return null;
					}
				}
				// 获取类 通过名字
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				// We have exactly one match.
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}
			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;
		}
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

主要看这一句:Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);

@Nullable
	private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {
		Class<?> type = descriptor.getDependencyType();
		if (descriptor instanceof StreamDependencyDescriptor) {
			// findAutowireCandidates 根据类型查询
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			if (autowiredBeanNames != null) {
				autowiredBeanNames.addAll(matchingBeans.keySet());
			}
			Stream<Object> stream = matchingBeans.keySet().stream()
					.map(name -> descriptor.resolveCandidate(name, type, this))
					.filter(bean -> !(bean instanceof NullBean));
			if (((StreamDependencyDescriptor) descriptor).isOrdered()) {
				stream = stream.sorted(adaptOrderComparator(matchingBeans));
			}
			return stream;
		}
		else if (type.isArray()) {
			Class<?> componentType = type.getComponentType();
			ResolvableType resolvableType = descriptor.getResolvableType();
			Class<?> resolvedArrayType = resolvableType.resolve(type);
			if (resolvedArrayType != type) {
				componentType = resolvableType.getComponentType().resolve();
			}
			if (componentType == null) {
				return null;
			}
			// findAutowireCandidates 根据类型查询
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,
					new MultiElementDescriptor(descriptor));
			if (matchingBeans.isEmpty()) {
				return null;
			}
			if (autowiredBeanNames != null) {
				autowiredBeanNames.addAll(matchingBeans.keySet());
			}
			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
			Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);
			if (result instanceof Object[]) {
				Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
				if (comparator != null) {
					Arrays.sort((Object[]) result, comparator);
				}
			}
			return result;
		}
		else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
			Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
			if (elementType == null) {
				return null;
			}
			// findAutowireCandidates 根据类型查询
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
					new MultiElementDescriptor(descriptor));
			if (matchingBeans.isEmpty()) {
				return null;
			}
			if (autowiredBeanNames != null) {
				autowiredBeanNames.addAll(matchingBeans.keySet());
			}
			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
			Object result = converter.convertIfNecessary(matchingBeans.values(), type);
			if (result instanceof List) {
				if (((List<?>) result).size() > 1) {
					Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
					if (comparator != null) {
						((List<?>) result).sort(comparator);
					}
				}
			}
			return result;
		}
		else if (Map.class == type) {
			ResolvableType mapType = descriptor.getResolvableType().asMap();
			Class<?> keyType = mapType.resolveGeneric(0);
			if (String.class != keyType) {
				return null;
			}
			Class<?> valueType = mapType.resolveGeneric(1);
			if (valueType == null) {
				return null;
			}
			// findAutowireCandidates 根据类型查询
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
					new MultiElementDescriptor(descriptor));
			if (matchingBeans.isEmpty()) {
				return null;
			}
			if (autowiredBeanNames != null) {
				autowiredBeanNames.addAll(matchingBeans.keySet());
			}
			return matchingBeans;
		}
		else {
			return null;
		}
	}

主要看这一段:

		else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
			Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
			if (elementType == null) {
				return null;
			}
			// findAutowireCandidates 根据类型查询
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,
					new MultiElementDescriptor(descriptor));
			if (matchingBeans.isEmpty()) {
				return null;
			}
			if (autowiredBeanNames != null) {
				autowiredBeanNames.addAll(matchingBeans.keySet());
			}
			TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
			Object result = converter.convertIfNecessary(matchingBeans.values(), type);
			if (result instanceof List) {
				if (((List<?>) result).size() > 1) {
				    // 调用比较器
					Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);
					if (comparator != null) {
						((List<?>) result).sort(comparator);
					}
				}
			}
			return result;
		}
	@Nullable
	private Comparator<Object> adaptDependencyComparator(Map<String, ?> matchingBeans) {
		// 获取比较器
		Comparator<Object> comparator = getDependencyComparator();
		if (comparator instanceof OrderComparator) {
			return ((OrderComparator) comparator).withSourceProvider(
					createFactoryAwareOrderSourceProvider(matchingBeans));
		}
		else {
			return comparator;
		}
	}
	public Comparator<Object> withSourceProvider(OrderSourceProvider sourceProvider) {
		return (o1, o2) -> doCompare(o1, o2, sourceProvider);
	}
	private int doCompare(@Nullable Object o1, @Nullable Object o2, @Nullable OrderSourceProvider sourceProvider) {
		// 是否实现了PriorityOrdered接口
		boolean p1 = (o1 instanceof PriorityOrdered);
		boolean p2 = (o2 instanceof PriorityOrdered);
		if (p1 && !p2) {
			return -1;
		}
		else if (p2 && !p1) {
			return 1;
		}
		int i1 = getOrder(o1, sourceProvider);
		int i2 = getOrder(o2, sourceProvider);
		return Integer.compare(i1, i2);
	}

接下来比较重要,上面注释了是否实现了PriorityOrdered接口,如果没有则调用getOrder方法,getOrder方法判断是否实现了Order接口,如果没有获取@Order注解值。

	private int getOrder(@Nullable Object obj, @Nullable OrderSourceProvider sourceProvider) {
		Integer order = null;
		if (obj != null && sourceProvider != null) {
			// 拿到实现了Order接口的类
			Object orderSource = sourceProvider.getOrderSource(obj);
			if (orderSource != null) {
				if (orderSource.getClass().isArray()) {
					for (Object source : ObjectUtils.toObjectArray(orderSource)) {
						order = findOrder(source);
						if (order != null) {
							break;
						}
					}
				}
				else {
					order = findOrder(orderSource);
				}
			}
		}
		return (order != null ? order : getOrder(obj));
	}
   protected int getOrder(@Nullable Object obj) {
		if (obj != null) {
			Integer order = findOrder(obj);
			if (order != null) {
				return order;
			}
		}
		return Ordered.LOWEST_PRECEDENCE;
	}
	@Override
	@Nullable
	protected Integer findOrder(Object obj) {
		Integer order = super.findOrder(obj);
		if (order != null) {
			return order;
		}
		return findOrderFromAnnotation(obj);
	}
	@Nullable
	private Integer findOrderFromAnnotation(Object obj) {
		AnnotatedElement element = (obj instanceof AnnotatedElement ? (AnnotatedElement) obj : obj.getClass());
		MergedAnnotations annotations = MergedAnnotations.from(element, SearchStrategy.TYPE_HIERARCHY);
		Integer order = OrderUtils.getOrderFromAnnotations(element, annotations);
		if (order == null && obj instanceof DecoratingProxy) {
			return findOrderFromAnnotation(((DecoratingProxy) obj).getDecoratedClass());
		}
		return order;
	}

总结下来就是:当我们通过构造函数或者set方法注入进某个List<类>时,Spring的DefaultListableBeanFactory类会在注入时获取AnnotationAwareOrderComparator比较器帮助我们对类进行排序,AnnotationAwareOrderComparator是OrderComparator的子类,而OrderComparator实现了比较器Comparator接口。排序的策略会先判断是否实现PriorityOrdered接口,如果没有接着会判断是否实现Order接口,此时也没有就会根据注解值进行比较。

到此这篇关于Spring @Order注解使用详解的文章就介绍到这了,更多相关Spring @Order 内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 基于SpringBoot开机启动与@Order注解

    目录 SpringBoot开机启动与@Order注解 spring @Order标记 @Order标记定义了组件的加载顺序 使用spring 3.x 和spring 4.x 的例子 SpringBoot开机启动与@Order注解 package com.example.zcw.runner; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.ApplicationArguments; import org.spri

  • Spring @Order注解使用详解

    目录 前言 例子一 例子二 例子三 实例论证 源码分析 前言 很长一段时间没有写博客了,今天一时心血来潮,突然写出这篇文章就很突兀.但是看到网上关于Spring的@Order注解的不是特别准确的结论,想着还是表达一下看法,当然也是通过写文章来让自己的思路更清晰一点,如果有不是很赞同的部分,希望可以一起讨论. 首先先说结论:Spring的@Order注解(或者实现Ordered接口.或者实现PriorityOrdered接口),不决定Bean的实例化顺序和执行顺序,更不会影响Spring的Bean

  • Shiro集成Spring之注解示例详解

    前言 Shiro的组件都是JavaBean/POJO式的组件,所以非常容易使用Spring进行组件管理,可以非常方便的从ini配置迁移到Spring进行管理,且支持JavaSE应用及Web应用的集成. 集成 Spring 后我们通过过滤器链来配置每个 URL 需要的权限,但当配置多了以后就会不方便,而且只支持 URL 级别的配置. 好在 Shiro 提供了相应的注解用于权限控制,此处使用了 Spring MVC 来测试Shiro注解,当然 Shiro 注解不仅仅可以在 web 环境使用,在独立的

  • JSP 中Spring组合注解与元注解实例详解

    JSP 中Spring组合注解与元注解实例详解 摘要: 注解(Annotation),也叫元数据.一种代码级别的说明.它与类.接口.枚举是在同一个层次.它可以声明在包.类.字段.方法.局部变量.方法参数等的前面,用来对这些元素进行说明 1. 可以注解到别的注解上的注解称为元注解,被注解的注解称为组合注解,通过组合注解可以很好的简化好多重复性的注解操作 2. 示例组合注解 import org.springframework.context.annotation.ComponentScan; im

  • Spring自动装配与扫描注解代码详解

    1 javabean的自动装配 自动注入,减少xml文件的配置信息. <?xml version="1.0" encoding="UTF-8"?> <!-- 到入xml文件的约束 --> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p&quo

  • Java @Async注解导致spring启动失败解决方案详解

    前言 在这篇文章里,最后总结处,我说了会讲讲循环依赖中,其中一个类添加@Async有可能会导致注入失败而抛异常的情况,今天就分析一下. 一.异常表现,抛出内容 1.1循环依赖的两个class 1.CycleService1 @Service public class CycleService1 { @Autowired private CycleService2 cycleService2; @WangAnno @Async public void doThings() { System.out

  • Spring入门到精通之注解开发详解

    目录 Spring原始注解 DI 依赖注入的注解实现方式 Spring新注解 @Configuration @ComponentScan @Import @PropertySource @value @Bean Spring整合Junit 原始Junit测试Spring的问题 上述问题解决思路 Spring集成Junit步骤 Spring集成Junit代码实现 Spring原始注解 Spring是轻代码而重配置的框架,配置比较繁重,影响开发效率,所以注解开发是一种趋势,注解代替xml配置文件可以

  • 8个Spring事务失效场景详解

    目录 前言 Spring事务原理 Spring事务失效场景 1. 抛出检查异常 2. 业务方法本身捕获了异常 3. 同一类中的方法调用 4. 方法使用 final 或 static关键字 5. 方法不是public 6. 错误使用传播机制 7. 没有被Spring管理 8. 多线程 总结 前言 作为Java开发工程师,相信大家对Spring种事务的使用并不陌生.但是你可能只是停留在基础的使用层面上,在遇到一些比较特殊的场景,事务可能没有生效,直接在生产上暴露了,这可能就会导致比较严重的生产事故.

  • Spring Java-based容器配置详解

    装Java-based的配置 使用 @Import 注解 跟在Spring XML文件中使用<import>元素添加模块化的配置类似,@Import注解允许你加载其他配置类中的@Bean定义: @Configuration public class ConfigA { @Bean public A a() { return new A(); } } @Configuration @Import(ConfigA.class) public class ConfigB { @Bean public

  • 简单实现Spring的IOC原理详解

    控制反转(InversionofControl,缩写为IoC) 简单来说就是当自己需要一个对象的时候不需要自己手动去new一个,而是由其他容器来帮你提供:Spring里面就是IOC容器. 例如: 在Spring里面经常需要在Service这个装配一个Dao,一般是使用@Autowired注解:类似如下 public Class ServiceImpl{ @Autowired Dao dao; public void getData(){ dao.getData(); } 在这里未初始化Dao直接

  • Spring @Transactional工作原理详解

    本文将深入研究Spring的事务管理.主要介绍@Transactional在底层是如何工作的.之后的文章将介绍: propagation(事务传播)和isolation(隔离性)等属性的使用 事务使用的陷阱有哪些以及如何避免 JPA和事务管理 很重要的一点是JPA本身并不提供任何类型的声明式事务管理.如果在依赖注入容器之外使用JPA,事务处理必须由开发人员编程实现. UserTransaction utx = entityManager.getTransaction(); try{ utx.be

随机推荐