My intention is run the aspect before get message method in service. I don't want to use xml configuration, so I add (hopefully) necessary annotation. But, when I run my application, aspect doesen't work, and nothing happen. Can You explain me why?
Service
public interface Service {
void getMessage();
}
Service implementation
import org.springframework.stereotype.Component;
#Service
public class ServiceImpl implements Service {
public void getMessage() {
System.out.println("Hello world");
}
}
Aspect
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class LoggingAspect {
#Before("execution(* com.example.aop.Service.getMessage())")
public void logBefore(JoinPoint joinPoint) {
System.out.println("AOP is working!!!");
}
}
Run
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#ComponentScan("com.example")
public class AopApplication {
public static void main(String[] args) {
final ConfigurableApplicationContext run = SpringApplication.run(AopApplication.class, args);
final Service bean = run.getBean(ServiceImpl.class);
bean.getMessage();
}
}
Output
Only Hello world
Probably, you have to add #Component annotation to LoggingAspect class.
I believe the pointcut expression syntax should be like this:
#Before("execution(void com.aop.service.Service+.getMessage(..))")
The + is used to apply the pointcut to subtypes (you can replace void with * too.
Related
I am new to Spring AOP and annotations. I tried to write a simple program that uses Aspect. I am unable to figure out where I went wrong. Its doesn't print the what I have in my Aspect.
package com.business.main;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#EnableAspectJAutoProxy
#Configuration
public class PrintMain {
public static void main(String[] args) {
// Do I always need to have this. Can't I just use #Autowired to get beans
ApplicationContext ctx = new AnnotationConfigApplicationContext(PrintMain.class);
CheckService ck = (CheckService)ctx.getBean("service");
ck.print();
}
#Bean(name="service")
public CheckService service(){
return new CheckService();
}
}
package com.business.main;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class SimpleAspect {
#Around("execution(* com.business.main.CheckService.*(..))")
public void applyAdvice(){
System.out.println("Aspect executed");
}
}
package com.business.main;
import org.springframework.stereotype.Component;
#Component
public class CheckService{
public void print(){
System.out.println("Executed service method");
}
}
Output: Executed service method
I expect to print what I have in my Aspect
I think your #Component isn't work!
Maybe, you need the #ComponentScan
#EnableAspectJAutoProxy
#ComponentScan
#Configuration
public class PrintMain {
public static void main(String[] args) {
// Do I always need to have this. Can't I just use #Autowired to get beans
ApplicationContext ctx = new AnnotationConfigApplicationContext(TNGPrintMain.class);
CheckService ck = (CheckService)ctx.getBean("service");
ck.print();
}
#Bean(name="service")
public CheckService service(){
return new CheckService();
}
}
I am learning spring the from book the "Spring in Action fourth edition" by Craig Walls. I am trying to apply advice to the method declared by the interface and I am getting Exception. When I apply the same advice to the class which doesn't implement anything, everything works fine.
Spring version - 4.3.2
Help would be appreciated.
Exception:
Exception in thread "main"org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.fifczan.bean.UserService] is defined
Code:
Interface:
package com.fifczan.bean;
public interface Service {
void doTask();
}
Implementation:
package com.fifczan.bean;
import org.springframework.stereotype.Component;
#Component
public class UserService implements Service {
public void doTask() {
System.out.println("doing task");
}
}
Aspect:
package com.fifczan;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class UserAspect {
//If i change Service(interface) to UserService(implementation)
//in pointcut I am getting the same exception
#Before("execution(* com.fifczan.bean.Service.doTask(..))")
public void userAdvice(){
System.out.println("doing sth before method doTask");
}
}
Configuration:
package com.fifczan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;
#Configuration
#EnableAspectJAutoProxy
#ComponentScan
public class AspectJAutoProxyConfig {
}
main :
package com.fifczan;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import com.fifczan.bean.UserService;
public class AspectJAutoProxyTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AspectJAutoProxyConfig.class);
UserService userService= ctx.getBean(UserService.class);
userService.doTask();
}
}
You're asking for a bean of UserService, which is the concrete class, not the interface. Retrieve or inject a bean of type Service.
I'm developing a WebSocket server application using spring.
Class PlayerHandler
import org.springframework.web.socket.WebSocketSession;
import org.springframework.web.socket.handler.TextWebSocketHandler;
import java.io.IOException;
/**
* Created by kris on 11.07.16.
*/
public class PlayerHandler extends TextWebSocketHandler{
public PlayerHandler(){}
#Override
#AuthorizationRequired
public void handleTextMessage(WebSocketSession session, TextMessage tm) throws IOException {
session.sendMessage(tm);
}
}
I want user to be authorized with every incoming request by token, so I created a Aspect UserAuthorization
package com.berrigan.axevor.authorization;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class UserAuthorization {
#Around("#annotation(com.berrigan.axevor.authorization.AuthorizationRequired)")
public void authorize(ProceedingJoinPoint jp) throws Throwable{
System.out.println("\n\n\n\n\Works\n\n\n\n\n\n");
jp.proceed();
}
}
I added the #AuthorizationRequired annotation, which indicates methods in which users are going to be authorized. Unfortunately method authorize never get called. I've added following code to my main class to check if the bean get created.
UserAuthorization ua = ctx.getBean(UserAuthorization.class); // ApplicationContext
if(au == null) System.out.println("is null")
But I don't get such log.
My spring config
#EnableAutoConfiguration
#Configuration
#EnableAspectJAutoProxy
#Import({com.berrigan.axevor.websocket.WebSocketConfig.class})
#ComponentScan(basePackages = {"com.berrigan.axevor"})
public class Config {}
Annotation code:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface AuthorizationRequired{}
#Configuration
#EnableWebSocket
public class WebSocketConfig implements WebSocketConfigurer{
#Override
public void registerWebSocketHandlers(WebSocketHandlerRegistry registry){
registry.addHandler(playerHandler(), "/game").setAllowedOrigins("*");
}
#Bean
public WebSocketHandler playerHandler(){
return new PlayerHandler();
}
}
Solution found, corrupted pom.xml file. After regenerating it, everything works like a charm
I try to improve my Spring knowledge by reading Spring in action 4.
When I've to to section, describing using of Qualifier annotation (3.3.2), i faced the problem.
To test this annotation in action, I wrote Dessert interface, which is implemented by 3 classes, creating in context using #Component annotation.
I also created class Taster, which "tastes" some dessert, autowired into by some qualifier.
When I run my application, using AnnotationConfigApplicationContext - everything works good. With SpringJUnit4ClassRunner - it does not. I guess I miss something in my test code, but I do not have enough knowledge to realize what.
Interface:
package bakery.intrface;
#FunctionalInterface
public interface Dessert {
void introduce();
}
Cake:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
#Component
public class Cake implements Dessert {
#Override
public void introduce() {
System.out.println("I am a cake!");
}
}
Cookie:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
#Component
public class Cookie implements Dessert {
#Override
public void introduce() {
System.out.println("I'm a cookie!");
}
}
Ice cream:
package bakery.desserts;
import bakery.intrface.Dessert;
import org.springframework.stereotype.Component;
#Component
public class IceCream implements Dessert {
#Override
public void introduce() {
System.out.println("I'm an ice cream!");
}
}
The class, consumes some bean, Taster:
package bakery;
import bakery.intrface.Dessert;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
#Component
public class Taster {
private Dessert dessert;
public void taste(){
dessert.introduce();
}
#Autowired
#Qualifier("iceCream")
public void setDessert(Dessert dessert) {
this.dessert = dessert;
}
}
Configuration:
package bakery.config;
import bakery.Bakery;
import bakery.Taster;
import bakery.desserts.Cake;
import bakery.intrface.Dessert;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
#ComponentScan(basePackageClasses = Bakery.class)
public class BakeryConfig {
}
Run class:
package bakery;
import bakery.config.BakeryConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Bakery {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(BakeryConfig.class);
String[] beans = context.getBeanDefinitionNames();
Taster taster = (Taster) context.getBean("taster");
taster.taste();
}
}
Test class:
package bakery;
import bakery.config.BakeryConfig;
import bakery.intrface.Dessert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.assertNotNull;
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = BakeryConfig.class)
public class BakeryTest {
#Autowired
Dessert dessert;
#Autowired
Taster taster;
#Test
public void contextInit(){
assertNotNull(dessert);
dessert.introduce();
}
#Test
public void tasterInit(){
assertNotNull(taster);
}
}
When I run the test, I'm getting the exception: No qualifying bean of type [bakery.intrface.Dessert] is defined: expected single matching bean but found 3: cookie,iceCream,cake.
There are 3 "Dessert" beans in your application context, you have to specify which one you want to wire.
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = BakeryConfig.class)
public class BakeryTest {
#Autowired
#Qualifier("iceCream") // <===================== you must specify which bean to be wired
Dessert dessert;
#Autowired
Taster taster;
This is to be expected.
The declaration
#Autowired
Dessert dessert;
is asking for a Dessert object. Dessert is the interface, and there are three implementing classes, Cookie, IceCream, and Cake. Since you haven't made it more explicit which of those implementations you want, Spring throws an error because it can't decide what to do.
If you need this in your test, you can do one of the following:
#Autowired
#Qualifier("iceCream")
Dessert dessert;
to get only the ice cream dessert,
OR
#Autowired
List<Dessert> desserts;
to get a list containing all the implementations.
I have been trying to add spring validators to a spring-data-rest project.
I followed along and setup the "getting started" application via this link: http://spring.io/guides/gs/accessing-data-rest/
...and now I am trying to add a custom PeopleValidator by following the documents here:
http://docs.spring.io/spring-data/rest/docs/2.1.0.RELEASE/reference/html/validation-chapter.html
My custom PeopleValidator looks like
package hello;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class PeopleValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return true;
}
#Override
public void validate(Object target, Errors errors) {
errors.reject("DIE");
}
}
...and my Application.java class now looks like this
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public PeopleValidator beforeCreatePeopleValidator() {
return new PeopleValidator();
}
}
I would expect that POSTing to the http://localhost:8080/people URL would result in an error of some kind since the PeopleValidator is rejecting everything. However, no error is thrown, and the validator is never called.
I have also tried manually setting up the validator as shown in section 5.1 of the spring-data-rest documentation.
What am I missing?
So it appears that the before/after "save" events only fire on PUT and PATCH. When POSTing, the before/after "create" events fire.
I tried it the manual way again using the configureValidatingRepositoryEventListener override and it worked. I'm not sure what I'm doing differently at work than here at home. I'll have to look tomorrow.
I sure would love to hear if others have suggestions on why it wouldn't work.
For the record, here is what the new Application.java class looks like.
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application extends RepositoryRestMvcConfiguration {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
protected void configureValidatingRepositoryEventListener(ValidatingRepositoryEventListener validatingListener) {
validatingListener.addValidator("beforeCreate", new PeopleValidator());
}
}
Looks like the feature is currently not implemented (2.3.0), unluckily there are no constants for the event names otherwise the solution below would not be that fragile.
The Configuration adds all properly named Validator beans to ValidatingRepositoryEventListener using the right event.
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ListableBeanFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.rest.core.event.ValidatingRepositoryEventListener;
import org.springframework.validation.Validator;
#Configuration
public class ValidatorRegistrar implements InitializingBean {
private static final List<String> EVENTS;
static {
List<String> events = new ArrayList<String>();
events.add("beforeCreate");
events.add("afterCreate");
events.add("beforeSave");
events.add("afterSave");
events.add("beforeLinkSave");
events.add("afterLinkSave");
events.add("beforeDelete");
events.add("afterDelete");
EVENTS = Collections.unmodifiableList(events);
}
#Autowired
ListableBeanFactory beanFactory;
#Autowired
ValidatingRepositoryEventListener validatingRepositoryEventListener;
#Override
public void afterPropertiesSet() throws Exception {
Map<String, Validator> validators = beanFactory.getBeansOfType(Validator.class);
for (Map.Entry<String, Validator> entry : validators.entrySet()) {
EVENTS.stream().filter(p -> entry.getKey().startsWith(p)).findFirst()
.ifPresent(p -> validatingRepositoryEventListener.addValidator(p, entry.getValue()));
}
}
}
A bit of a stab in the dark - I've not used spring-data-rest. However, after having a read of the tutorial you're following, I think the problem is that you need a PersonValidator not a PeopleValidator. Rename everything accordingly:
PersonValidator
package hello;
import org.springframework.validation.Errors;
import org.springframework.validation.Validator;
public class PersonValidator implements Validator {
#Override
public boolean supports(Class<?> clazz) {
return true;
}
#Override
public void validate(Object target, Errors errors) {
errors.reject("DIE");
}
}
Application
package hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
#Configuration
#EnableJpaRepositories
#Import(RepositoryRestMvcConfiguration.class)
#EnableAutoConfiguration
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Bean
public PersonValidator beforeCreatePersonValidator() {
return new PersonValidator();
}
}
Another way of doing it is to use annotated handlers as specified here
http://docs.spring.io/spring-data/rest/docs/2.1.0.RELEASE/reference/html/events-chapter.html#d5e443
Here is an example of how to use annotated handlers:
import gr.bytecode.restapp.model.Agent;
import org.springframework.data.rest.core.annotation.HandleBeforeCreate;
import org.springframework.data.rest.core.annotation.HandleBeforeSave;
import org.springframework.data.rest.core.annotation.RepositoryEventHandler;
import org.springframework.stereotype.Component;
#Component
#RepositoryEventHandler(Agent.class)
public class AgentEventHandler {
public static final String NEW_NAME = "**modified**";
#HandleBeforeCreate
public void handleBeforeCreates(Agent agent) {
agent.setName(NEW_NAME);
}
#HandleBeforeSave
public void handleBeforeSave(Agent agent) {
agent.setName(NEW_NAME + "..update");
}
}
Example is from github edited for brevity.