I am newbie in Spring and trying to understand AOP. Here's what I got
I have one simple aspect, which I want to run when any non-getter method gets called
#Aspect
#Component
public class LoggingAspect {
#Pointcut("execution(* org.practice.entity.Person.get*())")
private void getter() {}
#Before("!getter()")
public void noGetter() {
System.out.println("NO GETTER GETS CALLED");
}
}
Person class is simply
#Component
public class Person {
public void getPerson() {
System.out.println("GETTING PERSON....");
}
}
I am initializing the config using Java annotations
#Configuration
#EnableAspectJAutoProxy
#ComponentScan("org.practice")
public class DemoConfig {}
And then in my main method i have
public class MyApp {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DemoConfig.class);
context.close();
}
}
As you can see I am simply creating a context and closing it and not calling any getter or non-getter method. When I run the program, I got the following console output
NO GETTER GETS CALLED....
This makes semi-sense as I am not calling any getter method, but I expected this aspect to execute only if I am explicitly calling any non-getter not by just opening the context. Please let me know if I want my business logic to execute only if any non-getter method gets invoked then how would I do it?
Thanks
Try this:
#Pointcut("execution(* org.practice.entity.Person.*())")
private void methodCall() {}
#Before("!getter() && methodCall")
public void noGetter() {
System.out.println("NO GETTER GETS CALLED");
}
I think its happening during the initializing of Person bean at the time od application context loading
Since you have given joinpoint as not of getter so at the time of default construction execution (provided by compiler) the advice is getting triggered
Related
I'm very confused with the the different behaviors in spring bean with async mehtod. i have a Demo Bean with an #Async method that just print this.hashCode(). and a Main class that injected by Demo. The Demo bean'scope is singleton. but in fact, in Main class, the demo.hashcode() is not equal to this.hashCode() in the method test() of Demo class. why? they are not the same instance?
#Component
public class Demo {
#Async
public void test(){
System.out.println(this.hashCode());
}
}
#Component
public class Main implements CommandLineRunner {
#Autowired
Demo demo;
#Override
public void run(String... args) throws Exception {
System.out.println(demo.hashcode());
demo.test();
}
}
In Main you are calling the hashCode() method on the proxy object. To support #Async Spring places the concrete object inside a proxy to wrap the #Async logic around the concrete method call. Therefore you get the hashCode value of the proxy.
When invoking demo.hashCode() you are returning the hashCode() of the concrete object and not the proxy anymore. Therefore they differ.
It is a class which instance is connected to the external service and it is listening constantly of it.
#Component
public class Service extends PollingBot {
#Value("${token}")
private String token;
#Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
}
}
public void sendMessageToUser(String message) {
try {
execute(sendMessage);
} catch (ApiException e) {
}
}
}
You could see that there is a method called sendMessageToUser which send message. It could not be static because execute method not allow static context. This method could not be separeted to other class. /
So, I have to call this method from other class. However I don't want to create additional instance of Service class otherwise I have two instances which are listen for updates, but I want it is sole class instance doing so.
I have tried to run a Application Context and run method from it, but it was not worked.
So, my question is very simple. How could I run this class non-static(!) method from other class?
By default all spring managed beans are singleton. You need to use #Autowired to inject the bean into other and then you can call the methods of that bean.
#Autowired
private Service service;
public void sendMessage(String message){
service.sendMessageToUser(message);
}
You can use #Autowired annotation to call a method of a bean class(component) in Spring. Also, as mentioned by default beans are singleton in spring so you don't need to worry about creating a single instance explicitly every time.
Try to use the below code in the calling class:
#Autowired
private Service service;
public void sendText() {
service.sendMessage(message);
}
Case 1
Let's consider the following Spring configuration:
#Configuration
public class MyConf1 {
#Bean
public Foo getFoo() {
// Foo class is defined as part of an external lib.
return new Foo();
}
#Bean
public Bar getBar() {
return new Bar(getFoo());
}
}
For some reasons, I need to invoke a Foo's method (i.e. myFoo.shutdown();) when MyConf1 is destroyed.
Is there any way to perform this operation without retrieving the bean instance directly from the application context (via ApplicationContext.getBean())?
Case 2
Again, let's consider a second Spring configuration class:
#Configuration
public class MyConf2 {
#Bean
public ScheduledJob scheduledJob() {
Timer jobTimer = new Timer(true);
return new ScheduledJob(jobTimer);
}
}
This time, I need to invoke jobTimer.cancel() before destroying MyConf2. Indeed, I can instantiate jobTimer outside scheduledJob(), or making it a method's parameter, as scheduledJob(Timer jobTimer).
It will then be possible to define a proper destroyer method for MyConf2. However, I would like to know if there are other ways to proceed.
Any good suggestion?
Note: Foo, Bar, Timer, ScheduledJob classes are defined externally. Thus, there is no possibility to explicitly define an inner destroy method. As assumption, I can modify only MyConf1 and MyConf2.
I would suggest defining a destroy() method (annotated with #PreDestroy) in Foo class
Similarly, modify ScheduledJob class like
public class ScheduledJob {
private Timer timer;
public ScheduledJob(Timer timer){
this.timer = timer;
}
#PreDestroy
public void destroy(){
timer.cancel();
}
}
And add destroyMethod param in #Bean
#Configuration
public class MyConf2 {
#Bean(destroyMethod = "destroy")
public ScheduledJob scheduledJob() {
Timer jobTimer = new Timer(true);
return new ScheduledJob(jobTimer);
}
}
Please see the following page http://forum.spring.io/forum/spring-projects/container/48426-postconstruct-and-predestroy-in-javaconfig
DisposableBean should help you with case #1.
You can implement the DestructionAwareBeanPostProcessor interface that can adds a before-destruction callback when the bean is destroy.In that interface,the method postProcessBeforeDestruction is do that,see the following:
#Override
public void postProcessBeforeDestruction(Object bean, String beanName) throws BeansException {
System.out.println("before destory:"+bean);
}
#Override
public boolean requiresDestruction(Object bean) {
return true;
}
Pay attention to that the method requiresDestruction should return true,otherwise the method postProcessBeforeDestruction will not call when bean should destroy.
And i have a test:
public static void main(String[] args){
ClassPathXmlApplicationContext applicationContext=new ClassPathXmlApplicationContext("classpath:application-main.xml");
applicationContext.registerShutdownHook();
}
The postProcessBeforeDestruction really call when the bean is destroy.The output is :
before destory:com.zhuyiren.spring.learn.util.Handler#55141def
before destory:com.zhuyiren.spring.learn.controller.TestControleler#47eaca72
before destory:com.zhuyiren.spring.learn.service.impl.TestServiceImpl#7b2bbc3
before destory:com.zhuyiren.spring.learn.service.impl.TwoServiceImpl#48f2bd5b
before destory:com.zhuyiren.spring.learn.controller.ConverConroller#72967906
before destory:org.springframework.context.event.DefaultEventListenerFactory#1a482e36
before destory:org.springframework.context.event.EventListenerMethodProcessor#77fbd92c
A little bit new with spring. When I instantiate a bean via interface, it doesn't seem to get events, if however, I use actual class implementing the interface, then the event is received. Why is this? Code below.
package javabeans.di;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextStartedEvent;
public class HelloWorldImpl implements HelloWorld, ApplicationListener<ContextStartedEvent> {
private String msg;
public HelloWorldImpl(String s){
msg = s;
}
#Override
public void printHelloWorld() {
System.out.println("Hello : " + msg);
}
public void onApplicationEvent(ContextStartedEvent event) {
System.out.println("ContextStartedEvent Received");
}
}
Here is the calling code:
public static void main(String[] args) {
ConfigurableApplicationContext ctx = new AnnotationConfigApplicationContext(HelloWorldConfig.class);
// Let us raise a start event.
ctx.start();
HelloWorld obj = (HelloWorld) ctx.getBean("helloWorld");
obj.printHelloWorld();
ctx.stop();
}
Config class:
#Configuration
public class HelloWorldConfig {
#Bean
#Scope("prototype")
public HelloWorld helloWorld(){
return new HelloWorldImpl("Hello java beans");
}
}
The interface:
package javabeans.di;
public interface HelloWorld {
void printHelloWorld();
}
"ContextStartedEvent Received" never gets shown if the bean has a prototype scope.
NOTE: If I change return type of bean method to HelloWorldImpl in the config class, and also change HelloWorld to HelloWorldImpl inside main (two occurrences - basically on the line where I call getBean), then this works also with prototype beans.
Why would that be? Additionally if I create two instances of HelloWorldImpl in main, in a manner described in this paragraph, still the event is received only once (but that might be separate issue).
When using java based configuration what happens is very early in the process the #Configuration classes are read with ASM (they aren't loaded through a class loader yet). Based on that read bytecode Spring creates the bean definitions and proxy based classes.
A #Bean method (regardless where it is) is basically the same as a FactoryBean. It acts more or less in the same way. When the meta data is created it does so by inspecting the method signature and using the return type to create a factory. This return type is basically used for the getObjectType method of a FactoryBean. And this the result of that method is used to determine what the bean supports.
Now when return HelloWorld as a type you get a factory creating beans of that type. When using HelloWorldImpl you will get a factory creating beans of that type. The first doesn't contain the ApplicationListener interface and as such is ignored by spring, the second however does (it is detected at that point of generating the (auto) configuration meta data).
So when using #Configuration with #Bean it is important to be as specific as possible about the return type.
Isn't this because the interface itself doesn't have the listening method?
Shouldn't you
package javabeans.di;
public interface HelloWorld extends ApplicationListener<ContextStartedEvent>{
void printHelloWorld();
public void onApplicationEvent(ContextStartedEvent event);
}
And then #Override in the implementing class?
This is just a guess, but the ApplicationListener interface is only available for the concrete implementation class HelloWorldImpl. Thus when spring creates the helloWorld bean it creates a type HelloWorld which does not have the ApplicationListener interface and thus the event will not be propagated to this bean.
Let the HelloWorld interface extend ApplicationListener and you should receive an event.
I want to use interceptors in a Java-SE application and I am using weld as CDI implementation and i'm testing this here:
The Main-Class:
public static void main(String[] args) {
WeldContainer weldContainer = new Weld().initialize();
Service service = weldContainer.instance().select(Service.class).get();
service.methodCall();
service.methodCallNumberTwo();
}
The Service-Class:
public class Service {
#TestAnnotation
public void methodCall(){
System.out.println("methodCall...!");
methodCallNumberTwo();
}
#TestAnnotation
public void methodCallNumberTwo(){
System.out.println("methodCallNumberTwo...!");
}
}
The Interceptor-Class:
#Interceptor
#TestAnnotation
public class TestInterceptor {
#AroundInvoke
public Object interceptorMethod(InvocationContext invocationContext) throws Exception {
System.out.println("I'm the TestInterceptor of "+invocationContext.getMethod());
return invocationContext.proceed();
}
}
Aaaand the output:
I'm the TestInterceptor of public void Service.methodCall()
methodCall...!
methodCallNumberTwo...!
I'm the TestInterceptor of public void Service.methodCallNumberTwo()
methodCallNumberTwo...!
My Questions
First: Why isn't the interceptor called in methodCall() when i'm calling methodCallNumberTwo()?
Second: Is there a way to change that?
I'm only studying the behavior of interceptors and want to understand. Thank you in advance!
The interceptor is not called because you are calling it on the same instance of the object. If you're familiar with EJBs it's the same as calling a method on the same object instead of through the EJB context.
If you debug through it you'll notic that the method call on the injected objects goes through a proxy. The method call from methodOne to methodTwo isn't proxied.