Spring AOP介绍和JDK的动态代理

1.什么是AOP

       AOP的全称是Aspect-Oriented Programming,即面向切面的编程(面向方面编程)。它是面向对象编程(OOP)的一种补充,目前已成为一种比较成熟的编程方式。

       在传统的业务处理代码中,通常都会进行事务处理、日志记录等操作。虽然使用OOP可以通过组合或者继承的方式来达到代码的重用,但如果要实现某个功能(如日志记录),同样的代码仍然会分散到各个方法中。这样,如果想要关闭某个功能,或者对其进行修改,就必须要修改所有的相关方法。这不但增加了开发人员的工作量,而且提高了代码的出错率。

       为了解决这一问题,AOP思想随之产生。AOP采取横向抽取机制,将分散在各个方法中的重复代码提取出来,然后在程序编译或运行时,再将这些提取出来的代码应用到需要执行的地方。这种采用横向抽取机制的方式,采用传统的OOP思想显然是无法办到的,因为OOP只能实现父子关系的纵向的重用。虽然AOP是一种新的编程思想,但却不是OOP的替代品,它只是OOP的延伸和补充。

       AOP 的使用,使开发人员在编写业务逻辑时可以专心于核心业务,而不用过多地关注于其他业务逻辑的实现,这不但提高了开发效率,而且增强了代码的可维护性。
       目前最流行的AOP框架有两个,分别为Spring AOP和AspectJ。Spring AOP使用纯Java实现,不需要专门的编译过程和类加载器,在运行期间通过代理方式向目标类织入增强的代码。AspectJ是一个基于Java语言的AOP框架,从Spring 2.0开始,Spring AOP引入了对AspectJ的支持,AspectJ扩展了Java语言,提供了一个专门的编译器,在编译时提供横向代码的织入。

2.AOP术语

Aspect(切面):在实际应用中,切面通常是指封装的用于横向插入系统功能(如事务、日志等)的类,该类要被Spring容器识别为切面,需要在配置文件中通过<bean>元素指定。

Joinpoint(连接点):在程序执行过程中的某个阶段点,它实际上是对象的一个操作,例如方法的调用或异常的抛出。在Spring AOP中,连接点就是指方法的调用。

Pointcut(切入点):是指切面与程序流程的交叉点,即那些需要处理的连接点。通常在程序中,切入点指的是类或者方法名,如某个通知要应用到所有以add开头的方法中,那么所有满足这一规则的方法都是切入点。

Advice( 通知/增强处理 ):AOP 框架在特定的切入点执行的增强处理,即在定义好的切入点处所要执行的程序代码。可以将其理解为切面类中的方法,它是切面的具体实现。

Target Object(目标对象):是指所有被通知的对象,也称为被增强对象。如果AOP框架采用的是动态的AOP实现,那么该对象就是一个被代理对象。

Proxy(代理):将通知应用到目标对象之后,被动态创建的对象。

Weaving(织入):将切面代码插入到目标对象上,从而生成代理对象的过程。

3.JDK的动态代理

1.创建一个web项目,导入Spring框架所需的JAR包到项目lib目录中,并发布到类路径下。

2.在src目录下,创建一个包(例如我创建的叫yjf.jdk),在该包下创建接口UserDao.java

package yjf.jdk;

public interface UserDao {
	public void addUser();
	public void deleteUser();
}

3.在yjf.jdk包中,创建UserDao接口实现的类UserDaoImpl.java,分别实现接口中的方法

package yjf.jdk;

public class UserDaoImpl implements UserDao {
	public void addUser(){
		System.out.println("添加用户");
	}
	public void deleteUser(){
		System.out.println("删除用户");
	}
		
}

4.在src目录下创建另一个包(例如我创建的yjf.aspect包),并在该包下创建切面类MyAspect.java,在该类中定义一个模拟权限的检查方法和一个模拟记录日志的方法,这两个方法就是切面中的通知

package yjf.aspect;

public class MyAspect {
	public void check_Permissions(){
		System.out.println("模拟检查权限......");
	}
	public void log(){
		System.out.println("模拟记录日志......");
	}
}

5.在yjf.jdk包下,创建代理类JdkProxy,该类需要实现InvocationHandler接口,并编写代理方法。在代理方法中,需要通过Proxy类实现动态代理

package yjf.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import yjf.aspect.*;
/* JDK代理类 */
public class JdkProxy implements InvocationHandler {
	//声明目标类接口
	private UserDao userDao;
	//创建代理方法
	public Object createProxy(UserDao userDao){
		this.userDao = userDao;
		//1.类加载器
		ClassLoader classLoader = JdkProxy.class.getClassLoader();
		//2.被代理对象实现的所有接口
		Class[] clazz = userDao.getClass().getInterfaces();
		//3.使用代理类,进行增强,返回的是代理后的对象
		return Proxy.newProxyInstance(classLoader, clazz, this);
	}
	/*
	 * 所有动态代理类的方法调用,都会交由invoke()方法去处理
	 * proxy被代理后的对象
	 * method 将要被执行的方法信息(反射)
	 * asgs 执行方法时需要的参数
	 * 
	 * */
	@Override
	public Object invoke(Object proxy,Method method,Object[] args)throws Throwable{
		//声明切面
		MyAspect myAspect = new MyAspect();
		//前增强
		myAspect.check_Permissions();
		//在目标类上调用方法,并传入参数
		Object obj = method.invoke(userDao, args);
		//侯增强
		myAspect.log();
		return obj;
	}

}

6.在yjf.jdk包中,创建测试类JdkTest,该类中创建代理对象和目标对象,然后从代理对象中获得对目标对象userDao增强后的对象,最后调用该对象中的增加和删除方法

package yjf.jdk;

public class JdkTest {
	public static void main(String[] args){
		//创建代理对象
		JdkProxy jdkProxy = new JdkProxy();
		//创建目标对象
		UserDao userDao = new UserDaoImpl();
		//从代理对象中获取增强后的目标对象
		UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);
		//执行方法
		userDao1.addUser();
		userDao1.deleteUser();
	}
}

运行结果为:

模拟检查权限......
添加用户
模拟记录日志......
模拟检查权限......
删除用户
模拟记录日志......

可以看出,userDao实例中的添加用户和删除用户的方法已被成功调用,并且在调用前后分别增加了检查权限和记录日志权限的功能。这就是JDK动态代理。

上一篇:SSM整合(IDEA版)


下一篇:【Spring】用例子来初次理解IoC