7.Spring AOP动态代理

本文介绍了Spring AOP中的核心概念,包括切面(Aspect)、连接点(Joinpoint)、切入点(Pointcut)、通知(Advice)等,并通过示例展示了AOP的实现,包括有接口和无接口的目标对象的代理。同时,文章提到了SpringAOP的动态代理机制,即基于JDK和CGlib的代理方式。最后,讨论了AspectJ的切入点语法定义细节。
摘要由CSDN通过智能技术生成



一.AOP(面向切面编程)中的概念


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

     类是对物体特征的抽象,而切面横切性关注点的抽象.


joinpoint(连接点):所谓连接点是指那些被拦截到的点。在spring中,这些点指的是方

                 法,因为spring只支持方法类型的连接点,实际上joinpoint还可

                 以field或类构造器)


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


Advice(通知):所谓通知是指拦截到joinpoint之后所要做的事情就是通知.通知分为前置

             通知,后置通知,异常通知,最终通知,环绕通知


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


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


Introduction(引入):在不修改类代码的前提下,Introduction可以在运行期为类动态地

  添加一些方法或Field.






二.实现AOP


<?xml version="1.0" encoding="UTF-8"?>
<beans
        xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xmlns:context="http://www.springframework.org/schema/context"
        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/context
           http://www.springframework.org/schema/context/spring-context-2.5.xsd
           http://www.springframework.org/schema/aop
           http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">


    <!-- 配置AOP 注解方式 -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>

    <context:component-scan base-package="com.zyy.service"></context:component-scan>
    <context:component-scan base-package="com.zyy.aop"></context:component-scan>


</beans>



代理对象:

package com.zyy.service;

/**
 * Created by CaMnter on 2014/8/20.
 */
public interface PersonService_5 {

    String user = null;

    public void save(String name);

    public void update(Integer personId, String name);

    public void delete(Integer personId);

    public String getName();

    public String getUser();

}



SpringAOP设计:


package com.zyy.aop;

import com.zyy.service.PersonService_5;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Service;

/**
 * Created by CaMnter on 2014/8/21.
 */

/**
 * 切面  使用注解
 * <p/>
 * 必须交给Spring容器管理
 */

/*
 *    @Aspect (切面) 声明切面
 *
 *    @Pointcut 声明切入点
 *
 *    @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")
 *    当执行com.zyy.service.impl.PersonServiceBean_5类下所有方法的时候就会被拦截到
 *
 *    @Before("anyMethod()") 前置通知 这里表示在执行anyMethod方法前先执行doBefore
 *
 *    @AfterReturning("anyMethod()")  后置通知 这里表示在执行anyMethod方法后先执行doAfterReturning
 *
 *    @After("anyMethod()")  最后通知 无论怎样 最后都执行的
 *
 *     @AfterThrowing("anyMethod()") 出现异常的时候的通知
 *
 *     @Around("anyMethod()") 环绕通知 (应用比较多) 适合做权限
 *     注意:如果使用了环绕通知 就一定要执行 pjp.proceed(); 否则业务Bean中拦截的 Before...等方法不会执行
 *
 *
 *     @Before("anyMethod() && args(name)") 表示 在切入点
 *     @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
 *     还要求改切入点类的方法只有有一个是String类型 返回给doBefore 作为参数
 *
 *
 *     @AfterReturning(pointcut = "anyMethod()", returning = "user")  表示 在切入点
 *     @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
 *     还要求改切入点类的方法返回一个String的时候 返回给doAfterReturning 作为参数
 *
 */


@Aspect
@Service("springInterceptor")
public class SpringInterceptor {

    //@Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")

    /*
     * 切入点是 PersonService_5接口
     */
    @Pointcut("execution(* com.zyy.service.PersonService_5.*(..))")
    //定一个切入点
    private void anyMethod() {
    }

    /*
     * @Before("anyMethod() && args(name)") 表示 在切入点
     * @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
     * 还要求改切入点类的方法只有有一个是String类型 返回给doBefore 作为参数
     */
    @Before("anyMethod() && args(xxx)")
    public void doBefore(String xxx) {

        System.out.println("*****  SpringInterceptor 前置通知 " + xxx + "  *****");

    }

    /*
     *  @AfterReturning(pointcut = "anyMethod()", returning = "user")  表示 在切入点
     *  @Pointcut("execution(* com.zyy.service.impl.PersonServiceBean_5.*(..))")的情况下
     *  还要求改切入点类的方法返回一个String的时候 返回给doAfterReturning 作为参数
     */
    @AfterReturning(pointcut = "anyMethod()", returning = "user")
    public void doAfterReturning(String user) {

        System.out.println("*****  SpringInterceptor 后置通知  " + user + " *****");

    }

    @After("anyMethod()")
    public void doAfter() {

        System.out.println("*****  SpringInterceptor 最终通知 *****");

    }

    @AfterThrowing(pointcut = "anyMethod()", throwing = "e")
    public void doAfterThrowing(Exception e) {

        System.out.println("*****  SpringInterceptor (异常)通知 " + e + " *****");

    }

    /*
     *  @Around("anyMethod()") 环绕通知 (应用比较多) 适合做权限
     *  注意:如果使用了环绕通知 就一定要执行 pjp.proceed(); 否则业务Bean中拦截的 Before...等方法不会执行
     */
    @Around("anyMethod()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {

        //因为我们已知切入点@Pointcut("execution(* com.zyy.service.PersonService_5.*(..))")

        //所以可以得到代理对象

        PersonService_5 personService_5 = (PersonService_5) pjp.getTarget();

        Object result = null;


        //权限过滤 如果有user值
        if (!(personService_5.getUser() == null || "".equals(personService_5.getUser()))) {

            System.out.println("*****  SpringInterceptor (进入)环绕通知 *****");

            System.out.println("*****  代理对象有user值,允许调用save  *****");

            //如果不执行这个方法,那么代理对象的方法也不执行了
            result = pjp.proceed();

            System.out.println("*****  SpringInterceptor (退出)环绕通知 *****");


            return result;

        }

        System.out.println("*****  代理对象无user值,不允许调用  *****");

        return result;

    }


}



代理对象实现bean:


有user值的bean:


package com.zyy.service.impl;

/**
 * Created by CaMnter on 2014/8/21.
 */

import com.zyy.service.PersonService_5;
import org.springframework.stereotype.Service;

/**
 * 实现Spring AOP
 * 
 * 有user值
 */

@Service("personService_5")
public class PersonServiceBean_5 implements PersonService_5 {

    private String user = "CaMnter";


    public PersonServiceBean_5() {
    }

    public PersonServiceBean_5(String user) {

        this.user = user;

    }

    public void save(String name) {

        System.out.println("*****  save " + name + " *****");

        //throw new RuntimeException("Remember") ;


    }

    public void update(Integer personId, String name) {

        System.out.println("*****  update  *****");

    }

    public void delete(Integer personId) {

        System.out.println("*****  delete  *****");

    }

    public String getName() {

        return "Save you from anything 07";

    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }
}


无user值的bean:

package com.zyy.service.impl;

/**
 * Created by CaMnter on 2014/8/21.
 */

import com.zyy.service.PersonService_5;
import org.springframework.stereotype.Service;

/**
 * 实现Spring AOP
 *
 * 无user值
 */

@Service("personService_7")
public class PersonServiceBean_7 implements PersonService_5 {

    private String user = null;


    public PersonServiceBean_7() {
    }

    public PersonServiceBean_7(String user) {

        this.user = user;

    }

    public void save(String name) {

        System.out.println("*****  save " + name + " *****");

        //throw new RuntimeException("Remember") ;


    }

    public void update(Integer personId, String name) {

        System.out.println("*****  update  *****");

    }

    public void delete(Integer personId) {

        System.out.println("*****  delete  *****");

    }

    public String getName() {

        return "Save you from anything 07";

    }

    public String getUser() {
        return user;
    }

    public void setUser(String user) {
        this.user = user;
    }
}





junit4.4测试代码:


有user值的测试:


    @Test
    public void springAopTest_1() {

        AbstractApplicationContext abstractApplicationContext = new ClassPathXmlApplicationContext("beans_3.xml");

        System.out.println("*****  SpringAOP动态代理  *****");

        System.out.println("*****  动态代理对象有user值  *****");

        System.out.println("");

        System.out.println("*****  调用save()并且有参数String类型  *****");

        PersonService_5 personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_5");

        personService_5.save("07");

        System.out.println("");

        System.out.println("*****  getName()返回String类型  *****");

        personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_5");

        personService_5.getName();
    }





无user值的测试:


    @Test
    public void springAopTest_2() {

        AbstractApplicationContext abstractApplicationContext = new ClassPathXmlApplicationContext("beans_3.xml");

        System.out.println("*****  SpringAOP动态代理  *****");

        System.out.println("*****  动态代理对象无user值  *****");

        System.out.println("");

        System.out.println("*****  调用save()并且有参数String类型  *****");

        PersonService_5 personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_7");

        personService_5.save("07");

        System.out.println("");

        System.out.println("*****  调用save()返回String类型  *****");

        personService_5 = (PersonService_5) abstractApplicationContext.getBean("personService_7");

        personService_5.getName();
    }






由此可见,SpringAOP动态代理足够强大。其原理也是很简单:如果目标有接口那么

SpringAOP调用的是JDK动态代理,如果没接口SpringAOP调用用的是CGlib动态

代理,这就导致了为什么cglib-nodep.jar出现在Spring 的依赖包之中。







三.aspectj的切入点语法定义细节


@Pointcut("execution(java.lang.Stringcom.zyy.service.PersonService_5.*(..))")


拦截返回String的方法




 

@Pointcut("execution(* com.zyy.service.PersonService_5.*(java.lang.String,..))")

 

拦截第一个的参数为String类型的方法




 

@Pointcut("execution(!void com.zyy.service.PersonService_5.*(java.lang.String,..))")

 

拦截非void返回值的方法




 

@Pointcut("execution(java.lang.Stringcom.zyy.service.*.*(..))")

 

拦截com.zyy.service子包内所有的类的所有方法




 

@Pointcut("execution(java.lang.Stringcom.zyy.service.*.s*(..))")

 

拦截以s开头的方法






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值