Spring使用动态代理实现AOP。
注解方式实现AOP
定义一个目标类
@Component
public class CutPointTest{
public void useHasArg(int arg) {
System.out.println("useHasArg(),arg:" + arg);
}
public void use() {
System.out.println("use()");
}
}
定义一个切面
@Component
@Aspect
public class AspectTest {
//无参切点
@Pointcut("execution(* com.test.aop.*.use(..))")
public void usePoint(){}
//有参切点
@Pointcut("execution(* com.test.aop.*.useHasArg(int)) && args(arg)")
public void usePointHasArg(int arg){}
@Before("usePoint()")
public void doSomethingBefore(){
System.out.println("doSomethingBefore");
}
@Around("usePoint()")
public void doSomethingAround(ProceedingJoinPoint joinPoint){
try {
System.out.println("doSomethingAround before");
joinPoint.proceed();
System.out.println("doSomethingAround after");
} catch (Throwable throwable) {
throwable.printStackTrace();
}
}
@Before("usePointHasArg(arg)")
public void doSomethingBeforeHasArg(int arg){
System.out.println("doSomethingBeforeHasArg,arg:" + arg);
}
}
主启动类
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
CutPointTest test = (CutPointTest) context.getBean(CutPointTest.class);
test.use();
test.useHasArg(3);
}
}
运行结果:
doSomethingAround before
doSomethingBefore
use()
doSomethingAround after
doSomethingBeforeHasArg,arg:3
useHasArg(),arg:3
需要在xml配置文件中声明
<aop:aspectj-autoproxy/>
上面的方式是为方法提供新功能,下面介绍一下为对象添加新方法(即AOP的引入功能):
定义一个接口
public interface IntroductionInterface {
public void newMethod();
}
实现类
public class IntroductionImpl implements IntroductionInterface {
public void newMethod() {
System.out.println("new Method...");
}
}
要怎么引入这个接口呢,我们不应该让现有的目标类去实现这个接口,我们的做法应该是新建一个切面,为了简单明了,我们不在新建的切面内做任何通知类型的定义,只使用与引入功能有关的@DeclareParents注解:
@Component
@Aspect
public class NewAspect {
@DeclareParents(value = "com.test.aop.CutPointTest",defaultImpl = IntroductionImpl.class)
public static IntroductionInterface introductionInterface;
}
上面表示将接口引入到CutPointTest类,并且声明该接口的实现类为IntroductionImpl
下面是具体调用:
public class MainApp {
public static void main(String[] args) {
ApplicationContext context =
new ClassPathXmlApplicationContext("applicationContext.xml");
CutPointTest test = (CutPointTest) context.getBean(CutPointTest.class);
IntroductionInterface introductionInterface = (IntroductionInterface)test;
introductionInterface.newMethod();
}
}
运行结果:
new Method...
这里我们注意到这行代码:
IntroductionInterface introductionInterface = (IntroductionInterface)test;
我们并没有修改CutPointTest类的代码去实现新的接口,但是他的实例却可以转型为新接口类型,而且如果一个类没有实现某个接口是不能转型的,运行时也会抛出ClassCastException,但是现在我们却可以正常运行,我们可以这样简单的理解:spring使用运行时的自动代理模式为我们透明的实现了一个总代理类,我们在访问切点方法的时候其实都是由代理类来处理的,在这里test其实就是总代理类,由于是运行时才确定具体类型,所以我们不要被代码本身迷惑。