[SPRING] JPA EntityListener에 Spring 의존성 삽입하기
SPRINGJPA EntityListener에 Spring 의존성 삽입하기
JPA EntityListener에 Spring 종속성을 주입하려고합니다. 다음은 리스너 클래스입니다.
@Configurable(autowire = Autowire.BY_TYPE, dependencyCheck = true)
public class PliListener {
@Autowired
private EvenementPliRepository evenementPliRepository;
@PostPersist
void onPostPersist(Pli pli) {
EvenementPli ev = new EvenementPli();
ev.setPli(pli);
ev.setDateCreation(new Date());
ev.setType(TypeEvenement.creation);
ev.setMessage("Création d'un pli");
System.out.println("evenementPliRepository: " + evenementPliRepository);
evenementPliRepository.save(ev);
}
}
다음은 Entity 클래스입니다.
@RooJavaBean
@RooToString
@RooJpaActiveRecord
@EntityListeners(PliListener.class)
public class Pli implements Serializable{
...
그러나 내 의존성 (예 : evenementPliRepository)은 항상 null입니다.
아무도 도와 줄 수 있습니까?
해결법
-
==============================
1.상태없는 빈에 의존성을 주입하는 해킹은 의존성을 "정적"으로 정의하고, Spring이 의존성을 주입 (정적 종속성에 할당) 할 수 있도록 setter 메소드를 생성하는 것입니다.
상태없는 빈에 의존성을 주입하는 해킹은 의존성을 "정적"으로 정의하고, Spring이 의존성을 주입 (정적 종속성에 할당) 할 수 있도록 setter 메소드를 생성하는 것입니다.
종속성을 정적으로 선언하십시오.
static private EvenementPliRepository evenementPliRepository;
Spring이 그것을 삽입 할 수 있도록 메소드를 생성하십시오.
@Autowired public void init(EvenementPliRepository evenementPliRepository) { MyListenerClass.evenementPliRepository = evenementPliRepository; logger.info("Initializing with dependency ["+ evenementPliRepository +"]"); }
자세한 내용은 http://blog-en.lineofsightnet.com/2012/08/dependency-injection-on-stateless-beans.html을 참조하십시오.
-
==============================
2.이것은 실제로 오래된 질문이지만 대체 솔루션을 발견했습니다.
이것은 실제로 오래된 질문이지만 대체 솔루션을 발견했습니다.
public class MyEntityListener { @Autowired private ApplicationEventPublisher publisher; @PostPersist public void postPersist(MyEntity target) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); publisher.publishEvent(new OnCreatedEvent<>(this, target)); } @PostUpdate public void postUpdate(MyEntity target) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); publisher.publishEvent(new OnUpdatedEvent<>(this, target)); } @PostRemove public void postDelete(MyEntity target) { SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this); publisher.publishEvent(new OnDeletedEvent<>(this, target)); } }
아마도 가장 좋은 것은 아니지만 AOP + 직조가없는 정적 변수보다 낫습니다.
-
==============================
3.그리고이 솔루션은 어떻습니까?
그리고이 솔루션은 어떻습니까?
@MappedSuperclass @EntityListeners(AbstractEntityListener.class) public abstract class AbstractEntity { @Id @GeneratedValue(strategy = GenerationType.AUTO) @Column(name = "id") private Long id; @Column(name = "creation_date") private Date creationDate; @Column(name = "modification_date") private Date modificationDate; }
그런 다음 리스너 ...
@Component public class AbstractEntityListener { @Autowired private DateTimeService dateTimeService; @PreUpdate public void preUpdate(AbstractEntity abstractEntity) { AutowireHelper.autowire(this, this.dateTimeService); abstractEntity.setModificationDate(this.dateTimeService.getCurrentDate()); } @PrePersist public void prePersist(AbstractEntity abstractEntity) { AutowireHelper.autowire(this, this.dateTimeService); Date currentDate = this.dateTimeService.getCurrentDate(); abstractEntity.setCreationDate(currentDate); abstractEntity.setModificationDate(currentDate); } }
그리고 도우미는 ...
/** * Helper class which is able to autowire a specified class. It holds a static reference to the {@link org * .springframework.context.ApplicationContext}. */ public final class AutowireHelper implements ApplicationContextAware { private static final AutowireHelper INSTANCE = new AutowireHelper(); private static ApplicationContext applicationContext; private AutowireHelper() { } /** * Tries to autowire the specified instance of the class if one of the specified beans which need to be autowired * are null. * * @param classToAutowire the instance of the class which holds @Autowire annotations * @param beansToAutowireInClass the beans which have the @Autowire annotation in the specified {#classToAutowire} */ public static void autowire(Object classToAutowire, Object... beansToAutowireInClass) { for (Object bean : beansToAutowireInClass) { if (bean == null) { applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire); } } } @Override public void setApplicationContext(final ApplicationContext applicationContext) { AutowireHelper.applicationContext = applicationContext; } /** * @return the singleton instance. */ public static AutowireHelper getInstance() { return INSTANCE; } }
나를 위해 일합니다.
출처: http://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/
-
==============================
4.AOP를 사용하여 Entity Listener에 스프링 빈을 주입하는 길을 시작했습니다. 연구 1 일 반 동안 다른 일을 시도한 후에 다음과 같은 링크를 발견했습니다.
AOP를 사용하여 Entity Listener에 스프링 빈을 주입하는 길을 시작했습니다. 연구 1 일 반 동안 다른 일을 시도한 후에 다음과 같은 링크를 발견했습니다.
이 시점에서 나는 EclipseLink DescriptorEventAdapter를 재편성하여 우연히 발견했다. 이 정보를 사용하여 Descriptor Adapter를 확장 한 리스너 클래스를 만들었습니다.
public class EntityListener extends DescriptorEventAdapter { private String injectedValue; public void setInjectedValue(String value){ this.injectedValue = value; } @Override public void aboutToInsert(DescriptorEvent event) { // Do what you need here } }
클래스를 사용하기 위해 @EntityListeners 주석을 내 엔티티 클래스에 사용할 수있었습니다. 불행히도,이 방법은 Spring이 내 리스너의 생성을 제어하지 못하게하고 결과적으로 의존성 삽입을 허용하지 않을 것입니다. 대신 내 클래스에 다음 'init'함수를 추가했습니다.
public void init() { JpaEntityManager entityManager = null; try { // Create an entity manager for use in this function entityManager = (JpaEntityManager) entityManagerFactory.createEntityManager(); // Use the entity manager to get a ClassDescriptor for the Entity class ClassDescriptor desc = entityManager.getSession().getClassDescriptor(<EntityClass>.class); // Add this class as a listener to the class descriptor desc.getEventManager().addListener(this); } finally { if (entityManager != null) { // Cleanup the entity manager entityManager.close(); } } }
약간의 스프링 XML 설정 추가하기
<!-- Define listener object --> <bean id="entityListener" class="EntityListener " init-method="init"> <property name="injectedValue" value="Hello World"/> <property name="entityManagerFactory" ref="emf"/> </bean>
이제 우리는 Spring이 엔티티 리스너를 생성하고 필요한 모든 의존성을 주입하며 리스너 객체가 리스닝하려는 엔티티 클래스에 자신을 등록하는 상황을 보았습니다.
이게 도움이 되길 바란다.
-
==============================
5.나는 https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/에서 제안 된 접근법을 시험하고 일했다. 매우 깨끗하지는 않지만 일을합니다. 나를 위해 약간 수정 된 AutowireHelper 클래스는 다음과 같습니다.
나는 https://guylabs.ch/2014/02/22/autowiring-pring-beans-in-hibernate-jpa-entity-listeners/에서 제안 된 접근법을 시험하고 일했다. 매우 깨끗하지는 않지만 일을합니다. 나를 위해 약간 수정 된 AutowireHelper 클래스는 다음과 같습니다.
import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class AutowireHelper implements ApplicationContextAware { private static ApplicationContext applicationContext; private AutowireHelper() { } public static void autowire(Object classToAutowire) { AutowireHelper.applicationContext.getAutowireCapableBeanFactory().autowireBean(classToAutowire); } @Override public void setApplicationContext(final ApplicationContext applicationContext) { AutowireHelper.applicationContext = applicationContext; } }
그런 다음 엔티티 리스너에서 this를 다음과 같이 호출합니다.
public class MyEntityAccessListener { @Autowired private MyService myService; @PostLoad public void postLoad(Object target) { AutowireHelper.autowire(this); myService.doThings(); ... } public void setMyService(MyService myService) { this.myService = myService; } }
-
==============================
6.나는이 청자 콩이 봄 통제하에 있지 않기 때문에 그것이라고 믿는다. Spring은 인스턴스화하지 않습니다. Spring은 어떻게 bean을 찾아서 injection을 할 수 있습니까?
나는이 청자 콩이 봄 통제하에 있지 않기 때문에 그것이라고 믿는다. Spring은 인스턴스화하지 않습니다. Spring은 어떻게 bean을 찾아서 injection을 할 수 있습니까?
나는 그걸 시도하지 않았지만 AspectJ Weaver와 Spring의 Configurable annotation을 사용하여 스프링 컨트롤이 아닌 Spring 인스턴스화 된 빈을 가질 수 있다고 생각한다.
http://static.springsource.org/spring/docs/3.1.2.RELEASE/spring-framework-reference/html/aop.html#aop-using-aspectj
-
==============================
7.다른 옵션 :
다른 옵션 :
ApplicationContext에 액세스 할 수있게 해주는 서비스를 만듭니다.
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; import org.springframework.stereotype.Service; import lombok.Setter; @Service class ContextWrapper { @Setter private static ApplicationContext context; @Autowired public ContextWrapper(ApplicationContext ac) { setContext(ac); } public static ApplicationContext getContext() { return context; } }
그걸 써:
... public class AuditListener { private static final String AUDIT_REPOSITORY = "AuditRepository"; @PrePersist public void beforePersist(Object object){ //TODO: } @PreUpdate public void beforeUpdate(Object object){ //TODO: } @PreRemove public void beforeDelete(Object object) { getRepo().save(getAuditElement("DEL",object)); } private Audit getAuditElement(String Operation,Object object){ Audit audit = new Audit(); audit.setActor("test"); Timestamp timestamp = new Timestamp(System.currentTimeMillis()); audit.setDate(timestamp); return audit; } private AuditRepository getRepo(){ return ContextWrapper.getContext().getBean(AUDIT_REPOSITORY, AuditRepository.class); } }
이 클래스는 jpa에서 리스너로 작성됩니다.
... @Entity @EntityListeners(AuditListener.class) @NamedQuery(name="Customer.findAll", query="SELECT c FROM Customer c") public class Customer implements Serializable { private static final long serialVersionUID = 1L; ...
리스너는 Spring의 통제하에 있지 않기 때문에 컨텍스트 빈에 접근 할 수 없다. 여러 옵션 (@Configurable (...))을 시도했지만 none은 컨텍스트에 대한 정적 액세스 클래스를 만드는 것 외에는 작동하지 않았습니다. 이미 그 딜레마에 나는 이것이 우아한 선택이라고 생각합니다.
-
==============================
8.JPA Listeners의 문제점은 다음과 같습니다.
JPA Listeners의 문제점은 다음과 같습니다.
문제를 해결할 수있는 해결 방법은 다음과 같습니다.
1) public static LISTENERS 필드가있는 Listener 클래스를 만듭니다.
public abstract class Listener { // for encapsulation purposes we have private modifiable and public non-modifiable lists private static final List<Listener> PRIVATE_LISTENERS = new ArrayList<>(); public static final List<Listener> LISTENERS = Collections.unmodifiableList(PRIVATE_LISTENERS); protected Listener() { PRIVATE_LISTENERS.add(this); } }
2) Listener.LISTENERS에 추가 할 모든 JPA 리스너는이 클래스를 확장해야합니다.
public class MyListener extends Listener { @PrePersist public void onPersist() { ... } ... }
3) 이제 Spring의 Application Context가 준비된 직후에 모든 리스너를 얻고 콩을 주입 할 수 있습니다
@Component public class ListenerInjector { @Autowired private ApplicationContext context; @EventListener(ContextRefreshedEvent.class) public void contextRefreshed() { Listener.LISTENERS.forEach(listener -> context.getAutowireCapableBeanFactory().autowireBean(listener)); } }
from https://stackoverflow.com/questions/12155632/injecting-a-spring-dependency-into-a-jpa-entitylistener by cc-by-sa and MIT license
'SPRING' 카테고리의 다른 글
[SPRING] Spring Boot에서 암시 적으로 사용하는 Jackson JSON 매퍼를 사용자 정의하는 방법은 무엇입니까? (0) | 2018.12.08 |
---|---|
[SPRING] Spring XML 컨텍스트에서 조건부 리소스 가져 오기를 수행하는 방법? (0) | 2018.12.08 |
[SPRING] DispatcherServlet이 다른 응용 프로그램 컨텍스트를 만드는 이유는 무엇입니까? (0) | 2018.12.08 |
[SPRING] "NoClassDefFoundError : 클래스를 초기화 할 수 없습니다"오류 (0) | 2018.12.08 |
[SPRING] Spring 내에서 알려지지 않은 속성을 무시하도록 Jackson을 어떻게 세계적으로 설정합니까? (0) | 2018.12.08 |