Spring AOP can't apply advice to interface - java

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.

Related

Force autowire repository by name and not by type

I am trying to create a java + spring library (a seperate, reusable, application independent jar file) and use it in the application.
I show you a non-working example, that is already suitable to demonstrate my problem.
My problem is, that i have difficulties to autowire a repository by name and not by type.
The jar file logic is implemented here in one file called Library
package com.example;
import javax.persistence.MappedSuperclass;
import javax.persistence.OneToOne;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.repository.NoRepositoryBean;
import org.springframework.data.repository.Repository;
import org.springframework.stereotype.Service;
public class Library {
#MappedSuperclass
public static class ChildEntity {
public String childAttribute;
}
#MappedSuperclass
public static class RootEntity<T extends ChildEntity> {
public String rootAttribute;
#OneToOne
public T childEntity;
}
#Service
public static class RootEntityService<T extends ChildEntity> {
#Autowired
#Qualifier(Library.REPOSITORY_BEAN_NAME)
private RootRepository<T> repository;
public RootEntity<T> findMyEntity() {
return this.repository.findByChildEntity();
}
}
#NoRepositoryBean
public static interface RootRepository<T extends ChildEntity> extends Repository<T, Long> {
public RootEntity<T> findByChildEntity();
}
public final static String REPOSITORY_BEAN_NAME = "entityRepository";
}
As you can see i have a RootEntity and a ChildEntity with a OneToOne relation.
The RootRepository is defined as #NoRepositoryBean, since Repository cannot have generic parameters.
The RootService is referencing for the RootRepository and i am trying to autowire by name and not type - using the #Qualifier annotation.
Here comes the application itself:
package com.example;
import javax.persistence.Entity;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.WebApplicationType;
import org.springframework.stereotype.Repository;
import com.example.Library.ChildEntity;
import com.example.Library.RootEntity;
import com.example.Library.RootEntityService;
import com.example.Library.RootRepository;
public class Application implements ApplicationRunner {
#Entity
public static class RealChildEntity extends ChildEntity {
public String realChildAttribute;
}
#Entity
public static class RealRootEntity extends RootEntity<RealChildEntity> {
}
#Repository(Library.REPOSITORY_BEAN_NAME)
public static interface RealRootRepository extends RootRepository<RealChildEntity> {
}
public static void main(final String[] args) throws Exception {
SpringApplication application;
application = new SpringApplication(ApplicationConfig.class);
application.setWebApplicationType(WebApplicationType.NONE);
application.run(args);
}
#Autowired
private RootEntityService<RealChildEntity> rootEntityService;
#Override
public void run(final ApplicationArguments args) throws Exception {
this.rootEntityService.findMyEntity();
}
}
The RealChildEntity has an application specific attribute.
I define the RealRootRepository to be a real repository, without generic parameters. I have also defined a bean name for this component to refer to.
I also have an ApplicationConfig class to define the service bean and the repo:
package com.example;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import com.example.Library.ChildEntity;
import com.example.Library.RootEntityService;
#Configuration
#EnableJpaRepositories(basePackageClasses = { Application.class })
public class ApplicationConfig {
#Bean(Library.REPOSITORY_BEAN_NAME)
public <T extends ChildEntity> RootEntityService<T> entityService() {
return new RootEntityService<T>();
}
}
If i execute this application, than spring gives me the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field repository in com.example.Library$RootEntityService required a bean of type 'com.example.Library$RootRepository' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
- #org.springframework.beans.factory.annotation.Qualifier(value="entityRepository")
Spring tells me, that RootEntityService required a bean of type. Why by type?
I want a repository by name
What am i doing wrong?
How can i have a library service without extending it just because of the repository;
Thanks for your help in advance

Spring AOP - aspect not working without xml configuration

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.

Field in com.XXX required a bean of type that could not be found

I'm working on Spring over Hibernate project an i'm only in the beginning.
I'm tryng to hav a SpringBootApplication which writes to MsSql some LogEntries objects.
I have some different packages:
here is the classes:
LogEntryFacadeImpl.class :
package com.tradingSystem.dataAccess;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.tradingSystem.entity.LogEntry;
#Service
public class LogEntryFacadeImpl implements LogEntryFacade{
#Autowired
private LogEntryDAO logEntryDao;
#Transactional
#Override
public Long addLogEntry(LogEntry log) {
return this.logEntryDao.save(log).getId();
}
#Override
public LogEntry getLogEntry(Long logId) {
return this.logEntryDao.findOne(logId);
}
}
LogEntryDAO.class:
package com.tradingSystem.dataAccess;
import org.springframework.data.jpa.repository.JpaRepository;
import com.tradingSystem.entity.LogEntry;
public interface LogEntryDAO extends JpaRepository<LogEntry, Long> {
}
and I use this class as tester:
TestApplication.class:
package com.testings;
import java.util.Date;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.ComponentScan;
import com.tradingSystem.dataAccess.LogEntryFacade;
import com.tradingSystem.entity.LogEntry;
#SpringBootApplication
#ComponentScan({"com.tradingSystem" })
public class TestApplication implements CommandLineRunner{
#Autowired
private LogEntryFacade logEntryFacade;
public static void main(String[] args) {
SpringApplication.run(TestApplication.class, args);
}
#Override
public void run(String... args) throws Exception {
LogEntry log = new LogEntry(552266, "Testing of log entry save",
new Date(System.currentTimeMillis()),
new Date(System.currentTimeMillis()));
System.err.println(log);
Long id = logEntryFacade.addLogEntry(log);
LogEntry log2 = logEntryFacade.getLogEntry(id);
System.err.println(log2);
}
}
wher i run this as application i get this message in console:
APPLICATION FAILED TO START
Description:
Field logEntryDao in com.tradingSystem.dataAccess.LogEntryFacadeImpl required a bean of type 'com.tradingSystem.dataAccess.LogEntryDAO' that could not be found.
Action:
Consider defining a bean of type 'com.tradingSystem.dataAccess.LogEntryDAO' in your configuration.
I put the #ComponentScan({"com.tradingSystem" }) annotation in the tester as you can see. however, still get this message.
(when I didnt use any packages separation, everything works fine...)
Please help me solve this
Thanks
You should add #Repository annotation above your Repository interface.
Optionally you can add it like #Repository(value="logEntryRepository")
the default scan path is package of #SpringBootApplication class, so you must declare three scan path, but it's seems like that you missing two scan config, you need add
#EnableJpaRepositories(basePackages = "com.tradingSystem.dataAccess")
#EntityScan(basePackages = "com.tradingSystem.entity")
#ComponentScan(basePackages = "com.tradingSystem.dataAccess")
to the TestApplication class

Spring AOP - Unable to execute Aspect

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();
}
}

#Qualifier does not work within Junit4

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.

Categories

Resources