Wolf Larsen February 2016

Spring AOP doesn`t work with class comprising @Transactional method

I develop web-app, with a need to store heavy-weight files and use Apache FTP Server for this purpose. When a new user register his account, the folder named as his username must be created on remote server. To establish connection, before UserCreatingServiceImpl.createUser() method will be performed, I use 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;
            }
        }
    }
}

Here is the service class with createUser() method:

@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.printStac        

Answers


M. Deinum February 2016

The issue lies in your pointcut expression.

execution(* com.storehouse.business.services.impl.UserCreatingServiceImpl.createUser(..))

You are intercepting the execution of the createUser method on the UserCreatingServiceImpl. This works when you don't add something that creates a proxy for your implementation. As you will be directly calling this method.

However as you have added @Transactional a proxy is created and the method call is now done on the UserCreatingService as that is the interface that is left due do the created proxy. By default spring uses JDK Dynamic proxies, which are interface based.

To solve do one of these things

  1. Rewrite your pointcut to operate on the interface instead of implementing class
  2. Use class based instead of interface based proxies
  3. Use compile or load time weaving

Rewrite pointcut

Use execution(* com.storehouse.business.services.UserCreatingService+.createUser(..)) instead of what you have now. This will use the interface instead of the concrete class.

Use class based proxies

Assuming you use @EnableAspectJAutoProxy add proxyTargetClass=true to it, leading to @EnableAspectJAutoProxy(proxyTargetClass=true). This will create class based proxies and should make the original pointcut work.

Use compile or load time weaving

Instead of using proxies you also could change the way your code is build/loaded. For compile time weaving you would have to modify your build to use the AspectJ compiler to apply the aspects at compilation time, then you don't need the proxies any more.

Or instead of @EnableAspectJAutoProxy you could add @EnableLoadTimeWeaving which, if you use a recent servlet container, would weave the aspects as soon as a class is being loade

Post Status

Asked in February 2016
Viewed 1,563 times
Voted 14
Answered 1 times

Search




Leave an answer