After the DAO layer, let us monitor all service and @Controller request transactions by following these steps:
- This is the first recipe that will showcase the use of custom transaction management annotations in formulating Pointcuts for advices. Using Reflection APIs, create the following method-level annotation inside the org.packt.aop.transaction.annotation package:
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) public @interface MonitorService { }
- To make @MonitorTransaction transaction-aware, implement org.springframework.transaction.PlatformTransactionManager and place it inside org.packt.aop.transaction.core:
@Component public class TransactionManager implements PlatformTransactionManager { @Override public void commit(TransactionStatus status) throws TransactionException { } @Override public TransactionStatus getTransaction(TransactionDefinition definition) throws TransactionException { return new SimpleTransactionStatus(); } @Override public void rollback(TransactionStatus status) throws TransactionException { } }
- Inject the custom TransactionManager bean into the SpringContextConfig container and extract org.springframework.transaction.support.TransactionTemplate, which will be used by the advices:
@Configuration @EnableWebMvc @EnableAspectJAutoProxy @ComponentScan(basePackages = {"org.packt.aop.transaction", "org.packt.aop.transaction.core", "org.packt.aop.transaction.annotation"}) public class SpringContextConfig { @Autowired private PlatformTransactionManager transactionManager; // refer to sources @Bean("template") TransactionTemplate transactionTemplate(){ TransactionTemplate template = new TransactionTemplate(); template.setTransactionManager(transactionManager); return template; } }
- Implement an aspect class that includes a Pointcut that filters all methods, that has @MonitorTransaction, and contains an advice that recognizes the custom annotation as the main trigger to execute some events with the help of TransactionManager:
@Component @Aspect public class MonitorServiceAspect { @Autowired private TransactionTemplate template; private Logger logger = Logger.getLogger(MonitorTransactionAspect.class); @Around("execution(* *(..)) && @annotation(monitor)") public void logIt(ProceedingJoinPoint pjp, MonitorService monitor) { template.execute(s->{ try{ Employee employee = (Employee) pjp.proceed(); logger.info(employee.getFirstName()); } catch (Throwable ex) { throw new RuntimeException(); } return null; }); } }
- Apply now the preceding annotation to the readEmployee() method of EmployeeServiceImpl:
@Service public class EmployeeServiceImpl implements EmployeeService { @Autowired private EmployeeDao employeeDaoImpl; @MonitorService @Override public Employee readEmployee(@NegativeArgs Integer empId) { return employeeDaoImpl.getEmployee(empId); } // refer to sources }
- Create another custom annotation, @MonitorRequest, using step 1 to step 5. Implement also a MonitorRequestAspect that will use the annotation to trigger a logging event for the deleteRecord() handler of EmployeeController:
@Component @Aspect public class MonitorRequestAspect { @Autowired private TransactionTemplate template; private Logger logger = Logger.getLogger(MonitorRequestAspect.class); @Around("execution(* *(..)) && @annotation(monitor)") public void logIt(ProceedingJoinPoint pjp, MonitorRequest monitor) { String methodName = pjp.getSignature().getName(); template.execute(s->{ try{ logger.info("executing request handler: " + methodName); } catch (Throwable ex) { throw new RuntimeException(); } return null; }); } }
- Save all files. Then clean, install, and deploy the project. Check ch05.log to verify the result.