unable to use Spring Advice(#Before) using annotations - spring

I am new to springs and trying to run a simple java application with java advices....
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: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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<aop:aspectj-autoproxy>
<aop:include name="com.cts.two.Advices"/>
</aop:aspectj-autoproxy>
<context:annotation-config/>
<context:component-scan base-package="com.cts.two"></context:component-scan>
</beans>
Advice Class
package com.cts.two;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class Advices implements Adv{
#Pointcut("execution(* com.cts.two.*.*(..))")
public void advice(){
}
#Before("advice()")
public void before(JoinPoint name) throws Throwable{
System.out.println("inside advices");
/*System.out.println(name.getClass() + " this is get class");
System.out.println(name.getSignature().getName() + " this is the get signatue and get name");*/
}
}
class on which advice needs to be applied...I want the before method of Advice class to be executed before below mentioned test() method
package com.cts.two;
import org.springframework.stereotype.Component;
#Component
public class ClassA {
private ClassB b= new ClassB();
public void setB(ClassB b) {
this.b = b;
}
public void test(){
System.out.println("inside classA test");
//b.test();
}
}
caller of methods/test class/main class
package com.cts.two;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class CallerAB {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context = new ClassPathXmlApplicationContext(
"AllAnnotations.xml");
ClassA calledA = (ClassA) context.getBean("classA");
calledA.test();
}
}
the problem is that when I run the code directly the test method of class A is executed but the advice is not...
Kindly advice..
Am i missing something ???
AspectJ 1.6.12 jar is also added...

Aspects should be decalred as beans.
#Aspect doesn't do it automatically, <aop:include> doesn't do it as well (it sets additional restriction on beans that can be used as aspects).
So, you need
#Aspect
#Component
public class Advices implements Adv { ... }
and don't need <aop:include>.

As mentioned in the answer from #axtavt you need to add the #Component annotation. But you also need to remove the <aop:include>. Your spring wiring xml should just be:
<aop:aspectj-autoproxy/>
<context:annotation-config/>
<context:component-scan base-package="com.cts.two"/>
As stated in the spring AOP documentation, the name attribute in the <aop:include> element is supposed to be a bean name, not a class name. Specifying a bean explicitly overrides Spring's auto-detection and specifying it incorrectly means that there is no aspect used at all.

Related

How to pass base package as a variable inside pointcut expression in Spring AOP?

I am creating a java library for logging purpose so that if any application uses my library then spring AOP's advices are applied to each method of the application. But in my library, I don't know the base package of the applications that will be using it. So, the applications should specify their base package in the application.properties file and my library will use it and put it in my pointcut as a variable. But seems like pointcut expressions only accepts contants inside it.
I don't want to go for annotation based approach.
#Component
#Aspect
#Slf4j
public class LoggingAop {
#Value("${base.package}")
String basePackage;
#Pointcut("within("+basePackage+"..*)") //error
public void logAllMethods()
{
}
}
Answers for the following questions explains why this is not possible with annotation based Spring AOP configuration
Get rid of "The value for annotation attribute must be a constant expression" message
How to supply value to an annotation from a Constant java
Spring boot supports xml based configurations and the requirement can be achieved as follows.
Assuming we have to intercept all the method calls within base package com.app.service
Have an entry in resources/application.properties.
base.package=com.app.service
An Aspect to log method calls within the base package can be as follows
package com.app.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogAspect {
Logger log = LoggerFactory.getLogger(LogAspect.class);
public Object logAllMethods(ProceedingJoinPoint pjp) throws Throwable {
log.info("Log before method call");
try {
return pjp.proceed();
}finally {
log.info("Log after method call");
}
}
}
The aop configuration can be defined as follows .
File : resources/aop-application-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
https://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean id="logAspect" class="com.app.aspect.LogAspect"></bean>
<aop:config>
<aop:aspect id="logallaspect" ref="logAspect" >
<!-- #Around -->
<aop:pointcut id="logAllMethodsAround" expression="within(${base.package}..*)" />
<aop:around method="logAllMethods" pointcut-ref="logAllMethodsAround" />
</aop:aspect>
</aop:config>
</beans>
Do note that the pointcut expression is constructed with the application.properties entry.
Remember to load the aop-application-context.xml as follows
#SpringBootApplication
#ImportResource("classpath:aop-application-context.xml")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
The above code will advice all the interceptable Spring bean method calls within the defined base package and logs them .
Hope this helps.

Spring Autowiring a list into a bean results in a NoSuchBeanDefinitionException

In a Spring 3.1.3.RELEASE project I want to create and autowire a list that contains some enum to some service.
Unfortunately the autowiring fails (NoSuchBeanDefinitionException), while I can retrieve the bean in the context and manually wire the dependency.
Here is a small testcase showcasing the issue (using Spring 3.1.3 and JUnit):
The XML context (int package /junk):
<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: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/util http://www.springframework.org/schema/util/spring-util.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="junk"/>
<util:list id="myList" value-type="junk.GreatThings">
<value>SUPER</value>
<value>COOL</value>
</util:list>
</beans>
The enum:
package junk;
public enum GreatThings {AMAZING, GREAT, SUPER, COOL}
The test class (in package junk - I've removed the import for clarity):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath*:junkcontext.xml"})
public class TestAutowiringSupport {
#Autowired #Qualifier("myList") List<GreatThings> greatThings;
#Test public void testSuperCool() {
Assert.assertThat(greatThings, hasItem(SUPER));
Assert.assertThat(greatThings, hasItem(COOL));
}
}
This result in a NoSuchBeanDefinitionException.
I have try to put a #Qualifier with my bean id to help Spring to perform the wiring without any success.
My IDE is however able to detect the wiring by itself:
And if I use the Spring lifecycle callbacks to get back the bean and wire it manually then, it is fine.
Version that compiles and runs fine:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath*:junkcontext.xml"})
public class TestAutowiringSupport implements ApplicationContextAware
{
ApplicationContext ctx;
List<GreatThings> greatThings;
#Override
public void setApplicationContext(ApplicationContext ctx) throws BeansException {this.ctx = ctx;}
#PostConstruct
public void manualWiring() {greatThings = (List<GreatThings>) ctx.getBean("myList");}
#Test public void testSuperCool() {
Assert.assertThat(greatThings, hasItem(SUPER));
Assert.assertThat(greatThings, hasItem(COOL));
}
}
What's wrong with the autowiring in this case ?
Looks like some problem with generics.
With Spring 4.1 I am able to execute this code: where greatThings is of type List<Object>
#Qualifier("myList")
#Autowired List<Object> greatThings;
#Test
public void testSuperCool() {
Assert.assertThat(greatThings, Matchers.hasSize(2));
Assert.assertThat(greatThings, Matchers.hasItem(GreatThings.SUPER));
Assert.assertThat(greatThings, Matchers.hasItem(GreatThings.COOL));
}
As stated in the reference documentation, placing the #Autowired annotation over a typed collection means "find all beans of the given type (GreatThings in your case), put them in a collection and inject that collection". You are getting the exception because there are no beans of type GreatThings declared.
The problem is that there is no easy way to declare enum values as beans. Then again I don't see the use case to be honest.

Why it failed when combining #component and bean?

I am trying to use #component and regular beans. And below is my code:
MainApp.java
package com.company;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class MainApp {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("Beans.xml");
TextEditor te = (TextEditor) context.getBean("textEditor");
te.spellCheck();
}
}
SpellChecker.java
package com.company;
public class SpellChecker {
public SpellChecker(){
System.out.println("Inside SpellChecker constructor." );
}
public void checkSpelling(){
System.out.println("Inside checkSpelling." );
}
}
TextEditor.java
package com.company;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
#Component
public class TextEditor {
#Autowired
private SpellChecker spellChecker;
#Autowired
#Qualifier("region")
private String region;
public TextEditor() {
System.out.println("I am in " + region );
}
public SpellChecker getSpellChecker( ){
return spellChecker;
}
public void spellCheck(){
spellChecker.checkSpelling();
}
}
Beans.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-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="com.company" />
<bean id="spellChecker" class="SpellChecker" />
<bean id="region" class="java.lang.String">
<constructor-arg value="Vancouver" />
</bean>
</beans>
However, when I run this code, it gave error messages:
Cannot find class [SpellChecker] for bean with name 'spellChecker' defined in class path resource
When I remove the bean in the Beans.xml for SpellChecker and mark #component to it as well. Then it works (yet, the region String is still empty).
So my question is: why cannot we autowired a bean in a #Component class?
That isn't related to Spring Integration, please, choose tags properly for the questions.
You problem is that SpellChecker is in the com.company package, but <bean class=""> really requires the fully qualified class name to define the proper class to instantiate.
The base-package="com.company" is exactly for the #Component to scan, but that fully doesn't relate to regular <bean> definition.
By using fully qualified class name it should work...
<bean id="spellChecker" class="com.company.SpellChecker" />

How to load Spring applicationContext into Jersey Application

I'm creating a Jersey JAX-RS web service with the following:
package com.mycompany.rest.config;
import java.util.Set;
import javax.ws.rs.ApplicationPath;
import javax.ws.rs.core.Application;
#ApplicationPath("")
public class ApplicationConfig extends Application {
#Override
public Set<Class<?>> getClasses() {
return getRestResourceClasses();
}
private Set<Class<?>> getRestResourceClasses() {
Set<Class<?>> resources = new java.util.HashSet<Class<?>>();
resources.add(com.mycompany.rest.controllers.UserController.class);
return resources;
}
}
However, when I try and integrate a spring applicationContext such as the following :
<?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:component-scan base-package="com.mycompany.rest.user"/>
<bean id="userService" class="com.mycompany.rest.user.UserServiceImpl" />
</beans>
I get null pointers from my controller class for the userService being loaded by the bean pasted above. I'm assuming this is because the application context is not being loaded into the Jersey application.
Is there a way of doing this?
If you want to inject Spring beans into your Jersey resource classes, you need to add jersey-spring3 dependency to your class path. see https://jersey.java.net/documentation/latest/spring.html
An hello-world example can be found at https://github.com/jersey/jersey/tree/2.19/examples/helloworld-spring-webapp
I had the same problem, and i fixed it by adding #autowired to my service, and extending SpringBeanAutowiringSupport like the following :
#Path("/mails")
public class MailController extends SpringBeanAutowiringSupport{
#Autowired
private MailService mailService;
#GET
#Path("/{code}")
#Produces({ MediaType.APPLICATION_JSON, MediaType.APPLICATION_XML })
public String getMails(#PathParam("code") String code) throws Exception {
//function logic
}
Note that this is working without integrating jersey-springx.jar
And I'm using Spring 3 along with jersey-bundle-1.19.1.jar

No unique bean of type is defined: expected single bean but found 0:

I have one spring based standalone project (PTSJMSProxy). I refer http://sahits.ch/blog/?p=2326
In the PTSJMSProxy I have followings.
1) SimpleWriterService.java
package com.test;
import org.springframework.stereotype.Service;
#Service
public class SimpleWriterService {
public void sayHello() {
System.out.println("Hello Spring DI service!");
}
}
2) ComponentConsumer.java
package com.test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class ComponentConsumer {
#Autowired
private SimpleWriterService service;
public void consume() {
service.sayHello();
}
}
3) ProxyJMSClient.java
package com.test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class ProxyJMSClient {
// I commented some portions,but working fine
// Example #Autowired and also in the constructure
// #Autowired
private ComponentConsumer consumer;
public ProxyJMSClient() {
ApplicationContext context = new ClassPathXmlApplicationContext(
"applicationContext.xml");
// AutowireCapableBeanFactory acbFactory =
// context.getAutowireCapableBeanFactory();
// acbFactory.autowireBean(this);
consumer = context.getBean(ComponentConsumer.class);
}
public void callMyJMSClient() {
this.consumer.consume();
}
}
4) Test.java
package com.test;
public class Test {
public static void main(String[] args) {
(new ProxyJMSClient()).callMyJMSClient();
}
}
5) applicationContext.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"
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 http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<tx:annotation-driven />
<context:annotation-config />
<context:component-scan base-package="com.test" />
</beans>
Now when I invoke Test.java from eclipse Run As -Java Application I get the expected out put.
Output - Hello Spring DI service!
=============================================================================
Now I created the Jar with Eclipse export as Jar. Jar Name -PTSJMSProxy.jar
===============================================================================
My objective is to use this jar from a non spring java project
===============================================================================
I created another java project in eclipse "TestProxy"
In that project I add all the required Spring Jar and PTSJMSProxy.jar
Created TestJMSProxy.java class
package com.proxy.test;
import com.wiley.fts.ProxyJMSClient;
public class TestJMSProxy {
public static void main(String[] args) {
(new ProxyJMSClient()).callMyJMSClient();
}
}
When I run - I get following exceptions
log4j:WARN No appenders could be found for logger (org.springframework.context.support.ClassPathXmlApplicationContext).
log4j:WARN Please initialize the log4j system properly.
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [com.test.ComponentConsumer] is defined: expected single bean but found 0:
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:269)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1083)
at com.wiley.fts.ProxyJMSClient.<init>(ProxyJMSClient.java:19)
at com.proxy.test.TestJMSProxyJar.main(TestJMSProxyJar.java:8)
How can I resolve this
NOTE: -
PTSJMSProxy is a spring based project - which has its own applicationContext.xml (Refer point no -5)
TestProxy is a NON Spring Java project - where I use PTSJMSProxy Jar
PTSJMSProxy Jar folder structure
PTSJMSProxy jar contains com,META-INF and applicationContext.xml under same level
The problem is resolved.
This is due to the loading problem of spring configuration xml file.
Code
String fileUrl = PTSJMSProxyClient.class.getClassLoader()
.getResource(SPRING_JMS_CFG_FILE).toString();
LOG.info("Spring jmsContext.xml file path :" +fileUrl);
xmlApplicationContext = new ClassPathXmlApplicationContext(fileUrl);
AutowireCapableBeanFactory acbFactory = xmlApplicationContext
.getAutowireCapableBeanFactory();
acbFactory.autowireBean(this);
client = xmlApplicationContext.getBean(MessageSenderImpl.class);
Sometime it also happens when you define basePackages wrongly inside #ComponentScan annotation like:
#ComponentScan("com.whodesire.model", "com.whodesire.util")
here the above one will considered as single package, if there are multiple packages to scan within your project, then you must mention packages like String[]
#ComponentScan( { "com.whodesire.model" , "com.whodesire.util" } )

Resources