Decorate spring boot repository - spring

I'm working on a Spring Boot API that's supposed to be deployed later this month. We created our own interface for a repository, and extended the CrudRepository. Spring boot autowires everything.
What I would like to do is add more logging capabilities, such as LOGGER.info("searched for solution ID").
Currently our code looks like this:
#Repository
public interface ProductSolutionRepository extends CrudRepository<ProductSolution, String> {
public List<ProductSolution> findBySolutionId(#Param("solutionId") int solutionId);
Since Spring configures everything don't really see a way of decorating these functions to add logging functionality. Can someone help me out by pointing me to the documentation, showing a nice example, or explaining the concept behind logging decorators?

First, I would like to point out some redundant codes for you.
You don't need to annotate the repository with #Repository, spring boot can autowire it automatically.
#Param is used when you write a sql with #Query, you just need to declare your parameters here.
The repository is the dao layer. A normal practice, you should create a service for each repository and autowire the repository into the service. Then you can implement transaction or write logs there.

You can use a single file AOP Logging Aspect using AspectJ cutting across your repository interfaces layer and logging method name, input args and output.
Assuming for this purpose a RepositoryLoggingAspect class, you'd have to annotate it first with #Aspect:
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class RepositoryLoggingAspect {
//..
}
And then create a Pointcut aiming at your repository package you want to cut-accross:
#Pointcut("within(package.of.my.repositories..*)")
public void repositoriesPackagePointcut() {}
And finally define the logging logic in an #Around annotated method:
#Around("repositoriesPackagePointcut()")
public Object logMyRepos(ProceedingJoinPoint joinPoint) throws Throwable {
//log method name using: joinPoint.getSignature().getName()
//log method arguments using: Arrays.toString(joinPoint.getArgs())
//store and log method output using: Object myResult = joinPoint.proceed();
}

Related

Spring AOP pointcut execution not working

I'm working on a Spring Boot project that uses Spring Cloud (io.awspring.cloud:spring-cloud-aws-dependencies:2.4.2) to produce and consume AWS SQS messages. I have several message producers and several message consumers, and all is working fine from that perspective.
I now have a cross cutting concern where I need to set a header on all messages being produced/sent; and to read that header on all messages being consumed (correlationId), and AOP seems like a good fit.
My aspect for handling (receiving) a message works fine:
#Before("execution(* org.springframework.messaging.handler.invocation.AbstractMethodMessageHandler.handleMessage(..))")
fun beforeHandleMessage(joinPoint: JoinPoint) {
The class and method that it is targeting is:
package org.springframework.messaging.handler.invocation;
...
public abstract class AbstractMethodMessageHandler<T>
implements MessageHandler, ApplicationContextAware, InitializingBean {
...
#Override
public void handleMessage(Message<?> message) throws MessagingException {
As mentioned, this works great.
However, I can't get my pointcut for sending a message working. This is my aspect:
#Before("execution(* org.springframework.messaging.support.AbstractMessageChannel.send(..))")
// #Before("execution(* io.awspring.cloud.messaging.core.QueueMessageChannel.send(..))")
fun beforeSendMessage(joinPoint: JoinPoint) {
And the class and method that I'm trying to target is this:
package org.springframework.messaging.support;
...
public abstract class AbstractMessageChannel implements MessageChannel, InterceptableChannel, BeanNameAware {
...
#Override
public final boolean send(Message<?> message) {
But it doesn't seem to work. I've also tried writing the pointcut to target the concrete implementation class (as commented out above), but that also does nothing.
I can't see what the difference is between my working pointcut for beforeHandleMessage and beforeSendMethod, other than the pointcut for beforeSendMethod is targeting a final method. Is that relevant?
Any pointers to get this working would be very much appreciated;
Thanks
Spring AOP uses dynamic proxies, i.e. it works by subclassing (CGLIB proxy) or by implementing interfaces (JDK proxies). In your case, you are targeting a class method rather than an interface method. The class method is final, which explains why it cannot work that way, because you cannot override a final method in a CGLIB proxy. What you should do instead is to
target the interface method MessageChannel.send(Message) and
make sure to use JDK proxies, i.e. not the "proxy target class" (CGLIB) mode. In Spring core, JDK proxy mode is the default, in Spring Boot CGLIB mode. So in Boot, you need to manually reconfigure the framework to permit for JDK proxies, which is only possible there via config file, not via annotations (they come too late in the bootstrapping process for Boot).
More specifically, you need this in src/main/resources/application.properties for Spring Boot:
# This works, now we can create JDK interface proxies. The seemingly equivalent alternative
# #EnableAspectJAutoProxy(proxyTargetClass = false)
# where 'false' is even the default, does *not* work in Spring Boot.
spring.aop.proxy-target-class=false
I found the answer from this other SO answer: Spring AOP ignores some methods of Hessian Service
I know that Spring AOP won't intercept local method calls. I.e. the proxy which is applied doesn't intercept the calls if the same object calls its own method, even if it matches the pointcut expression.
The problem was that the send method I was targeting was called by a number of other methods in the class.
Looking at the call stack I found a different method that was the first method called in the class. Changing the pointcut to target that method has worked.

Spock with Spring Boot and Camel: Zero interactions with detached mock

I am having some issues with testing my camel context with spring boot.
I am using spring boot 1.5.6, spock 1.1-groovy-2.4, camel 2.19.2, and camel-spring-boot-starter 2.19.2.
I am using a spock mock, and I'm using the DetachedMockFactory in a #TestConfiguration class. All of my beans use constructor injection. I am injecting a mocked #Repository into one of the processor #Components, and I am also injecting it into my test class to define interactions.
I have my test annotated with #SpringBootTest with the classes list including all Processor implementations, and all RouteBuilder extensions. I also have an '#Import' with my TestConfiguration class. I am even using constructor injection for this repository bean in my test!
But it seems that the mock that is injected into the test class is not the one that is in use. Does anyone have an idea what could be wrong? I have tried #DirtiesContext to reload the context both before and after each test, but that did not help.
Problems with DetachedMocks not behaving correctly, e.g., appearing to be the same instance, are usually caused by some framework wrapping them in proxies. For example this can be caused by #Transactional annotation in Spring, which creates a proxy to facilitate jdbc-session management. See also issue #758
For spring you can use the methods of AopUtils (jdoc). The simple way is to use AopUtils.isAopProxy to check if it is proxied by spring an then unwrap it.
public static <T> T getTargetObject(Object proxy) throws Exception {
if (AopUtils.isAopProxy(proxy)) {
return (T) ((Advised) proxy).getTargetSource().getTarget();
} else {
return (T) proxy;
}
}
And in a Test
def "sample service test"() {
given:
def sampleRepositryMock = getTargetObject(sampleRepositry)
when:
sampleService.doSomething() // simply invoke sampleRepositry.doSomething() in it
then:
1 * sampleRepositryMock.doSomething()
0 * _
}
Edit: Since Spock 1.2 there is an extension to automatically unwrap injected beans #UnwrapAopProxy.
#Inject
#UnwrapAopProxy
SampleRepositry sampleRepositryMock
If someone comes up with the same problem.
Spock added additional #UnwrapAopProxy that will do the job for you instead of the util method mentioned above. You can also drop the DetachedMockFactory
#SpringSpy
#UnwrapAopProxy
Service service

Spring data - Manual implementation

Is it possible to write my own function implementation along usage of spring repositories?
I would like to actually implement the function
getUserByFirstName()
and not get it automagically.
While i still want to get
getUserById()
automagically from spring-data.
1) is it possible?
2) is it possible to achieve logging for all methods spring data automagically generates? (or should i write them manually with
logger.log("entering method ...");
See section 1.3 of the manual for the first requirement:
http://docs.spring.io/spring-data/jpa/docs/1.4.2.RELEASE/reference/html/repositories.html#repositories.single-repository-behaviour
For the second requirement I guess some AOP based solution might work for you well here. See here for example using Spring's AOP support:
logging with AOP in spring?
Yes, you can!!!
There is an amazing feature in Spring data that allows this:
Create an interface with your custom method:
public interface UserRepositoryCustom {
User getUserByFirstName();
}
Make your Spring Data interface extends this new interface, as well as the Spring data crud interface (or JPA, Mongo or whatever spring data interface you're extending):
public interface MySpringDataRepository extends CrudRepository<User, Long>, UserRepositoryCustom {
}
Create a class that implements only your custom interface. The name of the class must be Impl, for instance: UserRepositoryImpl:
public class MySpringDataRepositoryImpl implements UserRepositoryCustom {
public User getUserByFirstName(){
//Your custom implementation
}
}
Now, you only need to inject the Spring data repository in your service and you can use both methods: the spring-data implemented method and your custom implemented method:
#Inject private MySpringDataRepository myRepository;
That's it!!
Look at this section in the documentation:
Spring Data Custom implementations

Using PersistenceContext in a Quartz Job

We're using Spring 3.1, JPA (via Hibernate) and Quartz. Typically we interact with the DB via #PersistenceContext annotation on Service beans, and either SpringMVC controllers, or GraniteDS-managed service invocation.
I'm working on writing a Quartz job that needs to interact with the database. I've tried everything I can find to get this working. I tried passing in a Spring-managed component (annotated with #PersistenceContext and #Transactional) via the jobMap, the call to entityManager.persist(o) executes, but nothing happens in the database. I also tried similar to this answer, creating a factory class to call autowireBean() on the job object. I set up the job class like so:
public class CreateAlertJob implements Job {
#PersistenceContext
EntityManager entityManager;
#Override
#Transactional
public void execute(JobExecutionContext context) throws JobExecutionException {
SomeEntity entity = new SomeEntity();
entityManager.persist(entity);
}
}
Same result, the method executes but the database is unaltered. I found this blog post which references a GitHub project. There he is using JpaInterceptor to establish a Hibernate session, but this uses the DAO pattern and I'd like to stick with using #PersistenceContext.
Clearly there is something about the Quartz thread that is preventing this from working properly? I'm about out of ideas and considering making a web service call to a SpringMVC controller just to get this working.
Since your CreateAlertJob is not created by Spring, #Transactional in it doesn't take effect.
You have the following options:
Delegate actual work to Spring bean and put #Transactional there
Use programmatic transaction management
Use AspectJ-based AOP implementation instead of Spring default implementation (but it would be overkill for such a simple problem)

How to use spring to resolve dependencies of an object created manually?

I would like to know if it's possible to use Spring to resolve the dependencies of an object created manually in my program. Take a look at the following class:
public class TestClass {
private MyDependency md;
public TestClass() {
}
...
public void methodThaUsesMyDependency() {
...
md.someMethod();
...
}
}
This TestClass is not a spring bean, but needs MyDependency, that is a spring bean. Is there some way I can inject this dependency through Spring, even if I instantiate TestClass with a new operator inside my code?
Thanks
Edit: The method I'm describing in my original answer below is the general way to accomplish DI external of the container. For your specific need - testing - I agree with DJ's answer. It's much more appropriate to use Spring's test support, for example:
#Test
#ContextConfiguration(locations = { "classpath*:**/applicationContext.xml" })
public class MyTest extends AbstractTestNGSpringContextTests {
#Resource
private MyDependency md;
#Test
public void myTest() {
...
While the above example is a TestNG test, there is also Junit support explained in 8.3.7.2. Context management and caching.
General approach: Annotate your class with #Configurable and utilize AspectJ load-time or compile-time weaving. See 6.8.1 in the Spring documentation on AOP for more details.
You can then annotate your instance variables with #Resource or #Autowired. Though they accomplish the same goal of dependency injection, I recommend using #Resource since it's a Java standard rather than Spring-specific.
Lastly, remember to consider using the transient keyword (or #Transient for JPA) if you plan on serializing or persisting the objects in the future. Chances are you don't want to serialize references to your DI'd repository, service, or component beans.
See the autowire() method on the AutowireCapableBeanFactory class. If you use an ClasspathXmlApplicationContext, you can get the factory with getAutowireCapableBeanFactory()
To get the ApplicationContext, you would need to use a static singleton or other central repository, such as JNDI or a Servlet container. See DefaultLocatorFactory on how to get an instance of the ApplicationContext.
If what you need is for testing purposes, Spring has good support for the scenario that you described above.
Check out Spring Reference manual section on Testing

Resources