AOP with Spring controller Function using customer Annotations - java

I have a use case where I want to intercept the Spring controller functions that are annotated with my Custom annotation. Let's call my annotation #CustomerAnnotation.
I have a controller MyController
public class MyController extends Controller {
#CustomerAnnotation
#RequestMappint("/test")
public void test() {
// SOME CODE
}
My AspectJ class:
#Aspect
#Component
public class CustomImpl implements CustomAspect {
#Around("#annotation(CustomerAnnotation)")
#Override
public Object testAnnotation(ProceedingJoinPoint proceedingJoinPoint) {
System.out.println("Inside annotaion");
try {
Object returnObj = proceedingJoinPoint.proceed();
return returnObj;
} catch (Throwable e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
};
}
Bu whenever I call my API i get the following exception :
The mapped controller method class 'com.controller.MyController' is not an instance of the actual controller bean instance 'com.sun.proxy.$Proxy154'. If the controller requires proxying (e.g. due to #Transactional), please use class-based proxying.
HandlerMethod details:
My CustomerAnnotation class:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface CustomerAnnotation {
}
Any solutions ?
I have tried using the same logic on my service functions and it works there.
Update: When I add #EnableAspectJAutoProxy(proxyTargetClass = true) it gives following exception
Caused by: org.springframework.aop.framework.AopConfigException: Could not generate CGLIB subclass of class
Please help

If you are using JavaConfig use "#EnableAspectJAutoProxy" from "import org.springframework.context.annotation.EnableAspectJAutoProxy" at class level in your configuration class.

Related

Spring inherited #Component with constructor arguments

I have a service which needs to create Agents on the runtime. Agents inherit from a base Agent class. I would like to use the Autowired ability of spring instead of doing my own dependency injections.
But I am running into this issue, even though I am marking the component as scope=prototype, and even #Lazy to prevent anything from happening at compile-time.
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.my.project.AgentType1 required a bean of type 'com.my.project.POJO' that could not be found.
This is the service that tries to create the agents:
#Service
public class ProjectMain {
#Autowired
ApplicationContext context;
List<IAgent> agents = new ArrayList<>();
void SetupAgents(List<POJO> agentPojos) {
for(POJO agentPojo: agentPojos) {
IAgent agent = AgentFactory.CreateAgent(agentPojo, context);
agents.add(agent);
}
}
}
This is the factory class, not marked as #Component etc. It uses the context passed to it to create the child class beans. It tries to pass the constructor argument via the getBean method.
public class AgentFactory {
public static IAgent CreateAgent(POJO agentPojo, ApplicationContext context) {
if (agentPojo.type.equals("AgentType1")) {
return context.getBean(AgentType1.class, agentPojo);
} else {
return context.getBean(AgentType2.class, agentPojo);
}
}
}
This is a custom annotation which I found is needed for inheritance scenarios.
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Component
#Inherited
#Lazy
#Scope("prototype")
public #interface AgentAnnotation {}
These are the base and child agent classes, which need a custom data structure called POJO to work.
#AgentAnnotation
public class BaseAgent implements IAgent {
#Autowired
Environment env;
public BaseAgent(POJO agentPojo, String someotherdata) {
}
}
public class AgentType1 extends BaseAgent {
public AgentType1(POJO agentPojo) {
super(agentPojo, "mydata1");
...
}
}
public class AgentType2 extends BaseAgent {
public AgentType2(POJO agentPojo) {
super(agentPojo, "mydata2");
...
}
}
This is the starter app.
#ComponentScan(basePackages = "com.my.project", includeFilters = #ComponentScan.Filter(AgentAnnotation.class))
#EnableScheduling
#SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
I also tried the configuration approach:
#Configuration
public class BaseAgentConfig {
#Bean
#Scope("prototype")
public AgentType1 agentType1(POJO agentPojo) {
return new AgentType1(agentPojo);
}
#Bean
#Scope("prototype")
public AgentType2 agentType2(POJO agentPojo) {
return new AgentType2(agentPojo);
}
}
In this case, I removed the #AgentAnnotation from the baseAgent class as we are now instantiating through this config. Also removed the ComponentScan line from the main App.
This time around, the #Autowired doesn't work. All Autowired references in the baseAgent class are null.
Please advise on the best approach to solve this error. Thanks.
Found the issue and solution.
Basically, I was expecting child classes to inherit #Component and #Scope, which it doesn't.
So essentially, I need to annotate each child class with #Component and #Scope("prototype").
The other problem was that I was expecting Autowired items in the constructor, which was too early. Adding a #PostConstruct addressed that issue.
So I ended up deleting the custom annotation and the configuration class and making the changes I just described.

Why are #Bean Generics creation methods on a superclass called later than ones on the child-class in Spring Boot?

I have a spring boot base abstract config class that creates a bean. If I then inherit from it, the bean will be created later than my controller (which needs to auto-wire it and thus fails). Note: it does get created, just after the controller. So it can't be auto-wired but has to be found via appContext.getBean( BeanType.class )
If I instead override the bean method in the child class, then it's created before the controller and it can be auto-wired.
How can i fix this and make the super-class bean definition load at the same time as the child class?
#SpringBootApplication
public class ChildConfig extends ParentConfig<PCTestBean>
{
public ChildConfig()
{
super();
}
#Override
public PCTestBean getT()
{
return new PCTestBean();
}
}
public abstract class ParentConfig<T>
{
public ParentConfig() {}
#Bean
public T createTestBean()
{
return getT();
}
public abstract T getT();
}
public class PCTestBean
{
}
#RestController
#RequestMapping( "/client" )
public class MyController
{
#Autowired
private PCTestBean pcTestBean;
#RequestMapping( "/get" )
#ResponseBody
public String getClient(HttpServletRequest request) throws Exception
{
return pcTestBean.toString();
}
}
#RunWith( SpringJUnit4ClassRunner.class )
#SpringBootTest(
webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT
)
#ContextConfiguration(
classes = {
ChildConfig.class
}
)
public class TestConfigs
{
#LocalServerPort
private String port;
private MockMvc mockMvc;
#Autowired
private WebApplicationContext context;
#Before
public void setUp() throws Exception
{
mockMvc = MockMvcBuilders
.webAppContextSetup( context )
.build();
}
#Test
public void testValidCall() throws Exception
{
MvcResult result = mockMvc.perform(
MockMvcRequestBuilders.get( new URI( "http://localhost:" + port + "/client/get" ) )
)
.andExpect( MockMvcResultMatchers.status().isOk() ).andReturn();
System.out.println( result.getResponse().getContentAsString() );
}
}
When Spring scans your configuration class, ChildConfig, it discovers this inherited method
#Bean
public T createTestBean() {
return getT();
}
and registers a bean definition for it. That bean definition contains metadata about the type of the bean. That type is inferred from the return type of the method. In this case, it's resolved to Object because the type variable T has no bounds in its declaration and because Spring doesn't try to resolve it based on the type argument provided in ChildConfig's extends ParentConfig<PCTestBean> clause.
When Spring then tries to process the
#Autowired
private PCTestBean pcTestBean;
injection target, it looks for a PCTestBean bean, which it doesn't think it has, because the metadata is lacking. IF the bean hasn't been initialized through some other forced order, then Spring has no other information to go on and thinks the bean doesn't exist.
When you change your code to
instead override the bean method in the child class
the return type of the method is PCTestBean which Spring can then match to the #Autowired injection requirement, find (and initialize) the bean, and inject it.
By the time you use ApplicationContext#getBean(Class), the PCTestBean has been initialized. Spring can therefore rely on the actual type of the instance. It'll more or less loop through all beans and check whether beanClass.isInstance(eachBean), returning the one that matches (or failing if more than one does).
Pankaj, in their answer, suggests using #DependsOn (it was wrong when they suggested it, before you edited your question). That can help establish the order I mentioned earlier.
I don't how extensive your configuration class is that you think you need generics to abstract some behavior away, but I would suggest just dropping the generic behavior and be explicit in each class.
Try DependsOn annotation, it guarantees that the child bean should be created after the parent bean
#Configuration
public class ChildConfig extends ParentConfig
{
public ChildConfig()
{
super();
}
#DependsOn("parentConfig")
#Override
public TestBean createTestBean()
{
return super.createTestBean();
}*/
}
public abstract class ParentConfig
{
public ParentConfig() {}
#Bean (name ="parentConfig")
public TestBean createTestBean()
{
return new TestBean();
}
}

How to provide a #ControllerAdvice conditional on bean?

I'm creating a framework that I'd like to reuse in all of my future projects. If the sub project does not define a #ControllerAdvice, I want my framework to automatically initialize a default Advice for exception handling.
public class ExHandler implements IAdvice {
ExceptionHandler(Exception.class)
#ResponseStatus(HttpStatus.NOT_FOUND)
#ResponseBody
public ErrorDTO default(xception e) {
return new ErrorDTO();
}
}
I tried as follows, but does not work:
#Configuration
static class MyConfig {
#ControllerAdvice
#ConditionalOnMissingBean(IAdvice.class)
static class AdviceExHandler extends ExHandler {
}
}
In Sub project:
#ControllerAdvice
public class SubHandler extends ExHandler {
}
Result: It works. BUT: if the subproject does not define the ExHandler, the bean is not initialized at all! But why?
Sidenote: I'm trying to prevent mutliple ControllerAdvice because error handling depends on the order of methods inside the exception handler. Thus I don't want to mess the order by introducing multiple classes.
You may use #ConditionalOnMissingBean(annotation = ControllerAdvice.class) to configure condition on missing bean with ControllerAdvice annotation.
#ControllerAdvice
public abstract class FrameworkAdvice {
...
}
And conditionally configure it:
#Configuration
#ConditionalOnMissingBean(annotation = ControllerAdvice.class)
public class FrameworkAdviceConfig {
#Bean
public FrameworkAdvice frameworkAdvice() {
return new FrameworkAdvice() {
};
}
}
And if there is another controller advice in project, it will be used instead.
#ControllerAdvice
public class CustomAdvice {
...
}
You could use it without wrappers. Just declare #ControllerAdvice annotated class as following in Kotlin:
#ControllerAdvice
#ConditionalOnMissingBean(annotation = [ControllerAdvice::class])
class ExceptionHandler {
#ResponseStatus(HttpStatus.EXPECTATION_FAILED)
#ResponseBody
#ExceptionHandler
fun handleProcessingException(e: Exception): ErrorDto {
return ErrorDto()
}
}
And just declare it in spring.factories file if you're doing it for starter:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
ru.raiffeisen.ecommerce.controller.ExceptionHandler
That's all. If there is no #ControllerAdvice annotated classes, then will be used class from configuration.

How to test if #Valid annotation is working?

I have the following unit test:
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration(classes = {EqualblogApplication.class})
#WebAppConfiguration
#TestPropertySource("classpath:application-test.properties")
public class PostServiceTest {
// ...
#Test(expected = ConstraintViolationException.class)
public void testInvalidTitle() {
postService.save(new Post()); // no title
}
}
The code for save in PostService is:
public Post save(#Valid Post post) {
return postRepository.save(post);
}
The Post class is marked with #NotNull in most fields.
The problem is: no validation exception is thrown.
However, this happens only in testing. Using the application normally runs the validation and throws the exception.
Note: I would like to do it automatically (on save) and not by manually validating and then saving (since it's more realistic).
This solution works with Spring 5. It should work with Spring 4 as well. (I've tested it on Spring 5 and SpringBoot 2.0.0).
There are three things that have to be there:
in the test class, provide a bean for method validation (PostServiceTest in your example)
Like this:
#TestConfiguration
static class TestContextConfiguration {
#Bean
public MethodValidationPostProcessor bean() {
return new MethodValidationPostProcessor();
}
}
in the class that has #Valid annotations on method, you also need to annotate it with #Validated (org.springframework.validation.annotation.Validated) on the class level!
Like this:
#Validated
class PostService {
public Post save(#Valid Post post) {
return postRepository.save(post);
}
}
You have to have a Bean Validation 1.1 provider (such as Hibernate Validator 5.x) in the classpath. The actual provider will be autodetected by Spring and automatically adapted.
More details in MethodValidationPostProcessor documentation
Hope that helps
This is how I did it by loading ValidationAutoConfiguration.class into context:
#SpringBootTest
#ContextConfiguration(classes = { MyComponent.class, ValidationAutoConfiguration.class
public class MyComponentValidationTest {
#Autowired
private MyComponent myComponent;
#Test
void myValidationTest() {
String input = ...;
// static import from org.assertj.core.api.Assertions
assertThatThrownBy(() -> myComponent.myValidatedMethod(input))
.isInstanceOf(ConstraintViolationException.class)
.hasMessageContaining("my error message");
}
}
And MyComponent class:
#Component
#Validated
public class MyComponent {
public void myValidatedMethod(#Size(min = 1, max = 30) String input) {
// method body
}
)

#Producer annotation in Java EE

I am learning Java EE CDI, dependency injection, and #Produces in particular. I am wondering why in getGreedingCard() method, it needs a #Produces annotation at all, since the two classes GreetingCardImpl and AnotherGreetingCardImpl are already imported into the space. This is just like the regular package/class dependency and a simple import solves the problem. Why does it need a dependency injection through a #producer annotation?
Thanks for explanation in advance.
public interface GreetingCard {
void sayHello();
}
public class GreetingCardImpl implements GreetingCard {
public void sayHello() {
System.out.println("Hello!!!");
}
}
public class AnotherGreetingCardImpl implements GreetingCard {
public void sayHello() {
System.out.println("Have a nice day!!!");
}
}
import com.javacodegeeks.snippets.enterprise.cdibeans.impl.AnotherGreetingCardImpl;
import com.javacodegeeks.snippets.enterprise.cdibeans.impl.GreetingCardImpl;
#SessionScoped
public class GreetingCardFactory implements Serializable {
private GreetingType greetingType;
#Produces
public GreetingCard getGreetingCard() {
switch (greetingType) {
case HELLO:
return new GreetingCardImpl();
case ANOTHER_HI:
return new AnotherGreetingCardImpl();
default:
return new GreetingCardImpl();
}
}
}
I am wondering why in getGreedingCard() method, it needs a #Produces
annotation at all, since the two classes GreetingCardImpl and
AnotherGreetingCardImpl are already imported into the space.
Well, it's not that getGreetingCard needs the #Produces annotation. The point is to enable other classes to recieve GreetingCards via Dependency Injection.
public class Foo {
#Inject // <--- will invoke #Producer method
GreetingCard foosGreetingCard
...
}
See here for more details:
A producer method is a method that acts as a source of bean instances.
The method declaration itself describes the bean and the container
invokes the method to obtain an instance of the bean when no instance
exists in the specified context.
In your case it doesn't need #Produces as you will be injecting factory bean and using its method directly to create instances, and not injecting the greetingCard beans themseleves.
#Inject
GreetingCardFactory factory;
...
GreetingCard card = factory.getGreetingCard();
If you would define it as #Produces method, and the try to inject GreetingCard, then you would get exception that I've described in the comment.
However, if you would additionally create qualifier, like this:
#Qualifier
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE})
public #interface ProducedCard {}
and add it to the producer method:
#Produces #ProducedCard
public GreetingCard getGreetingCard() {
...
then you would be able to inject just GreetingCard beans using your producer method like this:
#Inject #ProducedCard
GreetingCard card;
since now there is no ambiguity, as there is only one place to create greeting cards marked with #ProducedCard :-)

Categories