Spring/AOP

切面编程,其实就是动态代理,使用反射动态的构造一个新的Class,新的Class包含旧的Class,可以在旧的Class执行前后和中间插入代码。
基于XML Schema的AOP:
AOP代理就是AOP框架通过代理模式创建的对象,Spring AOP默认首先使用JDK动态代理来代理目标对象,如果目标对象没有实现任何接口将使用CGLIB代理,如果需要强制使用CGLIB代理,对于Schema风格配置切面使用如下方式来指定使用CGLIB代理:

      
      

例子:

    //目标接口
    package cn.javass.spring.chapter6.service;  
    public interface IHelloWorldService {  
        public void sayHello();  
    }  

    //目标接口实现
    package cn.javass.spring.chapter6.service.impl;  
    import cn.javass.spring.chapter6.service.IHelloWorldService;  
    public class HelloWorldService implements IHelloWorldService {  
        @Override  
        public void sayHello() {  
            System.out.println("============Hello World!");  
        }  
    }  

    //切面支持类
    package cn.javass.spring.chapter6.aop;  
    public class HelloWorldAspect {  
        //前置通知  
        public void beforeAdvice() {  
            System.out.println("===========before advice");  
        }  
        //后置最终通知  
        public void afterFinallyAdvice() {  
            System.out.println("===========after finally advice");  
        }  
    } 

    //在XML中进行配置
      
      
      

      

      
      
      
          
              
              
          
      

    //测试
    package cn.javass.spring.chapter6;  
    import org.junit.Test;  
    import org.springframework.context.ApplicationContext;  
    import org.springframework.context.support.ClassPathXmlApplicationContext;  
    import cn.javass.spring.chapter6.service.IHelloWorldService;  
    import cn.javass.spring.chapter6.service.IPayService;  
    public class AopTest {  
        @Test  
        public void testHelloworld() {  
            ApplicationContext ctx =  new ClassPathXmlApplicationContext("chapter6/helloworld.xml");  
            IHelloWorldService helloworldService =  
            ctx.getBean("helloWorldService", IHelloWorldService.class);  
            helloworldService.sayHello();  
        }  
    }  

其他后置通知,后置返回通知,后置异常通知,环绕通知等参考:http://jinnianshilongnian.iteye.com/blog/1418598

基于@AspectJ的AOP:
在Spring和SpringMVC的配置文件中启用切面,注意Spring和SpringMVC分别起用
Spring配置文件 applicationContext.xml(JDK本身提供的DynamicProxy):


       

SpringMVC配置文件 spring-mvc.xml(由CGLib提供):


      

正如开篇提到,所谓切面编程就是动态代理,那么动态代理有两种方式:

  • JDK本身提供的DynamicProxy。JDK的动态代理是针对接口的,某个Class首先要实现某个接口,可以对接口方法进行代理。
  • CGLib提供的。CGLib是针对具体的Class的,不需要你的Object去implement某个接口。在SpringMVC配置中使用cglib进行代理,这里注意如果你的pom需要有cglib的依赖。

选择哪种代理:
一般在Service切入AOP,使用JDK本身的动态代理,即。因为按照习惯我们编写的Service都会继承接口。
在Controller切入AOP,使用CGLib,即。Controller一般不继承接口。

下面以在Service层前后打印日志为例:
servie层代码:

package com.tengj.demo.service
public interface UserService {
    public void sayHello(String name);
}

servie实现类代码:

package com.tengj.demo.service.impl;
@Service("userService")
public class UserServiceImpl implements UserService{
    @Override
    public void sayHello(String name) {
        System.out.println("hello,"+name);
    }
}

切点:

import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component //注入依赖
@Aspect //该注解标示该类为切面类
public class LogAspect {
    @Pointcut("execution(* com.tengj.demo.service.impl.UserServiceImpl.*(..))")
    public void logAop(){}

    @Before("logAop() && args(name)")
    public void logBefore(String name){
        System.out.println(name+"前置通知Before");
    }

    /**
     * 定义返回值的型参名称为r,并传入切面
     */
    @AfterReturning(value = "logAop()", returning = "r")
    public void logAfterReturning(JoinPoint joinPoint, Object r) {
        log.info("方法调用结束 返回值:{}",r.toString());
    }

    @After("logAop() && args(name)")
    public void logAfter(String name){
        System.out.println(name+"后置通知After");
    }

    /**
     * 定义抛出类的型参名称为ex,并传入切面
     */
    @AfterThrowing(value = "logAop()", throwing = "ex")
    public void logAfterThrowing(JoinPoint joinPoint, Exception ex) {
        String message = (ex.getMessage() == null)?ex.getClass().getName():ex.getMessage();
        log.warn("方法抛出异常: {} ",message);
    }
}

上面方法index()就是我们定义的切点,表示在哪里切入AOP:

Spring/AOP_第1张图片

后边的Before和After就很好理解,就是以下方法在动态代理的方法前还是之后执行,除了@Before和@After还有@Around和@AfterThrowing、@AfterReturning等。
@Pointcut注解是为了定义切面内重用的切点,也就是说把公共的东西抽出来,定义了任意的方法名称logAop,这样下面用到的各种类型通知就只要写成例如: @Before("logAop() && args(name)")。也可以直接在其余注解中使用表达式例如: @Before(value = “execution(public*com.company.service.impl..*.*(..))”)
@Before("logAop() && args(name)")这里多出来个&& args(name)
,这个是用来传递参数的,定义要跟Service里的方法sayHello参数名称一样就可以。

applicationContext配置文件:



    
    
    
    
    
    ******
    
    
        
        
    


参考:
http://www.jianshu.com/p/d35e46f27187
http://alanli7991.github.io/2016/10/21/%E5%88%87%E9%9D%A2%E7%BC%96%E7%A8%8B%E4%B8%89AspectJ%E4%B8%8EShiro%E4%B8%8D%E5%85%BC%E5%AE%B9%E5%92%8CSpring%E4%BA%8C%E6%AC%A1%E4%BB%A3%E7%90%86%E9%94%99%E8%AF%AF%E5%88%86%E6%9E%90/

你可能感兴趣的:(Spring/AOP)