【问题标题】:Spring AOP one method of a class before anotherSpring AOP 一个类的一个方法在另一个之前
【发布时间】:2018-10-12 05:31:38
【问题描述】:

我有一个使用 Spring Core、Spring MVC、Hibernate 的应用程序。 有很多 DAO 类在这样的每个方法中都会获得新的 Hibernate 会话

    @Autowired
private SessionFactory sessionFactory;

private Session session;

private void createSession() {
    session =  sessionFactory.openSession();
}


@Override
public List<User> listUsers() {
    createSession();
    List users;

    users = session.createQuery("from User").list();
    session.close();

    return users;
}

我想使用 AOP 在这些类方法之前执行 createSession 方法,但不知道该怎么做。

我所做的只是这方面

@Configuration
@Aspect
public class DaoSessionLifeCycle {

    @Before(value = "execution(* ua.com.alistratenko.dao.UserDaoImp.listUsers(..))")
    public void openSession(JoinPoint joinPoint){

        System.out.println("izi");
    }
}

【问题讨论】:

  • 将您的服务方法设为@Transactional。您的 Dao 类将使用来自服务的事务。另请阅读有关交易传播的信息。
  • @BogdanLukiyanchuk 因此,如果我使用此注释,我可以跳过创建/打开新会话。在这种情况下,我应该使用什么对象来处理 DB?我的意思是,如果我不需要创建会话,这意味着我不需要此类的实例,我还应该使用什么来处理 DB?
  • 切面无法工作的可能原因有很多:Spring配置、切面代码、应用程序代码、包名、组件扫描设置。请提供MCVE,而不仅仅是语无伦次的sn-ps。重现和分析您的问题比纯粹的猜测更容易帮助您。欢迎来到 SO!
  • @kriegaex 我没有说它不起作用。我说我不知道​​在使用 Aspects 的同一类的另一个方法之前调用一个类方法的正确方法。
  • 所以方面被触发并在您期望的时间和地点打印“izi”?而你只贴出正文的那个类实际上命名为ua.com.alistratenko.dao.UserDaoImp,因为我只能从切入点间接推断?

标签: java spring hibernate aop


【解决方案1】:

除了@Bogdan 说的,看看CrudRepositoryJpaRepository,它会为你节省很多时间。另外,在我看来,@Aspect 类用@Component 注释,而不是@Configuration

【讨论】:

  • 是的,Spring AOP 方面需要是@Component,而纯 AspectJ 方面不需要。
【解决方案2】:

由于您仍然没有提供MCVE 或至少一些更多(更现实)的示例代码,我只能推测并提供一个示意性的答案。

顺便说一句,我在这里使用的是 AspectJ 而不是 Spring AOP,但它应该在那里以相同的方式工作。

用于编译示例代码的虚拟类:

package ua.com.alistratenko.dao;

public class User {
  String name;

  public User(String name) {
    this.name = name;
  }

  @Override
  public String toString() {
    return "User [name=" + name + "]";
  }
}
package ua.com.alistratenko.dao;

import java.util.ArrayList;
import java.util.List;

public class QueryResult {
  public List<User> list() {
    List<User> users = new ArrayList<>();
    users.add(new User("jane"));
    users.add(new User("joe"));
    return users;
  }
}
package ua.com.alistratenko.dao;

public class Session {
  public QueryResult createQuery(String string) {
    return new QueryResult();
  }

  public void close() {}
}
package ua.com.alistratenko.dao;

public class SessionFactory {
  public Session openSession() {
    return new Session();
  }
}

应用类:

我改变了什么?

  • 在 Spring 中,您将使用 @Autowired 而不是自己创建会话工厂。我这样做是为了能够在没有 Spring 的情况下运行我的示例代码。
  • 为方面添加了一些设置器/获取器,以便能够访问会话工厂以及会话本身
  • 删除createSession()(代码迁移到方面,见下文)
  • listUsers() 中不再有杂乱的样板文件
  • 添加了用于演示目的的 main 方法
package ua.com.alistratenko.dao;

import java.util.List;

public class UserDaoImp {
  private SessionFactory sessionFactory = new SessionFactory();
  private Session session;

  public SessionFactory getSessionFactory() { return sessionFactory; }
  public Session getSession() { return session; }
  public void setSession(Session session) { this.session = session; }

  public List<User> listUsers() {
    return session.createQuery("from User").list();
  }

  public static void main(String[] args) {
    new UserDaoImp().listUsers();
  }
}

方面:

为了在拦截方法调用之后+之前做一些事情,请使用@Around

package de.scrum_master.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
//import org.springframework.stereotype.Component;

import ua.com.alistratenko.dao.UserDaoImp;

//@Component
@Aspect
public class DaoSessionLifeCycle {
  @Around("execution(public * listUsers(..)) && target(dao)")
  public Object openSession(ProceedingJoinPoint thisJoinPoint, UserDaoImp dao) throws Throwable {
    try {
      System.out.println("Creating session");
      dao.setSession(dao.getSessionFactory().openSession());
      System.out.println("Calling " + thisJoinPoint.getSignature());
      return thisJoinPoint.proceed();
    }
    finally {
      try {
        System.out.println("Closing session");
        dao.getSession().close();
      }
      catch (Exception e) {}
    }
  }
}

控制台日志:

Creating session
Calling List ua.com.alistratenko.dao.UserDaoImp.listUsers()
Closing session

现在有几个未解决的问题:

  • 您是否只想处理这一类或基类的所有子类或所有 DAO 通用的已实现接口?然后切入点看起来会有所不同。
  • 您是只想拦截方法listUsers() 还是还想拦截其他方法?由于您没有显示更多代码,因此除了您之外没有人知道。切入点也会因您的回答而有所不同。
  • 您真的想手动完成这一切,还是听从其他用户的建议并使用板载 Spring 工具来管理您的事务?我对Spring一无所知,所以我不能告诉你。我只是为您提供与方面相关的问题的答案。

【讨论】:

  • 更新:我添加了虚拟类以使示例代码在没有 Spring 容器的情况下使用纯 Java SE + AspectJ 进行编译
  • 谢谢,这是我一直在寻找的实现。
猜你喜欢
  • 1970-01-01
  • 2021-11-30
  • 2012-08-21
  • 1970-01-01
  • 1970-01-01
  • 2012-03-10
  • 2012-11-13
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多