I am using Spring4 along with Spring Boot.
Before I tired to use AOP, my Bean(CommandService),which is used in the controller, is auto injected well, but after I tired to use AOP to collect some debug message, the bean becomes null!
Here is my Application.java
#Configuration
#EnableAutoConfiguration
#ComponentScan({"hello","wodinow.weixin.jaskey"})
public class Application extends {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
LogUtil.info("Beans provided by Spring Boot:");
String[] beanNames = ctx.getBeanDefinitionNames();
Arrays.sort(beanNames);
for (String beanName : beanNames) {
LogUtil.info(beanName);
}
LogUtil.info("Application Boots completes!");
}
#Bean
public CommandService commandService(){
LogUtil.debug("CommandService.getInstance()"+ CommandService.getInstance()) ;//here indeed I could see spring executes this and returns a object when application boots
return CommandService.getInstance();//This returns a singleton instance
}
}
My controller that throws null pointer:
#Controller
public class CoreController {
#Autowired
CommandService commandService;//here the service is null after using aop
//...some request methods
}
The Aspect which I added just now:
//if I comment out these two annoations, the bean will be auto injected well
#Aspect
#Component
public class LogAspect {
#Pointcut("execution(* wodinow.weixin.jaskey..*.*(..))")
private void debug_log(){};
#Around("debug_log()")
public void debug(ProceedingJoinPoint joinPoint) throws Throwable{
LogUtil.debug("enter "+joinPoint.getSignature());
try{
joinPoint.proceed();
LogUtil.debug("returns from "+joinPoint.getSignature());
}
catch(Throwable t){
LogUtil.error(t.getMessage()+"occurs in "+joinPoint.getSignature(),t);
throw t;
}
}
}
I am new to Spring, can anybody help me with this?
Your #ComponentScan is trying to resolve and autowire your dependencies into CoreController. When it tries to resolve the dependency it finds the #Bean in your Application class. It then tries to resolve this dependency by calling Application.commandService(). When this method is called, it sees the matching #Pointcut and invokes your advice method. Since your #Advice is not returning anything, the callers will also see that nothing was returned, and it will say that that resolution of that dependency returned null.
The fix here is just to change your #Around advice to return the value of your invocation.
#Around("debug_log()")
public Object debug(ProceedingJoinPoint joinPoint) throws Throwable{
LogUtil.debug("enter "+joinPoint.getSignature());
try{
// return the invocation
return joinPoint.proceed();
}
catch(Throwable t){
LogUtil.debug(t.getMessage()+"occurs in "+joinPoint.getSignature(),t);
throw t;
}
}
You are used
joinPoint.proceed();
without return just add return to be
return joinPoint.proceed();
Related
I'm new to Spring Boot and AOP and I've spent the last three days trying to get this to work with no avail.
I have a class called App. This class invokes a method called invokeRetrieveMethod -- which invokes another method in an autowired businessClass object. I'm simply trying to log the time it takes to run a method annotated with my custom #LogExecutionTime annotation, but I get a null pointer exception when running my code. Please help!
#SpringBootApplication
public class App
{
#Autowired
BusinessClass businessClass;
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
System.out.println("Starting application...");
App app = new App();
app.invokeRetrieveSomething();
}
public void invokeRetrieveSomething() {
businessClass.retrieveSomething();
}
}
The Spring boot "bean"(?)
#Component
public class BusinessClass {
#LogExecutionTime
public void retrieveSomething() {
System.out.println("This is the retrieveSomething() method.");
}
}
my Aspect
#Aspect //specifies that this is an aspect
#Component //because we want this class to be turned into a bean in order for it to work supposedly
public class ExampleAspect {
#Around("#annotation(LogExecutionTime)")
public Object logExecutionTime(ProceedingJoinPoint joinPoint) throws Throwable {
long start = System.currentTimeMillis(); //executed before the method annotated with #LogExecutionTime is executed.
Object proceed = joinPoint.proceed();
//everything below gets executed after the method.
long executionTime = System.currentTimeMillis() - start;
System.out.println(joinPoint.getSignature() + " executed in " + executionTime + "ms");
return proceed;
}
}
My Custom annotation
#Target(ElementType.METHOD) //tells us *where* this annotation will be applicable (ElementType.METHOD means on methods only)
#Retention(RetentionPolicy.RUNTIME) //states whether the annotation will be available to the jvm at runtime or not. by default, it's not.
public #interface LogExecutionTime {
}
Enable the AspectJ in the App class using the annotation #EnableAspectJAutoProxy
Read more at: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/context/annotation/EnableAspectJAutoProxy.html
I'd like to weave an advice on a method that is NOT part of a Spring bean (Spring Boot 1.4.4.RELEASE) :
#Component
#Aspect
...
#Around("execution(public * com.netflix.appinfo.InstanceInfo.getId())")
I added aspectjrt and spring-instrument (??) dependencies
I added #EnableAspectJAutoProxy and #EnableLoadTimeWeaving(aspectjWeaving = AspectJWeaving.ENABLED) annotations
I added VM arguments:
-javaagent:d:\.m2\repository\org\springframework\spring-instrument\4.3.6.RELEASE\spring-instrument-4.3.6.RELEASE.jar
-javaagent:d:\.m2\repository\org\aspectj\aspectjweaver\1.8.9\aspectjweaver-1.8.9.jar
The bean is handled (postconstruct log) but the execution isn't intercepted.
Does anyone has a clue on something I could miss ? Thx in advance
Ok, here is the trick for those interested, a singleton pattern is handling access to a singleton for both LTW and Spring, so it can be injected with Spring dependencies after being weaved by LTW:
#Configuration
#Aspect
public class MyAspect {
#Value("${mycompany.property}")
private String myKey;
#Around("execution(public * com.mycompany.NotASpringean.getProperty())")
public String weave(ProceedingJoinPoint jp) throws Throwable {
String value = (String) jp.proceed();
// transform the value thx to injected myKey value
return value;
}
#Bean("post-construct-aspect")
public MyAspect init() {
return MyAspect.aspectOf(); // get existing instance via factory method
}
private static MyAspect instance = new MyAspect();
/** Singleton pattern used by LTW then Spring */
public static MyAspect aspectOf() {
return instance;
}
}
Try using the #Pointcut annotation too like this:
#Pointcut("execution(public * com.netflix.appinfo.InstanceInfo.getId())")
public void pointcut() {}
#Around("pointcut()")
public Object whatever(ProceedingJoinPoint joinPoint) throws {...}
I am new to Spring and AOP. I am trying this simple thing where I have created a custom annotation which when placed before any method should execute some code.
This is the annotation I created
// Declares a custom annotation that validates json
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface JsonSchemaAnnotation {
}
Next I created the Spring Aspect class which holds the logic
#Aspect
public class UpdateUIMetadataInterceptor {
#Pointcut("execution(public * com.fico.cardinal.cm.*.*(..))")
public void anyPublicMethod() {
System.out.println("Running");
}
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
}
And this is my simple test class
public class ValidationTest {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("spring/configuration.xml");
String jsondata = "{\"id\": \"EXPENSE_REPORT\",\"properties\": {\"transactionType\": \"EXPENSE_REPORT\"},\"sections\": []} ]}";
ValidationTest test = new ValidationTest();
test.jsonValidationTest("dummy", jsondata);
((AbstractApplicationContext) context).close();
}
#JsonSchemaAnnotation
public void jsonValidationTest(String dummy, String jsondata) {
System.out.println("Success");
}
The problem is my spring aop never gets triggered. I have included a bean in my configuration.xml
<aop:aspectj-autoproxy>
<aop:include name="UpdateUIMetadataInterceptor" />
</aop:aspectj-autoproxy>
<bean id="updateUI" class="com.fico.cardinal.cm.interceptor.UpdateUIMetadataInterceptor" />
Can anyone point out what I am missing?
You have several problems with your code:
You should create your ValidationTest object as a bean managed by Spring and not using new
<aop:include name="UpdateUIMetadataInterceptor" /> should be <aop:include name="updateUI"/>; you can actually just stick with <aop:aspectj-autoproxy/> for simplicity here
ProceedingJoinPoint is not supported for before aspects, so remove it; you can use JoinPoint instead if you need access to arguments
JsonSchemaAnnotation jsonSchemaAnnotation parameter should be present for validateJson method of your aspect, as pointed out by frant.hartm
I think you need either fully qualified name or a parameter in the method:
FQN:
#Before("anyPublicMethod() && #annotation(your.package.JsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("Running");
}
Parameter:
#Before("anyPublicMethod() && #annotation(jsonSchemaAnnotation)")
public void validateJson(ProceedingJoinPoint pjp, JsonSchemaAnnotation jsonSchemaAnnotation ) throws Throwable {
System.out.println("Running");
}
Source: http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop.html#aop-pointcuts
(and you also need to use the bean, as Dmitry Kuskov pointed out
Im fairly new to Java Spring IoC and here's my problem
I have a FactoryConfig class with all beans and annotation #Configuration and #ComponentScan written as below.
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
//And few more #Bean's
}
My Test class has a simple Print method
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
Now in my Main Class Ive created an ApplicationContentext of FactoryConfig. (I'm expecting all of my #Beans in Factory config will be initialised. However, it returns null when I access the Test class using #Autowired
My Main Class
public class Main {
#Autowired
protected static Test _autoTest;
public static void main(String[] args) throws InterruptedException {
// TODO Auto-generated method stub
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
// _autoTest.Print(); <--- Im getting NULL Pointer Ex here
}
}
What is the correct way to #Autowire and use objects/beans? any clearer explanation would be much appreciated.
Only beans managed by Spring can have #Autowire annotations. Your main class is not managed by Spring: it's created by you and not declared in a Spring context: Spring doesn't known anything about your class, and doesn't inject this property.
You can just access in your main method the Test bean with :
context.getBean(Test.class).Print();
Usually, you get a "bootstrap" from the context, and call this bootstrap to start your application.
Moreover:
On Java, a method shouldn't start with an uppercase. Your Test class should have a print method, not Print.
If you start with Spring, you should maybe try Spring Boot
Spring does not manage your Main class, that's why you are getting Nullpointer Exception.
Using ApplicationContext to load beans, you can get your beans and access Methods as you are already doing -
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
FactoryConfig config = context.getBean(FactoryConfig.class);
config.test().Print();
remove the static argument
protected Test _autoTest;
Your class
public class Test {
public void Print() {
System.out.println("Hello Test");
}
}
is not visible to Spring. Try adding an appropriate annotation to it, like #Component.
The reason is that your Main is not managed by Spring. Add it as bean in your configuration:
import org.springframwork.*
#Configuration
#ComponentScan(basePackages="package.name")
public class FactoryConfig {
public FactoryConfig() {
}
#Bean
public Test test(){
return new Test();
}
#Bean
public Main main(){
return new Main();
}
//And few more #Bean's
}
And then you can edit your main() as follows:
public class Main {
#Autowired
protected Test _autoTest;
public static void main(String[] args) throws InterruptedException {
ApplicationContext context =
new AnnotationConfigApplicationContext(FactoryConfig.class);
Test test = context.getBean(Test.class);
Main main = context.getBean(Main.class);
test.Print();
main._autoTest.Print();
}
}
I have a simple method in a Service in a SpringBoot Application. I have the retry mechanism setup for that method using #Retryable.
I am trying integration tests for the method in the service and the retries are not happening when the method throws an exception. The method gets executed only once.
public interface ActionService {
#Retryable(maxAttempts = 3, backoff = #Backoff(delay = 2000))
public void perform() throws Exception;
}
#Service
public class ActionServiceImpl implements ActionService {
#Override
public void perform() throws Exception() {
throw new Exception();
}
}
#SpringBootApplication
#Import(RetryConfig.class)
public class MyApp {
public static void main(String[] args) throws Exception {
SpringApplication.run(MyApp.class, args);
}
}
#Configuration
#EnableRetry
public class RetryConfig {
#Bean
public ActionService actionService() { return new ActionServiceImpl(); }
}
#RunWith(SpringJUnit4ClassRunner.class)
#SpringApplicationConfiguration( classes= {MyApp.class})
#IntegrationTest({"server.port:0", "management.port:0"})
public class MyAppIntegrationTest {
#Autowired
private ActionService actionService;
public void testAction() {
actionService.perform();
}
Your annotation #EnableRetry is at the wrong place, instead of putting it on the ActionService interface you should place it with a Spring Java based #Configuration class, in this instance with the MyApp class. With this change the retry logic should work as expected. Here is a blog post that I had written on if you are interested in more details - http://biju-allandsundry.blogspot.com/2014/12/spring-retry-ways-to-integrate-with.html
Biju Thanks for putting up a link for this, it helped me to resolve a lot of questions. The only thing that I had to seperately was I still had to add 'retryAdvice' as a bean in the spring xml based approach, Also had to make sure I had context annotation enabled and aspectj is available at classpath. After I added these below I could get it working.
<bean id="retryAdvice"
class="org.springframework.retry.interceptor.RetryOperationsInterceptor">
</bean>
<context:annotation-config />
<aop:aspectj-autoproxy />