Aspect Object Programing with Spring boot?Example

Aspect Object Programing with Spring boot?Example

Introduction:

In the previous article, we talked about Aspect and if you read the previous article you should know the aspect core concept and spring boot concept with aspect. In this article, I will write aspect code using java inside the spring boot project.

the github project link https://github.com/abdalrhmanAlkraien/Aspect-example


Aspect configuration:

To apply aspect programming inside any spring-boot application, you need to make an aspect configuration. In the current step we need to go to two locations:

For the first one, we need to update the pom.xml file and put Aspect dependency.

We can use the aspect dependency provided in spring, but in this article, I will use the AspectJ dependency.

Pom.xml file:

<!-- enable aspect -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.3.18</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.9.1</version>
</dependency>

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.9.1</version>
</dependency>

Now I will create a java file called AspectConfig.java and then I will add @EnableAspectJAutoProxy. From the last article, you can know why I need to enable proxy when using Aspect.

AspectConfig.java file

@EnableAspectJAutoProxy(exposeProxy = true)
public class AspectConfig {
}

Now I have a Controller package and Service Package, I will write business logic inside them.

Business logic:

In this section just we need to define some Api to take some action for Aspect.

controller.TaskController.java

@RestController
@RequestMapping("/api/tasks")
public class TaskController {

    private final TaskService taskService;
    public TaskController(TaskServiceImp taskService){
        this.taskService=taskService;
    }

    @GetMapping
    @ApiOperation(value = "Get all tasks")
    public void getTask(){
        taskService.getTask();
    }

    @PostMapping
    @ApiOperation(value = "save new task")
    public String saveTask(){
        return taskService.saveTask();
    }

    @GetMapping("/{id}")
    @ApiOperation(value = "get one task by id")
    public String getTaskById(@ApiParam(value = "1",required = true, example = "123") @PathVariable("id") Long id){
        return taskService.getTaskById(id);
    }

    @PutMapping
    @ApiOperation(value = "update any task")
    public String updateTask(){
        return taskService.updateTask();
    }

    @DeleteMapping("/{id}")
    @ApiOperation(value = "delete task by id")
    public String deleteTaskById(@ApiParam(value = "1",required = true, example = "123") @PathVariable("id") Long id){
        return taskService.deleteTaskById(id);
    }
}

service.TaskService.java

public interface TaskService {

    String getTask();
    String saveTask();
    String getTaskById(Long id);
    String updateTask();
    String deleteTaskById(Long id);

}

service.Impl.TaskServiceImpl.Java

@Service
public class TaskServiceImp implements TaskService {

    public  TaskServiceImp(){

    }

    @Override
    public String getTask() {
        return "execute getTask";
    }

    @Override
    public String saveTask() {
        return "execute saveTask";

    }

    @Override
    @Loggable
    public String getTaskById(Long id) throws Exception {
        if(id<1){
            throw new Exception("the id less than 1");
        }
        return "execute getTaskById ".concat(id.toString());
    }

    @Override
    public String updateTask() {
        return "execute updateTask";

    }

    @Override
    @Loggable
    public String deleteTaskById(Long id) throws Exception {
        if(id<1){
            throw new Exception("the id less than 1");
        }
        return "execute deleteTaskById ".concat(id.toString());
    }

}
Now All the Services and Api are Done.

Aspect Task:

In this session, I will create an aspect class to define our Pointcut and Advice to log all API executed and logs exceptions.

What the aspect will do?

the aspect will log different things:

  • We need log info when entering inside the API methods.
  • We need log info when exiting from the API.
  • We need logs info what the returning value from the service methods using annotations called Loggable.
  • We need logs info on the exceptions that happened in the project.

aspect.AspectTask.java file:

In the current session, I will split the explain each method and after finishing, I will put all the class methods in one block.

In the first step, I defined some variables to use as a Pointcut like the following code.

private final String apiPointcut="execution(* com.example.logger.springlogger.controller.*.*(..))";
private final String exceptionPointcut ="execution(* com.example.logger.springlogger.*.*.*(..))";

Actually, We can use this way to define point cut or another way you see it with other methods in this article.


pointcut as a method

We can use @Pointcut to define your pointcut but in this way, you must use a method to define it. Like the following code.

@Pointcut(apiPointcut)
public void APIPointCut(){}

Now we have our Pointcut but we need to use Advice to handle this Pointcut.

Advice in aspect

When using the Aspect, you must use advice with it, because we need to log any things when a specific event happens.

To use any advice, we need to Pointcut for the advice. we can define the pointcut inside any advice using this way @Advice(Pointcut).

You can see this link to know the Advice type in Aspect.
@Before("APIPointCut()")
public void logEnteringAPI(JoinPoint joinPoint){
    log.info("The API method will executing {}",joinPoint.getSignature().getName());
    if(joinPoint.getArgs().length>0){
        Object[] args=joinPoint.getArgs();
        log.info("the Api argument is ".concat(args[0].toString()));
    }
}

JoinPoint: Joinpoint is a reference from the original method, we can know what the method name and args from this method from JoinPoint Interface.

The above advice logEnteringAPI(JoinPoint joinPoint)will be executed before calling the method inside the pointCut, and then will execute the implementation inside it, and after that will execute the original method and forward with a happy scenario.

I used the Args object instance from JoinPoint, for logs the argument in the original methods.


Now we need to write the implementation for logs when exiting from the API method.

We will use the same pointCut called APIPointCut() method because we need logs in the same package. We used it with @Before advice.

@After("APIPointCut()")
public void logExitingAPI(JoinPoint joinPoint){
    log.info("The API method finished execute {}",joinPoint.getSignature().getName());
}

with the above advice will be executed after calling the method inside the Pointcut.


I created a new annotation for log specific methods called Loggable

public @interface Loggable {
}

and I am using it above two methods inside the TaskServiceImp called getTaskById and deleteTaskById 

like the following snippet code:

@Override
@Loggable
public String getTaskById(Long id)

and

@Override
@Loggable
public String deleteTaskById(Long id)

And the advice method must have a new pointcut for pointing on the annotation. 

@AfterReturning(value = "@annotation(com.example.logger.springlogger.aspect.TaskServiceAspect.Loggable)"
,returning = "returnValue")
public void logEachReturningValue(JoinPoint joinPoint
,Object returnValue){
    log.info("the value from method:"
.concat(joinPoint.getSignature().getName())
.concat(returnValue.toString()));
}

The above code for handling any method has @Loggable inside the project. And will log the return value.


Now we need a log of any exception and which method has this exception.

We use the variable as Pointcut in this case called exceptionPointcut and will use the variable with AfterThrowing advice to catch any exception in the project.

The exceptionPointcut will scan all the methods in the project, you can see the value of it from the follwoing line of code.

exceptionPointcut= ”execution(* com.example.logger.springlogger.*.*.*(..))” 

@AfterThrowing(value = exceptionPointcut ,throwing = "exception")
public void logsExceptionsFromAnyLocations(JoinPoint joinPoint,Throwable exception) throws Throwable {
    log.error("We have error in this method {}",joinPoint.getSignature().getName());
    log.error("The exception message: ".concat(exception.getMessage()));
}

The above advice will execute when any exception throwing in the application and will log which the method name has this exception and the exception message.


aspect.AspcetTask.java file:

@Aspect
@Log4j2
@Component
public class TaskServiceAspect {

    private final String apiPointcut="execution(* com.example.logger.springlogger.controller.*.*(..))";
    private final String exceptionPointcut ="execution(* com.example.logger.springlogger.*.*.*(..))";

    @Pointcut(apiPointcut)
    public void APIPointCut(){}


    @Before("APIPointCut()")
    public void logEnteringAPI(JoinPoint joinPoint){
        log.info("The API method will executing {}",joinPoint.getSignature().getName());
        if(joinPoint.getArgs().length>0){
            Object[] args=joinPoint.getArgs();
            log.info("the Api argument is ".concat(args[0].toString()));
        }
    }

    @After("APIPointCut()")
    public void logExitingAPI(JoinPoint joinPoint){
        log.info("The API method finished execute {}",joinPoint.getSignature().getName());
    }

    @AfterThrowing(value = exceptionPointcut ,throwing = "exception")
    public void logsExceptionsFromAnyLocations(JoinPoint joinPoint,Throwable exception) throws Throwable {
        log.error("We have error in this method {}",joinPoint.getSignature().getName());
        log.error("The exception message: ".concat(exception.getMessage()));
    }

    @AfterReturning(value = "@annotation(com.example.logger.springlogger.aspect.TaskServiceAspect.Loggable)",returning = "returnValue")
    public void logEachReturningValue(JoinPoint joinPoint,Object returnValue){
        log.info("the value from method: ".concat(joinPoint.getSignature().getName()).concat(returnValue.toString()));
    }

    public @interface Loggable {
    }
}

Log after running the application

After running the application and calling any API you can see something like this.

Conclusion

From this Article we learned and practiced how we can use aspect with spring boot, and how we can use our own annotations with aspect.

If we have been confused by the previous article, I think cleaned your think about aspect with this article.

You can use this link to access this project on GitHub, you can see it to learn, clone, and practice it.

要查看或添加评论,请登录

社区洞察

其他会员也浏览了