小菜鸟 SSM框架之 Spring复习-04(AOP-AspectJ-上 )

Spring AOP 简介

什么是AOP

AOP的全称是Aspect—Oriented Programing,即面向切面编程(也称面向方面编程)。他是面向对象编程(OOP)的一种扩充。

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 语言,提供了一个专门的编译器,再编译时提供横向代码的织入

AOP 术语
名称说明
Aspect(切面)在实际开发中,切面通常是指封装的用于横向插入系统功能的 类
joinpoint (连接点)程序执行过程中某个阶段点,他实际上是对象的一个操作,例如方法的调用或异常的抛出。Spring AOP中,就是指方法调用
Pointcut(切入点)切面与程序流程的交叉点,即那些需要处理的 连接点 。
Advice(通知/增强处理)AOP框架在待定的切入点要执行的增强处理,即定义好的要在切入点执行的代码。
Target Object(目标对象)指所有被通知的对象,也是被增强的对象。如果AOP框架才用的是动态AOP实现,那么该对象就是一个被代理的对象
Proxy(代理)将通知应用到目标对象之后,被动态创建的对象
Weaving (织入)将切面代码插入到目标对象上,从而生成代理对象的过程

动态代理

AOP 代理就是有AOP框架动态生成的一个对象,该对象可以作为目标对象使用。Spring 中的AOP代理,可以使JDK 动态代理,也可以是CGLIB代理

JDK 动态代理

JDK 动态代理是通过 java.lang.reflect.Proxy 类来实现的,我们可以通过 Proxy 类的 newProxyInstance() 方法来创建代理对象

对于使用业务接口的类,Spring默认会使用JDK动态代理来实现AOP。有接口实现的类,才能使用JDK动态代理。

例子来说话:

创建一个接口

package com.itheima.jdk;

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

创建接口的实现类

package com.itheima.jdk;

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

创建切面类

package com.itheima.jdk;

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

在该类中定义了一个模拟权限检查的方法、一个模拟记录日志的方法。这两个方法代表切面中的通知

创建代理类

package com.itheima.jdk;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JdkProxy implements InvocationHandler {
	//声明目标类接口
	private UserDao userDao;
	//创建代理对象方法
    //JDK 动态代理只能代理有接口的类是部分类 所以参数 userDao 要使用UserDao 类型 
         //而不能使用Object 类型
	public UserDao createProxy(UserDao userDao) {
		this.userDao = userDao;
		//1.创建类加载器 
			//一般都是当前类 :
					//  当前类.class.getClassLoader()  来加载类
			//如果是目标类的类加载器  要使用:
					//  目标类实例.getClass().getClassLoader()
			//JdkProxy 在不同的项目中是不同的,其位置的类名应该是:所在类的类名。
		ClassLoader classLoader = JdkProxy.class.getClassLoader(); 
						
		//2.被代理的对象实现的接口  可以是多个接口。返回值是一个数组
			//方式1   
				//目标类  目标类实例.getClass().getInterfaces()
			//方式2
				//new Class[]{UserDao.class}
				//例如: jdbc驱动 -->  DriveManager   获得接口 Connection
		Class<?>[] clazz = userDao.getClass().getInterfaces();
		//3.使用代理类进行增强,返回的是代理后的对象
					//this代指当前类  JdkProxy
		return (UserDao) Proxy.newProxyInstance(classLoader, clazz, this);
	}
	
	//所有动态代理类的方法调用, 都会交给invoke()方法去调用
	//proxy 被代理后的对象
	//method 将要被执行的方法信息
	//args 执行方法时需要的参数
	
	
	//invoke() 方法将目标类和切面类结合在一起。
	@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);
		System.out.println("正在执行方法:"+method.getName());
		//后增强
		myAspect.log();
		return obj;		
	}	
}

这只是一种方法,也可以使用匿名实现类来实现 InvocationHandler 接口类的Invoke 方法。这里不再详述,两种方法最好全部掌握

注解:

  • JdkProxy 类实现了 InvocationHandler 接口,并实现了接口中的 Invoke() 方法,所有动态代理所调用的方法都交给Invoke()方法去处理。在创建的代理方法 createProxy() 中,使用了Proxy 的 newProxyInstance() 方法来创建代理对象。该方法中包含了三个参数:第一个参数:当前类的加载类;第二个参数:被代理对象实现的多有接口;第三个参数:代理类JdkProxy 本身。

创建测试类

package com.itheima.jdk;

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

运行结果:

在这里插入图片描述


CGLIB 代理

JDK 动态代理的试用使用非常简单,但存在一定的局限性-------使用动态代理的对象必须实现一个或多个接口。如果要对没有实现接口的类进行代理,那么可以使用CGLIB 代理

CGLIB (code generation library) 是一个高性能开源的代码生成包,它采用非常底层的字节码技术,对指定的目标类生成一个子类,并对子类增强。在Spring 核心包中已经集成了 CGLIB 所需要的包,所以开发过程中就不在需要另外导入jar包

例子来说话

创建目标类

package com.itheima.cglib;

public class UserDao {
	public void addUser() {
		System.out.println("添加用户成功");
	}
	public void deteleUser() {
		System.out.println("删除用户成功");
	}
}

该目标类 不需要实现任何接口,只需要定义一个添加用户的方法和一个删除用户的方法。

创建代理类

package com.itheima.cglib;

import java.lang.reflect.*;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import com.itheima.jdk.MyAspect;

public class CglibProxy implements MethodInterceptor{
    						//CGLIB 代理的对象可以是任何类,所以target 的类型要使用 Object 类型
	public Object createProxy(Object target) {
		//创建一个动态对象
		Enhancer enhancer = new Enhancer();
		//确定需要增强的类,设置其父类
		enhancer.setSuperclass(target.getClass());
		//添加回调函数
		enhancer.setCallback(this);
		//返回创建的代理类
		return enhancer.create();
	}

	/*
	 * proxy cglib根据父类创建代理对象
	 * method 拦截方法
	 * args 拦截方法的参数列表
	 * methonProxy 方法的代理对象,用于执行父类的方法
	 */
	@Override
	public Object intercept(Object Proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		//创建切面类对象
		MyAspect myAspect = new MyAspect();
		//前增强
		myAspect.check_permissions();
		//目标方法的执行
		Object obj = methodProxy.invokeSuper(Proxy, args);
		//后增强
		myAspect.log();
		return obj;
	}
}

首先创建了一个动态类对象Enhancer,他是CGLIB 的核心类;然后调用了 Enhancer 类的 setSuperclass() 方法来确定目标对象;接下来调用了setCallback() 方法添加回调函数,其中this 代表代理类CglibProxy 本身;最后通过return 语句将创建的代理对象返回。intercept() 方法会在执行目标方法时被调用,方法执行时将会执行切面类中的增强方法。

创建测试类

package com.itheima.cglib;

public class CglibTest {

	public static void main(String[] args) {
		//创建代理对象
		CglibProxy cglibProxy = new CglibProxy();
		//创建目标对象
		UserDao userDao = new UserDao();
		//获取增强后的对象
		UserDao userDao1 = (UserDao)cglibProxy.createProxy(userDao);
		//执行方法
		userDao1.addUser();
		userDao1.deteleUser();
	}
}

运行结果

在这里插入图片描述


			在校学习生,不定时更新。还请各位看官谅解。
(参考书籍:JAVA EE 企业级应用开发教程(Spring+SpringMVC+MyBatis))
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值