I am working on one component to achieve audit using Spring AOP. I could use it for most of the service's methods. But found that with Mybatis mappers AOP point-cuts don't work.
Basically, Spring AOP only works with Spring-managed beans. But these mapper beans have been scanned using mybatis.spring.*.MapperScan and can be autowired in other Spring components.
Why can these beans not be scanned for Spring AOP? Any idea?
I can use AspectJ but was keen to find out how mybatis.spring.*.MapperScan works.
for example -
I have these configurations one for Mybatis mapper scan and other config for application specific configurations.
#Configuration
#MapperScan("com.test.mapper")
public class ProviderConfiguration {
#Bean
public SqlSessionFactory sqlSessionFactory(final DataSource src) throws Exception {
...
}
}
#Configuration
#EnableAspectJAutoProxy
public class MainConfiguration {
}
My Dao logic where i call mapper method -
#Component
public class TestDao {
//injecting mybatis mapper here
#Inject
private SaveTableData saveTableData;
public TableData save(TableData tableData) {
saveTableData.updateTableData(tableData);
}
}
I have registered my pointcuts as below
#Component
#Aspect
public class TestAdvices {
#Pointcut("execution(* com.test.mapper.SaveTableData.updateTableData(*))")
public void commonSaveTableData(TableData tableData) {
}
#Pointcut("execution(* com.test.service.CreateTableData.createTableData(*))")
public void commonCreateTableData(TableData tableData) {
}
//advices
#After("commonSaveTableData(tableData)")
public void addHistoryWhenSaveTableData(TableData tableData) throws Throwable {
//do stuff
}
//advices
#After("commonCreateTableData(tableData)")
public void addHistoryWhenCreateTableData(TableData tableData) throws Throwable {
//do stuff
}
}
Issue is commonCreateTableData which is on service method works as expected. But commonSaveTableData which is on Mybatis mapper method does't get invoke.
Question is if i can autowire these Mappers in any Spring bean why can't Spring AOP intercept method call using these pointcuts?
I think your pointcut expression is not correct, try this
#Component
#Aspect
public class TestAdvices {
#Pointcut("execution(* com.test.mapper.SaveTableData.updateTableData(*)) && args(tableData)", argNames="tableData")
public void commonSaveTableData(TableData tableData) {
}
//advices
#After("commonSaveTableData(tableData)", argNames="tableData")
public void addHistoryWhenSaveTableData(TableData tableData) throws Throwable {
//do stuff
}
//...
}
The reason you can't cut into the mapper like this is that when mapper is scanned by mybatis, it's bean definition has bean changed in a way that its interface is still the mapper interface but its class has been changed to MapperFactoryBean
Related
I have a Spring Boot + Apache Camel project that works brilliantly. I just added a new bean though where I wanted to have its implementation be profile-specific. I created Spring tests to verify it, and it works as expected, but when I run the server I get the following stack trace:
Caused by: org.apache.camel.NoSuchBeanException: No bean could be found in the registry for: MyFancyBean
at org.apache.camel.component.bean.RegistryBean.getBean(RegistryBean.java:94)
at org.apache.camel.model.language.MethodCallExpression.createExpression(MethodCallExpression.java:196)
at org.apache.camel.model.language.MethodCallExpression.createPredicate(MethodCallExpression.java:210)
at org.apache.camel.model.language.ExpressionDefinition.createPredicate(ExpressionDefinition.java:148)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:63)
at org.apache.camel.model.ValidateDefinition.createProcessor(ValidateDefinition.java:35)
at org.apache.camel.model.ProcessorDefinition.makeProcessorImpl(ProcessorDefinition.java:545)
at org.apache.camel.model.ProcessorDefinition.makeProcessor(ProcessorDefinition.java:506)
at org.apache.camel.model.ProcessorDefinition.addRoutes(ProcessorDefinition.java:222)
at org.apache.camel.model.RouteDefinition.addRoutes(RouteDefinition.java:1068)
I have an interface and two implementations:
public interface MyFancyBean { ... }
public class FooFancyBean implements MyFancyBean { ... }
public class NonFooFancyBean implements MyFancyBean { ... }
Depending on profile, the correct bean is read instantiated:
#Configuration
public class AppConfig {
#Bean
#Profile("foo")
MyFancyBean fooBean() {
return new FooFancyBean();
}
#Bean
#Profile("!foo")
MyFancyBean nonFooBean() {
return new NonFooFancyBean();
}
}
I've verified this works a couple of ways. First, a couple tests:
#ActiveProfiles("anything-but-foo")
#RunWith(SpringJUnit4ClassRunner.class)
#ComponentScan(basePackages = {"com.example", "com.jtv.spring.boot"})
#EnableAutoConfiguration
#Component
public class NonFooBean_SpringTest {
#Autowired
private MyFancyBean bean;
#Test
// ... here "bean" is instantiated as "NonFooFancyBean"
So the test works.
Further, when I start my app, depending on profile the correct bean in my #Configuration class above is called.
But Camel is still angry and says "NoSuchBeanException" on startup.
FWIW, here's how I'm referencing the bean:
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
from("direct:processStuff").
validate().method("MyFancyBean").
process("MyProcessor");
}
}
How do I get Camel to honor this config?
Whoooo... Y'all get to be my rubber duck today. I just autowired it. (This doesn't work for my processor, which is why it didn't occur to me initially.)
#Component
public class MyCamelRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
// [...]
#Autowired MyFancyBean myFancyBean;
from("direct:processStuff").
validate().method(myFancyBean).
process("MyProcessor");
}
}
There have been several arguments around not using ApplicationContext.getBean() to get a bean reference, of which most are based on logic that it violates the principles of Inversion of control.
Is there a way to get reference to prototype scoped bean without calling context.getBean() ?
Consider to use Spring Boot!
Than you can do something like this...
Runner:
#SpringBootApplication
public class Runner{
public static void main(String[] args) {
SpringApplication.run(Runner.class, args);
}
}
Some Controller:
#Controller
public class MyController {
// Spring Boot injecting beans through #Autowired annotation
#Autowired
#Qualifier("CoolFeature") // Use Qualifier annotation to mark a class, if for example
// you have more than one concreate class with differant implementations of some interface.
private CoolFeature myFeature;
public void testFeature(){
myFeature.doStuff();
}
}
Some cool feature:
#Component("CoolFeature") // To identify with Qualifier
public class CoolFeature{
#Autowired
private SomeOtherBean utilityBean;
public void doStuff(){
// use utilityBean in some way
}
}
No XML files to handle.
We can still access context for manual configurations if needed.
Suggested reading:
Spring Boot Reference
Pro Spring Boot
This type of problem can be solved using method injection, which is described in more detail here: https://docs.spring.io/spring/docs/current/spring-framework-reference/html/beans.html#beans-factory-method-injection
This is the most common approach to create prototype bean:
abstract class MyService {
void doSome() {
OtherService otherService = getOtherService();
}
abstract OtherService getOtherService();
}
#Configuration
class Config {
#Bean
public MyService myService() {
return new MyService() {
OtherService getOtherService() {
return otherService();
}
}
}
#Bean
#Scope("prototype")
public OtherService otherService() {
return new OtherService();
}
}
I struggle to use aspect in Spring MVC project.
Method that is a pointcut is running fine, but without advise.
Here is class, that starts whole spring boot and that is root of spring context:
#Lazy
#SpringBootApplication
#EnableAspectJAutoProxy(proxyTargetClass=true)
#Configuration
public class MainSpringBootClass{
public static void main(String[] args)
{
SpringApplication.run(MainSpringBootClass.class, args);
}
}
Here is class with method, that is pointcut.
#Component
#Log
#Aspect
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleClass
{
public void example()
{
System.out.println("example");
}
}
And here is my aspect:
#Aspect
#Component
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyAspect implements MethodBeforeAdvice
{
#Pointcut("execution(* com.example.MyExampleClass.example())")
public void asd()
{
// pointcut
}
#Before("asd()")
public void login()
{
System.out.println("im am logging in");
}
#Before("execution(* com.example.MyExampleClass.example())")
public void login2()
{
System.out.println("im am logging in2");
}
#Override
public void before(Method method, Object[] args, Object target) throws Throwable
{
System.out.println("aop before");
}
}
And here is my controller:
#RestController
#EnableAspectJAutoProxy(proxyTargetClass=true)
public class MyExampleController
{
private final MyExampleClass myExampleClass;
#Inject
public AdController(MyExampleClass myExampleClass)
{
this.myExampleClass = myExampleClass;
}
#RequestMapping("/")
public String index()
{
myExampleClass.example();
return "x";
}
}
As You can see, I have been trying to bruteforce correct result with annotations.
I have also seen on some website, that I need specific dependencies, so here are mine (pasting only those, related to aspects):
compile 'org.springframework:spring-aop:+'
compile 'org.aspectj:aspectjrt:+'
compile 'org.aspectj:aspectjweaver:+'
compile 'cglib:cglib:+'
compile 'cglib:cglib-nodep:+'
All dependencies have been successfully downloaded, project compiles and runs fine.
When I hit localhost:8080 then I see returned value "x", and inside logs I see "example".
However, I do not see any advices from spring aop nor aspectj - what am I doing wrong?
I am just using this project as sandbox to learn aspects, so I would be eager to learn, how to do it with each of Spring AOP and AspectJ.
The most important thing for me is to do it all without XML.
EDIT:
I have added simple constructor to MyAspect with println to check, if it is created (as it is normal spring bean with #Component after all) and it does - it is correctly created by spring.
EDIT 2:
IntelliJ IDEA tells me about methods login and login2: "This advice advices no method", but at the same time, I am able to jump (with control-click) from string, that is value in annotations to correct implementations.
All you should need is something like this:
#Aspect
#Component
public class MyAspect {
#Before("execution(* com.example.MyExampleClass.example(..))")
public void logBefore(JoinPoint pjp) throws Throwable {
System.out.println("before...");
}
}
You might have to replace all of the aspectJ dependencies with spring-boot-starter-aop .
Here's an example project that works (see RestControllerAspect.java):
https://github.com/khoubyari/spring-boot-rest-example
Spring is failing to autowire my object? Is it possible to autowire an object within an abstract class. Assume all schemas are supplied in application-context.xml
Question: What annotation should be on the base and extending classes (if any) #Service #Component?
Example
abstract class SuperMan {
#Autowire
private DatabaseService databaseService;
abstract void Fly();
protected void doSuperPowerAction(Thing thing) {
//busy code
databaseService.save(thing);
}
}
Extending class
public class SuperGirl extends SuperMan {
#Override
public void Fly() {
//busy code
}
public doSomethingSuperGirlDoes() {
//busy code
doSuperPowerAction(thing)
}
application-context.xml
<context:component-scan base-package="com.baseLocation" />
<context:annotation-config/>
I have that kind of spring setup working
an abstract class with an autowired field
public abstract class AbstractJobRoute extends RouteBuilder {
#Autowired
private GlobalSettingsService settingsService;
and several children defined with #Component annotation.
Normally, Spring should do the autowiring, as long as your abstract class is in the base-package provided for component scan.
See this and this for further reference.
#Service and #Component are both stereotypes that creates beans of the annotated type inside the Spring container. As Spring Docs state,
This annotation serves as a specialization of #Component, allowing for
implementation classes to be autodetected through classpath scanning.
What if you need any database operation in SuperGirl you would inject it again into SuperGirl.
I think the main idea is using the same object reference in different classes.
So what about this:
//There is no annotation about Spring in the abstract part.
abstract class SuperMan {
private final DatabaseService databaseService;
public SuperMan(DatabaseService databaseService) {
this.databaseService = databaseService;
}
abstract void Fly();
protected void doSuperPowerAction(Thing thing) {
//busy code
databaseService.save(thing);
}
}
#Component
public class SuperGirl extends SuperMan {
private final DatabaseService databaseService;
#Autowired
public SuperGirl (DatabaseService databaseService) {
super(databaseService);
this.databaseService = databaseService;
}
#Override
public void Fly() {
//busy code
}
public doSomethingSuperGirlDoes() {
//busy code
doSuperPowerAction(thing)
}
In my opinion, inject once run everywhere :)
In my case, inside a Spring4 Application, i had to use a classic Abstract Factory Pattern(for which i took the idea from - http://java-design-patterns.com/patterns/abstract-factory/) to create instances each and every time there was a operation to be done.So my code was to be designed like:
public abstract class EO {
#Autowired
protected SmsNotificationService smsNotificationService;
#Autowired
protected SendEmailService sendEmailService;
...
protected abstract void executeOperation(GenericMessage gMessage);
}
public final class OperationsExecutor {
public enum OperationsType {
ENROLL, CAMPAIGN
}
private OperationsExecutor() {
}
public static Object delegateOperation(OperationsType type, Object obj)
{
switch(type) {
case ENROLL:
if (obj == null) {
return new EnrollOperation();
}
return EnrollOperation.validateRequestParams(obj);
case CAMPAIGN:
if (obj == null) {
return new CampaignOperation();
}
return CampaignOperation.validateRequestParams(obj);
default:
throw new IllegalArgumentException("OperationsType not supported.");
}
}
}
#Configurable(dependencyCheck = true)
public class CampaignOperation extends EO {
#Override
public void executeOperation(GenericMessage genericMessage) {
LOGGER.info("This is CAMPAIGN Operation: " + genericMessage);
}
}
Initially to inject the dependencies in the abstract class I tried all stereotype annotations like #Component, #Service etc but even though Spring context file had ComponentScanning for the entire package, but somehow while creating instances of Subclasses like CampaignOperation, the Super Abstract class EO was having null for its properties as spring was unable to recognize and inject its dependencies.After much trial and error I used this **#Configurable(dependencyCheck = true)** annotation and finally Spring was able to inject the dependencies and I was able to use the properties in the subclass without cluttering them with too many properties.
<context:annotation-config />
<context:component-scan base-package="com.xyz" />
I also tried these other references to find a solution:
http://www.captaindebug.com/2011/06/implementing-springs-factorybean.html#.WqF5pJPwaAN
http://forum.spring.io/forum/spring-projects/container/46815-problem-with-autowired-in-abstract-class
https://github.com/cavallefano/Abstract-Factory-Pattern-Spring-Annotation
http://www.jcombat.com/spring/factory-implementation-using-servicelocatorfactorybean-in-spring
https://www.madbit.org/blog/programming/1074/1074/#sthash.XEJXdIR5.dpbs
Using abstract factory with Spring framework
Spring Autowiring not working for Abstract classes
Inject spring dependency in abstract super class
Spring and Abstract class - injecting properties in abstract classes
Spring autowire dependency defined in an abstract class
Please try using **#Configurable(dependencyCheck = true)** and update this post, I might try helping you if you face any problems.
I have Spring bean with annotations:
#Named
#Scope("session")
And this bean property:
#Autowired
ApplicationContext appContext;
The Spring configuration file has entry (that works for other anotations/injections):
<context:component-scan base-package="my.package.name" />
Why appContext is null after such code and configuration?
I am trying to get ApplicationContext (to call getBean(...) on it) and this can be quite involved task (judging from other discussions) in previous Spring versions (e.g. one is required to get ServletContext in Spring web application to create ApplicationContext and getting ServletContext can be quite involved task for beans that don't directly access HTTP Request objects). In Spring 3.x, as I understand, simple #Autwired injection can be used. How AppContext can be accessed?
Here the first problem is you are using #Named which is Java EE annotation and as for as I know Spring yet to support Java EE annotations. Hence instead of using #Named try to use Spring annotation #Service, #Component, #Repository etc.
Here is the example for you I have used JSF Managed bean as well to show how to integrate beans.
#ManagedBean(name="myBacking")
#RequestScoped
public class MyBacking {
private String myText;
#ManagedProperty(value="#{mySpring}")
MySpringBean mySpring;
public String getMyText() {
myText = mySpring.getText();
return myText;
}
public void setMyText(String myText) {
this.myText = myText;
}
public MySpringBean getMySpring() {
return mySpring;
}
public void setMySpring(MySpringBean mySpring) {
this.mySpring = mySpring;
}
}
#Service("mySpring")
#Scope("request")
public class MySpringBean {
#Autowired
MySecond mySecond;
public String getText(){
return "Hello KP" + mySecond.appObj();
}
}
#Service
#Scope("request")
public class MySecond {
#Autowired
ApplicationContext applicationContext;
public String appObj(){
MyThrid mythird =(MyThrid)applicationContext.getBean("myThrid");
return "My Second Bean calld "+ mythird.getTxt();
}
}
#Service
public class MyThrid {
public String getTxt(){
return "from thrid Bean";
}
}