Spring AOP应用
1.aop几个概念:
横切关注点: 对哪些方面进行拦截,拦截后怎么处理。
切面(aspect):切面是横切关注点的抽象。
连接点(joinpoint):被拦截的方法
切入点(pointcut):对连接点进行拦截的定义。
通知(advice):拦截到连接点之后要执行的代码
目标对象:代理的目标对象
织入
引入
2.主要功能:
日志记录
性能统计
安全控制
事物处理
异常处理
3.advice类型:
前置通知(before advice)
返回后通知(after returning advice)
抛出异常后通知(after throwing advice)
后通知(after advice)
环绕通知(around advice)
4.Spring对AOP的支持
Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。
springAOP 的原理:
- 当启动springy器的时 , 通过扫描或者创建对象
解析< aop:config >
1、 解析切入点表达式,把表达式解除出来以后和spring中的bean进行匹配
2、 如果匹配成功,则为该bean创建代理对象,在创建代理对象的过程中,把目标方法和通知结合在一起了 如果匹配不成功,则直接报错
3、 当客户端调用getBean时 , 如果该对象有代理对象,则返回代理对象 , 如果该对象没有代理对象,则返回对象本身
简单来说就是把 各种通知 绑定 到 目标方法上
- 说明:
spring容器内部会自动判断:
如果目标类实现了接口,则采用jdkproxy 如果目标类没有实现接口,则采用eglibproxy
目标类
@Component
public class ServiceImp implements Service {
@Override
public boolean save() {
System.out.println("调用dao层方法向数据库存数据");
return true;
}
}
切面拦截类
@Component
public class Logger {
//前置通知,方法执行之前执行
public void startMethod(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName()); //获取目标方法名称
System.out.println("前置通知 : 初始化logger~~~~~~~~~");
}
//后置通知,方法正常执行完毕之后执行
public void getReturnMethod(Object user) {
System.out.println("后置通知: " + user);
}
//最终通知,方法执行之后执行(不管是否发生异常)
public void finallyMethod() {
System.out.println("最终通知 : 结束logger~~~~~~~~~~~");
}
//异常通知,在方法抛出异常之后执行
public void throwMethod(Throwable throwable) {
System.out.println("异常通知:" + throwable.getMessage());
}
******************************************************************************************
/*
* 环绕通知 一般单独使用
* 如果不执行joinPoint.proceed(); 目标方法是不执行的
* 在目标方法上下文添加内容 (类似 jdk _ InvocationHandler代理拦截)
* 可以将以上通知糅合起来
* 和其他通知区别是: 环绕通知能控制目标方法执行
*
* 环绕通知 相当于 通知方法中包含 目标方法 ,如果通知方法返回值会void ,则 客户端调用目标方法时返回的是null
* 如果有后置通知,环绕通知返回的值就是后置通知获取的值
*
* ProceedingJoinPoint (程序连接点 :客户端代理对象调用哪个方法,哪个方法就是连接点)
* JoinPoint 能够调用该API得到连接点的一些信息
* */
public Object aroundMethod(ProceedingJoinPoint joinPoint) {
try {
System.out.println("环绕通知:~~~~~~~");
Object object = joinPoint.proceed(); //执行目标方法,返回的是该目标方法的返回值
System.out.println("目标方法返回值 : " + object);
return object; //需要重新将连接点的返回值返回出去
} catch (Throwable throwable) {
throwable.printStackTrace();
return "flase";
}
}
}
调用目标类的类
@Component
public class Component {
@Resource
private Service service;
public void save(){
service.save();
}
}
Spring 容器 applicationContext.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: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
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.2.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--配置切面 注意每个切面的前后顺序,影响程序的先后执行-->
<aop:config>
<!--expression 切入点表达式 : 锁定要绑定切面目标类的方法-->
<aop:pointcut id="former" expression="execution(* AOP.springaop.service.ServiceImp.*(..))"/>
<!--ref:引向 切面拦截 (将切面绑定到目标类方法)-->
<!--logger 日志切面拦截-->
<aop:aspect ref="logger">
<!--前置通知 : 在目标方法运行前加切面拦截-->
<aop:before method="startMethod" pointcut-ref="former"/>
<!--后置通知 获取目标方法的返回值 )-->
<aop:after-returning method="getReturnMethod" pointcut-ref="former" returning="user"/>
<!--最终通知 无论有无异常都执行-->
<aop:after method="finallyMethod" pointcut-ref="former" />
<!--异常通知 获取目标方法抛出的异常信息-->
<aop:after-throwing method="throwMethod" pointcut-ref="former" throwing="throwable"/>
******************************************************************************************
*
* <!--环绕通知 更为强大的通知,可以糅合 以上的通知!!! 一般单独使用
* 如果后面还有 befor切面 则执行befor切面,再执行目标方法-->
* <aop:around method="aroundMethod" pointcut-ref="former"/>
*
******************************************************************************************
</aop:aspect>
<!--security 安全框架 ,一个切点可以配置多个切面-->
<aop:aspect ref="security">
<aop:before method="security" pointcut-ref="former"/>
</aop:aspect>
</aop:config>
<!--类扫描机制-->
<context:component-scan base-package="AOP.springaop"/>
</beans>
测试
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("AOP/springaop/applicationContext.xml");
/*
* spring AOP执行的步骤:
* 为所有bean实例化
* 为目标对象创建代理对象
* 如果注入:调用set注入代理对象
* */
//用Service接收
// Service service=(Service) context.getBean("serviceImp");
// service.save();
// 此时切面绑定到service层,可以用controller接收,因为controller调用了service该代理对象
Component component = (Component) context.getBean("component");
component.save();
}
}
spring-AOP注解的形式
** 各种通知的注解用法**
@Component
@Aspect //该注解说明该类是切面类
public class Method {
/*
*定义一个方法,用于声明切点表达式,该方法一般没有方法体
*@Pointcut用来声明切点表达式
*通知直接使用定义的方法名即可引入当前的切点表达式
*/
@Pointcut("execution(* AOP.spring_aop_throw.service.userServiceImp.*(..))")
public void former() {
}
//前置通知,方法执行之前执行
@Before("former()")
public void start(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature().getName());
System.out.println("前置通知~~~~");
}
//后置通知,方法正常执行完毕之后执行
@AfterReturning(value = "former() ", returning = "result")
public void getReturnMethod(JoinPoint joinPoint, Object result) {
System.out.println("后置通知: " + result);
}
//最终通知,方法执行之后执行(不管是否发生异常)
@After(value = "former() ")
public void finaMethod(JoinPoint joinPoint) {
System.out.println("最终通知 : ~~~");
}
//异常通知,在方法抛出异常之后执行
@AfterThrowing(value = "former() ", throwing = "throwable")
public void throw1(JoinPoint joinPoint, Throwable throwable) {
System.out.println("异常通知:" + throwable.getMessage());
}
Spring容器开启自动注解扫描
<?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: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
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--</bean>-->
<!--类扫描机制-->
<context:component-scan base-package="AOP.spring_aop_throw"/>
<!--使AspectJ注解自动为匹配的类生成代理对象-->
<aop:aspectj-autoproxy/>
</beans>
环绕通知的用法 , 环绕通知一般单独使用
@Around("former()")
public Object aroundMethod(ProceedingJoinPoint joinPoint) {
try {
System.out.println("环绕通知:~~~~~~~");
Object object = joinPoint.proceed(); //执行目标方法,返回的是该目标方法的返回值
System.out.println("目标方法返回值 : " + object);
return object; //需要重新将目标连接点的返回值返回出去
} catch (Throwable throwable) {
throwable.printStackTrace();
return "flase";
}
}
JoinPoint 用法
能够调用该API得到连接点的一些信息, JoinPoint对象封装了SpringAop中切面方法的信息,在切面方法中添加JoinPoint参数,就可以获取到封装了该方法信息的JoinPoint对象.
-
ProceedingJoinPoint
(程序连接点 :客户端代理对象调用哪个方法,哪个方法就是连接点)