After a lot of time waste trying to find a answer, I decided to post this doubt.
I have a class that I would like to intercept by the Spring AOP.
ObjectToBeProxied.java
package com.ee.beans;
#Service
#Component
#Transactional
public ObjectToBeProxied implements IObjectToBeProxied {
#Autowired
private ParameterValueService parameterValueService;
public void doStuff() {
// do something before the call
getSelfRef().findEventParameterValue(new ParameterValueFilter());
// do something after
}
#HandleException
private Boolean findEventParameterValue(ParameterValueFilter parameterValueFilter) {
ParameterValue parameterValue = getSelfRef().parameterValueService.findParametertValueByFilter(parameterValueFilter);
return parameterValue.value();
}
private ObjectToBeProxied getSelfRef() {
return (ObjectToBeProxied) AopContext.currentProxy();
}
}
ExceptionHandlerAspect.java
package com.ee.aspects;
#Component
public class ExceptionHandlerAspect {
private static Logger LOGGER = Logger.getLogger(ObjectToBeProxied.class);
public Object handleAround(ProceedingJoinPoint joinPoint) throws Throwable {
// Handling the exception. Need to continue either the method throws a expcetion
// but it need to be logged
try {
return joinPoint.proceed();
} catch (Exception e) {
// something to handle the exception
}
return null;
}
}
Spring AOP configuration:
<?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"
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/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<aop:aspectj-autoproxy expose-proxy="true" proxy-target-class="true"/>
<!-- Activates various annotations to be detected in bean classes -->
<context:annotation-config/>
<context:spring-configured />
<!-- Scans the classpath of this application for #Components to deploy as beans -->
<context:component-scan base-package="com.ee.beans"/>
<bean id="exceptionHandlerAspect" class="com.ee.aspects.ExceptionHandlerAspect" />
<aop:config>
<aop:aspect id="exceptionHandlerConfig" ref="exceptionHandlerAspect">
<!-- Trata exceções lançadas -->
<aop:pointcut id="exceptionHandlerAroundMethod" expression="execution(* com.ee.beans.ObjectToBeProxied.*(..)) && #annotation(com.ee.exceptions.HandleException)" />
<aop:around pointcut-ref="exceptionHandlerAroundMethod" method="handleAround" />
</aop:aspect>
</aop:config>
When I call the method ObjectToBeProxied.doStuff(), ObjectToBeProxied.parameterValueService autowired is ok, not null.
But, when the aspect intercept the method call ObjectToBeProxied.findEventParameterValue(..) and execute the ExceptionHandlerAspect.handleAround(..), the ObjectToBeProxied.parameterValueService is not ok, it's null.
Debugging it, I can figure out that the Spring Aspect return the ObjectToBeProxied proxy after intercept it, but without the autowired attributes objects.
Where am I getting wrong?
Related
I have a question about Spring Integration (or basically Spring in common):
I use the a WebService Inbound Gateway in my Spring XML configuration:
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
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.xsd
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd" >
<int-ws:inbound-gateway
id="ws-in-gw-user"
request-channel="in-user"
reply-channel="out-user"
mapped-request-headers="*"
/>
...
</beans>
When I use <int-ws:inbound-gateway> Tag, a SimpleWebServiceInboundGateway is created. Now I want to exchange this implemantation with a self written extension of this class. Any ideas how to do it?
you could use a BeanPostProcessor bean.
import org.springframework.beans.factory.config.BeanPostProcessor;
public class SimpleWebServiceInboundGatewayBeanPostProcessor implements BeanPostProcessor{
public Object postProcessBeforeInitialization(Object bean, String beanName){
if(bean instanceof SimpleWebServiceInboundGateway) {
return new MyCustomWebSerivceInboundGateway();
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
In your config:
<bean class="....SimpleWebServiceInboundGatewayBeanPostProcessor" />
A BeanPostProcessor allows you to exhange beans during initialization.
I'm developing a module on an OSGi application, using Spring MVC and Virgo Webserver.
On my module I have a Controller, that access a Manager, which has a list of handlers that are responsible for handling report generation.
Everything was doing fine until I had to call a transactional method from an external service. Since none of my classes were transactional I had to add the references to the transation manager and annotation-driven. Then, my Manager stopped being notified.
I understand that when using annotation-driven all my beans must implement a public interface in order for the proxying mechanism to work. And as far as I know, all the classes are (one of them wasn't, but then I changed it).
My configuration files are:
bundle-context.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"
xmlns:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx"
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
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:annotation-config />
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="reportManager" class="reportmodule.manager.impl.ReportManagerImpl"/>
<bean id="mvpepReportHandler" class="reportmodule.manager.impl.MVPEPReportHandler"/>
<bean id="reportConfigDao" class="reportmodule.repository.impl.ReportConfigurationHibernateDAOImpl"/>
<bean id="oSGIChangeReportHandler" class="reportmodule.osgi.impl.OSGIChangeReportHandlerImpl"/>
<bean id="reportController"
class="reportmodule.controller.impl.ReportControllerImpl"/>
<bean id="reportControllerHandlerMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<value>/module/reportController/**=reportController</value>
</property>
<property name="alwaysUseFullPath" value="true"></property>
</bean>
</beans>
and my bundle-osgi.xml is as follows:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:osgi="http://www.springframework.org/schema/osgi"
xmlns:osgi-compendium="http://www.springframework.org/schema/osgi-compendium"
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
http://www.springframework.org/schema/osgi
http://www.springframework.org/schema/osgi/spring-osgi.xsd
http://www.springframework.org/schema/osgi-compendium
http://www.springframework.org/schema/osgi-compendium/spring-osgi-compendium-1.2.xsd">
<osgi:reference id="transactionManager" interface="org.springframework.transaction.PlatformTransactionManager" />
<osgi:reference id="sessionFactory" interface="org.hibernate.SessionFactory" />
<osgi:reference id="smaCoreUtilService" interface="core.util.service.SmaCoreUtilService" />
<osgi:service ref="reportControllerHandlerMapping"
interface="org.springframework.web.servlet.HandlerMapping"
context-class-loader="service-provider"
auto-export="interfaces"/>
<osgi:service interface="reportmodule.api.manager.ReportManager" ref="reportManager" auto-export="interfaces"/>
<osgi:service interface="reportmodule.api.manager.ReportHandler" ref="mvpepReportHandler" auto-export="interfaces"/>
<osgi:service interface="reportmodule.repository.ReportConfigurationDAO" ref="reportConfigDao" auto-export="interfaces"/>
<osgi:service interface="reportmodule.osgi.OSGIChangeReportHandler" ref="oSGIChangeReportHandler" auto-export="interfaces"/>
<osgi:list cardinality="0..N" id="reportHandler" interface="reportmodule.api.manager.ReportHandler" greedy-proxying="true">
<osgi:listener ref="oSGIChangeReportHandler" bind-method="register" unbind-method="unregister"/>
</osgi:list>
</beans>
So, after all the services are being published the oSGIChangeReportHandler.register is called (I'm able to debbug it):
#Service(value="oSGIChangeReportHandler")
public class OSGIChangeReportHandlerImpl implements OSGIChangeReportHandler {
private ReportManager reportManager;
/**
* #param reportManager the reportManager to set
*/
#Autowired
public void setReportManager(ReportManager reportManager) {
this.reportManager = reportManager;
}
#SuppressWarnings("rawtypes")
public void register(ReportHandler reportHandler, Map properties) {
reportManager.addReportHandler(reportHandler);
}
#SuppressWarnings("rawtypes")
public void unregister(ReportHandler reportHandler, Map properties) {
reportManager.removeReportHandler(reportHandler);
}
}
And although the debugger shows Proxies for both the reportManager and reportHandler on the register method, the debugger does not halts on the ReportManagerImpl.addReportHandler method:
#Service(value="reportManager")
#Transactional(propagation = Propagation.MANDATORY, rollbackFor = Exception.class)
public class ReportManagerImpl implements ReportManager {
private ReportConfigurationDAO reportConfigurationDAO;
private ArrayList<ReportHandler> reportHandlers = new ArrayList<ReportHandler>();
/**
* #param reportConfigurationDAO the reportConfigurationDAO to set
*/
#Autowired
public void setReportConfigurationDAO(ReportConfigurationDAO reportConfigurationDAO) {
this.reportConfigurationDAO = reportConfigurationDAO;
}
#Override
#Transactional
public InputStream gerarRelatorio(ReportRequest repoReq) throws NegocioException {
// Generates the report...
}
/* (non-Javadoc)
* #see reportmodule.api.manager.ReportManager#addReportHandler(reportmodule.api.manager.ReportHandler)
*/
#Override
public void addReportHandler(ReportHandler handler) {
if (handler != null) {
this.reportHandlers.add(handler);
}
}
/* (non-Javadoc)
* #see reportmodule.api.manager.ReportManager#removeReportHandler(reportmodule.api.manager.ReportHandler)
*/
#Override
public void removeReportHandler(ReportHandler handler) {
if (handler != null) {
this.reportHandlers.remove(handler);
}
}
}
I must stress that when I remove the tx:annotation-driven tag from the bundle-context.xml file, everything works fine (the handler is properly added to the list during startup).
So, what am I missing here?
Problem solved!
As you can see on my code above, I was defining the beans both via XML and Annotation, thus every bean was duplicated in runtime. Then, when I added the tx:annotation-driven tag the application begun intercepting the wrong bean. It was indeed notifying a bean, but an orphan bean.
Here´s an example working with tx. Look this line:
xmlns:tx="http://www.springframework.org/schema/tx"
and this on schemaLocation:
http://www.springframework.org/schema/tx/spring-tx.xsd
Source:
http://www.springbyexample.org/examples/hibernate-transaction-annotation-config.html
Consider the following configuration
public class MainApp {
public static void main(String args[]){
ApplicationContext ac=new ClassPathXmlApplicationContext("src/Beans.xml");
HelloWorld obj1=(HelloWorld) ac.getBean("helloWorld");
obj1.setMessage("OBJ1");
HelloWorld obj2=(HelloWorld) ac.getBean("helloWorld");
//obj2.setMessage("OBJ2");
System.out.println(obj1.getMessage());
System.out.println(obj2.getMessage());
}
}
#Scope("prototype")
public class HelloWorld {
String message;
public String getMessage() {
return "Your Message:"+message;
}
public void setMessage(String message) {
this.message = message;
}
}
<?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.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:annotation-config />
<context:component-scan base-package="SpringDemo.src" />
<bean id="helloWorld" class="src.HelloWorld">
</bean>
</beans>
If i am not wrong it is showing the behavior of a Singleton scope. can someone let me know why it is not behaving as a "Prototype" scope?
You have this <bean id="helloWorld" class="src.HelloWorld"> in the xml configuration. When no scope is specified the scope defaults to singleton. The xml configuration overrides the annotation. Remove #Scope("prototype") and add scope="prototype" in the xml.
I'm trying to get a grip on auto-wiring in Spring, but I can't seem to properly instantiate the bean (a DocumentBuilder). I have created a custom JSP tag as such:
public class MyTag extends SimpleTagSupport {
#Autowired
private DocumentBuilder documentBuilder;
public void setBuilder(DocumentBuilder builder) {
this.documentBuilder = builder;
}
#Override
public void doTag() throws IOException {
// documentBuilder is null in here!
}
}
This is the servlet configuration:
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
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">
<!-- Scan for HTTP/REST controllers -->
<context:component-scan base-package="the.right.package" />
<context:annotation-config/>
<bean id="documentBuilderFactory"
class="javax.xml.parsers.DocumentBuilderFactory"
factory-method="newInstance">
<property name="validating" value="false" />
<property name="ignoringElementContentWhitespace" value="true" />
</bean>
<bean id="documentBuilder" class="javax.xml.parsers.DocumentBuilder"
factory-bean="documentBuilderFactory"
factory-method="newDocumentBuilder">
</bean>
</beans>
Any ideas?
You can only inject in spring beans! But Jsp-Tags are no Spring Beans, so the Autowird annotation will be completely ignored, and therefore the field is null.
There are two solution:
use the #Configurable Support. -- But that requires real AspectJ. (I have never tried it for Tags, but I guess it will work for tags like for every other normal class). #see Spring Reference: Chapter 7.8.1 Using AspectJ to dependency inject domain objects with Spring
Extend your tag from the abstract Spring class RequestContextAwareTag. This provides access to the WebApplicationContext via getRequestContext().getWebApplicationContext(). Then you can use the WebApplicationContext to obtain the required beans programmatic.
Try to modify the code like this
public class MyTag extends SimpleTagSupport {
private DocumentBuilder documentBuilder;
#Autowired
public void setBuilder(DocumentBuilder builder) {
this.documentBuilder = builder;
}
#Override
public void doTag() throws IOException {
// documentBuilder is null in here!
}
}
You can use #Autowired if you mark your tag class as Spring bean. But it's stupid, because simple tags not caching by container. Each request creates own tag instance, but wiring happend only conteiner starts.
How do I implement AOP with an annotated Controller?
I've search and found two previous posts regarding the problem, but can't seem to get the solutions to work.
posted solution 1
posted solution 2
Here's what I have:
Dispatch Servlet:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
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/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-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="com.foo.controller"/>
<bean id="fooAspect" class="com.foo.aop.FooAspect" />
<aop:aspectj-autoproxy>
<aop:include name="fooAspect" />
</aop:aspectj-autoproxy>
</beans>
Controller:
#Controller
public class FooController {
#RequestMapping(value="/index.htm", method=RequestMethod.GET)
public String showIndex(Model model){
return "index";
}
}
Aspect:
#Aspect
public class FooAspect {
#Pointcut("#target(org.springframework.stereotype.Controller)")
public void controllerPointcutter() {}
#Pointcut("execution(* *(..))")
public void methodPointcutter() {}
#Before("controllerPointcutter()")
public void beforeMethodInController(JoinPoint jp){
System.out.println("### before controller call...");
}
#AfterReturning("controllerPointcutter() && methodPointcutter() ")
public void afterMethodInController(JoinPoin jp) {
System.out.println("### after returning...");
}
#Before("methodPointcutter()")
public void beforeAnyMethod(JoinPoint jp){
System.out.println("### before any call...");
}
}
The beforeAnyMethod() works for methods NOT in a controller; I cannot get anything to execute on calls to controllers. Am I missing something?
In order to put an aspect on a HandlerMethod in a class annotated with #Controller in Spring 3.1 you need to have the proxy-target-class="true" attribute on the aspectj-autoproxy element. You also need to have the CGLIB and the ASM libraries as a dependency in your WAR/EAR file. You can either specify your annotated aspect class as a bean and use the aop:include as stated above or you can leave the aop:include out and add a filter similar to this in your component scan element:
<context:component-scan>
<context:include-filter type="aspectj"
expression="com.your.aspect.class.Here"/>
</context:component-scan>
I do not know if this is a requirement as a result of Spring 3.1 only but I know that without this, you will not be able to put an aspect on your controller HandlerMethod. You would get an error similar to:
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
HandlerMethod details:
Controller [$Proxy82]
Method [public void com.test.TestController.testMethod(java.security.Principal,javax.servlet.http.HttpServletResponse) throws javax.servlet.ServletException]
Resolved arguments:
[0] [null]
[1] [type=com.ibm.ws.webcontainer.srt.SRTServletResponse] [value=com.ibm.ws.webcontainer.srt.SRTServletResponse#dcd0dcd]
This is not required if your aspect is on a method in a class that is not a controller
I am going to state an alternative solution (sorry not a direct answer) but what you wanting to do is probably best done via interceptors and filters.