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。
原文地址:https://www.cnblogs.com/llq1214/p/11256270.html