Spring Aop
1.Spring AOP概述:
AOP:面向切面编程,通过预编译方式和运行时期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可用性,同时提高开发效率。
AOP是一个概念,并没有设定具体语言的实现,它能客服那些只有但继承特性语言的缺点,Spring2.0之后整合Aspentj第三方AOP技术。
AOP底层实现
AOP分为静态AOP和动态AOP。静态AOP是指Aspectj实现的AOP,他将切面代码直接编译到Java类文件中。动态AOP是指将切面代码进行织入实现AOP。Spring的AOP为动态AOP,实现的技术为:JDK提供的动态代理技术和CGLIB(动态字节码增强技术)
- JDK动态代理
在运行时,在JVM内部动态生成class字节码对象(Class对象)
jdk动态代理只针对接口操作 - CGLIB动态代理
CGLIB是一个开源项目
是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展java类实现java接口。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。
SpringAOP编程 - Spring的传统aop编程
1.前置通知,目标方法执行前增强
2.后置通知,目标方法执行后增强
3.环绕通知,目标方法执行前后进行增强
4.异常通知,目标方法抛出异常后增强
5.引介通知,在目标类中一些新的方法或属性
第一步编写目标(target)
@Override
public void add() {
System.out.println("orderService add...");
}
@Override
public void update() {
System.out.println("orderService update...");
}
第二步增强(advice)
public class OrderHelper implements MethodBeforeAdvice,AfterReturningAdvice,MethodInterceptor{
@Override
public void before(Method arg0, Object[] arg1, Object arg2) throws Throwable {
System.out.println("前置通知");
}
@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2, Object arg3) throws Throwable {
System.out.println("后置通知");
}
@Override
public Object invoke(MethodInvocation mi) throws Throwable {
System.out.println("环绕前");
Object proceed = mi.proceed();
System.out.println("环绕后");
return proceed;
}
}
第三步在applicationContext.xml文件中配置
<!-- 目标target -->
<bean id="orderService" class="com.baidu.aop.OrderServiceImpl"></bean>
<!-- 通知advice -->
<bean id="orderServiceAdvice" class="com.baidu.aop.OrderHelper"></bean>
<!-- 定义切点pointcut -->
<bean id="orderServicePointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
<property name="mappedNames">
<list>
<value>add</value>
<value>update</value>
</list>
</property>
</bean>
<!-- 切面aspect=pointcut+advice -->
<bean id="orderServiceAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="orderServiceAdvice"/>
<property name="pointcut" ref="orderServicePointCut"/>
</bean>
<!-- 代理proxy -->
<bean id="orderServiceproxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="orderService"></property>
<property name="interceptorNames" value="orderServiceAspect"/>
<property name="proxyInterfaces" value="com.baidu.aop.IOrderService"></property>
</bean>
第四步测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class AopTest {
@Autowired
@Qualifier("orderServiceproxy")
private IOrderService orderService;
@Test
public void test1(){
orderService.add();
}
}
对于基于代理的aop开发,可以简化
<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean>
Spring的配置文件拆分
<import resource="./aop1.xml">
传统aop开发基于aspectJ切点表达式
基于aspectJ切点传统开发
我们可以使用spring提供的aop:xxx标签来简化上述操作
在这个操作中,我们配置减少,但是他依赖于aspectj框架部分内容
注意事项:
1.在配置文件中导入aop的名称空间
<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/context http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
以下配置简化了之前的操作
<!-- 目标target -->
<bean id="orderService" class="com.baidu.aop.OrderServiceImpl"></bean>
<!-- 通知advice -->
<bean id="orderServiceAdvice" class="com.baidu.aop.OrderHelper"></bean>
<!-- 使用aop相关的标签来完成配置 -->
<aop:config> <!-- 它帮助我们完成自动代理功能 -->
<aop:pointcut expression="excution(* *.*Order(..))" id="mypointcut"/> <!-- 声明我们的切点 -->
<aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="mypointcut"/> <!-- 声明我们的切面 -->
</aop:config>
- Spring整合aspectj框架实现的AOP
在Spring2.0以后它支持jdk1.5注解,在整合aspectj后可以使用aspectj语法,可以简化开发
Aspect切面=切点+通知(多个切点与多个通知的组合)
Aspectj它是一个第三方框架,spring从2.0后可以使用aspectj框架的部分语法
一:AspectJ框架它定义的通知类型有6种
1.前置通知Before相当于BeforeAdvice
2.后置通知AfterReturning相当于AfterReturningAdvice
3.环绕通知Around相当于MethodInterceptor
4.抛出通知AfterThrowing相当于ThrowAdvice
5.引介通知DeclareParents相当于IntroductionInterceptor
6.最终通知After不管是否异常,该通知都会执行
两部分:
(1)基于xml配置方案
前值通知
<aop:config>
<aop:aspect ref="userServiceAdvice">
<aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
</aop:aspect>
</aop:config>
后置通知
<aop:after-returning method="afterReturning" pointcut-ref="delPointCut" />
环绕通知
public Object around(ProceedingJoinPoint pjp)throws Throwabl{
system.out.printn("环绕前...");
Object value=pjp.proceed();
System.out.prinle("环绕后...");
return value;
}
<aop:around method="around" pointcut-ref="delPointCut"/>
异常抛出
<aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut"/>
注意:目标行为只有抛出了异常后才会执行这个增强方法
最终通知
<aop:after method="after" pointcut-ref="delPointCut"/>
无论是否有异常,最终通知都会执行
二:关于通知上的参数
1.前置通知上可以添加JoinPoint参数
通过它可以获取目标相关的信息
// 前置通知
public void before(JoinPoint jp) {
System.out.println("拦截的目标类:" + jp.getSignature().getDeclaringTypeName());
System.out.println("拦截的方法名称:" + jp.getSignature().getName());
System.out.println("前置通知");
}
使用前置通知可以完成日志记录,权限控制
2.在后置通知上添加的参数
// 后置通知
public void afterReturning(JoinPoint jp, Object val) {
System.out.println("目标方法返回值:" + val);
System.out.println("后置通知");
}
第二个参数val它可以获取目标方法的返回值
注意:需要在配置文件中配置
<aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
3.环绕通知上的参数
// 环绕通知
public Object around(ProceedingJoinPoint pjp) throws Throwable {
System.out.println("环绕前....");
Object value = pjp.proceed(); // 执行目标行为
System.out.println("环绕后....");
return value;
}
它是我们开发中应用最多的,可以完成日志、权限操作、性能监控、事务管理
4.抛出异常通知上的参数
// 异常抛出通知
public void afterThrowing(JoinPoint jp,Throwable ex) {
System.out.println("发现了异常。。。。"+ex);
}
第二个参数Throwable它是用于接收抛出的异常
注意:需要在配置文件中声明
<aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut" throwing="ex"/>
5.最终通知上的参数
// 最终通知
public void after(JoinPoint jp) {
System.out.println(jp.getSignature().getName());
System.out.println("最终通知");
}
可以使用最终通知完成资源释放
三:关于代理方式的选择
在Spring的aop开发中,它使用的是代理方案,代理实现有两种:
1.jdk的proxy
2.CGlib
Spring框架默认情况下,会对有接口的类使用proxy代理。没有接口的类使用cglib代理。
<aop:config proxy-target-class="false">
proxy-target-class的值默认是false它代表有接口使用proxy代理
如果现在对目标要使用cglib代理(不考是否有虑接口)
只需要proxy-target-class=“true”
(2)基于annotation方案
第一步:编写目标
@Service
public class CustomerServiceImpl implements ICustomerService{
@Override
public void save() {
System.out.println("customerService save...");
}
@Override
public void search() {
System.out.println("customerService search...");
}
@Override
public void update() {
System.out.println("customerService update...");
}
}
在spring的配置文件中配置扫描注解
<context:component-scan base-package="com.baidu"/>
第二步:编写增强(advice)
//通知
@Component
@Aspect//声明当前的bean就是一个切面
public class CustomerServiceHelper {
//前置通知
@Before("execution(*.*.s*(..))")
public void before(){
System.out.println("前置通知...");
}
}
使用@Aspect来声明切面
使用@Before来声明前置通知
注意:必须在Spring的配置文件中开启aspectJ注解自动代理功能。
<!-- 开启aspectj注解自动代理 -->
<aop:aspectj-autoproxy/>
第三步:测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class AspectAnnotationTest {
@Autowired
private ICustomerService customerService;
@Test
public void test1(){
customerService.save();
}
其他通知类型及参数
后置通知
//后置通知
@AfterReturning(value="execution(* *.update(..))",returning="value")
public void afterReturning(JoinPoint jp,Object value){
System.out.println("后置通知,目标方法的返回是"+value);
}
环绕通知
//环绕通知
@Around("execution(* *.s*(..))")
public Object around(ProceedingJoinPoint pjp) throws Throwable{
System.out.println("环绕前...");
Object value = pjp.proceed();
System.out.println("环绕前...");
return value;
}
异常抛出通知
//异常抛出通知
@AfterThrowing(value="execution(* *.s*(..))",throwing="ex")
public void afterThrowing(JoinPoint jp,Throwable ex){
System.out.println("异常抛出通知"+ex);
}
最终通知
//最终通知
@After("execution(* *.s*(..))")
public void after(){
System.out.println("最终通知");
}
使用@Pointcut注解定义切点
@Pointcut("execution(* *.s*(..))")
private void mypointcut(){
}