SPRING AOP 与 ASPECTJ

 

AOP:面向切面编程

简介

AOP解决的问题:将核心业务代码与外围业务(日志记录、权限校验、异常处理、事务控制)代码分离出来,提高模块化,降低代码耦合度,使职责更单一。

 

AOP应用场景:

日志记录、权限校验、异常处理、事务控制等

 

 

相关概念

 SPRING AOP 与 ASPECTJ_第1张图片

 

图片来源:

https://raw.githubusercontent.com/WarframePrimer/mysmart4j/master/img/AOP.jpg

 

 

joinPoint:连接点。在spring中只支持方法连接点,连接点指的是可以使用advice(增强)的地方,例如一个类中有5个方法,那么这5个方法,那么这5个方法都可以是连接点。

 

pointcut:切点。可理解为实实在在的连接点,即切入advice(增强)的点。例如

一个类中有5个方法,其中有3个方法(连接点)需要织入advice(增强),那么这3个需要织入advice的连接点就是切点。

 

advice:增强。实际中想要添加的功能,如日志、权限校验。

before:前置增强,目标方法执行前之前执行。

after:后置增强,目标方法执行后执行。

around:环绕增强,在目标方法执行时执行,可控制目标方法是否执行。

after throwing:异常增强,目标方法抛出异常时执行。

weaving:织入。即对方法的增强,将切面的代码织入(应用)到目标函数的过程。

introduction advice:引入增强。即对类的增强。

 

advisor:切面。由切点和增强相结合而成,定义增强应用到哪些切点上。

 

Aop的两种实现

pom.xml依赖

复制代码




    4.0.0

 

    com.lnjecit

    chapter4

    1.0-SNAPSHOT
 

    

        4.1.7.RELEASE

    
 

    

        

            cglib

            cglib

            3.2.0

        

        

            org.springframework

            spring-aop

            ${spring.version}

        

        

            org.springframework

            spring-aspects

            ${spring.version}

        

        

            org.springframework

            spring-beans

            ${spring.version}

        

        

            org.springframework

            spring-context

            ${spring.version}

        

        

            org.springframework

            spring-context-support

            ${spring.version}

        

        

            org.springframework

            spring-core

            ${spring.version}

        

    

复制代码

  

Spring aop

基于代理(jdk动态代理、cglib动态代理)实现的aop

Spring aop使用了两种代理机制。一种是jdk动态代理,另一种是cglib动态代理。

Jdk动态代理只支持接口代理,cglib支持类的代理。

 

编程式

前置增强 before advice

前置增强:实现MethodBeforeAdvice接口,执行目标方法前执行before方法

复制代码

/**
 * 编程式前置增强
 */
public class UserBeforeAdvice implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("Before");
    }
}

复制代码

 

 后置增强 after advice

后置增强:实现AfterReturningAdvice接口,执行目标方法后执行afterReturning方法

复制代码

/**
 * 编程式后置增强
 */
public class UserAfterAdvice implements  AfterReturningAdvice  {
 
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("After");
    }
}

复制代码

 

 

环绕增强 around advice

环绕增强:实现MethodInterceptor接口,执行目标方法前后执行invoke方法

复制代码

/**
 * 编程式环绕增强
 **/
public class UserAroundAdvice implements MethodInterceptor{
    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("Before");
        Object result = invocation.proceed();
        System.out.println("After");
        return result;
    }
}

复制代码

 

 

用户服务接口类:

public interface UserService { 
    void queryAll();
}

 

 

用户服务接口实现类:

复制代码

public class UserServiceImpl implements UserService {
 
    @Override
    public void queryAll() {
        System.out.println("查询全部用户并返回");
    }
}

复制代码

  

测试代码:

复制代码

public class Test {
 
    public static void main(String[] args) {
        /**
         * 测试前置增强和后置增强
         */
        ProxyFactory proxyFactory = new ProxyFactory();//创建代理工厂
        proxyFactory.setTarget(new UserServiceImpl());//射入目标类对象
        proxyFactory.addAdvice(new UserBeforeAdvice());//添加前置增强
        proxyFactory.addAdvice(new UserAfterAdvice());//添加后置增强
        UserService userService = (UserService) proxyFactory.getProxy();//从代理工厂获取代理
        userService.queryAll();//调用代理的方法
 
        /**
         * 测试环绕增强
         */
        ProxyFactory proxyFactory2 = new ProxyFactory();//创建代理工厂
        proxyFactory2.setTarget(new UserServiceImpl());//射入目标类对象
        proxyFactory2.addAdvice(new UserAroundAdvice());//添加环绕增强
        UserService userService2 = (UserService) proxyFactory2.getProxy();//从代理工厂获取代理
        userService2.queryAll();//调用代理的方法
 
    }
}

复制代码

 

 

声明式(基于xml配置)

环绕增强 around advice

spring-aop.xml配置文件如下:

复制代码


 
    
    
 
    
    
        
        
        
        
        
        
            
                userAroundAdvice
            
        
 
    
 

复制代码

 

 

 

用户服务接口类:

public interface UserService {
    void queryAll();
}

 

 

用户服务接口实现类:

复制代码

@Component
public class UserServiceImpl implements UserService {
 
    @Override
    public void queryAll() {
        System.out.println("查询全部用户并返回");
    }
}

复制代码

 

 

测试环绕增强代码:

复制代码

public class Test {
 
    public static void main(String[] args) {
        // 获取spring context
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");
        // 从context中获取id为userServiceProxy的代理对象
        UserService userServiceProxy = (UserService) applicationContext.getBean("userServiceProxy");
        // 调用代理的方法
        userServiceProxy.queryAll();
 
    }
}

复制代码

 

  

抛出增强 throws advice

抛出增强:实现ThrowsAdvice接口。当执行目标方法出现异常会执行抛出增强中的afterThrowing方法。

 

复制代码

/**
 * 抛出增强
 */
@Component
public class UserThrowAdvice implements ThrowsAdvice {
 
    public void afterThrowing(Method method, Object[] args, Object target, Exception e) {
        System.out.println("Throw exception:");
        System.out.println("Target class name:" + target.getClass().getName());
        System.out.println("Method name: " + method.getName());
        System.out.println("Exception message:" + e.getMessage());
    }
}

复制代码

  

将UserServiceImpl类修改如下:故意抛出运行时异常,用于测试抛出增强

 

复制代码

@Component
public class UserServiceImpl implements UserService {
 
    @Override
    public void queryAll() {
        System.out.println("查询全部用户并返回");
        throw new RuntimeException("Error");
    }
}

复制代码

  

Spring-aop.xml配置文件修改如下:

复制代码




 

    

    

 

    

    

        

        

        

        

        

        

            

                userThrowAdvice

            

        

     

复制代码

 

 

测试代码与环绕增强的测试代码相同,不再复制。

 

切面 advisor

advisor(切面)封装了advice(增强)和pointcut(切点)

 

在UserService接口中添加两个方法query、save.

UserService代码如下:

复制代码

public interface UserService { 

    void queryAll(); 

    void query(); 

    void save();

}

复制代码

 

 

UserServiceImpl代码如下:

复制代码

@Component

public class UserServiceImpl implements UserService { 

    @Override

    public void queryAll() {

        System.out.println("查询全部用户并返回");

    }
 

    @Override

    public void query() {

        System.out.println("根据条件查询用户");

    }
 

    @Override

    public void save() {

        System.out.println("新增用户");

    }
 

}

复制代码

 

 

spring-aop.xml配置文件如下:

复制代码




 

    

    

 

    

    

        

        

        

        

    

 

    

    

        

        

        

        

        

        

    

复制代码

 

 

测试代码:

 

复制代码

public class Test {
 

    public static void main(String[] args) {

        // 获取spring context

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml");

        // 从context中获取id为userServiceProxy的代理对象

        UserService userServiceProxy = (UserService) applicationContext.getBean("userServiceProxy");

        // 调用代理的方法

        userServiceProxy.queryAll();

        userServiceProxy.query();

        userServiceProxy.save();

 

    }

}

复制代码

 

 

 

测试结果如下:

Before

查询全部用户并返回

After

Before

根据条件查询用户

After

新增用户

可以看出UserService中的queryAll、query方法被拦截,执行这两个方法前后执行了环绕增强代码。而save方法没有被拦截。

 

自动代理

aspectj

基于注解

环绕增强 around advice

使用@Aspect注解定义切面类UserAdvisor

 

复制代码

/**

 * 切面

 */

@Aspect

@Component

public class UserAdvisor { 

    @Around("execution(* com.lnjecit.chapter4.user.UserServiceImpl.*(..))")

    public Object arount(ProceedingJoinPoint joinPoint) throws Throwable {

        before();

        Object result = joinPoint.proceed();

        after();

        return result;

    } 

    public void before() {

        System.out.println("Before");

    } 

    public void after() {

        System.out.println("After");

    }

}

复制代码

 

 

切点表达式:

execution(* com.lnjecit.chapter4.user.UserServiceImpl.*(..))

l  Execution表示要拦截的方法

l  第一个“*”表示方法返回值任意

l  第二个“*”表示匹配类中的所有方法

l  (..)表示方法参数任意

 

spring-aspectj.xml配置文件:

复制代码




 

    

    

 

    

    

 

复制代码

 

 

测试代码:

复制代码

public class Test {

 

    public static void main(String[] args) {

        // 获取spring context

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj.xml");

        // 从context中获取id为userServiceImpl的对象

        UserService userServiceImpl = (UserService) applicationContext.getBean("userServiceImpl");

        userServiceImpl.queryAll();

        userServiceImpl.query();

        userServiceImpl.save();

 

    }

}

复制代码

 

 

拦截指定注解

简单案例:使用@Annotation注解拦截Log注解,记录日志

 

 

定义Log注解

复制代码

/**

 * 日志注解

 */

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.METHOD)

@Documented

public @interface Log {
   

}

复制代码

 

 

定义切面LogAspect

 

复制代码

 
@Aspect
@Component

public class LogAspect {
   @Before(value = "@annotation(com.lnjecit.chapter4.user.Log)")
    public void before(JoinPoint joinPoint) {
        // 目标类名称
        String targetClazzName = joinPoint.getTarget().getClass().getName();
        // 目标方法名称
        String targetMethodName = joinPoint.getSignature().getName();
        System.out.println("执行目标方法" + targetClazzName + "." + targetMethodName +"前,记录日志");
        // 可在此处将日志异步存储到数据库表中
    }

}

复制代码

 

在UserServiceImpl类的save方法上加上Log注解

复制代码

@Component

public class UserServiceImpl implements UserService { 

    @Override

    public void queryAll() {

        System.out.println("查询全部用户并返回");

//        throw new RuntimeException("Error");

    }
 

    @Override

    public void query() {

        System.out.println("根据条件查询用户");

    }
 

    @Log

    @Override

    public void save() {

        System.out.println("新增用户");

    } 

}

复制代码

 

 

测试代码:

复制代码

public class Test {
 

    public static void main(String[] args) {

        // 获取spring context

        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj.xml");

        // 从context中获取id为userServiceImpl的对象

        UserService userServiceImpl = (UserService) applicationContext.getBean("userServiceImpl");

        userServiceImpl.save();

    }

}

复制代码

 

 

测试结果如下:

执行目标方法com.lnjecit.chapter4.user.UserServiceImpl.save前,记录日志
新增用户

 

根据结果可以看出执行save方法之前,执行了LogAspect中的前置增强。

 

基于配置

除了使用Aspect注解外,还可以使用xml配置的方式来实现aspect的应用

环绕增强 around advice

切面类UserAdvisor

复制代码

/**
 * 切面
 */
public class UserAdvisor {

    public Object arount(ProceedingJoinPoint joinPoint) throws Throwable {
        before();
        Object result = joinPoint.proceed();
        after();
        return result;
    }

    public void before() {
        System.out.println("Before");
    }

    public void after() {
        System.out.println("After");
    }
}

复制代码

 

spring-aspectj.xml配置:

复制代码



    
    

    
    

    
        
        
            
            
        
    

复制代码

 

UserServiceImpl:

复制代码

public class UserServiceImpl implements UserService {

    @Override
    public void queryAll() {
        System.out.println("查询全部用户并返回");
    }

    @Override
    public void query() {
        System.out.println("根据条件查询用户");
    }

    @Log
    @Override
    public void save() {
        System.out.println("新增用户");
    }

}

复制代码

 

 测试代码:

复制代码

public class Test {

    public static void main(String[] args) {
        // 获取spring context
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj.xml");
        // 从context中获取id为userServiceImpl的对象
        UserService userServiceImpl = (UserService) applicationContext.getBean("userServiceImpl");
        userServiceImpl.queryAll();
        userServiceImpl.query();
        userServiceImpl.save();

    }
}

复制代码

测试结果与使用注解实现效果相同。

 

拦截指定注解

Log注解:

复制代码

/**
 * 日志注解
 */
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
public @interface Log {

}

复制代码

 

切面LogAspect:

复制代码

/**
 * 日志切面
 */
public class LogAspect {

    public void before(JoinPoint joinPoint) {
    // 目标类名称
    String targetClazzName = joinPoint.getTarget().getClass().getName();
    // 目标方法名称
    String targetMethodName = joinPoint.getSignature().getName();
    System.out.println("执行目标方法" + targetClazzName + "." + targetMethodName +"前,记录日志");
    // 可在此处将日志异步存储到数据库表中
 }

复制代码

 

spring-aspectj.xml配置:

复制代码



    
    

    
        
        
            
            
        
    

复制代码

 

UserService、UserServiceImpl类与环绕通知中代码中相同。

 

测试代码:

复制代码

public class Test {

    public static void main(String[] args) {
        // 获取spring context
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj.xml");
        // 从context中获取id为userServiceImpl的对象
        UserService userServiceImpl = (UserService) applicationContext.getBean("userServiceImpl");
        userServiceImpl.queryAll();
        userServiceImpl.query();
        userServiceImpl.save();

    }
}

复制代码

 

测试结果与基于注解测试结果相同。

 

Spring aop与aspectJ的区别

1、织入的时期不同

spring aop采用的动态织入,而aspectJ是静态织入。

静态织入:指在编译时期就织入,即:编译出来的class文件,字节码就已经被织入了。

动态织入:分静动两种,静则指织入过程只在第一次调用时执行;动则指根据代码动态运行的中间状态来决定如何操作,每次调用target的时候都执行。

 

Spring aspectJ简单应用案例

日志记录

可参考前面案例中的定义日志记录,使用切面记录日志。

性能监控

 定义性能监控切面:

复制代码

/**
 * 性能监控切面
 */
@Aspect
@Component
public class MonitorAspect {

    @Around("execution(* com.lnjecit.chapter4.user.UserServiceImpl.*(..))")
    public Object arount(ProceedingJoinPoint joinPoint) throws Throwable {
        // 目标类名称
        String targetClazzName = joinPoint.getTarget().getClass().getName();
        // 目标类方法名称
        String targetMethodName = joinPoint.getSignature().getName();

        // 计时并调用目标函数
        long start = System.currentTimeMillis();
        Object result = joinPoint.proceed();
        long time = System.currentTimeMillis() - start;

        System.out.println("执行" + targetClazzName + "." + targetMethodName + "方法耗时" + time + "毫秒");
        // 可在此处将监控信息存储
        return result;
    }

}

复制代码

 

 UserService:

复制代码

public interface UserService {

    void queryAll();

    void query();

    void save();
}

复制代码

 

UserServiceImpl:

复制代码

@Component
public class UserServiceImpl implements UserService {

    @Override
    public void queryAll() {
        System.out.println("查询全部用户并返回");
    }

    @Override
    public void query() {
        System.out.println("根据条件查询用户");
    }

     @Override
    public void save() {
        System.out.println("新增用户");
    }

复制代码

 

spring-aspectj.xml配置:

复制代码



    
    

    
    

复制代码

 

测试代码:

复制代码

public class Test {

    public static void main(String[] args) {
        // 获取spring context
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aspectj.xml");
        // 从context中获取id为userServiceImpl的对象
        UserService userServiceImpl = (UserService) applicationContext.getBean("userServiceImpl");
        userServiceImpl.queryAll();
        userServiceImpl.query();
        userServiceImpl.save();
    }
}

复制代码

 

测试结果如下:

查询全部用户并返回
执行com.lnjecit.chapter4.user.UserServiceImpl.queryAll方法耗时54毫秒
根据条件查询用户
执行com.lnjecit.chapter4.user.UserServiceImpl.query方法耗时0毫秒
新增用户
执行com.lnjecit.chapter4.user.UserServiceImpl.save方法耗时1毫秒

你可能感兴趣的:(java)