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

时间:2016-02-08 13:45:01

标签: java spring spring-aop spring-aspects

我开发了web-app,需要存储重量级文件并为此目的使用Apache FTP Server。当新用户注册其帐户时,必须在远程服务器上创建名为其用户名的文件夹。要建立连接,在执行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-class的方法不会调用。你能解释一下原因吗?在此先感谢您的帮助。

1 个答案:

答案 0 :(得分:3)

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

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

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

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

要解决这些问题之一

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

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

    使用基于类的代理

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

    使用编译或加载时间编织

    您也可以更改代码的构建/加载方式,而不是使用代理。对于编译时编织,您必须修改构建以使用AspectJ编译器在编译时应用方面,然后您不再需要代理。

    或者代替@EnableAspectJAutoProxy,您可以添加@EnableLoadTimeWeaving,如果您使用最近的servlet容器,则会在加载类后立即编织方面。

    两者都不需要代理(至少对于这部分),并且会使原始切入点工作。