spring与Aop

AOP面向方面/面向切面编程

AOP将分散在系统中的功能块放到一个地方- 切面

可以声明式的定义何时何地应用这些功能,而不是在需要新功能的地方修改代码

好处
每个业务逻辑放在一个地方,不是分散到代码各个角落。业务模块只包含核心功能,辅助功能转到切面中,使其更加清晰。
关注的是共同处理。通过spring配置把某个作用应用到多个点上。提高灵活性

重要术语
切面(Aspect):就是你要实现的交叉功能---共通业务处理可以被切入到多个目标对象。并且多次使用
连接点(Joinpoint):应用程序执行过程中插入切面的地点
通知(Advice):通知切面的实际实现
切入点(Pointcut):定义通知应用在哪些连接点---连接点的集和
目标对象(Target Object):被通知的对象,也就是目标对象
代理(Proxy):将通知应用到目标对象后创建的对象---AOP中容器返回的对象是代理对象。用户在使用的时候,由代理对象调用切面组件和目标对象的功能
织入(Weaving):将切面应用到目标对象从而创建一个新的代理对象的过程


我们进入案例01---需要引入额外jar包,这点要注意通过实验慢慢引入
1:编写dao类用来进行正常的增删改查
2:编写切面用于共同业务处理---记录日志
3:编写我们的xml文件
<aop:config>
<aop:pointcut id="userPointCut"
expression="execution(* com.wode.service.UserService.add(..))" />
<aop:aspect id="logAspect" ref="userLogger">
<aop:before method="testLogger" pointcut-ref="userPointCut"></aop:before>
</aop:aspect>
</aop:config>

重要概念:
<aop:config>---指的是需要进行AOP配置了

<aop:pointcut id="userPointCut"
expression="execution(* com.wode.service.UserService.*(..))" />----设置切入点
expression这个用于设置规则
execution--具体规则类容 我们现在设置的就是不用管返回值+包类.*(所有方法)不限定参数类型
我们可以做这样的实验
01:expression="execution(* com.wode.service.UserService.delete(..))"
02:expression="execution(void com.wode.service.UserService.*(..))" />
03:<aop:pointcut id="userPointCut" expression="execution(* com.wode.service.impl.UserServiceImpl.user*(..))"/>
看看有什么不一样
这里是使用的切入点表达式我们可以看一下规则-参考网站可以参照一下表达式.txt从网上下载的
注意我们还可以使用一种within的方式
within用于匹配指定类型内的方法执行--这里和上边对应上边用于指定方法(更灵活)
例如<aop:pointcut id="userPointCut" expression="within(com.wode.service.UserService)" />和上边的就一个意思
within(com.wode.service.*)---包下边所有的方法
within(com.wode.service..*)这样写的话就包括了子包

<aop:aspect>--用于配置切面
id---起id名称
ref--指向我们的切面文件
<aop:before method="testLogger" pointcut-ref="userPointCut"></aop:before>--指定方法以及在什么时候发生以及他的切入点

 注意除了aop:before是指在之前发生,这里总共有这么多类型可以让我们选择

默认提供通知类型
<aop:before>---前置通知
<aop:after-returning>方法调用之后,但是如果有异常将不通知
<aop:after>最终通知
<aop:after-throwing>方法调用发生异常之后
<aop:around>环绕通知,也就是方法之前和之后

分步骤测试 演示前置和后置通知

这里需要注意的是Around他相当于之前所有返回的结合体,需要注意的是它的返回值异常-----方法返回值和我们切面的返回值

但是它非常好用,我们可以通过它通过ProceedingJoinPoint--来获取方法名、类名等等东西

演示ProceedingJoinPoint 案例
Object obj=pjo.proceed();
System.out.println(obj);
System.out.println(pjo.getTarget());
System.out.println(pjo.getArgs()[0]);
System.out.println(pjo.getSignature().getName());
System.out.println(pjo.getTarget().getClass().getName());


除了他之外,我们的后置通知也能获取方法的返回值需要做的
不过需要我们做的是
target
public int add(int i){
System.out.println("增加了");
return 99;
}
1:在xml中做改变
<aop:after-returning method="testLoggerafetr" returning="test" pointcut-ref="userPointCut"></aop:after-returning>
2:在切面中做改变
public void testLoggerafetr(int test) throws Throwable{
System.out.println("后置记录日志");
System.out.println(test);
}

最后我们来测试异常通知aop:after-throwing
理所当然的,我们也可以在切面中获取这个异常
<aop:after-throwing method="testLoggerException" throwing="e" pointcut-ref="userPointCut"></aop:after-throwing>
public void testLoggerException(Exception e) throws Throwable{
System.out.println("异常记录日志"+e);
}

最后:福利(使用注解)

1:删除所有在xml中关于AOP的东西
2:加入这样一句话
<aop:aspectj-autoproxy />
3:使用注解
@Component("userLogger")
@Aspect
4:定义切入点:
@Pointcut("within(com.wode.service.UserService)")
public void pointCut(){}
5:设置
@Before("pointCut()")
public void testLogger() throws Throwable{
System.out.println("前置记录日志");
}

切入点表达式第二种方式:
@Pointcut("execution(* com.wode.service.UserService.*(..))")
public void pointCut(){}

我们来看特殊的
@Around("pointCut()")
public int testLogger(ProceedingJoinPoint pjo) throws Throwable{
System.out.println("前置记录日志");
Object obj=pjo.proceed();
System.out.println(obj);
System.out.println(pjo.getTarget());
System.out.println(pjo.getArgs()[0]);
System.out.println(pjo.getSignature().getName());
System.out.println(pjo.getTarget().getClass().getName());
return (int) obj;
}

这里还是需要注意返回值的问题

异常通知
@AfterThrowing(pointcut="pointCut()",throwing="e")
public int testLogger(Exception e) throws Throwable{
System.out.println("前置记录日志"+e);
return 1;
}


当然别的通知中我们需要获得参数啊什么的就可以这么做
@Before("pointCut()")
public int testLogger(JoinPoint jpt) throws Throwable{
System.out.println("前置记录日志");
System.out.println(jpt.getTarget());
return 1;
}

常用表达式:

任意公共方法的执行:
execution(public * *(..))
任何一个以“set”开始的方法的执行:
execution(* set*(..))
AccountService 接口的任意方法的执行:
execution(* com.xyz.service.AccountService.*(..))
定义在service包里的任意方法的执行:
execution(* com.xyz.service.*.*(..))
定义在service包或者子包里的任意方法的执行:
execution(* com.xyz.service..*.*(..))
在service包里的任意连接点(在Spring AOP中只是方法执行) :
within(com.xyz.service.*)
在service包或者子包里的任意连接点(在Spring AOP中只是方法执行) :
within(com.xyz.service..*)
实现了 AccountService 接口的代理对象的任意连接点(在Spring AOP中只是方法执行) :
this(com.xyz.service.AccountService)
'this'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得代理对象可以在通知体内访问到的部分。
实现了 AccountService 接口的目标对象的任意连接点(在Spring AOP中只是方法执行) :
target(com.xyz.service.AccountService)
'target'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得目标对象可以在通知体内访问到的部分。
任何一个只接受一个参数,且在运行时传入的参数实现了 Serializable 接口的连接点 (在Spring AOP中只是方法执行)
args(java.io.Serializable)
'args'在binding form中用的更多:- 请常见以下讨论通知的章节中关于如何使得方法参数可以在通知体内访问到的部分。 请注意在例子中给出的切入点不同于 execution(* *(java.io.Serializable)): args只有在动态运行时候传入参数是可序列化的(Serializable)才匹配,而execution 在传入参数的签名声明的类型实现了 Serializable 接口时候匹配。
有一个 @Transactional 注解的目标对象中的任意连接点(在Spring AOP中只是方法执行)
@target(org.springframework.transaction.annotation.Transactional)
'@target' 也可以在binding form中使用:请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。
任何一个目标对象声明的类型有一个 @Transactional 注解的连接点(在Spring AOP中只是方法执行)
@within(org.springframework.transaction.annotation.Transactional)
'@within'也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。
任何一个执行的方法有一个 @Transactional annotation的连接点(在Spring AOP中只是方法执行)
@annotation(org.springframework.transaction.annotation.Transactional)
'@annotation' 也可以在binding form中使用:- 请常见以下讨论通知的章节中关于如何使得annotation对象可以在通知体内访问到的部分。
任何一个接受一个参数,并且传入的参数在运行时的类型实现了 @Classified annotation的连接点(在Spring AOP中只是方法执行)
@args(com.xyz.security.Classified)

一:xml配置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-3.0.xsd 
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <context:component-scan base-package="com.wode">
    </context:component-scan>
    <aop:config>
        <aop:pointcut id="userPointCut" expression="execution(* com.wode.service.impl.UserServiceImpl.user*(..))"/>
        <aop:aspect id="logAspect" ref="userLog">
            <aop:before method="addLogger" pointcut-ref="userPointCut"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>

二:注解配置

<?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/aop 
    http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
    http://www.springframework.org/schema/context
    http://www.springframework.org/schema/context/spring-context-4.2.xsd">
    <!-- 启动注解 -->    
    <context:component-scan base-package="com.wode">
    </context:component-scan>
        
    <aop:config>
        <aop:pointcut id="userPointCut" expression="execution(int com.wode.service.*.*(..))" />
        <aop:aspect id="logAspect" ref="userLog">                    
            <aop:around method="testLogger" pointcut-ref="userPointCut"/>
        </aop:aspect>
    </aop:config>
    
</beans>
原文地址:https://www.cnblogs.com/xieshunjin/p/5797199.html