Spring AOP 、AspectJ

AOP 的概念
1:理解AOP是什么
    * AOP是Aspect-oriented programming 面向切面(方面)的编程的简写
    * AOP的实现可以把业务逻辑和系统级的服务进行隔离,使业务逻辑跟各个系统级服务的耦合度降低, 
        提高程序的重用性和开发效率
        业务逻辑:某个类的某个方法本身要实现的功能.
        系统级的服务:系统的日志,事务,权限验证
     * 系统级服务的插入


2:怎么实现AOP    
     * AOP的底层实现的原理都是动态代理
    注意:动态代理有两种方式:jdk动态代理 /cglib动态代理
        jdk的动态代理机制只能代理实现了接口的类
        cglib的动态代理既可以代理实现了接口的类,也可以代理没有实现接口的类


3:AOP一些专业术语
    * 切面(Aspect) > 就是交叉在各个业务逻辑中的系统服务,类似于安全验证,事务处理,日志记录都可以理解为切面
    * 织入(weaving) > 就是将切面代码插入到目标对象某个方法的过程,相当于我们在jdk动态代理里面的 invocationHandler接口方法的内容 
    * 连接点(JointPoint) > 理论上可能被切面织入的所有方法 ,比如addStudent,delStudent ,getStudentList.....
        通常所有方法都可以被称为连接点
    * 切入点(PointCut) 就是实际上被切面织入的方法
    * 目标对象(target) 就是切入点和连接点所属的类 StudentService 
    * 通知(Advice) 就是切面的实现,切面织入的目标对象,时间点(方法执行前 ,后,前后,异常)以及内容  
    * 顾问(Advisor) 其实就是通知的一个封装和延伸,可以将通知以更为复杂的方式织入到某些方法中

    将切面织入到目标对象的连接点方法,使连接点成为切入点 

4:  Spring的AOP实现
    4.1 搭建springAOP开发环境需要引入的jar包
        * aopalliance-1.0.jar
        * spring-aop-4.2.1.RELEASE.jar
        注意:引入lib包到项目中

    4.2 :各种通知(前置通知,后置通知,环绕通知,异常通知)  
        * 前置通知  implement MethodBeforeAdvice
        * 后置通知  implement AfterReturningAdvice
        * 环绕通知  implement org.aopalliance.intercept.MethodInterceptor;
        * 异常通知  implement ThrowsAdvice

    4.3 :配置通知和应用通知
        * 注册通知 <bean id = "beforeAdvice" class = ""/>
        * 注册代理生成器(注入目标类接口,目标类,通知)

    4.4 通知的注意点
        * 在代理类生成器的配置里面 配置接口的<property name = "interfaces" value="接口" >默认可以省略
        * 因为后置通知是在目标方法执行后才执行 ,他可以得到目标方法返回的值 ,但是不能改变他的值
        * 环绕通知有在目标方法执行前的代码,也有在目标方法执行后的代码,可以得到目标方法的值,也可以改变它 

    总结:一个代理对象只能代理一个bean,意味着在实际应用中要定义多个代理(通过默认advisor自动代理生成器来解决)
        从容器中获取对象是通过代理的bean的id,而不是我们在容器里面定义的目标对象的id(通过默认advisor自动代理生成器来解决)
        通知只能切入到目标类的所有方法,不能指定某些方法(通过顾问对通知的封装实现)

    4.5:顾问(Advisor) 他将通知进行了包装,根据通知的不同类型,在不同的时间点,将切面织入到指定的目标对象的某些连接点(方法)
        PointCutAdvisor是顾问的一种,它是一个接口,有两个实现类

        * NameMatchMethodPointCutAdvisor   名称匹配方法 切入点顾问 
        * RegexpMethodPointCutAdvisor  正则表达式方法匹配切入点顾问
            a: * 匹配前面的子表达式任意次  比如: ao* 能匹配 a ao aoo aoooooo 
            b: + 匹配前面的子表达式一次或者多次   比如: ao+ 能匹配 ao aoo aooo
            c: . 匹配任意字符  除"\r\n"之外的
                .*  代表任意的一个字符串  .*add.* 代表包含add字符的任意字符串


    4.6 spring提供了自动代理生成器来解决要定义多个代理的问题 ,有如下两种方式
        * 默认advisor自动代理生成器 (为配置文件里面所有的advisor自动生成代理)

            注意:1 :为配置文件里面所有的advisor自动生成代理 /会代理配置文件里面所有注册的目标对象

        * bean名称自动代理生成器



总结  :  以上各种技术的运用 ,无非就是为了一个目标:将我们编写的切面的实现(通知/顾问)织入到某些类的某些方法当中
1 :AspectJ
    对于AOP的这种编程思想 ,有很多框架或者组件进行了实现,spring实现AOP就是其中的一种
AspectJ也实现了AOP,而且实现方式更为简单,使用起来更为方便,所以spring将AspectJ对于AOP的实现引入了自己的框架当中

2:AspectJ是什么
    AspectJ是一个面向切面的框架,他定义了AOP的一些语法,有一个专门的字节码生成器来生成遵守java规范的class文件

3:AspectJ的通知类型
    * 前置通知
    * 后置通知
    * 环绕通知
    * 异常通知
    * 最终通知  :无论程序是否正常执行,最终通知的代码会得到执行 类似于 try{}catch{}finally{}

4:切入点表达式    标识切面织入到哪些类的哪些方法当中
    * 语法格式如下: public boolean com.tz.spring.service.impl.StudentService.addStudent(Student student)
        execution(
            modifiers-pattern?          //访问权限匹配   如public、protected
            ret-type-pattern            //返回值类型匹配  
            declaring-type-pattern?     //全限定性类名
            name-pattern(param-pattern) //方法名(参数名)
            throws-pattern?             //抛出异常类型
            )
            注意:中间以空格隔开,有问号的属性可以省略
    * 特殊符号 
        a: * 代表0到多个任意字符
        b: .. 放在方法参数中 ,代表任意个参数 ,放在包名后面表示当前包及其所有子包路径
        c: + 放在类名后,表示当前类及其子类,放在接口后,表示当前接口及其实现类
    * 比如:
        a:execution(public * *(..))   表示任意的public方法
        b:execution(* set*(..))       表示任意包含以set字符开头的方法
        c:execution(* com.tz.spring.service.impl.*.*(..)) 表示com.tz.spring.service.impl的任意类的任意方法
        d:execution(* com.tz.spring.service..*.*(..)) 
                        表示com.tz.spring.service包下面的所有方法以及所有子包下面的所有方法
        e:execution(* com.tz.spring.service.IStudentService+.*(..))     

        f:execution(* add(String,int))  代表任意返回类型的add方法 有两个参数,类型分别为String,int
        g:execution(* add(String,*))


5:AspectJ+Spring的环境搭建       
    * 引入jar包
        * aopalliance-1.0.jar
        * spring-aop-4.2.1.RELEASE.jar
        * aspectjweaver-1.6.6.jar
        * spring-aspects-4.2.1.RELEASE.jar

    * 引入aop的约束  


6:基于xml配置的
    * 编写切面类
    * 在切面类里面定义各种通知的实现方法
    * 在配置文件里面对aop进行配置


7:基于注解的 
    * 编写切面类,加上@Aspect注解
    * 实现各种通知,在实现通知的方法上加上通知的注解以及切入点表达式的注解
    * 在配置文件注册切面,且加上aspectJ的自动代理
<?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">

   <!--注册studentService  -->
   <bean id = "studentService" 
                class = "com.tz.spring.sysmanage.service.impl.StudentService">
   </bean>

    <!--注册切面  -->
    <bean id= "myAspect" class = "com.tz.spring.sysmanage.aspect.MyAspectAnnotation"></bean>

     <aop:aspectj-autoproxy/>
</beans>         

StudentService

package com.tz.spring.sysmanage.service.impl;

import java.util.ArrayList;
import java.util.List;

import org.springframework.transaction.annotation.Transactional;

import com.tz.spring.sysmanage.entity.Student;
import com.tz.spring.sysmanage.service.IStudentService;

/**
 * 用于对外提供学生服务类的增删改查实现
 * @author Administrator
 *
 */
public class StudentService implements IStudentService{


    @Override
    public boolean addStudent(Student student) {
        //System.out.println("进行权限验证");

        System.out.println("执行增加功能");

        //System.out.println("进行日志记录");
        return true;
    }

    @Override
    public boolean delStudent(Integer studentId) {
        //System.out.println("进行权限验证");
        int i= 1/0;
        System.out.println("执行删除功能");
        //System.out.println("进行日志记录");

        return true;

    }

    @Override
    public boolean updateStudent(Student student) {
        //System.out.println("进行权限验证");
        System.out.println("执行修改功能");
        //System.out.println("进行日志记录");

        return true;

    }

    @Override
    public List<Student> getStudentList() {
        //System.out.println("进行权限验证");
        System.out.println("执行查询功能");
        //System.out.println("进行日志记录");
        return new ArrayList<Student>();
    }
}

MyAspectAnnotation

package com.tz.spring.sysmanage.aspect;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * 基于xml配置方式来实现AspectJ 定义通知
 * @author Administrator
 *
 */
@Aspect
public class MyAspectAnnotation {
    //定义前置通知
    @Before(value="execution(* add*(..))")
    public void beforeAdvice(){
        System.out.println("这是一个前置通知,应该在目标方法之前打印出来");
    }

    //定义后置通知
    @AfterReturning(value="execution(* update*(..))")
    public void afterAdvice(){
        System.out.println("这是一个后置通知,应该在目标方法之后打印出来");
    }

    //定义后置通知包含返回值
    @AfterReturning(value="execution(* update*(..))",returning="result")
    public void afterAdvice(Object result){
        System.out.println("这是一个后置通知,应该在目标方法之后打印出来");
        System.out.println("后置通知得到目标方法的返回值="+result.toString());
    }

    //定义环绕通知
    @Around(value="execution(* get*(..))")
    public void aroundAdvice(ProceedingJoinPoint pjp) throws Throwable{
        System.out.println("这是一个环绕通知,应该在目标方法之前打印出来");
        Object result = pjp.proceed();
        System.out.println("这是一个环绕通知,应该在目标方法之后打印出来");
    }

    //定义异常通知
    @AfterThrowing(value="execution(* del*(..))")
    public void afterThrowingAdvice(){
        System.out.println("这是一个异常通知,应该在目标方法出现异常时候打印出来");
    }

    //定义异常通知
    @AfterThrowing(value="execution(* del*(..))",throwing="ex")
    public void afterThrowingAdvice(Exception ex){
        System.out.println("这是一个异常通知,应该在目标方法出现异常时候打印出来 ex="+ex);
    }


    //定义最终通知 finally
    @After(value="execution(* del*(..))")
    public void lastAdvice(){
        System.out.println("这是一个最终通知,无论如何会执行 ");
    }

}

AspectAnnotationTest

package com.tz.spring.sysmanage.test;

import org.junit.Before;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.tz.spring.sysmanage.entity.Student;
import com.tz.spring.sysmanage.service.IStudentService;

public class AspectAnnotationTest {


    private  ApplicationContext ac = null;

    @Before
    public void init(){
        ac = new ClassPathXmlApplicationContext("applicationContext-annotation.xml");
    }
    @Test
    public void testAspectXmlConfig(){
        IStudentService studentService = (IStudentService) ac.getBean("studentService");
        studentService.addStudent(new Student());
    }


    //测试前置通知
    @Test
    public void testBeforeAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("studentService");
        studentService.addStudent(new Student());
    }

    //测试后置通知
    @Test
    public void testAfterAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("studentService");
        studentService.updateStudent(new Student());
    }


    //测试环绕通知
    @Test
    public void testAroundAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("studentService");
        studentService.getStudentList();
    }   

    //测试异常通知
    @Test
    public void testThrowingAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("studentService");
        studentService.delStudent(1);
    }

    //测试最终通知
    @Test
    public void testLastAdvice(){
        IStudentService studentService = (IStudentService) ac.getBean("studentService");
        studentService.delStudent(1);
    }   
}
  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值