zoukankan      html  css  js  c++  java
  • spring4.1--AOP

    术语:

      切面:理论概念,用于增加功能。eg:日志和事物。在代码中,用通知和顾问表示切面。

          通知和顾问通过注解和配置文件实现。

      织入:是指将切面代码插入到目标业务对象的过程。包装的那个方法过程称为织入。

      连接点:业务方法,没有增强能力的业务方法。这些业务方法准备增加功能。

      切入点:哪些业务方法可以增加功能 。表示位置。(给哪个方法增加)。

      目标对象:要增加功能的类。

      通知:(叫增强) 具体实现了增加的功能,不仅表示切面和功能的增强。还具备 表示 增强的时间点。业务方法之前 加 还是业务方法之后加。

      顾问:也就增强。(高级应用) 

    spring实现aop比较笨重。所以引入了AspectJ框架。

    这个框架,是专门做面向切面的框架。扩展了java语言,有专门的的编译器生成class文件。

    功能:在编译器编译的时候,可以修改类的内容。

    两种实现方式:注解和配置XML

    AspectJ 5种切面的表现形式,即通知

      1.前置通知,业务方法之前

      2.后置通知,业务方法之后

      3.环绕通知,业务方法之前后

      4.异常通知,业务方法抛出异常时执行

      5.最终通知,总是会执行的

    切入点表达式

    告诉框架哪些业务方法需要增加功能

      原型:execution(访问权限类型 返回值类型  全限定类名  方法名(参数名)  抛出的异常类型 )

      5部分以空格分隔,是一个完整的方法的定义,标黄的是必需的两部分。

      切入点表达式是为了找方法。

      可以用

        * 0至多个任意字符 

        .. 用在参数列表,表示0个/多个参数;用在包名后,表示当前包及其子包。

        + 用在类名 当前类及其子类;用在接口 当前接口及其实现类。 

      

      eg: 

          1. execution(public * *(...))   指定切入点为:任意公共方法。 第一个* 是返回类型 第二个是方法名字

          2. execution(* set*(...))  任何以set开头的方法。第一个* 是返回类型 ,第二个 是匹配方法名字

          3. execution(* com.xy2.service.*.*(...))   service包下的任意类的任意方法(与子包无关)。第一个*是返回类名,第二个 是类名 第三个是方法名字

          4. execution(* com.xy2.service..*.*(...))  service包及其子包下的任意类的任意方法。第一个*是返回值,第二个是类名,第三个是方法名字。

            .. 在包名出出现,必须跟* ,任意包及其子包下的类

          5. execution(* *.service.*.*(..)) 一级包下的service的子包下的任意类任意方法。第二个*是一级包

          6.execution(* *..service.*.*(..)) 只要包路径有 service的子包下的任意类任意方法。

    Spting实现了AOP思想,但是很笨重。基于接口实现

    AspectJ实现了AOP

    通知:实现增强的功能,并且表示执行的时间点(例如业务方法前)

    切入点表达式:表示哪些业务方法需要增强功能。(表示位置) 

    demo 

    package com.cn.service;
    
    public interface TargetInter {
    
        public String doBefore();
    }
    
    
    package com.cn.service;
    
    public class TargetInterImpl implements TargetInter {
    
        public String doBefore() {
            System.out.println("do something。");
            return null;
        }
    
    }
    
    
    
    package com.cn.service;
    
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    //放在类上面,表示当前类是切面类,能够给业务方法增强功能
    @Aspect
    public class MyInvocationHandler  {
    
    
        
        /**
         * //前置通知
         * 有value属性:表示切入点表达式。表示位置
         * 特点:
         *         1.在目标方法之前先执行
         *         2.不会改变目标方法的执行流程
         *         3.不会改变目标方法的执行结果
         */
        @Before("execution(* com.cn.service.TargetInterImpl.doBefore())")
        public void myBefore() {
            //给业务方法增强的代码
            System.out.println("切面类====在业务方法之前 增强功能。");
            
        }
    
    }
    
    
    
    
    <?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.xsd
               http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd"
        >
        
        <!--  
        需要的jar包
            1.aop联盟包。aop要实现的功能,以接口的形式。
            2.spring-aop.jar spring对aop思想的实现
            3.aspectj的相关包。 aspectjweaver-1.9.4.jar (weaver或者tools)
            4.spring整合aspectJ 的包。
         -->
         <!-- 注册业务对象 -->
        <bean id = "targetA" class ="com.cn.service.TargetInterImpl" />
        <!-- 注册切面类 -->
        <bean id = "handler" class ="com.cn.service.MyInvocationHandler" />
        <!-- aop 执行这句话的时候,aspectj 会搜索在容器中的每一个bean对象,找自己的注解。@Aspect
        
         -->
         <aop:aspectj-autoproxy />
    </beans>
    
    
    
    
    package com.cn.test;
    
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.support.ClassPathXmlApplicationContext;
    
    import com.cn.service.TargetInter;
    
    public class Test {
    
        public static void main(String[] args) {
            String resource = "applicationContext.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
            
            TargetInter service=  (TargetInter) ac.getBean("targetA");
            System.out.println(service.getClass().getName());
            service.doBefore();
            
        }
    }
    
    
    
    com.sun.proxy.$Proxy5
    切面类====在业务方法之前 增强功能。
    do something。

    前置通知方法的参数

        /**
         * //前置通知
         * 有value属性:表示切入点表达式。表示位置
         * 特点:
         *         1.在目标方法之前先执行
         *         2.不会改变目标方法的执行流程
         *         3.不会改变目标方法的执行结果
         */
        @Before("execution(* com.cn.service.TargetInterImpl.doBefore())")
        public void myBefore(JoinPoint point) {
            System.out.println("point Signature==="+point.getSignature());
            System.out.println("point Kind==="+point.getKind());
            //给业务方法增强的代码
            System.out.println("切面类====在业务方法之前 增强功能。");
            
        }
    
    
    
    
    com.sun.proxy.$Proxy5
    point Signature===String com.cn.service.TargetInter.doBefore()
    point Kind===method-execution
    切面类====在业务方法之前 增强功能。
    do something。

    后置通知

    package com.cn.service;
    
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.AfterReturning;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    
    //放在类上面,表示当前类是切面类,能够给业务方法增强功能
    @Aspect
    public class MyInvocationHandler  {
    
    
        
        /**
         * //前置通知
         * 有value属性:表示切入点表达式。表示位置
         * 特点:
         *         1.在目标方法之前先执行
         *         2.不会改变目标方法的执行流程
         *         3.不会改变目标方法的执行结果
         */
        @Before("execution(* com.cn.service.TargetInterImpl.doBefore())")
        public void myBefore(JoinPoint point) {
            System.out.println("point Signature==="+point.getSignature());
            System.out.println("point Kind==="+point.getKind());
            //给业务方法增强的代码
            System.out.println("切面类====在业务方法之前 增强功能。");
            
        }
        
        
        /**
         * 1.在目标方法之后执行
         * 2.能获取目标方法的执行结果,并修改其值。只能修改对象的属性值
         * 3.不能影响方法的执行
         * @param rel
         */
        @AfterReturning(value="execution(* com.cn.service.TargetInterImpl.doAfter())",returning="rel")
        public void myAfter(String rel) {
            //给业务方法增强的代码
            System.out.println("rel is  "+ rel);
            rel = rel.toUpperCase();
            System.out.println("切面类====在业务方法之后 增强功能。");
            System.out.println("rel is  "+ rel);
        }
    
    }
    
    
        public static void main(String[] args) {
            String resource = "applicationContext.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
            
            TargetInter service=  (TargetInter) ac.getBean("targetA");
            System.out.println(service.getClass().getName());
            String a = service.doAfter();
            System.out.println("a is "+a);
            
        }
    
    
    com.sun.proxy.$Proxy6
    rel is  aaa
    切面类====在业务方法之后 增强功能。
    rel is  AAA
    a is aaa
        @AfterReturning(value="execution(* com.cn.service.TargetInterImpl.doAfterInt())",returning="rel")
        public void myAfterInt(int rel) {
            //给业务方法增强的代码
            System.out.println("rel is  "+ rel);
            rel = 10;
            System.out.println("切面类====在业务方法之后 增强功能。");
            System.out.println("rel is  "+ rel);
        }
    
    
    public static void main(String[] args) {
            String resource = "applicationContext.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
            
            TargetInter service=  (TargetInter) ac.getBean("targetA");
            System.out.println(service.getClass().getName());
            int a = service.doAfterInt();
            System.out.println("a is "+a);
            
        }
    
    
    com.sun.proxy.$Proxy6
    rel is  4
    切面类====在业务方法之后 增强功能。
    rel is  10
    a is 4
    
    
    
    
        @AfterReturning(value="execution(* com.cn.service.TargetInterImpl.doAfterObj())",returning="rel")
        public void myDoAfterObj(Strudent rel) {
            //给业务方法增强的代码
            System.out.println("rel is  "+ rel.toString());
            rel.setAge(55);
            System.out.println("切面类====在业务方法之后 增强功能。");
            System.out.println("rel is  "+ rel.toString());
        }
    
    
    
    com.sun.proxy.$Proxy6
    rel is  Strudent [name=zhangsan, age=34]
    切面类====在业务方法之后 增强功能。
    rel is  Strudent [name=zhangsan, age=55]
    a is Strudent [name=zhangsan, age=55]

    环绕通知

        /**
         * 和JDK的invoke的功能一模一样
         * 1.在目标方法的前后执行。
         * 2.可以控制目标方法的执行
         * 3.可以修改执行的结果
         * @param point
         * @return
         * @throws Throwable 
         */
        @Around("execution(* com.cn.service.TargetInterImpl.doAround())")
        public Object mydoAround(ProceedingJoinPoint point) throws Throwable {
            //给业务方法增强的代码
            System.out.println("目标方法之前执行功能");
            //调用目标方法
            Object o = point.proceed();
            System.out.println("目标方法之后执行功能");
            return o;
        }
    
    
        public static void main(String[] args) {
            String resource = "applicationContext.xml";
            ApplicationContext ac = new ClassPathXmlApplicationContext(resource);
            
            TargetInter service=  (TargetInter) ac.getBean("targetA");
            System.out.println(service.getClass().getName());
            Strudent a = service.doAround();
            System.out.println("a is "+a.toString());
            
        }
    
    
    
    
    com.sun.proxy.$Proxy7
    目标方法之前执行功能
    目标方法之后执行功能
    a is Strudent [name=zhangsan, age=34]

    异常通知

        /**
         * 并不是异常处理方法,只是获取到目标方法处理异常 的信息
         * throwing 表示目标方法抛出的异常对象,自定义变量名
         * 在目标方法抛出异常时执行,不是异常处理程序。
    可把异常信息记录到数据库,日志,发邮件等 * @param point * @return
    */ @AfterThrowing(value="execution(* com.cn.service.TargetInterImpl.doExcep())",throwing="e") public void mydoExcep(Exception e) { System.out.println("目标方法的异常"+e); } public static void main(String[] args) { String resource = "applicationContext.xml"; ApplicationContext ac = new ClassPathXmlApplicationContext(resource); TargetInter service= (TargetInter) ac.getBean("targetA"); System.out.println(service.getClass().getName()); service.doExcep(); // System.out.println("a is "+a.toString()); } @Override public void doExcep() { System.out.println(1/0); } com.sun.proxy.$Proxy8 目标方法的异常java.lang.ArithmeticException: / by zero Exception in thread "main" java.lang.ArithmeticException: / by zero at com.cn.service.TargetInterImpl.doExcep(TargetInterImpl.java:34) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) at java.lang.reflect.Method.invoke(Method.java:498) at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:343) at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:197) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) at org.springframework.aop.aspectj.AspectJAfterThrowingAdvice.invoke(AspectJAfterThrowingAdvice.java:62) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92) at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185) at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) at com.sun.proxy.$Proxy8.doExcep(Unknown Source) at com.cn.test.Test.main(Test.java:17)

     最终通知

        /**
         * 1.总是会执行
         * 2.目标方法之后执行
         * @param e
         */
        @After(value="execution(* com.cn.service.TargetInterImpl.doFinal())")
        public void mydoFinal()  {
            System.out.println("总是会执行");
        }
    
    
    com.sun.proxy.$Proxy9
    最终通知目标方法
    总是会执行

    切入点表达式的辅助注解

    /**
         * 1.总是会执行
         * 2.目标方法之后执行
         * @param e
         */
        @After(value="mypt()")
        public void mydoFinal()  {
            System.out.println("总是会执行");
        }
    
        /**
         * @Pointcut 定义切入点的辅助注解
         * 管理切入点的注解
         * value:切入点表达式
         * 凡是使用该切入点表达式的,都可以用 “mypt()” 代替
         * 方法名代替了一个切入点表达式(集中管理)。方法只是一个标识作用,辅助作用
         */
        @Pointcut(value="execution(* com.cn.service.TargetInterImpl.doFinal())")
        private void mypt()  {
            //方法不需要代码
        }
    
    
    
    com.sun.proxy.$Proxy10
    最终通知目标方法
    总是会执行

    目标对象实现了接口,默认用的JDK的代理。

    想用CHLIB的代理,修改配置文件如下:

    <!-- 注册业务对象 -->
        <bean id = "targetA" class ="com.cn.service.TargetInterImpl" />
        <!-- 注册切面类 -->
        <bean id = "handler" class ="com.cn.service.MyInvocationHandler" />
        <!-- aop 执行这句话的时候,aspectj 会搜索在容器中的每一个bean对象,找自己的注解。@Aspect
            proxy-target-class 的值默认是false.表示用JDK,true用cglib
            cglib的效率更高
         -->
         <aop:aspectj-autoproxy proxy-target-class="true"/>
    
    
    com.cn.service.TargetInterImpl$$EnhancerBySpringCGLIB$$3d4062f2
    最终通知目标方法
    总是会执行

    假如目标对象没有实现接口,框架自动使用cglib代理。

    注解实现的过程

    1.加入jar包

    2.定义目标类(接口类,实现类)

    3.定义切面类(功能增强类,@Aspect,@Before,@AfterReturning,@Around,@AfterThrowing,@After)

    4.配置文件

      注册目标类

      注册切面类

      注册自动代理生成器

    配置文件实现AOP

    配置文件实现的

    1.注册目标对象,service对象

    2.注册切面类

    3.配置AOP,实现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.xsd
               http://www.springframework.org/schema/context 
               http://www.springframework.org/schema/context/spring-context.xsd
               http://www.springframework.org/schema/aop
               http://www.springframework.org/schema/aop/spring-aop.xsd"
        >
        
        <!--  
            1.注册目标对象,service对象
            2.注册切面类
            3.配置aop,实现aop的功能
                    切入点
                    切面
         -->
         <!-- 1.注册业务对象 -->
        <bean id = "targetA" class ="com.cn.service.TargetInterImpl" />
        <!-- 2. 注册切面类 -->
        <bean id = "handler" class ="com.cn.service.MyInvocationHandler" />
        
        <aop:config>
            <!-- 切入点配置 配置的是哪个业务方法需要增强功能 -->
            <aop:pointcut expression="execution(* com.cn.service.TargetInterImpl.doBefore())" id="mypointcut"/>
            <!-- 切面配置,配置的是增强功能的方法,以及增强的执行位置(前后) -->
            <aop:aspect ref="handler">
                <aop:before method="myBefore" pointcut-ref="mypointcut" />
            </aop:aspect>
        </aop:config>
        
    </beans>
    
    
    com.sun.proxy.$Proxy4
    point Signature===String com.cn.service.TargetInter.doBefore()
    point Kind===method-execution
    切面类====在业务方法之前 增强功能。
    do something。
  • 相关阅读:
    C struct 中字节对齐问题(转)
    蚁群算法,PSO算法以及两种算法可以融合的几种方法
    遗传及蚁群算法
    ListBox FAQ常用问题
    关于C#中ListBox控件重绘Item项
    创业艰难,问题多多
    asp.net客户端脚本验证小技巧
    防止ASP.NET按钮多次提交的办法
    鼠标点到文本框时的提示信息
    枚举的转换
  • 原文地址:https://www.cnblogs.com/llq1214/p/11256270.html
Copyright © 2011-2022 走看看