AOP的原理
AOP的实现有两种方式,一种是使用代理,一种是直接修改.class(即使用CGLib等类库)。
问题的引入
<!-- 事物切面配置 -->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="insert" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="testService" expression="execution (* com.baobao.service.MyBatisService.*(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="testService"/>
</aop:config>
好神奇!这样就可以了?tx:advice
是怎么实现的?如果我想要自己编写advice
,那我应该要怎么做呢?
何谓Advice
如果要自己实现一个Advice,要怎么做?一个Advice包含了什么东西?怎么样的才算一个Advice?
来,先看网上一个例子:
一个需要注入的类:
package com.mkyong.customer.services;
public class CustomerService {
private String name;
private String url;
public void setName(String name) {
this.name = name;
}
public void setUrl(String url) {
this.url = url;
}
public void printName() {
System.out.println("Customer name : " + this.name);
}
public void printURL() {
System.out.println("Customer website : " + this.url);
}
public void printThrowException() {
throw new IllegalArgumentException();
}
}
before advice
package com.mkyong.aop;
import java.lang.reflect.Method;
import org.springframework.aop.MethodBeforeAdvice;
public class HijackBeforeMethod implements MethodBeforeAdvice
{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("HijackBeforeMethod : Before method hijacked!");
}
}
如何把before advice注入到CustomerService
类中:
<beans xmlns="http://www.springframework.org/schema/beans"
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-2.5.xsd">
<bean id="customerService" class="com.mkyong.customer.services.CustomerService">
<property name="name" value="Yong Mook Kim" />
<property name="url" value="http://www.mkyong.com" />
</bean>
<bean id="hijackBeforeMethodBean" class="com.mkyong.aop.HijackBeforeMethod" />
<bean id="customerServiceProxy"
class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="customerService" />
<property name="interceptorNames">
<list>
<value>hijackBeforeMethodBean</value>
</list>
</property>
</bean>
</beans>
customerServiceProxy
就是注入了HijackBeforeMethod
的CustomerService
吗?是的!
package com.mkyong.common;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import com.mkyong.customer.services.CustomerService;
public class App {
public static void main(String[] args) {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
new String[] { "Spring-Customer.xml" });
CustomerService cust =
(CustomerService) appContext.getBean("customerServiceProxy");
System.out.println("*************************");
cust.printName();
System.out.println("*************************");
cust.printURL();
System.out.println("*************************");
try {
cust.printThrowException();
} catch (Exception e) {
}
}
}
customerServiceProxy
怎么会是CustomerService
?customerServiceProxy
什么时候继承CustomerService
的?
这是Spring使用CGLib实现的。
什么是Advice和Advisor
从上面的例子中我们可以很清楚地明白什么是Advice:HijackBeforeMethod
就是Advice, Advice定义了如何对要被注入的方法加入相关的逻辑,比如是在被注入方法的前面加上逻辑,还是在方法的后面。
下面的类使用了aspectj注解:
package test.mine.spring.bean;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
@Aspect
public class SleepHelper {
@Pointcut("execution(* *.sleep())")
public void sleeppoint(){}
@Before("sleeppoint()")
public void beforeSleep(){
System.out.println("睡觉前要脱衣服!");
}
@AfterReturning("sleeppoint()")
public void afterSleep(){
System.out.println("睡醒了要穿衣服!");
}
}
beforeSleep()
和@Before
是Advice,而sleeppoint()
(PointCut)和beforeSleep()
(包括@Before
)是Advisor。
再看问题
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<!-- 事物切面配置 -->
<tx:advice id="advice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
<tx:method name="insert" propagation="REQUIRED" read-only="false"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="testService" expression="execution (* com.baobao.service.MyBatisService.*(..))"/>
<aop:advisor advice-ref="advice" pointcut-ref="testService"/>
</aop:config>
对于以上的配置,我们可以在看看另一个例子:
<bean id="customizableTraceInterceptor" class="
org.springframework.aop.interceptor.CustomizableTraceInterceptor">
<property name="enterMessage" value="Entering $[methodName]($[arguments])"/>
<property name="exitMessage" value="Leaving $[methodName](): $[returnValue]"/>
</bean>
<aop:config>
<aop:advisor advice-ref="customizableTraceInterceptor"
pointcut="execution(public * org.springframework.data.jpa.repository.JpaRepository+.*(..))"/>
</aop:config>
稍微看一些org.springframework.aop.interceptor.CustomizableTraceInterceptor
的源码就会知道CustomizableTraceInterceptor
实现了MethodInterceptor
接口:
public class CustomizableTraceInterceptor extends AbstractTraceInterceptor
public abstract class AbstractTraceInterceptor implements MethodInterceptor, Serializable
那么我们可不可以自己也写一个DataSourceTransactionManager
和tx:advice
?
再看注解的AOP
@Before("sleeppoint()")
public void beforeSleep(){
System.out.println("睡觉前要脱衣服!");
}
只是在一个方法上加上@Before("sleeppoint()")
,就等于:
<bean id="beforeSleepInterceptor" class="BeforeSleepInterceptor">
</bean>
<aop:config>
<aop:pointcut id="sleeppoint" expression="execution(* *.sleep())"/>
<aop:advisor advice-ref="beforeSleepInterceptor" pointcut-ref="sleeppoint"/>
</aop:config>
public class BeforeSleepInterceptor implements MethodBeforeAdvice
{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("BeforeSleepInterceptor : Before method hijacked!");
}
}
网友评论