how to create annotation in spring - spring-boot

#Target({ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Import({
testMethod.class
})
public #interface test{
public String value() default "";
}
#Component
public class testMethod{
...
}
in my controller, I want to use the annotation I created
#test
#RequestMapping(...)
public response getAll(){
...}
I put break point in the testMethod, and it could not hit the break point. It seems like it could not find the testMethod component.

You need to import a Configuration class. As documented #Import annotation used to import a configuration as follows.
Indicates one or more {#link Configuration #Configuration} classes to import.
public class TestBean {
public TestBean() {
}
}
#Configuration
public class TestMethod {
#Bean
public TestBean testBean() {
return new TestBean(); //put break-point here
}
}

Related

Cannot Mock an Injected Dependency in Controller Constructor

I am having a problem with mocking an object being injected in to a controller.
I am running an integration test. In my test I have the following setup:
#RunWith(SpringRunner.class)
#SpringBootTest
public class AuthenticationTests {
#TestConfiguration
public class Config {
#Bean
#Primary
public AbstractClient client() {
return new AbstractClient() {
#Override
public ManagedChannel getChannel() {
return new ManagedChannel();
}
};
}
}
}
In the controller being tested, AbstractClient is dependency injected like so:
#Controller
public class MyController {
private ManagedChannel managedChannel;
public MyController(AbstractClient client) {
managedChannel = client.getChannel();
}
}
Whenever I run the test, AbstractClient defined in the #TestConfiguration class is never injected - instead the default one is (which is annotated as #Service).
Can anyone help?

MethodValidationPostProcessor breaks #Transactional

I have a Controller which calls a Service which has #Transactional annotation.
But when I declare a bean MethodValidationPostProcessor, no transaction is created (could not initialize proxy - no Session).
#EnableWebMvc
#ComponentScan(basePackages = {"my"})
public class Application extends WebMvcConfigurerAdapter {
#Bean
public MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}
}
Controller bean:
#RestController
#RequestMapping(path = "/my", produces = APPLICATION_JSON_VALUE)
public class MyController {
#Autowired
private TransactionalService transactionalService;
#RequestMapping(method = POST)
public void post(#SafeHtml #RequestBody String hey) {
transactionalService.doStuff(hey);
}
}
Service bean:
#Service
public class TransactionalService {
#PersistenceContext
private EntityManager entityManager;
#Transactional
public void doStuff(String hey) {
Item h = entityManager.find(Item.class, hey);
h.getParent(); // could not initialize proxy - no Session
}
}
I'd like to understand why #Transactional doesn't work when I declare MethodValidationPostProcessor. Thanks !
Note: If I add #Transactional on my Controller, it works. But it's not what I want to do.
Thanks to #Kakawait, I got a work-around: declaring my bean MethodValidationPostProcessor. Needs to be static so that #Transactional still work properly.
/**
* This bean must be static, to be instantiated before the other MethodValidationPostProcessors.
* Otherwise, some are not instantiated.
*/
#Bean
public static MethodValidationPostProcessor methodValidationPostProcessor() {
return new MethodValidationPostProcessor();
}

Can't make Spring's ImportAware to work

I am trying to create my own #EnableXxx-style annotation (namely #EnableCustomizedPropertySources). For this the annotation imports the class CustomizedPropertySourcesConfiguration which in turn implements ImportAware, in order to have access to attributes of the #EnableCustomizedPropertySources annotation.
Annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Import(CustomizedPropertySourcesConfiguration.class)
public #interface EnableCustomizedPropertySources {
String externalFolderName();
String propertiesFileName();
(...)
}
Imported configuration class:
#Configuration
public class CustomizedPropertySourcesConfiguration implements ImportAware {
protected AnnotationAttributes enableCustomizedPropertySourcesAttributes;
#Override
public void setImportMetadata(AnnotationMetadata importMetadata) {
Map<String, Object> attributes = importMetadata.getAnnotationAttributes(EnableCustomizedPropertySources.class.getName(), false);
this.enableCustomizedPropertySourcesAttributes = AnnotationAttributes.fromMap(attributes);
}
#Bean
public PropertySourcesPlaceholderConfigurer propertySource() {
return (...);
}
}
The problem is, the method setImportMetadata is not invoked by Spring when I annotate some #Configuration class with my #EnableCustomizedPropertySources annotation, so I cannot access the annotations attributes.
The ImportAware class (CustomizedPropertySourcesConfiguration) needs both:
#Configuration
#Component

Spring: Injecting property as a value into an annotation

I am trying to inject a value into a Custom Annotation but Spring doesn't seem to be evaluating.
Here is my annotation:
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MyCustomAnno {
String var1();
String var2();
}
Here is my Bean (it is created in a Spring Configuration file) with Annotation:
public class MyClass {
#MyCustomAnno(var1 = "${some.property.one}",
var2 = "${some.property.two}")
public void someMethod() {
// do something here
}
}
Here is the Aspect where I am trying to use the values passed into the annotation:
#Aspect
public class MyAop {
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public Object aroundMethod(MyCustomAnno myCustomAnno) {
int intVar1 = Integer.parseInt(myCustomAnno.var1());
int intVar2 = Integer.parseInt(myCustomAnno.var2());
// ....
}
}
In the around method I am receiving a NumberFormatException: For input string: ${some.property.one}. This means that Spring didn't inject the value for some reason.
In case you are wondering, in the same class I can do the normal #Value annotation and the value gets injected properly:
#Value("${some.property.one}")
private propertyOne; // This works
Is it possible to do what I want to and if so, how?
AFAIK, placeholders are not resolved in custom annotations. However you could resolve them in the Aspect itself.
For example:
#Aspect
class MyAop implements EmbeddedValueResolverAware {
private StringValueResolver resolver;
#Around(value="#annotation(myCustomAnno)",argNames="myCustomAnno")
public void play(MyCustomAnno ann) {
System.out.println(resolver.resolveStringValue(ann.var1()));
}
#Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.resolver = resolver;
}
}

#Specializes in Spring

CDI has the feature of Specialization, and I'm looking for that in the Spring world.
Details.
In CDI, the #Specializes annotation allows one to change the behaviour of a bean just by overriding it. This is completely transparent to users of that bean, e.g. if we'd have
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
#Specializes
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
we could
public class SomewhereElse {
#Inject
OneBean oneBean; // we know nothing of AnotherBean here!
public void guessWhosThere() {
return oneBean.whoAmI(); // yet it returns "AnotherBean"
}
}
This gets really useful as soon as OneBean is actually used with and without AnotherBean. For example, if OneBean is in one.jar and AnotherBean is in another.jar, we can change the bean's behaviour just by reconfiguring the classpath.
Question. Does something like Specialization also exist in Spring?
I could only find the #Primary annotation, which however has a different semantics: #Primary does not replace one bean, but only marks one of multiple alternatives as the primary one. Especially, as I understood, I could not build a deep inheritance hierarchy as it's possible with #Specializes.
Short answer
In Spring 4, this is not possible. Period. Still, in 2016, nothing like this is possible with Spring's obsolete dependency injection model.
Seems like there is no similar annotation in spring, but you can achive it via #Qualifier.
Beans:
#Resource("oneBean")
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
#Resource("anotherBean")
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
SomewhereElse:
public class SomewhereElse {
#Autowired
#Qualifier("anotherBean")
OneBean oneBean;
public void guessWhosThere() {
return oneBean.whoAmI(); // returns "AnotherBean"
}
}
Edited.
Also you can develop your own annotation and use it in BeanPostProcessor, look at spring docs here
OR even better to use CustomAutowireConfigurer, see here
With Spring boot, you could probably get a similar result by leveraging its auto-configure mechanism, e.g. with a bean condition such as #ConditionalOnMissingBean:
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
#Configuration
public class OneConfiguration {
#Bean
#ConditionalOnMissingBean
public OneBean getBean() { return new OneBean(); }
}
#Component
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
However, you would have to make sure that all configurations are built accordingly if you don't know for sure which ones will be specialized:
public class OneBean {
public String whoAmI() { return "OneBean"; }
}
public class AnotherBean extends OneBean {
#Override
public String whoAmI() { return "AnotherBean"; }
}
public class YetAnotherBean extends AnotherBean {
#Override
public String whoAmI() { return "YetAnotherBean"; }
}
#Configuration
public class OneConfiguration {
#Bean
#ConditionalOnMissingBean
public OneBean getBean() { return new OneBean(); }
}
#Configuration
public class AnotherConfiguration {
#Bean
#ConditionalOnMissingBean
public AnotherBean getBean() { return new AnotherBean(); }
}
#Configuration
public class YetAnotherConfiguration {
#Bean
#ConditionalOnMissingBean
public YetAnotherBean getBean() { return new YetAnotherBean(); }
}
// and so on...

Resources