Spring AOP - #AfterReturning does not work - spring

I have a Java method, getAllItems(), and I created an aspect method, which is called after getAllItems() ends.
#Repository
#Scope("prototype")
public class ItemDao {
// not using database connection for this question; the program works fine, except aspects
public List<String> getIAllItems() {
List<String> items = new ArrayList();
items.add("item1");
return items;
}
}
The aspect class is this:
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import java.util.List;
#Aspect
public class MyAspects {
#AfterReturning(pointcut = "execution(* ro.telacad.dao.ItemDao.getIAllItems(..))", returning="items")
public void afterGetAllItems(List<String> items) {
System.out.println(items);
}
}
So, after calling getAllItems() I expect to see in console printed "[item1]", but the aspect method is not called. There is no error in console, and the application, except aspects, works fine. So I consider all the spring beans are created.
In appConfig.xml, the beans are declared like this:
<context:component-scan base-package="ro.telacad.*" />
<aop:aspectj-autoproxy/>
My question is what I did wrong for aspects.

MyAspects is not picked up by Spring's component scanning as it does not have the #Component annotation, and neither has #Aspect. #Repository does have the annotation.
Either add the #Component annotation to MyAspects, or explicitly declare the bean in your XML config:
<bean id="myAspects" class="com.yourpackage.MyAspects">
...
</bean>

Related

How does Spring know where to search for Components or Beans?

In an interview i was asked by the interviewer that "How does Spring know where to search for Components or Beans?".
As I was not aware about the internal flow details I was not able to answer the question properly.
I said through #Component and #Bean we can find. But the interviewer was not happy with the question.
If anybody knows please share your knowledge. TIA
I love to nswer interview questions. Read below...
#ComponentScan
If you understand Component Scan, you understand Spring.
Spring is a dependency injection framework. It is all about beans and wiring in dependencies.
The first step of defining Spring Beans is by adding the right annotation — #Component or #Service or #Repository.
However, Spring does not know about the bean unless it knows where to search for it.
This part of “telling Spring where to search” is called a Component Scan.
You define the packages that have to be scanned.
Once you define a Component Scan for a package, Spring would search the package and all its sub packages for components/beans.
Defining a Component Scan
If you are using Spring Boot, check the configuration in Approach 1.
If you are doing a JSP/Servlet or a Spring MVC application without
using Spring Boot, use Approach 2.
Approach 1: Component Scan in a Spring Boot Project
If your other package hierarchies are below your main app with the #SpringBootApplication annotation, you’re covered by the implicit Component Scan.
If there are beans/components in other packages that are not sub-packages of the main package, you should manually add them as #ComponentScan
Consider below class
package com.in28minutes.springboot.basics.springbootin10steps;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class SpringbootIn10StepsApplication {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(SpringbootIn10StepsApplication.class, args);
for (String name: applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
#SpringBootApplication is defined in the SpringbootIn10StepsApplication class which is in the package com.in28minutes.springboot.basics.springbootin10steps
#SpringBootApplication defines an automatic Component Scan on the package com.in28minutes.springboot.basics.springbootin10steps.
You are fine if all your components are defined in the above package or a sub-package of it.
However, let’s say one of the components is defined in package com.in28minutes.springboot.somethingelse
In this case, you would need to add the new package into Component Scan.
You have two options:
Option 1:
#ComponentScan(“com.in28minutes.springboot”)
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Option 2:: Define as array
#ComponentScan({"com.in28minutes.springboot.basics.springbootin10steps","com.in28minutes.springboot.somethingelse"})
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Approach 2: Non-Spring Boot Project
Option 1:
#ComponentScan(“com.in28minutes)
#Configuration
public class SpringConfiguration {...}
Option 2:
#ComponentScan({"com.in28minutes.package1","com.in28minutes.package2"})
#Configuration
public class SpringConfiguration {...}
XML application context:
<context:component-scan base-package="com.in28minutes" />
Specific multiple packages:
<context:component-scan base-package="com.in28minutes.package1, com.in28minutes.package2" />
The IoC (Inversion of Control) container, represented in Spring by the class ApplicationContext, is the brain behind all of it. It all comes down to using reflection in a really powerful way.
To simplify, let's consider the following steps (all done through reflection):
Search all classes in the classpath
From those classes, get all classes annotated with #Component
For each class annotated with #Component, create a new instance of that class
Check for dependencies, i.e, for each created instance, check all fields annotated with #Autowired and create an instance for each one of them.
Keep everything in the context so they can be used later.
The remaining of this answer is an oversimplified version of how this happens as if we did it ourselves. Thankfully, Spring exists and we don't need to do this ourselves.
The annotations
#Retention(RetentionPolicy.RUNTIME)
public #interface Node {}
#Retention(RetentionPolicy.RUNTIME)
public #interface Wire { }
Some annotated classes for testing
#Node
public class ServiceA {
#Wire
private ServiceB serviceB;
public void doAStuff() {
System.out.println("A stuff");
serviceB.doBStuff();
}
}
#Node
public class ServiceB {
public void doBStuff() {
System.out.println("B stuff");
}
}
The IoC Container
import org.reflections.Reflections;
/* dependency org.reflections:reflections:0.9.12 */
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class IoC {
private final Map<Class<?>, Object> allNodes = new HashMap<>();
public void start() {
Reflections reflections = new Reflections(IoC.class.getPackageName());
Set<Class<?>> nodeClasses = reflections.getTypesAnnotatedWith(Node.class);
try {
for (Class<?> c : nodeClasses) {
Object thisInstance = c.getDeclaredConstructor().newInstance();
for (Field f : c.getDeclaredFields()) {
f.setAccessible(true);
if (f.getDeclaredAnnotation(Wire.class) != null) {
Object o = f.getType().getDeclaredConstructor().newInstance();
f.set(thisInstance, f.getType().cast(o));
}
}
allNodes.put(c, thisInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> T getNodeByType(Class<T> cls) {
return cls.cast(allNodes.get(cls));
}
}
And the main class to get it all started.
public class Application {
public static void main(String[] args) {
IoC ioc = new IoC();
ioc.start();
ServiceA serviceA = ioc.getNodeByType(ServiceA.class);
serviceA.doAStuff();
}
}
This will output:
A stuff
B stuff
Of course, Spring is a lot more powerful (and robust) than this. It allows for custom package scanning using #ComponentScan, beans of the same type with different names, singleton/prototype scoped beans, constructor wiring, properties files injection, amongst many other things. When it comes to Spring Boot, the #SpringBootApplication annotation make sure it finds and wire all #Controller annotated classes and set up a Netty/Jetty/Tomcat embedded server to listen to the requests and redirect to the proper controller based on the annotated types.
Well where to search for the beans is defined by the #ComponentScan which can be annotated on the #Configuration class that is used to bootstrap Spring.
For example , it has an attribute called scanBasePackages which tells Spring to scan the beans (A class that is annotated with #Component or its sterotypes such as #Service , #Repository , #Controller etc. ) from certain packages and its sub-packages only.
Then for each bean that are registered , it goes on see if there are any methods annotation with #Bean.If yes, also register them as beans.

Autowire working in unit test but not in main java class

I've a domain class that I want to auto-populate from external config. Here is my domain class:
#Data
#Configuration
#PropertySource("classpath:application.properties")
public class StudioVo {
#Value("${studio.code}")
private code;
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Here is my context xml:
<bean class="org.springframework.batch.core.scope.StepScope" />
<bean id="ItemReader" class="com.sdm.studio.reader.StudioReader" scope="step">
<property name="studioVo" ref="StudioVo" />
</bean>
<bean id="StudioConfigVo" class="com.sdm.studio.domain.StudioVo" />
</bean>
Here is the class where I want to use the vo:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
private StudioVo studioVo;
public List<Studio> read() throws Exception {
System.out.println("getCode: " + studioVo.getCode()); //code is null here
return null;
}
}
However when I run it via unit test by autowiring, it runs fine. Like this:
#RunWith(SpringRunner.class)
#SpringBootTest
public class StudioTest {
#Autowired
private StudioVo studioVo;
#Test
public void testAutoPopulationOfStudio(){
System.out.println("getCode: "+ studioVo.getCode()); // works!
// Assert.assertTrue(studioVo.getCode().equals("102"));
}
}
Not sure what's going on here - I'm working with an old Spring Batch application wrapped in Spring Boot (so there is a mix of XML based and Java based config - and may be that is the cause of this issue). What am I missing?
In your StudioTest, you are autowiring StudioReader where as you missed the #Autowired in your StudioReader code, so add it as shown below:
#Slf4j
#Data
public class StudioReader implements ItemReader<List<Studio>> {
#Autowired //add this so that studioVo can be injected
private StudioVo studioVo;
//add other code
}
Please be certain to note that using #Autowire requires a chain of Spring-managed beans below it from wherever you are using it including the class in which you are using #Autowire. That is because Spring needs the precedent references to match up the object-reference hierarchy. I.e., in business logic layer ClassA, you want to #Autowire a field. ClassA itself needs to be a managed bean. Further, if the field you want to #Autowire holds an object that has referential dependencies to other objects (and most do), these also must be Spring-managed.
For example, the following will work:
package com.example.demo;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MessageRunner {
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage = (SetterMessage) (new AnnotationConfigApplicationContext(DemoConfiguration.class)).getBean("setterMessage");
setterMessage.setMessage("Finally it works.");
p(setterMessage.getMessage());
}
private static void p(String s) {
System.out.println(s);
}
}
DemoConfiguration.java looks like this:
package com.example.demo;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
#Configuration
#ComponentScan(basePackages = "com.example.demo")
public class DemoConfiguration {
}
SetterMessage.java, this:
package com.example.demo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage {
private String message = null;
#Autowired
private SetterMessage2 setterMessage2;
public String getMessage(){
return message+setterMessage2.getSubMessage();
}
public void setMessage(String message) {
this.message = message;
setterMessage2.setSubMessage("("+message+")");
}
}
SetterMessage2.java:
package com.example.demo;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;
#Service
#Scope("prototype")
public class SetterMessage2 {
private String subMsg = "";
public void setSubMessage(String msg) {
subMsg = msg;
}
public String getSubMessage() {
return subMsg;
}
}
Note that SetterMessage2.java is annotated as a Component (#Service) but no field in it is autowired. That is because it's the end of the object reference chain. But because it is a Component, it can be autowired into SetterMessage.java. However look at MessageRunner.java's main() method and field declarations. Note that the class field SetterMessage is NOT autowired. If it were annotated as #Autowired, main() would fail at runtime, throwing an NPE with the reference to setterMessage in main(). This is because MessageRunner.java is not marked as some kind of component. So we need to grab a valid instance of MessageSetter from the application context and use it.
To emphasize, the following version of MessageRunner.java's main() method WILL FAIL, throwing an NPE, if MessageRunner.java looked like this:
...
public class MessageRunner {
#Autowired // <-- This will not do the job for us
private static SetterMessage setterMessage;
public static void main(String[] args) {
setterMessage.setMessage("Finally it works."); // NPE here on ref to setterMessage
p(setterMessage.getMessage());
}
...
This is a real gotchya for people new to Spring. In fact, I'd place it among the Top Five Spring Newbie Discouragers and a really evil, pernicious detail that has caused new Spring programmers countless hours in aggravation and Google searches. So I do hope that noting this phenom here will save at least some newbies time and high blood pressure spikes.
Note: If you go to create the above classes in your IDE, bear in mind these were developed with Spring Boot enabled.

#Autowired dependencies are null in compile time weaving of #Aspect class [duplicate]

I have the following spring configuration:
<context:component-scan base-package="uk.co.mysite.googlecontactsync.aop"/>
<bean name="simpleEmailSender" class="uk.co.mysite.util.email.simple.SimpleEmailSenderImplementation"/>
<aop:aspectj-autoproxy/>
Then I have an aspect:
#Aspect
public class SyncLoggingAspect {
#Autowired
private SimpleEmailSender simpleEmailSender
#AfterReturning(value="execution(* uk.co.mysite.datasync.polling.Poller+.doPoll())", returning="pusher")
public void afterPoll(Pusher pusher) {
simpleEmailSender.send(new PusherEmail(pusher));
}
}
This aspect works (I can hit a breakpoint on afterPoll) but simpleEmailSender is null. Unfortunately I cannot find clear documentation on why this is. (For the record, my simpleEmailSender bean exists and is correctly wired into other classes) The following things confuse me:
Is context:component-scan supposed to be picking up #Aspect? If it is then surely it would be a spring managed bean, thus autowired should work?
If context:component-scan isn't for creating aspects, how is my aspect being created? I thought aop:aspectj-autoproxy just creates a beanPostProcessor to proxy my #Aspect class? How would it do this if it isn't a spring managed bean?
Obviously you can tell I don't have an understanding of how things should be working from the ground up.
The aspect is a singleton object and is created outside the Spring container. A solution with XML configuration is to use Spring's factory method to retrieve the aspect.
<bean id="syncLoggingAspect" class="uk.co.demo.SyncLoggingAspect"
factory-method="aspectOf" />
With this configuration the aspect will be treated as any other Spring bean and the autowiring will work as normal.
You have to use the factory-method also on Enum objects and other objects without a constructor or objects that are created outside the Spring container.
For Spring Boot to use #Autowired with AspectJ I have found the following method.
In configuration class add your aspect:
#Configuration
#ComponentScan("com.kirillch.eqrul")
public class AspectConfig {
#Bean
public EmailAspect theAspect() {
EmailAspect aspect = Aspects.aspectOf(EmailAspect.class);
return aspect;
}
}
Then you can successfully autowire your services in your aspect class:
#Aspect
public class EmailAspect {
#Autowired
EmailService emailService;
Another option is to add #Configurable to your aspect class instead of messing around with XML.
Configuring #Autowired with java config only (so no XML based configuration) requires a bit of extra work than just adding #Configuration to the class, as it also needs the aspectOf method.
What worked for me was creating a new class:
#Component
public class SpringApplicationContextHolder implements ApplicationContextAware {
private static ApplicationContext applicationContext = null;
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
}
And then use that in you aspect in conjunction with using #DependsOn #Configured and #Autowired:
#DependsOn("springApplicationContextHolder")
#Configuration
#Aspect
public class SomeAspect {
#Autowired
private SomeBean someBean;
public static SomeAspect aspectOf() {
return SpringApplicationContextHolder.getApplicationContext().getBean(SomeAspect.class);
}
The #DependsOn is needed because spring can't determine the dependency because the bean is used staticly.
I dont have 50 rep to comment on a question so here is another answer relating to #
Jitendra Vispute answer.
The official Spring doc mentions:
You may register aspect classes as regular beans in your Spring XML configuration, or autodetect them through classpath scanning - just like any other Spring-managed bean. However, note that the #Aspect annotation is not sufficient for autodetection in the classpath: For that purpose, you need to add a separate #Component annotation (or alternatively a custom stereotype annotation that qualifies, as per the rules of Spring’s component scanner).Source: Spring '4.1.7.Release' documentation.
This would mean that adding a #Component annotation and adding the #ComponentScan on your Configuration would make #Jitendra Vispute's example work. For the spring boot aop sample it worked, though I did not mess around with context refreshing.Spring boot aop sample:
Application:
package sample.aop;
#SpringBootApplication
public class SampleAopApplication implements CommandLineRunner {
// Simple example shows how an application can spy on itself with AOP
#Autowired
private HelloWorldService helloWorldService;
#Override
public void run(String... args) {
System.out.println(this.helloWorldService.getHelloMessage());
}
public static void main(String[] args) throws Exception {
SpringApplication.run(SampleAopApplication.class, args);
}
}
The application should also run as plain Spring Framework application with the following annotations instead of #SpringBootApplication:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
and an AnnotationConfigApplicationContext instead of SpringApplication.
Service:
package sample.aop.service;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
#Component
public class HelloWorldService {
#Value("${name:World}")
private String name;
public String getHelloMessage() {
return "Hello " + this.name;
}
}
Monitor Aspect:
package sample.aop.monitor;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class ServiceMonitor {
#AfterReturning("execution(* sample..*Service.*(..))")
public void logServiceAccess(JoinPoint joinPoint) {
System.out.println("Completed: " + joinPoint);
}
}
This blog post explains it really well. Due to the fact that aspect singleton is created outside spring container you'd need to use factory-method=”aspectOf” that is only available after it is woven in by AspectJ ( not Spring AOP ) :
Notice factory-method=”aspectOf” that tells Spring to use a real
AspectJ ( not Spring AOP ) aspect to create this bean. So that after
the aspect is woven in it has an
“aspectOf” method.
So that :
No matching factory method found: factory method 'aspectOf()' - That
would mean that the aspect was not woven by AspectJ weaver.
From my experience with spring 3.1, if I don't use #Autowired but traditional setter for dependency injection, it gets injected and works as expected without aspectJ weaver. Although I'm encountering problems with the aspect being singleton... It results in 'perthis' instantiation model.
.
Add #Component to aspect class and your dependencies should get injected automatically.
and add context:component-scan for package where your aspect is in spring context file.
#Component
#Aspect
public class SomeAspect {
/* following dependency should get injected */
#Autowired
SomeTask someTask;
/* rest of code */
}
Use compile time weaving, see for plugin example at: https://github.com/avner-levy/minimal_spring_hibernate_maven_setup/blob/master/pom.xml
The following combination of annotation and Spring config works for me thanks to notes above by Tobias/Willie/Eric:
Class:
package com.abc
#Configurable
#Aspect
public class MyAspect {
#Autowired
protected SomeType someAutoWiredField;
}
XML:
<context:spring-configured />
<context:component-scan base-package="com.abc" />
#Configurable(autowire = Autowire.BY_TYPE)
Add this annotation to your Aspectj class. Then it will be handled by Spring IOC.
For Spring Boot using #Autowired in #Aspect,
my way is using spring.factories configuration file
create a file named spring.factories in src/main/resources
the file content is as following
org.springframework.boot.autoconfigure.EnableAutoConfiguration=pack.age.to.YourAspect,pack.age.to.YourDAO

Spring AOP annotation based pointcuts for ElementType.TYPE?

I have a service implementation carrying a class-wide #Transactional annotation. I also have an aspect that uses the #Around advice to retry failed transactions. I'm now trying (for type-safety reasons) to make the pointcut definition annotation based:
#Around("#annotation(TransactionRetryable)")
TransactionRetryable.java:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import java.lang.annotation.ElementType;
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.TYPE})
public #interface TransactionRetryable {
}
Service layer:
#Transactional
public class ... {
#Override
#TransactionRetryable
public String forceError() {
throw new RuntimeException(someNastyMessage);
}
In that form, it only applies to method, not whole classes. However, the #Transactional annotation propagates from class level to each method. Is there a way to avoid putting the #TransactionRetryable annotation above each method and simply once above the class like the #Transactional annotation? Desired form:
#Transactional
#TransactionRetryable
public class ... {
#Override
public String forceError() {
throw new RuntimeException(someNastyMessage);
}
This pointcut would advise all public methods of a class annotated with #TransactionRetryable:
#Around("execution(public * *(..)) && within(#your.package.TransactionRetryable *)")

Jersey;: Registering Injected ExceptionMapper Instances via SpringServlet

Jersey: 1.12
Spring: 3.11
JDK: 1.6.0_35
Tomcat: 6..0.33
I am trying to register ExceptionMapper instances for a Jersey servlet that are instantiated by a Spring IoC container in the context of a SpringServlet, and am running into ye olde "The scope of the component class X must be a singleton" exception during the Jersey servlet initialization. I'm not in a position to post the actual code at this time, so I'm going to give the essentials of the implementation to see if something obvious jumps out at someone, or if I can be pointed at some documentation that I have missed.
The ExceptionMapper instance is essentially as follows:
// ... package definitions omitted ...
#Provider
#Singleton
public class MyExceptionMapper
implements ExceptionMapper<MyException>
{
// ... injected attributes omitted ...
#Override
public Response toResponse(MyException exception)
{
// ... specific handling logic omitted ...
return Response.status(Status.BAD_REQUEST).entity(exception.getMessage()).build();
}
}
The bean definition in applicationContext.xml is as follows, which defaults to singleton scope (and adding an explicit scope specifier doesn't seem to change the behavior):
<bean id="myExceptionMapper" class="my.package.MyExceptionMapper" />
The Spring context loader listener and SpringServlet configuration in web.xml are essentially boilerplate, and the servlet will load, initialize and operate properly with other inject attributes when the bean definition for MyExceptionMapper is commented out of the applicationContext.xml. But when the bean definition is present in applicationContext.xml, I get log messages to the effect of:
SpringProviderFactory - Registering Spring bean, myExpectionMapper, of type my.package.MyExceptionMapper as a provider class
SpringProviderFactory - Registering Spring bean, myServiceController, of type my.package.MyServiceController as a root resource class
... other root resource classes loading ...
SpringServlet - Exception occurred when initialization
java.lang.RuntimeException: The scope of the component class my.package.MyExceptionMapper must be a singleton
at som.sun.jersey.core.spi.component.ioc.IoCProviderFactory.wrap(...)
I have tried placing MyExceptionMapper in the scanned packages hierarchy to be picked up by the SpringServlet during initialization, and in a separate package hierarchy, and the results do not change.
Any assistance or guidance on this would be greatly appreciated.
My ExceptionMappers are under the structure where I'm doing component scan, and mine are simply annotated with #Component for Spring registry and #Provider for Jersey.
My component scan just looks like:
<context:component-scan base-package="com.company.web"/>
and the ExceptionMapper looks pretty much like yours:
package com.mycompany.web.provider;
import com.mycompany.exception.SpecialException;
import com.mycompany.exception.Error;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.ws.rs.core.MediaType;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Provider
#Component
public class SpecialExceptionMapper implements ExceptionMapper<SpecialException> {
#Autowired
private MessageSource messageSource;
#Override
public Response toResponse(SpecialException exception) {
Error error = new Error();
error.errorMessage = messageSource.getMessage(exception.getResourceBundleKey(), exception.getArgs());
return Response.status(Response.Status.BAD_REQUEST).
entity(error).
type(MediaType.APPLICATION_JSON).
build();
}
}
Adding the scope explicitly in the spring config did the trick for me:
<bean id="exceptionMapper" class="my.pkg.MyExceptionMapper" scope="singleton" />
Stepped on same problem with Spring 3.2.3 and Jersey 2.3.1 integration.
What worked for me was combination of two:
Annotating jersey #Provider class with #Service or making it explicitly singleton in app-context xml
#Service //implies a singleton-scope
#Provider
public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) { //...
Adding package that contains provider class to jersey packages, I prefer to do this in MyApplication class:
public class MyApplication extends ResourceConfig {
public MyApplication() {
packages("com.mycompany.api.controller", "com.mycompany.api.jersey");
//....
}
}
I had the same issue, but with a MessageReaderWriterProvider class:
package my.provider.package;
...
#Provider
#Consumes({MediaType.APPLICATION_JSON, "text/json"})
#Produces({MediaType.APPLICATION_JSON, "text/json"})
public class ReRwProvider extends AbstractMessageReaderWriterProvider<Object> {
...
}
The solution was to add the package of this class in an init-param tag for Jersey's SpringServlet in web.xml:
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>my.provider.package</param-value>
</init-param>
Note that you should not define this bean in your Spring context.
Looks to me that the ExceptionMapper has to be configured as a "singleton" with Spring. If you want to use the annotation configuration you have to pay attentions because jersey has a "#Singleton" annotation too (with a different meaning of the javax.inject.Singleton interpreted by spring)
To avoid any confusion I prefer use the spring #Service annotation, that implies a singleton scope... so:
import org.springframework.stereotype.Service;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
import javax.ws.rs.ext.Provider;
#Service //implies a singleton-scope
#Provider
public class UnhandledExceptionMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) {
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity(new ErrorBean(false, exception.getMessage(), null))
.build();
}
}
I also found sometime coustom ExceptionMapper not work, and it's not always work or not work.
so i debug the jersey's source code .
class: org.glassfish.jersey.server.ServerRuntime, methed:mapException
...
final long timestamp = tracingLogger.timestamp(ServerTraceEvent.EXCEPTION_MAPPING);
**ExceptionMapper mapper = runtime.exceptionMappers.findMapping(throwable);**
if (mapper != null) {
request.getRequestEventBuilder().setExceptionMapper(mapper);
...
if the mapper is null, the coustom ExceptionMapper will be not work.
class: org.glassfish.jersey.internal.ExceptionMapperFactory methed: ExceptionMapperFactory
the Exception Mapper :(there two Exception mapping one same Exception: java.lang.Exception)
org.glassfish.jersey.server.mvc.internal.ErrorTemplateExceptionMapper#6473fc2,class java.lang.Exception
...
com.baidu.ssp.web.ws.exception.BaseExceptionMapper#7a84639c,class java.lang.Exception
it's because in MvcFeature:
#Override
public boolean configure(final FeatureContext context) {
final Configuration config = context.getConfiguration();
if (!config.isRegistered(ErrorTemplateExceptionMapper.class)) {
context.register(ErrorTemplateExceptionMapper.class);
context.register(new MvcBinder());
return true;
}
return false;
}
the ErrorTemplateExceptionMapper is also add to ExceptionMapper.
so i change my custom MapperException's genericity type : ExceptionMapper to ExceptionMapper
may be my resolution is not fit for you , the main problem is the ExceptionMapper

Resources