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