我交了个新朋友——TA叫AOP(JDK动态代理和CGLIB代理)(附加小型面试问答)

AOP概述

AOP(Aspect Oriented Program,面向切面编程)是现在比较热门的话题。AOP的历史可以追溯到1990年,当时面向对象编程(OOP)已经趋于成熟,并应用于软件开发。但是来自PARC研究中心的研究人员发现,在使用面向对象编程的过程中会产生局限性,他们对这种局限性做了深入的分析后,提出了一种新的编程思想,这种编程思想就是今天的AOP。

Spring AOP是继Spring IoC(Inversion of Control)之后的Spring框架的又一大特性,它也是Spring框架的核心内容。AOP是一种思想,所有符合AOP思想的技术,都可以看作AOP的实现。AOP是建立在Java的代理机制之上,Spring框架已经基本实现了AOP思想。在众多的AOP实现技术当中,Spring AOP做得最好,也是最为成熟的。

Spring AOP的实现是基于Java的代理机制,从JDK 1.3开始就支持代理功能,但是性能成为一个很大问题,为了解决JDK代理性能问题,出现了CGLIB代理机制。它可以生成字节码,所以它的性能会高于JDK代理。Spring支持这两种代理方式。但是,随着JVM(Java虚拟机)性能的不断提高,这两种代理性能的差距会越来越小。
在这里插入图片描述

Spring AOP术语

在这里插入图片描述
在这里插入图片描述

切面(Aspect)

由于平行四边形拦截了程序流程,Spring形象地把它叫作切面,所谓“面向切面编程”正是指的这个。以后提到的“切面”是形象地指这个“平行四边形”。实际上“切面”是一段程序代码,这段代码将被“植入”到程序流程中。在配置文件通过< bean >元素指定

连接点(Join Point)

对象操作过程中的某个阶段点。在程序流程上的任意一点,都可以是连接点。它实际上是对象的一个操作,方法调用。例如,对象调用某个方法、读写对象的实例或者某个方法抛出了异常等。

切入点(Point cut)

切入点是连接点的集合。切面与程序流程的“交叉点”便是程序的切入点。确切地说,它是“切面注入”到程序中的位置。换句话说,“切面”是通过切入点被“注入”的。在程序中可以有很多个切入点。

通知(Advice)

通知是某个切入点被横切后,所采取的处理逻辑。也就是说,在“切入点”处拦截程序后,通过通知来执行切面
在这里插入图片描述

目标对象(Target)

所有被通知的对象(也可以理解为被代理的对象)都是目标对象。目标对象被AOP所关注,它的属性的改变会被关注,它行为的调用也会被关注,它的方法传参的变化仍然会被关注。AOP会注意目标对象的变动,随时准备向目标对象“注入切面”。

织入(Weaving)

织入是将切面代码插入到目标对象上,从而生成代理对象的过程。由代理工厂创建一个代理对象,这个代理可以为目标对象执行切面功能。

Proxy(代理)

将通知应用到目标对象之后,被动态创建的对象。在Spring中,AOP代理可以是JDK动态代理或者CGLIB代理。

AOP入门小案例

JDK动态代理

UserDao.java

package beginner.jdk;

public interface UserDao {
	public void addUser();

	public void deleteUser();
}

目标类:UserdaoImpl.java

package beginner.jdk;

//目标类
public class UserdaoImpl implements UserDao {

	@Override
	public void addUser() {
		// TODO Auto-generated method stub
		System.out.println("添加用户");
	}

	@Override
	public void deleteUser() {
		// TODO Auto-generated method stub
		System.out.println("删除用户");
	}

}

切面类MyAspect.java

package beginner.asp;

//切面类:可以存在多个通知advice
public class MyAspect {
	public void check_Permissions() {
		System.out.println("模拟检查权限...");
	}

	public void log() {
		System.out.println("模拟记录日志...");
	}
}

JDK代理类JdkProxy.java

package beginner.jdk;

//JDK代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import beginner.asp.MyAspect;

public class JdkProxy implements InvocationHandler {
	// 声明目标类接口
	private UserDao userDao;

	// 创建代理方法
	public Object createProxy(UserDao userDao) {
		this.userDao = userDao;

		ClassLoader classLoader = JdkProxy.class.getClassLoader(); // 1、类加载器

		Class[] clazz = userDao.getClass().getInterfaces(); // 2、被代理对象实现的所有接口

		return Proxy.newProxyInstance(classLoader, clazz, this); // 3、使用代理类,进行增强,返回的是代理后的对象
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		// TODO Auto-generated method stub

		// 声明切面
		MyAspect myAspect = new MyAspect();
		// 执行业务前增强
		myAspect.check_Permissions();
		// 在目标类上调用方法,并传入参数
		Object obj = method.invoke(userDao, args);
		// 方法执行后增强
		myAspect.log();
		return obj;
	}

}

测试类JdkTest.java

package beginner.jdk;

public class JdkTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub

		// 创建代理对象
		JdkProxy jdkProxy = new JdkProxy();
		// 创建目标对象
		UserDao userDao = new UserdaoImpl();
		// 从代理对象中获取增强后的目标对象
		UserDao userDao1 = (UserDao) jdkProxy.createProxy(userDao);

		userDao1.addUser();
		userDao1.deleteUser();
	}

}

总体布局:
在这里插入图片描述
运行结果如图:
在这里插入图片描述
可见实现了动态代理和AOP思想

CGLIB代理

在这里插入图片描述
目标类UserDao.java

package com.beginner.cglib;

public class UserDao {
	public void addUser() {
		System.out.println("添加用户");
	}

	public void deleteUser() {
		System.out.println("删除用户");
	}
}

切面类MyAspect.java

package com.beginner.cglib;

//切面类:可以存在多个通知advice
public class MyAspect {
	public void check_Permissions() {
		System.out.println("模拟检查权限...");
	}

	public void log() {
		System.out.println("模拟记录日志...");
	}
}

代理类CglibProxy.java

package com.beginner.cglib;

import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

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

	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		// TODO Auto-generated method stub
		MyAspect myAspect = new MyAspect();

		myAspect.check_Permissions();

		Object obj = methodProxy.invokeSuper(proxy, args);
		myAspect.log();
		return obj;
	}

}

测试类CglibTest.java

package com.beginner.cglib;

public class CglibTest {
	public static void main(String[] args) {
		CglibProxy cglibProxy = new CglibProxy();
		UserDao userDao = new UserDao();
		UserDao userDao2 = (UserDao) cglibProxy.createProxy(userDao);
		userDao2.addUser();
		userDao2.deleteUser();
	}
}

总体布局:
在这里插入图片描述
运行结果如下:
在这里插入图片描述

小型面试题

1、面试官:使用Spring框架的好处是什么?

应聘者:
(1)轻量:Spring是轻量的。
(2)控制反转IoC:Spring通过控制反转实现了松散耦合,对象们给出它们的依赖,而不是创建或查找依赖的对象们。
(3)面向切面的编程(AOP):Spring支持面向切面的编程,并且把应用业务逻辑和系统服务分开。
(4)容器:Spring包含并管理应用中对象的生命周期和配置。
(5)MVC框架:Spring的Web框架是个精心设计的框架,是Web框架的一个很好的替代品。
(6)事务管理:Spring提供一个持续的事务管理接口,可以扩展到上至本地事务下至全局事务(JTA)。
(7)异常处理:Spring提供方便的API把具体技术相关的异常(比如由JDBC、Hibernate或JDO抛出的)转换为一致的unchecked 异常。

面试官:什么是Bean的自动装配?

应聘者:Spring容器能够自动装配相互合作的Bean,这意味着容器不需要< constructor-arg >和< property >配置,能通过Bean工厂自动处理Bean之间的协作。

3、面试官:解释AOP。

应聘者:面向切面的编程或AOP,是一种编程技术,允许程序模块化横向切割关注点,或横切典型的责任划分,如日志和事务管理。

面试官:在Spring AOP中,关注点和横切关注的区别是什么?

应聘者:关注点是应用中一个模块的行为,一个关注点可能会被定义成一个我们想实现的功能。横切关注点是一个关注点,此关注点是整个应用都会使用的功能,并影响整个应用,比如日志、安全和数据传输,几乎应用的每个模块都需要的功能。因此这些都属于横切关注点。

之后我会持续更新,如果喜欢我的文章,请记得一键三连哦,点赞关注收藏,你的每一个赞每一份关注每一次收藏都将是我前进路上的无限动力 !!!↖(▔▽▔)↗感谢支持!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值