基于代理类 ProxyFactoryBean 的AOP实现
ProxyFactoryBean 是 FactoryBean 接口的实现类,FactoryBean 负责实例化一个 Bean,ProxyFactoryBean 则负责为其他 Bean 创建代理实例。使用 Spring 提供的 org.springframework.aop.framework.ProxyFactoryBean 是创建 AOP 的最基本方式,ProxyFactoryBean 的常用属性如下:
- target: 代理的目标对象
- proxyInterfaces: 代理所要实现的接口,可以是多个接口
- interceptorNames: 需要织入目标对象的 Bean 的列表
- singleton: 返回的代理是否是单实例,默认为单实例
如果需要给添加用户和删除用户等业务逻辑方法添加业务日志功能,要求在业务方法调用时进行日志记录,日志内容包括被调用的类名、方法名等,并在控制台输出。以此为例,下面是基于代理类 ProxyFactoryBean 的AOP实现流程。
编写DAO层
在数据访问层的 UserDAO 接口中声明用于添加和删除用户的两个方法并实现:1
2
3
4
5public interface UserDAO
{
void addUser(User user);
void deleteUser(Integer id);
}
1 | public class UserDAOImpl implements UserDAO |
编写业务逻辑层
1 | public interface UserBiz |
1 | public class UserBizImpl implements UserBiz |
编写方面代码
实现特定功能的方面代码在 AOP 概念中称为 “通知(advice)”。通知根据其织入到业务代码时执行的时间进行划分,分为前置通知、后置通知、环绕通知和异常通知。
首先编写实现前置通知的 LogAdvice 类:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class LogAdvice implements MethodBeforeAdvice
{
//获取日志记录器Logger
private Logger logger=Logger.getLogger(LogAdvice.class);
@Override
public void before(Method method,Object[] args,Object target) throws Throwable
{
//获取被调用的类名
String targetClassName=target.getClass().getName();
//获取被调用的方法名
String targetMethodName=method.getName();
//日志格式字符串
String logInfoText="前置通知:"+ targetClassName + "类的" + targetMethodName + "方法开始执行";
//将日志信息写入配置的文件中
logger.info(logInfoText);
前置通知类要实现 MethodBeforeAdvice 接口,实现 before 方法;
后置通知类要实现 AfterReturningAdvice 接口,实现 afterReturning 方法;
异常通知类要实现 ThrowsAdvice 接口,实现 afterThrowing 方法;
环绕通知类要实现 MethodInterceptor 接口,实现 invoke 方法;
将”业务逻辑代码”和”方面代码”组装进代理类
Spring 采用代理的方式将通知织入到原先的 Bean 中。即 Spring 将原来的 Bean 和通知都封装到 org.springframework.aop.framework.ProxyFactoryBean 类中。该工作可在配置文件 applicationContext.xml 中完成。1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31<!-- 配置创建 UserBizImpl 的实例 -->
<bean id="userBiz" class="com.shw.biz.UserBizImpl">
<!-- 依赖注入数据访问层组件 -->
<property name="userDAO" ref="userDAO"/>
</ben>
<!-- 定义前置通知 -->
<bean id="logAdvice" class="com.shw.aop.LogAdvice"/>
<!-- 使用 Spring 代理工厂定义一个代理,名称为 ub,通过 ub 访问业务类中的方法 -->
<bean id="ub" class="org.springframework.aop.framework.ProxyFactoryBean">
<!-- 指定代理接口 -->
<property name="proxyInterfaces">
<value>com.shw.biz.UserBiz</value>
</proerty>
<!-- 指定通知 -->
<property name="interceptorNames">
<list>
<!-- 织入前置通知 -->
<value>logAdvice</value>
<!-- 织入后置通知 -->
<value>afterAdvice</value>
<!-- 织入环绕通知 -->
<value>logAroundAdvice</value>
<!-- 织入异常通知 -->
<value>throwsLogAdvice</value>
</list>
</property>
<!-- 指定目标对象 -->
<property name="target" ref="userBiz"/>
</bean>