AOP介绍及优缺点
AOP(面向切面编程)是一种编程范式,它允许开发者在不修改现有代码的情况下,通过定义“切面”来横切多个模块,从而实现对这些模块的某些行为进行统一管理和控制。 AOP的核心概念包括切面(Aspect)、连接点(Join point)、通知(Advice)和切点(Pointcut)。
横切关注点:跨越应用程序多个模块的方法和功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。例如,事务管理、日志记录、权限控制、性能监控等都可以看作是横切关注点。
- 切面:横切关注点 被模块化 的特殊对象。即,它是一个类。
- 连接点:在程序执行过程中某个特定的点,如方法调用或异常处理时。在Spring AOP中,一个连接点总是代表一个方法的执行。
- 通知:在切面的某个特定的连接点上执行的动作。通知有多种类型,包括“around”、“before”和“after”等,它们定义了在连接点上应该执行的具体操作。
- 切点:匹配连接点的断言。通知和切点表达式相关联,并在满足这个切点的连接点上运行。
AOP的主要优点包括:
- 代码模块化:将横切关注点从业务逻辑中分离,使代码结构更清晰,模块划分更合理。例如,将日志记录、权限控制等与业务无关的功能独立出来,业务代码更专注于核心业务。
- 提高代码复用性:横切关注点的代码可以在多个地方重复使用,无需在每个相关位置重复编写。比如,一个通用的事务处理逻辑可以应用于多个业务方法。
- 增强可维护性:当横切关注点的逻辑需要修改时,只需在切面中进行修改,而不必在众多的业务方法中逐一修改。
AOP 技术的缺点:
- 增加复杂性:对于初次接触和理解的开发者来说,AOP 的概念和实现机制可能较为复杂,增加了学习成本。
- 调试困难:由于横切逻辑的织入是在运行时动态进行的,可能会增加调试的难度,特别是在出现问题时定位和排查故障。
aop的实现原理
AOP的实现原理通常依赖于动态代理技术。动态代理技术可以分为两类:基于接口的动态代理和基于类的动态代理。
-
基于接口的动态代理:通过Java的java.lang.reflect.Proxy类和InvocationHandler接口实现。
-
基于类的动态代理:通常使用第三方库,如CGLIB,它通过继承的方式在运行时创建代理类。
AOP利用动态代理的设计模式。
AOP底层默认jdk动态代理,如果没有接口使用cglib动态代理。
应用场景
常用于日志记录,性能统计,安全控制,事务处理,异常处理等等。
代理模式的分类
为什么要学习代理模式?因为这就是SpringAop的底层!
静态代理
角色分析:
- 抽象角色:一般会使用接口或者抽象类来解决
- 真实角色:被代理的角色
- 代理角色:代理真实角色,代理真实角色后,我们一般会做一些附属操作
- 客户:访问代理对象的人
package com.lx.pojo;
// 租房
public interface Rent {
// 出租
void rent();
}
package com.lx.pojo;
// 房东,往外租房子
public class Landlord implements Rent {
@Override
public void rent() {
System.out.println("房东要出租房子");
}
}
// 到这儿,我们就找到房子了
// 然而现实情况是很难直接找到房东,通常都是通过中介来租房子
package com.lx.pojo;
public class Client {
public static void main(String[] args) {
Landlord landlord = new Landlord();
// 房东要出租房子
landlord.rent();
}
}
package com.lx.pojo;
public class Client {
public static void main(String[] args) {
Landlord landlord = new Landlord();
Proxy proxy = new Proxy(landlord);
// 通过代理找到 房东要出租房子
// 代理,中介帮房东租房子,但是代理角色一般会有一些附属操作
proxy.rent();
}
}
// 通过代理不仅可以帮房东租房子,还可以做很多其他的附属操作
package com.lx.pojo;
public class Proxy implements Rent {
private Landlord landlord;
public Proxy() {
}
public Proxy(Landlord landlord) {
this.landlord = landlord;
}
@Override
public void rent() {
seeHouse();
landlord.rent();
fare();
signContract();
}
// 看房
// 这个只有中介能做,房东只有几套房子,而中介会有很多房子
public void seeHouse(){
System.out.println("中介带你看房");
}
// 收中介费
public void fare(){
System.out.println("收中介费");
}
// 签租赁合同
public void signContract(){
System.out.println("签合同");
}
}
代理模式的好处:
- 可以使真实角色的操作更加纯粹!不用去关注一些公共的业务
- 公共业务就交给代理角色!实现了业务的分工
- 公共业务发生拓展的时候,方便集中管理
缺点:
- 一个真实角色就会产生一个代理角色。代码量会翻倍,开发效率会变低
静态代理的第二个例子
package com.lx.service;
public interface UserService {
void add();
void delete();
void update();
void query();
}
package com.lx.service;
public class UserServiceImpl implements UserService{
@Override
public void add() {
System.out.println("增加了一个用户");
}
@Override
public void delete() {
System.out.println("删除了一个用户");
}
@Override
public void update() {
System.out.println("修改了一个用户");
}
@Override
public void query() {
System.out.println("查询了一个用户");
}
}
package com.lx.service;
import java.time.LocalDateTime;
public class UserServiceProxy implements UserService{
private UserServiceImpl userServiceImpl;
public UserServiceProxy() {
}
public UserServiceProxy(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
@Override
public void add() {
getLocalDateTime();
userServiceImpl.add();
}
@Override
public void delete() {
getLocalDateTime();
userServiceImpl.delete();
}
@Override
public void update() {
getLocalDateTime();
userServiceImpl.update();
}
@Override
public void query() {
getLocalDateTime();
userServiceImpl.query();
}
// 现在增加一个日志功能,检测方法是什么时间开始执行的
public void getLocalDateTime(){
System.out.println(LocalDateTime.now());
}
}
// 如果要增加日志功能可以在impl中直接添加代码
// 但是改动原有的业务代码,在公司中是大忌
// 我们就可以用代理来实现
package com.lx.pojo;
import com.lx.service.UserServiceImpl;
import com