“Spring:AOP”的版本间差异
		
		
		
		
		
		跳到导航
		跳到搜索
		
				
		
		
	
| (未显示同一用户的3个中间版本) | |||
| 第121行: | 第121行: | ||
| === 相关术语 === | === 相关术语 === | ||
| * Joinpoint(连接点):所谓连接点是指那些被拦截到的点。【可以被切入的方法】 | * Joinpoint(连接点):所谓连接点是指那些被拦截到的点。【可以被切入的方法】 | ||
| **  | ** 在 Spring 中,这些点指的是'''方法''',因为,Spring 只支持方法类型的连接点; | ||
| * '''Pointcut'''(切入点):所谓切入点是指我们要对哪些Joinpoint 进行拦截的定义;【用来切入的方法】 | * '''Pointcut'''(切入点):所谓切入点是指我们要对哪些Joinpoint 进行拦截的定义;【用来切入的方法】 | ||
| * '''Advice'''(通知/增强):所谓通知是指拦截到Joinpoint 之后所要做的事情就是通知;【切面处理】 | * '''Advice'''(通知/增强):所谓通知是指拦截到Joinpoint 之后所要做的事情就是通知;【切面处理】 | ||
| 第129行: | 第129行: | ||
| *# “最终通知”:无论程序是否正常执行,最终通知的代码会得到执行; | *# “最终通知”:无论程序是否正常执行,最终通知的代码会得到执行; | ||
| *# “环绕通知”:; | *# “环绕通知”:; | ||
| * Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下,  | * Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field; | ||
| * '''Target'''(目标对象):代理的目标对象; | * '''Target'''(目标对象):代理的目标对象; | ||
| * '''Weaving'''(织入):是指把增强应用到目标对象来创建新的代理对象的过程;【应用增强生成代理对象】 | * '''Weaving'''(织入):是指把增强应用到目标对象来创建新的代理对象的过程;【应用增强生成代理对象】 | ||
| ** spring采用“动态代理织入”,而AspectJ采用“编译期织入”和“类装载期织入”;【?】 | ** spring采用“动态代理织入”,而AspectJ采用“编译期织入”和“类装载期织入”;【?】 | ||
| * '''Proxy''' | * '''Proxy'''(代理):一个类被 AOP 织入增强后,就产生一个结果代理类; | ||
| * '''Aspect'''(切面):  | * '''Aspect'''(切面): 是 Pointcut(切入点)和 Advice(增强)/Introduction(引介)的结合; | ||
| === jar包 === | === jar包 === | ||
|  spring 的传统 AOP 的开发的包: | |||
|     “spring-aop-4.2.4.RELEASE.jar” | |||
|     “com.springsource.org.aopalliance-1.0.0.jar” | |||
|  aspectJ 的开发包: | |||
|     “com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar” | |||
|     “spring-aspects-4.2.4.RELEASE.jar” | |||
| === 配置文件 === | === 配置文件 === | ||
| 需要在“'''applicationContext.xml'''”中引入 AOP 相关约束: | |||
| <syntaxhighlight lang="properties"> | : <syntaxhighlight lang="properties"> | ||
| <beans xmlns="http://www.springframework.org/schema/beans" | <beans xmlns="http://www.springframework.org/schema/beans" | ||
|     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" |     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" | ||
| 第158行: | 第159行: | ||
| === 切入点表达式 === | === 切入点表达式 === | ||
| '''execution([方法访问修饰符]  |  注意:<span style="color: green; font-size: 150%">Spring 中,'''切入点是“方法”,而非“类”'''</span> | ||
|  '''execution([方法访问修饰符] <方法返回类型> <包名.类名.方法名>(<方法的参数>) [异常])''' | |||
| 定义切点: | |||
| * “.”:表示包下的所有类; | # 通过'''方法签名''': | ||
| * “..”:表示包、子孙包下的所有类; | #: 示例: | ||
| # execution(* com.baobaotao.*(..)):匹配com.baobaotao包下所有类的所有方法; | #: execution(public * *(..)):匹配所有目标类的 public方法; | ||
| # execution(* com.baobaotao..*(..)):匹配com.baobaotao包、子孙包下所有类的所有方法; | #: execution(* *To(..)):匹配目标类所有以To为后缀的方法; | ||
| # execution(* com...*Dao.find(..)):匹配:前缀为com的任何包下,后缀为Dao的任何类中,以find为前缀的方法; | # 通过'''类''': | ||
| #: 示例: | |||
| #: execution(* com.baobaotao.Waiter.*(..)):匹配Waiter接口的所有方法; | |||
| * “”:表示任意类型的参数; | #: execution(* com.baobaotao.Waiter+.*(..)):匹配Waiter接口及其所有实现类的方法; | ||
| * “..”:表示任意类型、任意个数的参数; | # 通过'''类包''': | ||
| * 如果参数类型是“java.lang”包下的类,可以直接使用类名;否则必须使用全限定类名; | #* “.”:表示包下的所有类; | ||
| # execution(* joke(String,int))) | #* “..”:表示包、子孙包下的所有类; | ||
| # execution(* joke(String,*))): | #: 示例: | ||
| # execution(* joke(String,..))): | #: execution(* com.baobaotao.*(..)):匹配com.baobaotao包下所有类的所有方法; | ||
| # execution(* joke(Object+))):匹配参数是“Object”或“Object”子类  | #: execution(* com.baobaotao..*(..)):匹配com.baobaotao包、子孙包下所有类的所有方法; | ||
| #: execution(* com...*Dao.find(..)):匹配:前缀为com的任何包下,后缀为Dao的任何类中,以find为前缀的方法; | |||
| # 通过'''方法入参''': | |||
| #* “”:表示任意类型的参数; | |||
| #* “..”:表示任意类型、任意个数的参数; | |||
| #* 如果参数类型是“java.lang”包下的类,可以直接使用类名;否则必须使用全限定类名; | |||
| #: 示例: | |||
| #: execution(* joke(String,int))):匹配第一参数是 String,第二个参数是 int 的 joke()方法; | |||
| #: execution(* joke(String,*))): | |||
| #: execution(* joke(String,..))): | |||
| #: execution(* joke(Object+))):匹配参数是“Object”或“Object”子类 的 joke()方法; | |||
| == 基于AspectJ:XML == | == 基于AspectJ:XML == | ||
| 第216行: | 第217行: | ||
| 		<aop:pointcut expression="execution(* cn.itcast.aop.Book.*(..))" id="pointcut1"/> | 		<aop:pointcut expression="execution(* cn.itcast.aop.Book.*(..))" id="pointcut1"/> | ||
| 		<!-- 2.2  | 		<!-- 2.2 配置切面 --> | ||
| 		<aop:aspect ref="myBook"> | 		<aop:aspect ref="myBook"> | ||
| 			<!--  | 			<!-- 配置增强(“method”:增强;“pointcut-ref”:切入点)--> | ||
| 			<aop:before method="before1" pointcut-ref="pointcut1"/> | 			<aop:before method="before1" pointcut-ref="pointcut1"/> | ||
| 第259行: | 第260行: | ||
| == 基于AspectJ:注解 == | == 基于AspectJ:注解 == | ||
| 步骤: | |||
| # 创建对象; | |||
| # 开启aop操作(“applicationContext.xml”中); | |||
| #: <syntaxhighlight lang="properties" highlight="6"> | |||
| 	<!-- 1  配置对象 --> | |||
| 	<bean id="book" class="cn.itcast.aop.Book"></bean> | |||
| 	<bean id="myBook" class="cn.itcast.aop.MyBook"></bean> | |||
|    <!-- 2 开启aop操作 --> | |||
| 	<aop:aspectj-autoproxy></aop:aspectj-autoproxy> | |||
| </syntaxhighlight> | |||
| # 在增强类中使用注解; | |||
| ## “'''@Aspect'''”:注解增强类; | |||
| ## “'''@Before'''”、“'''@After'''”、“'''@Around'''”、“@AfterReturning”、“@AfterThrowing”:注解增强; | |||
| ##* 需要配置“'''切入点表达式'''”(value属性中); | |||
| #: <syntaxhighlight lang="java" highlight="1,5"> | |||
| @Aspect | |||
| public class MyBook { | |||
| 	//在方法上面使用注解完成增强配置 | |||
| 	@Before(value="execution(* cn.itcast.aop.Book.*(..))") | |||
| 	public void before1() { | |||
| 		System.out.println("before.............."); | |||
| 	} | |||
| } | |||
| </syntaxhighlight> | |||
| == 事务增强 == | |||
| <syntaxhighlight lang="properties"> | |||
| 	<!-- dataSource --> | |||
|     ... | |||
| 	<!-- 第一步 配置事务管理器 --> | |||
| 	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> | |||
| 		<!-- 注入dataSource --> | |||
| 		<property name="dataSource" ref="dataSource"></property> | |||
| 	</bean> | |||
| 	<!-- 第二步 配置事务增强 --> | |||
| 	<tx:advice id="txadvice" transaction-manager="transactionManager"> | |||
| 		<!-- 做事务操作 --> | |||
| 		<tx:attributes> | |||
| 			<!-- 设置进行事务操作的方法匹配规则  --> | |||
| 			<tx:method name="account*" propagation="REQUIRED"/> | |||
| 			<!-- <tx:method name="insert*" /> --> | |||
| 		</tx:attributes> | |||
| 	</tx:advice> | |||
| 	<!-- 第三步 配置切面 --> | |||
| 	<aop:config> | |||
| 		<!-- 切入点 --> | |||
| 		<aop:pointcut expression="execution(* cn.itcast.service.OrdersService.*(..))" id="pointcut1"/> | |||
| 		<!-- 切面 --> | |||
| 		<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/> | |||
| 	</aop:config> | |||
| </syntaxhighlight> | |||
2022年11月5日 (六) 12:07的最新版本
关于AOP
Aspect Oriented Programming:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的。
- AOP是OOP的延续。
- 可以在不修改源码的情况下,对程序进行增强;
- 应用如:权限校验,日志记录,性能监控,事务控制;
AOP 实现
Spring 的AOP 的底层用到两种代理机制:
- JDK 动态代理:(实现了接口)使用动态代理创建接口实现类代理对象;
- CGLIB 动态代理:(没有实现接口)使用动态代理创建类的子类代理对象;(在子类中调用父类的方法,来完成增强)
- 应用的是底层的字节码增强的技术生成当前类的子类对象;
 
JDK 动态代理
- 参见:JavaCore:接口(代理)中的代理部分;
public class MyJDKProxy implements InvocationHandler {
	private UserDao userDao;
	
	public MyJDKProxy(UserDao userDao) {
		this.userDao = userDao;
	}
	
	// 编写工具方法:生成代理:
	public UserDao createProxy() {
		UserDao userDaoProxy = (UserDao)Proxy.newProxyInstance(userDao.getClass().getClassLoader(),
																userDao.getClass().getInterfaces(), this);
		return userDaoProxy;
	}
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		if("save".equals(method.getName())) {
			System.out.println("权限校验================");
		}
		return method.invoke(userDao, args);
	}
}
CGLIB 动态代理
public class MyCglibProxy implements MethodInterceptor {
	private CustomerDao customerDao;
	
	public MyCglibProxy(CustomerDao customerDao) {
		this.customerDao = customerDao;
	}
	
	// 生成代理的方法:
	public CustomerDao createProxy() {
		// 创建Cglib 的核心类:
		Enhancer enhancer = new Enhancer();
		// 设置父类:
		enhancer.setSuperclass(CustomerDao.class);
		// 设置回调:
		enhancer.setCallback(this);
		// 生成代理:
		CustomerDao customerDaoProxy = (CustomerDao) enhancer.create();
		return customerDaoProxy;
	}
	
	@Override
	public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
		if("delete".equals(method.getName())) {
			Object obj = methodProxy.invokeSuper(proxy, args);
			System.out.println("日志记录================");
			return obj;
		}
		return methodProxy.invokeSuper(proxy, args);
	}
}
关于AspectJ
AspectJ,(Eclipse AspectJ)是一个易用的功能强大的AOP框架;可以单独使用(需要专门的编译器“ajc”),也可以整合到其它框架中。
- 官网:“http://www.eclipse.org/aspectj/”;
- AspectJ是AOP编程的完全解决方案,可以做Spring AOP干不了的事情;
- AspectJ不是spring一部分,和spring一起使用进行aop操作(Spring2.0以后新增了对AspectJ支持);
关于织入
AOP编程,存在三种织入方式:
- 编译期织入(Compile-time weaving):(编译期、编译后)在Java编译期,采用特殊的编译器,将切面织入到Java类中;
- 编译后:对已经生成的“.class”文件、“jar”包进行织入;
 
- 类加载期织入(Load-time weaving):通过特殊的类加载器,在类字节码加载到JVM时,织入切面;
- 或在JVM启动的时候指定 AspectJ 提供的“agent:-javaagent:xxx/xxx/aspectjweaver.jar”;【?】
 
- 运行期织入:采用CGLib工具或JDK动态代理进行切面的织入;
AspectJ 与 Spring AOP
Spring AOP 和 AspectJ 之间的关键区别:
| AspectJ | Spring AOP | 
|---|---|
| 静态织入 
 | 运行期织入 | 
| 支持所有切入点 
 | 仅支持方法执行切入点 
 | 
| 使用 Java 编程语言的扩展实现 
 | 在纯 Java 中实现 
 | 
| 更好的性能 | 较 AspectJ 慢 | 
- Spring AOP使用了Aspect的Annotation,但是并没有使用它的编译器和织入器;
- “@Before”、“@After”等是Aspect的Annotation;
 
- AspectJ在实际运行之前就完成了织入,所以其生成的类没有额外运行时开销。
Spring:AOP
相关术语
- Joinpoint(连接点):所谓连接点是指那些被拦截到的点。【可以被切入的方法】
- 在 Spring 中,这些点指的是方法,因为,Spring 只支持方法类型的连接点;
 
- Pointcut(切入点):所谓切入点是指我们要对哪些Joinpoint 进行拦截的定义;【用来切入的方法】
- Advice(通知/增强):所谓通知是指拦截到Joinpoint 之后所要做的事情就是通知;【切面处理】
- “前置通知”:;
- “后置通知”:;
- “异常通知”:;
- “最终通知”:无论程序是否正常执行,最终通知的代码会得到执行;
- “环绕通知”:;
 
- Introduction(引介):引介是一种特殊的通知在不修改类代码的前提下, Introduction 可以在运行期为类动态地添加一些方法或 Field;
- Target(目标对象):代理的目标对象;
- Weaving(织入):是指把增强应用到目标对象来创建新的代理对象的过程;【应用增强生成代理对象】
- spring采用“动态代理织入”,而AspectJ采用“编译期织入”和“类装载期织入”;【?】
 
- Proxy(代理):一个类被 AOP 织入增强后,就产生一个结果代理类;
- Aspect(切面): 是 Pointcut(切入点)和 Advice(增强)/Introduction(引介)的结合;
jar包
spring 的传统 AOP 的开发的包: “spring-aop-4.2.4.RELEASE.jar” “com.springsource.org.aopalliance-1.0.0.jar” aspectJ 的开发包: “com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar” “spring-aspects-4.2.4.RELEASE.jar”
配置文件
需要在“applicationContext.xml”中引入 AOP 相关约束:
- <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> 
切入点表达式
注意:Spring 中,切入点是“方法”,而非“类”
execution([方法访问修饰符] <方法返回类型> <包名.类名.方法名>(<方法的参数>) [异常])
定义切点:
- 通过方法签名:
- 示例:
- execution(public * *(..)):匹配所有目标类的 public方法;
- execution(* *To(..)):匹配目标类所有以To为后缀的方法;
 
- 通过类:
- 示例:
- execution(* com.baobaotao.Waiter.*(..)):匹配Waiter接口的所有方法;
- execution(* com.baobaotao.Waiter+.*(..)):匹配Waiter接口及其所有实现类的方法;
 
- 通过类包:
- “.”:表示包下的所有类;
- “..”:表示包、子孙包下的所有类;
 - 示例:
- execution(* com.baobaotao.*(..)):匹配com.baobaotao包下所有类的所有方法;
- execution(* com.baobaotao..*(..)):匹配com.baobaotao包、子孙包下所有类的所有方法;
- execution(* com...*Dao.find(..)):匹配:前缀为com的任何包下,后缀为Dao的任何类中,以find为前缀的方法;
 
- 通过方法入参:
- “”:表示任意类型的参数;
- “..”:表示任意类型、任意个数的参数;
- 如果参数类型是“java.lang”包下的类,可以直接使用类名;否则必须使用全限定类名;
 - 示例:
- execution(* joke(String,int))):匹配第一参数是 String,第二个参数是 int 的 joke()方法;
- execution(* joke(String,*))):
- execution(* joke(String,..))):
- execution(* joke(Object+))):匹配参数是“Object”或“Object”子类 的 joke()方法;
 
基于AspectJ:XML
步骤:
- 配置Bean对象;
- 配置aop操作:
- 配置切入点;
- 配置切面;(AspectJ)
 
xml配置示例:
<?xml version="1.0" encoding="UTF-8"?>
<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">
	
	<!-- 1  配置对象 -->
	<bean id="book" class="cn.itcast.aop.Book"></bean>
	<bean id="myBook" class="cn.itcast.aop.MyBook"></bean>
	
	<!-- 2 配置aop操作 -->
	<aop:config>
		<!-- 2.1 配置切入点 -->
		<aop:pointcut expression="execution(* cn.itcast.aop.Book.*(..))" id="pointcut1"/>
		
		<!-- 2.2 配置切面 -->
		<aop:aspect ref="myBook">
			<!-- 配置增强(“method”:增强;“pointcut-ref”:切入点)-->
			<aop:before method="before1" pointcut-ref="pointcut1"/>
			
			<aop:after-returning method="after1" pointcut-ref="pointcut1"/>
			
			<aop:around method="around1" pointcut-ref="pointcut1"/>
		</aop:aspect>
	</aop:config>
</beans>
- 切入点“execution(* cn.itcast.aop.Book.*(..))”匹配到“book”类中的所有方法;(切入点是方法,而非类)
- 增强类“myBook”的“before1”、“after1”、“around1”分别被织入到切入点的前、后、环绕;
增强类示例:
public class MyBook {
	public void before1() {
		System.out.println("前置增强......");
	}
	
	public void after1() {
		System.out.println("后置增强......");
	}
	
	//环绕通知
	public void around1(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		//方法之前
		System.out.println("方法之前.....");
		
		//执行被增强的方法
		proceedingJoinPoint.proceed();
		
		//方法之后
		System.out.println("方法之后.....");
	}
}
- “环绕通知”中,调用执行切入点;(切入点是方法,而非类)
基于AspectJ:注解
步骤:
- 创建对象;
- 开启aop操作(“applicationContext.xml”中);
- <!-- 1 配置对象 --> <bean id="book" class="cn.itcast.aop.Book"></bean> <bean id="myBook" class="cn.itcast.aop.MyBook"></bean> <!-- 2 开启aop操作 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> 
 
- 在增强类中使用注解;
- “@Aspect”:注解增强类;
- “@Before”、“@After”、“@Around”、“@AfterReturning”、“@AfterThrowing”:注解增强;
- 需要配置“切入点表达式”(value属性中);
 
 - @Aspect public class MyBook { //在方法上面使用注解完成增强配置 @Before(value="execution(* cn.itcast.aop.Book.*(..))") public void before1() { System.out.println("before.............."); } } 
 
事务增强
	<!-- dataSource -->
    ...
	<!-- 第一步 配置事务管理器 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<!-- 注入dataSource -->
		<property name="dataSource" ref="dataSource"></property>
	</bean>
	
	<!-- 第二步 配置事务增强 -->
	<tx:advice id="txadvice" transaction-manager="transactionManager">
		<!-- 做事务操作 -->
		<tx:attributes>
			<!-- 设置进行事务操作的方法匹配规则  -->
			<tx:method name="account*" propagation="REQUIRED"/>
			<!-- <tx:method name="insert*" /> -->
		</tx:attributes>
	</tx:advice>
	
	<!-- 第三步 配置切面 -->
	<aop:config>
		<!-- 切入点 -->
		<aop:pointcut expression="execution(* cn.itcast.service.OrdersService.*(..))" id="pointcut1"/>
		<!-- 切面 -->
		<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1"/>
	</aop:config>