Adding and implementing interfaces with Annotations and Spring AOP? - java

is it possible to implement an interface using Annotaitons/Spring AOP/Proxies? For example, I have the simplified interface Foo. The first and second method usually only return a value. These values could be annotated on class level like shown in class Bar. The third method will be dynamically implemented by class Bar and annotated with the #CallMethod annotation.
At the end Spring beans of class Bar should implement the interface Foo.
public interface Foo {
public URI getID();
public String getRequires();
public void call(Item item);
}
#FooAnnotation(id = "myID", requires = "text/plain")
public class Bar {
#CallMethod
public void myCallMethod(Item item)
}

Related

Spring REST controller with service using proxy pattern

I have created REST controller with base request mapping on class.
#RestController
#RequestMapping(".../{type}/{typeId}/param..")
public class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
/*
public Type getType(???){
return type;
}
*/
}
Next I have enum Type which determines what service will be used by proxy service (ie. proxy has injected list of serivces and gets one that supports type). I am wondering if there is any way how to make part of request mapping {type} and get it in getter method below so I don't have to repeat it in every request mapping in this class.
I only figured one alternative solution - make this class abstract and then extend it and return constant. This would however leave me with lot of classes without any added value. For example:
#RequestMapping(".../{typeId}/param..")
public abstract class FooController{
#Autowired
BarServiceProxy proxy;
public List<Foo> getFoo(){
return proxy.get(getType());
}
protected abstract Type getType();
}
#RestController
#RequestMapping("/typeAbc)
public class TypeAbcFooController extends FooController{
public Type getType{
return Type.Abc;
}
}
So is it possible to bind #PathVariable from URL specified on class #RequestMapping in some shared method? Thanks
I hope i've understood your problem, but one way of improving your design could be to implement a strategy per type, to inject them, and to use them corresponding to your type received in your controller.
Exemple:
public enum MyType {
TYPE1,
TYPE2
}
public interface IService {
MyType getHandledType();
List<Foo> getFoo();
}
#Service
public class Type1Service implements IService {
#Override
public MyType getHandledType() {
return MyType.TYPE1;
}
#Override
public List<Foo> getFoo() {
// IMPLEMENTATION FOR TYPE1;
}
}
public class FooController{
#Autowired
List<IService> services;
public List<Foo> getFoo(MyType requestType){
IService service = services.stream().filter(iService -> iService.getHandledType() == requestType).findFirst().get();
return service.getFoo();
}
}
This way your controller is agnostic of the underlying service implementation, which is a big responsability.

Creating Dynamic Advice by Passing in Strategies to Aspect

I am trying to make my advice more dynamic based on the class/method it is providing advice for. Looking for something like this pseudoish code:
class Activity
private TheAdviceStrategyInterface activityAdviceStrategy = new ActivityAdviceStrategy();
#Entity(adviceStrategy = activityAdviceStrategy)
public void doSomething(ActivityInput ai) { ... }
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Entity {
public TheAdviceStrategyInterface adviceStrategy();
}
#Aspect
public class TheAdvice {
#Around("#annotation(entity)")
public Object doStuff (ProceedingJoinPoint pjp, Entity entity) {
...
TheAdviceStrategyInterface theStrat = entity.adviceStrategy();
....
}
}
But of course we can not have Objects or Interfaces as annotation parameters.
Any "advice" on how I can implement this? I basically want one Aspect annotation to handle very similar situations, with a slight difference depending on which class is using the annotation.
But of course we can not have Objects or Interfaces as Annotation
parameters. Any "advice" on how I can implement this?
1- Create a String parameter in the Entity interface to represent the possible strategies:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Entity {
public String adviceStrategy();
}
2- Create a class that implements the factory pattern, for instance:
public class TheAdviceStrategyFactory {
//use getShape method to get object of type shape
public TheAdviceStrategyInterface getStrategy(String strategy){
if(strategy == null){
return null;
}
if(strategy.equalsIgnoreCase("Strategy1")){
return new TheAdviceStrategy1();
} else if(strategy.equalsIgnoreCase("Strategy2")){
return new TheAdviceStrategy2();
return null;
}
}
with the Classes TheAdviceStrategy1 and TheAdviceStrategy2 implementing the interface TheAdviceStrategyInterface.
Take advantage of both in the advice:
#Aspect
public class TheAdvice {
#Around("#annotation(entity)")
public Object doStuff (ProceedingJoinPoint pjp, Entity entity) {
...
TheAdviceStrategyFactory factory = new TheAdviceStrategyFactory();
TheAdviceStrategyInterface theStrat = factory.getStrategy(entity.adviceStrategy());
....
}
}

Autowire Spring Bean into interface for default method

I need to add a default method to an interface some classes implement, but my IDE complains (bean may not have been initialized).
Code would be something like this:
public interface IValidator {
MyValidationBean beanToBeAutowired;
...
default Boolean doSomeNewValidations(){
return beanToBeAutowired.doSomeNewValidations();
}
}
Is it just that autowiring into interfaces is not allowed or there's something wrong with the code?
Using #Component on the interface doesn't make any difference.
I'd rather keep this design instead of using an abstract class.
Adding a Variable into interface is not possible in Java. It will be by default a public static final constant. So you have to do either the following:
MyValidationBean beanToBeAutowired = new MyValidationBeanImpl();
or the following:
MyValidationBean beanToBeAutowired();
default Boolean doSomeNewValidations(){
return beanToBeAutowired().doSomeNewValidations();
}
And you can override the beanToBeAutowired method in the implementation class.
i can think of solution as below -
public interface IValidator {
public Service getBeanToBeAutowired();
default Boolean doSomeNewValidations(){
return getBeanToBeAutowired().doSomeNewValidations();
}
}
public class ValidatorClass implements IValidator {
#Autowire private Service service;
#Override
public Service getBeanToBeAutowired() {
return service;
}
}
Just an idea, send validation bean to interface as parameter;
public interface IValidator {
default Boolean doSomeNewValidations(MyValidationBean beanToBeAutowired){
return beanToBeAutowired.doSomeNewValidations();
}
}
Your callerClass;
public class CallerClass implements IValidator{
#Autowired
MyValidationBean beanToBeAutowired;
...
doSomeNewValidations(beanToBeAutowired);
}

Spring aspect call on custom annotation on interface method

I have this interface:
public interface FakeTemplate {
#CustomAnnotation
void foo() {
}
}
And this implementation of the interface:
#Component
public FakeImpl implements FakeTemplate {
#Override
public void foo() {
//Do Stuff
}
}
And this aspect:
#Aspect
#Component
public class CustomAspect {
#Before(value = "#annotation(com.fake.CustomAnnotation)")
public void doStuffBefore(JoinPoint joinPoint} {
}
}
I'm using spring with AspectJ enabled using: #EnableAspectJAutoProxy(proxyTargetClass = true)
My issue is that the aspect doStuffBefore method is not being called before the execution of FakeImpl's foo() method. It Does work when I put the #CustomAnnotation on FakeImpl instead of FakeTemplate, but I'd much prefer to put the annotation on FakeTemplate as it's in a separate API package and I've kind of delegated it as the place where I put all my annotations.
I'd also like to ensure that the CustomAnnotation is called on every class that implements FakeTemplate without remembering to put the annotation on all the implementation classes themselves.
Is there any way to get the advice to be called if the annotation is only on the interface class?
Annotation inheritance doesn't work for methods in java.
But you can use other pointcut expression, something like
execution(public * FakeTemplate+.foo(..))
There is sort of a workaround. In your example, you can add an extra 'default' method e.g. named fooFacade to your FakeTemplate interface, annotate that with your #CustomAnnotation, and then delegate to the 'real' foo method:
public interface FakeTemplate {
#CustomAnnotation
default void fooFacade() {
foo();
}
void foo();
}
Now whenever you call fooFacade(), execution of the pointcut on #CustomAnnotation will be triggered.

Add spring component to list overwrites component from the same type

hi following the spring factory pattern (following this great blog) I created a factory class
#Component
public interface FooFactory {
Foo getFoo(String fooType);
}
public abstract class Foo {
private Integer calc
public Integer getCalc(){return calc}
public void setCalc(Integer calc){
this.calc = calc)
}
}
#Component("bar")
public class Bar extends Foo {
#Overrride
public void setCalc(Integer calc){
this.calc = calc*10)
}
#Component("bazz")
public class Bazz extends Foo {
#Override
public void setCalc(Integer calc){this.calc = calc+10)
}
}
in my main class
private List<Foo> createFoo(){
List<Foo> fooList = ArrayList<Foo>(3)
Foo bar1 = fooFactory.getFoo("bar")
bar1.setCalc(10)
Foo bar2 = fooFactory.getFoo("bar")
bar2.setCalc(20)
Foo bazz1 = fooFactory.getFoo("bazz")
barzz1.setCalc(30)
fooList.add(bar1)
fooList.add(bar2)
fooList.add(bazz1)
for (Foo f:fooList)
System.out.println(f.getCalc());
}
the result that I am getting is
200
200
40
and not as I expected
100
200
40
which means that the first object in the list is overwritten by the second one from the same type.
how can I solve that ?
I do need to created them this way . because I actually add them to list at run time according to user input
context xml:
<bean class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean" id="fooFactory">
<property name="serviceLocatorInterface" value="com.myname.FooFactory">
</property></bean>
You must annotate your beans with #Scope(BeanDefinition.SCOPE_PROTOTYPE)
#Component("bar")
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Bar extends Foo {
#Overrride
public void setCalc(Integer calc){this.calc = calc*10)
}
#Component("bazz")
#Scope(BeanDefinition.SCOPE_PROTOTYPE)
public class Bazz extends Foo {
#Override
public void setCalc(Integer calc){this.calc = calc+10)
}
See http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/ServiceLocatorFactoryBean.html
ServiceLocatorFactoryBean...They will typically be used for prototype beans, i.e. for factory methods that are supposed to return a new instance for each call.
andresoviedo's answer is right
some more explanation:
The ServiceLocatorFactoryBean is just an proxy of the normal bean factory.
Therefore the prototype/singleton scope is handled in the same way as it is for normal injection.
So when you expect Prototype scope beans (that is what you expect (two beans of the same type with different values)), you need to configure that the beans are of this scope. So you need to add #Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) to your two bean.

Categories

Resources