大家好,我是被白菜拱的猪。
一个热爱学习废寝忘食头悬梁锥刺股,痴迷于girl的潇洒从容淡然coding handsome boy。
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
通俗的讲就是不改变源代码的方式,在主干功能中添加新的功能,比如我们在实现登录功能时想来一个权限判断,是管理员还是普通用户。
AOP底层采用的是动态代理,什么是代理,就是我们把要做的事情交给比人去做,比如我们要租房子,我们就是被代理对象,中介就是代理对象。
动态代理是利用反射机制创建代理对象,动态代理分为两种:
为什么要通过反射呢?因为我们是在运行期间创建代理类,静态代理是是编译时创建,不利于程序的扩展,而且每个代理类只能为一个借口服务,这样在程序开发的过程中就会产生很多个代理类,那么我们最好通过一个代理类实现全部的代理功能。这就是动态代理。
package com.codingboy.spring5.test;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* @author: ljl
* @date: 2020/9/4 11:33
* @description: 动态代理举例
*/
//接口
interface Human{
void eat(String food);
//信仰
String getBelieve();
}
//实现类
class SuperMan implements Human {
@Override
public void eat(String food) {
System.out.println("我喜欢吃" + food);
}
@Override
public String getBelieve() {
return "good good study,day day up!";
}
}
/*
要实现动态代理,需要解决的问题?
问题一: 如何根据加载内存的被代理类,动态的创建代理类及其对象?
问题二: 当通过代理类调用方法时,如何动态的调用被代理类的同名方法?
*/
class ProxyFactory {
//调用此方法,返回一个代理类对象
public static Object getProxyInstance(Object obj) {
MyInvocationHandler handler = new MyInvocationHandler(obj);
//使用Proxy.newProxyInstance创建代理类对象,代理类和被代理类实现的是同一个接口
return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),handler);
}
}
//解决问题二
class MyInvocationHandler implements InvocationHandler {
private Object obj; //声明被代理类
//通过构造方法进行绑定
public MyInvocationHandler (Object obj) {
this.obj = obj;
}
//当代理类执行方法时,就会执行下面的方法,所以我们可以在里面写代理类执行的方法,这时候就需要声明一个被代理类
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在这里可以增强写法
System.out.println("执行方法前,增强......");
//通过反射执行被代理类中的方法
Object returnValue = method.invoke(obj,args);
System.out.println("执行方法后,增强......");
return returnValue;
}
}
public class ProxyTest {
public static void main(String[] args) {
//创建被代理对象
SuperMan superMan = new SuperMan();
//创建代理对象,注意这里是Human类型,接口类型
Human proxyInstance = (Human) ProxyFactory.getProxyInstance(superMan);
//通过代理对象执行被代理对象中的方法
proxyInstance.eat("北京烤鸭");
}
}
1、连接点
类中哪些方法可以增强,这些方法称为连接点
2、切入点
具体增强的方法称为切入点
3、通知
增强方法中的逻辑部分称为通知。
通知又分为如下几种:
前置通知、后置通知、环绕通知、异常通知、最终通知
4、切面
切面是一个动作,将通知应用于切入点的过程称为切面
AspectJ不是Spring的组成部分,他是一个独立的框架,一般把AspectJ和Spring一起使用来实现AOP的操作,假如不使用maven构建工程的话还要引入相关jar包。
除此之外,还要了解什么是切入点表达式,知道对哪个类里面的哪个类方法进行增强。
切入点表达式格式:execution([修饰符] 返回值类型 包名.类名.方法名(参数)),
修饰符默认为public可以不用写,返回值类型必须写*可以匹配所有的返回值类型。
示例一:
对com.coding.spring5中包中所有的类中的所有方法进行增强(…)是参数列表,不管一个参数还是多个参数
execution( * com.codingboy.spring5.*.*(..))
(2)使用注解@Component创建对象(即增强类和要增强的类)
(3)在增强类上加上@Aspect注解(注意选择的包是在aspectJ)
(4)在Spring配置文件中开启生成代理对象,让@Aspect生效
3.在增强类中配置不同的通知,在方法上使用不同的注解,以及切入点表达式来表明对哪个方法进行增强
package com.codingboy.spring5.aopannotation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;
/**
* @author: ljl
* @date: 2020/9/4 16:35
* @description: book的增强类
*/
@Aspect
@Component
public class BookProxy {
//前置通知
@Before(value = "execution(* com.codingboy.spring5.aopannotation.Book.*(..)")
public void before() {
System.out.println("前置通知 before...");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.codingboy.spring5.aopannotation.Book.*(..)")
public void afterReturning() {
System.out.println("后置通知(返回通知) afterReturning");
}
//环绕通知
@Around(value = "execution(* com.codingboy.spring5.aopannotation.Book.*(..)")
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕通知前 around...");
//被增强方法执行
proceedingJoinPoint.proceed();
System.out.println("环绕通知后 around...");
}
//最终通知
@After(value = "execution(* com.codingboy.spring5.aopannotation.Book.*(..)")
public void after() {
System.out.println("最终通知 after...");
}
//异常通知
@AfterThrowing(value = "execution(* com.codingboy.spring5.aopannotation.Book.*(..)")
public void afterThrowing() {
System.out.println("异常通知 afterThrowing...");
}
}
这是一个方法只被一个类拦截时通知执行的顺序
正常执行:
出现异常:
@Pointcut(value = "execution(* com.codingboy.spring5.aopannotation.Book.*(..)")
public void point() {
}
//前置通知
@Before(value = "point()")
public void before() {
System.out.println("前置通知 before...");
}
这句话,下面是配置类的内容
@Configuration
@ComponentScan(basePackages = {"com.codingboy.spring5"})
@EnableAspectJAutoProxy
public class AopConfig {
}
使用配置文件的形式在实际开发过程中使用的不多,但是还是要有所了解的。这里不再对各个标签进行描述,直接上代码:
一天的内容,学了学aop,让我收获最多的是动态代理,看视频看了两遍才搞清楚,学习这条路任重而道远啊,要学的东西是在太多了。以上还仅仅是基础,学完这一篇还要在在了解了解源码,什么时候上SpringBoot这条船还不得而知。
长春的风太大,吹乱了我的秀发…一个人在风中前行,孤独又增添了几分。