AOP 面向切麵的程式設計(Aspect oriented programming,AOP,又譯作面向方面的程式設計、剖面導向程式設計)是電腦科學中的一種程式設計思想,旨在將橫切關註點與業務主體進行進一步分離,以提高程式代碼的模塊化程度。通過在現有代碼基礎上增加額外的通知(Advice)機制,能夠 ...
AOP
面向切麵的程式設計(Aspect-oriented programming,AOP,又譯作面向方面的程式設計、剖面導向程式設計)是電腦科學中的一種程式設計思想,旨在將橫切關註點與業務主體進行進一步分離,以提高程式代碼的模塊化程度。通過在現有代碼基礎上增加額外的通知(Advice)機制,能夠對被聲明為“切點(Pointcut)”的代碼塊進行統一管理與裝飾,如“對所有方法名以‘set*’開頭的方法添加後臺日誌”。該思想使得開發人員能夠將與代碼核心業務邏輯關係不那麼密切的功能(如日誌功能)添加至程式中,同時又不降低業務代碼的可讀性。面向切麵的程式設計思想也是面向切麵軟體開發的基礎。
Maven 依賴
<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
實現方式一:原生 Spring API 介面
public interface UserService {
void add();
int delete();
void query();
int update();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("function:add()");
}
@Override
public int delete() {
System.out.println("function:delete()");
return 0;
}
@Override
public void query() {
System.out.println("function:query()");
}
@Override
public int update() {
System.out.println("function:update()");
return 0;
}
}
//方法執行前的log
public class BeforeLog implements MethodBeforeAdvice {
@Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.err.println("method before log" + target.getClass().getName() + method.getName());
}
}
//方法執行後的log
public class AfterLog implements AfterReturningAdvice {
@Override
public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
System.err.println("method before log" + target.getClass().getName() + "-->" + method.getName() + "---return:" + returnValue);
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="log1" class="com.youzi.log.BeforeLog"/>
<bean id="log2" class="com.youzi.log.AfterLog"/>
<bean id="userservice" class="com.youzi.service.UserServiceImpl"/>
<!--配置AOP-->
<aop:config>
<aop:pointcut id="pointcut1" expression="execution(int com.youzi.service.UserServiceImpl.*(..))"/>
<aop:pointcut id="pointcut2" expression="execution(void com.youzi.service.*.add(..))"/>
<aop:advisor advice-ref="log1" pointcut-ref="pointcut1"/>
<aop:advisor advice-ref="log2" pointcut-ref="pointcut2"/>
</aop:config>
</beans>
execution()
切點函數語法:
execution(返回類型 包名.類名.方法名(參數類型))
也可以用*號表示所有的類型。
類名也可以用*代替表示包下麵的所有類
*(..):這個星號表示方法名,*號表示所有的方法,括弧中是方法的參數,兩個點表示任何參數。
實現方式二:自定義類
第一種實現方式雖然實現了在方法前或者方法後添加日誌,但是每實現一個功能就需要實現一個介面,通過下麵的方法可以把這些方法寫在一個類中
public class MethodLog {
public void beforeMethod() {
System.out.println("方法執行前");
}
public void afterMethod() {
System.out.println("方法執行後");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="MethodLog" class="com.youzi.log.MethodLog"/>
<bean id="userservice" class="com.youzi.service.UserServiceImpl"/>
<aop:config>
<aop:aspect ref="MethodLog">
<aop:pointcut id="poindcut1" expression="execution(* com.youzi.service.*.*(..))"/>
<aop:before method="beforeMethod" pointcut-ref="poindcut1"/>
<aop:after method="afterMethod" pointcut-ref="poindcut1"/>
</aop:aspect>
</aop:config>
</beans>
上面的方法雖然簡化了代碼,但是目前看來不能像第一種方式一樣通過反射取到執行方法的信息
實現方式三:使用註解
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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">
<bean id="MethodLog" class="com.youzi.log.MethodLog"/>
<bean id="userservice" class="com.youzi.service.UserServiceImpl"/>
<!--在xml中聲明使用註解的方式-->
<aop:aspectj-autoproxy/>
</beans>
@Aspect
public class MethodLog {
@Before("execution(* com.youzi.service.UserServiceImpl.*(..))")
public void beforeMethod() {
System.out.println("方法執行前1");
}
@After("execution(* com.youzi.service.UserServiceImpl.*(..))")
public void afterMethod() {
System.out.println("方法執行後1");
}
@Around("execution(* com.youzi.service.UserServiceImpl.update(..))")
public int aroundMethod(ProceedingJoinPoint joinPoint) throws Throwable {
System.err.println("環繞前");
System.err.println(joinPoint.getSignature());
Object proceed = joinPoint.proceed();
System.err.println("環繞後");
return Integer.valueOf((Integer) proceed);
}
}
測試代碼統一為
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userservice", UserService.class);
userService.query();
userService.add();
userService.delete();
userService.update();