Spring AOP
AOP stands for Aspect-Oriented Programming, which is programming for cross-sectional concerns. Cross-sectional concerns refer to functions common to multiple modules. For example, logging is a function that both the member module and the bulletin board module have. The following AOP example will show us how to separate cross-sectional concerns.
Add the following to pom.xml.
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver --> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.9.19</version> </dependency>
Create TestLogger.java like below.
TestLogger.java
package net.java_school.commons; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @Aspect public class TestLogger { Logger logger = LoggerFactory.getLogger(this.getClass()); @AfterReturning("execution(* net.java_school.bank.BankDao.deposit(..))") public void depositLog(JoinPoint point) { Object[] a = point.getArgs(); String accountNo = (String) a[0]; Long amount = (Long) a[1]; String methodName = point.getSignature().getName(); logger.debug("{}|{}|{}", methodName, accountNo, amount); } @AfterReturning("execution(* withdraw(..)) && args(accountNo, amount)") public void withdrawLog(String accountNo, double amount) { logger.debug("WITHDRAW|{}|{}", accountNo, amount); } }
Remove logging code from all Java sources like below.
MyBankDao.java
package net.java_school.bank; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; public class MyBankDao implements BankDao { static final String URL = "jdbc:oracle:thin:@127.0.0.1:1521:XE"; static final String USER = "scott"; static final String PASSWORD = "tiger"; //..omit.. @Override public void deposit(String accountNo, double amount) { Connection con = null; PreparedStatement pstmt = null; String sql = "UPDATE bankaccount " + "SET balance = balance + ? " + "WHERE accountNo = ?"; try { con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setLong(1, amount); pstmt.setString(2, accountNo); pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(null, pstmt, con); } } @Override public void withdraw(String accountNo, double amount) { Connection con = null; PreparedStatement pstmt = null; String sql = "UPDATE bankaccount " + "SET balance = balance - ? " + "WHERE accountNo = ?"; try { con = getConnection(); pstmt = con.prepareStatement(sql); pstmt.setLong(1, amount); pstmt.setString(2, accountNo); pstmt.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } finally { close(null, pstmt, con); } } //..omit.. }
XML configuration
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <aop:aspectj-autoproxy /> <bean id="testLogger" class="net.java_school.commons.TestLogger" /> <bean id="bankUi" class="net.java_school.bank.BankUi"> <property name="stream" value="#{T(System).out}" /> <property name="bank" ref="myBank" /> </bean> <bean id="myBank" class="net.java_school.bank.MyBank"> <property name="dao" ref="myBankDao" /> </bean> <bean id="myBankDao" class="net.java_school.bank.MyBankDao"> </bean> </beans>
Modify the main method of BankUi.java.
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML //AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig
JavaConfig configuration
BankConfig.java
package net.java_school.bank; import net.java_school.commons.TestLogger; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy public class BankConfig { @Bean public TestLogger testLogger() { return new TestLogger(); } @Bean public BankDao myBankDao() { return new MyBankDao(); } @Bean public Bank myBank() { Bank bank = new MyBank(); bank.setDao(myBankDao()); return bank; } @Bean public BankUi bankUi() { BankUi ui = new BankUi(); ui.setBank(myBank()); ui.setStream(System.out); return ui; } }
First, comment out all contents of the beans element in applicationContext.xml and modify the main method of BankUi.java as follows.
//ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml"); //XML AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(BankConfig.class); //JavaConfig
We confirmed that the limitations of Object-Oriented Programming could be supplemented by applying AOP to the cross-sectional concerns of the system in this section.
Final Source: https://github.com/kimjonghoon/aop
References