03.Spring AOP

本文详细介绍了面向切面编程(AOP)的概念与优点,通过具体示例解释了AOP中的核心术语,包括方面、目标、切入点等,并展示了如何使用Spring框架进行AOP编程,涵盖了XML配置和注解方式。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第三章AOP

5.1AOP概念及优点

AOP为Aspect Oriented Programming的缩写,被称为面向切面编程。

AOP主要用于处理通用逻辑,例如日志记录,性能统计,安全控制,事务处理,异常处理等等。AOP可以将这些共通逻辑从普通业务逻辑代码中分离出来,这样在日后修改这些逻辑的时就不会影响普通业务逻辑的代码。

利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP、OOP在字面上虽然非常类似,但却是面向不同领域的两种设计思想。

OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元。

AOP则是针对业务处理过程中切面进行提取,他所面对的是处理过程中某个步骤或阶段,以获得逻辑过程中各个部分之间低耦合性的隔离效果。

AOP需要以OOP为前提和基础。

什么是方面

方面是指封装统一处理的组件,该组件被作用到其他目标组件方法上。

什么是目标

目标是指被一个或多个方面作用的对象。

什么是切入点

切入点是用于指定那些组件和方法使用方面功能,在Spring中利用一个表达式指定切入目标。

Spring提供了以下常用的切入点表达式

​ 方法限定表达式

​ executtion(修饰符?返回类型 方法名(参数)throws 异常类型?)

​ 类型限定表达式

​ Within(包名.类名)

​ Bean名称限定表达式

​ Bean(“Bean的id或name属性”)

什么是通知

通知是用于指定方面组件和目标组件作用时机。例如方面功能在目标方法之前或之后执行等时机。

Spring框架提供以下集中类型的通知

​ 前置通知:先执行方面功能再执行目标功能

​ 后置通知:先执行目标功能再执行方面功能(目标无异常才执行方面)

​ 最终通知:先执行目标功能再执行方面功能(目标有无异常都执行方面)

​ 异常通知:先执行目标,抛出异常后执行方面

​ 环绕通知:先执行方面前置部分,然后执行目标,最后再执行方面后置部分

Spring框架提供5中通知,可以按照下面try-catch-finally结构理解。

try{
		//前置通知—执行方面
		//环绕通知前置部分
		//执行目标组件方法
		//环绕通知后置部分
		//后置通知—执行方面
}catch(){	
		//异常通知—执行方面
}finally{
		//最终通知—执行方面
}

AOP实现原理

Spring AOP实现主要是基于动态代理技术。当Spring采用AOP配置后,Spring容器返回目标对象,实质上是Spring利用动态代理技术生成一个代理类。代理类重写了原目标组件方法的功能,再代理类中调用方面对象和目标对象功能。

Spring框架采用了两种动态代理实现:

利用cglib工具包

​ 目标接口采用此方法,代理类是利用继承方法生成一个目标子类。

利用JDK Proxy API

​ 目标有接口采用此方法,代理类是采用实现目标接口方法生成一个类。

5.1.1动态代理(JDK代理)

被代理对象必须要实现接口,才能产生代理对象.如果没有接口将不能使用动态代理技术

1.创建接口

package com.xyd.test;

public interface UserService {
	void select();
	void del();
	void update();
	void add();
}

2.创建实现类

package com.xyd.test;

public class UserServiceImpl implements UserService {

	public void select() {
		System.out.println("select:查询用户");

	}

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

	public void update() {
		System.out.println("update:修改用户");
	}

	public void add() {
		System.out.println("add:添加用户");
	}
}

3.创建动态代理工厂

package com.xyd.test;

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

public class UserServiceProxyFactory implements InvocationHandler{
	private UserService userService;

	public UserServiceProxyFactory(UserService userService) {
		super();
		this.userService = userService;
	}
	
	//生成代理
	public UserService getUserService() {
		UserService uService = (UserService) Proxy.newProxyInstance(this.getClass().getClassLoader(), 
				//代理目标对象
				UserServiceImpl.class.getInterfaces(), (InvocationHandler) this);
		return uService;
	}
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		System.out.println("开启事务");
		Object invoke= method.invoke(userService, args);	
		System.out.println("关闭事务");
		return null;
	}	
}

4.测试

package com.xyd.test;

import org.junit.Test;

public class TestCase {
	@Test
	public void test() {
		//1.获取接口对象
		UserService uService = new UserServiceImpl();
		//2.获取动态代理工厂
		UserServiceProxyFactory proxyFactory = new UserServiceProxyFactory(uService);
		//3.获取代理对象
		UserService service = proxyFactory.getUserService();
		//4.执行方法
		service.select();
		service.add();
		service.del();
		service.update();
	}
}

5.1.2cglib代理(没有接口)

1.定义一个Student接口

package com.xyd.test2;

public interface StudentDao {
	void select();
	void del();
	void add();
	void update();
}

2.学生的实现类,并没有实现接口,所以不能使用jdk动态代理,只能使用cglib动态代理

package com.xyd.test2;
/*
 * 学生的实现类,并没有实现接口,所以不能使用jdk动态代理,只能使用cglib动态代理
 */
public class StudentDaoImpl {
	public void select() {
		System.out.println("select:查询学生");
	}
	public void add() {
		System.out.println("add:增加学生");
	}
	public void del() {
		System.out.println("del:删除学生");
	}
	public void update() {
		System.out.println("update:修改学生");
	}
}

3.动态代理类实现

package com.xyd.test2;

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 StudentProxy implements MethodInterceptor {
	private Object obj;
	//创建代理对象
	public Object getInstance(Object obj) {
		this.obj = obj;
		//帮我生成代理对象
		Enhancer enhancer = new Enhancer();
		//设置对谁进行代理
		enhancer.setSuperclass(this.obj.getClass());
		//代理要做什么
		enhancer.setCallback(this);
		//返回代理对象
		return enhancer.create();
	}

	public Object intercept(Object arg0, Method arg1, Object[] arg2, MethodProxy arg3) throws Throwable {
		//业务方法之前
		System.out.println("=========开启事务");
		arg3.invokeSuper(arg0, arg2);
		System.out.println("=========关闭事务");
		return null;
		
	}

}

4.测试

package com.xyd.test2;

import org.junit.Test;

public class TestCase {
	@Test
	public void test() {
		//获取接口对象
		StudentDaoImpl studentDaoImpl= new StudentDaoImpl();
		//获取动态代理工厂
		StudentProxy proxy = new StudentProxy();
		//获取代理对象
		StudentDaoImpl stu = (StudentDaoImpl) proxy.getInstance(studentDaoImpl);
		
		//执行方法
		stu.add();
		stu.del();
		stu.update();
		stu.select();
	}
}

5.2 Spring AOP相关术语

一、joinpoint(连接点):

目标对象中,所有都<可以增强>的方法,叫做连接点

二、Pointcut(切入点):

目标对象,<已经增强>的方法,叫做切入点

三、Advice(通知/增强):

​ 1、<增强的>代码
​ 2、所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.
|-通知分为
​ |-前置通知
​ -目标方法运行前调用
​ |-后置通知
​ -目标方法运行后调用
​ |-异常通知
​ -如出现异常就会调用
​ |-最终通知 (无论是否出现异常都会调用)
​ -目标方法运行后调用
​ |-环绕通知(切面要完成的功能)
​ -目标方法运行前后调用

四、Target(目标对象):

代理的目标对象(被代理的对象)

五、Weaving(织入):

将通知应用到切入点的过程

六、Proxy(代理):

将通知织入到目标对象之后,而形成代理对象

七、Aspect(切面):

切入点和通知结合

5.2XML配置实现AOP开发步骤

  1. 创建测试接口

    package com.xyd.test;
    
    public interface UserService {
    	void select();
    	void del();
    	void update();
    	void add();
    }
    
    
  2. 准备目标对象

    package com.xyd.test;
    
    public class UserServiceImpl implements UserService {
    
    	public void select() {
    		System.out.println("select:查询用户");
    	}
    	public void del() {
    		System.out.println("del:删除用户");
    	}
    	public void update() {
    		System.out.println("update:修改用户");
    	}
    	public void add() {
    		System.out.println("add:添加用户");
    	}
    }
    
  3. 准备通知

    1、前置通知 |-目标方法运行前调用
    2、后置通知 |-目标方法运行后调用
    3、环绕通知 |-目标方法运行前后调用
    4、异常通知 |-如出现异常就会调用
    5、最终通知 |-(无论是否出现异常都会

    ​ 调用) 目标方法运行后调用

    package com.xyd.test;
    
    import org.aspectj.lang.ProceedingJoinPoint;
    
    public class UserAdvice {
    	//指定前置通知方法
    	public void getBefore() {
    		System.out.println("--前置通知--");
    	}
    	//最终后置通知
    	public void getAfter() {
    		System.out.println("--最终后置通知-无论是否出现异常都会调用--");
    	}
    	//指定环绕通知
    	public Object getAround(ProceedingJoinPoint pj)throws Throwable {
    		System.out.println("--环绕通知-前--");
    		Object proceed = pj.proceed();//调用目标方法
    		System.out.println("--环绕通知-后--");
    		return proceed;
    	}
    	//异常拦截 通知
    	public void getException() {
    		System.out.println("--异常通知--");
    	}
    	//后置通知
    	public void getAfterException() {
    		System.out.println("--后置通知-如出现异常不会调用--");
    	}
    }
    
    
  4. 配置将通知织入目标对象

    
    	<!-- 1.配置目标对象 -->
    	<bean name="userServiceImpl" class="com.xyd.test.UserServiceImpl"></bean>
    	<!-- 2.配置通知对象 -->
    	<bean name="userAdvice" class="com.xyd.test.UserAdvice"></bean>
    	<!-- 3.将通知织入目标对象 -->
    	<aop:config>
    		<!-- 3.1配置切入点 -->
    		<!-- 当前只对select方法增强 -->
    		<aop:pointcut expression="execution(* com.xyd.test.UserServiceImpl.select())" id="pt"/>
    		<!-- 3.2指定通知 -->
    		<aop:aspect ref="userAdvice">
    			<!-- 3.2.1指定前置通知,并指定切入点 -->
    			<aop:before method="getBefore" pointcut-ref="pt"/>
    			<!-- 3.2.2指定后置通知 ,并指定切入点-->
    			<aop:after method="getAfterException" pointcut-ref="pt"/>
    			<!-- 3.2.3指定环绕通知,并指定切入点- -->
    			<aop:around method="getAround" pointcut-ref="pt"/>
    			<!-- 3.2.4指定异常拦截通知,并指定切入点- -->
    			<aop:after-throwing method="getException" pointcut-ref="pt"/>
    			<!-- 3.2.5指定异常拦截通知,并指定切入点-->
    			<aop:after method="getAfter" pointcut-ref="pt"/>
    		</aop:aspect>
    	</aop:config>
    

5.测试(使用Spring提供的方式测试)

package com.xyd.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springmvc-aop.xml")
public class SpringTest {
	@Resource(name="userServiceImpl")
	private UserService userServiceImpl;
	@Test
	public void test1() {
		userServiceImpl.select();
	}
}

6.使用通配符给所有的方法增强:

通配符:

  1. ():无参数

  2. (…):匹配任意个参数

  3. (String**,*)匹配仅有2个参数的方法,且第一个参数为String

    <aop:config>
    		<!-- 3.1配置切入点 -->
    		<!-- 当前只对select方法增强 -->
    		<aop:pointcut expression="execution(* com.xyd.test.*ServiceImpl.*(..))" id="pt"/>
    		<!-- 3.2指定通知 -->
    		<aop:aspect ref="userAdvice">
    			<!-- 3.2.1指定前置通知,并指定切入点 -->
    			<aop:before method="getBefore" pointcut-ref="pt"/>
    			<!-- 3.2.2指定后置通知 ,并指定切入点-->
    			<aop:after method="getAfterException" pointcut-ref="pt"/>
    			<!-- 3.2.3指定环绕通知,并指定切入点- -->
    			<aop:around method="getAround" pointcut-ref="pt"/>
    			<!-- 3.2.4指定异常拦截通知,并指定切入点- -->
    			<aop:after-throwing method="getException" pointcut-ref="pt"/>
    			<!-- 3.2.5指定异常拦截通知,并指定切入点-->
    			<aop:after method="getAfter" pointcut-ref="pt"/>
    		</aop:aspect>
    	</aop:config>
    

5.3 注解实现AOP

开发步骤:

1.创建测试接口

package com.xyd.test;

public interface UserService {
	void select();
	void del();
	void update();
	void add();
}

2.准备目标对象

package com.xyd.test;

public class UserServiceImpl implements UserService {

	public void select() {
		System.out.println("select:查询用户");
	}
	public void del() {
		System.out.println("del:删除用户");
	}
	public void update() {
		System.out.println("update:修改用户");
	}
	public void add() {
		System.out.println("add:添加用户");
	}
}

3.将通知织入目标对象

package com.xyd.test;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class UserAdvice {
	//指定前置通知方法
	@Before("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getBefore() {
		System.out.println("--前置通知--");
	}
	//最终后置通知
	@After("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getAfter() {
		System.out.println("--最终后置通知-无论是否出现异常都会调用--");
	}
	//指定环绕通知
	@Around("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public Object getAround(ProceedingJoinPoint pj)throws Throwable {
		System.out.println("--环绕通知-前--");
		Object proceed = pj.proceed();//调用目标方法
		System.out.println("--环绕通知-后--");
		return proceed;
	}
	//异常拦截 通知
	@AfterThrowing("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getException() {
		System.out.println("--异常通知--");
	}
	//后置通知
	@AfterReturning("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getAfterException() {
		System.out.println("--后置通知-如出现异常不会调用--");
	}
}

  1. 配置Spring配置文件
	<!-- 1.配置目标对象 -->
	<bean name="userServiceImpl" class="com.xyd.test.UserServiceImpl"></bean>
	<!-- 2.配置通知对象 -->
	<bean name="userAdvice" class="com.xyd.test.UserAdvice"></bean>
	<!-- 3.开启注解完成织入 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

5.测试

package com.xyd.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springmvc-aop2.xml")
public class SpringTest {
	@Resource
	private UserService userServiceImpl;
	@Test
	public void test1() {
		userServiceImpl.del();
		userServiceImpl.add();
		userServiceImpl.select();
		userServiceImpl.update();
	}

}

public class UserServiceImpl implements UserService {

public void select() {
	System.out.println("select:查询用户");
}
public void del() {
	System.out.println("del:删除用户");
}
public void update() {
	System.out.println("update:修改用户");
}
public void add() {
	System.out.println("add:添加用户");
}

}


3.将通知织入目标对象

```java
package com.xyd.test;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
@Aspect
public class UserAdvice {
	//指定前置通知方法
	@Before("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getBefore() {
		System.out.println("--前置通知--");
	}
	//最终后置通知
	@After("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getAfter() {
		System.out.println("--最终后置通知-无论是否出现异常都会调用--");
	}
	//指定环绕通知
	@Around("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public Object getAround(ProceedingJoinPoint pj)throws Throwable {
		System.out.println("--环绕通知-前--");
		Object proceed = pj.proceed();//调用目标方法
		System.out.println("--环绕通知-后--");
		return proceed;
	}
	//异常拦截 通知
	@AfterThrowing("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getException() {
		System.out.println("--异常通知--");
	}
	//后置通知
	@AfterReturning("execution(* com.xyd.test.*ServiceImpl.*(..))")
	public void getAfterException() {
		System.out.println("--后置通知-如出现异常不会调用--");
	}
}

  1. 配置Spring配置文件
	<!-- 1.配置目标对象 -->
	<bean name="userServiceImpl" class="com.xyd.test.UserServiceImpl"></bean>
	<!-- 2.配置通知对象 -->
	<bean name="userAdvice" class="com.xyd.test.UserAdvice"></bean>
	<!-- 3.开启注解完成织入 -->
	<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

5.测试

package com.xyd.test;

import javax.annotation.Resource;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.context.annotation.Configuration;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:springmvc-aop2.xml")
public class SpringTest {
	@Resource
	private UserService userServiceImpl;
	@Test
	public void test1() {
		userServiceImpl.del();
		userServiceImpl.add();
		userServiceImpl.select();
		userServiceImpl.update();
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XYDrestart

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值