您的位置:首页 > 编程语言 > Java开发

Spring之Spring AOP

2016-07-08 20:53 316 查看

什么是AOP

AOP是Aspect Oriented Programming的缩写,意为:面向切面编程,是一种

通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术

spring AOP核心技术就是使用了Java 的动态代理技术

有jdk和cglib两种技术。

AOP的目的

将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码

AOP与OOP的区别

字面的区别是面向切面编程与面向对象编程。我概括为,区别是AOP是面向动作(V)编程、OOP是面向实体(n)编程。比如我们要封装一个学生类,类里有学生的属性和方法,此时我们需要用OOP编程,但是我们要封装一个日志记录的动作,就需要AOP编程了。

AOP的应用场景

一个不错的例子,有多个访问类需要访问一个封装好的数据类(比如学生类),访问类里有三个方法,涉及到线程,所以访问类有三个方法

数据上锁、数据访问、数据解锁。

我们可以发现几个问题

1.这个数据访问类的主要职责是访问数据,但是因为类中存在数据上锁和数据解锁的方法,当这个访问类需要被重用时,我们发现它只能被重用在需要上锁和解锁的场景,重用范围很窄。

2.这个访问类的主要功能是访问数据、我们当然希望这个类中只有访问数据的相关代码(单一职责)!而上锁解锁这两个方法是多种访问类都涉及到的,是多种访问类访问数据的一个门槛,也可以说是一个切面!so我们完全可以把这部分代码独立出去,当访问类需要访问这个数据时,需要经过这个门槛,这个门槛给这些访问类上锁,这样不就解决了上面两个问题呢!

3.每个访问类都含有上锁和解锁的代码,统一独立出来,也精简了代码与类的结构!

对于像只能单一继承的比如JAVA语言,AOP可以克服这个弱点。

在web工程中,AOP的主要应用场景有:

Authentication 权限

Caching 缓存

Context passing 内容传递

Error handling 错误处理

Lazy loading 懒加载

Debugging 调试

logging, tracing, profiling and monitoring 记录跟踪 优化 校准

Performance optimization 性能优化

Persistence 持久化

Resource pooling 资源池

Synchronization 同步

Transactions 事务

AOP相关概念

切面(Aspect):横切的关注点,是切入点和通知的结合,比如日志切面。

连接点(Joinpoint):在哪里切入。在spring中,这些点指的是方法,因为spring只支持方法类型的连接点。

切入点:(Pointcut):所谓切入点是指我们要对哪些Joinpoint进行拦截的定义,也就是JoinPoint的集合。

通知/增强:(Advice):所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)。

引介:(Introduction) : 引介是一种特殊的通知,在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field

目标对象:(Target) : 代理的目标对象。

织入(Weaving):是指把增强应用到目标对象来创建新的代理对象的过程。

spring采用动态代理织入,而AspectJ采用编译期织入和类装在期织入。

Proxy(代理):一个类被AOP织入增强后,就产生一个结果代理类。

Spring支持五种类型的通知:

Before(前) org.apringframework.aop.MethodBeforeAdvice

After-returning(返回后) org.springframework.aop.AfterReturningAdvice

After-throwing(抛出后) org.springframework.aop.ThrowsAdvice

Arround(周围) org.aopaliance.intercept.MethodInterceptor

Introduction(引入) org.springframework.aop.IntroductionInterceptor

周围通知:他是由AOP Alliance中的接口定义的而非Spring,周围通知相当于前通知、返回后通知、抛出后通知的结合

Spring AOP 实践

Spring实现有4种方式

1.经典的基于代理的AOP

2.@AspectJ注解驱动的切面

3.纯POJO切面

4.注入式AspectJ切面

1 基于代理的AOP的方式实现:

(1) 、编写一个sayHello的接口,并实现:

package com.spring.hello;

public interface HelloSpring {
public void sayHello();
}


package com.spring.hello;

public class HelloSpringImpl implements HelloSpring{

@Override
public void sayHello() {
System.out.println("i am like");
}
}


并在spring配置中注册这个bean:

<bean id="helloBean" class="com.spring.hello.HelloSpringImpl">
</bean>


(2)、在说你好之前和说你好之后我们也会做一些事情,比如这些事情是我们都需要做的,我们便希望把这些共用的事情独立出去,一方面简化代码,另一方面单一职责。所以我们要对sayHello创建通知,并继承通知类型的一些接口:

package com.spring.hello;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;
import org.springframework.aop.MethodBeforeAdvice;

public class HelloHelper implements MethodBeforeAdvice,AfterReturningAdvice{

@Override
public void afterReturning(Object arg0, Method arg1, Object[] arg2,
Object arg3) throws Throwable {
System.out.println("说你好之后要干嘛?");

}

@Override
public void before(Method arg0, Object[] arg1, Object arg2)
throws Throwable {
System.out.println("说你好之前放松哦");

}

}


(3)、把这个通知放到spring配置文件的bean里:

<bean id="helloHelper" class="com.spring.hello.HelloHelper"></bean>


在spring配置文件中配置通知:

<bean id="helloHelperAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor">
<property name="advice" ref="helloHelper"/>
<property name="pattern" value=".*sayHello"/>
</bean>


加入如下配置让切点和通知自动匹配(Spring提供的自动代理的功能)

<bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>


(4)、测试:

package com.spring.hello;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.spring.hello.HelloSpring;

public class TestAOP {

public static void main(String[] args){
ApplicationContext appCtx = new ClassPathXmlApplicationContext("Spring.xml");
HelloSpring sleeper = (HelloSpring)appCtx.getBean("helloBean");
sleeper.sayHello();
}
}


执行结果:

说你好之前放松哦

i am like

说你好之后要干嘛?

2、基于AspectJ提供的注解方式实现:

百度百科:

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

@Pointcut指定注解的切入点

@Before和@AfterReturning指定了通知

(1) 、同样第一步需要编写一个sayHello的接口,并实现:

package com.spring.hello;

public interface HelloSpring {
public void sayHello();
}


package com.spring.hello;

public class HelloSpringImpl implements HelloSpring{

@Override
public void sayHello() {
System.out.println("i am like");
}
}


并在spring配置中注册这个bean:

<bean id="helloBean" class="com.spring.hello.HelloSpringImpl">
</bean>


(2)、对sayHello创建通知:

package com.spring.hello;

import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class HelloHelper {

@Pointcut("execution(* *.sayHello()))")
public void helloPoint()
{

}
@Before("helloPoint()")
public void beforeHello(){
System.out.println("说你好之前放松注解!");
}

@AfterReturning("helloPoint()")
public void afterHello(){
System.out.println("说你好之后的注解!");
}

}


(3)、修改配置文件:

<?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-2.0.xsd  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<aop:aspectj-autoproxy/>

<bean id="human" class="com.spring.hello.HelloSpringImpl"/>
<bean id="hellohelper" class="com.spring.hello.HelloHelper">
</bean>

</beans>


加入了

xmlns:aop="http://www.springframework.org/schema/aop"
 http://www.springframework.org/schema/aop  http://www.springframework.org/schema/aop/spring-aop-2.0.xsd


加入
<aop:aspectj-autoproxy/>
,可以使spring自动扫描被@Aspect标注的切面。

配置文件的bean只需要放入需要被切入的bean和切入到需要被切入的bean中的通知bean。

(4)、测试(同上一种方法)

3、基于Spring定义pojo切面的方式实现:

spring AOP的一些配置元素:

<aop:advisor> 定义一个AOP通知者
<aop:after> 后通知
<aop:after-returning> 返回后通知
<aop:after-throwing> 抛出后通知
<aop:around> 周围通知
<aop:aspect>定义一个切面
<aop:before>前通知
<aop:config>顶级配置元素,类似于<beans>这种东西
<aop:pointcut>定义一个切点


所以我们把上面实现的方式改用aop配置实现:

通知类里的注解全部去掉,spring配置文件里去掉
<aop:aspectj-autoproxy/>


加入

<aop:config>
<aop:aspect ref="hellohelper">
<aop:before method="beforeHello" pointcut="execution(* *.sayHello(..))"/>
<aop:after method="afterHello" pointcut="execution(* *.sayHello(..))"/>
</aop:aspect>
</aop:config>


方法2和3中execution语法:

execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)
例如
  匹配所有类public方法 execution(public * *(..))
  匹配指定包下所有类方法 execution(* cn.itcast.dao.*(..)) 不包含子包
  execution(* cn.itcast.dao..*(..)) ..*表示包、子孙包下所有类
  匹配指定类所有方法 execution(* cn.itcast.service.UserService.*(..))
  匹配实现特定接口所有类方法 execution(* cn.itcast.dao.GenericDAO+.*(..))
  匹配所有save开头的方法 execution(* save*(..))


SPRING AOP中用到了java的反射和代理设计模式,今后继续深入学习一下这些相关知识

get that and go !

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: