В документации Spring говорится, что:
CGLIB-прокси перехватывает только публичные вызовы методов!Не вызывайте непубличные методы на таком прокси.Они не делегируются фактическому целевому объекту с областью действия.
https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-factory-scopes-other
Но после наблюдения я думаю, что либо мой эксперимент (код ниже) не подходит, либо это поведение изменилосьсо временем.
Я заметил, что обходятся только финальные или частные методы.
Вот эксперимент: (весенняя версия: 5.1.3
)
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private StudentService studentService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Bean
StudentService studentService() {
ProxyFactory proxyFactory = new ProxyFactory(new StudentService());
proxyFactory.setProxyTargetClass(true);
proxyFactory.addAdvice(new MethodInterceptor() {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("method " + invocation.getMethod() + " is called on " +
invocation.getThis() + " with args " + invocation.getArguments());
Object ret = invocation.proceed();
System.out.println("method " + invocation.getMethod() + " returns " + ret);
return ret;
}
});
return (StudentService) proxyFactory.getProxy();
}
@Override
public void run(String... args) throws Exception {
studentService.doIt();
}
class StudentService {
void doIt() {
System.out.println("doIt");
}
}
Вывод:
method void com.example.demo.DemoApplication$StudentService.doIt() is called on com.example.demo.DemoApplication$StudentService@127a7a2e with args [Ljava.lang.Object;@14008db3
doIt
method void com.example.demo.DemoApplication$StudentService.doIt() returns null
Также - экспериментально - CGLIB библиотека (без пружины, используя класс Enhancer) также позволяет использовать методы уровня пакета для проксирования.
ОБНОВЛЕНИЕ
У меня есть еще одно замечание (противоположное вышеприведенному).В типичном приложении jdbc:
@SpringBootApplication
public class DemoApplication implements CommandLineRunner {
@Autowired
private ExampleService exampleService;
@Autowired
private XExampleService xExampleService;
@Autowired
private XXExampleService xxExampleService;
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
System.out.println(exampleService.getClass());
System.out.println(xExampleService.getClass());
System.out.println(xxExampleService.getClass());
}
@Service
class ExampleService {
void tranx() {
}
}
@Service
class XExampleService {
@org.springframework.transaction.annotation.Transactional
void tranx() {
}
}
@Service
class XXExampleService {
@Transactional
public void tranx() {
}
}
}
Вывод:
class com.example.demo.DemoApplication$ExampleService
class com.example.demo.DemoApplication$XExampleService
class com.example.demo.DemoApplication$XXExampleService$$EnhancerBySpringCGLIB$$2b1603e8
Что означает, что в случае пружинного создания прокси в моем поведении - как в TransactionInterceptor -Прокси-сервер CGLIB создан только для общедоступного метода.
UPDATE2
Мне кажется, я обнаружил, что происходит только такое поведение метода accept public.Это происходит в AnnotationTransactionAttributeSource
, который используется PointCut (TransactionAttributeSourcePointcut
).
Из кода:
/**
* Create a custom AnnotationTransactionAttributeSource, supporting
* public methods that carry the {@code Transactional} annotation
* or the EJB3 {@link javax.ejb.TransactionAttribute} annotation.
* @param publicMethodsOnly whether to support public methods that carry
* the {@code Transactional} annotation only (typically for use
* with proxy-based AOP), or protected/private methods as well
* (typically used with AspectJ class weaving)
*/
public AnnotationTransactionAttributeSource(boolean publicMethodsOnly) {