公司最近要求在某些程序出异常的时候,发封邮件至某个coder,用来及时检查问题所在,要求详细知道哪个类,哪个方法出的异常,出的什么异常等,而且不修改以前的程序代码!
于是想到了spring的AOP.想到了其中的ThrowsAdvice接口,这是用来再程序出了异常之后拦截,而后添加功能的.
本以为很简单,于是简单些了下测试类,[b]测试的业务类没有实现接口,就是一个java类[/b],本身应用了spring的申明事务.步骤如下:
1.先写一个pojo的advice通知:
2.spring配置文件,把通知应用到pointcut中:
之后运行,发现错了,原因在于我的测试业务类没有实现某一接口,因为事务管理用了AOP,我的advice又用了AOP,事务管理时我的业务类成了proxy,应用我的advice时,proxy无法类型转换至业务类,报错.后来又了解决方式,就是指定proxyTargetClass的值为true,强制使用cglib生成子类,代码如下:
但是这样做的后果就是拦截器重复拦截,因为2个AOP功能,2个子类,同时起作用,就拦截了2次.解决方案:把我的业务类换成接口,方法调用改成接口调用.
本以为可以了,但是发现advice的method,target参数无法得到,配置如下:
无法给通知传参,只能得到一个exception类型的参数,得不到method,target等参数,琢磨了2小时,没有解决,头昏脑胀!
休息一小时,继续琢磨,得到正确解决方式:
1.写一个依赖于spring的advice:
2.重新写配置文件:
解决问题!
************************************************************************************************
可是由于公司项目业务层是java类,又不能修改以前的代码,只能采用链式加AOP的方式去做了:
程序中最后调用"boLogin"这个spring Bean 就没有问题了.
Ok!解决!
于是想到了spring的AOP.想到了其中的ThrowsAdvice接口,这是用来再程序出了异常之后拦截,而后添加功能的.
本以为很简单,于是简单些了下测试类,[b]测试的业务类没有实现接口,就是一个java类[/b],本身应用了spring的申明事务.步骤如下:
1.先写一个pojo的advice通知:
public class Advice {
public void throwing(Exception ex){
System.out.println("pojo的afterThrowing...exception:"+ex.getMessage());
}
}
2.spring配置文件,把通知应用到pointcut中:
<bean id="adviceBean" class="com.zhangwei.struts.Advice"></bean>
<!-- 利用spring AOP的配置元素定义一个通知 -->
<aop:config >
<!-- 引用刚才定义的一个纯粹的POJO切面Bean -->
<aop:aspect ref="adviceBean">
<!-- 定义一个命名切点,必点切点定义的重复,expression用于配置匹配规则,(返回类型 返回类.方法(参数设置) -->
<aop:pointcut id="todo" expression="execution(* com.zhangwei.struts.Bo.*(..)) " />
<!-- 方法跑出异常后通知 -->
<aop:after-throwing pointcut-ref="todo" method="throwing" throwing="ex"/>
</aop:aspect>
</aop:config>
之后运行,发现错了,原因在于我的测试业务类没有实现某一接口,因为事务管理用了AOP,我的advice又用了AOP,事务管理时我的业务类成了proxy,应用我的advice时,proxy无法类型转换至业务类,报错.后来又了解决方式,就是指定proxyTargetClass的值为true,强制使用cglib生成子类,代码如下:
<aop:config proxy-target-class="true">
<!-- 引用刚才定义的一个纯粹的POJO切面Bean -->
<aop:aspect ref="adviceBean">
<!-- 定义一个命名切点,必点切点定义的重复,expression用于配置匹配规则,(返回类型 返回类.方法(参数设置) -->
<aop:pointcut id="todo" expression="execution(* com.zhangwei.struts.Bo.*(..)) " />
<!-- 方法前通知 -->
<aop:after-throwing pointcut-ref="todo" method="throwing" throwing="ex"/>
</aop:aspect>
</aop:config>
<bean id="txProxyTemplate" abstract="true"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="login*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
但是这样做的后果就是拦截器重复拦截,因为2个AOP功能,2个子类,同时起作用,就拦截了2次.解决方案:把我的业务类换成接口,方法调用改成接口调用.
本以为可以了,但是发现advice的method,target参数无法得到,配置如下:
<aop:after-throwing pointcut-ref="todo" method="throwing" throwing="ex"/>
无法给通知传参,只能得到一个exception类型的参数,得不到method,target等参数,琢磨了2小时,没有解决,头昏脑胀!
休息一小时,继续琢磨,得到正确解决方式:
1.写一个依赖于spring的advice:
public class Advisor implements ThrowsAdvice {
public void afterThrowing(Method method, Object[] args, Object target,
Exception ex) {
System.out.println("传统经典aop的afterThrowing2...");
System.out.println("methodName:"+method.getName());
System.out.println("traget:"+target.getClass().getInterfaces()[0].getName());
}
}
2.重新写配置文件:
<!-- 定义一个依赖于spring的通知 -->
<bean id="advice" class="com.zhangwei.struts.Advisor"></bean>
<aop:config >
<!-- 定义AOP的切入点 -->
<aop:pointcut id="todo" expression="execution(* com.zhangwei.struts.Bo.*(..)) " />
<!-- 定义建议者,需要通知以及切入点2个元素 -->
<aop:advisor advice-ref="advice" pointcut-ref="todo"/>
</aop:config>
解决问题!
************************************************************************************************
可是由于公司项目业务层是java类,又不能修改以前的代码,只能采用链式加AOP的方式去做了:
<bean id="login" class="com.zhangwei.struts.BoLogin">
<property name="daoLogin">
<ref bean="daoLogin" />
</property>
</bean>
<!-- 定义一个依赖于spring的通知 -->
<bean id="advice" class="com.zhangwei.struts.Advisor"></bean>
<bean id="memberAopAdvisor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor">
<property name="mappedName" value="*" />
<property name="advice" ref="advice" />
</bean>
<!-- 第一层代理 -->
<bean id="loginProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<!--启用CGLib,代理的不再是接口-->
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="target">
<ref bean="login"/>
</property>
<property name="interceptorNames">
<value>memberAopAdvisor</value>
</property>
</bean>
<!-- 第二层代理对第一层代理继续代理 -->
<bean id="boLogin"
class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<!--启用CGLib,代理的不再是接口-->
<property name="proxyTargetClass">
<value>true</value>
</property>
<property name="target">
<ref bean="loginProxy"/>
</property>
<property name="transactionManager" ref="transactionManager" />
<property name="transactionAttributes">
<props>
<prop key="save*">PROPAGATION_REQUIRED</prop>
<prop key="remove*">PROPAGATION_REQUIRED</prop>
<prop key="update*">PROPAGATION_REQUIRED</prop>
<prop key="login*">PROPAGATION_REQUIRED</prop>
<prop key="*">PROPAGATION_REQUIRED,readOnly</prop>
</props>
</property>
</bean>
程序中最后调用"boLogin"这个spring Bean 就没有问题了.
Ok!解决!