博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring_AOP
阅读量:4102 次
发布时间:2019-05-25

本文共 7204 字,大约阅读时间需要 24 分钟。

AOP简介

AOP(Aspect-Oriented Programming,面向切面编程):是一种新的方法论,是传统OOP(Object-Oriented Programming,面向对象编程)的补充。
AOP的主要编程对象是切面(aspect),而切面模块化横切关注点。
在应用AOP编程时,仍然需要定义公共功能,但可以明确的定义这个功能在哪里,以什么方式应用,并且不必修改受影响的类,这样一来横切关注点就被模块化到特殊的对象(切面)里

AOP的好处:

每个事务逻辑位于一个位置,代码不分散,便于维护和升级
业务模块更简洁,只包含核心业务代码,非侵入式编程

AOP术语

切面(Aspect):横切关注点(跨越应用程序多个模块的功能)被模块化的特殊对象
通知(Advice):切面必须要完成的工作
目标(Target):被通知的对象
代理(Proxy):向目标对象应用通知之后创建的对象
连接点(Joinpoint):程序执行的某个特定位置:如类某个方法调用前、调用后、方法抛出异常后等。连接点由两个信息确定:方法表示的程序执行点,相对点表示的方位。
切点(pointcut):每个类都拥有多个连接点
例如:ArithmethicCalculator的所有方法实际上都是连接点,即连接点是程序类中客观存在的事务。AOP通过切点定位到特定的连接点。
类比:连接点相当于数据库中的记录,切点相当于查询条件。切点和连接点不上一对一的关系,一个切点匹配多个连接点,切点通过org.springframework.aop.Pointcut接口进行描述,它使用类和方法作为连接点的查询条件
AspectJ:java社区里最完整最流行的AOP框架。
在Spring2.0以上版本中,可以使用基于AspectJ注解或基于XML配置的AOP
在spring中启用AspectJ注解支持
要在Spring应用中使用AspectJ注解,必须在classpath下包含AspectJ类库:aopalliance.jar、aspectj.weaver.jar和spring-aspects.jar
将aop Schema添加到<beans>根元素中
要在Spring IOC容器中启用AspectJ注解支持,只要在Bean配置文件中定义一个空的XML元素<aop:apectij-autoproxy>
当spring IOC容器侦测到Bean配置文件中的<aop:apectij-autoproxy>元素时,会自动为与AspectJ切面匹配的Bean创建代理。
利用方法签名名编写AspectJ切入点表达式
最经典的切入点表达式是根据方法的签名来匹配各种方法:
execution*com.easytop.spring.ArithmeticCalculator.*(..):匹配ArithmeticCalculator中声明的所有方法,第一个*代表任意修饰符及任意返回值,第二个*代表任意方法。..匹配任意数量的参数。若目标类与接口与该切面在同一个包中,可以省略包名。
execution public * ArithmeticCalculator.*(..):匹配ArithmeticCalculator接口的所有公有方法。
execution public doubleArithmeticCalculator.*(..):匹配AtithmeticCalculator中返回double类型数值的方法
execution public doubleArithmeticCalculator.*(double..):匹配第一个参数为double类型的方法,..匹配任意数量任意类型的参数
execution publicdoubleArithmeticCalculator.*(double,double):匹配参数类型为double,double类型的方法。

用基于XML的匹配声明切面

除了使用AspectJ注解声明切面,Spring也支持在Bean匹配文件中声明切面,这种声明式通过aop schema中的XML元素完成的。

正常情况下,基于XML的声明要优先于基于注解的声明。通过AspectJ注解,切面可以与AspectJ兼容,而基于XML的匹配则是Spring专有的,由于AspectJ得到越来越多的AOP框架支持,所以以注解风格编写的切面将会有更多重用的机会。
当使用XML声明切面时,需要在<beans>根元素中导入aop Schema
在Bean配置文件中,所有的Spring AOP配置都必须定义在<aop:config>元素内部,对于每个切面而言,都要创建一个<aop:aspect>元素来为具体的切面实现引用后端Bean示例。
切面Bean必须有一个标示符id,供<aop:aspect>元素引用

Spirng的AOP支持

Spring中的AIO代理有IOC容器负责生成、管理、其依赖关系也由IOC容器负责管理。Spring关于AOP支持有两种实现方法,一种是Java的动态代理,一种是Cglib。默认是使用动态代理的,但这种方式需要有接口,略显麻烦,这里就用后者。
首先引入SpringAOP的包,这里是用maven构建的项目,直接上maven依赖
org.springframework
spring-aop
4.3.11.RELEASE
sping核心配置文件中的配置
1.添加aop schema文件:
2.为了在应用中使用@AspectJ支持,Spring需要添加三个库:
aspectjweaver.jar
aspectijrt.jar
aopalliance.jar
maven依赖:
aopalliance
aopalliance
1.0
aspectj
aspectjweaver
1.5.3
aspectj
aspectjrt
1.2
完成上述步骤之后,项目可以用AspectJ来实现AOP了。
配置文件:
aop:pointcut:声明一个切点,切点可以理解为一个条件,切点决定了能够定位到哪些连接点上。连接点可以认为是某个方法,一个切点下可以有多个连接点。
aspect:代理,ref的值是一个类,这个可是代理类,类中可以定义代理的要实现的功能。
aop:before:前置通知,其实就是个方法,在连接点执行之前执行。
aop:after:后置通知,连接点执行之后执行。
aop:after-throwing:异常通知,在连接点出现异常后执行。
各种通知都有method属性,表示要执行的方法,方法需要在代理类中定义,pointcut-ref表示属于哪个切点,图中很显然就是myPointCut
以租房为例,写个小例子:
package cn.et.aop;import org.springframework.stereotype.Component;/** * 房东类  *///注册到配置文件@Componentpublic class AfHoserOwer{		public void seekMyHouse() {		System.out.println("安防房东租房");		int i=5/0;	}		public void addHouse() {		System.out.println("房东买了新房");	}}
package cn.et.aop;import org.aspectj.lang.JoinPoint;import org.springframework.stereotype.Component;/** * 通知类  */@Componentpublic class MyMessage {	/**	 * 前置通知	 */	public void beforeSeek(){		System.out.println("打扫卫生---");	}	/**	 * 后置通知	 */	public void afterSeek(){		System.out.println("收钱");	}	/**	 * 异常通知	 */	public void throwException(JoinPoint jp){		System.out.println("出现异常");		//获取错误对象地址		System.out.println(jp.getTarget());		//获取错误方法名		System.out.println(jp.getSignature().getName());	}	}
main
package cn.et.aop;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.support.GenericXmlApplicationContext;/** * spring实现aop 用了两种技术 * 	1.java动态代理(面向接口编程) *      2.cglib实现动态代理  * @author Administrator */public class Test {	static ConfigurableApplicationContext context;	static{		context = new GenericXmlApplicationContext("classpath:spring.xml");	}	public static void main(String[] args) {		AfHoserOwer bean = (AfHoserOwer) context.getBean("afHoserOwer");		//bean.seekMyHouse();		bean.addHouse();		context.close();	}}
通过上面所示,我们是直接输出在控制台,我们通常利用通知和日志联合使用,利于以后的统计(比如统计访问量,购买商品总数量)
加载日志依赖
log4j
log4j
1.2.17
创建log4j配置文件
### 设置全局控制机制 ###log4j.rootLogger = debug ,  stdout ,  D #log4j日志分为5中级别#  		debug  	调试(开发阶段)#  		info	运行信息(测试或者运行阶段)#  		warn	警告消息#  		error	程序错误消息#  		fatal	系统错误消息#  	通过5种级别输出的日志 打印在文件中#  		int i=0;#  		my.debug("定义了变量i");#  		i=10#  		my.debug("变量i设置了值10");#  		......#  		#  	全局控制机制#  		root=debug#  	#  	日志级别 fatal>error>warn>info>debug  所有全局控制中设置的级别以下的所以日志都不打印#  		#  		比如:info 不打印debug#  			fatal 前面四个日志都不打印### 输出到控制台 ###log4j.appender.stdout = org.apache.log4j.ConsoleAppenderlog4j.appender.stdout.Target = System.outlog4j.appender.stdout.layout = org.apache.log4j.PatternLayoutlog4j.appender.stdout.layout.ConversionPattern =%-d{yyyy-MM-dd HH\:mm\:ss}  [ %t\:%r ] - [ %p ]  %m%n### 输出到日志文件 ###log4j.appender.D = org.apache.log4j.DailyRollingFileAppender#日志位置log4j.appender.D.File = c:/logs/log.loglog4j.appender.D.Append = truelog4j.appender.D.Threshold = DEBUG ## 输出DEBUG级别以上的日志log4j.appender.D.layout = org.apache.log4j.PatternLayoutlog4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH\:mm\:ss}  [ %t\:%r ] - [ %p ]  %m%n
配置文件
用户类
package cn.et.logs;import org.springframework.stereotype.Component;@Componentpublic class User {	public void login(){		System.out.println("用户登录");	}	public void buy(){		System.out.println("用户购物");	}}
管理类
package cn.et.logs;import org.springframework.stereotype.Component;@Componentpublic class Admin {		public void addCom(){		System.out.println("添加商品");	}}
通知类
package cn.et.logs;import org.apache.log4j.Logger;import org.aspectj.lang.JoinPoint;import org.springframework.stereotype.Component;@Componentpublic class MyMessage {	Logger logger = Logger.getLogger(MyMessage.class);	public void beforeSeek(JoinPoint jp){		//获取方法名		String name = jp.getSignature().getName();		logger.debug("方法"+name+":调用");	}}
main
package cn.et.logs;import org.springframework.context.ConfigurableApplicationContext;import org.springframework.context.support.GenericXmlApplicationContext;public class Test {	static ConfigurableApplicationContext context;	static{		context = new GenericXmlApplicationContext("classpath:/cn/et/logs/spring.xml");	}	public static void main(String[] args) {		User user = (User) context.getBean("user");		Admin admin = (Admin) context.getBean("admin");		for(int i=0;i<3;i++){			user.buy();			admin.addCom();		}		for(int i=0;i<5;i++){			user.buy();			user.login();		}		context.close();	}}
执行
查看日志
这样我们就可以通过工具从该文件里统计出各方法调用的次数
你可能感兴趣的文章
MySQL | MySQL 主从复制
查看>>
Zookeeper | 环境搭建
查看>>
Zookeeper | 实现服务注册与发现
查看>>
Zookeeper | 分布式锁的实现
查看>>
Redis | 分布式锁的实现
查看>>
《反本能》读后感(一) | 是什么阻止了我们成功
查看>>
Redis | 事务机制
查看>>
Elasticsearch | 安装(Linux 环境)
查看>>
Elasticsearch | Kibana 安装与使用
查看>>
SpringBoot 2.0 | SpringBoot 集成 Elasticsearch
查看>>
分布式的冰与火 | 分布式日志收集 ELK 搭建
查看>>
分布式的冰与火 | 分布式事务解决方案 LCN
查看>>
Spring Cloud Alibaba 极速通关 | 分布式系统的流量防卫兵 Sentinel
查看>>
面试必问的设计模式 | 外观模式
查看>>
面试必问的设计模式 | 状态模式
查看>>
Spring-Cloud-Finchley | 路由网关 GateWay
查看>>
Spring Cloud Alibaba 极速通关 | Sentinel 整合 Apollo 实现配置持久化
查看>>
面试必问的设计模式 | 模板方法模式
查看>>
Redis | Redis 主从复制
查看>>
面试必问的设计模式 | 代理模式
查看>>