spring之AOP源码深入理解(一)aop拦截

(一)  源码角度

拦截机 (Interceptor), 是 AOP (Aspect-Oriented Programming) 的另一种叫法。AOP本身是一门语言,只不过我们使用的是基于JAVA的集成到Spring 中的 SpringAOP。同样,我们将通过我们的例子来理解陌生的概念。

1、接口类

package com.test.TestSpring3;  
  
public interface UserService // 被拦截的接口  
...{  
    public void printUser(String user);  
}  

 2、实现类

package com.test.TestSpring3;  
​
public class UserServiceImp implements UserService // 实现UserService接口  
...{  
    public void printUser(String user) ...{  
        System.out.println("printUser user:" + user);// 显示user  
    }  
}  

3、 AOP拦截器

package com.test.TestSpring3;  
  
import org.aopalliance.intercept.MethodInterceptor;  
import org.aopalliance.intercept.MethodInvocation;  
  
public class UserInterceptor implements MethodInterceptor  
// AOP方法拦截器  
...{  
  
    public Object invoke(MethodInvocation arg0) throws Throwable ...{  
  
        try ...{  
  
            if (arg0.getMethod().getName().equals("printUser"))  
            // 拦截方法是否是UserService接口的printUser方法  
            ...{  
                Object[] args = arg0.getArguments();// 被拦截的参数  
                System.out.println("user:" + args[0]);  
                arg0.getArguments()[0] = "hello!";// 修改被拦截的参数  
  
            }  
  
            System.out.println(arg0.getMethod().getName() + "---!");  
            return arg0.proceed();// 运行UserService接口的printUser方法  
  
        } catch (Exception e) ...{  
            throw e;  
        }  
    }  
}  

(二)spring aop Aspect拦截器注解开发

    Spring AOP 与ApectJ 的目的一致,都是为了统一处理横切业务,但与AspectJ不同的是,Spring AOP 并不尝试提供完整的AOP功能(即使它完全可以实现),Spring AOP 更注重的是与Spring IOC容器的结合,并结合该优势来解决横切业务的问题,因此在AOP的功能完善方面,相对来说AspectJ具有更大的优势,同时,Spring注意到AspectJ在AOP的实现方式上依赖于特殊编译器(ajc编译器),因此Spring很机智回避了这点,转向采用动态代理技术的实现原理来构建Spring AOP的内部机制(动态织入),这是与AspectJ(静态织入)最根本的区别。在AspectJ 1.5后,引入@Aspect形式的注解风格的开发,Spring也非常快地跟进了这种方式,因此Spring 2.0后便使用了与AspectJ一样的注解。请注意,Spring 只是使用了与 AspectJ 5 一样的注解,但仍然没有使用 AspectJ 的编译器,底层依是动态代理技术的实现,因此并不依赖于 AspectJ 的编译器。下面我们先通过一个简单的案例来演示Spring AOP的入门程序。

1、定义目标类接口和实现类:

 //接口类
public interface UserDao {

    int addUser();

    void updateUser();

    void deleteUser();

    void findUser();
}
//实现类
import com.zejian.spring.springAop.dao.UserDao;
import org.springframework.stereotype.Repository;

@Repository
public class UserDaoImp implements UserDao {
    @Override
    public int addUser() {
        System.out.println("add user ......");
        return 6666;
    }

    @Override
    public void updateUser() {
        System.out.println("update user ......");
    }

    @Override
    public void deleteUser() {
        System.out.println("delete user ......");
    }

    @Override
    public void findUser() {
        System.out.println("find user ......");
    }
}

 2、使用Spring 2.0引入的注解方式,编写Spring AOP的aspect 类:

@Aspect
public class MyAspect {

    /**
     * 前置通知
     */
    @Before("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void before(){
        System.out.println("前置通知....");
    }

    /**
     * 后置通知
     * returnVal,切点方法执行后的返回值
     */
    @AfterReturning(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",returning = "returnVal")
    public void AfterReturning(Object returnVal){
        System.out.println("后置通知...."+returnVal);
    }


    /**
     * 环绕通知
     * @param joinPoint 可用于执行切点的类
     * @return
     * @throws Throwable
     */
    @Around("execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("环绕通知前....");
        Object obj= (Object) joinPoint.proceed();
        System.out.println("环绕通知后....");
        return obj;
    }

    /**
     * 抛出通知
     * @param e
     */
    @AfterThrowing(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))",throwing = "e")
    public void afterThrowable(Throwable e){
        System.out.println("出现异常:msg="+e.getMessage());
    }

    /**
     * 无论什么情况下都会执行的方法
     */
    @After(value="execution(* com.zejian.spring.springAop.dao.UserDao.addUser(..))")
    public void after(){
        System.out.println("最终通知....");
    }
}

3、编写配置文件交由Spring IOC容器管理:

<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"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

    <!-- 启动@aspectj的自动代理支持-->
    <aop:aspectj-autoproxy />

    <!-- 定义目标对象 -->
    <bean id="userDaos" class="com.zejian.spring.springAop.dao.daoimp.UserDaoImp" />
    <!-- 定义aspect类 -->
    <bean name="myAspectJ" class="com.zejian.spring.springAop.AspectJ.MyAspect"/>
</beans>

简单说明一下,定义了一个目标类UserDaoImpl,利用Spring2.0引入的aspect注解开发功能定义aspect类即MyAspect,在该aspect类中,编写了5种注解类型的通知函数,分别是前置通知@Before、后置通知@AfterReturning、环绕通知@Around、异常通知@AfterThrowing、最终通知@After,这5种通知与前面分析AspectJ的通知类型几乎是一样的,并注解通知上使用execution关键字定义的切点表达式,即指明该通知要应用的目标函数,当只有一个execution参数时,value属性可以省略,当含两个以上的参数,value必须注明,如存在返回值时。当然除了把切点表达式直接传递给通知注解类型外,还可以使用@pointcut来定义切点匹配表达式,这个与AspectJ使用关键字pointcut是一样的,后面分析。目标类和aspect类定义完成后,最后需要在xml配置文件中进行配置,同样的所有类的创建都交由SpringIOC容器处理,注意,使用Spring AOP 的aspectJ功能时,需要使用以下代码启动aspect的注解功能支持:

<aop:aspectj-autoproxy />

猜你喜欢

转载自blog.csdn.net/u014252478/article/details/84798700