AOP作用:把分布在程序各处的’交叉事务”与业务逻辑分离开来,形成可重用的模块,减少重复代码,并让类着重于其主要的功能上。
下面这个例子模拟了多店铺商城各店铺在后台登录后,记录各个功能操作信息的日志。
环境:
struts2+spring2.5 , tomcat6
1.RedirectAction 类主要代码,
public String direct() throws Exception{
funcid = request.getParameter(”funcid”);
request.getSession().setAttribute(”funcid”, funcid);
shopid = (String)request.getSession().getAttribute(”shopid”);
memo = logService.getFuncInfo(shopid, funcid);
request.getSession().setAttribute(”operatormemo”, memo);
// TODO something
System.out.println(”…….execute end…”);
return “direct”;
}
以direct()方法为切入点(用spring的AOP配置元素配置),在执行完 direct()方法后记录日志,但是除了放到session里一些信息的代码外,没有任何与记录日志有关的代码
2. Service类主代码
public void saveLog(LogBean log) throws Exception {
System.out.println(”log : user:” + log.getUserid() + “,shop:”+ log.getShopid()
+”,ip:”+log.getIp()+”,funcid:”+log.getFunid()+”,memo:”+log.getOperatorMemo()
+”,time:”+log.getLogdate());}
模拟保存日志,在控制台打印日志信息
3.AspectAdvice通知类,它只是个普通类,并没有任何接口和注解,通过spring的AOP配置元素把它转化为切面
public class AspectAdvice {
……………
public void after() throws Exception{
HttpServletRequest request = ServletActionContext.getRequest();
String userid = (String)request.getSession().getAttribute(”userid”);
String shopid = (String)request.getSession().getAttribute(”shopid”);
String operatormemo = (String)request.getSession().getAttribute(”operatormemo”);
String funcid = (String)request.getSession().getAttribute(”funcid”);
String ip = request.getRemoteAddr();Timestamp time = new Timestamp(System.currentTimeMillis());
LogBean log = new LogBean();
log.setUserid(userid);
log.setShopid(shopid);
log.setFunid(funcid);
log.setIp(ip);
log.setOperatorMemo(operatormemo);
log.setLogdate(time);logService.saveLog(log);
}
在advice类里声明了用于执行后通知的 after 方法
4.struts.xml
<struts>
<include file=”struts-default.xml”/>
<package name=”default” extends=”struts-default” namespace=”/”>
<action name=”*x” class=”index” method=”{1}”>
<result name=”logged”>index.jsp</result>
<result name=”direct” type=”redirect”>destx.action?funcid=${funcid}</result>
<result name=”done”>done.jsp</result>
</action>
</package>
</struts>
5.spring aplicationContext.xml
<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-2.5.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd“>
<bean id=”logService” class=”com.aspect.services.impl.LogServiceImpl”></bean>
<bean id=”index” class=”com.aspect.services.actions.RedirectAction”>
<property name=”logService” ref=”logService”></property>
</bean>
<!– 声明通知类,其实就是一个普通类的声明 –>
<bean id=”aspect” class=”com.aspect.services.aop.AspectAdvice”>
<property name=”logService” ref=”logService”></property>
</bean><aop:config proxy-target-class=”true”> <!–强制使用cglib –>
<aop:pointcut id=”pointcutx” expression=”execution(* *.direct(..))“/> <!– 定义切点,把通知应用到所有的 direct方法 –>
<aop:aspect ref=”aspect“>
<aop:after pointcut-ref=”pointcutx” method=”after“/> <!– 定义后通知引用切点 –>
</aop:aspect>
</aop:config></beans>
如果要使用aop配置元素,要在SPRING配置文件里加入aop命名空间(上面加下划线的红色部分),属性proxy-target-class=”true”,是强制使用cglib代理,因为这里的切入点方法direct()所在的类继承了ActionSupport类,如果不强制使用cglib代理,会抛出找不到代理方法的错误。
6.主要页面index.jsp
启动tomcat,输入 http://localhost:88/AspectTestWeb/loginx.action 进入index.jsp
点击各个链接可以在控制台看到输出结果:
附:cglib代理和jdk动态代理
1 如果目标对象实现了接口,默认情况下会使用jdk的动态代理实现AOP
2 如果目标对象实现了接口、可以强制使用cglib实现aop
加入 cglib-nodep.jar
加入 <aop:aspectj-autoproxy proxy-target-class=”true”/>强制使用cglib
3 如果目标对象没有实现了接口,必须采用cglib库,spring会在jdk动态代理和cglib之间转换
jdk动态代理和cglib动态代理区别
* jdk动态代理只能对实现了接口的类生成代理,而不能针对没有实现接口的类代理
* cglib是针对类实现代理、主要是对指定的类生成一个子类,覆盖了其中的方法
因为是继承。所以该类或方法最好不要声明成final,导致无法继承父类生成代理类