【理论】Spring AOP

面向切面:利用Spring可以遵循完美的面向对象设计,编写松耦合代码,但其中还有一些核心业务与辅助性业务交织在一起,使得代码杂乱,难于维护;AOP的思想则将这些在整个系统使用较多的非核心业务的辅助性业务整合出来让主业务模块被动的实现辅助性功能(实现对日志的管理、事务的处理、SQL注入等);
核心问题解决:1、实现系统核心业务的清晰,使组件更加专注自身业务 2、便于系统的维护
AOP的原理:将分布在系统很多组件中的交叉业务,整合在一个服务模块中,这些模块通过组件覆盖的横切面提供服务。这种实现从点的服务到面的服务的思维就是面向切面的思维。(提高了代码的清晰性、模块化、可测试性)
AOP(面向切面编程)核心概念
切面(Aspect):切面实现交叉功能,是应用系统模块化的一个切面或领域(日志记录)
连接点(Joinpoint):是应用程序插入切面的地点,可是方法调用,异常抛出,修改的字段;在Spring中特指对某个方法的调用;
通知(Advice):定义切面行为,是指某个切入点执行的代码,应该与某个切入点表达式相连,并满足该切入点的连接点上运行(around、before、after、Throws等类型)
切入点(Point cut):定义什么地方执行,切入点通知应该应用在哪些连接点;通知可以应用到框架支持的任何连接点;但这你并不希望如此,切入点让你指定通知应用到什么地方。通常指定类名和方法名,或者匹配类名和方法名的正则表达式来指定切入点。并可以做到动态根据条件决定是否应用切面,如方法参数值;
引入(Instroduction):引入允许你为已存在的类添加方法和属性;可以在不改变已存在类的情况下将这个引入给他们新的行为和状态;
目标对象(Target):是被通知的对象,可以是自己编写的类,也可以是自己要添加的定制的第三方类。
代理(Proxy):AOP创建框架的对象,用来实现切面契约,包括通知方法执行;
织入(Weaving):将切面应用到目标对象,从而创建一个新的代理对象的过程。切面在指定接入点被织入到目标对象中,织入发生在目标对象的生命周期的多个点上;
切面编程思想是spring提出的,结合Aspects框架使用;
Spring提供了4种实现AOP的方式:
1.经典的基于代理的AOP
2.@AspectJ注解驱动的切面
3.纯POJO切面
4.注入式AspectJ切面
举例:水的处理,过滤
在业务层对事务进行处理, 则出现了在核心代码中包含了非核心的代码;(交叉业务)
需求:记录整个系统的操作日志(将数据的操作累内容进行记录);某人从登陆到退出之后进行的增删改;

运用到动态代理模式:在实际执行中使用的代理对象;(针对现有java,为了隐藏自己,真实类的代理类同共同的行为;代理有真实的引用,真实真正干事情 )
静态代理:有一个代理对象,看得到代理的整个过程和流程; 【查看实战:动态代理模式】
动态代理:程序有真实类,没有代理类;永远不会出现代理类,只会出现代理对象;JDK实现继承Proxy;
J DK动态代理
主要使用到 InvocationHandler 接口和 Proxy.newProxyInstance() 方法。 JDK动态代理要求被代理实现一个接口,只有接口中的方法才能够被代理 。其方法是将被代理对象注入到一个中间对象,而中间对象实现InvocationHandler接口,在实现该接口时,可以在 被代理对象调用它的方法时,在调用的前后插入一些代码。而 Proxy.newProxyInstance() 能够利用中间对象来生产代理对象。插入的代码就是切面代码。所以使用JDK动态代理可以实现AOP。

【代码实现】
二、在容器中配置切面编程:
1、导入aop 的配置;
<?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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd">
<!-- 开启切面的自动代理功能 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

2、开启切面中的自动代理;然后在切面中实现注解;
@Aspect :将一个java类标记为一个切面;一个切面就对应着一个交叉业务;

3、在java类(logAspect)中定义:
1-1、定义事务切面:放在持久层中
1-2、定义日志切面:放在业务中
@Aspect//将该JAVA类标记为一个切面,一个切面就对应着一个交叉业务
@Component
public class LogAspect {

}
前置通知:
@Before(value="execution(public* com.lovo.service.impl.*.*(..))"):annotation包下的
value :定义什么情况下执行代码;
execution:连接点
*: 代表返回类型
* com.lovo.service.impl*:下所有的类
* com.lovo.service.impl.User*. : 所有User开头的类
* com.lovo.service.impl.*.*(..): service.impl下的所有类及所有方法
(..):代表传参;
JoinPoint jp 为目标方法;
目标对象:jp.getTarget();
目标方法:jp.getSignature().getName();
目标方法接收的参数:jp.getArgs();
通过记录以上信息,就可以知道操作者都执行了哪些程序并,操作了什么数据;
如何加入操作人的信息呢?method.invoke(obj,"xiaowang")
得到方法:Method m = c.getMethod("setName",String.class); 方法名,传入参数;
得到对象对应的类: Class c = obj.getClass();
/**
* 定义一个前置通知
*/
@Before(value="execution(* com.lovo.service.impl.*.*(..))")
public void beforeAdvice(JoinPoint jp){
Object obj = jp.getTarget();//目标对象
String method = jp.getSignature().getName();//目标方法
Object[] objts = jp.getArgs();//传递的参数
System.out.println("我是前置通知,我执行在目标对象【"+obj+"】的目标方法执行【"+method+"】之前!"+ ",目标方法接收到的参数是:"+Arrays.toString(objts));
//该通知可以做一些,目标方法执行之前的初始设置
Class c = obj.getClass();//得到对象所对应的类型
Method m;
try {
m = c.getMethod("setName",String.class);
m.invoke(obj,"xiaomiang");
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
后置返回通知 执行在目标方法正常执行完成之后;(没有正常执行完成则不会出现);
/**
* 后置返回通知
* @param jp 连接点
* @param ret 返回对象
*/
@AfterReturning(returning="ret",pointcut="execution(* com.lovo.service.impl.*.*(..))")
public void afterReturningAdvice(JoinPoint jp,Object ret){
System.out.println("我是后置返回通知,我执行在目标方法正常执行完成之后!我收到的结果是:"+ret);
}
后置抛出异常通知
/**
* 后置异常通知
* @param jp 连接点
* @param tx 异常对象
*/
@AfterThrowing(throwing="tx",pointcut="execution(* com.lovo.service.impl.*.*(..))")
public void afterThrowingAdvice(JoinPoint jp,Throwable tx){
}
后置通知:不论是否排除异常,都会执行,并且执行在后置抛出异常之前;
@After(execution(public* com.lovo.service.impl.*.*(..))")
public void after(JoinPoint jp){
System.out.println("我是后置通知,无论目标方法是否抛出异常,我都会执行!");
}
环绕通知:相当于before+after,是唯一一个可能抛出异常的通知;1、环绕通知通知包裹的方法执行,才能执行; 2、并且环绕可以修改其中传入的数据参数,也可以修改返回数据;
能不用就不用】切面很容易造成大面积的问题;能用before+after解决就用这个解决;
@Around("execution(public* com.lovo.service.impl.*.*(..))"")
public Object aroundAdvice(ProceedingJoinPoint jp)throws Throwable{
UserBean user = new UserBean(2L, "xiaohuang", "111111");
Object[] objts = jp.getArgs();
objts[0] = user;
Object obj = jp.proceed(objts);
System.out.println(obj);
obj = 2;
return obj;
}
ProceedingJoinPoint jp:目标对象
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值