Spring AOP

Spring AOP(面向切面)编程的原理 ?
  • AOP面向切面编程,它是一种思想。它就是针对业务处理过程中的切面进行提取,以达到优化代码的目的,减少重复代码的目的。 就比如,在编写业务逻辑代码的时候,我们习惯性的都要写:日志记录,事物控制,以及权限控制等,每一个子模块都要写这些代码,代码明显存在重复。这时候,我们运用面向切面的编程思想,采用横切技术,将代码中重复的部分,不影响主业务逻辑的部分抽取出来,放在某个地方进行集中式的管理,调用。 形成日志切面,事物控制切面,权限控制切面。 这样,我们就只需要关系业务的逻辑处理,即提高了工作的效率,又使得代码变的简洁优雅。这就是面向切面的编程思想,它是面向对象编程思想的一种扩展。
  • AOP的使用场景: 缓存、权限管理、内容传递、错误处理、懒加载、记录跟踪、优化、校准、调试、持久化、资源池、同步管理、事物控制等。 AOP的相关概念: 切面(Aspect) 连接点(JoinPoint) 通知(Advice) 切入点(Pointcut) 代理(Proxy): 织入(WeaVing)
  • Spring AOP的编程原理? 代理机制 JDK的动态代理:只能用于实现了接口的类产生代理。 Cglib代理:针对没有实现接口的类产生代理,应用的是底层的字节码增强技术,生成当前类的子类对象。
AOP : Aspect-Oriented Programming

​ 是传统的OOP编程的完善和补充的有效解决方案,OOP(object oriented program,用封装、继承和多态定义对象的层次结构)允许定义从上到下的关系,却不能定义从左到右的关系,当要为多个松散的对象引入公共行为的时候,OOP显得力不从心。AOP使用横切技术解决这个问题。

横切技术:AOP把系统分为核心关注点和横切点关注点。核心关注点即业务逻辑。横切关注点即和业务逻辑无关的如日志、权限、事务等,他们的特点是代码相似且重复性高。AOP的作用就是将他们分离开来,这就叫横切技术。

  • AOP实现

    • 静态织入。让编译器在编译期间或类加载期间就织入装饰代码即横切代码。实现框架有AspectJ。优点是性能较高;不受类的特殊限制,private、final、static都可以代理;

      AspectJ(使用各种注解告诉编译器如何织入,如@Pointcut定义切点,@After("pointcut")、@Before、@AfterThrowing、@AfterReturning、@Around)

    • 动态织入。使用动态代理技术,在运行时对核心关注点进行拦截,织入横切逻辑,同时执行核心逻辑。实现有:JDK动态代理(基于接口)、CGlib(基于类实现),SpringAOP编程基于这2种方式。

  • AOP技术概念和术语

    • Joinpoint:连接点。也就是核心关注点。程序执行的某个特定位置(如:某个方法调用前、调用后,方法抛出异常后)。一个类或一段程序代码拥有一些具有边界性质的特定点,这些代码中的特定点就是连接点。Spring仅支持方法的连接点。使用@Before、@After等来指定

    • Pointcut:切入点。也就是横切关注点,可以用切点表达式来限定PointCut。用于查找具体哪个方法作为切入点。使用@Pointcut指定。

    • Advice:通知(增强)。pointCut的执行代码。在@Before、@After标注的方法中写逻辑。

      ​ 通知类型有:(before:前置通知,在一个方法执行前被调用。after: 在方法执行之后调用的通知,无论方法执行是否成功。after-returning: 仅当方法成功完成后执行的通知。after-throwing: 在方法抛出异常退出时执行的通知。around: 在方法执行之前和之后调用的通知。around先于before和先于after

    • Aspect:切面。Advice和PointCut结合起来就是Aspect。使用@Aspect标注切面类

    • Introduce:引入。为对象引入属性或方法。使用@DeclareParents指定,其value属性来指定要增强的类,defaultImpl指定要加入的增强逻辑类。

    • Weaving:将advice织入JoinPoint的过程。分为静态织入、动态织入。

    • AOP Proxy:为AOP操作提供的业务逻辑代理对象。

  • 使用场景

    日志、权限、事务、ORM框架懒加载、缓存、错误处理、记录跟踪优化、资源池等。

  • 具体例子

    例如对于某个controller的某个URL访问,有些用户可能需要记录日志,有些不需要。不用AOP,就在原controller的mapping中添加日志记录代码,可能某一天需求改动如记录日志的格式要改,就又要去翻动原来的业务逻辑改动代码,而且可能是好几个甚至几十几百个日志记录代码都需要在业务逻辑中一起改,可能一不小心改错又要全部重来,动则要做这么大的修改,说明耦合性很高,让人头疼。用了AOP,只需要新建一个日志类,注解标上连接点即哪些方法需要用到日志记录,再一个注解标有哪些切点,短短几行就搞定了,日后再改,需要改动的地方也很少。

  • AOP编程优势

    解耦、提高代码复用性、提高扩展性、利于后期维护、低侵入式编程。

  • JDK动态代理

    public interface UserService {
        public void addUser(User user);
        public void getUser();
    }
    public class UserServiceImpl implements UserService {
        public void addUser(User user) {
            System.out.println("add user into database.");
        }
        public void getUser() {
            System.out.println("getUser from database.");
        }
    }
    public ProxyUtil implements InvocationHandler(){
        Object target;
        ProxyUtil(Object o){
            this.target=o;
        }
        public Object invoke(Object p, Method m, Object[] args){
            System.out.println("do sth before....");
            Object res = m.invoke(target, args);
            System.out.println("do sth after....");
            return res;
        }
    }
    
    import java.lang.reflect.Proxy;
    public class ProxyTest {
        public static void main(String[] args){
            Object proxy = new UserServiceImpl();
            ProxyUtil pu = new ProxyUtil(proxy);
            //生成代理对象  proxy.getClass().getClaccLoader()
            UserServiceImpl proxyObject = (UserServiceImpl)Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader, UserServiceImpl.class.getInterfaces(), pu);
            proxyObject.getUser();
            proxyObject.addUser();
        }
        /*执行结果
        do sth before....
        getUser from database.
        do sth after....
        do sth before....
        add user into database.
        do sth after....
    	*/
    }
    
    
  • CGlib

    import java.lang.reflect.Method;
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    public class CGProxy implements MethodInterceptor{
        public Object intercept(Object o, Method m, Object[] args, MethodProxy proxy){
            Object result = proxy.invoke(o, args);
            return result;
        }
        public Object getProxyObject(){
            Enhancer e = new Enhancer();
            e.setSuperclass(this.target.getClass);//设置父类为要代理的目标类target
            e.setCallback(this);//回调this.intercept
            return e.create();
        }
    }
    
    
  • SpringAop编程

    例如:对事务配置AOP、对数据源Bean配置AOP

    底层,如果实现了接口,用jdk动态代理,否则cglib。

    • 基于xml

      <aop:aspect> 配置一个切面;
      <aop:pointcut>配置一个切点,基于切点表达式;
      <aop:before>,<aop:after>,<aop:around>是定义不同类型的advise. a
      
    • 基于注解

      /*例如配置事务
      启用基于注解的事务配置
      <tx:annotation-driven transaction-manager="transactionManager" />
      扫描Service包
      <context:component-scan base-package="net.aazj.service,net.aazj.aop" />
      然后Service上@Transactional就可以了
      */
      //例如配置数据源,需要自定义切面代码
      //先<context:component-scan base-package="net.aazj.aop" />
      @Aspect    // for aop
      @Component // for auto scan
      @Order(0)  // execute before @Transactional
      public class DataSourceInterceptor {
          @Pointcut("execution(public * net.aazj.service..*.get*(..))")
          public void dataSourceSlave(){};
          @Before("dataSourceSlave()")
          public void before(JoinPoint jp) {
              DataSourceTypeManager.set(DataSources.SLAVE);
          }
      }
      
  • AOP在项目中的具体应用

    • 权限认证:比如对于某个包下的接口方法都是敏感操作,需要验证用户属不属于某种高级用户,看看cookie里是否带有某个字段(就不用去数据库查了),但接口方法很多,一个个加验证很麻烦,而且验证字段都类似,此时可以用AOP,写个pointcut做针对这个包下的权限认证。
    • 日志:对于某个包下的方法,新加了某些功能,也就新增了要记录某个用户关于这个功能动作的需求,记录字段类似,可以用AOP做。
    • 方法耗时统计:服务超时,需求是统计各个方法的耗时分布,可以用AOP做。
    • 自定义数据源:不同方法需要不同数据源,自己写Aspect类,为不同方法织入不同的数据源获取逻辑
Spring AOP 和 AspectJ AOP 有什么区别?
  • Spring AOP 属于运行时增强,而 AspectJ 是编译时增强。 Spring AOP 基于代理(Proxying),而 AspectJ 基于字节码操作(Bytecode Manipulation)。因此AspectJ 一般比较快。

  • Spring AOP 已经集成了 AspectJ ,AspectJ 应该算的上是 Java 生态系统中最完整的 AOP 框架了。AspectJ 相比于 Spring AOP 功能更加强大,但是 Spring AOP 相对来说更简单。

posted @ 2021-04-21 23:31  i%2  阅读(65)  评论(0)    收藏  举报