Spring面向切面编程(AOP)的简单实例 超、凢脫俗 2021-08-19 17:46 369阅读 0赞 AOP是Aspect Oriented Programming的简称,意思是面向切面编程。Spring AOP的实现是基于Java的代理机制,从JDK1.3开始就支持代理功能,但是性能成为一个很大问题,为了解决JDK代理性能问题,出现了CGLIB代理机制。它可以生成字节码,所以它的性能会高于JDK代理。Spring支持这两种代理方式。但是,随着JVM(Java虚拟机)的性能的不断提高,这两种代理性能的差距会越来越小。 **【实例】**给一个系统的业务逻辑方法添加业务日志功能。要求在其业务方法调用前记录日志,记录方法调用的时间,调用的业务方法名和调用的参数,运行效果如下: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbl9qdW5iaWFv_size_16_color_FFFFFF_t_70][] ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbl9qdW5iaWFv_size_16_color_FFFFFF_t_70 1][] ### (1)实现模拟业务系统 ### 仅模拟业务逻辑层的两个方法:register(用户注册)、comment(用户评论)。在使用Spring的时候,业务逻辑层也被称作“服务层”。 创建UserService.java用户信息业务逻辑接口。 package com.pjb.aop; /** * 用户信息业务逻辑接口 * @author pan_junbiao **/ public interface UserService { /** * 用户注册 */ public boolean register(String userName, String blogUrl, String sex); /** * 用户评论 */ public void comment(String userName,String comments); } 创建UserServiceImpl.java用户信息业务逻辑实现类。 package com.pjb.aop; /** * 用户信息业务逻辑实现类 * @author pan_junbiao **/ public class UserServiceImpl implements UserService { /** * 用户注册 */ @Override public boolean register(String userName, String blogUrl, String sex) { System.out.println("业务方法register开始执行:"); System.out.println("用户名称:"+userName); System.out.println("博客地址:"+blogUrl); System.out.println("用户性别:"+sex); System.out.println("业务方法register执行完成"); return true; } /** * 用户评论 */ @Override public void comment(String userName, String comments) { System.out.println("业务方法comment开始执行:"); System.out.println("用户名称:"+userName); System.out.println("评论内容:"+comments); System.out.println("业务方法comment执行完成"); } } ### (2)编写切面代码 ### 实现特定功能的切面代码在AOP概念中又称为“通知(Advice)”。通知分为前置通知、后置通知、环绕通知和异常通知。 这个分类是根据通知织入到业务代码时执行的时间划分的。前置通知是在方法执行前自动执行的通知;后置通知是在方法执行后自动执行的通知;环绕通知能力最强,它可以在方法调用前执行通知代码,可以决定是否还调用目标方法;异常通知是方法抛出异常时自动执行的切面代码。 前置通知:org.springframework.aop.MethodBeforeAdvice 后置通知:org.springframework.aop.AfterReturningAdvice 环绕通知:org.aopalliance.intercept.MethodInterceptor 异常通知:org.springframework.aop.ThrowsAdvice 这里使用前置通知。 package com.pjb.aop; import org.springframework.aop.MethodBeforeAdvice; import org.springframework.lang.Nullable; import java.lang.reflect.Method; import java.text.DateFormat; import java.text.SimpleDateFormat; import java.util.Arrays; import java.util.Date; /** * 日志通知 * @author pan_junbiao **/ public class LogAdvice implements MethodBeforeAdvice { @Override public void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable { DateFormat sdf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒"); System.out.println("\n[系统日志]"); System.out.println("执行时间:" + sdf.format(new Date())); System.out.println("方法名称:" + var1.getName()); System.out.println("执行参数:" + Arrays.toString(var2)); System.out.println("===================================================================="); } } 编写前置通知需要实现MethodBeforeAdvice接口,这个接口要求实现的方法是: public void before(Method var1, Object[] var2, @Nullable Object var3) throws Throwable; 参数Method var1是被通知目标方法对象;参数Object\[\] var2是传入被调用方法的参数;参数Object var3是被调用方法所属的对象实例。通过这些参数,我们几乎可以在方法代码中完成很多工作。 ### (3)将切面代码织入到业务对象中 ### 如果直接访问原来的Bean,通知代码肯定不会被执行。Spring采用“代理”的方法将通知织入到原Bean中。Spring将原Bean和通知都封装到org.springframework.aop.framework.ProxyFactoryBean类别中。用户通过访问代理类访问原Bean,这样就能保证在目标方法调用前先执行前置通知的代码了。 无需编写一行程序代码,只需要通过配置完成织入的过程即可,配置工作仍然是在Spring配置文件中完成的。 在项目中添加Spring的jar包。 **如果使用Maven,则pom.xml文件配置如下:** <!-- Spring框架 --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>5.2.3.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.2.3.RELEASE</version> </dependency> 在src目录下创建applicationContext.xml配置文件。 <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd"> <!-- Spring配置文件 --> <beans> <bean id="userServiceTarget" class="com.pjb.aop.UserServiceImpl"/> <bean id="logAdvice" class="com.pjb.aop.LogAdvice"/> <!-- 定义代理类 --> <bean id="userService" class="org.springframework.aop.framework.ProxyFactoryBean"> <!-- 被代理的接口 --> <property name="proxyInterfaces"> <value>com.pjb.aop.UserService</value> </property> <!-- 织入的通知列表 --> <property name="interceptorNames"> <list> <value>logAdvice</value> </list> </property> <!-- 被代理的原Bean --> <property name="target" ref="userServiceTarget"/> </bean> </beans> 首先定义了原Bean“userServiceTarget”和“logAdvice”。然后定义代理类,名称为userService,我们将通过这个Bean访问业务方法。代理类有3个必须设置的属性:proxyInterfaces,表示被代理的接口;interceptorNames表示织入的通知列表;target表示被代理的原Bean。 ### (4)运行测试 ### 创建AopTest.java类,编写测试代码。 package com.pjb.aop; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; /** * 运行测试 * @author pan_junbiao **/ public class AopTest { public static void main(String[] args) { //装载配置文件 ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml"); //获取UserService的代理类 UserService userService = (UserService)context.getBean("userService"); //调用注册方法 userService.register("pan_junbiao的博客","https://blog.csdn.net/pan_junbiao","男"); //调用用户评论方法 userService.comment("pan_junbiao的博客","您好,欢迎访问 pan_junbiao的博客!"); } } [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbl9qdW5iaWFv_size_16_color_FFFFFF_t_70]: /images/20210724/7306134d889747f3b73a6e83557a4e50.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3Bhbl9qdW5iaWFv_size_16_color_FFFFFF_t_70 1]: /images/20210724/fa36ce89badf40aca3c2caf245cd9113.png
还没有评论,来说两句吧...