I'm trying to test out Spring Annotations to see how they work with some simple examples derived from the Spring 3.0 Source (in this case the "#Required" annotation specifically).
To start, I came up with a basic "Hello World" type example that doesn't use any annotations. This works as expected (i.e. prints "Hello Spring 3.0~!").
I then added a DAO object field to the Spring3HelloWorld class. My intention was to deliberately cause an exception to occur by annotating the setter for the DAO with #Required but then not setting it. However, I get a null pointer exception (since this.dao is null) when I was expecting an exception based on not following the annotation "rules/requirements".
I thought I would have needed to set the DAO object before calling any method from Spring3HelloWorld, but apparently that's not the case. I assume I'm misunderstanding how #Required works.
So basically how would I get the following to give me an error along the lines of "Hey you can't do that, you forgot to set DAO blah blah blah".
Spring3HelloWorldTest.java:
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;
public class Spring3HelloWorldTest {
public static void main(String[] args) {
XmlBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource ("SpringHelloWorld.xml"));
Spring3HelloWorld myBean = (Spring3HelloWorld) beanFactory.getBean("spring3HelloWorldBean");
myBean.sayHello();
}
}
Spring3HelloWorld.java:
import org.springframework.beans.factory.annotation.Required;
public class Spring3HelloWorld {
private DAO dao;
#Required
public void setDAO( DAO dao ){
this.dao = dao;
}
public void sayHello(){
System.out.println( "Hello Spring 3.0~!" );
//public field just for testing
this.dao.word = "BANANA!!!";
}
}
SpringHelloWorld.xml:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config/>
<bean class="org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor"/>
<bean id="dao" class="src.DAO" ></bean>
<bean id="spring3HelloWorldBean" class="src.Spring3HelloWorld" ></bean>
</beans>
My first guess is you won't get any of the advanced behaviour with Spring and annotations because you are using an XmlBeanFactory instead of the recommended ApplicationContext.
-- edit --
Yup - see this Stack Overflow question/answer.
Related
When the aspect is enabled, the #Autowired bean in BeanB becomes a proxy, and the name field is null. Why? What should I do if I wish the original code to work properly?
Here is the code:
public class BeanA
{
#Value("jami")
//public String name;
String name; //package visiblity
}
public class BeanB
{
#Autowired
private BeanA beanA;
public void noLongerWorks()
{
System.out.println(beanA.name);
}
}
public class Main
{
public static void main(String[] args)
{
String[] configs = {"applicationContext.xml", "applicationContext-aop.xml"};//prints null
// String[] configs = {"applicationContext.xml"};//prints jami
ApplicationContext ctx = new ClassPathXmlApplicationContext(configs);
BeanB beanB = ctx.getBean(BeanB.class);
beanB.noLongerWorks();
}
}
---------- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:annotation-config />
<bean class="aop.pack1.BeanA" />
<bean class="aop.pack1.BeanB" />
</beans>
------ applicationContext-aop.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<aop:aspectj-autoproxy />
<bean class="aop.pack1.TestAspect" />
</beans>
#Aspect
public class TestAspect
{
#Pointcut("target(aop.pack1.BeanA)")
public void pointcut() {}
#Before("pointcut()")
public void advice()
{
System.err.println("___________advice__________");
}
}
EDIT:
I figured out one possible solution. But it does not seems very clean. Is there any elegant way to this? without making changes to existing code?
The solution I found:
is to make all the fields in BeanA private, and only access them via getter setters.
This approach, however, requires a lot of modification of the original code (e.g. the BeanA class).
You have already figured out the issue but, I wanted to share this article I came across that lists what Spring AOP can and cannot do.
In your case
Since it uses proxy-based AOP, only method-level advising is supported; it does not support field-level interception So join-points can be at method level not at field level in a class.
Only methods with public visibility will be advised: Methods with private, protected, or default visibility will not be advised.
Just a recommendation and I think it's also a good OOP practice to create fields with private or protected visibility and provide appropriate getters and setters to access them.
These SO Q/A might be useful
Spring AOP - get old field value before calling the setter
spring singleton bean fields are not populated
Spring AOP CGLIB proxy's field is null
I am trying to auto-wire a spring managed bean to use in my unit test case. But autowired bean is always null. Below are my setting.
my unit test class
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = "classpath*:business-context-test.xml")
public class SMSGatewayTest {
#Autowired
#Qualifier("mySMSImpl")
private ISMSGateway smsGateway;
#Test
public void testSendTextMessage() throws Exception {
smsGateway.sendText(new TextMessage("TEST"));
// ^___________ this is null, even though I have set ContextConfiguration
}
}
spring managed bean
package com.myproject.business;
#Component("mySMSImpl")
public class MySMSImpl implements ISMSGateway {
#Override
public Boolean sendText(TextMessage textMessage ) throws VtsException {
//do something
}
}
context for unit test case
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<context:annotation-config/>
<context:component-scan base-package="com.myproject.business"/>
</beans>
I have seen many questions and all are giving the answers that I've already incorporated in my code. Can some one tell me what I am missing. I am using intellij 14 to run my test case.
You are having code like this :
#RunWith(MockitoJUnitRunner.class)
#ContextConfiguration(locations = "classpath*:business-context-test.xml")
public class SMSGatewayTest {
..
..
}
Do you really wannna use MockitoJUnitRunner as I am not seeing any more mocks in the class.
Please try running the JUnit with like
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = "classpath*:business-context-test.xml")
public class SMSGatewayTest {
..
..
}
Edit -
#RunWith(SpringJUnit4ClassRunner.class) makes all those dependencies available which are declared using #ContextConfiguration(..) to the class on which it is used.
For example, in your case you have #RunWith(SpringJUnit4ClassRunner.class) on the class - SMSGateway. So it makes available all those dependencies to SMSGateway which are configured using #ContextConfiguration(locations = "classpath*:business-context-test.xml")
This helps to autowire 'smsGateway' inside your class SMSGateway. As you was using #RunWith(MockitoJUnitRunner.class), this dependency was not available for autowiring and hence spring was complaining. You will need MockitoJUnitRunner.class if you are using Mockito in your application. As you are not mocking any of your classes, you don't need MockitoJUnitRunner as of now.
Please take a look at - Mockito, JUnit and Spring
for more clarifications.
Take a look at http://www.alexecollins.com/tutorial-junit-rule/ . This will help you to understand how '#Runwith' and '#ContextConfiguration' annotations work behind the scenes.
There is an extra whitespace in the Bean name
#Component("mySMSImpl ")
#Qualifier("mySMSImpl")
Why would you use the Qualifier annotation in this case? Are there multiple implementations of the ISMSGateway interface?
Hello I'm newbie in Spring AOP.
I have writed something like this:
My Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface ExceptionHandling {
String onSuccess();
String onFailture();
}
Aspect Class:
#Aspect
public class ExceptionHandler implements Serializable {
#Pointcut(value="execution(public * *(..))")
public void anyPublicMethod() {
}
#Around("anyPublicMethod() && #annotation(exceptionHandling)")
public Object displayMessage(ProceedingJoinPoint joinPoint,ExceptionHandling exceptionHandling) throws FileNotFoundException {
try{
Object point = joinPoint.proceed();
new PrintWriter(new File("D:\\log.txt")).append("FUUCK").flush();
FacesMessageProvider.showInfoMessage(
FacesContext.getCurrentInstance(),exceptionHandling.onSuccess());
return point;
} catch(Throwable t) {
new PrintWriter(new File("D:\\log.txt")).append("FUUCK").flush();
FacesMessageProvider.showFatalMessage(
FacesContext.getCurrentInstance(),
exceptionHandling.onFailture());
return null;
}
}
}
Method from ManagedBean
#ExceptionHandling(onSuccess=IMessages.USER_UPDATED,onFailture=IMessages.WRONG_DATA)
public void onClickUpdateFromSession(){
onClickUpdate(sessionManager.getAuthenticatedUserBean());
}
And app-config.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
">
<aop:aspectj-autoproxy/>
<bean id="exceptionHandler"
class="eteacher.modules.ExceptionHandler"/>
<bean id="sessionManager"
class="eteacher.modules.SessionManager"
scope="session"/>
</beans
I'm trying to make exception handler using Spring AOP
and JSF messages but it does not fire the advice.
Please help me.
Spring AOP will only work on Spring managed beans i.e. beans in the ApplicationContext. As your JSF beans aren't managed by Spring but by the JSF container the AOP part isn't going to work.
To make it work either make your JSF managed beans Spring managed beans (see the Spring Reference Documentation for that) or switch to loadtime or compile time weaving of your Aspects.
A note on loadtime weaving is that it might nog work if your JSF classes get loaded before the Spring context is loaded, the newly registered custom classloader cannot modify the bytecode of already loaded classes.
I'm trying to test a simple Aspect.
The app compiles and runs fine, BUT I do not get the Aspect executed. Or at least, I do not get the output the aspect should produce.
(my aim is to write an exception logger for any ex that occures in the app. but first this test aspect should run...)
Maybe someone who has more experience in aspects see's what I'm doing wrong?
package business;
public interface Customer {
void addCustomer();
}
import org.springframework.stereotype.Component;
#Component
public class CustomerImpl implements Customer {
public void addCustomer() {
System.out.println("addCustomer() is running ");
}
}
#RequestScoped #Named
//this is backing bean for jsf page
public class Service {
#Inject
Customer cust;
add() {
System.out.println("Service is running ");
cust.addCustomer();
}
}
#Aspect
public class AspectComp {
#Before("within(business..*)")
public void out() {
System.out.println("system out works!!");
}
}
Spring:
<beans
xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
">
<context:annotation-config />
<context:component-scan base-package="business" />
<aop:aspectj-autoproxy />
</beans>
Output:
Service is running
addCustomer() is running
The Aspect statement is missing.
You are creating your Component with its constructor, and not getting it from Spring container! That's the problem, or you must use AspectJ's load-time weaver.
Just inject your component (CustomerImpl) in your service and then use the injected instance.
I remember having a similar problem once; Spring wasn't actually loading the proxy as it did not recognize the #Aspect annotation as being an annotation-scanable bean. I added the #Component annotation to the #Aspect notation and Spring started scanning it.
I never looked into the reasons why this happened, and why I needed to do that, so I cannot confirm that is the "proper" way of doing things. My gut would tell me that I had something missing in my config file; I can't imagine why Spring would not scan for #Aspect beans.
The other thing you can do, is to explicitly declare your Aspect bean in the XML config file as well to see if this the same type of problem you're having.
You can also enable debug logging in the Spring framework and see if your bean is being loaded by Spring. If not, then it gives you an idea where to start looking.
I have a ServiceA which has a dependency on ServiceB. The serviceB comes from a spring bean file with lazy-init=true i.e, I only want serviceB to be initialised when and if I ask for that bean.
However I do use ServiceA throughout my application and when we do a setter based injection ServiceB gets initialised.
I want ServiceA to not initialise ServiceB until any method in ServiceA is called that needs ServiceB. One way of doing this was using the Aspects but I was looking at the simplest possible solution for this particularly in the Spring XML file for serviceB or some annotation in serviceB or any proxy flag.
I think LazyInitTargetSource does what you need.
Useful when a proxy reference is needed on initialization but the actual target object should not be initialized until first use. When the target bean is defined in an ApplicationContext (or a BeanFactory that is eagerly pre-instantiating singleton beans) it must be marked as "lazy-init" too, else it will be instantiated by said ApplicationContext (or BeanFactory) on startup.
Another approach that you might want to try is discussed at http://static.springsource.org/spring/docs/3.1.x/javadoc-api/org/springframework/aop/framework/autoproxy/target/LazyInitTargetSourceCreator.html:
[LazyInitTargetSourceCreator is a] TargetSourceCreator that enforces a LazyInitTargetSource for each bean
that is defined as "lazy-init". This will lead to a proxy created for
each of those beans, allowing to fetch a reference to such a bean
without actually initialized the target bean instance.
To be registered as custom TargetSourceCreator for an auto-proxy
creator, in combination with custom interceptors for specific beans or
for the creation of lazy-init proxies only. For example, as
autodetected infrastructure bean in an XML application context
definition:
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="customTargetSourceCreators">
<list>
<bean class="org.springframework.aop.framework.autoproxy.target.LazyInitTargetSourceCreator"/>
</list>
</property>
</bean>
<bean id="myLazyInitBean" class="mypackage.MyBeanClass" lazy-init="true">
...
</bean>
If you find yourself doing this several times in an application context, this will save on configuration.
I don't know about Spring, but in Guice you would use a Provider<ServiceB>, so that when you were ready to use the service you'd go provider.get().callMethodOnB(...).
This is, I believe, part of the JSR-330 spec, so it's possible there's an equivalent thing in Spring (I haven't used an up-to-date version of Spring for quite some time).
You can also use method injection.
As a rule it's required to obtain a new instance of a singleton bean from a context, but it can be used in your case too.
Sample:
package org.test.lazy;
public abstract class ParentBean {
public abstract LazyBean getLazy();
public void lazyDoingSomething() {
getLazy().doSomething();
}
}
package org.test.lazy;
public class LazyBean {
public void init() {
System.out.println("Initialized");
}
public void doSomething() {
System.out.println("Doing something");
}
}
package org.test.lazy;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
#ContextConfiguration
#RunWith(SpringJUnit4ClassRunner.class)
public class LazyTest {
#Autowired
private ParentBean parentBean;
#Test
public void test() {
parentBean.lazyDoingSomething();
parentBean.lazyDoingSomething();
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
">
<import resource="classpath:org/test/lazy/Lazy-context.xml"/>
<bean id="parentBean" class="org.test.lazy.ParentBean">
<lookup-method bean="lazyBean" name="getLazy"/>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
"
default-lazy-init="true">
<bean id="lazyBean" class="org.test.lazy.LazyBean" init-method="init" />
</beans>
So you lazy bean will be initialized only once on demand.