Spring入门到进阶 - Spring AOP

2021-05-05 10:28

阅读:448

标签:cti   监视   this   void   single   不使用接口   连接点   tca   要求   

目录
  • AOP概述
    • 什么是AOP?
    • AOP相关术语
  • AOP的底层原理(实现)
    • JDK的动态代理
    • CGLIB的动态代理
    • 代理知识总结
  • Spring的AOP的通知类型
  • Spring的AOP的切面
    • Spring的AOP的切面类型
    • Advisor(一般切面的实现)
    • PointcutAdvisor:代表具有切点的切面实现(拦截目标类指定方法)
  • Spring的传统AOP的动态代理
    • 自动代理创建
    • 基于Bean名称创建代理 (BeanNameAutoProxyCreator )
    • 基于切面信息的自动代理 (DefaultAdvisorAutoProxyCreator)

AOP概述

AOP : Aspect Oriented Programing 面向切面编程

AOP采用横向抽取机制(代理机制), 取代了传统纵向继承体系重复性代码在性能监视, 事务管理, 安全检查, 缓存中使用

Spring AOP是使用纯Java实现,不需要专门的编译过程和类加载器,在运行期通过代理方式向目标类织入增强代码

什么是AOP?

面向切面编程,是oop(面向对象编程)的补充和完善,oop是纵向的继承结构来代表公共行为的集合,当需要引入另外一个与业务无关的松散的行为的话就很无力,比如日志系统,所有的对象系统都需要日志,以往的做法会产生大量的重复代码,耦合度提高,重用性降低,显然是不合适的。

aop用横向的代理机制将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低 模块间的耦合度,并有利于未来的可操作性和可维护性

AOP相关术语

Joinpoint(连接点):所谓连接点是指那些可以被拦截到的点。在Spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义;个人理解:一个要拦截或者已经被拦截的方法被称为一个切入点。

Advice(通知/增强):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知。个人理解:对方法进行拦截之后所做的增强方法就是通知,分为前置通知,后置通知,异常通知,最终通知,环绕通知。
Target(目标对象)代理的目标对象

Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程。spring采用动态代理织入,而AspectJ采用编译期织入和类装载期织入

Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类

Aspect(切面):是切入点和通知(引介)的结合

AOP的底层原理(实现)

JDK的动态代理

public interface UserDao {
    public void save();
    public void update();
    public void delete();
    public void find();
}
public class UserDaoImpl implements UserDao {
    public void save() {
        System.out.println("保存用户...");
    }
    public void update() {
        System.out.println("修改用户...");
    }
    public void delete() {
        System.out.println("删除用户...");
    }
    public void find() {
        System.out.println("查询用户...");
    }
}

/**
 * JDK的动态代理
 * 动态代理类只能代理接口(不支持抽象类),代理类都需要实现InvocationHandler类,实现invoke方法。
 * 该invoke方法就是调用被代理接口的所有方法时需要调用的,该invoke方法返回的值是被代理接口的一个实现类
 */
public class MyJdkProxy implements InvocationHandler {

    // 目标对象
    private final UserDao userDao;

    public MyJdkProxy(UserDao userDao) {
        this.userDao = userDao;
    }

    // 绑定关系,也就是关联到哪个接口(与具体的实现类绑定)的哪些方法将被调用时,执行invoke方法。
    public Object createProxy() {
        //该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例
        //第一个参数指定产生代理对象的类加载器,需要将其指定为和目标对象同一个类加载器
        //第二个参数要实现和目标对象一样的接口,所以只需要拿到目标对象的实现接口
        //第三个参数表明这些被拦截的方法在被拦截时需要执行哪个InvocationHandler的invoke方法
        //根据传入的目标返回一个代理对象
        return Proxy.newProxyInstance(userDao.getClass().getClassLoader(), userDao.getClass().getInterfaces(), this);
    }

    //关联的这个实现类的方法被调用时将被执行
    /*InvocationHandler接口的方法,proxy表示代理,method表示原对象被调用的方法,args表示方法的参数*/
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        if ("save".equals(method.getName())) {
            System.out.println("权限校验...");
            //调用目标方法
            return method.invoke(userDao, args);
        }
        return method.invoke(userDao, args);
    }
}

// 测试类
public class SpringDemo1 {
    @Test
    public void demo1() {
        UserDao userDao = new UserDaoImpl();
        UserDao proxy = (UserDao) new MyJdkProxy(userDao).createProxy();
        proxy.save();
        proxy.update();
        proxy.delete();
        proxy.find();
    }
}

CGLIB的动态代理

  • 使用场景:对于不使用接口的业务类,无法使用JDK动态代理

  • CGlib采用非常底层字节码技术,可以为一个类动态的增加一些方法也可以生成一个类去继承这个类,解决无接口代理问题

public class ProductDao {
    public void save() {System.out.println("保存商品...");}
    public void update() {System.out.println("修改商品...");}
    public void delete() {System.out.println("删除商品...");}
    public void find() {System.out.println("查询商品...");}
}

// Cglib实现
public class MyCglibProxy implements MethodInterceptor {

    private ProductDao productDao;

    public MyCglibProxy(ProductDao productDao) {
        this.productDao = productDao;
    }

    public Object createProxy() {
        // 1.创建核心类
        Enhancer enhancer = new Enhancer();
        // 2.设置父类
        enhancer.setSuperclass(productDao.getClass());
        // 3.设置回调
        enhancer.setCallback(this);
        // 4.生成代理
        Object proxy = enhancer.create();
        return proxy;
    }

    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        if ("save".equals(method.getName())) {
            System.out.println("权限校验================");
            return methodProxy.invokeSuper(proxy, args);
        }
        return methodProxy.invokeSuper(proxy, args);
    }
}

// 测试类
public class SpringDemo2 {
    @Test
    public void demo1() {

        ProductDao productDao = new ProductDao();

        ProductDao proxy = (ProductDao) new MyCglibProxy(productDao).createProxy();
        proxy.save();
        proxy.update();
        proxy.delete();
        proxy.find();
    }
}

代理知识总结

  • Spring在运行期,生成动态代理对象,不需要特殊的编译器

  • Spring AOP的底层就是通过JDK动态代理或CGLib动态代理技术 为目标Bean执行横向织入

    1. 若目标对象实现了若干接口,spring使用JDK的动态代理

    2. 若目标对象没有实现任何接口,spring使用CGLIB动态代理

Spring的AOP的通知类型

1、AOP联盟为通知Advice定义了org.aopalliance.aop.Interface.Adive

2、Spring按照通知Adive在目标类方法的连接点位置,可以分为5类

  1. 前置通知:在目标方法执行前实施增强
  2. 后置通知:在目标方法执行后实施增强
  3. 环绕通知:在目标方法执行前后实施增强
  4. 异常通知:在方法抛出异常后实施增强
  5. (Spring只支持方法通知)引介通知:在目标类中添加一些新的方法和属性

Spring的AOP的切面

Spring的AOP的切面类型

Advisor:一般切面(拦截目标类所有方法)
PointcutAdvisor:代表具有切点的切面(拦截目标类指定方法)
IntroductionAdvisor:引介切面(不要求掌握)

Advisor(一般切面的实现)

准备工作:

1、引入AOP的两个包:
aopalliance
spring-aop

2、配置目标类

 

开始配置:

1、编写增强类,如

public class MyBeforeAdvice implements MethodBeforeAdvice {}

2、配置到xml

3、配置增强
ProxyFactoryBean常用可配置属性

  • target:代理的目标对象

  • proxyInterfaces:代理要实现的接口
    注意:如果多个接口可以使用list赋值

代码:


    

在测试类注入代理类:

@Resource(name="studentDaoProxy")
private StudentDao studentDao;
public void demo1(){
	studentDao.find();
}

配置代理类的相关属性:
-proxyTargetClass:是否对类代理而不是接口,设置为ture时,使用CGLib代理
-interceptorNames:需要织入目标的Advice
-singleton:返回代理是否为单实例,默认为单例
-optimize:当设置为ture时,强制使用CGLib代理

PointcutAdvisor:代表具有切点的切面实现(拦截目标类指定方法)


// 目标类
public class CustomerDao {
    public void find(){
        System.out.println("查询客户...");
    }
    public void save(){
        System.out.println("保存客户...");
    }
    public void update(){
        System.out.println("修改客户...");
    }
    public void delete(){
        System.out.println("删除客户...");
    }
}
// 通知类
public class MyAroundAdvice implements MethodInterceptor {
    public Object invoke(MethodInvocation invocation) throws Throwable {
        System.out.println("环绕前增强===================");
        Object obj = invocation.proceed();
        System.out.println("环绕后增强===================");
        return obj;
    }
}

Spring的传统AOP的动态代理

自动代理创建

前面的案例中,每个代理都是通过ProxyFacaoryBean织入切面代理,在实际开发中,非常多的Bean每个都配置ProxyFactoryBean开发维护量巨大

解决办法:自动创建代理

  • BeanNameAutoProxyCreator 根据Bean名称创建代理

  • DefaultAdvisorAutoProxyCreator 根据Advisor本身包含信息创建代理

  • AnnotationAwareAspectJAutoProxyCreator 基于Bean中的AspectJ注解进行自动代理

基于Bean名称创建代理 (BeanNameAutoProxyCreator )


基于切面信息的自动代理 (DefaultAdvisorAutoProxyCreator)


Spring入门到进阶 - Spring AOP

标签:cti   监视   this   void   single   不使用接口   连接点   tca   要求   

原文地址:https://www.cnblogs.com/greycdoer0/p/13192817.html


评论


亲,登录后才可以留言!