I need help making AOP work. What am I missing here?
<?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-2.0.xsd">
<bean id="duke" class="com.tutorial.springidol.Singer">
<constructor-arg value="Duke"/>
<constructor-arg>
<bean class="com.tutorial.springidol.Song">
<property name="title" value="ABC"/>
</bean>
</constructor-arg>
</bean>
<bean id="audienceAdvice" class="com.tutorial.advice.AudienceAdvice">
<property name="audience">
<bean class="com.tutorial.springidol.Audience"/>
</property>
</bean>
<bean id="audienceAdvisor"
class="org.springframework.
aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="audienceAdvice"/>
<property name="pattern" value=".*perform"/>
</bean>
</beans>
AudienceAdvice.java
public class AudienceAdvice implements MethodBeforeAdvice,
AfterReturningAdvice {
#Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
audience.takeSeats();
audience.turnOffCellphones();
}
#Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
audience.applaud();
}
private Audience audience;
public void setAudience(Audience audience) {
this.audience = audience;
}
}
The AOP does not work but the target executes though.
You've declared the target bean and the advice, but by default Spring doesn't know to actually apply the advice to the target.
You need to run an Autoproxy.
One way to do this:
<!-- Autoproxy -->
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>duke</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>audienceAdvisor</value>
</list>
</property>
</bean>
Related
I'm currently doing novice tutorial about Spring, it seems i have everything the same like person who makes this tutorial but i keep getting errors NullPointerException , could someone help me?
There is section in my xml that is commented if you uncomment it, program will work. But without it, when I try to use autowire byName it doesn't work.
Main Class:
public class MainSpring {
public static void main(String[] args) {
ApplicationContext context= new ClassPathXmlApplicationContext("konfiguracja.xml");
SomeBean bean = context.getBean("SomeBean",SomeBean.class);
System.out.println(bean.getInjectedBean1().getName());
System.out.println(bean.getInjectedBean2().getName());
}
}
InjectedBean Class
package springTutorial;
public class InjectedBean {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
SomeBean Class
import org.springframework.beans.factory.annotation.Autowired;
public class SomeBean {
private InjectedBean InjectedBean1;
private InjectedBean InjectedBean2;
public InjectedBean getInjectedBean1() {
return InjectedBean1;
}
public void setInjectedBean1(InjectedBean injectedBean1) {
this.InjectedBean1 = injectedBean1;
}
public InjectedBean getInjectedBean2() {
return InjectedBean2;
}
public void setInjectedBean2(InjectedBean injectedBean2) {
this.InjectedBean2 = injectedBean2;
}
}
Configuration.xml file
<?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:c="http://www.springframework.org/schema/c"
xmlns:p="http://www.springframework.org/schema/p" xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.2.xsd">
<bean id="InjectedBean1" class="springTutorial.InjectedBean" >
<property name="name" value="asasa" >
</property>
</bean>
<bean id="InjectedBean2" class="springTutorial.InjectedBean" >
<property name="name" value="vbvb">
</property>
</bean>
<bean id="SomeBean" class="springTutorial.SomeBean" autowire="byName" >
<!-- <property name="injectedBean1"> -->
<!-- <ref bean="InjectedBean1"/> -->
<!-- </property> -->
<!-- <property name="injectedBean2"> -->
<!-- <ref bean="InjectedBean2"/> -->
<!-- </property> -->
</bean>
</beans>
As per Configuration.xml ; InjectedBean1,InjectedBean2 are not injected with any values.
So basically InjectedBean1 and InjectedBean2 are null.
On null you are trying for get Operation which is leading to null pointer exception.
Adding ref to InjectedBean1 and InjectedBean2 will solve problem.
<bean id="SomeBean" class="com.stackoverflow.SomeBean" autowire="byName">
<property name="injectedBean1">
<ref bean="inject1" />
</property>
<property name="injectedBean2">
<ref bean="inject2" />
</property>
</bean>
Is it possible to invoke static method in Spring configuration file?
public MyClass {
public static void staticMethod() {
//do something
}
}
<bean id="myBean" class="MyClass">
<!-- invoke here -->
</bean>
When the static method creates an instance of MyClass you an do it like this
config
<bean id="myBean" class="MyClass" factory-method="staticMethod">
<!-- invoke here -->
</bean>
code
public static MyClass staticMethod() {
//create and Configure a new Instance
}
If you want the method only to be called on bean instantiation spring can't do it this way.
config
<bean id="myBean" class="MyClass" init-method="init">
<!-- invoke here -->
</bean>
code
public static void staticMethod() {
//create and Configure a new Instance
}
public void init() {
staticMethod();
}
try this
<bean id="b1" class="org.springframework.beans.factory.config.MethodInvokingBean">
<property name="staticMethod" value="MyClass.staticMethod" />
</bean>
see http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/factory/config/MethodInvokingBean.html
Try something like this:
<!-- call static method -->
<bean id="test" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetClass" value="MyClass" />
<property name="targetMethod" value="staticMethod" />
<property name="arguments">
<list>
<value>anArgument</value>
</list>
</property>
</bean>
Remove arguments as you might not need them.
Taken from https://gist.github.com/bulain/1139874
I was needing to call a static method. The above code worked fine.
This might be useful as well: How to make spring inject value into a static field.
If you are using annotations for spring configuration you can add the following method into your #Configuration class:
#Bean
public MethodInvokingFactoryBean methodInvokingFactoryBean() {
MethodInvokingFactoryBean methodInvokingFactoryBean = new MethodInvokingFactoryBean();
methodInvokingFactoryBean.setStaticMethod("MyClass.staticMethod");
return methodInvokingFactoryBean;
}
I have set up my resources like this:
<bean id="messageSource"
class="org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename">
<value>locale\\messages</value>
</property>
</bean>
My propertyFile:
battle.name=TestBattle
I would like to reach the text "TestBattle" when I use a bean:
<bean id="battlefield" class="com.mypackage.Battlefield" scope="prototype">
<constructor-arg index="0" value="battle.name" />
<constructor-arg index="1" ref="armies" />
</bean>
I want to refeer the message in the propertyFile in this line
<constructor-arg index="0" value="battle.name" />
Is there a way to do it without going into java using the
getMessage("battle.name",...
code in java?
At least, you could use spel to do it.
for example
<bean id="messageSourceAccessor" class="org.springframework.context.support.MessageSourceAccessor">
<constructor-arg ref="messageSource" />
</bean>
<bean id="battlefield" class="com.mypackage.Battlefield" scope="prototype">
<constructor-arg index="0" value="#{messageSourceAccessor.getMessage('battle.name')}" />
<constructor-arg index="1" ref="armies" />
</bean>
However it seems cumbersome if you have to translate many codes.
Other option is using a String to String PropertyEditor to do the translation.
public class MessageSourcePropertyEditor extends PropertyEditorSupport {
private MessageSourceAccessor messageSourceAccessor;
public MessageSourcePropertyEditor(MessageSource messageSource) {
this.messageSourceAccessor = new MessageSourceAccessor(messageSource);
}
#Override
public void setAsText(String text) throws IllegalArgumentException {
String value = text;
if (text.startsWith("i18n:")) {
value = messageSourceAccessor.getMessage(text.substring(5));
}
setValue(value);
}
}
public class MessageEditorRegistrar implements PropertyEditorRegistrar {
private MessageSource messageSource;
#Override
public void registerCustomEditors(PropertyEditorRegistry registry) {
registry.registerCustomEditor(String.class, new MessageSourcePropertyEditor(messageSource));
}
public MessageSource getMessageSource() {
return messageSource;
}
public void setMessageSource(MessageSource messageSource) {
this.messageSource = messageSource;
}
}
And use the prefix i18n: to translate codes, ie
<bean id="propertyEditorConfigure" class="org.springframework.beans.factory.config.CustomEditorConfigurer">
<property name="propertyEditorRegistrars">
<list>
<bean class="message.MessageEditorRegistrar">
<property name="messageSource" ref="messageSource" />
</bean>
</list>
</property>
</bean>
<bean id="battlefield" class="com.mypackage.Battlefield" scope="prototype">
<constructor-arg index="0" value="i18n:battle.name" />
<constructor-arg index="1" ref="armies" />
</bean>
So, I read that the best place to put the #Transactional annotation was outside the DAO classes which contains the db access methods, like in a service class which use those methods.
Now, the problem is, once I've already remove this annotations from the DAO classes, I launch the DAO test methods and the aforementioned exception raised. I put back the annotations in the DAO classes and this exception doesn't raises anymore.
Then my question is: how can I clear my DAOs of this annotations and still have my tests working?
Let's add some code:
DAO class
public class UserDAO extends IDAO implements IUserDAO {
#Override
//#Transactional(readOnly=true)
public User get(int idUser) {
return (User) currentSession().get(User.class,idUser);
}}
IDAO Class
public abstract class IDAO {
protected SessionFactory sessionFactory;
public void setSessionFactory(SessionFactory sessionFactory) {
this.sessionFactory = sessionFactory;
}
public Session currentSession()
{
return sessionFactory.getCurrentSession();
}
}
Test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:META-INF/spring/app-context.xml" })
public class UserDAOTest extends AbstractJUnit4SpringContextTests {
#Autowired
private IUserDAO userDAO;
#Test
public void testGetUser() throws Exception {
User user = userDAO.get(2);
assertNotNull(user);
}
}
app-config
<?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:tx="http://www.springframework.org/schema/tx" xmlns:jdbc="http://www.springframework.org/schema/jdbc"
xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
destroy-method="close">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url"
value="jdbc:mysql://localhost:3306/waldb" />
<property name="username" value="user" />
<property name="password" value="password" />
</bean>
<bean id="sessionFactory"
class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="annotatedClasses">
<list>
<value>com.wal.serverside.persistence.domain.User</value>
</list>
</property>
<property name="hibernateProperties">
<props>
<prop key="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</prop>
<prop key="hibernate.hbm2ddl.auto">update</prop>
</props>
</property>
</bean>
<bean id="transactionManager"
class="org.springframework.orm.hibernate4.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<bean id="userDAO" class="com.wal.serverside.persistence.DAO.UserDAO">
<property name="sessionFactory" ref="sessionFactory" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager" />
<context:component-scan base-package="com.wal.serverside.persistence" />
</beans>
Gosh, how much stupid can I be?
My test class didn't extend from AbstractTransactionalJUnit4SpringContextTests, so there were nor transaction nor session inside my tests.
This fixed it all:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:META-INF/spring/app-context.xml" })
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
#Transactional
public class UserDAOTest extends AbstractTransactionalJUnit4SpringContextTests {
#Autowired
private IUserDAO userDAO;
public void setUserDAO(IUserDAO userDAO) {
this.userDAO = userDAO;
}
#Test
public void testGetUser() throws Exception {
User user = userDAO.get(2);
assertNotNull(user);
}
}
Try to put not only #Transactional annotaition on your test class but also #TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false). Where you explicitly set the name of the transaction manager that you have defined in xml.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath:META-INF/spring/app-context.xml" })
#TransactionConfiguration(transactionManager="transactionManager", defaultRollback=false)
#Transactional(propagation=Propagation.REQUIRED, rollbackFor={Exception.class})
public class UserDAOTest {
...
}
Also transaction will not work if you explicitly create the application context in your test method and then get the bean from it:
ApplicationContext appContext = new ClassPathXmlApplicationContext(...);
SomeDAO someDAO = (SomeDAO) appContext.getBean(...);
instad of inhjecting it.
But I see this is not your case.
My code:-
<context:annotation-config/>
<bean id="arthmeticCalculator" class="com.manoj.aop.test.CalculatorImpl" lazy-init="true"/>
<bean id="stubCalculator" class="com.manoj.aop.test.StubCalculator" lazy-init="true"/>
<bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>*Calculator</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>methodNameAdvisor</value>
</list>
</property>
</bean>
<bean id="methodNameAdvisor"
class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedNames">
<list>
<value>add</value>
<value>sub</value>
</list>
</property>
<property name="advice" ref="loggingAroundAdvice" />
</bean>
<bean id="loggingAroundAdvice" class="com.manoj.aop.test.LoggingAroundAdvice">
<constructor-arg><ref bean="arthmeticCalculator"/></constructor-arg>
<constructor-arg><ref bean="stubCalculator"/></constructor-arg>
<constructor-arg><value>false</value></constructor-arg>
</bean>
<bean id="testService" class="com.manoj.aop.test.TestService">
<!--
<property name="arthmeticCalculator" ref="arthmeticCalculator"/>
-->
</bean>
Java Code:
package com.manoj.aop.test;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
public class LoggingAroundAdvice implements MethodInterceptor{
Calculator actualCalculator;
Calculator stubCalculator;
boolean useStub;
public LoggingAroundAdvice(Calculator actualCalculator, Calculator stubCalculator, boolean useStub) {
this.actualCalculator = actualCalculator;
this.stubCalculator = stubCalculator;
this.useStub = useStub;
}
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
System.out.println("Around Invoice called");
Calculator calc = useStub ? stubCalculator: actualCalculator;
System.out.println(calc.getClass().getName());
Object result = methodInvocation.getMethod().invoke(calc, methodInvocation.getArguments());
return result;
}
}
import org.springframework.beans.factory.annotation.Autowired;
public class TestService {
#Autowired
private Calculator arthmeticCalculator;
public void test(){
System.out.println(arthmeticCalculator.getClass().getName());
System.out.println(arthmeticCalculator.add(5, 10.5));
}
}
Sorry guys I dont know how to format text in this editor,
My problem is :-
Spring is creating proxy for the class but never execute the Invoke method of the Around advice. Can some body please tell me whats going on and how to make it call the invoke method?
Here is the output of test class:-
$Proxy4
15.5
Thanks ,
Manoj
Which version of Spring are you using? The way you are doing proxy is the older way. The better way is to use the annotation or pure POJO+XML way. You can check a short introduction on AOP section here