Using #Aspect with #Conditional - java

I want to conditionally create an Aspect depending on a value read from the properties file. (It is related to profiling, that's why the intended selective use).
I have tried to create a class that implements Condition and therefore the matches() method that would allow me to perform the validation necessary and set the output to the corresponding Boolean, enabling or disabling it.
#Aspect
#Conditional(MyCondition.class)
public class MyAspect {
...
pointCuts, methods and etc...
...
}
Thing is: The Aspect is instantiated by Spring anyway apparently it does not respect the #Conditional annotation output.
Are there any caveats that I am missing here?
Versions:
Spring version: 4.1.4.RELEASE
AspectJ version: 1.7.3
(The project dependency tree is a bit 'complicated' so updating libs has to be taken with a grain of salt :) )

You could manage to achive a conditional Aspect Behaviour if you register your aspect as a component, as described in the #EnableAspectJAutoProxy configuration.
From the docs:
Enables support for handling components marked with AspectJ's #Aspect annotation, similar to functionality found in Spring's XML element. To be used on #Configuration
Note that #Aspect beans may be component-scanned like any other. Simply mark the aspect with both #Aspect and #Component:
So your problem should be solved by just adding #Component to your bean and setting up a proper component-scan if needed.
For example:
#Aspect
#Component
#Conditional(MyCondition.class)
public class MyAspect {
...
pointCuts, methods and etc...
...
}

Related

Spring boot - is there a way to disable AOP for a given profile?

I have been having issues with AOP when running the build and/or running tests. I tried to annotate the #Aspect class with #Profile("local", "war") to make sure that aspect is only accessible to the "local" and "war" profiles, but a scenario might incur when we need to add that aspect support to another profile. And we prefer not to have this hard-coded there.
Is there a clean way to forbid, for instance, the "test" profile to access a given aspect class?
Assuming, aspects are treated as beans in spring:
If its only about "create aspect only if not in profile test", then you can merely go with:
#Profile("!test")
If you want a more sophisticated condition, then keep reading:
Spring profiles were introduced in Spring 3.x but in 4.x the were rewritten with more flexible conditionals.
Take a look at #Profile source code:
#Target({ElementType.TYPE, ElementType.METHOD})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Conditional(ProfileCondition.class)
public #interface Profile {
/**
* The set of profiles for which the annotated component should be registered.
*/
String[] value();
}
Note the #Conditional with ProfileCondition as an implementation of "evaluation" logic.
This class's implementation is pretty straightforward.
You can create your custom annotation like a profile that will call a similar evaluation logic.

How to use Spring #Component annotation with AspectJ compiler

I excluded part of my project for easier reproduce problem: GitHub repo.
When I compile it by Javac everything works as expected. I see logging in console when I open URLs /user/ and /user/2/:
Access: execution(List ru.krivochenko.demo.user.UserController.getAll())
Access: execution(User ru.krivochenko.demo.user.UserController.getOne(Integer))
But I wanna use AspectJ compiler. When I switch to it, error occurs:
java.lang.NoSuchMethodError: ru.krivochenko.demo.logging.LoggingAspect: method <init>()V not found
As I understood it happens because there is not no-args constructor in LoggingAspect. If I add it, I get another error, because logger is not injected:
java.lang.NullPointerException: null
at ru.krivochenko.demo.logging.LoggingAspect.beforeGettingUsers(LoggingAspect.java:28) ~[classes/:na]
So, how we can see, AspectJ ignores Autowired constructor with args.
In branch via-setter of my repo I implemented another solution. I removed #Component annotation of LoggingAspect and replaced constructor injection to setter injection. In DemoApplication.java I added #Bean configuration of LoggingAspect. It works fine, but in some situations it requires getting dependencies from application context. What is the best practice to resolve it?
Thanks for help.
Spring Aspects and compile time weaving don't automatically integrate. This is primary because aspectj and spring are fairly separate and I suspect Spring's recommended approach is not to use compile time weaving.
So thus by default Aspects are not spring magic and we need to add a little bit of plumbing to ensure they are.
In this regard, it is important to note that Aspects are not spring managed (they are managed by aspectj so we need to add something to ensure they are).
Thus the reason why you need a parameterless constructor on your aspect (so must use field injection).
Traditionally I have had to add the following piece of xml to my xml config files:
<bean id="securityAspect" class="com.<skip>.security.AuthorizationAspect"
factory-method="aspectOf" autowire="byType" />
So this works because the AspectJ compiler adds the static method aspectOf to the aspects and this method is available for acquiring the instance of the Aspect that aspectj creates (and uses).
This method is obviously not available in the source so we can't just add to our application class (DemoApplication):
#Bean
public LoggingAspect loggingAspect() {
return LoggingAspect.aspectOf();
}
Then what to do? My next option was to write some reflective code to call this method then having looked at this very helpful example that demonstrates exactly what you need - The Aspects class from AspectJ has a utilty method that does this work for us, so adding the following to our DemoApplication we have success:
#Bean
public LoggingAspect loggingAspect() {
return Aspects.aspectOf(LoggingAspect.class);
}
Btw, remove the #Component from the LoggingAspect as that will mean both Aspectj and Spring create an instance of the class...
Btw, I'd also suggest you add the following to your test class to demonstrate the problem in a test:
#Autowired
private UserController controller;
#Test
public void contextLoads() {
controller.getAll();
controller.getOne(1);
}
Btw, other suggestions to address this problem used #Configurable. I suspect this might work but you'll need to make sure you include the spring aspects java in your aspectj compile time config and I suspect it may still not work as I'm not sure the Spring context will be ready in time. i.e. if the Aspect is created before the spring context then #Configurable won't work as the beans to be injected will not yet be created.
Your approach to configure the aspect via setter injection looks valid to me. For more information about how to use AspectJ in combination with Spring check out the corresponding chapter in the Spring manual, specifically the description about how to configure AspectJ aspects by Spring IoC. It is mostly explained in the context of LTW, but it should work pretty much the same for CTW.

Spring, annotation based, overriding of components

When I setup Spring with XML a can override component definitions in XML files that are loaded later.
It's very usefull for tests - I create default config set and than load it with addition test configuration that replaces some of components with specials (stubs, mocks, and so on).
Now i start to migrate to annotation based configurations and it causes some problems.
The direct way to use annotations is auto-discovering of packages with #Component
So I have
#Configuration
#ComponentScan({"some.pack1", "some.pack2"})
public class ProductConfig{}
And when
#Configuration
#Import({ProductConfig.class})
#ComponentScan({"test.pack"})
public class TestConfig{}
But it will cause conflict if I try to override components in test.pack
And what I can do?
After some investigations where are 3 answers with some issues on them
Worst - i can use #Filter on ComponentScan - it's worst way,
i must not import existed config (that can has some additional beans)
i must rescan all components, and explicitly define set of filters
i can use #Profile and activeProfiles - it's better, while it's more sophistical, implict, but
it means that i must to know at product classes that they can be disabled in some tests
not to use #ComponentScan on override Config and using #Bean insted of it
it's maybe well on test configurations, but it means that I lost ability to use #Component annotation
use setParent on contexts - it works well, but
it's explicit operation on implementation of ApplicationContext not on interface
it's not hard to setup if overriding services has #Autwire dependency on some components from overriden config - require manual register and refresh
What is best and standard way to override conigurations??? When I used XML-based it was not a problem...
#profile plays a crucial role while implementing the testing strategy for your service/code.
For example, in development, you may have:
public interface DataSource{
public String getHost();
}
Default implementation is
#Component
#Profile("Prod")
public class DevDataSource implements DataSource {
public String getHost(){
// return actual value
}
And the implementation for component tests(Fake impl)
#Component
#Profile("test")
public class StubbyDataSource implements DataSource {
public String getHost(){
return "some-host"; // return mocked data
}
Now you can write a test here which can act as integration test, unit test and component tests (https://martinfowler.com/bliki/ComponentTest.html)
In that way, your testing strategy would be much more elegant, concise and easy to maintain. Just by changing the profile, the same test can point to different environments (real or fake).

Is it possible to add #Secured or #PreAuthorized annotations on an entire class

It seems logical to secure an entire class of controllers rather than each method. Can I do this:
#Controller
#Secured("ROLE_USER")
public class accountPages {
//Controllers
}
from Spring Security 3 - PACKT Publishing
Be aware that the method-level security
annotations can also be applied at the
class level as well! Method-level
annotations, if supplied, will always
override annotations specified at the
class level

Custom Spring sterotype annotation with scope of prototype?

I created a custom sterotype #Action, and Spring has managed to detect it in the package scan I configured in the configurations.
The next step I would like to do is to tell Spring that all classes with #Action should be created with prototype, instead of Singleton.
My #Action interface is as follows:
#Target({ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Component
public #interface Action {
}
I tried to mark it with #Scope("prototype") but that does not seem to help.
Is what I desire possible?
Kent
The context:component-scan can be configured with a custom scope-resolver, which implements org.springframework.context.annotation.ScopeMetadataResolver.
I created a custom scope-resolver that not only checks the bean for a #Scope annotation (with the default resolver of org.springframework.context.annotation.AnnotationScopeMetadataResolver), but looks up annotations of annotations too (recursively).
One thing to note though, that looking up annotations recursively might go into an endless loop, as java.lang.annotation.Documented is annotated with java.lang.annotation.Documented. Best to maintain a table that indicates which annotation has been looked up already.
Unfortunately not with spring 2.5.X. Your #Component-annotation describes the role of the bean while the scope is a separate axis, so a role and scope descriptor typically have to be applied separately to the implementation class. When viewed in this way it makes some sense (edit: at least it did so for a few seconds, anyway)
I don't know how this will change i spring 3.x, which is not too far away. There seems to be some room for improvement.

Categories