Why does my Spring AOP aspect work in my unit test but not my webapp? - spring

I have an aspect working correctly in my unit tests, a log message is printed from the actual method, and afterwards from the aspect applied.
When running my webapp though, I only see the result of the '#afterReturning' advice applied, my method does not execute first.
My config:
<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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="loggingAspect" class="com.mypackage.MyAspect" />
</beans>
Any ideas ?
Thanks for your help.

Information you provided in not enough to analyze. So check below things in your web app,
1) Verify that the method you are expecting to be intercepted by your advice, belongs to spring bean class & not some other servlet or other class defined outside spring. If any class is not initialized through spring then aop advice can not be applied to that class.
2) Verify that above aop & advice configs are put in proper context xml for spring. Like in case of spring-mvc, you have to explicitely define the name of your application context xml. And in that application context xml only you have to write aop configurations.
3) Verify if your pointcut is appropriate to find exact class & method for advice.
See if these things are in place.

Related

Where is hided <context:annotation-config/> in spring boot?

To work via annotations in Spring need to define:
<?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.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"
xmlns:context="http://www.springframework.org/schema/context">
<context:annotation-config/>
</beans>
at
annotation-config.xml
But I created a simplest Spring boot Application (lets say I select lust Web in initialazr)
It works on annotations but there isn't any annotation-config.xml there and nor mentioning of ,
where is hidden?
You only need to add <context:annotation-config /> or <context:component-scan /> (which implies annotation driven config) when using an ApplicationContext implementation that doesn't support annotations out-of-the-box.
When only using XML based configuration you also use one of the XML enabled ApplicationContext implementations, generally that would be XmlWebApplicationContext. With these you would need to instruct the ApplicationContext to enable annotation processing.
When using Java based configuration you generally use an annotation based ApplicationContext, default would be AnnotationConfigWebApplicationContext. Due to its nature of processing Java configuration classes it has annotation processing enabled by default.
Spring Boot uses the latter (it actually uses a specialized subclass for this). Hence you don't need to explicitly enable it.

How to make Aspect Advise a Junit test in Spring?

My Aspect:
#Aspect
public class testAutomationAspect {
#Before(value="execution(* my.package.*.*(..))")
public void BeforeTest(){
System.out.println("?????");
}
}
My Test:
#ContextConfiguration(locations={"classpath:context/automation-context.xml"})
public class TestNgExample extends TestCase{
public void testSomething2() throws java.io.IOException{
Assert.assertNotNull(null);
}
}
My 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-3.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="true">
<aop:include name="automationAspect" />
</aop:aspectj-autoproxy>
<bean id="automationAspect" class="my.package.AutomationAspect" />
</beans>
Right now, the aspect doesn't run when the junit runs. My guess is that the xml context doesn't define the Test cases so they are in separate contexts somehow. How would you get the junit test in the same context? I tried using #ContextConfiguration to achieve this. Not sure if that is the right way to do it.
Spring AOP is proxy-based and only works with Spring Beans. Unless your test class is a Spring bean, it will not work. In order to apply aspects to normal (non Spring Bean) POJOs you can use full-blown AspectJ which works without proxies.
Anyway your problem is that the class TestNgExample is not part of Spring context. Wrap each method in one class and declare it as bean.
You have to run your test by using SpringJUnit4ClassRunner then all of your aspects, validators and others will work :) #ContextConfiguration is ok. You need it to define the test context.
#RunWith(SpringJUnit4ClassRunner.class)
but I am not sure Aspects will work if you attach them to the tests. Maybe it is better to invoke them around Service methods that you are going to test.

Spring managed Transaction commits where it shouldn't

in "applicationContext-base.xml" I add below:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:p="http://www.springframework.org/schema/p"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:tx="http://www.springframework.org/schema/tx"
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.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
>
.....
<!-- transaction support-->
<!-- PlatformTransactionMnager -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- enable transaction annotation support -->
<tx:annotation-driven transaction-manager="txManager" />
and I want to setup a transaction to a function in the "controller"
#Controller
#RequestMapping("/ywdata")
public class YwController extends BaseController{
....
#Transactional(timeout=1)
private void submitNewSXSQ(Map map, HttpServletRequest request, HttpServletResponse response) throws Exception {
...//STEP1 :do some db insert and update STEP1
if(true)
throw new Exception("test transaction ");
...//STEP2: do another db insert and update
and I expected the db operation should never be commit since I throw a exception before return. but actually not.
There are multiple issues with your code:
#Transactional on private methods don't work
#Transactional on #Controller annotated classes usually don't work
rollback is not performed for checked exceptions
The last issue can be easily understood. Let me explain the first two problems. AOP in Spring works like this:
before the application context is initialized, Spring searches for beans which require method interception
a special proxy bean is registered for each of these beans... the proxy is either dynamic interface implementation (JDK proxy) or a dynamic subclass (CGLIB proxy) of your target bean
the proxy replaces the definition of your bean... original definition is renamed and marked as not eligible for autowiring (but it is still present on application context)
the methods on proxy are very dumb - all they do is interception logic (i.e. calling some aspect before/after/around execution) and calling the original proxied target bean method
Why private methods are problem:
with JDK proxying (default):
#Transactional won't work if you have #Transactional on non-interface method (only interface methods are present on the proxy)
with CGLIB proxying:
##Transactional won't work if you have #Transactional on private or final method (only non-private and non-final methods can be overridden in dynamic subclass)
And why controllers are problem:
Spring's RequestMappingHandlerMapping (bean responsible for mapping requests to your #Controllers) asks application context to get all beans with #Controller annotation
this might return your original class, not the proxy (I think there was a bug for this in Spring JIRA, so it might be already fixed)
in case of JDK proxying, you need to add the annotation to the interface (so that the proxy is annotated)... this means that you would need to define interfaces for your controllers
What to do:
I suggest you to move transaction handling to service level
If you want your transaction to be wrapped around the whole request, you might take your inspiration from OpenSessionInViewFilter.
Also I encourage you to put breakpoint in your code and check the stack trace and look for the AOP proxy.
If you still want to manually handle transactions in some random part of code, you can use TransactionTemplate helper class.
From Spring documentation, it is the default behavior: Transactions are marked for rollback only for unchecked exceptions.
See section 10.5.3 of doc

Spring claiming that identical classnames are different

I'm making my first forays into spring and am getting the following error trying to run on eclipse:
org.springframework.beans.factory.BeanNotOfRequiredTypeException: Bean named 'model' must be of type [com.myCompany.project.Model], but was actually of type [com.myCompany.project.Model]
Code causing the exception:
import com.myCompany.project.Model;
// some code
public Model getModel() {
ClassPathXmlApplicationContext applicationContext =
new ClassPathXmlApplicationContext("client-context.xml");
return applicationContext.getBean("model", Model.class);
}
Spring 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:p="http://www.springframework.org/schema/p"
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-3.0.xsd">
<context:annotation-config />
<bean id="model" class="com.myCompany.project.Model" />
</beans>
The client-context.xml is located in project/resources, the code in project/src/main/
I assume this is a classpath issue, but I am at a loss as to what to do to fix it.
Java and Spring are case sensitive. This:
<bean id="model" class="com.myCompany.project.model" />
is not the same as this:
<bean id="model" class="com.myCompany.project.Model" />
I'd think about some better names. Those aren't very insightful.
I don't like the way you're going about this. You shouldn't have a bean that has to access the app context this way. You'll have to post more code to be sure, but you ought to be wiring that model bean into object that wants it, not doing what you're doing.
The only reason for doing what you're proposing is if the bean interacting with the app context is creating using new rather than the Spring bean factory. Since you're just starting with Spring, I would recommend letting Spring handle all dependencies.

404 when adding beans in the applicationContext xml file

I'm trying to learn Spring, Hibernate and Webflow.
Why is it that when I add beans to my applicationContext i get a 404 when I run the project.
Here's my context 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"
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">
<bean id="roleService" name="roleService" class="ws.Service.RoleServiceImp" />
</beans>
Now when I add another bean, it goes 404. I'm not sure what's wrong. i.e:
<bean id="userService" name="userService" class="ws.Service.UserServiceImp" />
Am I missing something? Any help will be appreciated.
If an Web Spring Application can not start, for example if the context-xml contains a bean that does not exist, or a syntax error, then Spring will not "start" the context.
If you have a spring web application and the application does not start then, the whole spring web request handling stuff does not too. And when you not try to access an web page normally handled by the spring controllers the web server can not found this handler, so the web server says: not found - 404.
that is the answer to your question: "Why is it that when I add beans to my applicationContext i get a 404 when I run the project."
Anyway I believe you are more interested in why the application did not start:
have a look in the logs, there must be a stack trace
if you did not find the problem, with help of the stack trace, then add it to your question (or ask a new one).

Resources