引介/引入切面有两个实现类:
- DefaultIntroductionAdvisor:常用的实现类
- DeclareParentsAdvisor:用于实现AspectJ语言的DeclareParent注解表示的引介/引入切面
实际上,我们使用AOP往往是Spring内部使用BeanPostProcessor帮我们创建代理。
这些代理的创建器可以分成三类:
- 基于Bean配置名规则的自动代理创建器:BeanNameAutoProxyCreator
- 基于Advisor匹配机制的自动代理创建器:它会对容器所有的Advisor进行扫描,实现类为DefaultAdvisorAutoProxyCreator
- 基于Bean中的AspectJ注解标签的自动代理创建器:AnnotationAwareAspectJAutoProxyCreator
对应的类继承图:
嗯,基于代理的经典SpringAOP就讲到这里吧,其实我是不太愿意去写这个的,因为已经几乎不用了,在《Spring 实战 第4版》也没有这部分的知识点了。
- 但是通过这部分的知识点可以更加全面地认识Spring AOP的各种接口吧~
三、拥抱基于注解和命名空的AOP编程
Spring在新版本中对AOP功能进行了增强,体现在这么几个方面:
- 在XML配置文件中为AOP提供了aop命名空间
- 增加了AspectJ切点表达式语言的支持
- 可以无缝地集成AspectJ
那我们使用@AspectJ
来玩AOP的话,学什么??其实也就是上面的内容,学如何设置切点、创建切面、增强的内容是什么…
具体的切点表达式使用还是前往:Spring【AOP模块】就这么简单看吧~~
对应的增强注解:
3.1使用引介/引入功能实现为Bean引入新方法
其实前置啊、后置啊这些很容易就理解了,整篇文章看下来就只有这个引介/引入切面有点搞头。于是我们就来玩玩吧~
我们来看一下具体的用法吧,现在我有个服务员的接口:
public interface Waiter { // 向客人打招呼 void greetTo(String clientName); // 服务 void serveTo(String clientName); }
一位年轻服务员实现类:
public class NaiveWaiter implements Waiter { public void greetTo(String clientName) { System.out.println("NaiveWaiter:greet to " + clientName + "..."); } @NeedTest public void serveTo(String clientName) { System.out.println("NaiveWaiter:serving " + clientName + "..."); } }
现在我想做的就是:想这个服务员可以充当售货员的角色,可以卖东西!当然了,我肯定不会加一个卖东西的方法到Waiter接口上啦,因为这个是暂时的~
所以,我搞了一个售货员接口:
public interface Seller { // 卖东西 int sell(String goods, String clientName); }
一个售货员实现类:
public class SmartSeller implements Seller { // 卖东西 public int sell(String goods,String clientName) { System.out.println("SmartSeller: sell "+goods +" to "+clientName+"..."); return 100; } }
此时,我们的类图是这样子的:
现在我想干的就是:借助AOP的引入/引介切面,来让我们的服务员也可以卖东西!
我们的引入/引介切面具体是这样干的:
@Aspect public class EnableSellerAspect { @DeclareParents(value = "com.smart.NaiveWaiter", // 指定服务员具体的实现 defaultImpl = SmartSeller.class) // 售货员具体的实现 public Seller seller; // 要实现的目标接口 }
写了这个切面类会发生什么??
- 切面技术将SmartSeller融合到NaiveWaiter中,这样NaiveWaiter就实现了Seller接口!!!!
是不是很神奇??我也觉得很神奇啊,我们来测试一下:
我们的bean.xml
文件很简单:
<?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-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd"> <aop:aspectj-autoproxy/> <bean id="waiter" class="com.smart.NaiveWaiter"/> <bean class="com.smart.aspectj.basic.EnableSellerAspect"/> </beans>
测试一下:
public class Test { public static void main(String[] args) { ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("com/smart/aspectj/basic/beans.xml"); Waiter waiter = (Waiter) ctx.getBean("waiter"); // 调用服务员原有的方法 waiter.greetTo("Java3y"); waiter.serveTo("Java3y"); // 通过引介/引入切面已经将waiter服务员实现了Seller接口,所以可以强制转换 Seller seller = (Seller) waiter; seller.sell("水军", "Java3y"); } }
具体的调用过程是这样子的:
当引入接口方法被调用时,代理对象会把此调用委托给实现了新接口的某个其他对象。实际上,一个Bean的实现被拆分到多个类中
3.2在XML中声明切面
我们知道注解很方便,但是,要想使用注解的方式使用Spring AOP就必须要有源码(因为我们要在切面类上添加注解)。如果没有源码的话,我们就得使用XML来声明切面了~
其实就跟注解差不多的功能:
我们就直接来个例子终结掉它吧:
首先我们来测试一下与传统的SpringAOP结合的advisor是怎么用的:
实现类:
xml配置文件:
…….
一个一个来讲解还是太花时间了,我就一次性用图的方式来讲啦:
最后还有一个切面类型总结图,看完就几乎懂啦:
三、总结
看起来AOP有很多很多的知识点,其实我们只要记住AOP的核心概念就行啦。
下面是我的简要总结AOP:
- AOP的底层实际上是动态代理,动态代理分成了JDK动态代理和CGLib动态代理。如果被代理对象没有接口,那么就使用的是CGLIB代理(也可以直接配置使用CBLib代理)
- 如果是单例的话,那我们最好使用CGLib代理,因为CGLib代理对象运行速度要比JDK的代理对象要快
- AOP既然是基于动态代理的,那么它只能对方法进行拦截,它的层面上是方法级别的
- 无论经典的方式、注解方式还是XML配置方式使用Spring AOP的原理都是一样的,只不过形式变了而已。一般我们使用注解的方式使用AOP就好了。
- 注解的方式使用Spring AOP就了解几个切点表达式,几个增强/通知的注解就完事了,是不是贼简单…使用XML的方式和注解其实没有很大的区别,很快就可以上手啦。
- 引介/引入切面也算是一个比较亮的地方,可以用代理的方式为某个对象实现接口,从而能够使用借口下的方法。这种方式是非侵入式的~
- 要增强的方法还可以接收与被代理方法一样的参数、绑定被代理方法的返回值这些功能…