Spring AOP не работает с классом, содержащим метод @Transactional
Я разрабатываю веб-приложение, в котором нужно хранить тяжелые файлы и для этой цели использую FTP-сервер Apache. Когда новый пользователь регистрирует свою учетную запись, папка, названная как его имя пользователя, должна быть создана на удаленном сервере. Чтобы установить соединение, перед выполнением метода UserCreatingServiceImpl.createUser() я использую Spring AOP:
@Component
@Aspect
public class RemoteServerConnectionEstablisher {
private static boolean connectionEstablished = false;
@Autowired
private RemoteServerConnector serverConnector;
@Pointcut("execution(* com.storehouse.business.services.impl.UserCreatingServiceImpl.createUser(..)) ||"
+ " execution (* com.storehouse.business.services.impl.ItemCreatingServiceImpl.createItem(..)) ||"
+ "execution (* com.storehouse.business.services.impl.FileDownloadingServiceImpl.downloadFile(..))")
public void pointcut() {
}
@Before("pointcut()")
public void establishConnection(JoinPoint jp) {
if (!connectionEstablished) {
if (serverConnector.connectToRemoteServer()) {
connectionEstablished = true;
}
}
}
@After("pointcut()")
public void disconnect(JoinPoint jp) {
if (connectionEstablished) {
if (serverConnector.disconnect()) {
connectionEstablished = false;
}
}
}
}
Вот класс обслуживания с методом createUser ():
@Service
public class UserCreatingServiceImpl implements UserCreatingService {
@Autowired
private UserService userService;
@Autowired
private FTPClient ftpClient;
@Override
public boolean createUser(UserDto userDto) {
try {
ftpClient.makeDirectory(userDto.getUsername());
UserMapper userMapper = new UserMapper();
userService.persistUser(userMapper.dtoToEntity(userDto));
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
@Transactional
public void checkIfUsernameExist(String username) {
}
}
Все работало нормально, пока я не добавил метод @Transactional в service -class:
@Transactional
public void checkIfUsernameExist(String username) {
}
Теперь методы Aspect-класса не вызывают. Не могли бы вы объяснить причину. Заранее спасибо за помощь.
1 ответ
Проблема заключается в вашем выражении pointcut.
execution(* com.storehouse.business.services.impl.UserCreatingServiceImpl.createUser(..))
Вы перехватываете исполнение createUser
метод на UserCreatingServiceImpl
, Это работает, когда вы не добавляете что-то, что создает прокси для вашей реализации. Как вы будете напрямую вызывать этот метод.
Однако, как вы добавили @Transactional
прокси создан, и теперь вызов метода выполняется на UserCreatingService
поскольку это интерфейс, который оставлен из-за созданного прокси. По умолчанию Spring использует JDK Dynamic прокси, которые основаны на интерфейсе.
Чтобы решить, сделать одну из этих вещей
- Перепишите свой pointcut для работы с интерфейсом вместо реализации класса
- Используйте основанные на классе вместо основанных на интерфейсе прокси
- Использовать компиляцию или ткачество времени загрузки
Переписать pointcut
использование execution(* com.storehouse.business.services.UserCreatingService+.createUser(..))
вместо того, что у вас есть сейчас. Это будет использовать интерфейс вместо конкретного класса.
Используйте прокси на основе классов
Предполагая, что вы используете @EnableAspectJAutoProxy
добавлять proxyTargetClass=true
к этому, что приводит к @EnableAspectJAutoProxy(proxyTargetClass=true)
, Это создаст прокси на основе классов и должно заставить работать оригинальный pointcut.
Использовать компиляцию или ткачество времени загрузки
Вместо использования прокси вы также можете изменить способ сборки / загрузки вашего кода. Для ткачества во время компиляции вам придется изменить свою сборку, чтобы использовать компилятор AspectJ для применения аспектов во время компиляции, тогда вам больше не нужны прокси.
Или вместо @EnableAspectJAutoProxy
Вы могли бы добавить @EnableLoadTimeWeaving
который, если вы используете последний контейнер сервлета, будет переплетать аспекты, как только будет загружен класс.
И то, и другое устранит необходимость в прокси-серверах (по крайней мере, для этой части) и позволит работать оригинальным pointcut.