Skip to content

Commit 70e6606

Browse files
committed
Improve @transactional docs regarding method visibility
Closes gh-27003
1 parent 5b55cef commit 70e6606

File tree

2 files changed

+82
-37
lines changed

2 files changed

+82
-37
lines changed

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

Lines changed: 11 additions & 8 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
*

src/docs/asciidoc/data-access.adoc

Lines changed: 71 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -888,9 +888,9 @@ that test drives the configuration shown earlier:
888888
public final class Boot {
889889
890890
public static void main(final String[] args) throws Exception {
891-
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml", Boot.class);
892-
FooService fooService = (FooService) ctx.getBean("fooService");
893-
fooService.insertFoo (new Foo());
891+
ApplicationContext ctx = new ClassPathXmlApplicationContext("context.xml");
892+
FooService fooService = ctx.getBean(FooService.class);
893+
fooService.insertFoo(new Foo());
894894
}
895895
}
896896
----
@@ -1365,18 +1365,22 @@ Consider the following class definition:
13651365
@Transactional
13661366
public class DefaultFooService implements FooService {
13671367
1368+
@Override
13681369
public Foo getFoo(String fooName) {
13691370
// ...
13701371
}
13711372
1373+
@Override
13721374
public Foo getFoo(String fooName, String barName) {
13731375
// ...
13741376
}
13751377
1378+
@Override
13761379
public void insertFoo(Foo foo) {
13771380
// ...
13781381
}
13791382
1383+
@Override
13801384
public void updateFoo(Foo foo) {
13811385
// ...
13821386
}
@@ -1407,11 +1411,13 @@ Consider the following class definition:
14071411
}
14081412
----
14091413

1410-
Used at the class level as above, the annotation indicates a default for all public methods
1411-
of the declaring class (as well as its subclasses). Alternatively, each method can
1412-
get annotated individually. Note that a class-level annotation does not apply to
1413-
ancestor classes up the class hierarchy; in such a scenario, methods need to be
1414-
locally redeclared in order to participate in a subclass-level annotation.
1414+
Used at the class level as above, the annotation indicates a default for all methods of
1415+
the declaring class (as well as its subclasses). Alternatively, each method can be
1416+
annotated individually. See <<transaction-declarative-annotations-method-visibility>> for
1417+
further details on which methods Spring considers transactional. Note that a class-level
1418+
annotation does not apply to ancestor classes up the class hierarchy; in such a scenario,
1419+
inherited methods need to be locally redeclared in order to participate in a
1420+
subclass-level annotation.
14151421

14161422
When a POJO class such as the one above is defined as a bean in a Spring context,
14171423
you can make the bean instance transactional through an `@EnableTransactionManagement`
@@ -1441,7 +1447,8 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
14411447
<bean id="fooService" class="x.y.service.DefaultFooService"/>
14421448
14431449
<!-- enable the configuration of transactional behavior based on annotations -->
1444-
<tx:annotation-driven transaction-manager="txManager"/><!-- a TransactionManager is still required --> <1>
1450+
<!-- a TransactionManager is still required -->
1451+
<tx:annotation-driven transaction-manager="txManager"/> <1>
14451452
14461453
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
14471454
<!-- (this dependency is defined somewhere else) -->
@@ -1456,7 +1463,7 @@ In XML configuration, the `<tx:annotation-driven/>` tag provides similar conveni
14561463

14571464

14581465
TIP: You can omit the `transaction-manager` attribute in the `<tx:annotation-driven/>`
1459-
tag if the bean name of the `TransactionManager` that you want to wire in has the name,
1466+
tag if the bean name of the `TransactionManager` that you want to wire in has the name
14601467
`transactionManager`. If the `TransactionManager` bean that you want to dependency-inject
14611468
has any other name, you have to use the `transaction-manager` attribute, as in the
14621469
preceding example.
@@ -1471,18 +1478,22 @@ programming arrangements as the following listing shows:
14711478
@Transactional
14721479
public class DefaultFooService implements FooService {
14731480
1481+
@Override
14741482
public Publisher<Foo> getFoo(String fooName) {
14751483
// ...
14761484
}
14771485
1486+
@Override
14781487
public Mono<Foo> getFoo(String fooName, String barName) {
14791488
// ...
14801489
}
14811490
1491+
@Override
14821492
public Mono<Void> insertFoo(Foo foo) {
14831493
// ...
14841494
}
14851495
1496+
@Override
14861497
public Mono<Void> updateFoo(Foo foo) {
14871498
// ...
14881499
}
@@ -1518,17 +1529,47 @@ Reactive Streams cancellation signals. See the <<tx-prog-operator-cancel>> secti
15181529
"Using the TransactionOperator" for more details.
15191530

15201531

1532+
[[transaction-declarative-annotations-method-visibility]]
15211533
.Method visibility and `@Transactional`
1522-
****
1523-
When you use proxies, you should apply the `@Transactional` annotation only to methods
1524-
with public visibility. If you do annotate protected, private or package-visible
1525-
methods with the `@Transactional` annotation, no error is raised, but the annotated
1526-
method does not exhibit the configured transactional settings. If you need to annotate
1527-
non-public methods, consider using AspectJ (described later).
1528-
****
1534+
[NOTE]
1535+
====
1536+
When you use transactional proxies with Spring's standard configuration, you should apply
1537+
the `@Transactional` annotation only to methods with `public` visibility. If you do
1538+
annotate `protected`, `private`, or package-visible methods with the `@Transactional`
1539+
annotation, no error is raised, but the annotated method does not exhibit the configured
1540+
transactional settings. If you need to annotate non-public methods, consider the tip in
1541+
the following paragraph for class-based proxies or consider using AspectJ compile-time or
1542+
load-time weaving (described later).
1543+
1544+
When using `@EnableTransactionManagement` in a `@Configuration` class, `protected` or
1545+
package-visible methods can also be made transactional for class-based proxies by
1546+
registering a custom `transactionAttributeSource` bean like in the following example.
1547+
Note, however, that transactional methods in interface-based proxies must always be
1548+
`public` and defined in the proxied interface.
1549+
1550+
[source,java,indent=0,subs="verbatim,quotes"]
1551+
----
1552+
/**
1553+
* Register a custom AnnotationTransactionAttributeSource with the
1554+
* publicMethodsOnly flag set to false to enable support for
1555+
* protected and package-private @Transactional methods in
1556+
* class-based proxies.
1557+
*
1558+
* @see ProxyTransactionManagementConfiguration#transactionAttributeSource()
1559+
*/
1560+
@Bean
1561+
TransactionAttributeSource transactionAttributeSource() {
1562+
return new AnnotationTransactionAttributeSource(false);
1563+
}
1564+
----
1565+
1566+
The _Spring TestContext Framework_ supports non-private `@Transactional` test methods by
1567+
default. See <<testing.adoc#testcontext-tx,Transaction Management>> in the testing
1568+
chapter for examples.
1569+
====
15291570

15301571
You can apply the `@Transactional` annotation to an interface definition, a method
1531-
on an interface, a class definition, or a public method on a class. However, the
1572+
on an interface, a class definition, or a method on a class. However, the
15321573
mere presence of the `@Transactional` annotation is not enough to activate the
15331574
transactional behavior. The `@Transactional` annotation is merely metadata that can
15341575
be consumed by some runtime infrastructure that is `@Transactional`-aware and that
@@ -1550,12 +1591,13 @@ the proxy are intercepted. This means that self-invocation (in effect, a method
15501591
the target object calling another method of the target object) does not lead to an actual
15511592
transaction at runtime even if the invoked method is marked with `@Transactional`. Also,
15521593
the proxy must be fully initialized to provide the expected behavior, so you should not
1553-
rely on this feature in your initialization code (that is, `@PostConstruct`).
1594+
rely on this feature in your initialization code -- for example, in a `@PostConstruct`
1595+
method.
15541596

1555-
Consider using of AspectJ mode (see the `mode` attribute in the following table) if you
1556-
expect self-invocations to be wrapped with transactions as well. In this case, there no
1557-
proxy in the first place. Instead, the target class is woven (that is, its byte code is
1558-
modified) to turn `@Transactional` into runtime behavior on any kind of method.
1597+
Consider using AspectJ mode (see the `mode` attribute in the following table) if you
1598+
expect self-invocations to be wrapped with transactions as well. In this case, there is
1599+
no proxy in the first place. Instead, the target class is woven (that is, its byte code
1600+
is modified) to support `@Transactional` runtime behavior on any kind of method.
15591601

15601602
[[tx-annotation-driven-settings]]
15611603
.Annotation driven transaction settings
@@ -1608,14 +1650,14 @@ NOTE: The `proxy-target-class` attribute controls what type of transactional pro
16081650
created for classes annotated with the `@Transactional` annotation. If
16091651
`proxy-target-class` is set to `true`, class-based proxies are created. If
16101652
`proxy-target-class` is `false` or if the attribute is omitted, standard JDK
1611-
interface-based proxies are created. (See <<core.adoc#aop-proxying>> for a discussion of the
1612-
different proxy types.)
1653+
interface-based proxies are created. (See <<core.adoc#aop-proxying, Proxying Mechanisms>>
1654+
for a discussion of the different proxy types.)
16131655

1614-
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` looks for
1656+
NOTE: `@EnableTransactionManagement` and `<tx:annotation-driven/>` look for
16151657
`@Transactional` only on beans in the same application context in which they are defined.
16161658
This means that, if you put annotation-driven configuration in a `WebApplicationContext`
16171659
for a `DispatcherServlet`, it checks for `@Transactional` beans only in your controllers
1618-
and not your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
1660+
and not in your services. See <<web.adoc#mvc-servlet, MVC>> for more information.
16191661

16201662
The most derived location takes precedence when evaluating the transactional settings
16211663
for a method. In the case of the following example, the `DefaultFooService` class is
@@ -1663,8 +1705,8 @@ precedence over the transactional settings defined at the class level.
16631705
===== `@Transactional` Settings
16641706

16651707
The `@Transactional` annotation is metadata that specifies that an interface, class,
1666-
or method must have transactional semantics (for example, "`start a brand new read-only
1667-
transaction when this method is invoked, suspending any existing transaction`").
1708+
or method must have transactional semantics (for example, "start a brand new read-only
1709+
transaction when this method is invoked, suspending any existing transaction").
16681710
The default `@Transactional` settings are as follows:
16691711

16701712
* The propagation setting is `PROPAGATION_REQUIRED.`

0 commit comments

Comments
 (0)