Spring AOP 代理机制 JDK&CGLIB
Spring AOP使用JDK动态代理或者CGLIB来为目标对象创建代理。(建议优先使用JDK的动态代理)
如果被代理的目标对象实现了至少一个接口,则会使用JDK动态代理。所有该目标类型实现的接口都将被代理。 若该目标对象没有实现任何接口,则创建一个CGLIB代理。
如果你希望强制使用CGLIB代理,(例如:希望代理目标对象的所有方法,而不只是实现自接口的方法) 那也可以。但是需要考虑以下问题:?
无法通知(advise)final方法,因为他们不能被覆写。
强制使用CGLIB代理需要将<aop:config>的proxy-target-class属性设为true:
1
<
aop:config
?proxy-target-class
=
"true"
>
2
???
...
3
</
aop:config
>
当使用@AspectJ自动代理时要强制使用CGLIB,请将<aop:aspectj-autoproxy>的proxy-target-class属性设置为true:
<aop:aspectj-autoproxy proxy-target-style="line-height: 1.1em !important; margin: 1em 0px; width: 716px; font-family: Consolas,'Bitstream Vera Sans Mono','Courier New',Courier,monospace !important; float: none !important; height: auto !important; color: #333333; font-size: 10pt !important; vertical-align: baseline !important; border: 0px; padding: 1px !important;">01
public
?class
?SimplePojo?
implements
?Pojo {
02
????
public
?void
?foo() {
03
???????
// this next method invocation is a direct call on the 'this' reference
04
???????
this
.bar();
05
????
}
06
????????????????
?07
????
public
?void
?bar() {
08
????????
// some logic...
09
????
}
10
}
?
当你调用一个对象引用的方法时,此对象引用上的方法直接被调用,如下所示?
?
1
public
?static
?void
?main(String[] args) {
2
???
Pojo pojo =?
new
?SimplePojo();
3
???
// this is a direct method call on the 'pojo' reference
4
???
pojo.foo();
5
}
当客户代码所持有的引用是一个代理的时候则略有不同了。请考虑如下图示和代码段片断?
?
?
1
ProxyFactory proxyFactory =?
new
?ProxyFactory(
new
?SimplePojo());
2
proxyFactory.addInterface(Pojo.
class
);
3
// 添加前置通知
4
proxyFactory.addAdvice(
new
?BeforeAdviceImpl());
5
?6
Pojo pojo = (Pojo) proxyFactory.getProxy();
7
pojo.foo();
理解此处的关键方法中的客户代码?拥有一个代理的引用。这意味着对这个对象引用中方法的调用就是对代理的调用, 而这个代理能够代理所有跟特定方法调用相关的拦截器。不过,一旦调用最终抵达了目标对象?(此处为SimplePojo类的引用),任何对自身的调用例如this.bar()或者this.foo()将对this引用进行调用而非代理。这一点意义重大, 它意味着自我调用将不会导致和方法调用关联的通知得到执行的机会。
那好,为此要怎么办呢?最好的办法就是重构你的代码使自我调用不会出现。 当然,这的确需要你做一些工作,但却是最好的,最少侵入性的方法。另一个方法则很可怕, 也正因为如此我几乎不愿指出这种方法。你可以象如下这样完全把业务逻辑写在你的Spring AOP类中:?
01
public
?class
?SimplePojo?
implements
?Pojo {
02
???
public
?void
?foo() {
03
??????
// this works, but... gah!
04
??????
((Pojo) AopContext.currentProxy()).bar();
05
???
}
06
????????????????
?07
???
public
?void
?bar() {
08
???????
// some logic...
09
???
}
10
}
这样完全将你的代码交给了Spring AOP,?并且让类本身知道它正被用于一个AOP的上下文中, 而它其中的文件直接面对AOP。当代理在被创建时也需要一些额外的配置:
?
?
01
public
?static
?void
?main(String[] args) {
02
????
ProxyFactory factory =?
new
?ProxyFactory(
new
?SimplePojo());
03
????
factory.adddInterface(Pojo.
class
);
04
????
factory.addAdvice(
new
?RetryAdvice());
05
????
factory.setExposeProxy(
true
);
06
????????????????
?07
????
Pojo pojo = (Pojo) factory.getProxy();
08
????????????????
?09
????
// this is a method call on the proxy!
10
????
pojo.foo();
11
}
上面的例子中用到了Spring AOP中ProxyFactory这些特定的API。在使用Spring容器配置的环境下也同样有此问题,同样以之前的支付为例:
?
?
01
public
?interface
?IPayService {
02
????
String pay(
long
?userId,?
long
?money);
03
????
String inner();
04
}
05
// 实现类,在pay方法中调用了inner()方法
06
@Service
07
public
?class
?RMBPayService?
implements
?IPayService {
08
????
private
?static
?final
?Logger LOGGER = LoggerFactory.getLogger(RMBPayService.
class
);
09
?10
????
@Override
11
????
public
?String pay(
long
?userId,?
long
?money) {
12
????????
LOGGER.info(
"用户:"
?+ userId +?
"使用人民币支付金额:"
?+ money);
13
????????
inner();
14
????????
return
?"ok"
;
15
????
}
16
?17
????
@Override
18
????
public
?String inner() {
19
????????
LOGGER.info(
"inner method,can you see the aop advice...."
);
20
????????
return
?"go"
;
21
????
}
22
}
这样从容器中获取RMBPayService实例对象时,调用pay()方法,则只能在pay()方法环境下看到aop的特性,而在inner()中则看不到,可以使用如下方法来解决(虽然不建议):
?
?
view source?print?01
@Service
02
public
?class
?RMBPayService?
implements
?IPayService {
03
????
private
?static
?final
?Logger LOGGER = LoggerFactory.getLogger(RMBPayService.
class
);
04
?05
????
@Override
06
????
public
?String pay(
long
?userId,?
long
?money) {
07
????????
LOGGER.info(
"用户:"
?+ userId +?
"使用人民币支付金额:"
?+ money);
08
?09
????????
ApplicationContext ctx =?
new
?ClassPathXmlApplicationContext(
"applicationContext.xml"
);
10
?11
????????
IPayService service = ctx.getBean(RMBPayService.
class
);
12
????????
service.inner();
13
????????
return
?"ok"
;
14
????
}
15
?16
????
@Override
17
????
public
?String inner() {
18
?19
????????
LOGGER.info(
"inner method,can you see the aop advice...."
);
20
????????
return
?"go"
;
21
????
}
22
}