一.AOP介绍
1.概念
AOP(Aspect Oriented Programming):面向切面编程,是一种编程思想
目标对象:增强方法对应的类对象(通常用target)
切面类:带有@Aspect注解,实现增强功能的类
连接点:目标方法和增强方法的桥梁,里面存储着目标方法的信息(方法名、方法参数、目标对象)
2.优势
不用改变原有代码,在原有代码上增加功能,主要使用在与业务无关的公共代码
二.AOP使用
1.在pom.xml中添加AOP依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
2.创建切面类(带有@Aspect注解配合@Component注解)
@Aspect
@Component
public class TimeAspect{}
3.编写指定通知类型和切点表达式,告诉AOP哪些方法需要切入
@Aspect
@Component
public class TimeAspect{
//com.gok.javaee.aop.UserService类下,任意参数、任意返回值、任意方法名的方法都切入
@Around("execution(* com.gok.javaee.aop.UserService.*(..))")
}
4.编写增强方法
@Aspect
@Component
public class TimeAspect {
@Around("execution(* com.gok.javaee.aop.UserService.*(..))")
public Object getTime(ProceedingJoinPoint joinPoint) throws Throwable {
long begin = System.currentTimeMillis();
//执行被切入的方法
Object res = joinPoint.proceed(joinPoint.getArgs());
System.out.println("执行时间为:" + (System.currentTimeMillis() - begin));
return res;
}
}
三.AOP通知
1.概念
通知:增强方法在目标方法的哪个位置执行
2.类型
(1).环绕通知
@Around:增强方法把目标方法包围
(2).前置通知
@Before:增强方法在目标方法之前执行
(3).后置通知
@After:增强方法在目标方法之后执行
(4).后置异常
@AfterThrowing:增强方法在目标方法出现异常后执行
(5).返回通知
@AfterReturning:增强方法在目标方法返回后执行
3.通知执行顺序
正常:前置通知-->目标方法-->返回通知-->后置通知
异常:前置通知-->目标方法-->异常通知-->后置通知
四.AOP切点表达式
指定哪些方法需要切入
1.execution(方法级匹配)
execution(com.gok.User com.gok.service.UserService.remove(Integer))
com.gok.User:代表返回值,可用*代表所有返回值
com.gok.service:包名,可用*代表任何包名,..代表子孙包
UserService:类名,可用*代表任何类名
remove:方法名,可用*代表任何方法名
Integer:参数,可用..代表任何参数
2.within(类级匹配)
//匹配com.gok.aop.UserService类的所有方法
within(com.gok.aop.UserService)
3.@within(类注解匹配)
//匹配带有@Service注解类的所有方法
@within(org.springframework.stereotype.Service)
4.@annotation(方法注解匹配)
//匹配带有@Log注解的方法
@annotation(com.gok.Log)
5.混合表达式
通过逻辑运算符(与或非)连接表达式
//匹配带有@Service注解类的所有方法,除了toString方法
@within(org.springframework.stereotype.Service) && !execution(String toString())
五.AOP原理
1.原理:
AOP基于动态代理,使用代理对象来增强目标对象,可以在不修改目标对象的前提下,拓展目标对象的功能,将核心业务代码和非核心公共代码分离解耦,提高代码可维护性,降低代码复杂度
2.图解:现实中租房的中介
3.动态代理:
(1).JDK动态代理(通过实现接口,生成代理类)
//ILandlord.java 房东接口
public interface ILandlord {
void signContract();
}
//Landlord.java 房东
public class Landlord implements ILandlord {
@Override
public void signContract() {
System.out.println("签合同");
}
}
//Main.java
public static void main(String[] args) {
//创建目标对象
Landlord landlord = new Landlord();
//创建代理对象
ILandlord landlordProxy = (ILandlord) Proxy.newProxyInstance(
Landlord.class.getClassLoader(), Landlord.class.getInterfaces(),
new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("带租客看房");
//调用目标方法
return method.invoke(landlord, args);
}
});
landlordProxy.signContract();
}
缺点:必须实现接口的目标对象,才能使用JDK动态代理
(2).CGLIB动态代理(通过继承,生成代理类)
SpringBoot默认使用CGLIB动态代理实现AOP
//Landlord.java 房东
public class Landlord {
public void signContract() {
System.out.println("签合同");
}
}
//Main.java
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
//设置父类
enhancer.setSuperclass(Landlord.class);
//设置增强方法
enhancer.setCallback(new MethodInterceptor() {
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("带租客看房");
return proxy.invokeSuper(obj, args);
}
});
Landlord landlord = (Landlord) enhancer.create();
landlord.signContract();
}
(3).JDK动态代理和CGLIB动态代理区别
1.实现方式不同,JDK使用接口实现,CGLIB使用继承实现
2.局限性:
JDK动态代理必须实现接口
CGLIB动态代理目标类不能有final,方法不能为private