什么是面向切面编程
在软件开发中,散布于应用中多处的功能被称为横切关注点
这些横切关注点从概念上讲是与应用的业务逻辑相分离的,但是实际情况是,这些横切关注点往往嵌入到应用的业务逻辑中
把这些横切关注点与业务逻辑互相分离正是面向切面编程所要解决的问题
切面能帮助我们模块化横切关注点。简单而言,横切关注点可以被描述为影响应用多处的功能。
image图展示了一个被划分为模块的典型应用。每个模块的核心功能都有其特定的业务,但是这些模块都会需要一些辅助功能,如安全、日志。
如果要重用通用功能的话、常见的面向对象技术是继承和委托。但是如果整个应用都使用相同的基类,继承往往导致一个脆弱的对象体系、而是用委托可能需要丢委托对象进行复杂的调用
切面提供了取代继承和委托的另一种可选方案,而且在很多长期下更加清晰简洁。在使用面向切面编程时,我们仍然需要在一个地方定义通用功能,但是可以通过声明的方式定义这个功能要以何种方式应用在何处,而无需修改受影响的类。横切关注点可以被模块化为特殊的类,这些类被称为切面。这样做有两个好处:首先每个横切关注点都集中于一个地方,而不是分散到多处代码;其次服务模块更加简洁因为他们只有核心代码
AOP术语
通知Advice
在AOP术语中,切面的工作被称为通知。通知定义了切面是什么以及何时使用。Spring切面可以应用5种类型的通知:
- 前置通知: 在目标方法被调用之前调用通知功能
- 后置通知: 在目标方法完成之后调用通知,此时不会关心方法的输出是什么
- 返回通知:在目标方法成功执行之后调用通知
- 异常通知:在目标方法抛出异常后调用通知
- 环绕通知:通知包裹了被通知的方法,在被通知方法调用之前和调用之后执行自定义的行为
连接点
我们的应用有数以千计的时机应用通知,这些时机称为连接点。连接点是在应用执行过程中能够插入切面的一个点,这个点可以是方法调用时、方法抛出异常时甚至修改一个字段时。切面代码可以利用这些点插入到应用代码的正常逻辑之中、并添加新的行为
切点
如果说通知定义了切面是什么和何时的话、那么切点就定义了何处。切点的定义会匹配通知所要织入的一个或多个连接点。我们通常使用明确的类和方法名称、或是正则表达式定义匹配类和方法来制定切点、有些aop框架支持创建动态的切点、可以根据运行时的策略来决定是否应用通知
切面
切面是通知和切点的结合,通知和切点定义了切面的全部内容---它是什么、在何时和何处完成其功能
引入
引入允许我们向现有的类添加新方法或属性。
public interface Animal {
void eat();
}
@Component("women")
public class Women implements Person {
@Override
public void likePerson() {
System.out.println("我是女生,我喜欢帅哥");
}
}
public interface Animal {
void eat();
}
@Component
public class FemaleAnimal implements Animal {
@Override
public void eat() {
System.out.println("我是雌性,我比雄性更喜欢吃零食");
}
}
@Aspect
@Component
public class AspectConfig {
//"+"表示person的所有子类;defaultImpl 表示默认需要添加的新的类
@DeclareParents(value = "com.demo.introduce.Person+", defaultImpl = FemaleAnimal.class)
public Animal animal;
// Person 以及其子类都 可以算作是Animal的子类
}
@Configuration
@ComponentScan
@EnableAspectJAutoProxy
public class AnnotationConfig {
}
public class MainTest {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(AnnotationConfig.class);
Person person = (Person) ctx.getBean("women");
person.likePerson();
Animal animal = (Animal) person;
animal.eat();
}
}
打印结果
我是女生,我喜欢帅哥
我是雌性,我比雄性更喜欢吃零食
织入
织入是把切面应用到目标对象 并创建新的代理对象的过程。切面在指定的连接点织入到目标对象中,在目标对象的生命周期里有多个点可以进行织入
- 编译期。切面在目标类编译时被织入,这种方式需要特殊的编译器,aspectJ的织入编译器就是以这种方式织入切面
- 类加载期。切面在目标类加载到JVM时被织入,这种方式需要特殊的类加载器,aspectJ5的加载时织入就支持以这种方式织入切面
- 运行期。切面在应用运行的某个时刻被织入。一般情况下,在织入切面时,aop容器会为目标对象动态地创建一个代理对象。spring aop就是这种方式织入的
Spring 对aop的支持
Spring提供了4种类型的aop支持:
- 基于代理的经典Spring AOP
- 纯POJO切面
- @AspectJ注解驱动的切面
- 注入式Aspect切面
Spring AOP 构建在动态代理基础上,因此Spring对AOP的支持局限于方法的拦截
Spring通知是java编写的
Spring 在运行时通知对象
通过在代理类中包裹切面,Spring在运行时期把切面织入到Spring管理的bean
imageSpring只支持方法级别的连接点
因为Spring基于动态代理,所以Spring只支持方法连接点。Spring缺少对字段连接点、构造器连接点支持,无法让我们创建细粒度的通知。
网友评论