AOP
参照《Spring思维导图,让Spring不再难懂(aop篇)》
AOP(Aspect-OrientedProgramming,面向方面编程),可以说是OOP( -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.
<? version="1.0" encoding="UTF-8"?><beans ns="http://www.spring work.org/schema/beans" ns:xsi="http://www.w3.org/2001/ Schema-instance" ns:context="http://www.spring work.org/schema/context" ns:aop="http://www.spring work.org/schema/aop" xsi:schemaLocation="http://www.spring work.org/schema/beans http://www.spring work.org/schema/beans/spring-beans.xsd http://www.spring work.org/schema/context http://www.spring work.org/schema/context/spring-context-4.0.xsd http://www.spring work.org/schema/aop http://www.spring work.org/schema/aop/spring-aop-4.0.xsd"> <context:component-scan -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.spring work.stereotype.Component;@Component@Aspectpublic 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.spring work.stereotype.Service;@Servicepublic class UserService { public void save() { System.out.println("调用Dao层保存用户信息"); }}Test.java
package com.neuedu.aspect;import org.spring work.context.ApplicationContext;import org.spring work.context.support.ClassPath ApplicationContext;import com.neuedu.service.UserService;public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPath ApplicationContext("applicationContext. "); UserService us = ac.getBean(UserService.class); us.save(); } }输出:
修改代码:
UserService.java
package com.neuedu.service;import org.spring work.stereotype.Service;@Servicepublic 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.spring work.stereotype.Component;@Component@Aspectpublic class LoggingAspect { @Before("execution(public void com.neuedu.service.UserService.*(..))") //修改代码 //执行这个注解之后,下面方法就会在一些方法执行之前执行 public void before(){ System.out.print("方法执行之前..."); } }Test.java
package com.neuedu.aspect;import org.spring work.context.ApplicationContext;import org.spring work.context.support.ClassPath ApplicationContext;import com.neuedu.service.UserService;public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPath ApplicationContext("applicationContext. "); UserService us = ac.getBean(UserService.class); //us.save(); us.delete("zhang"); //修改代码 } }输出:
修改代码:新建 GoodsServiec.java
package com.neuedu.service;import org.spring work.stereotype.Service;@Servicepublic class GoodsService { public void save() { System.out.println("保存商品信息..."); }}Test.java
package com.neuedu.aspect;import org.spring work.context.ApplicationContext;import org.spring work.context.support.ClassPath ApplicationContext;import com.neuedu.service.GoodsService;import com.neuedu.service.UserService;public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPath ApplicationContext("applicationContext. "); 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.spring work.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.spring work.stereotype.Service;@Servicepublic 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.spring work.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.spring work.context.ApplicationContext;import org.spring work.context.support.ClassPath ApplicationContext;import com.neuedu.service.GoodsService;import com.neuedu.service.UserService; public class Test { public static void main(String[] args) { ApplicationContext ac = new ClassPath ApplicationContext("applicationContext. "); 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.*.*(..))")
//执行这个注解之后,下面方法只有当方法抛出异常时才会执行
继续阅读与本文标签相同的文章
初识 Spring(06)---(MVC原理)
初识 Spring(02)---(IOC)
-
C#中用WMI实现对驱动的查询
2026-05-26栏目: 教程
-
WMI_COM_API
2026-05-26栏目: 教程
-
C#窗体实现打开关闭VM虚拟机
2026-05-26栏目: 教程
-
C#控制台打开VM虚拟机
2026-05-26栏目: 教程
-
C语言调用VIX_API开关虚拟机
2026-05-26栏目: 教程
