spring 提供了自动代理机制,可以让容器自动生成代理,从而把开发人员从繁琐的配置中解脱出来 。 具体是使用 beanpostprocessor 来实现这项功能。
1 beanpostprocessor
beanpostprocessor 代理创建器的实现类可以分为 3 类:
类型 | 实现类 |
---|---|
基于 bean 配置名规则 | beannameautoproxycreator |
基于 advisor 匹配规则 | defaultadvisorautoproxycreator |
基于 bean 中的 aspectj 注解标签的匹配规则 | annotationawareaspectjautoproxycreator |
beanpostprocessor 类继承关系
所有的自动代理器类都实现了 beanpostporcessor ,在容器实例化 bean 时, beanpostprocessor 将对它进行加工处理,所以自动代理创建器能够对满足匹配规则的 bean 自动创建代理对象。
2 beannameautoproxycreator
假设有以下两个实体类(用户与充电宝)。
用户类:
1
2
3
4
5
6
7
8
9
10
11
|
public class user {
public void rent(string userid) {
system.out.println( "user:租赁【充电宝】" );
}
public void back(string userid){
system.out.println( "user:归还【充电宝】" );
}
}
|
充电宝:
1
2
3
4
5
6
|
public class charger {
public void rent(string userid) {
system.out.println( "charger:【充电宝】被租赁" );
}
}
|
我们希望通过 beannameautoproxycreator 通过 bean 的名称来自动创建代理,实现增强:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
<?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:p= "http://www.springframework.org/schema/p"
xsi:schemalocation= "http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd" >
<bean id= "user" class = "net.deniro.spring4.aop.user" />
<bean id= "charger" class = "net.deniro.spring4.aop.charger" />
<!-- 前置增强-->
<bean id= "rentbeforeadvice" class = "net.deniro.spring4.aop.rentbeforeadvice" />
<!-- 使用 beannameautoproxycreator-->
<bean
class = "org.springframework.aop.framework.autoproxy.beannameautoproxycreator"
p:beannames= "*er"
p:interceptornames= "rentbeforeadvice"
p:optimize= "true"
></bean>
</beans>
|
beannameautoproxycreator的 beannames 属性允许指定一组需要自动代理的 bean 名称, 这里可以使用 *
通配符 。
因为我们需要代理的类名分别是 user 与 charger,都是以 er 结尾的,所以我们这里定义为 *er
。
也可以通过 beannames 的 value 值来明确指定需要代理的 bean 名称,多个以逗号分隔(更常用)。
1
2
3
|
<!-- 指定自动代理的 bean 名称-->
<property name= "beannames" value= "user,charger" >
</property>
|
也可以通过 list 方式来指定 beannames 的值:
1
2
3
4
5
6
|
<property name= "beannames" >
<list>
<value>user</value>
<value>charger</value>
</list>
</property>
|
p:optimize 设置为 true,则表示使用 cglib 动态代理技术。
通过这样的配置之后,容器在创建 user 和 charger bean 的实例时,就会自动为它们创建代理对象,而这一操作对于使用者来说完全是透明的 。
单元测试:
1
2
3
4
5
6
|
user user = (user) context.getbean( "user" );
charger charger = (charger) context.getbean( "charger" );
string userid = "001" ;
user.rent(userid);
charger.rent(userid);
|
输出结果:
准备租赁的用户 id:001
user:租赁【充电宝】
准备租赁的用户 id:001
charger:【充电宝】被租赁
3 defaultadvisorautoproxycreator
切面 advisor 是切点和增强的复合体,而 defaultadvisorautoproxycreator 能够扫描 advisor, 并将 advisor 自动织入到匹配的目标 bean 中。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
|
<bean id= "user" class = "net.deniro.spring4.aop.user" />
<bean id= "charger" class = "net.deniro.spring4.aop.charger" />
<!-- 前置增强-->
<bean id= "rentbeforeadvice" class = "net.deniro.spring4.aop.rentbeforeadvice" />
<!-- 静态正则表达式方法名匹配-->
<bean id= "regexpadvisor"
class = "org.springframework.aop.support.regexpmethodpointcutadvisor"
p:advice-ref= "rentbeforeadvice" >
<!-- 匹配模式-->
<property name= "patterns" >
<list>
<!-- 匹配字符串-->
<value>.*rent.*</value>
</list>
</property>
</bean>
<!-- 使用 defaultadvisorautoproxycreator-->
<bean
class = "org.springframework.aop.framework.autoproxy.defaultadvisorautoproxycreator" />
|
首先我们配置了以静态正则表达式方法名匹配的切面,然后直接配置了 defaultadvisorautoproxycreator bean。
测试代码与输出结果与上一小节的 beannameautoproxycreator 相同。
jdk 动态代理是通过接口来实现方法拦截,所以必须确保要拦截的目标在接口中有定义。
cglib 动态代理是通过动态生成代理子类来实现方法拦截,所以必须确保要拦截的目标方法可以被子类所访问,也就是目标方法必须定义为非 final, 且非私有实例方法 。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持服务器之家。
原文链接:https://www.jianshu.com/p/816078a79001