Spring基于AOP(面向切面编程)开发

发布时间:2024年01月16日

概述

????????AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

为什么要学习AOP

在不修改源代码的情况下,对原方法进行增强

AOP可以校验,日志记录,性能监控,事务控制

底层实现(了解)

代理机制:

Spring的AOP的底层用到两种代理机制

? ? ? ? JDK动态代理:针对实现了接口的类产生的代理

? ? ? ? Cglib动态代理:针对没有接口的类产生的代理,应用了底层的字节码增强的技术,生成当前的子类对象

JDK动态代理

创建一个代理类

public class ProxyUser implements InvocationHandler {
	private Object target; // 代理的目标对象
	public LogJdkProxy(Object target) {
		this.target = target;
	}
	/** 创建代理对象 */
	public Object createProxyInstance() {
		// 第1个参数设置代码使用的类装载器,一般采用跟目标类相同的类装载器
		// 第2个参数设置代理类实现的接口
		// 第3个参数设置回调对象,当代理对象的方法被调用时,会调用该参数指定对象的invoke方法
		return Proxy.newProxyInstance(//
				getClass().getClassLoader(), // 第1个参数
				target.getClass().getInterfaces(), // 第2个参数
				this); // 第3个参数
	}
	/**
	 * @param proxy  目标对象的代理类实例
	 * @param method 对应于在代理实例上调用接口方法的Method实例
	 * @param args   传入到代理实例上方法参数值的对象数组
	 * @return 方法的返回值,没有返回值(void)时是null
	 */
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("== Log:开始执行操作,方法名:"+method.getName()+" ==");
		Object result = method.invoke(target, args); // 执行原方法,把方法调用委派给目标对象
		System.out.println("== Log:操作执行完毕 ==");
		return result;
	}
}

测试

Cglib动态代理

创建对象接口

// 此类不能是final的,否则不能有子类,CGLIB也就不能工作了
public class UserServiceImpl {
?
	// 这是final的方法,不能被子类重写,所以CGLIB不会拦截这个方法
	public final void foo1() {
		System.out.println(">> final的方法 <<");
	}
?
	// 这是static的方法,CGLIB不会拦截这个方法
	public static void foo2() {
		System.out.println(">> static的方法 <<");
	}
?
	// 这是private的方法,CGLIB不会拦截这个方法
	private void foo3() {
		System.out.println(">> private的方法 <<");
	}
?
	// CGLIB会拦截这个方法,可以是public, protected, default的修饰符
	public void deleteUser() {
		System.out.println(">> 删除一个User <<");
	}
}

创建Cglib代理类

// CGLIB代理类
public class LogCglibProxy implements MethodInterceptor {
	private Object target; // 代理的目标对象
	/**
	 * 创建代理对象
	 * @param <T>
	 * @param target 目标对象
	 */
	public <T> T createProxyInstance(T target) {
		this.target = target;
		Enhancer enhancer = new Enhancer(); // 该类用于生成代理对象
		enhancer.setSuperclass(target.getClass()); // 设置父类
		enhancer.setCallback(this); // 设置回调对象为自己这个对象
		return (T) enhancer.create(); // 创建代理对象并返回
	}
	/**
	 * @param proxy 		 目标对象代理类的实例
	 * @param method		代理类实例上调用父类方法的Method实例
	 * @param args 			传入到方法参数值的对象数组
	 * @param methodProxy 	使用它调用父类的方法
	 */
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, 
MethodProxy methodProxy)	throws Throwable {
		System.out.println(proxy.getClass());
		System.out.println("== Log:开始执行操作,方法名:" + method.getName() + " ==");
		Object result = methodProxy.invoke(target, args); // 执行原方法
		System.out.println("== Log:操作执行完毕 ==");
		return result;
	}
}

测试

public class MainTest {

	@Test
	public void test() throws Exception {
		UserService userService = new UserService();
		userService = (UserService) new LogCglibProxy().createProxyInstance(userService); // 使用包装之的后的代理对象

		userService.saveUser();
		System.out.println();

		userService.deleteUser();
		System.out.println();
}
}

Spring使用AspectJ进行AOP开发

引入lib包

引入配置文件

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
使用xml配置文件形式

通知类型

切入点表达式

编写一个切面类

public class MyAspectXml {
// 前置增强
public void before(){
System.out.println("前置增强===========");
    }
}

在application Context.xml中配置

<!-- 配置切面类 -->
<bean id="myAspectXml" class="cn.test.spring.demo3.MyAspectXml"></bean>
<!-- 进行 aop 的配置 -->
<aop:config>
<!-- 配置切入点表达式:哪些类的哪些方法需要进行增强 -->
<aop:pointcut expression="execution(* cn.test.spring.demo3.OrderDao.save(..))" id="pointcut1"/>
<!-- 配置切面 -->
<aop:aspect ref="myAspectXml">
<aop:before method="before" pointcut-ref="pointcut1"/> </aop:aspect>
</aop:config>
使用注解来实现切面类

开启aop注解自动代理

AspectJ的AOP注解

@Aspect? 定义切面类

通知类型

@Before? ? ? ? 前置通知

@AfterReturing? ? ? ? 后置通知

@Around? ? ? ? 环绕通知

@After? ? ? ? 最终通知

@AfterThrowing? ? ? ? 异常抛出通知

@Pointcut? ? ? ? 定义切入点

编写切面类

@Component
@Aspect
public class MyAspect {
    @Before("MyAspect.pointcut01()")
    public void before(){
        System.out.println("前置-----");
    }

    @After("MyAspect.pointcut01()")
    public void after(){
        System.out.println("后置-----");
    }
    @Pointcut("execution(* com.xuexi.dao.*.*(..))")
    public void pointcut01(){

    }
}

测试

文章来源:https://blog.csdn.net/weixin_73056048/article/details/135620069
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。