Is it possible for class to inherit the annotations of the super class

24,740

Solution 1

Annotation inheritance works basically the same way as inheritance of methods or fields.

Since you can access annotations only through reflection, there are two basic methods in Class:

  • getAnnotations() returns all annotations on the current class and its super classes
  • getDeclaredAnnotations() returns all annotations on the current class

The problem the article you linked talks about is that Method#getAnnotation(...) accesses declaredAnnotations() of the class the method has been defined in, which as stated above only returns the annotations defined in that class and not those of the super classes.

This means that if you override one of the methods annotated with @Transactional you'd have to add the annotation there (or if the frameworks also looks in the class annotation it should find the @Transactional declared on AbstractMaqraaService).

Solution 2

Yes it is possible if the annotation has @Inherited added to it. For example, the @Transactional annotation has @Inherited.

From the docs:

Indicates that an annotation type is automatically inherited. If an Inherited meta-annotation is present on an annotation type declaration, and the user queries the annotation type on a class declaration, and the class declaration has no annotation for this type, then the class's superclass will automatically be queried for the annotation type. This process will be repeated until an annotation for this type is found, or the top of the class hierarchy (Object) is reached. If no superclass has an annotation for this type, then the query will indicate that the class in question has no such annotation.

Note that this meta-annotation type has no effect if the annotated type is used to annotate anything other than a class. Note also that this meta-annotation only causes annotations to be inherited from superclasses; annotations on implemented interfaces have no effect.

Docs of @Transactional:

@Target(value={METHOD,TYPE})
@Retention(value=RUNTIME)
@Inherited
@Documented
public @interface Transactional

Off topic: You cannot subtype an annotation though in Java.

Share:
24,740
Mohammed Amr
Author by

Mohammed Amr

Updated on March 27, 2020

Comments

  • Mohammed Amr
    Mohammed Amr about 4 years

    I am using Spring Framework transactional annotations for transaction managing and I have an abstract class annotated @Transactional as seen below:

    package org.tts.maqraa.service;
    
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.List;
    import java.util.Set;
    
    import javax.persistence.EntityManager;
    import javax.persistence.EntityNotFoundException;
    import javax.persistence.PersistenceContext;
    import javax.persistence.PersistenceContextType;
    import javax.persistence.Query;
    
    import org.springframework.transaction.annotation.Propagation;
    import org.springframework.transaction.annotation.Transactional;
    
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    /**
     * Parts of this code have been copied from JARVANA site.
     * 
     * @author Younis alomoush
     * 
     */
    @Transactional(propagation=Propagation.REQUIRED)
    public abstract class AbstractMaqraaService implements MaqraaService {
    
    
        private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    
        private int defaultMaxResults = DEFAULT_MAX_RESULTS;
    
        @PersistenceContext(type=PersistenceContextType.TRANSACTION)
        private EntityManager em;
        /**
         * The {@link EntityManager} which is used by all query manipulation and
         * execution in this DAO.
         * 
         * @return the {@link EntityManager}
         */
        public  EntityManager getEntityManager(){
    
            return em;
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#getTypes()
         */
        public abstract Set<Class<?>> getTypes();
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#store(java.lang.Object)
         */
        @Transactional(propagation = Propagation.REQUIRED)
        public <T extends Object> T store(T toStore) {
            return getEntityManager().merge(toStore);
    
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#remove(java.lang.Object)
         */
        @Transactional(propagation = Propagation.REQUIRED)
        public void remove(Object toRemove) {
            toRemove = getEntityManager().merge(toRemove);
            getEntityManager().remove(toRemove);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#flush()
         */
        @Transactional(propagation = Propagation.REQUIRED)
        public void flush() {
            getEntityManager().flush();
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#refresh(java.lang.Object)
         */
        @Transactional(propagation = Propagation.SUPPORTS)
        public void refresh(Object o) {
            try {
                if (o != null) {
                    if (o instanceof java.util.Collection) {
                        for (Iterator<?> i = ((Collection<?>) o).iterator(); i
                                .hasNext();) {
                            try {
                                refresh(i.next());
                            } catch (EntityNotFoundException x) {
                                // This entity has been deleted - remove it from the
                                // collection
                                i.remove();
                            }
                        }
                    } else {
                        if (getTypes().contains(o.getClass())) {
                            getEntityManager().refresh(o);
                        }
                    }
                }
            } catch (EntityNotFoundException x) {
                // This entity has been deleted
            }
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#setDefaultMaxResults(int)
         */
        @Transactional(propagation = Propagation.SUPPORTS)
        public void setDefaultMaxResults(int defaultMaxResults) {
            this.defaultMaxResults = defaultMaxResults;
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#getDefaultMaxResults()
         */
        public int getDefaultMaxResults() {
            return defaultMaxResults;
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQueryByNameSingleResult(java
         * .lang.String)
         */
        @SuppressWarnings("unchecked")
        public <T extends Object> T executeQueryByNameSingleResult(String queryName) {
            return (T) executeQueryByNameSingleResult(queryName, (Object[]) null);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQueryByNameSingleResult(java
         * .lang.String, java.lang.Object[])
         */
    
        @SuppressWarnings("unchecked")
        public <T extends Object> T executeQueryByNameSingleResult(
                String queryName, Object... parameters) {
            Query query = createNamedQuery(queryName, DEFAULT_FIRST_RESULT_INDEX,
                    1, parameters);
            return (T) query.getSingleResult();
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQueryByName(java.lang.String)
         */
        public <T extends Object> List<T> executeQueryByName(String queryName) {
            return executeQueryByName(queryName, DEFAULT_FIRST_RESULT_INDEX,
                    getDefaultMaxResults());
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQueryByName(java.lang.String,
         * java.lang.Integer, java.lang.Integer)
         */
    
        public <T extends Object> List<T> executeQueryByName(String queryName,
                Integer firstResult, Integer maxResults) {
            return executeQueryByName(queryName, firstResult, maxResults,
                    (Object[]) null);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQueryByName(java.lang.String,
         * java.lang.Object[])
         */
    
        public <T extends Object> List<T> executeQueryByName(String queryName,
                Object... parameters) {
            return executeQueryByName(queryName, DEFAULT_FIRST_RESULT_INDEX,
                    getDefaultMaxResults(), parameters);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQueryByName(java.lang.String,
         * java.lang.Integer, java.lang.Integer, java.lang.Object[])
         */
        @SuppressWarnings("unchecked")
        public <T extends Object> List<T> executeQueryByName(String queryName,
                Integer firstResult, Integer maxResults, Object... parameters) {
            Query query = createNamedQuery(queryName, firstResult, maxResults,
                    parameters);
            return query.getResultList();
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#createNamedQuery(java.lang.String,
         * java.lang.Integer, java.lang.Integer)
         */
    
        public Query createNamedQuery(String queryName, Integer firstResult,
                Integer maxResults) {
            return createNamedQuery(queryName, firstResult, maxResults,
                    (Object[]) null);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#createNamedQuery(java.lang.String,
         * java.lang.Integer, java.lang.Integer, java.lang.Object[])
         */
    
        public Query createNamedQuery(String queryName, Integer firstResult,
                Integer maxResults, Object... parameters) {
            Query query = getEntityManager().createNamedQuery(queryName);
            if (parameters != null) {
                for (int i = 0; i < parameters.length; i++) {
                    query.setParameter(i + 1, parameters[i]);
                }
            }
    
            query.setFirstResult(firstResult == null || firstResult < 0 ? DEFAULT_FIRST_RESULT_INDEX
                    : firstResult);
            if (maxResults != null && maxResults > 0)
                query.setMaxResults(maxResults);
    
            return query;
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#executeQuery(java.lang.String,
         * java.lang.Integer, java.lang.Integer, java.lang.Object[])
         */
        @SuppressWarnings("unchecked")
        public <T extends Object> List<T> executeQuery(String queryString,
                Integer firstResult, Integer maxResults, Object... parameters) {
            Query query = createQuery(queryString, firstResult, maxResults,
                    parameters);
            return query.getResultList();
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#executeQuery(java.lang.String,
         * java.lang.Object[])
         */
        @SuppressWarnings("unchecked")
        public <T extends Object> List<T> executeQuery(String queryString,
                Object... parameters) {
            Query query = createQuery(queryString, DEFAULT_FIRST_RESULT_INDEX,
                    getDefaultMaxResults(), parameters);
            return query.getResultList();
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQuerySingleResult(java.lang.
         * String)
         */
        @SuppressWarnings("unchecked")
        public <T extends Object> T executeQuerySingleResult(String queryString) {
            return (T) executeQuerySingleResult(queryString, (Object[]) null);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see
         * org.skyway.spring.util.dao.JpaDao#executeQuerySingleResult(java.lang.
         * String, java.lang.Object[])
         */
        @SuppressWarnings("unchecked")
        public <T extends Object> T executeQuerySingleResult(String queryString,
                Object... parameters) {
            Query query = createQuery(queryString, DEFAULT_FIRST_RESULT_INDEX, 1,
                    parameters);
            return (T) query.getSingleResult();
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#createQuery(java.lang.String,
         * java.lang.Integer, java.lang.Integer)
         */
    
        public Query createQuery(String queryString, Integer firstResult,
                Integer maxResults) {
            return createQuery(queryString, firstResult, maxResults,
                    (Object[]) null);
        }
    
        /*
         * (non-Javadoc)
         * 
         * @see org.skyway.spring.util.dao.JpaDao#createQuery(java.lang.String,
         * java.lang.Integer, java.lang.Integer, java.lang.Object[])
         */
        public Query createQuery(String queryString, Integer firstResult,
                Integer maxResults, Object... parameters) {
            Query query = getEntityManager().createQuery(queryString);
            if (parameters != null) {
                for (int i = 0; i < parameters.length; i++) {
                    query.setParameter(i + 1, parameters[i]);
                }
            }
    
            query.setFirstResult(firstResult == null || firstResult < 0 ? DEFAULT_FIRST_RESULT_INDEX
                    : firstResult);
            if (maxResults != null && maxResults > 0)
                query.setMaxResults(maxResults);
    
            return query;
        }
    
        public final void log(LogLevel logLevel, String message,
                Object... messageParam) {
    
            switch (logLevel) {
            case TRACE:
                if (logger.isTraceEnabled()) {
    
                    logger.trace(message, messageParam);
                }
    
                break;
    
            case DEBUG:
                if (logger.isDebugEnabled()) {
    
                    logger.debug(message, messageParam);
                }
                break;
    
            case INFO:
                if (logger.isInfoEnabled()) {
    
                    logger.info(message, messageParam);
                }
                break;
    
            case WARN:
                if (logger.isWarnEnabled()) {
    
                    logger.warn(message, messageParam);
                }
                break;
    
            case ERROR:
                if (logger.isErrorEnabled()) {
    
                    logger.error(message, messageParam);
                }
                break;
    
            default:
                throw new IllegalArgumentException("Log Level is not defined: "
                        + logLevel);
    
            }
    
        }
    
        public final void log(LogLevel logLevel,  String message, Throwable throwable) {
    
            switch (logLevel) {
            case TRACE:
                if (logger.isTraceEnabled()) {
    
                    logger.trace(message, throwable);
                }
    
                break;
    
            case DEBUG:
                if (logger.isDebugEnabled()) {
    
                    logger.debug(message, throwable);
                }
                break;
    
            case INFO:
                if (logger.isInfoEnabled()) {
    
                    logger.info(message, throwable);
                }
                break;
    
            case WARN:
                if (logger.isWarnEnabled()) {
    
                    logger.warn(message, throwable);
                }
                break;
    
            case ERROR:
                if (logger.isErrorEnabled()) {
    
                    logger.error(message, throwable);
                }
                break;
    
            default:
                throw new IllegalArgumentException("Log Level is not defined: "
                        + logLevel);
    
            }
    
        }
    
    
        public enum LogLevel{
    
            TRACE, DEBUG, INFO, WARN, ERROR;
    
        }
    }
    

    Also, i have another concrete class

    package org.tts.maqraa.service;
    
    import java.lang.annotation.Annotation;
    import java.util.HashSet;
    import java.util.List;
    import java.util.Set;
    import org.tts.maqraa.data.Student;
    
    
    public class StudentsService extends AbstractMaqraaService {
    
        @Override
        public Set<Class<?>> getTypes() {
            Set<Class<?>> set = new HashSet<Class<?>>();
            set.add(Student.class);
            return set;
        }
    
        public Student registerStudent(Student student) {
            Annotation [] annotation = StudentsService.class.getAnnotations();
            System.out.println(annotation);
            return this.store(student);
        }
    
        public Student editStudent(Student student){
            return this.store(student);
        }
    
        public void deregisterStudent(Student student){
            this.remove(student);
        }
    
        public List<Student> findAllStudents(){
            return this.executeQueryByName("Student.findAll");
        }
    
    }
    

    If you notice that the method register student has already provided code for examine the annotations where I really find the @Transactional annotation.

    That's a contradiction where I have another link talks about inheritance of annotations and it says that there is no inheritance at all.

    review this link: http://fusionsoft-online.com/articles-java-annotations.php

    Can anyone help me to solve this contradiction ?