Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Annotation scan not scanning external jars in classpath

Issue: Spring Component Annotation scan not picking up the class annotated in the external jar which is not included in pom.xml. But i need to scan for classes with specific annotation from external jars. These external jars will be placed in the classpath but will not be known to my application during compile time.

1) We have a maven module(artifactId="metric_processor") which produces a jar file(metric_processor.jar) and has following classes

package com.metric;
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ProcessMetric {
    String name();
}

package com.metric;
public interface MetricProcessor {
   int computeMetric();
}

package com.metric;
@ProcessMetric(name="LATENCY")
@Component
public class LatencyMetricProcessor implements MetricProcessor {
    .....
}

2) We have another maven module ("artifactId="metric_processor_external") which produces a jar(metric_processor_external.jar) and includes "metric_processor" module as compile time scope.

package com.metric;
@ProcessMetric(name="TEST_METRIC_EXTERNAL")
@Component
public class TestMetricProcessor implements MetricProcessor {
    ....
}

3) We have a third(main) maven module(artifactId="main_application") which is a stand alone application(uses spring) which includes module "metric_processor" in compile scope. (But does not include "metric_processor_external"). The build plugin for the third module is

<build>
    <plugins>
        <plugin>
            <artifactId>maven-assembly-plugin</artifactId>
            <version>2.4</version>
            <configuration>
                <descriptorRefs>
                    <descriptorRef>jar-with-dependencies</descriptorRef>
                </descriptorRefs>
                <archive>
                    <manifest>
                        <mainClass>com.main.TriggerMetricProcessor</mainClass>
                    </manifest>
                </archive>
            </configuration>
            <executions>
                <execution>
                    <id>make-assembly</id>
                    <phase>package</phase>
                    <goals>
                        <goal>single</goal>
                    </goals>
                </execution>
            </executions>
        </plugin>
    </plugins>
</build>

Application context xml for this module is

    <beans>
        <context:component-scan base-package="com.metric">
            <context:include-filter type="annotation" expression="com.metric.ProcessMetric" />
        </context:component-scan>
        <bean id="triggerMetricProcessor" class="com.main.TriggerMetricProcessor" />
    </beans>

I have the following class which is the starting point of the application

package com.main;

import ...

public class TriggerMetricProcessor {
    public static void main(String[] args) throws  Exception {
        ApplicationContext context =
                new ClassPathXmlApplicationContext("application-context.xml");

        TriggerMetricProcessor triggerMetricProcessor = (TriggerMetricProcessor) context.getBean("triggerMetricProcessor");
        triggerMetricProcessor.initMetricProcessor(context);
    }

    private void initMetricProcessor(ApplicationContext context) {
        GenericBeanFactoryAccessor beanFactoryAccessor = new GenericBeanFactoryAccessor(context);
        final Map<String, Object> metricProcessors = beanFactoryAccessor.getBeansWithAnnotation(ProcessMetric.class);
        for (final Object metricProcessor : metricProcessors.values()) {
            final Class<? extends MetricProcessor> metricProcessorClass = (Class<? extends MetricProcessor>)metricProcessor.getClass();
            final ProcessMetric annotation = metricProcessorClass.getAnnotation(ProcessMetric.class);
            System.out.println("Found MetricProcessor class: " + metricProcessorClass + ", with name: " + annotation.name());
        }
    }
}

we compile the third module as

maven clean install assembly:single 

This produces the jar file "main_application-with-dependencies.jar"

Then we run its as

java -cp "metric_process_external.jar" -jar main_application-with-dependencies.jar

Now the application finds only "LatencyMetricProcessor" and does not find the "TestMetricProcessor".

Can someone please help?

like image 888
Thiyanesh Avatar asked May 12 '13 09:05

Thiyanesh


People also ask

What is classpath scan?

A Classpath scanning basically means, detecting the classes that need to be managed by the Spring under a specified package. You need to make use of the spring @ComponentScan annotation with the @Configuration for classpath scanning.

Why can I not add external JARs in eclipse?

If your project builds with Java 9+ make sure you've selected Classpath (as shown here). You should also be able to add the JAR with right-click on the project > Build Path > Add External Archives.... If that doesn't help, would you tell use the version and package of Eclipse IDE you're using?

How do I know if a jar is in classpath?

A pragmatic way: Class. forName("com. myclass") where com. myclass is a class that is inside (and only inside) your target jar; if that throws a ClassNotFoundException , then the jar is not on you current classpath.


1 Answers

When you use the -jar option to execute a jar file, the -cp option is ignored.

The Oracle Java docs for the -jar option say:

-jar

Execute a program encapsulated in a JAR file. The first argument is the name of a JAR file instead of a startup class name. In order for this option to work, the manifest of the JAR file must contain a line of the form Main-Class: classname. Here, classname identifies the class having the public static void main(String[] args) method that serves as your application's starting point. See the Jar tool reference page and the Jar trail of the Java Tutorial for information about working with Jar files and Jar-file manifests.

When you use this option, the JAR file is the source of all user classes, and other user class path settings are ignored.

Also check out this post: stackoverflow.com/questions/5879925/in-linux-how-to-execute-java-jar-file-with-external-jar-files

So you'll need to specify the metric_process_external.jar in your manifest file using a Class-Path: header. You should be able to get your Maven assembly plugin to do that.

If that's not practical, you'll need to run your application without the -jar flag:

java -cp "metric_process_external.jar:main_application-with-dependencies.jar" com.main.TriggerMetricProcessor
like image 78
Will Keeling Avatar answered Oct 31 '22 07:10

Will Keeling