Spring AOP 不适用于包含@Transactional 方法的class

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

我开发网络应用程序,需要存储重量级文件并为此使用 Apache FTP 服务器。当新用户注册他的帐户时,必须在远程服务器上创建以他的用户名命名的文件夹。要建立连接,在执行 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() 方法的服务 class:

@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 方法添加到服务中 -class:

@Transactional
public void checkIfUsernameExist(String username) {

}

现在 Aspect-class 的方法不会调用。能解释一下原因吗。在此先感谢您的帮助。

问题出在你的切入点表达式上。

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

您正在拦截 createUser 方法在 UserCreatingServiceImpl 上的执行。当您不添加为您的实施创建代理的内容时,这会起作用。因为您将直接调用此方法。

但是,当您添加 @Transactional 时,会创建一个代理,并且现在会在 UserCreatingService 上完成方法调用,因为这是创建的代理留下的接口。默认情况下 spring 使用 JDK 基于接口的动态代理。

要解决做这些事情之一

  1. 重写切入点以在界面上操作而不是实现 class
  2. 使用基于 class 而不是基于接口的代理
  3. 使用编译或加载时编织

重写切入点

使用 execution(* com.storehouse.business.services.UserCreatingService+.createUser(..)) 而不是您现在拥有的。这将使用接口而不是具体的 class.

使用基于 class 的代理

假设您使用 @EnableAspectJAutoProxy 添加 proxyTargetClass=true 到它,导致 @EnableAspectJAutoProxy(proxyTargetClass=true)。这将创建基于 class 的代理,并且应该使原始切入点起作用。

使用编译或加载时编织

除了使用代理之外,您还可以更改代码的方式 build/loaded。对于编译时编织,您必须修改您的构建以使用 AspectJ 编译器在编译时应用方面,然后您不再需要代理。

或者您可以添加 @EnableLoadTimeWeaving 而不是 @EnableAspectJAutoProxy,如果您使用最近的 servlet 容器,它会在加载 class 时立即编织方面。

两者都将消除对代理的需求(至少对于这部分)并且将使原始切入点起作用。