初识 Spring(07)---(AOP)

本文涉及的产品
日志服务 SLS,月写入数据量 50GB 1个月
简介: AOP参照《Spring思维导图,让Spring不再难懂(aop篇)》AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。

AOP

参照《Spring思维导图,让Spring不再难懂(aop篇)》

AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP(Object-Oriented Programing,面向对象编程)的补充和完善。

面向对象的特点是继承、多态和封装。而封装就要求将功能分散到不同的对象中去,这在软件设计中往往称为职责分配。实际上也就是说,让不同的类设计不同的方法。这样代码就分散到一个个的类中去了。这样做的好处是降低了代码的复杂程度,使类可重用。      

但是人们也发现,在分散代码的同时,也增加了代码的重复性。比如说,我们在两个类中,可能都需要在每个方法中做日志。按面向对象的设计方法,我们就必须在两个类的方法中都加入日志的内容。也许他们是完全相同的,但就是因为面向对象的设计让类与类之间无法联系,而不能将这些重复的代码统一起来。    

也许有人会说,那好办啊,我们可以将这段代码写在一个独立的类独立的方法里,然后再在这两个类中调用。但是,这样一来,这两个类跟我们上面提到的独立的类就有耦合了,它的改变会影响这两个类。那么,有没有什么办法,能让我们在需要的时候,随意地加入代码呢?

这种在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。      

一般而言,我们管切入到指定类指定方法的代码片段称为切面,而切入到哪些类、哪些方法则叫切入点。有了AOP,我们就可以把几个类共有的代码,抽取到一个切片中,等到需要时再切入对象中去,从而改变其原有的行为。这样看来,AOP其实只是OOP的补充而已。OOP从横向上区分出一个个的类来,而AOP则从纵向上向对象中加入特定的代码。有了AOP,OOP变得立体了。如果加上时间维度,AOP使OOP由原来的二维变为三维了,由平面变成立体了。从技术上来说,AOP基本上是通过代理机制实现的。      AOP在编程历史上可以说是里程碑式的,对OOP编程是一种十分有益的补充。

OOP允许你定义从上到下的关系,但并不适合定义从左到右的关系。例如日志功能。日志代码往往水平地散布在所有对象层次中,而与它所散布到的对象的核心功能毫无关系。这种散布在各处的无关的代码被称为横切(cross-cutting)代码,

在OOP设计中,它导致了大量代码的重复,而不利于各个模块的重用。

而AOP技术则允许你定义从左到右的关系,它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即方面。所谓“方面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

描述AOP常用的一些术语有通知(Adivce)、切点(Pointcut)、连接点(Join point)、切面(Aspect)、引入(Introduction)、织入(Weaving)、通知(Advice)等。

aop使用场景

aop框架种类

  • AspectJ

  • JBoss AOP

  • Spring AOP

使用aop可以做的事情有很多。

  • 性能监控,在方法调用前后记录调用时间,方法执行太长或超时报警。

  • 缓存代理,缓存某方法的返回值,下次执行该方法时,直接从缓存里获取。

  • 软件破解,使用AOP修改软件的验证类的判断逻辑。

  • 记录日志,在方法执行前后记录系统日志。

  • 工作流系统,工作流系统需要将业务代码和流程引擎代码混合在一起执行,那么我们可以使用AOP将其分离,并动态挂接业务。

  • 权限验证,方法执行前验证是否有权限执行当前方法,没有则抛出没有权限执行异常,由业务代码捕捉。

观察一下传统编码方式与使用aop的区别

两种动态代理方式

1:JDK 通过proxy对象和InvocationHandler对象
2:CGlib 通过操纵二进制字节码的方式产生动态代理

Spring默认采取的动态代理机制实现AOP,当动态代理不可用时(代理类无接口)会使用CGlib机制。

Spring提供了两种方式来生成代理对象: JDKProxy和Cglib,具体使用哪种方式生成由AopProxyFactory根据AdvisedSupport对象的配置来决定。默认的策略是如果目标类是接口,则使用JDK动态代理技术,否则使用Cglib来生成代理。

JDK动态代理

  • JDK动态代理主要涉及到java.lang.reflect包中的两个类:Proxy和InvocationHandler。InvocationHandler是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动态将横切逻辑和业务逻辑编制在一起。

  • Proxy利用InvocationHandler动态创建一个符合某一接口的实例,生成目标类的代理对象。

CGLib动态代理

  • CGLib全称为Code Generation Library,是一个强大的高性能,高质量的代码生成类库,可以在运行期扩展Java类与实现Java接口,CGLib封装了asm,可以再运行期动态生成新的class。和JDK动态代理相比较:JDK创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通过接口定义业务方法的类,则可以通过CGLib创建动态代理。

构建项目

文件目录

applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

		<context:component-scan base-package="com.neuedu"></context:component-scan>
		<!-- 自动生成代理类 -->
		<aop:aspectj-autoproxy>
			
		</aop:aspectj-autoproxy>
	
</beans>

LoggingAspect.java

package com.neuedu.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {
	@Before("execution(public void com.neuedu.service.UserService.save())")
	//执行这个注解之后,下面方法就会在一些方法执行之前执行
	public void before(){
		System.out.print("方法执行之前...");
	}
 
}

UserService.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
	public void save() {
		System.out.println("调用Dao层保存用户信息");
	}
}

Test.java

package com.neuedu.aspect;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neuedu.service.UserService;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		us.save();
	} 
}

输出:

修改代码:

UserService.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class UserService {
	public void save() {
		System.out.println("调用Dao层保存用户信息");
	}
	
	public void delete(String username){           //添加代码
		System.out.println("删除" + username);      //添加代码
	}

}

LoggingAspect.java

package com.neuedu.aspect;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect
public class LoggingAspect {
	@Before("execution(public void com.neuedu.service.UserService.*(..))")  //修改代码
	//执行这个注解之后,下面方法就会在一些方法执行之前执行
	public void before(){
		System.out.print("方法执行之前...");
	}
 
}

Test.java

package com.neuedu.aspect;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neuedu.service.UserService;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		//us.save();
		us.delete("zhang");  //修改代码
	} 
}

输出:

修改代码:新建 GoodsServiec.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class GoodsService {
	public void save() {
		System.out.println("保存商品信息...");
	}
}

Test.java

package com.neuedu.aspect;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neuedu.service.GoodsService;
import com.neuedu.service.UserService;

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		us.save();
		//us.delete("zhang");
		
		GoodsService gs = ac.getBean(GoodsService.class);  //新增代码
		gs.save();                                        //新增代码
	} 
}

输出:

@Aspect    //整个叫做切面类
public class LoggingAspect {
    @Before("execution(public void com.neuedu.service.UserService.*(..))")  //切入点语法 ,确定切入点位置,即所有类的所有方法(before 通知)
@Before("execution(* com.neuedu.service..*(..))")    //终版,扫描该包下的所有子包和所有类的所有方法
    public void before(){      //这个方法叫做切入点 
        System.out.print("方法执行之前...");
    }

修改代码:

LoggingAspect.java

package com.neuedu.aspect;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.stereotype.Component;

@Component
@Aspect  //切面类
public class LoggingAspect {
	@Before("execution(public void com.neuedu.service.*.*(..))")
	//执行这个注解之后,下面方法就会在一些方法执行之前执行
	public void before(){
		System.out.print("方法执行之前...");
	}
    @After("execution(public void com.neuedu.service.*.*(..))")      //新增代码
    public void after() {
    	System.out.println("执行方法之后...");
    }
    @AfterReturning("execution(public void com.neuedu.service.*.*(..))")  //新增代码
    public void afterReturning() {
    	System.out.println("只有方法正确执行之后才会执行...");
    }
    @AfterThrowing("execution(public void com.neuedu.service.*.*(..))")  //新增代码
    public void afterThrow() {
    	System.out.println("当方法抛出异常时调用的方法");
    }
}

UserServiceImpl.java

package com.neuedu.service;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{
	public void save() {
		System.out.println("调用Dao层保存用户信息");
	}
	
	public void delete(String username){
		System.out.println("删除" + username);
	}

	@Override
	public int div(int a, int b) {   //新增代码
		int c = a / b;
		return c;
	}
	
	
}

UserService.java

package com.neuedu.service;

import org.springframework.stereotype.Service;


public interface UserService {
	public void save() ;
	
	public void delete(String username);
	public int div(int a,int b);  //新增代码
}

Test.java

package com.neuedu.aspect;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.neuedu.service.GoodsService;
import com.neuedu.service.UserService; 

public class Test {
	public static void main(String[] args) {
		ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
		UserService us = ac.getBean(UserService.class);
		//us.save();
		//us.delete("zhang");
		
		GoodsService gs = ac.getBean(GoodsService.class);
		gs.save();
		us.div(2, 1);    //新增代码
		
	} 
}

输出:

@Before("execution(public void com.neuedu.service.*.*(..))")
    //执行这个注解之后,下面方法就会在一些方法执行之前执行


    @After("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法就会在一些方法执行之后执行

    @AfterReturning("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法只有方法正确执行之后才会执行

    @AfterThrowing("execution(public void com.neuedu.service.*.*(..))")

 //执行这个注解之后,下面方法只有当方法抛出异常时才会执行
 

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
20天前
|
存储 缓存 Java
Spring高手之路23——AOP触发机制与代理逻辑的执行
本篇文章深入解析了Spring AOP代理的触发机制和执行流程,从源码角度详细讲解了Bean如何被AOP代理,包括代理对象的创建、配置与执行逻辑,帮助读者全面掌握Spring AOP的核心技术。
28 3
Spring高手之路23——AOP触发机制与代理逻辑的执行
|
5天前
|
Java Spring
[Spring]aop的配置与使用
本文介绍了AOP(面向切面编程)的基本概念和核心思想。AOP是Spring框架的核心功能之一,通过动态代理在不修改原代码的情况下注入新功能。文章详细解释了连接点、切入点、通知、切面等关键概念,并列举了前置通知、后置通知、最终通知、异常通知和环绕通知五种通知类型。
14 1
|
2月前
|
设计模式 Java 测试技术
spring复习04,静态代理动态代理,AOP
这篇文章讲解了Java代理模式的相关知识,包括静态代理和动态代理(JDK动态代理和CGLIB),以及AOP(面向切面编程)的概念和在Spring框架中的应用。文章还提供了详细的示例代码,演示了如何使用Spring AOP进行方法增强和代理对象的创建。
spring复习04,静态代理动态代理,AOP
|
30天前
|
Java 编译器 Spring
Spring AOP 和 AspectJ 的区别
Spring AOP和AspectJ AOP都是面向切面编程(AOP)的实现,但它们在实现方式、灵活性、依赖性、性能和使用场景等方面存在显著区别。‌
49 2
|
1月前
|
Java Spring 容器
Spring IOC、AOP与事务管理底层原理及源码解析
【10月更文挑战第1天】Spring框架以其强大的控制反转(IOC)和面向切面编程(AOP)功能,成为Java企业级开发中的首选框架。本文将深入探讨Spring IOC和AOP的底层原理,并通过源码解析来揭示其实现机制。同时,我们还将探讨Spring事务管理的核心原理,并给出相应的源码示例。
117 9
|
29天前
|
XML Java 数据格式
Spring的IOC和AOP
Spring的IOC和AOP
43 0
|
2月前
|
Java 数据库连接 数据库
Spring基础3——AOP,事务管理
AOP简介、入门案例、工作流程、切入点表达式、环绕通知、通知获取参数或返回值或异常、事务管理
Spring基础3——AOP,事务管理
|
3月前
|
XML Java 数据格式
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
这篇文章是Spring5框架的AOP切面编程教程,通过XML配置方式,详细讲解了如何创建被增强类和增强类,如何在Spring配置文件中定义切入点和切面,以及如何将增强逻辑应用到具体方法上。文章通过具体的代码示例和测试结果,展示了使用XML配置实现AOP的过程,并强调了虽然注解开发更为便捷,但掌握XML配置也是非常重要的。
Spring5入门到实战------11、使用XML方式实现AOP切面编程。具体代码+讲解
|
3月前
|
缓存 Java 开发者
Spring高手之路22——AOP切面类的封装与解析
本篇文章深入解析了Spring AOP的工作机制,包括Advisor和TargetSource的构建与作用。通过详尽的源码分析和实际案例,帮助开发者全面理解AOP的核心技术,提升在实际项目中的应用能力。
42 0
Spring高手之路22——AOP切面类的封装与解析