文档章节

Spring应用学习——AOP

江左煤郎
 江左煤郎
发布于 11/18 01:03
字数 2497
阅读 17
收藏 11

1. AOP

    1. AOP:即面向切面编程,采用横向抽取机制,取代了传统的继承体系的重复代码问题,如下图所示,性能监控、日志记录等代码围绕业务逻辑代码,而这部分代码是一个高度重复的代码,也就是在每一个业务逻辑的代码中都会有相同的代码围绕业务逻辑代码,而AOP就是将这些重复代码抽取出

    2. AOP实现原理:就是代理模式,主要有两种方式,分别是静态代理和动态代理,

  • 静态代理:采用一些工具类对原来的类生成一个代理类,代理类以.class存在
  • 动态代理(重点,spring基于动态代理实现aop):在运行中,通过反射生成类的代理对象,在代理对象中对原来的对象进行增强。

    3. spring采用动态代理的技术实现包括:

  • 基于接口生成动态代理对象:使用jdk提供的反射机制实现,参考这篇博客,JDK动态代理实现详解
  • 基于类生成动态代理对象:通过继承实现,根据类生成一个子类(代理对象),在代理对象(子类)中对父类进行增强,参考这篇博客。实现

    cglib通过继承的形式来实现动态功能的代理 那么就无法避免一些继承的缺点,那就是无法代理final方法和无法代理final类,示例使用代码如下

import java.lang.reflect.Method;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor  {

	//obj生成的代理对象
	//method对目标对象方法的引用
	//args目标对象方法调用参数
    //MethodProxy代理对象中对方法的引用
	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		//在方法之前增强,开启事务
        
		System.out.println("开启事务...");
		//执行父类方法
		Object result = methodProxy.invokeSuper(obj, args);
		//在方法之后增强,提交事务
		System.out.println("提交事务...");
		return result;
	}
}

import org.springframework.cglib.proxy.Enhancer;
public class CglibProxyFactory {
	/**
	 * 
	 * <p>Title: createCglibProxy</p>
	 * <p>Description: </p>
	 * @param clazz 父类的class,通过class操作字节码创建代理对象
	 * @return
	 */
	public static Object createCglibProxy(Class clazz){
		//代理对象,这里边有增强的代码
		CglibProxy cglibProxy = new CglibProxy();
		//创建增强器,需要设置父类及代理对象
		Enhancer enhancer = new Enhancer();
		//在增强器设置父类
		enhancer.setSuperclass(clazz);
		//在增强器设置代理对象(包括增强代码)
		enhancer.setCallback(cglibProxy);
		//创建一个代理对象
		return enhancer.create();
		
	}

}

 

    4. cglib与jdk实现动态代理的区别:jdk是基于接口生成代理对象,而cglib是基于类生成代理对象。spring底层使用jdk和cglib,如果原始对象实现了一个接口,spring使用jdk,否则 使用cglib生成代理。

    5. 相关术语:

  • Pointcut(切入点):确定在哪个方法上增强,一个类有多个切点
  • Advice(通知/增强):在切点上进行增强,包括:前置增强、后置增强、抛出异常增强
  • Target(目标对象):对目标对象进行增强,生成代理对象
  • Proxy(代理):对目标对象进行增强,生成代理对象
  • Weaving(织入):生成代理的时机(过程)
    • 动态代理织入,在运行期为目标类生成代理对象
    • 编译期织入
  • Aspect(切面): 包括切点和增强,面向哪个切点进行增强(编程)。

2. SpringAOP的简单使用

    1. 搭建环境:通过Maven加入spring-webmvc.jar和aspectjweaver.jar包即可导入所有需要依赖的包

    2. AspectJ相关语法:

  • 配置切点的语法:切点就是方法,所以必须要确认是那一个类的哪一个方法,
    • 用法:匹配任意字符
           1. com.cloud_note.entity.*:该包下所有类,仅限于此包下,不包括子包
           2. * com.cloud_note.controller.user.UserLoginController.excute(..):即com.cloud_note.controller.user.UserLoginController类中的任意返回值的excute方法
    •  .. 用法:匹配子包或方法形参,匹配子包下的类要与*结合使用
               1. com.cloud_note.entity..*:匹配该包下所有子包的所有类
               2. com.cloud_note.controller.user.UserLoginController.excute(..):即com.cloud_note.controller.user.UserLoginController类中的所有excute方法,无论该方法的参数是什么
               3. com.cloud_note.entity.*.*(..):匹配该包下所有类的所有方法
    • 用法:按类型匹配指定类的所有类
               1. com.cloud_note.controller.user.UserLoginController+:指定匹配继承UserLoginController类的所有子类,包括本身
    • 切点语法:表达式包括两部分,即函数和参数,函数有execution、within、target、args,参数就是通配表达式,如
      • execution(com.entity..*)
      • within(com.entity..*)
      • target(某个类的全限定名):也就表示匹配该类中的所有方法,每个方法都是切点,参数必须是类的全限定名
      • args(java.lang.String):表示以java.lang.String类型数据作为参数的方法全部都是切点,参数必须是类的全限定名
      • 与或非逻辑操作:execution、within、target、args直接可以进行与或非操作,用符号&&、||、!或者用字符and、or、not表示
  • 增强:AspectJ支持的增强类型有
    • Before :前置增强/通知,相当于BeforeAdvice
    • AfterReturning :后置增强/通知,相当于AfterReturningAdvice
    • Around :环绕增强/通知,相当于MethodInterceptor
    • AfterThrowing:抛出增强/通知,相当于ThrowAdvice
    • After:最终finally增强/通知,不管是否异常,该通知都会执行
public void save(){
	try{
	//前置增强
	调用目标对象方法执行
	//后置增强

	}catch(Exception ex){
		//抛出异常增强
	}finally{
		//最终finally增强
	}
}

    3. 在Spring中的AOP配置:

  • 使用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:context="http://www.springframework.org/schema/context" 
    	xmlns:jdbc="http://www.springframework.org/schema/jdbc"  
    	xmlns:jee="http://www.springframework.org/schema/jee" 
    	xmlns:tx="http://www.springframework.org/schema/tx"
    	xmlns:aop="http://www.springframework.org/schema/aop" 
    	xmlns:mvc="http://www.springframework.org/schema/mvc"
    	xmlns:util="http://www.springframework.org/schema/util"
    	xmlns:jpa="http://www.springframework.org/schema/data/jpa"
    	xsi:schemaLocation="
    		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd
    		http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.2.xsd
    		http://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.2.xsd
    		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
    		http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa-1.3.xsd
    		http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.2.xsd
    		http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.2.xsd
    		http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-3.2.xsd">
    	
    	<!--配置AOP中的增强类,即将LoggerBean中的方法作用到Controller组件的所有方法上-->
    	<bean id="loggerBean" class="LoggerBean"></bean>
    
    	<!--配置AOP-->
    	<aop:config>
    	<!--配置切面:切面包括切点和增强  -->
    		<aop:aspect ref="loggerBean"><!--ref属性就表示指定增强类-->
    		<!--配置切点  -->
    			<aop:pointcut expression="execution(* controller.user.*.*(..))" id="point"/>
    		<!--配置前置增强 ,增强就是 LoggerBean类中的logController方法-->	 
    			<aop:before method="logController" pointcut-ref="point"/>
    		<!-- 配置后置增强,增强就是 LoggerBean类中的outController方法-->
    			<aop:after-returning method="outController" pointcut-ref="point"/>
    		</aop:aspect>
    	</aop:config>
    
    	<!--开启注解扫描,只有被扫描进Spring容器的类才能配置AOP来增强代码-->
    	<context:component-scan base-package="controller.user"></context:component-scan>
    	
    	<!--如果要通过AOP注解来配置AOP,除了context:component-scan标签,还有下面这个标签开启AOP注解标示的使用-->
    	<aop:aspectj-autoproxy/>
    </beans>
  • 通过AOP注解来配置:首先要编写增强类,然后再添加注解配置,AOP注解有
    • @Aspect:指定切面,将组件指定为切面组件
    • @AfterThrowing(throwing="e",pointcut="within(controller..*)"):指定切点、增强方法代码以及增强的位置,是前置增强、后置增强、环绕增强等,比如当前就是抛出异常增强,e就是目标组件方法抛出的异常对象
    • @Before("within(controller..*)"):等价于<aop:before>定义
    • @After
    • @Around
    • @AfterReturning
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
//一个简单的日志记录增强代码
@Component//扫描添加到spring容器
@Aspect//将组件指定为切面组件
public class ExceptionBean {
	//e就是目标组件方法抛出的异常对象
	@AfterThrowing(throwing="e",pointcut="within(* controller..*)")
	public void excute(Exception e){
		//将异常信息写入文件中
		System.out.println("发生异常:"+e.getMessage());
		FileWriter fw;
		try {
			fw = new FileWriter("D:note_error.log",true);
			PrintWriter pw=new PrintWriter(fw);
			//利用pw对象写信息
			Date time=new Date();
			SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
			String str=sdf.format(time);
			pw.println("***************************");
			pw.println("异常类型:"+e);
			pw.println("发生时间:"+str);
			pw.println("异常详情:");
			e.printStackTrace(pw);
			pw.close();
			fw.close();
		} catch (IOException e1) {
			// TODO Auto-generated catch block
			System.out.println("记录异常失败");
		}
	
	}
}

3. 配置开启Spring声明式事务控制

    1. 关于什么是事务,可以参考我的另一篇博客JDBC,而关于Spring中的事务管理详细参考Spring事务管理

  • 通过注解开启:在想要添加事务控制的方法或类(类上标示类中所有方法都要进行事务管理)上添加@Transactional注解即可对该方法开启Spring事务,但必须先在Spring的xml配置文件中添加一行标签,org.springframework.jdbc.datasource.DataSourceTransactionManager该类就是JDBC提供的事务增强类,如下
    	<context:property-placeholder location="classpath:conf/db.properties"/>
    	<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="username" value="${jdbc.name}"></property>
    	</bean>
    
    	<!--Spring事务管理-->
    	<!--1.定义事务管理Bean-->
    	<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="ds"></property><!--注意,该行标签指定数据源-->
    	</bean>
    	<!--2.通过配置将txManager作用到Service方法上-->
    	<!--开启@Transactional标记,使用该标记就表示在方法上使用txManager所代表的增强类-->
    	<tx:annotation-driven transaction-manager="txManager"/>

     

  • 在Spring的xml配置文件中通过标签配置:
    	<context:property-placeholder location="classpath:conf/db.properties"/>
    	<bean id="datasource" class="org.apache.commons.dbcp.BasicDataSource">
    		<property name="driverClassName" value="${jdbc.driver}"></property>
    		<property name="url" value="${jdbc.url}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    		<property name="username" value="${jdbc.name}"></property>
    	</bean>
    	<!-- 事务管理配置 -->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    		<property name="dataSource" ref="datasource"></property>
    	</bean>
    	<tx:advice id="txAdvice" transaction-manager="transactionManager">
    		<tx:attributes>
    		<!-- 传播行为 -->
    			<tx:method name="save*" propagation="REQUIRED"/>
    			<tx:method name="insert*" propagation="REQUIRED"/>
    			<tx:method name="add*" propagation="REQUIRED"/>
    			<tx:method name="update*" propagation="REQUIRED"/>
    			<tx:method name="delete*" propagation="REQUIRED"/>
    			<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
    		</tx:attributes>
    	</tx:advice>
    	
    	<!-- AOP配置 -->
    	<aop:config>
    		<aop:advisor advice-ref="txAdvice" pointcut="execution(* service.*.*(..))"/>
    	</aop:config>

     

© 著作权归作者所有

共有 人打赏支持
江左煤郎
粉丝 26
博文 81
码字总数 204804
作品 0
西安
后端工程师
私信 提问
那些年,我们一起追的Spring

学无止境,但仍需及时总结。 自去年开始写作以来,写了一些关于Spring的文章,今天将它们汇总起来,一方面方便大家阅读,另一方面,也是一次小的复盘总结。 IOC 首先是Spring的IOC,也就是控...

SexyCode
08/14
0
0
Aspectj与Spring AOP比较

1、简介 今天有多个可用的 AOP 库, 它们需要能够回答许多问题: 是否与用户现有的或新的应用程序兼容? 在哪里可以实现 AOP? 与自己的应用程序集成多快? 性能开销是多少? 在本文中, 我们将...

沈渊
04/18
0
0
Java程序员从笨鸟到菜鸟之(六十七)细谈Spring(一)spring简介

Spring 是一个开源框架,是为了解决企业应用程序开发复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许您选择使用哪一个组件,同时为 J2EE 应用程序开发提供集成的框架。 然而...

长平狐
2012/11/12
72
0
spring和springmvc的区别

spring 是一个开源框架,是为了解决企业应用程序开发,功能如下 ◆目的:解决企业应用开发的复杂性 ◆功能:使用基本的JavaBean代替EJB,并提供了更多的企业应用功能 ◆范围:任何Java应用 ...

明理萝
07/30
0
0
Spring AOP 实现原理与 CGLIB 应用

AOP(Aspect Orient Programming),作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。AOP 实现的关键就在于 AOP 框架...

只想一个人静一静
2014/02/27
0
0

没有更多内容

加载失败,请刷新页面

加载更多

崛起于Springboot2.X之集成工作流Activiti5.22(42)

声明:该博客主要是Springboot1.X和Springboot2.X集成Activiti5.22版本,并说一下两个版本的搭建不同的地方 技术:Springboot2.0.3+mysql+jpa(自动生成25张表)+Activiti5.22 /然后Springboo...

木九天
7分钟前
0
1
windows环境下搭建rabbitMQ开发环境

windows环境下搭建rabbitMQ开发环境 下载与安装 erlang rabbitmq 是使用erlang语言开发的,所以需要erlang环境; 下载地址 rabbitmq 下载地址 rabbitmq与erlang版本关系 下载之后直接安装即可...

晨猫
19分钟前
0
0
JVM 中的守护线程

特点 通常由JVM启动 运行在后台处理任务,比如垃圾回收等 用户启动线程执行结束或者JVM结束时,会等待所有的非守护线程执行结束,但是不会因为守护线程的存在而影响关闭。 判断线程是否为守护...

小刀爱编程
22分钟前
1
0

参考 极客时间《数据结构与算法之美》

grace_233
35分钟前
2
0
谈谈KMP算法

KMP算法的资料网上已经一大把了,主要用来解决某个文本片段是否包含另一个子串问题。这里假设文本片段的长度n大于子串长度m,如: 文本串为ABCDABGHIJK 子串为 ABCDABE 在传统的暴力解法中当...

FAT_mt
37分钟前
3
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部