7.Spring AOP动态代理

来源:互联网 发布:翻墙推荐 知乎 编辑:程序博客网 时间:2024/04/30 17:04



一.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开头的方法






0 0
原创粉丝点击