现在的位置: 首页 > 综合 > 正文

Spring容器的AOP编程

2013年06月08日 ⁄ 综合 ⁄ 共 5339字 ⁄ 字号 评论关闭

什么是AOP(Aspect OrientedProgramming)

AOP是一种编程范式,提供从另一个角度来考虑程序结构以完善面向对象编程(OOP)。

AOP为开发者提供了一种描述横切关注点的机制,并能够自动将横切关注点织入到面向对象的软件系统中,从而实现了横切关注点的模块化。

AOP能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任,例如事务处理、日志管理、权限控制等,封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

AOP能干什么,也是AOP带来的好处
1:降低模块的耦合度
2:使系统容易扩展
3:更好的代码复用性

常用的开发模式,都是以竖行考虑开发的

但有些业务需要横向的需求


当项目要对每一步的操作进行一个日志的记录,权限的审查时,则是一个横向的关注点,如果在每一个方法中都写上一段几乎相同的代码,那就太冗余了,所以可以将日志记录,权限审查的模块提取出来

AOP中的概念

Aspect(切面):指横切性关注点的抽象即为切面,它与类相似,只是两者的关注点不一样,类是对物体特征的抽象,而切面是横切性关注点的抽象.

joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点,实际上joinpoint还可以是field或类构造器)

Pointcut(切入点):所谓切入点是指我们要对那些joinpoint进行拦截的定义.

Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知

Target(目标对象):代理的目标对象

Weave(织入):指将aspects应用到target对象并导致proxy对象创建的过程称为织入.

Introduction(引入):在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field

AOP中类及方法的注解

Aspect(切面):通过@Aspect来对拦截的自定义类进行注解,用于实现AOP.

Pointcut(切入点):切入点是在自定义类中进行声明的个私有的方法,使用@Pointcut进行注解,如: @Pointcut("execution(*biz.impl.PersonBizImpl.*(..))").

前置通知:使用: @Before(“切入点的方法名称() && args(参数名)”)进行注解

后置通知:使用: @AfterReturning(pointcut = “切入点的方法名称()”, returning = “参数名")进行注解,返回值为拦截方法的返回值

例外通知:使用: @AfterThrowing("切入点的方法名称()")进行注解

最终通知:使用: @After(“切入点的方法名称()”)进行注解

环绕通知:使用: @Around(“切入点的方法名称() ”)进行注解

权限拦截类

实现权限拦截的同时,记录操作日志

package interceptor;
//省略了导入的库包
import util.BeansUtil;  //获取bean对象的工具类
import util.UserUtil;  //初始化用户的工具类
import biz.SysLogBiz;   //日志业务操作类
import entity.SysLog;   //日志实体类
import entity.UserInfo;  //用户实体类
@Aspect
@Component
public class PowerInterceptor {
    // 第一个* 代表任意的返回类型 (..) 所有参数
    @Pointcut("execution(* action.*.*(..))")
    private void anyMethod() {
    }; //声明一个切入点,自定义一个私有的方法
    @Before("anyMethod() && args()")
    public void doBefore() {
        System.out.println("前置通知");
    }
 
    @AfterReturning(pointcut = "anyMethod()", returning = "name")
    public void doAfterReturning(String name) {
        System.out.println("后置通知:"+name);
    }
    @AfterThrowing("anyMethod()")
    public void doAfterThrowing() {
        System.out.println("例外通知");
    }
    @After("anyMethod()")
    public void doAfter() {
        System.out.println("最终通知");
    }
    @Around("anyMethod()")   //环绕通知
    public Object doAround(ProceedingJoinPoint pjp) throws Throwable { // 要执行pip.proceed方法
        Object obj = null;
  //初始化用户信息,如果在web项目中是通过session回话中获取当前登入的用户
        UserInfo user = UserUtil.getCurrentUser(); 
         //通过pjp对象可以获取到需要操作的类和类中的方法
        String className = pjp.getSignature().getDeclaringTypeName();
        System.out.println(className);  //打印操作的类,结果为action.DeptAction
         //将获取到的action.DeptAction,转化为deptAction
        className = className.substring(className.lastIndexOf(".") + 1);
        className = className.substring(0, 1).toLowerCase()
                + className.substring(1);
        if (UserUtil.checkPower(className, user.getPower())){
              //判断用户的权限
            try {
                System.out.println("有权限");
                obj = pjp.proceed();// 允许用户接着调用目标的方法
                SysLog log = new SysLog();
                log.setLogId(1);
                log.setLogMsg(BeansUtil.getLogMsg(className,pjp.getSignature()
                        .getName())
                        + ",操作成功!");
                log.setLogTime(new Date());
                // 获取日志操作 BEAN
                SysLogBiz logbiz = (SysLogBiz) BeansUtil
                        .getBean("sysLogBizImpl");
                logbiz.addLog(log);     //保存日志信息
                System.out.println(log.getLogMsg());
            } catch (Exception e) {
                SysLog log = new SysLog();
                log.setLogId(1);
                log.setLogMsg(BeansUtil.getLogMsg(className,pjp.getSignature()
                        .getName())
                        + ",操作失败!");
                log.setLogTime(new Date());
                // 获取日志操作 BEAN
                SysLogBiz logbiz = (SysLogBiz) BeansUtil
                        .getBean("sysLogBizImpl");
                logbiz.addLog(log);
                System.out.println(log.getLogMsg());
            }
        } else {
            System.out.println("你没有权限。");
        }
        return obj;
    }
}

测试类:

package test;
import util.BeansUtil;
import action.DeptAction;
public class PowerTest {
    /**
     * @param args
     */
    public static void main(String[] args) {
        // UserAction action =(UserAction) BeansUtil.getBean("userAction");
        // action.add();
        DeptAction action = (DeptAction) BeansUtil.getBean("deptAction");
        action.add();       //调用DeptAction的添加方法
    }
}

测试结果为:

结果中后置通知的:ok22是DeptAction中的返回方法

DeptActon中的add方法就返回了ok22操作

Spring提供了两种切面使用方式

Spring提供了两种切面使用方式,实际工作中我们可以选用其中一种:

基于XML配置方式进行AOP开发。

基于注解方式进行AOP开发。

上面介绍的是使用注解方式开发,在使用这种方式开发的时候还需要在spring配置文件中加配置

<aop:aspectj-autoproxyproxy-target-class="true"/>

基于XML配置方式使用切面

自定义类

public classLogPrint {
       public void doAccessCheck() {}定义前置通知
       public void doReturnCheck() {}定义后置通知
    public void doExceptionAction() {}定义例外通知
       public void doReleaseAction() {}定义最终通知
       public ObjectdoBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
              return pjp.proceed();环绕通知
       }
}

配置文件的配置

<bean id="orderservice"class="cn.itcast.service.OrderServiceBean"/>
<bean id="log"class="cn.itcast.service.LogPrint"/>
<aop:config>
  <aop:aspect id="myaop"ref="log">
    <aop:pointcut id="mycut" expression="execution(*cn.itcast.service..*.*(..))"/>
    <aop:before pointcut-ref="mycut" method="doAccessCheck"/>
    <aop:after-returning pointcut-ref="mycut" method="doReturnCheck"/>
    <aop:after-throwing pointcut-ref="mycut" method="doExceptionAction"/>
    <aop:after pointcut-ref="mycut" method=“doReleaseAction"/>
    <aop:around pointcut-ref="mycut" method="doBasicProfiling"/>
  </aop:aspect>
</aop:config>

指示符

expression-匹配方法执行的连接点,是Spring最主要的切入点

execution(modifiers-pattern?ret-type-pattern declaring-type-pattern? name-pattern(param-pattern)throws-pattern?)

除了喊会类型模式(),名字模式和参数模式以外,其余的都是可选的,*为任意通配符

expression=“execution(*cn.itcast.service..*.*(..))”

    表示作用在cn.itcast.service包及子包下所有的类的所有方法上

expression=“execution(*cn.itcast.service..*.*(java.lang.String,..))”

    表示作用在cn.itcast.service包及子包下所有的类的第一个参数为String类型的方法上

expression=“execution(java.lang.Stringcn.itcast.service..*.*(..))”

    表示作用在cn.itcast.service包及子包下所有的类的返回值类型为String的方法上

expression=“execution(!java.lang.Stringcn.itcast.service..*.*(..))”

    表示作用在cn.itcast.service包及子包下所有的类的返回值类型不是String的所有方法上

expression=“execution(* set**(..))”

    表示作用在任意以set开始的方法上

within-限定匹配特定类型的连接点

expression=“winthin(cn.itcast.*)”

  表示作用在cn.itcast包及子包下的所有连接点

 

抱歉!评论已关闭.