I am trying to log a method using AOP in Spring. I tried the following code with just System.out.println() but it's not getting called.
Created annotation:
#Retention(value = RetentionPolicy.RUNTIME)
#Target(value = ElementType.METHOD)
public #interface Loggable {
}
Created Aspect
#Aspect
public class MethodLogger {
#Around("execution(* *(..)) && #annotation(Loggable)")
public Object around(ProceedingJoinPoint point) throws Throwable {
System.out.println("this is called");
return result;
}
}
Used logging in a method in service layer
#Service("RegionService")
#Transactional(readOnly = false)
public class RegionService implements IRegionService{
#Loggable
#Override
public List<> fetch() {
return dao.xyz();
}
}
Spring configuration
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"
>
<context:component-scan base-package="com.sst.tender.spring"/>
<context:annotation-config/>
<context:spring-configured/>
</beans>
Add #Component to MethodLogger class. Also you have to enable AspectJ like one of the following ways:
Adding #EnableAspectJAutoProxy to your configuration bean class. (annotation driven approach)
Adding <aop:aspectj-autoproxy /> to application context file. (XML driven approach)
Related
I started to work in a simple Spring web-service and getting the following errors provided below,
org.springframework.beans.factory.BeanCreationException:
Error creating bean with name 'blogEntryController':
Injection of autowired dependencies failed; nested exception
is org.springframework.beans.factory.BeanCreationException:
Could not autowire field: private core.services.BlogEntryService
rest.mvc.BlogEntryController.service; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException:
No qualifying bean of type [core.services.BlogEntryService] found for
dependency: expected at least 1 bean which
qualifies as autowire candidate for this dependency. Dependency
annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
The project structure is as following,
Spring MVC project structure
I have the following service code,
package core.services;
import core.entities.BlogEntry;
public interface BlogEntryService {
public BlogEntry find(Long id); // Returns the BlogEntry or null if it can't be found
public BlogEntry delete(Long id); // Deletes the found BlogEntry or returns null if it can't be found
public BlogEntry update(Long id, BlogEntry data);
}
and, the following Controller code,
#Controller
#RequestMapping("/rest/blog-entries")
public class BlogEntryController {
#RequestMapping("/")
public String test(){
return "view";
}
public BlogEntryController() {
}
#Autowired
private BlogEntryService service;
public BlogEntryController(BlogEntryService service)
{
this.service = service;
}
#RequestMapping(value="/{blogEntryId}",
method = RequestMethod.GET)
public ResponseEntity<BlogEntryResource> getBlogEntry(
#PathVariable Long blogEntryId) {
BlogEntry entry = service.find(blogEntryId);
if(entry != null)
{
BlogEntryResource res = new BlogEntryResourceAsm().toResource(entry);
return new ResponseEntity<BlogEntryResource>(res, HttpStatus.OK);
} else {
return new ResponseEntity<BlogEntryResource>(HttpStatus.NOT_FOUND);
}
}
}
Update: Dispatcher Servlet.xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:component-scan base-package="rest.mvc"/>
<mvc:annotation-driven/>
<bean id="jspViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
I created the empty constructure as it was asked by the debugger earlier. I cant even see the view.jsp file returned from the test method. What should I do now ?
As I stated in my first comment beneath your question, you are missing the implementation class of the interface. An implementation class could be the one below, however you have to provide the functionality of the methods:
#Service
public class BlogEntryServiceImpl implements BlogEntryService {
public BlogEntry find(Long id) {
//Do your stuff here
}
public BlogEntry delete(Long id) {
//Do your stuff here
}
public BlogEntry update(Long id, BlogEntry data) {
//Do your stuff here
}
}
#Autowired
private BlogEntryService service;
Above lines says you are having class BlogEntryService annotated
with #Service or mentioned in bean tag or as component scan(package) in dispatcher-servlet xml.
If you are missing mentioning in any of one place you will get the exception No qualifying bean of type [core.services.BlogEntryService] found for dependency:
So your BlogEntryService interface should be
#Service
public interface BlogEntryService {
Update : In dispathcer-servlet.xml you have to mention the packages to scan for beans.In your case it is rest.mvc.Since it's an Interface you should have the implemented class(annotated with #Service),which will have definition of interface methods.
<context:component-scan base-package="Your Service layer Package" />
It looks that BlogEntryService bean class is not provided. Do you have some implementation class annotated wit #Service?
Either you provide an implementation class for your interface and annotate it with #service, OR make the BlogEntryService interface extend CrudRepository and Spring will provide you with the proper implementation for your cruds :save, getAll, find...
It looks like an implementation of your service is missing.
Just annotating an interface with #Service is not enough for Spring to generate a bean unless you are extending your interface with some of the specific Spring Data interface (CrudRepository, Repository ...). In that specific case and if you respect sone convention Spring will be able to automatically generate the implementation for you.
Please have a look to the documentation: http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories
I want to use custom JsonSerializer for JSON response of SpringMVC4.
In order to add JsonSerializer, I created WebMvcConfigurerAdapter subclass.
But customization of MappingJackson2HttpMessageConverter didn't work.
Simplify the problem, I tried setJsonPrefix.
But it also didn't work. The response didn't changed.
My code is below. Please tell me what is wrong.
ControllerClass
#Controller
public class SampleController {
#RequestMapping("/sample")
#ResponseBody
public ResponseModel action() {
return new ResponseModel();
}
public static class ResponseModel {
public String id = "001";
public String text = "aaa";
}
}
Configuration
#Configuration
#EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
super.configureMessageConverters(converters);
}
#Bean
protected MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setJsonPrefix("prefix");
return converter;
}
}
dispatcher-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/mvc"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
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
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
xmlns:aop="http://www.springframework.org/schema/aop">
<!-- base package -->
<context:annotation-config />
<context:component-scan base-package="jp.co.xxx.*" /><!-- my package. contains WebMvcConfiguration class -->
<annotation-driven />
<!-- aop -->
<aop:aspectj-autoproxy />
</beans:beans>
Note.
When server starts, configureMessageConverters method was called.
(Breakpoint confirmation)
I am using AbstractJsonpResponseBodyAdvice subclass for JSONP
(I removed this class, but nothing was changed.)
I used below as reference.
How to configure MappingJacksonHttpMessageConverter while using spring annotation-based configuration?
http://www.baeldung.com/spring-httpmessageconverter-rest
SpringMVC version is 4.1.6
P.S.
In JSONSerializer case is below.
#Configuration
#EnableWebMvc
public class WebMvcConfiguration extends WebMvcConfigurerAdapter {
#Autowired
protected CustomObjectMapper mapper;
#Override
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(converter());
super.configureMessageConverters(converters);
}
#Bean
protected MappingJackson2HttpMessageConverter converter() {
MappingJackson2HttpMessageConverter converter = new MappingJackson2HttpMessageConverter();
converter.setObjectMapper(mapper);
return converter;
}
}
ObjectMapper
#Component
public class CustomObjectMapper extends ObjectMapper {
private static final long serialVersionUID = -6987863269632420904L;
public CustomObjectMapper() {
setSerializationInclusion(Include.NON_NULL);
enable(SerializationFeature.INDENT_OUTPUT);
SimpleModule module = new SimpleModule();
module.addSerializer(DateTime.class, new DateTimeSerializer());
registerModule(module);
}
}
In each case I had no error. But customization didn't work.
In the configureMessageConverters method, if you are not covered and no other converter is added, converters are empty, and WebMvcConfigurationSupport will call addDefaultHttpMessageConverters, which will configure the default converter, which contains the default MappingJackson2HttpMessageConverter.
So adding MappingJackson2HttpMessageConverter in extendMessageConverters will not work.
There are two solutions:
You add the required converter in the configureMessageConverters method itself
To determine the type of converter in extendMessageConverters, set the required properties
sorry,i speek broken english.
I was having this problem as well and discovered this problem, thanks to another site:
#EnableWebMvc is equivalent to in XML based configuration.
If you have BOTH, then the extendMessageConverters doesn't seem to be effective. As soon as I removed the XML entry, bingo.. the custom converters started working.
I have a problem getting EJB beans working when using Spring's java config classes.
Specifically I have the following that works:
#Configuration
#ComponentScan(basePackages = "com.company.web.config")
#ImportResource(value = {"classpath:spring-beans.xml"})
public class AppConfig {
}
#Configuration
#ComponentScan(basePackages = "com.company.web")
public class WebConfig extends WebMvcConfigurationSupport {
// basic Spring MVC setup omitted
}
My spring-beans.xml looks like this:
<?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:jee="http://www.springframework.org/schema/jee"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee.xsd">
<jee:local-slsb id="fooService" jndi-name="java:app/model/FooServiceBean!com.company.ejb.FooService"
business-interface="com.company.ejb.FooService" />
</beans>
With this configuration, everything works, and I can do this:
#Controller
public class HomeController {
private final FooService fooService;
#Autowired
public MyPageController(FooService fooService){
this.fooService = fooService;
}
// request methods
}
Now i try to get rid of the XML file. According to the documentation the local-slsb should be equivalent
<bean id="fooService"
class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
<property name="jndiName" value="java:app/model/FooServiceBean!com.company.ejb.FooService"/>
<property name="businessInterface" value="com.company.ejb.FooService"/>
</bean>
However, if I remove the #ImportResource from AppConfig and put this #Bean method instead, deployment fails because the Controller cannot be instantiated (no autowire candidates found for FooService):
#Bean
public LocalStatelessSessionProxyFactoryBean fooService(){
LocalStatelessSessionProxyFactoryBean factory = new LocalStatelessSessionProxyFactoryBean();
factory.setBusinessInterface(FooService.class);
factory.setJndiName("java:app/model/FooServiceBean!com.company.ejb.FooService");
return factory;
}
Any ideas why this doesn't work? I am using Spring version 4.0.2.
It seems that the issue was related to the order in which configuration was read, and possibly double configuration loading.
Specifically, introducing a separate configuration class and importing it before WebConfig seems to do the trick, like so:
#Configuration
#Import({EJBConfig.class, WebConfig.class})
public class AppConfig {
}
#Configuration
public class EJBConfig {
#Bean
public LocalStatelessSessionProxyFactoryBean fooService(){
LocalStatelessSessionProxyFactoryBean factory = new LocalStatelessSessionProxyFactoryBean();
factory.setBusinessInterface(FooService.class);
factory.setJndiName("java:app/model/FooServiceBean!com.company.ejb.FooService");
return factory;
}
}
#Configuration
#ComponentScan(basePackages = "com.company.web")
public class WebConfig extends WebMvcConfigurationSupport {
// basic Spring MVC setup omitted
}
My Aspect class will be ,
#Configuration
#EnableAspectJAutoProxy
#Component
#Aspect
public class AspectClass {
#Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("Before running the beforeAspect() in the AopTest.java class!");
System.out.println("Hijacked Method name : " + joinPoint.getSignature().getName());
System.out.println("************************");
}
}
My other java Class
public class AopTest {
public void beforeAspect() {
System.out.println("This is beforeAspect() !");
}
}
My Main Class is
public class MainMethod {
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext("ApplicationContext/applicationContext.xml");
AopTest test = (AopTest)context.getBean("bean1");
test.beforeAspect();
}
}
My applicationContext.xml is ,
<?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-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">
<bean id="bean1" class="com.pointel.aop.test1.AopTest" />
</beans>
In this the #Before("execution(* com.pointel.aop.test1.AopTest.beforeAspect())") in the AspectClass will not be executed before the beforeAspect() in the AopTest , when running Main method.
Good answers are definitely appreciated.
First of all if you're going to use an annotation based configuration, use AnnotationConfigApplicationContext instead of FileSystemXmlApplicationContext. And get rid of the applicationContext.xml file and simply add a #Bean method in your configuration class. Something like this:
#Configuration
#EnableAspectJAutoProxy
#ComponentScan(basePackages = "your.aspect.package")
public class AspectConfig {
#Bean
public AopTest aopTest() {
return new AopTest();
}
}
In your main
public class MainMethod {
public static void main(String[] args) {
AnnotationConfigApplicationContextcontext = new AnnotationConfigApplicationContext(AspectConfig.class);
// don't forget to refresh
context.refresh();
AopTest test = (AopTest)context.getBean("aopTest");
test.beforeAspect();
}
}
In AspectClass you should have #Component, #Aspect, and your method should have the advice or pointcut annotation like #Before. It needs to be a #Component, so that Spring knows to scan it.
Here some code need to add in xml to use annotations-
1.for #component annotation.
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd"
2.after that use component scan to get all annotated bean class which use #component annotation,and use aop autoproxy-
<context:annotation-config/>
<context:component-scan base-package="mypackage"></context:component-scan>
<aop:aspectj-autoproxy>
</aop:aspectj-autoproxy>
for examples visit-www.technicaltoday.com/p/spring.html
You are missing the point cut definition in your aspect class.
For example;
#Pointcut("execution(* *.advice(..))")
public void logBefore(){}
#Before("logBefore()")
public void beforeAdvicing(){
System.out.println("Listen Up!!!!");
}
You first have to defin the point to weave your aspect to. You do this by using Point cuts.It is the point cut name you give within your #Before annotation. Have a look at my blog post for more information # http://dinukaroshan.blogspot.com/2010/06/aop-with-spring.html
I don't see your AspectClass in the beans configuration. You should also declare it as a Bean.
I am trying to get Aspect working with Spring 3 and annotations.
#Aspect
public class AttributeAspect {
#Pointcut("#annotation(com.mak.selective.annotation.Attribute)")
public void process(){
System.out.println("Inside Process ....");
}
#Around("process()")
public void processAttribute(){
System.out.println("Inside Actual Aspect ..");
}
}
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:p="http://www.springframework.org/schema/p"
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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<aop:aspectj-autoproxy proxy-target-class="false" />
<context:component-scan base-package="com.mak.selective.annotation.*" />
<bean name="attribute" class="com.mak.selective.annotation.AttributeAspect"/>
</beans>
MY Test to test the Aspect:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration("/springcontext/*.xml")
public class AttributeTest {
#Attribute(tableName = "firstTable", columnName = "New Column")
private void getAttribute() {
System.out.println("Inside Attribute call...");
}
#Test
public void testAttributeAspect() {
getAttribute();
}
}
With this code i can only see "Inside Attribute call..." but nothing from Aspect.
Please guide.
Got this working by making a new Object (Component) and injected to the Junit test class.
Good to see that you got it working from XML, but you could have also done it from annotations.
The issue is that the #Aspect annotation is not a Spring stereotype, so the scanner is not registering the aspect as a Spring Bean. Just add either #Service or #Component above or below #Aspect and it will be registered.
Also, either directly name the bean (e.g., #Service("myNamedService")) or have it implement an interface (e.g., public class AttributeAspect implements IAspect {), as per standard Spring design.
You need to use real AspectJ if you want to intercept invocations of methods within the same bean form where it is invoked. (What you have done, should work if the method testAttributeAspect() is located in an other bean.)
How to do real AspectJ?
Using the AspectJ compiler and weaver enables use of the full AspectJ language, and is discussed in Section 7.8, “Using AspectJ with Spring applications”.
#See Spring Reference
A few things:
Firstly, when you do around advice you need to write the advice method like this:
#Around(...)
public void aroundAdviceMethod(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("before...");
pjp.proceed();
}
finally {
System.out.println("After...");
}
}
But also (and this at least applies when you're using proxies, not entirely sure in your case), the method you're putting advice on needs to be public (yours isn't), spring managed (via #Component or otherwise) and called external from the class so the proxy can take effect (also not the case in your example). So you need something like this:
#Component
public class SomeClass {
#Attribute
public void someMethodCall() {
System.out.println("In method call");
}
}
public class SomeUnitTest {
#Autowired SomeClass someClass;
#Test
public void testAspect() {
someClass.someMethodCall();
}
}