I have one controller which has 4 request mapping and I cannot do changes in this controller. Is it possible to have a class(controller/component) which overrides one of the request mapping methods to have my custom implementation for this request mapping and using the other 3 request mapping methods as is. Is it possible?
Below is a sample code:
#Controller
public class Base {
#RequestMapping("/add")
public String add() throws Exception {
return "add";
}
#RequestMapping("/update")
public String update() throws Exception {
return "update";
}
#RequestMapping("/remove")
public String remove() throws Exception {
return "remove";
}
#RequestMapping("/show")
public String show() throws Exception {
return "show";
}
}
public class ExtendedBase extends Base {
public String add() throws Exception {
return "newAdd";
}
}
Since you want to override just one method from the parent controller and retain the URLs for all the controller methods, you need a way to prevent Spring from mapping URLs to the parent controller since otherwise you will get duplicate URL mappings when you add the child controller.
Assuming your controllers are:
package com.theirs.web.controller;
#Controller
#RequestMapping("/base")
public class Base {
#RequestMapping("/add")
public String add() { ... }
#RequestMapping("/update")
public String update() { ... }
#RequestMapping("/remove")
public String remove() { ... }
#RequestMapping("/show")
public String show() { ... }
}
package com.mine.web.controller;
#Controller
#RequestMapping("/base")
public class ExtendedBase extends Base {
#Override
#RequestMapping("/add")
public String add() { ... }
}
You could try this in dispatcher-servlet.xml:
<context:component-scan base-package="com.theirs.web,com.mine.web">
<context:exclude-filter type="regex" expression="com\.theirs\.web\.controller\.Base" />
</context:component-scan>
This will exclude the parent controller class from being managed by Spring while allowing the child class to be managed by Spring. Thus, there will be only one class mapped to /base and its child URLs.
It is possible only in case if you provide different mapping for the child controller. Otherwise Spring will complain about ambiguous mapping (as you will have two conrollers with the same mapping).
The solution would be to have different mapping for your child controller.
#Controller
#RequestMapping(value = "/child")
public class ExtendedBase extends Base {
public String add() throws Exception {
return "newAdd";
}
}
Note here, that you should annotate child class with #Controller, otherwise it wouldn't recognize it.
So in this case, you can access your add() via both - child and parent controllers. e.g.
/add - will result calling the Base.add()
/show - will result calling the Base.show()
/child/add - will result calling the ExtendedBase.add()
/child/show - will result calling the Base.show()
Related
I am writing a Spring Boot Application. I want to audit methods with my annotation #AuditMetod: For example I have method foo() with the annotation:
#AuditMetod(name = "SomeValue")
foo() {...}
I want to handle and audit such methods like this (the simplest example):
auditMethod(Method method) {
if (method.hasAnnotation(AuditMethod.class)) {
System.out.println (method.getName() + " was called at " + new Date())
}
}
upd
Thanks to #Karthikeyan #Swapnil Khante and #misha2048 I understood, that I need to use AOP. But I have 2 problems:
The only method in Aspect class in not being called and I don't see the inscription "----------ASPECT METHOD IS CALLED-----------" in log
How can I check in aspect method what method it is intercepting. To get an instance of Method class.
Now I have the following code:
Controller:
#PostMapping
#LoggingRest(executor = "USER", method = "CREATE", model = "SUBSCRIPTION")
public ResponseEntity<?> create(#Valid #RequestBody SubscriptionRequestDto dto) {
...
}
Aspect:
`#Aspect
#Slf4j
#Component
public class AuditAspect {
#Pointcut(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
log.info("----------ASPECT METHOD IS CALLED------------");
}`
And annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface LoggingRest {
String executor() default "SYSTEM";
String method() default "";
String model() default "";
}
Auditing is a cross-cutting concern and can be handled using AOP.
Another solution would be to use a low-level solution by writing a custom annotation and using a Spring interceptorto write your business logic.
To use the Spring interceptor you will need to implement the HandlerInterceptor interface
Example of the annotation
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface Audit {
boolean active() default true;
}
Interceptor example
#Component
public class AuditInterceptor implements HandlerInterceptor {
#Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if (handler instanceof HandlerMethod) {
HandlerMethod handlerMethod = (HandlerMethod) handler;
Audit annotation = handlerMethod.getMethodAnnotation(Audit.class);
if (annotation != null && annotation.active()) {
// your business logic
}
}
HandlerInterceptor.super.afterCompletion(request, response, handler, ex);
}
check this interceptor example
I think one of the solutions here, as #Karthikeyan mentioned, is to use Spring AOP.
If you are not aware a brief introduction - spring-aop module implements the aspect oriented programming paradigm. We extract some common functionality, that we generally want to apply to some subset of functions/methods, to an entity called Aspect (see class annotated with #Aspect). This class will contain out cross-cutting functionality - such as auditing, for instance we want to audit the methods execution time, lets say. We just put the code to be executed, the condition, which tell the spring what exact beans methods should be affect by this aspect, see below.
For example, if I can audit the method execution duration with the following very simple example (in my case I said that any public method, returning void inside the Class com.example.stackoverflow.BusinessLogicClass must be inspected by this Aspect):
#SpringBootApplication
#EnableAspectJAutoProxy
public class StackoverflowApplication implements ApplicationRunner {
#Autowired
private BusinessLogicClass businessLogicClass;
public static void main(String[] args) {
SpringApplication.run(StackoverflowApplication.class, args);
}
#Override
public void run(ApplicationArguments args) throws Exception {
businessLogicClass.test();
}
}
#Aspect
#Component
class MyAspectLogicClass {
#Around("execution(public void com.example.stackoverflow.BusinessLogicClass.*(..))")
public Object hangAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long before = System.currentTimeMillis();
Object returnedValue = proceedingJoinPoint.proceed();
long after = System.currentTimeMillis();
System.out.printf("Retruned in '%s' ms %n", (after - before));
return returnedValue;
}
}
#Component
class BusinessLogicClass {
public void test() {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
In my case, I will get the time before method execution, then by the means of
proceedingJoinPoint.proceed() call I delegate the execution to the real method, and then, once I get the response back, I will get the current system time and calculate the execution time, fairly simple.
I hope I have at least directed you somewhere, if you are looking for documentation, this are the resources I suggest you should look for:
https://docs.spring.io/spring-framework/docs/2.5.x/reference/aop.html offical spring doc (stale a bit, but there are some valuable things to learn)
https://docs.spring.io/spring-framework/docs/4.3.15.RELEASE/spring-framework-reference/html/aop.html is more fresh doc
Hope it helped :)
The problem was in right annotation. In Aspect class I tried #Around and everything works as I need.
#Aspect
#Slf4j
#Component
public class AuditAspect {
#Around(value = "#annotation(com.aspect.annotations.LoggingRest)")
public void auditMethod(ProceedingJoinPoint proceedingJoinPoint) {
var method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
log.info("----------ASPECT METHOD IS CALLED------------");
}
}
For getting a Method instance I use fallowing code
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
I have a server built with java and spring.
What i am trying to do is that my controller with the same endpoint will get two different objects.
This is an example for what I mean:
I know I can do that:
public class Option1{
private String name;
...
//getter and setter
}
public class Option2{
private Long id;
...
//getter and setter
}
#Controller
public class Controller{
#RequestMapping(value = "service/getData/option1", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option1 data1){
return "option1"
}
#RequestMapping(value = "service/getData/option2", method = RequestMethod.POST)
#ResponseBody
public String searchProv(#ResponseBody Option2 data2){
return "option2"
}
}
but I wonder if it is possible to passing different json object to the same endpoint and do that:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#ResponseBody Option1And2 data){
if(data instanceof Option1){
return return ResponseEntity<Any>(data.name,HttpStatus.OK)
}
if(data instanceof Option2){
return ResponseEntity<Any>(data.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
such that 'Option1And2' is generic object can be option1 or option2.
I tried to replace 'Option1And2' to 'Any' but it didn't went well because I get a list of keys and values
You should use JsonNode object.
for your example you should do this:
#Controller
public class Controller{
#RequestMapping(value = "service/getData", method = RequestMethod.POST)
#ResponseBody
public ResponseEntity<Any> getData(#RequestBody JsonNode jsonNode){
ObjectMapper obj = new ObjectMapper();
if(jsonNode.has("name"){
Option1 result= obj.convertValue(jsonNode,Option1.class)
return ResponseEntity<Any>(result.name,HttpStatus.OK)
}
else {
Option2 result= obj.convertValue(jsonNode,Option2.class)
return ResponseEntity<Any>(result.id,HttpStatus.OK)
}
return ResponseEntity<Any>("ok",HttpStatus.OK)
}
the JsonNode and the ObjectMapper you should import from here:
import com.fasterxml.jackson.databind.ObjectMapper
import com.fasterxml.jackson.databind.JsonNode;
this link should help you to understand better on JsonNode and give you more details.
and this link should help you with the convertValue from JsonNode to java object(POJO).
This is a good time to use inheritance and Java Generics. It is worth noting, if your controller has any dependencies such as a #Service or #Repository, then those too must be generic.
You might have a generic controller:
abstract class GenericController<T> {
public abstract GenericService<T> getService();
#GetMapping
public ResponseEntity<Iterable<T>> findAll() {
return ResponseEntity.ok(getService().findAll());
}
#PostMapping
public ResponseEntity<T> save(T entity) {
return ResponseEntity.ok(getService().save(entity));
}
// #DeleteMapping, #PutMapping
// These mappings will automatically be inherited by
// the child class. So in the case of findAll(), the API
// will have a GET mapping on /category as well as a GET
// mapping on /product. So, by defining and annotating the
// CRUD operations in the parent class, they will automatically
// become available in all child classes.
}
#Controller
#RequestMapping("/category")
class CategoryContr extends GenericController<Category> {
#Autowired CategoryServ serv;
#Override
public GenericService<Category> getService() {
return serv;
}
}
#Controller
#RequestMapping("/product")
class ProductContr extends GenericController<Product> {
#Autowired ProductServ serv;
#Override
public GenericService<Product> getService() {
return serv;
}
}
You then have to have abstract versions of the dependencies. The services:
abstract class GenericService<T> {
public abstract GenericRepository<T> getRepository();
public Iterable<T> findAll() {
return getRepository().findAll();
}
public T save(T entity) {
return getRepository().save(entity);
}
}
#Service
class CategoryServ extends GenericService<Category> {
#Autowired CategoryRepo repo;
#Override
public GenericRepository<Category> getRepository() {
return repo;
}
}
#Service
class ProductServ extends GenericService<Product> {
#Autowired ProductRepo repo;
#Override
public GenericRepository<Product> getRepository() {
return repo;
}
}
Then, the services have their dependencies as well - the repositories:
#NoRepositoryBean
interface GenericRepository<T> extends JpaRepository<T, Long> {
}
#Repository
interface CategoryRepo extends GenericRepository<Category> {
}
#Repository
interface ProductRepo extends GenericRepository<Product> {
}
This was my first approach. It works very nicely. However, this does create a strong coupling between the business logic of each service and the generic service. The same holds true for the generic controller and its child classes. You can of course always override a particular CRUD operation. But, you must do this with care as you may created unexpected behavior. It is also worth noting that inheriting from classes that have methods that are annotated with #RequestMapping automatically exposes all of the annotated methods. This may be undesirable. For example, we may not want a delete option for categories, but we want it for products. To combat this, instead of annotating the method in the parent class, we can simply define it in the parent class, and override the desired CRUD operations with the added #RequestMapping annotation and then call the super class method.
Another approach is using annotations.
Seems like you want program itself to determine what type the option is.But before you do that,are you sure what is the difference between these two Object?
First is,what is the Option1And2 actually is?If the Option1And2 contains all the field of Option1 and Option2 but it's not the subclass of those,then probably the Option1And2 could be like:
#Data
public class Option1And2{
private String name;
private Long id;
}
If you have other limits like "one of them and only one of them has
to be null",then you could determine it by this rule.
If you don't have any other limitation,then maybe you could add a new
field as a flag.
In fact those code style are not recommend.If those two functions have different responsibilities,then maybe it's better to not mix them together.You will understand what I mean when you have to refactor these code.
If these two functions do have lots of things in common,maybe it's better for you to refactor the service logic instead of just combining two service roughly by creating a new param Option1And2.
By the way,what are you exactly want to do?Why do you want to merge those two object into one?
I'm using Spring Boot to set up a REST API. I'll be making a bunch of #RestControllers and want to set a pointcut on those methods that return a subtype of a specific abstract class I call Model. These controllers look something like this:
#RestController
public class UserController {
#RequestMapping(...)
public Person getAllPeople() {
...
}
}
Where my Person class would look something like this:
public class Person extends Model {
...
}
So would it be possible to write advice that looks something like this:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(<T extends mypackages.Model> T mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
Of course that won't work because the advice is not valid syntactically. In the reference documentation, I have only found information about generic parameters, not return types (Spring AOP reference). What I have now is this, but I think something like the example above would be a lot more efficient:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api.*.*(..))",
returning = "model")
public void doSomething(Object model) {
if (model instanceof Model)
doSomethingWithModel((Model) model);
}
}
My next question would be, is the same possible for those methods that return a Collection of suptypes of Model? Because the reference states that parameter types cannot be generic Collections.
Have you tried using + after your interface?
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(mypackages.Model+ mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
You could try do not specify the return type. Based on the documentation it will be resolved by the type of the parameter used at the returning clause:
A returning clause also restricts matching to only those method
executions that return a value of the specified type ( Object in this
case, which will match any return value).
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api.*.*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
Have a look to the below link. It answers also your second question, about generic collections.
Aspectj Matching Return Type
Just for curiosity I have created a project for testing this and it started working for me straight forward. I can only think the path your pointcut is pointing to is wrong. Try with:
#Aspect
#Component
public class ModelAspect {
#AfterReturning(
value = "execution(* mypackages.api..*(..))",
returning = "model")
public void doSomethingWithModel(Model model) {
...
}
}
You can have a look to my project at: spring-aspectj-interfaces
There you will see different values for the pointcut (only one not commented, of course), all of them valid.
I am looking for solution to intercept parent class inherited method which is called from child another method.
Parent class LoggerException class having handleException method and I am calling this method from its child class SubLoggerException's method getException,
trying to intercept inherited method handleException from aspect programming
public class LoggerException{
public String handleException(Exception genericException) {
System.out.println("enter in LoggerException ");
return "success";
}
}
public class SubLoggerException extends LoggerException{
public void getException(){
handleException(null);
}
}
#Aspect
public class ErrorNotificationLogger {
private Logger logger = Logger.getLogger(ErrorNotificationLogger.class);
#Around("setterMethod(o)")
public Object markedMethodsAdvice(ProceedingJoinPoint joinPoint, Object o) throws Throwable {
System.out.println(" ****** Around Advice called ***************** ");
return null;
}
//#Pointcut("execution(* com.aop.LoggerException+.handleException(..)) && target(com.aop.SubLoggerException)")
//#Pointcut("execution(* com.aop.LoggerException+.handleException(..)) && this(o)")
#Pointcut("execution(* com.aop.LoggerException.handleException(..)) && this(o)")
public void setterMethod(Object o) {}
}
public class App extends AbstractService{
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] { "Spring-Customer.xml" });
SubLoggerException cust = (SubLoggerException)appContext.getBean("subLoggerExceptionBean");
System.out.println("*************************");
cust.getException();
System.out.println("*************************");
try {
} catch (Exception e) {
}
}
}
When calling another instance method from a Spring AOP proxy, these calls are not intercepted. The reason is that the actual execution of an intercepted method always ocurrs at the original bean (the proxy's target bean). As a consequence the other method will neven be called on the proxy object, but always on the target bean itself. What you need is a way to access the proxy from inside the target bean and then call the method on the proxy. You can get the proxy this way:
AopContext.currentProxy()
So, what you have to do is:
public void getException(){
((LoggerException)AopContext.currentProxy).handleException(null);
}
But consider that the proxy must be accessible if this shall work. This can be configured in your appContext.xml:
<aop:aspectj-autoproxy expose-proxy="true"/>
Hope it helps!
I am trying to create a custom Converter for my Spring MVC application but it is never triggered.
Here's how I implemented it and registered it :
public class ObjectIdSpringConverter implements Converter<String, ObjectId>{
public ObjectIdSpringConverter(){
System.out.println("ObjectIdSpringConverter picked up ");
}
#Override
public ObjectId convert(String source) {
System.out.println("ObjectIdSpringConverter converting");
return new ObjectId(source);
}
}
And in my Spring MVC configuration I register it as follow :
#Configuration
#ComponentScan(basePackages = {"myapp.web.controller"})
public class SpringMvcConf extends WebMvcConfigurationSupport{
#Override
protected void addFormatters(FormatterRegistry registry) {
registry.addConverter(new ObjectIdSpringConverter());
}
//other stuff here
}
I placed some breakpoints in the converter's constructor and convert() method, and I can see that it is properly constructed, however the convert() method is never called which results in icorrect values received by my controller.
Here's an example controller method :
#Controller
public class HomeController {
#RequestMapping(value="/home/testObjectId.amlgm", method = RequestMethod.GET)
public ObjectId testObjectId(ObjectId oid){
System.out.println("expected 552952cec9ac88712ee0d36b, actual " + oid.toString());
System.out.println(oid);
return oid;
}
}
I know the convert method is never called because the break point is never hit and because the value is not the expected one. I also debugged / traced what spring was doing and it does not seem to use my converter to instantiate the parameter ObjectId of the controller method.
Can anyone point me to what I am missing ?
Thanks
Your formatter should implement Formatter<T>, where T will be ObjectId in your case
http://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/format/FormatterRegistry.html#addFormatter-org.springframework.format.Formatter-