AOP的原理
AOP的实现有两种方式,一种是使用代理,一种是直接修改.class(即使用CGLib等类库)。
问题的引入
好神奇!这样就可以了?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
类中:
hijackBeforeMethodBean
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。
再看问题
对于以上的配置,我们可以在看看另一个例子:
稍微看一些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()")
,就等于:
public class BeforeSleepInterceptor implements MethodBeforeAdvice
{
@Override
public void before(Method method, Object[] args, Object target)
throws Throwable {
System.out.println("BeforeSleepInterceptor : Before method hijacked!");
}
}