Skip to content

Commit da9ee06

Browse files
committed
Improve @transactional docs regarding method visibility
Closes gh-27003
1 parent 4c28266 commit da9ee06

File tree

2 files changed

+88
-43
lines changed

2 files changed

+88
-43
lines changed

spring-tx/src/main/java/org/springframework/transaction/annotation/Transactional.java

Lines changed: 17 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2020 the original author or authors.
2+
* Copyright 2002-2021 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -29,10 +29,13 @@
2929
/**
3030
* Describes a transaction attribute on an individual method or on a class.
3131
*
32-
* <p>At the class level, this annotation applies as a default to all methods of
33-
* the declaring class and its subclasses. Note that it does not apply to ancestor
34-
* classes up the class hierarchy; methods need to be locally redeclared in order
35-
* to participate in a subclass-level annotation.
32+
* <p>When this annotation is declared at the class level, it applies as a default
33+
* to all methods of the declaring class and its subclasses. Note that it does not
34+
* apply to ancestor classes up the class hierarchy; inherited methods need to be
35+
* locally redeclared in order to participate in a subclass-level annotation. For
36+
* details on method visibility constraints, consult the
37+
* <a href="https://docs.spring.io/spring-framework/docs/current/reference/html/data-access.html#transaction">Transaction Management</a>
38+
* section of the reference manual.
3639
*
3740
* <p>This annotation type is generally directly comparable to Spring's
3841
* {@link org.springframework.transaction.interceptor.RuleBasedTransactionAttribute}
@@ -46,14 +49,14 @@
4649
* consult the {@link org.springframework.transaction.TransactionDefinition} and
4750
* {@link org.springframework.transaction.interceptor.TransactionAttribute} javadocs.
4851
*
49-
* <p>This annotation commonly works with thread-bound transactions managed by
52+
* <p>This annotation commonly works with thread-bound transactions managed by a
5053
* {@link org.springframework.transaction.PlatformTransactionManager}, exposing a
5154
* transaction to all data access operations within the current execution thread.
5255
* <b>Note: This does NOT propagate to newly started threads within the method.</b>
5356
*
5457
* <p>Alternatively, this annotation may demarcate a reactive transaction managed
55-
* by {@link org.springframework.transaction.ReactiveTransactionManager} which
56-
* uses the Reactor context instead of thread-local attributes. As a consequence,
58+
* by a {@link org.springframework.transaction.ReactiveTransactionManager} which
59+
* uses the Reactor context instead of thread-local variables. As a consequence,
5760
* all participating data access operations need to execute within the same
5861
* Reactor context in the same reactive pipeline.
5962
*
@@ -94,12 +97,12 @@
9497
String transactionManager() default "";
9598

9699
/**
97-
* Defines zero (0) or more transaction labels. Labels may be used to
98-
* describe a transaction and they can be evaluated by individual transaction
99-
* manager. Labels may serve a solely descriptive purpose or map to
100-
* pre-defined transaction manager-specific options.
101-
* <p>See the description of the actual transaction manager implementation
102-
* how it evaluates transaction labels.
100+
* Defines zero (0) or more transaction labels.
101+
* <p>Labels may be used to describe a transaction, and they can be evaluated
102+
* by individual transaction managers. Labels may serve a solely descriptive
103+
* purpose or map to pre-defined transaction manager-specific options.
104+
* <p>See the documentation of the actual transaction manager implementation
105+
* for details on how it evaluates transaction labels.
103106
* @since 5.3
104107
* @see org.springframework.transaction.interceptor.DefaultTransactionAttribute#getLabels()
105108
*/

src/docs/asciidoc/data-access.adoc

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -837,9 +837,9 @@ that test drives the configuration shown earlier:
837837
public final class Boot {
838838
839839
public static void main(final String[] args) throws Exception {
840-
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
841-
FooService fooService = (FooService) ctx.getBean("fooService");
842-
fooService.insertFoo (new Foo());
840+
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
841+
FooService fooService = ctx.getBean(FooService.class);
842+
fooService.insertFoo(new Foo());
843843
}
844844
}
845845
----
@@ -1314,18 +1314,22 @@ Consider the following class definition:
13141314
@Transactional
13151315
public class DefaultFooService implements FooService {
13161316
1317+
@Override
13171318
public Foo getFoo(String fooName) {
13181319
// ...
13191320
}
13201321
1322+
@Override
13211323
public Foo getFoo(String fooName, String barName) {
13221324
// ...
13231325
}
13241326
1327+
@Override
13251328
public void insertFoo(Foo foo) {
13261329
// ...
13271330
}
13281331
1332+
@Override
13291333
public void updateFoo(Foo foo) {
13301334
// ...
13311335
}
@@ -1356,11 +1360,13 @@ Consider the following class definition:
13561360
}
13571361
----
13581362

1359-
Used at the class level as above, the annotation indicates a default for all public methods
1360-
of the declaring class (as well as its subclasses). Alternatively, each method can
1361-
get annotated individually. Note that a class-level annotation does not apply to
1362-
ancestor classes up the class hierarchy; in such a scenario, methods need to be
1363-
locally redeclared in order to participate in a subclass-level annotation.
1363+
Used at the class level as above, the annotation indicates a default for all methods of
1364+
the declaring class (as well as its subclasses). Alternatively, each method can be
1365+
annotated individually. See <<transaction-declarative-annotations-method-visibility>> for
1366+
further details on which methods Spring considers transactional. Note that a class-level
1367+
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
1368+
inherited methods need to be locally redeclared in order to participate in a
1369+
subclass-level annotation.
13641370

13651371
When a POJO class such as the one above is defined as a bean in a Spring context,
13661372
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
13901396
<bean id="fooService" class="x.y.service.DefaultFooService"/>
13911397
13921398
<!-- enable the configuration of transactional behavior based on annotations -->
1393-
<tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required --> <1>
1399+
<!-- a TransactionManager is still required -->
1400+
<tx:annotation-driven transaction-manager="txManager"/> <1>
13941401
13951402
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
13961403
<!-- (this dependency is defined somewhere else) -->
@@ -1405,7 +1412,7 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
14051412

14061413

14071414
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
1408-
tag if the bean name of the `TransactionManager` that you want to wire in has the name,
1415+
tag if the bean name of the `TransactionManager` that you want to wire in has the name
14091416
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
14101417
has any other name, you have to use the `transaction-manager` attribute, as in the
14111418
preceding example.
@@ -1420,18 +1427,22 @@ programming arrangements as the following listing shows:
14201427
@Transactional
14211428
public class DefaultFooService implements FooService {
14221429
1430+
@Override
14231431
public Publisher<Foo> getFoo(String fooName) {
14241432
// ...
14251433
}
14261434
1435+
@Override
14271436
public Mono<Foo> getFoo(String fooName, String barName) {
14281437
// ...
14291438
}
14301439
1440+
@Override
14311441
public Mono<Void> insertFoo(Foo foo) {
14321442
// ...
14331443
}
14341444
1445+
@Override
14351446
public Mono<Void> updateFoo(Foo foo) {
14361447
// ...
14371448
}
@@ -1467,17 +1478,47 @@ Reactive Streams cancellation signals. See the <<tx-prog-operator-cancel>> secti
14671478
"Using the TransactionOperator" for more details.
14681479

14691480

1481+
[[transaction-declarative-annotations-method-visibility]]
14701482
.Method visibility and `@Transactional`
1471-
****
1472-
When you use proxies, you should apply the `@Transactional` annotation only to methods
1473-
with public visibility. If you do annotate protected, private or package-visible
1474-
methods with the `@Transactional` annotation, no error is raised, but the annotated
1475-
method does not exhibit the configured transactional settings. If you need to annotate
1476-
non-public methods, consider using AspectJ (described later).
1477-
****
1483+
[NOTE]
1484+
====
1485+
When you use transactional proxies with Spring's standard configuration, you should apply
1486+
the `@Transactional` annotation only to methods with `public` visibility. If you do
1487+
annotate `protected`, `private`, or package-visible methods with the `@Transactional`
1488+
annotation, no error is raised, but the annotated method does not exhibit the configured
1489+
transactional settings. If you need to annotate non-public methods, consider the tip in
1490+
the following paragraph for class-based proxies or consider using AspectJ compile-time or
1491+
load-time weaving (described later).
1492+
1493+
When using `@EnableTransactionManagement` in a `@Configuration` class, `protected` or
1494+
package-visible methods can also be made transactional for class-based proxies by
1495+
registering a custom `transactionAttributeSource` bean like in the following example.
1496+
Note, however, that transactional methods in interface-based proxies must always be
1497+
`public` and defined in the proxied interface.
1498+
1499+
[source,java,indent=0,subs="verbatim,quotes"]
1500+
----
1501+
/**
1502+
* Register a custom AnnotationTransactionAttributeSource with the
1503+
* publicMethodsOnly flag set to false to enable support for
1504+
* protected and package-private @Transactional methods in
1505+
* class-based proxies.
1506+
*
1507+
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
1508+
*/
1509+
@Bean
1510+
TransactionAttributeSource transactionAttributeSource() {
1511+
return new AnnotationTransactionAttributeSource(false);
1512+
}
1513+
----
1514+
1515+
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods by
1516+
default. See <<testing.adoc#testcontext-tx,Transaction Management>> in the testing
1517+
chapter for examples.
1518+
====
14781519

14791520
You can apply the `@Transactional` annotation to an interface definition, a method
1480-
on an interface, a class definition, or a public method on a class. However, the
1521+
on an interface, a class definition, or a method on a class. However, the
14811522
mere presence of the `@Transactional` annotation is not enough to activate the
14821523
transactional behavior. The `@Transactional` annotation is merely metadata that can
14831524
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
14991540
the target object calling another method of the target object) does not lead to an actual
15001541
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
15011542
the proxy must be fully initialized to provide the expected behavior, so you should not
1502-
rely on this feature in your initialization code (that is, `@PostConstruct`).
1543+
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
1544+
method.
15031545

1504-
Consider using of AspectJ mode (see the `mode` attribute in the following table) if you
1505-
expect self-invocations to be wrapped with transactions as well. In this case, there no
1506-
proxy in the first place. Instead, the target class is woven (that is, its byte code is
1507-
modified) to turn `@Transactional` into runtime behavior on any kind of method.
1546+
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
1547+
expect self-invocations to be wrapped with transactions as well. In this case, there is
1548+
no proxy in the first place. Instead, the target class is woven (that is, its byte code
1549+
is modified) to support `@Transactional` runtime behavior on any kind of method.
15081550

15091551
[[tx-annotation-driven-settings]]
15101552
.Annotation driven transaction settings
@@ -1557,14 +1599,14 @@ NOTE: The `proxy-target-class` attribute controls what type of transactional pro
15571599
created for classes annotated with the `@Transactional` annotation. If
15581600
`proxy-target-class` is set to `true`, class-based proxies are created. If
15591601
`proxy-target-class` is `false` or if the attribute is omitted, standard JDK
1560-
interface-based proxies are created. (See <<core.adoc#aop-proxying>> for a discussion of the
1561-
different proxy types.)
1602+
interface-based proxies are created. (See <<core.adoc#aop-proxying, Proxying Mechanisms>>
1603+
for a discussion of the different proxy types.)
15621604

1563-
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` looks for
1605+
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` look for
15641606
`@Transactional` only on beans in the same application context in which they are defined.
15651607
This means that, if you put annotation-driven configuration in a `WebApplicationContext`
15661608
for a `DispatcherServlet`, it checks for `@Transactional` beans only in your controllers
1567-
and not your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
1609+
and not in your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
15681610

15691611
The most derived location takes precedence when evaluating the transactional settings
15701612
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.
16121654
===== `@Transactional` Settings
16131655

16141656
The `@Transactional` annotation is metadata that specifies that an interface, class,
1615-
or method must have transactional semantics (for example, "`start a brand new read-only
1616-
transaction when this method is invoked, suspending any existing transaction`").
1657+
or method must have transactional semantics (for example, "start a brand new read-only
1658+
transaction when this method is invoked, suspending any existing transaction").
16171659
The default `@Transactional` settings are as follows:
16181660

16191661
* The propagation setting is `PROPAGATION_REQUIRED.`

0 commit comments

Comments
 (0)