学习Spring AOP

首先看一下Spring AOP的相关知识
1)通知(Advice):
定义了切面是什么以及何时使用,描述切面要完成的工作和何时需要执行这个工作
2)连接点(JOINPOINT)
程序能够应用通知的一个时机,这些时机就是连接点,例如方法被调用时,异常被抛出时。
3)切入点(PointCut)
通知定义了切面要发生的故事和时间,那么切入点则通知了故事发生的地点,例如某个类或某个方法
4)切面(Aspect)
通知和切点共同定义了切面:时间,地点和要发生的故事
5)引入(Introduction)
引入运行我们向现有的类添加新的方法(Spring提供了一个方法注入的功能)
6)代理(Proxy)
应用通知的对象
7)织入(Weaving)
8)目标(Target)
目标,即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事。
Spring Aop的核心技术其实也是动态代理,动态代理主要的作用是方法的增强,在一些业务限制中,我们可能不能改变某些方法的签名以及方法体内的原有代码,我们就可以动态代理的方法增强方法,可以在方法执行前后做任何你想做的事情(甚至根本不去执行这个方法),因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。因为在InvocationHandler的invoke方法中,你可以直接获取正在调用方法对应的Method对象,具体应用的话,比如可以添加调用日志,做事务控制等。
对于JDK动态代理,只能针对接口实现动态代理,无法对于类直接实现动态代理,这是由于java单继承特性留下的天然桎梏。但是对于cglib代理来说,cglib直接改变字节码,可以直接对类进行动态代理。直接改变字节码,打破了java无法对类实现动态代理的天然限制。
下面再解释下AOP
AOP(Aspect Oriented Programming),即面向切面编程,可以说是OOP(Object Oriented Programming,面向对象编程)的补充和完善。OOP引入封装、继承、多态等概念来建立一种对象层次结构,用于模拟公共行为的一个集合。不过OOP允许开发者定义纵向的关系,但并不适合定义横向的关系,例如日志功能。日志代码往往横向地散布在所有对象层次中,而与它对应的对象的核心功能毫无关系对于其他类型的代码,如安全性、异常处理和透明的持续性也都是如此,这种散布在各处的无关的代码被称为横切(cross cutting),在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

AOP技术恰恰相反,它利用一种称为”横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其命名为”Aspect”,即切面。所谓”切面”,简单说就是那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块之间的耦合度,并有利于未来的可操作性和可维护性。

使用”横切”技术,AOP把软件系统分为两个部分:核心关注点和横切关注点。业务处理的主要流程是核心关注点,与之关系不大的部分是横切关注点。横切关注点的一个特点是,他们经常发生在核心关注点的多处,而各处基本相似,比如权限认证、日志、事物。AOP的作用在于分离系统中的各种关注点,将核心关注点和横切关注点分离开来。
二.实现AOP有两种方式,一种是全注解,一种是用配置文件实现。
1)首先是全注解方式
先看配置文件

<?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:aop="http://www.springframework.org/schema/aop"  
            xmlns:p="http://www.springframework.org/schema/p"  
            xmlns:tx="http://www.springframework.org/schema/tx"  
            xmlns:context="http://www.springframework.org/schema/context"  
            xsi:schemaLocation="  
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
             <context:component-scan base-package="wangcc"></context:component-scan>
                <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
                </beans> 

在使用Spring实现注解时,必须添加Spring-context.jar,

 <context:component-scan base-package="wangcc"></context:component-scan>

base-package=”wangcc”代表wangcc这个包以及旗下的包里的注解都会被扫描 aop命名空间的声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被隐藏起来了 有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

再看AOP实现类

package wangcc.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

/** 
* @ClassName: AroundTest 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author wangcc
* @date 2017-1-6 下午1:33:19 
**  Before 和After属于消极处理,他不会对Pointcut切入点方法有影响,典型的用于记录日志
*  只是在方法调用前后进行一些处理,不会影响方法中的逻辑体本身
*  而Around是积极处理,他可以影响方法中的逻辑处理,切人点方法的处理与否由Around通知来决定。  
*/
@Aspect
@Component
public class AroundTest {
    @Pointcut("execution(* wangcc.service..*.update(..))")
    public void test(){};
    @Before("test()")
    public void authority(JoinPoint jp){
        System.out.println("Before增强:模拟执行权限检查");  
        // 返回被织入增强处理的目标方法  
        System.out.println("Before增强:被织入增强处理的目标方法为:"  
            + jp.getSignature().getName());  
        // 访问执行目标方法的参数  
        System.out.println("Before增强:目标方法的参数为:"  
            + Arrays.toString(jp.getArgs()));  
        // 访问被增强处理的目标对象  
        System.out.println("Before增强:被织入增强处理的目标对象为:"  
            + jp.getTarget());  

    }
    @Around("test()")
    public Object doTranscation(ProceedingJoinPoint pjp) throws Throwable{
          System.out.println("Around增强:执行目标方法之前,模拟开始事务...");  
          Object[] args=pjp.getArgs();
          if(args!=null&&args[0].getClass()==Integer.class)
          {
             args[0]=(Integer)args[0]+10;
          }
          Object id=pjp.proceed(args);
          System.out.println("Around增强:执行目标方法之后,模拟结束事务...");  
          if(id!=null&&id instanceof Integer)
          {
              id=(Integer) id* (Integer) id;
          }
          return id;
    }
      @After("test()")  
        public void release(JoinPoint jp)  
        {  
            System.out.println("After增强:模拟方法结束后的释放资源...");  
            // 返回被织入增强处理的目标方法  
            System.out.println("After增强:被织入增强处理的目标方法为:"  
                + jp.getSignature().getName());  
            // 访问执行目标方法的参数  
            System.out.println("After增强:目标方法的参数为:"  
                + Arrays.toString(jp.getArgs()));  
            // 访问被增强处理的目标对象  
            System.out.println("After增强:被织入增强处理的目标对象为:"  
                + jp.getTarget());  
        }  
    @AfterReturning(pointcut="test()",returning="retVal")
    public void log(JoinPoint jp,Object retVal){
           System.out.println("AfterReturning增强:获取目标方法返回值:"  
                   + retVal);  
               System.out.println("AfterReturning增强:模拟记录日志功能...");  
               // 返回被织入增强处理的目标方法  
               System.out.println("AfterReturning增强:被织入增强处理的目标方法为:"  
                   + jp.getSignature().getName());  
               // 访问执行目标方法的参数  
               System.out.println("AfterReturning增强:目标方法的参数为:"  
                   + Arrays.toString(jp.getArgs()));  
               // 访问被增强处理的目标对象  
               System.out.println("AfterReturning增强:被织入增强处理的目标对象为:"  
                   + jp.getTarget());  
    }
}

业务代码

package wangcc.service;

import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {
    public void addUser(){
        System.out.println("add user");

    }
    public Integer update(int id){
        Integer realId=id+1;
        return realId;
    }
    public Integer insertUser(){
        System.out.println("insert user");
        return 20;
    }
}

``

测试代码

package wangcc.test;

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

import wangcc.service.UserService;

public class TestAspectJ {
    @Test 
    public void testUpdate(){
        ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) ctx.getBean("userService");
        userService.update(10);
    }
    @Test
    public void testAdd(){
        ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) ctx.getBean("userService");
        userService.addUser();
    }
    @Test
    public void testInsert(){
        ClassPathXmlApplicationContext ctx=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserService userService=(UserService) ctx.getBean("userService");
        userService.insertUser();
    }

}

2)使用XML文件配置方法
配置文件

<?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:aop="http://www.springframework.org/schema/aop"  
            xmlns:p="http://www.springframework.org/schema/p"  
            xmlns:tx="http://www.springframework.org/schema/tx"  
            xmlns:context="http://www.springframework.org/schema/context"  
            xsi:schemaLocation="  
                http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd  
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd  
                http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
             <bean id="userManager" class="wangcc.dao.UserManagerImpl">
              </bean>
              <bean id="xmlHandler" class="wangcc.aop.AdviseHandler"></bean>
                 <aop:config>
                 <aop:aspect id="aspect" ref="xmlHandler">
                  <aop:pointcut expression="execution(* wangcc.dao.*.find*(..))" id="pointUser"/>

                <!-- 注意  before after around 三者配置的先后顺序将会影响程序的执行顺序
                    以pjp.proceed();为界
                    当before after around 顺序时 
                                after 对应函数会在pjp.proceed();之后的代码块之前执行
                    当before around after 顺序时
                         正常流转
                    当after before around 顺序时   
                                    after 对应函数会在pjp.proceed();之后的代码块之前执行 
                    当after around before 顺序时
                    before 对应函数会在pjp.proceed();执行之前执行
                    before 对应函数会在pjp.proceed();之后的代码块执行之前执行
                 -->


                   <aop:after method="doAfter" pointcut-ref="pointUser"/>
                 <aop:around method="doAround" pointcut-ref="pointUser"/>
                   <aop:before method="doBefore" pointcut-ref="pointUser"/>

                 <aop:after-returning method="doAfterReturn" pointcut-ref="pointUser"/>
                 <aop:after-throwing method="doAfterThrow" pointcut-ref="pointUser" throwing="ex"/>
                 </aop:aspect>
                 </aop:config>
                             </beans> 

aop实现类

package wangcc.aop;

import java.util.Arrays;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

public class AdviseHandler {
    private void doBefore(JoinPoint jp){
        System.out.println("-----doBefore().invoke-----");  
        System.out.println(" 此处意在执行核心业务逻辑前,做一些安全性的判断等等");  
        System.out.println(" 可通过joinPoint来获取所需要的内容"); 
        System.out.println("目标方法的参数列表:"+Arrays.toString(jp.getArgs()));
        System.out.println(":被织入处理的目标对象为"+jp.getTarget());
        System.out.println("Before增强:被织入增强处理的目标方法为:"  
                + jp.getSignature().getName());  
        System.out.println("-----End of doBefore()------");  

    }
    private void doAfter(JoinPoint jp){
        System.out.println("--doAfter().invoke--");
          System.out.println(" 此处意在执行核心业务逻辑之后,做一些日志记录操作等等");  
            System.out.println(" 可通过joinPoint来获取所需要的内容");  
            System.out.println("-----End of doAfter()------");  
    }
    private Object doAround(ProceedingJoinPoint pjp) throws Throwable {  
        System.out.println("-----doAround().invoke-----");  
        System.out.println(" 此处可以做类似于Before Advice的事情");  

        //调用核心逻辑  
        Object retVal = pjp.proceed();  

        System.out.println(" 此处可以做类似于After Advice的事情");  
        System.out.println("-----End of doAround()------");  
        return retVal;  
    }  
    private void doAfterReturn(JoinPoint jp){
        System.out.println("-----doReturn().invoke-----");  
        System.out.println(" 此处可以对返回值做进一步处理");  
        System.out.println(" 可通过joinPoint来获取所需要的内容");  
        System.out.println("-----End of doReturn()------");  

    }
    private void doAfterThrow(JoinPoint jp,Throwable ex){
        System.out.println("-----doThrowing().invoke-----");  
        System.out.println(" 错误信息:"+ex.getMessage());  
        System.out.println(" 此处意在执行核心业务逻辑出错时,捕获异常,并可做一些日志记录操作等等");  
        System.out.println(" 可通过joinPoint来获取所需要的内容");  
        System.out.println("-----End of doThrowing()------");  
    }
}

业务代码

package wangcc.dao;

public interface UserManager {
    public String findUserById(int id);
}
package wangcc.dao;

public class UserManagerImpl implements UserManager {

    @Override
    public String findUserById(int id) {
        // TODO Auto-generated method stub
         System.out.println("---------UserManagerImpl.findUserById()--------");  
            if (id <= 0) {  
                throw new IllegalArgumentException("该用户不存在!");   
            }  
            return "Kobe Bryant";
    }

}
package wangcc.service;

import org.springframework.stereotype.Component;

@Component("userService")
public class UserService {
    public void addUser(){
        System.out.println("add user");

    }
    public Integer update(int id){
        Integer realId=id+1;
        return realId;
    }
    public Integer insertUser(){
        System.out.println("insert user");
        return 20;
    }
}

测试代码

package wangcc.test;

import org.junit.Test;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import wangcc.dao.UserManager;
import wangcc.service.UserService;

public class TestAspectJ {
    @Test 
    public void test(){
        BeanFactory factory=new ClassPathXmlApplicationContext("applicationContext.xml");
        UserManager userManager=(UserManager) factory.getBean("userManager");
        userManager.findUserById(1);
        System.out.println("---------分割线---------");
        try {
            userManager.findUserById(0);
        } catch (IllegalArgumentException e) {
            // TODO: handle exception
        }
    }

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值