SpringBoot 对于AOP的理解

SpringBoot 对于AOP的理解

我们都知道OOP(面向对象编程),它允许开发者定义纵向的关系,可以将具体的事务抽取成一个类,并在类的内部定义需要的属性和方法,但是OOP不能定义横向的关系,便造成了大量代码的重复,而不利于各个模块之间的重用性。此时便诞生了我们的AOP(面向切面编程),它作为面向对象的一种补充,可以将那些与业务无关,却对多个对象产生影响的共同行为和逻辑,抽取并封装成一个可重用的模块,这个模块就称为切面(Aspect),从而减少系统中重复的代码,并降低了代码之间的耦合度,同时提高了系统之间的可维护性,现在AOP主要用于权限认证,日志处理,事务处理等方面

在Spring中,AOP的动态代理的方式主要有两种:

  • JDK动态代理
  • CGLIB动态代理

其中我们分别简单介绍一下JDK动态代理CGLIB动态代理

  • JDK动态代理只提供接口的代理,不支持类的代理。核心InvocationHandler接口Proxy类,InvocationHandler 通过invoke()方法反射来调用目标类中的代码,动态地将横切逻辑和业务编织在一起;接着,Proxy利用 InvocationHandler动态创建一个符合某一接口的的实例, 生成目标类的代理对象。
  • 如果代理类没有实现 InvocationHandler 接口,那么Spring AOP会选择使用CGLIB来动态代理目标类。CGLIB(Code Generation Library),是一个代码生成的类库,可以在运行时动态的生成指定类的一个子类对象,并覆盖其中特定方法并添加增强代码,从而实现AOP。CGLIB是通过继承的方式做的动态代理,因此如果某个类被标记为final,那么它是无法使用CGLIB做动态代理的

JDK动态代理的简单实现

条件:

  • (1) 进行动态代理的类需要存在接口
  • (2) 利用java.lang.reflect.Proxy类的newProInstance()方法,以及InvocationHandler实现动态代理

我们首先定义一个接口:

CustomerDao

public interface CustomerDao {
    void add();
    void update();
    void delete();
}

然后我们创建一个上面接口的实现类:

CustomerDaoImpl

public class CustomerDaoImpl implements CustomerDao{
    @Override
    public void add() {
        System.out.println("CustomerDaoImpl add() running...");
    }

    @Override
    public void update() {
        System.out.println("CustomerDaoImpl update() running...");
    }

    @Override
    public void delete() {
        System.out.println("CustomerDaoImpl delete() running...");
    }
}

紧接着,我们此时创建一个代理对象【proxyInstance】,然后使用该代理对象完成目标类CustomerDaoImpl的业务逻辑

public class CustomerProxy {
    public static void main(String[] args) {
        //创建我们的CustomerDao接口的实现类
        CustomerDaoImpl customerDao = new CustomerDaoImpl();
        //使用java.lang.reflect.Proxy的newProInstance()方法创建一个CustomerDaoImpl的实例customerDao的一个代理类
        CustomerDao proxyInstance = (CustomerDao)Proxy.newProxyInstance(
                //需要传入三个参数:分别为目标类的类加载器,目标类的接口,增强的业务(创建一个InvocationHandler接口匿名内部类完成)
                customerDao.getClass().getClassLoader(),
                customerDao.getClass().getInterfaces(),
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //invoke()方法的参数:proxy【代理固定值】,method【需要增强的方法,切入点】,args【方法运行时的参数】
                        //我们可以自定义获取需要增强的方法,进行方法的增强操作
                        if ("add".equals(method.getName())) {
                            System.out.println("add() before running");
                            method.invoke(customerDao, args);
                            System.out.println("add() after running");
                        } else {
                            method.invoke(customerDao, args);
                        }
                        return null;
                    }
                }
        );
        
        //此时proxyInstance即为customerDao【目标类】的一个代理对象
        proxyInstance.add();
        proxyInstance.update();
        proxyInstance.delete();
    }
}

输出结果:

add() before running
CustomerDaoImpl add() running...
add() after running
CustomerDaoImpl update() running...
CustomerDaoImpl delete() running...

这样我们就简单地实现了利用JDK方式完成动态代理!

CGLIB动态代理的简单实现

条件:

  • 需要导包 : import org.springframework.cglib.proxy.Enhancer;

创建一个目标类:

Person

public class Person {
    public void run() {
        System.out.println("Person run() running...");
    }
}

然后我们使用CGLIB创建一个该目标类的代理类:

public class PersonCGLIBProxy {
    public static void main(String[] args) {
        Person person = new Person();
        //利用CGLIB动态代理方式
        Person personProxy = (Person)Enhancer.create(
                // 参数一: 目标类的字节码文件类型  因为用于继承
                // 参数二: 增强的业务逻辑
                Person.class,
                new MethodInterceptor() {
                    @Override
                    // 参数一: 代理对象的类型 固定值
                    // 参数二: 目标类要增强的方法
                    // 参数三: 方法运行时期需要的参数
                    // 参数四: 代理方法 忽略
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        //拦截执行名称方法
                        if("run".equals(method.getName())){
                            System.out.println("run方法之前的功能加强...");
                            method.invoke(person,args);
                            System.out.println("run方法之后的功能加强...");
                        }else{
                            method.invoke(person,args);
                        }
                        return null;
                    }
                }
        );
        personProxy.run();
    }
}

输出结果:

run方法之前的功能加强...
Person run() running...
run方法之后的功能加强...    

AOP的常见概念

术语名称定义
target(目标对象)即要增强的队象
Proxy(代理对象)增强以后的对象
JoinPoint(连接点)可以被增强的方法
PointCut(切入点)要被增强的方法
Weaving(织入)切入点集成到切面形成代理类的过程
Aspect(切面)切入点+通知/增强 = 切面(即里面有要被增强的方法,以及增强方法具体的增强代码程序)
Advice(通知/增强)做了增强的那段程序代码

在这里插入图片描述

AOP通知的类型

在AOP术语中,切面的工作被称为通知,实际上是程序执行时要通过SpringAOP框架触发的代码段。

Spring切面可以应用5种类型的通知:

  • 前置通知(Before):在目标方法被调用之前调用通知功能;
  • 后置通知(After):在目标方法完成之后调用通知,此时不会关心方法的输出是什么;
  • 返回通知(After-returning ):在目标方法成功执行之后调用通知;
  • 异常通知(After-throwing):在目标方法抛出异常后调用通知;
  • 环绕通知(Around):通知包裹了被通知的方法,在被通知的方法调用之前和调用之后执行自定义的行为。

手写AOP : 基于AspectJ实现

第一步 : 在AppConfig类上配置@EnableAspectJAutoProxy
@Configuration
@EnableAspectJAutoProxy   //开启基于Aspect实现AOP
@ComponentScan("com.feng")
public class AppConfig {

}
第二步 : 定义配置类
@Component
@Aspect
public class UserAspect {

}

注意 : 因为Aspect不是Spring内部的包,需要我们自己引入jar包,这里我们使用gradle构建的Spring源码项目,我们在配置文件中引入AspectJ

build.gradle文件 :

plugins {
    id 'java'
}

group 'org.springframework'
version '5.0.18.BUILD-SNAPSHOT'

sourceCompatibility = 1.8

repositories {
    mavenCentral()
}

dependencies {
    compile project(':spring-context')
    compile(project(":spring-instrument"))
    testCompile group: 'junit', name: 'junit', version: '4.12'
    
	//引入AspectJ : https://mvnrespository.com/artifact.aspectj/aspectjweaver
    compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.9.0'
    
}
第三步 : 定义一个切入点PointCut,以及通知Advice
@Component
@Aspect
public class UserAspect {

	@Pointcut("execution(* com.feng.*.*.*(..))")
	public void anyPublicMethod(){

	}

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

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

}
第四步 : 在com.feng包下定义一个User类 :
@Component
public class User {
	public void run() {
		System.out.println("User run()方法...");
	}
}
测试类 :
public class TestAppConfig {
	public static void main(String[] args) {
		AnnotationConfigApplicationContext ac = new AnnotationConfigApplicationContext(AppConfig.class);
		User user = ac.getBean(User.class);
		user.run();
	}
}
测试结果 :
UserAspect.before
User run()方法...
UserAspect.after

项目中AOP思想的使用

  • 权限管理的拦截器

  • 日志处理

  • 等等

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值