Spring面向切面编程(AOP)的简单实例

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接: https://blog.csdn.net/pan_junbiao/article/details/101535889

AOP是Aspect Oriented Programming的简称,意思是面向切面编程。Spring AOP的实现是基于Java的代理机制,从JDK1.3开始就支持代理功能,但是性能成为一个很大问题,为了解决JDK代理性能问题,出现了CGLIB代理机制。它可以生成字节码,所以它的性能会高于JDK代理。Spring支持这两种代理方式。但是,随着JVM(Java虚拟机)的性能的不断提高,这两种代理性能的差距会越来越小。

【实例】给一个系统的业务逻辑方法添加业务日志功能。要求在其业务方法调用前记录日志,记录方法调用的时间,调用的业务方法名和调用的参数,运行效果如下:

(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)”。通知分为前置通知、后置通知、环绕通知和异常通知。

这个分类是根据通知织入到业务代码时执行的时间划分的。前置通知是在方法执行前自动执行的通知;后置通知是在方法执行后自动执行的通知;环绕通知能力最强,它可以在方法调用前执行通知代码,可以决定是否还调用目标方法;异常通知是方法抛出异常时自动执行的切面代码。

这里使用前置通知。

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包和commons-logging.jar文件。

在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的博客!");
    }
}

猜你喜欢

转载自blog.csdn.net/pan_junbiao/article/details/101535889
今日推荐