探秘Spring aop

探秘SpringAop
概述:
一 aop使用
二 aop原理
三 aop运用
四 aop实战
五 总结

1.面向切面的编程
2.Spring中的aop
3.小结

面向切面的编程就是一个编程范式,不是一种编程语言,解决特定的问题,不能解决所有问题.是对oop的补充,不是替代.
其他编程方式:
    面向过程
    面向对象
    函数式编程
    事件驱动式编程
    面向切面的编程
    
aop的初衷:
dry:dont't repeat yourself
soc:separation of concerns 关注点分离

水平分离:展示层 ->服务层 -> 持久层
垂直分离:模块划分(订单,库存等)
切面分离:分离功能性需求与非功能性需求

好处:
    集中处理某一关注点/横切逻辑
    可以很方便的添加/删除 关注点
    侵入性少,增强代码可读性及可维护性
    
使用场景:
    权限控制 缓存控制 事务控制 审计日志 性能监控 分布式追踪 异常处理

支持aop的语言
    java .net c/c++ ruby python php



二实战:利用aop改善既有代码
案例背景:
    1.有一个产品管理的服务
    2.产品添加/删除的操作只能管理员才能进行
    3.普通实现  vs aop实现
技术:Springboot 1.4.3.release

pom.xml
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>1.4.3.RELEASE</version>
    <relativepath/>
</parent>

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <!-- 其依赖会引用aspectjweaver.jar和 assertj-core.jar-->
</dependency>

项目架构
aop-guide
    src
        main
            java
                com.imooc.aop
                    domain
                        Product.java
                    security
                        AdminQnly.java
                        CurrentUserHolder.java
                    service
                        ProductService.java
                        AuthService.java
                    AopGuideApplication.java
        resources
            application.properties
        test
            java
                com.imooc.aop
                    AopGuideApplicationTests.java

代码实现

1.
package com.imooc.aop.domain;

public class Product {
    private Long id;
    private String name;
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

2.
package com.imooc.aop.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.imooc.aop.domain.Product;
import com.imooc.aop.security.AdminOnly;

@Service
public class ProductService {
    
    /**
     * 也可以拦截方法,真正的做到无侵入
     */
    @Autowired
    AuthService authService;
    
    @AdminOnly
    public void insert(Product product){
        //authService.checkAcess();
        System.out.println("insert product");
    }
    
    @AdminOnly
    public void delete(Long id){
        //authService.checkAcess();
        System.out.println("delete product");
    }
}

3.
package com.imooc.aop.service;

import org.springframework.stereotype.Service;

import com.imooc.aop.security.CurrentUserHolder;

@Service
public class AuthService {
    public void checkAcess(){
        String user = CurrentUserHolder.get();
        if(!"admin".equals(user)){
            throw new RuntimeException("oprator not allow");
        }
    }
}

4.
package com.imooc.aop.security;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface AdminOnly {

}
5.
package com.imooc.aop.security;

public class CurrentUserHolder {
    private static final ThreadLocal<String> holder = new ThreadLocal<String>();

    public static String get() {
        return holder.get()==null?"":holder.get();
    }
    
    public static void set(String user) {
        holder.set(user);
    }
    
}

6.
package com.imooc.aop.security;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import com.imooc.aop.service.AuthService;

@Aspect
@Component
public class SecurityAspect {
    
    @Autowired
    AuthService authService;
    
    //拦截标注有AdminOnly这种注解的方法
    @Pointcut("@annotation(AdminOnly)")
    public void adminOnly(){
        
    }
    
    @Before("adminOnly()")
    public void check(){
        authService.checkAcess();
    }
}

7.
package com.imooc.aop.tesst;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import com.imooc.aop.security.CurrentUserHolder;
import com.imooc.aop.service.ProductService;

@RunWith(SpringRunner.class)
@SrpingBootTest
public class AopGuideApplicationTests {
    
    /**
     * 这种方式,代码有侵入性,如果使用aop的方式,可以把代码给分离开来.
     */
    @Autowired
    ProductService productService;
    @Test(expected=Exception.class)
    public void annoInsertTest(){
        CurrentUserHolder.set("tom");
        productService.delete(1L);
    }
    
    @Test
    public void adminInsertTest(){
        CurrentUserHolder.set("admin");
        productService.delete(1L);
    }
}


上节内容:
什么是面向切面编程,为何用及好处
概念:1.切面2织入
aop分类及应用场景

本节:
Springaop 使用
xml
注解
 
 pointcut
 expression
 
关于切面的注解:
AspectJ
    @Aspect
    @Pointcut
    @Advice
切面示例:
@Aspect
@Component
public class SecurityAspect {
    
    @Autowired
    AuthService authService;
    
    //拦截标注有AdminOnly这种注解的方法
    @Pointcut("@annotation(AdminOnly)")
    public void adminOnly(){
        
    }
    
    @Before("adminOnly()")
    public void check(){
        authService.checkAcess();
    }
}

Pointcut
express
5种advice

切面表达式介绍:expression
指示器:designators
    匹配方法
        execution()
    匹配注解 :
        @target()
        @args()
        @within()
        @annotation()
    匹配包/类型
        within()
    匹配对象
        this()
        bean()
        target()
    匹配参数
        args()

wildcards(通配符):
    * 匹配任意数量的字符
    ..匹配任意的子包或参数
    + 匹配指定类及其子类

operators(运算符):
    && 与
    || 或
    ! 非


within表达式
@Aspect
@Component
public class PackageOrTypeAspect {
    
    @Autowired
    AuthService authService;
    
    //拦截匹配ProductService类里头的所有方法
    //@Pointcut("within(com.imooc.service.ProductService)")
    
    //拦截匹配的com.imooc包下及子包下类里头的所有方法
    @Pointcut("within(com.imooc.service..*)")
    public void matchType(){
        
    }
    
    @Before("matchType()")
    public void check(){
        authService.checkAcess();
    }
}


对象匹配

@Aspect
@Component
public class ObjectAspect {
    
    @Autowired
    AuthService authService;
    
    //拦截匹配的实现IDao接口的目标对象(而非aop代理后的对象)的方法,这里就是DemoDao的方法
    @Pointcut("this(com.imooc.log.Loggable)")
    public void matchCondition(){
        
    }
    
    //拦截匹配aop对象的目标对象为指定类型的方法,就是DemoDao的aop代理对象的方法
    @Pointcut("target(com.imooc.log.Loggable)")
    public void matchCondition(){
        
    }
    
    //拦截匹配的所有以service结尾的bean里头的方法
    @Pointcut("bean(LogService)")
    public void matchCondition(){
        
    }
    
    @Before("matchCondition()")
    public void check(){
        authService.checkAcess();
    }
}


参数匹配

@Aspect
@Component
public class ArgsAspect {
    
    @Autowired
    AuthService authService;
    
    //拦截匹配的任何一个只有Long参数的方法与com.imooc.service包下所有的类里头中方法
    //@Pointcut("args(Long) && within(com.imooc.service.*)")
    
    //拦截匹配的任何一个只有Long,String参数的方法与com.imooc.service包下所有的类里头中方法
    //@Pointcut("args(Long,String) && within(com.imooc.service.*)")
    
    //拦截匹配的任何以find开头而且只有一个Long参数的方法
    //@Pointcut("execution(* *..find *(Long)")
    
    //拦截匹配的任何以find开头而且第一个Long参数的方法
    //@Pointcut("execution(* *..find *(Long,..)")
    
    //拦截匹配的第一个参数为Long型的方法
    //@Pointcut("args((Long,..)")
    public void matchArgs(){
        
    }
    
    @Before("matchArgs()")
    public void check(){
        authService.checkAcess();
    }
}


注解匹配

@Aspect
@Component
public class AnnotationAspect {
    
    @Autowired
    AuthService authService;
    
    //匹配方法标注有AdminOnly的注解的方法
    @Pointcut("@annotation(com.imooc.aop.security.AdminOnly)")
    
    //匹配方法标注有Beta类底下的方法,要求的annotation的retentionPolicy级别为class
    @Pointcut("@within(com.imooc.aop.security.Beta)")
    
    //匹配方法标注有Repository的类底下的方法,要求的annotation的retentionPolicy级别为runtime
    @Pointcut("@target(com.imooc.aop.security.Repository)")
    
    //匹配传入的参数类标注有Repository注解的方法
    @Pointcut("@args(com.imooc.aop.security.Repository)")
        
    public void matchAnno(){
        
    }
    
    @Before("matchAnno()")
    public void check(){
        authService.checkAcess();
    }
}

execution表达式

/**
 * //匹配任何公共方法
 @Pointcut("execution(public * com.imooc.service.*.*(..))")

 //匹配com.imooc包及子包下Service类中无参方法
 @Pointcut("execution(* com.imooc..*Service.*())")

 //匹配com.imooc包及子包下Service类中的任何只有一个参数的方法
 @Pointcut("execution(* com.imooc..*Service.*(*))")

 //匹配com.imooc包及子包下任何类的任何方法
 @Pointcut("execution(* com.imooc..*.*(..))")

 //匹配com.imooc包及子包下返回值为String的任何方法
 @Pointcut("execution(String com.imooc..*.*(..))")

 //匹配异常
 execution(public * com.imooc.service.*.*(..) throws java.lang.IllegalAccessException)

 * Created by cat on 2017-02-19.
 */
@Aspect
@Component
public class ExecutionAspectConfig {

// @Pointcut("execution(public * com.imooc.service..*Service.*(..) throws java.lang.IllegalAccessException)")
// public void matchCondition(){}
//
// @Before("matchCondition()")
// public void before(){
//  System.out.println("");
//  System.out.println("###before");
// }

}



5种advice注解

1.@Before  前置通知
2.@After(finally)  后置通知,方法执行完之后
3.@AfterReturning   返回通知,成功执行之后
4.@AfterThrowing  异常通知,抛出异常之后
5.@Around  环绕通知

@Aspect
@Component
public class AdviceAspect {
    @Pointcut("@within(com.imooc.demo.security.NeedSecured)")
    public void annoTargetVsWithinDemo(){}
    
    @Before("annoTargetVsWithinDemo && within(com.imooc..*)")
    public void demo(){
        System.out.println("##aop by within");
        throw new RuntimeException("do not proceed");
    }
    
    
    
    
    @After("annoTargetVsWithinDemo && within(com.imooc..*)")
    public void afterDemo(){
        System.out.println("##aop by within");
    }
    
    @AfterReturning("annoTargetVsWithinDemo && within(com.imooc..*)")
    public void afterReturningDemo(){
        System.out.println("##aop by within");
    }
    
    @AfterThrowing("annoTargetVsWithinDemo && within(com.imooc..*)")
    public void afterThrowingDemo(){
        System.out.println("##aop by within");
    }
    
    public Object AfterThrowing(ProceedingJoinPoint pjp)throws Throwable{
        System.out.println("#Before");
        try{
            Object result = pjp.proceed(pjp.getArgs());
            System.out.println("#return");
            return result;
        }catch(Throwable throwable){
            System.out.println("#ex");
            throw throwable;
        }finally{
            System.out.println("#after");
        }
    }
}


/**
 * @Before("matchAnno()")
 * @After("matchAnno())") //相当于finally
 * @AfterReturning("matchException()")
 * @AfterThrowing("matchException()")
 * @Around("matchException()")
 * @Before(value = "matchLongArg() && args(productId)")
 * public void beforeWithArgs(Long productId)
 * @AfterReturning(value = "matchReturn()",returning = "returnValue")
 * public void getReulst(Object returnValue)
 * Created by cat on 2017-02-19.
 */
@Aspect
@Component
public class AdviceAspectConfig {

    /******pointcut********/

    @Pointcut("@annotation(com.imooc.anno.AdminOnly) && within(com.imooc..*)")
    public void matchAnno(){}

    @Pointcut("execution(* *..find*(Long)) && within(com.imooc..*) ")
    public void matchLongArg(){}

    @Pointcut("execution(public * com.imooc.service..*Service.*(..) throws java.lang.IllegalAccessException) && within(com.imooc..*)")
    public void matchException(){}

    @Pointcut("execution(String com.imooc..*.*(..)) && within(com.imooc..*)")
    public void matchReturn(){}


    /******advice********/
    @Before("matchLongArg() && args(productId)")
    public void before(Long productId){
        System.out.println("###before,get args:"+productId);
    }
//    @Around("matchException()")
//    public java.lang.Object after(ProceedingJoinPoint joinPoint){
//        System.out.println("###before");
//        java.lang.Object result = null;
//        try{
//            result = joinPoint.proceed(joinPoint.getArgs());
//            System.out.println("###after returning");
//        }catch (Throwable e){
//            System.out.println("###ex");
//            //throw
//            e.printStackTrace();
//        }finally {
//            System.out.println("###finally");
//        }
//        return result;
//    }

}


实现原理

原理概述:织入的时机
    1.编译期(AspectJ)
    2.类加载时(AspectJ 5+)
    3.运行时(Spring aop)
    
运行时织入是如何实现的:
    静态代理  到 动态代理
    动态代理:基于接口代理的实现 和 基于继承代理的实现
    
调用者,通过 proxy对象 间接与 Target 对象打交道

public interface Subject {
    void request();
    void hello();
}

public class RealSubject implements Subject{
    @Override
    public void request() {
        System.out.println("real subject execute request");
    }

    @Override
    public void hello() {
        System.out.println("hello");
    }
}

public class Proxy implements Subject{

    private RealSubject realSubject;

    public Proxy(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public void request() {
        System.out.println("before");
        try{
            realSubject.request();
        }catch (Exception e){
            System.out.println("ex:"+e.getMessage());
            throw e;
        }finally {
            System.out.println("after");
        }
    }

    @Override
    public void hello() {
        realSubject.hello();
    }
}


public class Client {

    public static void main(String[] args){
        Subject subject = new Proxy(new RealSubject());
        subject.request();
    }
}

静态代理 以及 动态代理

缺点:代理的方法越多,重复的逻辑代码就越多

动态代理:基于接口 基于继承 实现动态代理

实现代表: jdk代理  cglib代理

jdk代理要点:
    1.通过java.lang.reflect.Proxy 去生成动态代理类
    2.代理类实现织入的逻辑需要实现InvocationHandler接口
    3.只能基于接口进行动态代理
    
public class JdkProxySubject implements InvocationHandler{

    private RealSubject realSubject;

    //强引用,通过构造器把他注入进来
    public JdkProxySubject(RealSubject realSubject) {
        this.realSubject = realSubject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = null;
        try{
            result = method.invoke(realSubject,args);
        }catch (Exception e){
            System.out.println("ex:"+e.getMessage());
            throw e;
        }finally {
            System.out.println("after");
        }
        return result;
    }
}

public class Client {
    public static void main(String[] args){
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Subject subject = (Subject) Proxy.newProxyInstance(Client.class.getClassLoader(),new Class[]{Subject.class},new JdkProxySubject(new RealSubject()));
        subject.hello();
    }
}


cglib代理

是基于继承的方式实现的动态代理

public class DemoMethodInterceptor implements MethodInterceptor{
    @Override
    public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
        System.out.println("before in cglib");
        Object result = null;
        try{
            result = proxy.invokeSuper(obj, args);
        }catch (Exception e){
            System.out.println("get ex:"+e.getMessage());
            throw e;
        }finally {
            System.out.println("after in cglib");
        }
        return result;
    }
}

public class Client {

    public static void main(String[] args){
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealSubject.class);
        enhancer.setCallback(new DemoMethodInterceptor());
        Subject subject = (Subject) enhancer.create();
        subject.hello();
    }
}

Spring如何创建代理bean的

如果目标对象实现了接口,默认采用jdk动态代理,如若强制使用cglib代理,就采用cglib动态代理
如果目标对象没有实现接口,默认采用cglib动态代理.


多个aop叠加

public abstract class Handler {

    private Handler sucessor;

    public Handler getSucessor() {
        return sucessor;
    }

    public void setSucessor(Handler sucessor) {
        this.sucessor = sucessor;
    }

    public void execute(){
        handleProcess();
        if(sucessor != null){
            sucessor.execute();
        }
    }

    protected abstract void handleProcess();
}


public abstract class ChainHandler {

    public void execute(Chain chain){
        handleProcess();
        chain.proceed();
    }

    protected abstract void handleProcess();
}

public class Client {
    static class HandlerA extends Handler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by a");
        }
    }
    static class HandlerB extends Handler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by b");
        }
    }
    static class HandlerC extends Handler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by c");
        }
    }

    public static void main(String[] args){
        Handler handlerA = new HandlerA();
        Handler handlerB = new HandlerB();
        Handler handlerC = new HandlerC();

        handlerA.setSucessor(handlerB);
        handlerB.setSucessor(handlerC);

        handlerA.execute();
    }
}

public class Chain {

    private List<ChainHandler> handlers;

    private int index = 0;

    public Chain(List<ChainHandler> handlers) {
        this.handlers = handlers;
    }

    public void proceed(){
        if(index >= handlers.size()){
            return ;
        }
        handlers.get(index++).execute(this);
    }
}

public class ChainClient {
    static class ChainHandlerA extends ChainHandler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by chain a");
        }
    }
    static class ChainHandlerB extends ChainHandler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by chain b");
        }
    }
    static class ChainHandlerC extends ChainHandler{
        @Override
        protected void handleProcess() {
            System.out.println("handle by chain c");
        }
    }

    public static void main(String[] args){
        List<ChainHandler> handlers = Arrays.asList(
                new ChainHandlerA(),
                new ChainHandlerB(),
                new ChainHandlerC()
        );
        Chain chain = new Chain(handlers);
        chain.proceed();
    }
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值