Interceptors are used in conjunction with Java EE managed classes to allow developers to invoke interceptor methods on an associated target class, in conjunction with method invocations or lifecycle events. Common uses of interceptors are logging, auditing, and profiling.
The Interceptors 1.1 specification is part of the final release of JSR 318, Enterprise JavaBeans 3.1, available from http://jcp.org/en/jsr/detail?id=318.
An interceptor can be defined within a target class as an interceptor method, or in an associated class called an interceptor class. Interceptor classes contain methods that are invoked in conjunction with the methods or lifecycle events of the target class.
Interceptor Classes: Interceptor classes may be designated with the optional javax.interceptor.Interceptor annotation, but interceptor classes aren’t required to be so annotated. An interceptor class must have a public, no-argument constructor.
The target class can have any number of interceptor classes associated with it. The order in which the interceptor classes are invoked is determined by the order in which the interceptor classes are defined in the javax.interceptor.Interceptors annotation. However, this order can be overridden in the deployment descriptor.
Interceptor classes may be targets of dependency injection. Dependency injection occurs when the interceptor class instance is created, using the naming context of the associated target class, and before any @PostConstruct callbacks are invoked.
Interceptor Lifecycle: Interceptor classes have the same lifecycle as their associated target class. When a target class instance is created, an interceptor class instance is also created for each declared interceptor class in the target class. That is, if the target class declares multiple interceptor classes, an instance of each class is created when the target class instance is created. The target class instance and all interceptor class instances are fully instantiated before any @PostConstruct callbacks are invoked, and any @PreDestroy callbacks are invoked before the target class and interceptor class instances are destroyed.
Interceptors and CDI: Contexts and Dependency Injection for the Java EE Platform (CDI) build on the basic functionality of Java EE interceptors. For information on CDI interceptors, including a discussion of interceptor binding types, see Using Interceptors in CDI Applications.
Here we will be looking at various ways of intercepting operations within Hibernate's abstracted relational mapping implementation.
Hibernate Interceptors
The Hibernate Interceptor is an interface that allows us to react to certain events within Hibernate. These interceptors are registered as callbacks and provide communication links between Hibernate's session and application. With such a callback, an application can intercept core Hibernate's operations such as save, update, delete, etc.
Types of Interceptors
There are two ways of defining interceptors:
- implementing the org.hibernate.Interceptor interface
- extending the org.hibernate.EmptyInterceptor class
2.1: Implementing an Interceptor Interface: Implementing org.hibernate.Interceptor requires implementing about 14 accompanying methods. These methods include onLoad, onSave, onDelete, findDirty, and a few more. It is also important to ensure that any class that implements Interceptor interface is serializable (implements java.io.Serializable).
A typical example would look as follows:
// Class
public class CustomInterceptorImpl
implements Interceptor, Serializable {
// Annotation
@Override
// Method
public boolean onLoad(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types)
throws CallbackException {
// ... return false; }
// ... @Override public String onPrepareStatement(String sql)
{
// ... return sql; }
}
If there are no special requirements, extending the EmptyInterceptor class and only overriding the required methods is highly recommended.
2.2: Extending EmptyInterceptor: Extending the org.hibernate.EmptyInterceptor class provides an easier way of defining an interceptor. We now only need to override the methods that relate to the operation we want to intercept.
For example, we can define our CustomInterceptor as:
public class CustomInterceptor extends EmptyInterceptor { }
And if we need to intercept data saving operations before they are executed, we need to override onSave method:
// Annotation
@Override
// Method
public boolean onSave(Object entity, Serializable id, Object[] state,
String[] propertyNames, Type[] types) {
if (entity instanceof User) {
logger.info(((User) entity).toString());
}
return super.onSave(entity, id, state, propertyNames, types);
}
Notice how this implementation simply prints out the entity – if it's a User. While it's possible to return a value of true or false, it's a good practice to allow propagation of onSave event by invoking super.onSave(). Another use case would be providing an audit trail for database interactions. We can use the onFlushDirty() method to know when an entity changes.
For the User object, we can decide to update its lastModified date property whenever changes on entities of type User happen.
In the below method we will be illustrating how this can be achieved which is as follows:
// Annotation
@Override
// Method
public boolean onFlushDirty(Object entity, Serializable id,
Object[] currentState, Object [] previousState,
String[] propertyNames, Type[] types) {
if (entity instanceof User) {
((User) entity).setLastModified(new Date());
logger.info(((User) entity).toString());
}
return super.onFlushDirty(entity, id, currentState, previousState, propertyNames, types);
}
Other events such as delete and load (object initialization) can be intercepted by implementing the corresponding onDelete and onLoad methods respectively.
Registering Interceptors
A Hibernate interceptor can either be registered as Session-scoped or SessionFactory-scoped.
3.1: Session-scoped Interceptor: A Session-scoped interceptor is linked to a specific session. It's created when the session is being defined or opened as:
public static Session getSessionWithInterceptor(Interceptor interceptor)
throws IOException {
return getSessionFactory().withOptions() .interceptor(interceptor).openSession();
}
In the above, we explicitly registered an interceptor with a particular hibernate session.
3.2: SessionFactory-scoped Interceptor: A SessionFactory-scoped interceptor is registered before building a SessionFactory. This is typically done through the applyInterceptor method on a SessionFactoryBuilder instance:
ServiceRegistry serviceRegistry = configureServiceRegistry();
SessionFactory sessionFactory = getSessionFactoryBuilder(serviceRegistry).applyInterceptor(new CustomInterceptor()) .build();
It's important to note that a SessionFactory-scoped interceptor will be applied to all sessions. Hence, we need to be careful not to store session-specific states as this interceptor will be used by different sessions concurrently.
- For a session-specific behavior, it's recommended to explicitly open a session with a different interceptor as earlier shown.
- For SessionFactory-scoped interceptors, we naturally need to ensure that it's thread-safe. This can be achieved by specifying a session context in the properties file:
hibernate.current_session_context_class=org.hibernate.context.internal.ThreadLocalSessionContext
Or by adding this to our XML configuration file:
<property name="hibernate.current_session_context_class">
org.hibernate.context.internal.ThreadLocalSessionContext
</property>
Also, to ensure serializability, SessionFactory-scoped interceptors must implement the readResolve method of the Serializable interface.
Conclusion: We've seen how to define and register Hibernate interceptors either as Session-scoped or SessionFactory-scoped. In either case, we must ensure that the interceptors are serializable especially if we want a serializable session.
Other alternatives to interceptors include Hibernate Events and JPA Callbacks. And, as always, you can check out the complete source code over on Github.
Similar Reads
Spring Tutorial
Spring Framework is a comprehensive and versatile platform for enterprise Java development. It is known for its Inversion of Control (IoC) and Dependency Injection (DI) capabilities that simplify creating modular and testable applications. Key features include Spring MVC for web development, Spring
13 min read
Basics of Spring Framework
Software Setup and Configuration
Core Spring
Spring - BeanFactory
The first and foremost thing when we talk about Spring is dependency injection which is possible because Spring is a container and behaves as a factory of Beans. Just like the BeanFactory interface is the simplest container providing an advanced configuration mechanism to instantiate, configure, and
4 min read
Spring - Injecting Objects By Constructor Injection
Spring IoC (Inversion of Control) Container is the core of Spring Framework. It creates the objects, configures and assembles their dependencies, and manages their entire life cycle. The Container uses Dependency Injection (DI) to manage the components that make up the application. It gets the infor
5 min read
Spring - Setter Injection with Dependent Object
Dependency Injection is the main functionality provided by Spring IOC(Inversion of Control). The Spring-Core module is responsible for injecting dependencies through either Constructor or Setter methods. In Setter Dependency Injection(SDI) the dependency will be injected with the help of setters and
3 min read
Spring - JMS Integration
JMS is a standard Java API that allows a Java application to send messages to another application. It is highly scalable and allows us to loosely couple applications using asynchronous messaging. Using JMS we can read, send, and read messages. Benefits of using JMS with Spring IntegrationLoad balanc
8 min read
Spring @Bean Annotation with Example
The @Bean annotation in Spring is a powerful way to define and manage beans in a Spring application. Unlike @Component, which relies on class-level scanning, @Bean explicitly declares beans inside @Configuration classes, offering greater flexibility in object creation. In this article, we will explo
9 min read
Spring Boot @Controller Annotation with Example
Spring is one of the most popular frameworks for building enterprise-level Java applications. It is an open-source, lightweight framework that simplifies the development of robust, scalable, and maintainable applications. Spring provides various features such as Dependency Injection (DI), Aspect-Ori
3 min read
Spring @ComponentScan Annotation with Example
Spring is one of the most popular frameworks for building enterprise-level Java applications. It is an open-source, lightweight framework that simplifies the development of robust, scalable, and maintainable applications. Spring provides various features such as Dependency Injection (DI), Aspect-Ori
3 min read
Spring Boot @Service Annotation with Example
Spring is one of the most popular frameworks for building enterprise-level Java applications. It is an open-source, lightweight framework that simplifies the development of robust, scalable, and maintainable applications. Spring provides various features such as Dependency Injection (DI), Aspect-Ori
3 min read
Spring Boot Tutorial
Spring Boot is a Java framework that makes it easier to create and run Java applications. It simplifies the configuration and setup process, allowing developers to focus more on writing code for their applications. This Spring Boot Tutorial is a comprehensive guide that covers both basic and advance
10 min read