Spring - how to rename #Bean - java

How do you define an annotation in Spring that is semantically equivalent to #Bean, but has another name?
(Why? I'm building a DSL in which the functionality would fit, but it would greatly benefit from naming the annotation more closely to the role it plays in the library).

Well... It was as easy as:
#Target({ElementType.METHOD, ElementType.ANNOTATION_TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Bean
public #interface CustomAnnotation {
}

Related

Why is Spring's #Component Annotation RUNTIME?

I am wondering, why Spring's implementation of the #Component Annotation has RetentionPolicy.RUNTIME.
Here's the official implementation from github
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Indexed
public #interface Component {
String value() default "";
}
In my thought, the scanning for all Spring-Components annotated with #Component would be done at compile-time and all the results (annotated classes) would somehow be stored inside the ApplicationContext. I guess I am wrong, but why does this annotation need to be RetentionPolicy.Runtime?
The component scanning is done at the startup, not at the compilation.
Also, the aspects (AOP) are created at the runtime

Executing Spring AOP aspects for annotations aliased with #AliasFor

The idea is to create annotations hierarchy (similar to #Service, #Component etc) using #AliasFor annotation. This should give me the possibility to define aspect, that would execute on parent annotation, and every alias of it. But somehow it doesn't work for me.
#ComponentScan is fine, #EnableAspectJAutoProxy is set.
Example:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.TYPE, ElementType.METHOD})
public #interface ParentAnnotation {
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
#ParentAnnotation
public #interface ChildAnnotation {
#AliasFor(annotation = ParentAnnotation.class)
String value() default "";
}
#Aspect
#Component
public class EventRecorderAspect {
#Around("#annotation(com.example.ParentAnnotation)")
public void exampleMethod(ProceedingJoinPoint joinPoint) throws Throwable {
// This should be executed for both #ParentAnnotation and #ChildAnnotation
}
}
#RestController
public class ExampleController {
#ChildAnnotation // This should result in executing aspect for every implementation
String controllerMethod();
}
UPDATE:
I've updated code, as #M.Deinum suggested in a comment below. But it still doesnt work.
AspectJ pointcut matching syntax, a subset of which is used by Spring AOP, is ignorant of meta annotations, even though withing the Spring framework as such there is support for it in other places. I think the closest you can get to specifying meta annotations in your pointcut is to do it explicitly up to the nesting level you require. See this answer for examples showing the syntax variants for both
class-level, e.g. within(#(#com.example.ParentAnnotation *) *),
method-level, e.g. execution(#(#com.example.ParentAnnotation *) * *(..))
annotations.
Update: #Ariel Grabijas asked in a follow-up comment:
So is there a way to somehow inherit annotations from interface method to class method?
Not in Java and also not by means of Spring AOP. But there is a workaround using native AspectJ inter-type definitions (ITD).

Spring Security add multiple meta annotation

I created a Spring Boot 2.3 application. I securited my REST endpoint and my services.
I created some meta annotations because I have many role. For example:
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole('ROLE_CUSTOMER')")
public #interface IsCustomerUser {
}
and
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#PreAuthorize("hasAnyRole('ROLE_API')")
public #interface IsApiUser {
}
I've several of this meta annotations with many other roles. I thought I could add multiple meta annotations on my endpoints like this:
#IsCustomerUser
#IsApiUser
#GetMapping(path = "/tenants/current/avatar/{version}")
public ResponseEntity<?> getAvatar(#PathVariable("version") long version) {
//TODO some stuff
}
but it seems only one annotation is evalued. I see there is a feature request here. I'd like to know if you have some workaround in order to add multiple meta annotations.
It is very annoying be forced to create all permutations of my basic security annotation.
In fact I should create something like #IsAdminOrCustomerOrApiOrManagerOr.... and it's not really convenient.

Spring - differences between #Named and #Component

I am trying to understand the differences between these two annotations and how they affect injection in Spring. Consider the following piece of code -
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface ExternalPropertiesHolder {}
When I mark a class with this annotation -
#ExternalPropertiesHolder
public class SomeProperties {}
and then this dependency is injected using #Inject, it works perfectly -
#Service
public class SomeService {
private SomeProperties someProperties;
#Inject
public SomeService(SomeProperties someProperties) {
this.someProperties = someProperties;
}
}
However, when I replace #Component with #Named -
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Named // --> Here!
public #interface ExternalPropertiesHolder {}
Then the injection fails with the usual bean not found exception -
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type [com.hogehoge.SomeProperties] found for
dependency: expected at least 1 bean which qualifies as autowire
candidate for this dependency. Dependency annotations: {}
I searched the Spring reference documentation, and all it has to say about the difference is this -
JSR-330 does not provide a composable model, just a way to identify
named components.
What does that mean? Does it mean that I cannot use #Named to compose a custom marker like this? Or is there something else?
P.S.: Of course by #Component I am referring to org.springframework.stereotype.Component and by #Named I am referring to javax.inject.Named.
So I got the answer directly from Juergen Hoeller. According to him, this line -
JSR-330 does not provide a composable model, just a way to identify
named components.
means that the javax.inject.Named can only be declared directly on a given bean class. The composable annotation story just works with Spring's own annotations, which is exactly what I suspected.
Correct, javax.inject.Named and javax.anotations.ManagedBean doesn't provide a composable model. Thus, can't be used with the same intent as org.springframework.stereotype.Component.
But i could see from the documentation, we could use javax.inject.Qualifier in this use-case, as it's meant to be used for defining custom annotations.
Did you gave #Qualifier from javax a try ?
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Qualifier // --> from javax.inject
public #interface ExternalPropertiesHolder {}
Even the #Named is defined using #Qualifier.
#Qualifier
#Documented
#Retention(RUNTIME)
public #interface Named {
/** The name. */
String value() default "";
}

Is it possible to create my own Custom Annotation that act as Spring #Controller annotation?

In my Spring application I have controllers with two annotations:
#Controller - Spring annotation
#AdminPanelController - my annotation
Is it possible change my annotation so I can used if with out the need to place #Controller in addition?
I want that Spring will process my annotation as #Controller annotation.
Your question is missing some detailed explanation on your needs, but my assumption is that you do not want to bother putting both your annotation and #Controller on your admin panel controllers. You want Spring-MVC you understand that any #AdminPanelController is-a #Controller.
This is exactly what #RestController annotation in Spring 4.0 any #RestController is-a #Controller (See the source code).
So your #AdminPanelController annotation should be similar to the one below
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Controller
public #interface AdminPanelController {
String value() default "";
}
So although java annotation does not support inheritance in the sense we expect (see Why is not possible to extend annotations in Java?) this will work for spring-mvc thanks to meta-annotation (I tested this with 4.1).
I'm reasonably sure that all you need is to annotate the AdminPanelController with #Controller

Categories

Resources