AOP(面向切面)
AOP:Aspect Oriented Programming,面向切面编程。
Aop的优点:
1、降低模块之间的耦合度。
2、使系统更容易扩展。
3、更好的代码复用。
4、非业务代码更加集中,便于统一管理。
5、业务代码更加简洁。
AOP是面向对象编程的一个补充,在运行时,动态的将代码切入到制定方法、指定位置上的编程思想,就是面向切面编程。将不同方法的同一个位置抽象成一个切面对象,对该切面对象进行编程就是AOP。
代码示例:
首先我们需要先导入对应的jar包。
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.3.22</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>5.3.22</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>5.3.22</version> </dependency>
首先我们定义一个计算器接口,接口里边实现了加减乘除4个方法,并定义实现类,并且记录入参及出参。
package com.exambner.aop; public interface Cal { int add(int num1, int num2); int subtract(int num1, int num2); int multiply(int num1, int num2); int divide(int num1, int num2); }
package com.exambner.aop; import java.lang.reflect.InvocationHandler; public class CalImpl implements Cal { /** * 加法 * * @param num1 * @param num2 * @return */ @Override public int add(int num1, int num2) { int res = num1 + num2; return res; } /** * 减法 * * @param num1 * @param num2 * @return */ @Override public int subtract(int num1, int num2) { int res = num1 - num2; return res; } /** * 乘法 * * @param num1 * @param num2 * @return */ @Override public int multiply(int num1, int num2) { int res = num1 * num2; return res; } /** * 除法 * * @param num1 * @param num2 * @return */ @Override public int divide(int num1, int num2) { int res = num1 / num2; return res; } }
然后我们去调用它的方法。
public static void main(String[] args) { Cal cal = new CalImpl(); cal.add(1, 1); cal.subtract(5, 1); cal.multiply(3, 3); cal.divide(6, 2); }
输出结果:
add方法开始执行,参数为:1,1 add方法执行结果:2 subtract方法开始执行,参数为:5,1 add方法执行结果:4 multiply方法开始执行,参数为:3,3 add方法执行结果:9 divide方法开始执行,参数为:6,2 add方法执行结果:3
这时我们会发现每个方法都存在相同的代码,也就是入参和出参的打印,如果我们后续需要在添加方法时,可能还需要再加上信息打印,这时候就造成了代码的复用,并且提高了代码的耦合性。所以我们可以将这部分代码提取出来统一进行管理,这时就用到了AOP。
我们可以通过实现 InvocationHandler 接口来实现AOP的功能。
package com.exambner.aop; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.Arrays; public class MyInvocationHandler implements InvocationHandler { /** * 接收委托对象 */ private Object object; public Object getProxyInstance(Object object) { //赋值委托对象 this.object = object; //返回一个代理对象 // newProxyInstance,方法有三个参数: // loader: 用哪个类加载器去加载代理对象。 // interfaces:动态代理类需要实现的接口。动态代理类需要具备委托对象所有的功能。 // h:动态代理方法在执行时,会调用h里面的invoke方法去执行。也就是当前类中的invoke方法。 return Proxy.newProxyInstance(this.object.getClass().getClassLoader(), this.object.getClass().getInterfaces(), this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println(method.getName() + "方法开始执行,参数为:" + Arrays.toString(args)); //通过反射去调用类中的方法。这里传入委托对象,及调用方法所需的参数 Object o = method.invoke(this.object, args); System.out.println(method.getName() + "方法执行结果:" + o); return o; } }
更改Cal实现类,不再做输出操作,只保留纯粹的业务代码。
package com.exambner.aop; public class CalImpl implements Cal { /** * 加法 * * @param num1 * @param num2 * @return */ public int add(int num1, int num2) { int res = num1 + num2; return res; } /** * 减法 * * @param num1 * @param num2 * @return */ public int subtract(int num1, int num2) { int res = num1 - num2; return res; } /** * 乘法 * * @param num1 * @param num2 * @return */ public int multiply(int num1, int num2) { int res = num1 * num2; return res; } /** * 除法 * * @param num1 * @param num2 * @return */ public int divide(int num1, int num2) {int res = num1 / num2;return res; } }
通过AOP来执行方法的调用。
public static void main(String[] args) { //实例化委托对象 Cal cal = new CalImpl(); //创建AOP实例 MyInvocationHandler myInvocationHandler = new MyInvocationHandler(); //通过AOP实例创建代理对象 Cal proxyInstance = (Cal) myInvocationHandler.getProxyInstance(cal); //通过代理对象执行委托对象中的方法 proxyInstance.add(1, 2); proxyInstance.subtract(5, 3); proxyInstance.multiply(1, 3); proxyInstance.divide(4, 2); }
输出结果:
add方法开始执行,参数为:[1, 2] add方法执行结果:3 subtract方法开始执行,参数为:[5, 3] subtract方法执行结果:2 multiply方法开始执行,参数为:[1, 3] multiply方法执行结果:3 divide方法开始执行,参数为:[4, 2] divide方法执行结果:2
以上是通过动态代理的方式实现AOP的过程,比较复杂,且不好理解。
Spring对AOP进行了封装,使用Spring框架可以用面对对象的思想来实现AOP。
Spring中不需要创建 InvocationHandler ,只需要创建一个切面对象,将所有的非业务代码放在切面对象中实现。Spring框架底层会直接根据切面类和目标类来生成一个代理对象。
创建代理对象:
package com.exambner.aop; import org.aspectj.lang.JoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.*; import org.springframework.stereotype.Component; import java.util.Arrays; /** * Component:将切面类交由IOC容器进行管理 * Aspect:指定当前类为切面对象 */ @Component @Aspect public class LoggerAspect { /** * 在业务代码执行之前进行的操作 * 使用Before注解,切入CallImpl中所有的方法。使用*()通配符表示所有的方法;..通配符表示参数 * * @param joinPoint 切点 */ @Before(value = "execution(public int com.exambner.aop.CalImpl.*(..))") public void before(JoinPoint joinPoint) { //根据切点获取方法 Signature signature = joinPoint.getSignature(); //获取方法名称 String name = signature.getName(); //获取方法参数 Object[] args = joinPoint.getArgs(); System.out.println(name + "方法开始执行,参数为:" + Arrays.toString(args)); } /** * 业务代码执行完成后进行的操作 * * @param joinPoint 切点 */ @After(value = "execution(public int com.exambner.aop.CalImpl.*(..))") public void after(JoinPoint joinPoint) { //根据切点获取方法 Signature signature = joinPoint.getSignature(); //获取方法名称 String name = signature.getName(); System.out.println(name + "方法执行结束"); } /** * 业务代码返回结果后进行的操作 * * @param joinPoint 切点 * @param result 业务代码执行结果 */ @AfterReturning(value = "execution(public int com.exambner.aop.CalImpl.*(..))", returning = "result") public void afterReturning(JoinPoint joinPoint, Object result) { //根据切点获取方法 Signature signature = joinPoint.getSignature(); //获取方法名称 String name = signature.getName(); System.out.println(name + "方法执行结果是:" + result); } /** * 业务代码发生异常后进行的操作 * * @param joinPoint 切点 * @param exception 异常信息 */ @AfterThrowing(value = "execution(public int com.exambner.aop.CalImpl.*(..))", throwing = "exception") public void afterThrowing(JoinPoint joinPoint, Exception exception) { //根据切点获取方法 Signature signature = joinPoint.getSignature(); //获取方法名称 String name = signature.getName(); System.out.println(name + "方法抛出异常:" + exception); } }
将CalImpl交由IoC容器进行管理。
package com.exambner.aop; import org.springframework.stereotype.Component; /** * 将CalImpl交由IoC容器进行管理 */ @Component public class CalImpl implements Cal { /** * 加法 * * @param num1 * @param num2 * @return */ public int add(int num1, int num2) { int res = num1 + num2; return res; } /** * 减法 * * @param num1 * @param num2 * @return */ public int subtract(int num1, int num2) { int res = num1 - num2; return res; } /** * 乘法 * * @param num1 * @param num2 * @return */ public int multiply(int num1, int num2) { int res = num1 * num2; return res; } /** * 除法 * * @param num1 * @param num2 * @return */ public int divide(int num1, int num2) { int res = num1 / num2; return res; } }
配置文件中配置AOP。
<?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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd"> <!-- 自动扫描包 --> <context:component-scan base-package="com.exambner"></context:component-scan> <!-- 使Aspect注解生效 ,为目标类自动生成代理对象--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> </beans>
context:component-scan:对 com.exambner 包中的所有类进行扫描,如果该类同时添加了@Component注解,则将该类交由IoC容器进行管理。
aop:aspectj-autoproxy:让Spring框架结合切面类和目标类自动生成代理对象。
通过Spring框架封装的AOP执行方法的调用。
//解析配置文件 ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring-aop.xml"); //获取代理对象 Cal proxy = (Cal) applicationContext.getBean("calImpl"); proxy.add(1, 4); proxy.subtract(7, 4); proxy.multiply(4, 4); proxy.divide(6, 4);
add方法开始执行,参数为:[1, 4] add方法执行结果是:5 add方法执行结束 subtract方法开始执行,参数为:[7, 4] subtract方法执行结果是:3 subtract方法执行结束 multiply方法开始执行,参数为:[4, 4] multiply方法执行结果是:16 multiply方法执行结束 divide方法开始执行,参数为:[6, 4] divide方法执行结果是:1 divide方法执行结束

浙公网安备 33010602011771号