提交 da9ee06e 编写于 作者: S Sam Brannen

Improve @Transactional docs regarding method visibility

Closes gh-27003
上级 4c28266e
/*
* Copyright 2002-2020 the original author or authors.
* Copyright 2002-2021 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
......@@ -29,10 +29,13 @@ import org.springframework.transaction.TransactionDefinition;
/**
* Describes a transaction attribute on an individual method or on a class.
*
* <p>At the class level, this annotation applies as a default to all methods of
* the declaring class and its subclasses. Note that it does not apply to ancestor
* classes up the class hierarchy; methods need to be locally redeclared in order
* to participate in a subclass-level annotation.
* <p>When this annotation is declared at the class level, it applies as a default
* to all methods of the declaring class and its subclasses. Note that it does not
* apply to ancestor classes up the class hierarchy; inherited methods need to be
* locally redeclared in order to participate in a subclass-level annotation. For
* details on method visibility constraints, consult the
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction">Transaction Management</a>
* section of the reference manual.
*
* <p>This annotation type is generally directly comparable to Spring's
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
......@@ -46,14 +49,14 @@ import org.springframework.transaction.TransactionDefinition;
* consult the {@link org.springframework.transaction.TransactionDefinition} and
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
*
* <p>This annotation commonly works with thread-bound transactions managed by
* <p>This annotation commonly works with thread-bound transactions managed by a
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
* transaction to all data access operations within the current execution thread.
* <b>Note: This does NOT propagate to newly started threads within the method.</b>
*
* <p>Alternatively, this annotation may demarcate a reactive transaction managed
* by {@link org.springframework.transaction.ReactiveTransactionManager} which
* uses the Reactor context instead of thread-local attributes. As a consequence,
* by a {@link org.springframework.transaction.ReactiveTransactionManager} which
* uses the Reactor context instead of thread-local variables. As a consequence,
* all participating data access operations need to execute within the same
* Reactor context in the same reactive pipeline.
*
......@@ -94,12 +97,12 @@ public @interface Transactional {
String transactionManager() default "";
/**
* Defines zero (0) or more transaction labels. Labels may be used to
* describe a transaction and they can be evaluated by individual transaction
* manager. Labels may serve a solely descriptive purpose or map to
* pre-defined transaction manager-specific options.
* <p>See the description of the actual transaction manager implementation
* how it evaluates transaction labels.
* Defines zero (0) or more transaction labels.
* <p>Labels may be used to describe a transaction, and they can be evaluated
* by individual transaction managers. Labels may serve a solely descriptive
* purpose or map to pre-defined transaction manager-specific options.
* <p>See the documentation of the actual transaction manager implementation
* for details on how it evaluates transaction labels.
* @since 5.3
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#getLabels()
*/
......
......@@ -837,9 +837,9 @@ that test drives the configuration shown earlier:
public final class Boot {
public static void main(final String[] args) throws Exception {
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
FooService fooService = (FooService) ctx.getBean("fooService");
fooService.insertFoo (new Foo());
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
FooService fooService = ctx.getBean(FooService.class);
fooService.insertFoo(new Foo());
}
}
----
......@@ -1314,18 +1314,22 @@ Consider the following class definition:
@Transactional
public class DefaultFooService implements FooService {
@Override
public Foo getFoo(String fooName) {
// ...
}
@Override
public Foo getFoo(String fooName, String barName) {
// ...
}
@Override
public void insertFoo(Foo foo) {
// ...
}
@Override
public void updateFoo(Foo foo) {
// ...
}
......@@ -1356,11 +1360,13 @@ Consider the following class definition:
}
----
Used at the class level as above, the annotation indicates a default for all public methods
of the declaring class (as well as its subclasses). Alternatively, each method can
get annotated individually. Note that a class-level annotation does not apply to
ancestor classes up the class hierarchy; in such a scenario, methods need to be
locally redeclared in order to participate in a subclass-level annotation.
Used at the class level as above, the annotation indicates a default for all methods of
the declaring class (as well as its subclasses). Alternatively, each method can be
annotated individually. See <<transaction-declarative-annotations-method-visibility>> for
further details on which methods Spring considers transactional. Note that a class-level
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
inherited methods need to be locally redeclared in order to participate in a
subclass-level annotation.
When a POJO class such as the one above is defined as a bean in a Spring context,
you can make the bean instance transactional through an `@EnableTransactionManagement`
......@@ -1390,7 +1396,8 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
<bean id="fooService" class="x.y.service.DefaultFooService"/>
<!-- enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required --> <1>
<!-- a TransactionManager is still required -->
<tx:annotation-driven transaction-manager="txManager"/> <1>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- (this dependency is defined somewhere else) -->
......@@ -1405,7 +1412,7 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
tag if the bean name of the `TransactionManager` that you want to wire in has the name,
tag if the bean name of the `TransactionManager` that you want to wire in has the name
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
has any other name, you have to use the `transaction-manager` attribute, as in the
preceding example.
......@@ -1420,18 +1427,22 @@ programming arrangements as the following listing shows:
@Transactional
public class DefaultFooService implements FooService {
@Override
public Publisher<Foo> getFoo(String fooName) {
// ...
}
@Override
public Mono<Foo> getFoo(String fooName, String barName) {
// ...
}
@Override
public Mono<Void> insertFoo(Foo foo) {
// ...
}
@Override
public Mono<Void> updateFoo(Foo foo) {
// ...
}
......@@ -1467,17 +1478,47 @@ Reactive Streams cancellation signals. See the <<tx-prog-operator-cancel>> secti
"Using the TransactionOperator" for more details.
[[transaction-declarative-annotations-method-visibility]]
.Method visibility and `@Transactional`
****
When you use proxies, you should apply the `@Transactional` annotation only to methods
with public visibility. If you do annotate protected, private or package-visible
methods with the `@Transactional` annotation, no error is raised, but the annotated
method does not exhibit the configured transactional settings. If you need to annotate
non-public methods, consider using AspectJ (described later).
****
[NOTE]
====
When you use transactional proxies with Spring's standard configuration, you should apply
the `@Transactional` annotation only to methods with `public` visibility. If you do
annotate `protected`, `private`, or package-visible methods with the `@Transactional`
annotation, no error is raised, but the annotated method does not exhibit the configured
transactional settings. If you need to annotate non-public methods, consider the tip in
the following paragraph for class-based proxies or consider using AspectJ compile-time or
load-time weaving (described later).
When using `@EnableTransactionManagement` in a `@Configuration` class, `protected` or
package-visible methods can also be made transactional for class-based proxies by
registering a custom `transactionAttributeSource` bean like in the following example.
Note, however, that transactional methods in interface-based proxies must always be
`public` and defined in the proxied interface.
[source,java,indent=0,subs="verbatim,quotes"]
----
/**
* Register a custom AnnotationTransactionAttributeSource with the
* publicMethodsOnly flag set to false to enable support for
* protected and package-private @Transactional methods in
* class-based proxies.
*
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
*/
@Bean
TransactionAttributeSource transactionAttributeSource() {
return new AnnotationTransactionAttributeSource(false);
}
----
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods by
default. See <<testing.adoc#testcontext-tx,Transaction Management>> in the testing
chapter for examples.
====
You can apply the `@Transactional` annotation to an interface definition, a method
on an interface, a class definition, or a public method on a class. However, the
on an interface, a class definition, or a method on a class. However, the
mere presence of the `@Transactional` annotation is not enough to activate the
transactional behavior. The `@Transactional` annotation is merely metadata that can
be consumed by some runtime infrastructure that is `@Transactional`-aware and that
......@@ -1499,12 +1540,13 @@ the proxy are intercepted. This means that self-invocation (in effect, a method
the target object calling another method of the target object) does not lead to an actual
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
the proxy must be fully initialized to provide the expected behavior, so you should not
rely on this feature in your initialization code (that is, `@PostConstruct`).
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
method.
Consider using of AspectJ mode (see the `mode` attribute in the following table) if you
expect self-invocations to be wrapped with transactions as well. In this case, there no
proxy in the first place. Instead, the target class is woven (that is, its byte code is
modified) to turn `@Transactional` into runtime behavior on any kind of method.
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
expect self-invocations to be wrapped with transactions as well. In this case, there is
no proxy in the first place. Instead, the target class is woven (that is, its byte code
is modified) to support `@Transactional` runtime behavior on any kind of method.
[[tx-annotation-driven-settings]]
.Annotation driven transaction settings
......@@ -1557,14 +1599,14 @@ NOTE: The `proxy-target-class` attribute controls what type of transactional pro
created for classes annotated with the `@Transactional` annotation. If
`proxy-target-class` is set to `true`, class-based proxies are created. If
`proxy-target-class` is `false` or if the attribute is omitted, standard JDK
interface-based proxies are created. (See <<core.adoc#aop-proxying>> for a discussion of the
different proxy types.)
interface-based proxies are created. (See <<core.adoc#aop-proxying, Proxying Mechanisms>>
for a discussion of the different proxy types.)
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` looks for
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` look for
`@Transactional` only on beans in the same application context in which they are defined.
This means that, if you put annotation-driven configuration in a `WebApplicationContext`
for a `DispatcherServlet`, it checks for `@Transactional` beans only in your controllers
and not your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
and not in your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
The most derived location takes precedence when evaluating the transactional settings
for a method. In the case of the following example, the `DefaultFooService` class is
......@@ -1612,8 +1654,8 @@ precedence over the transactional settings defined at the class level.
===== `@Transactional` Settings
The `@Transactional` annotation is metadata that specifies that an interface, class,
or method must have transactional semantics (for example, "`start a brand new read-only
transaction when this method is invoked, suspending any existing transaction`").
or method must have transactional semantics (for example, "start a brand new read-only
transaction when this method is invoked, suspending any existing transaction").
The default `@Transactional` settings are as follows:
* The propagation setting is `PROPAGATION_REQUIRED.`
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册