为什么添加超类会破坏我的Spring bean?

时间:2018-10-04 04:43:32

标签: java spring spring-boot abstract-class

我有一个正常运行的Spring Boot Web应用程序。我注意到两个@Repository bean有很多共同点,所以我使用抽象超类重构了它们,现在我的应用程序坏了。我已经仔细检查过,这是我在工作状态和非工作状态之间所做的唯一更改。谁能看到我做错了什么?

这是我的工作代码:

public class One { ... }
public class Two { ... }

@Repository
public class RepoOne {
    private final ISource<One> sourceOne;
    private ICache<One> cache;
    @Value("${com.example.lifetime.one}")
    private int lifetime;
    public RepoOne(ISource<One> sourceOne) {
       this.sourceOne = sourceOne;
    }
    @PostConstruct
    public void createCache() {
       Duration lifetime = Duration.ofMinutes(this.lifetime);
       this.cache = new Cache<>(lifetime, sourceOne);
    }
    public One get(String key) {
      return cache.get(key);
    }
}

@Repository
public class RepoTwo {
    private final ISource<Two> sourceTwo;
    private ICache<Two> cache;
    @Value("${com.example.lifetime.two}")
    private int lifetime;
    public RepoOne(ISource<Two> sourceTwo) {
        this.sourceTwo = sourceTwo;
    }
    @PostConstruct
    public void createCache() {
        Duration lifetime = Duration.ofMinutes(this.lifetime);
        this.cache = new Cache<>(lifetime, sourceTwo);
    }
    public Two get(String key) {
        return cache.get(key);
    }
}

@Service
public class RepoService {
    private final RepoOne repoOne;
    private final RepoTwo repoTwo;
    public RepoService(RepoOne repoOne, RepoTwo repoTwo) {
        this.repoOne = repoOne;
        this.repoTwo = repoTwo;
    }
    public void doSomething(String key) {
        One one = repoOne.get(key);
        ...
    }
}

这是我的重构代码,在其中我引入了抽象的通用超类。

abstract class AbstractRepo<T> {
    private final ISource<T> source;
    private ICache<T> cache;
    AbstractRepo (ISource<T> source) {
       this.source = source;
    }
    @PostConstruct
    private void createCache() {
       Duration lifetime = Duration.ofMinutes(lifetime());
       this.cache = new Cache<>(lifetime, source);
    }
    protected abstract int lifetime();
    public final T get(String key) {
        return cache.get(key);
    }
}

@Repository
public class RepoOne extends AbstractRepo<One> {
    @Value("${com.example.lifetime.one}")
    private int lifetime;
    public RepoOne(ISource<One> sourceOne) {
       super(source);
    }
    protected int lifetime() { return lifetime; }
}

@Repository
public class RepoTwo extends AbstractRepo<Two> {
    @Value("${com.example.lifetime.two}")
    private int lifetime;
    public RepoTwo(ISource<Two> sourceTwo) {
       super(source);
    }
    protected int lifetime() { return lifetime; }
}

使用重构的代码时,我在NullPointerException中得到了一个AbstractRepo::get()。我已经通过调试器确认cachenull(以及source)。但是,我还通过调试器确认创建了RepoOne和RepoTwo实例,并调用了它们的createCache()方法。好像每个实例都被创建了两个实例,而只有一个实例被初始化了。有什么想法吗?

1 个答案:

答案 0 :(得分:3)

不是您引入了父类,而是您将get方法变成了final方法。

@Repository注释的类将获得自动异常翻译。通过使用AOP添加了自动异常转换。在Spring中应用AOP的默认机制是使用代理,在这种情况下是基于类的代理。

发生的事情是CgLib通过子类化为您的类创建了一个代理,以便在调用方法时可以添加建议。但是,final方法不能在子类中覆盖。这将导致在代理而不是实际实例上调用get方法。

有2种解决方法

  1. 删除final关键字
  2. 引入一个定义存储库合同的接口。这将导致创建JDK动态代理。 JDK动态代理基于接口,不需要子类化您的实际类(仅适用于基于类的代理)。