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

Spring AOP详解(示例)

2017-08-14 15:00 351 查看
一、什么是AOP

  AOP(Aspect-Oriented Programming), 即 面向切面编程, 它与 OOP( Object-Oriented Programming, 面向对象编程) 相辅相成, 提供了与 OOP 不同的抽象软件结构的视角.在 OOP 中, 我们以类(class)作为我们的基本单元, 而 AOP 中的基本单元是 Aspect(切面)。

二、AOP的优点

  它利用一种称为“横切”的技术,剖解开封装的对象内部,并将那些影响了多个类的公共行为封装到一个可重用模块,并将其名为“Aspect”,即切面。所谓“切面”,简单地说,就是将那些与业务无关,却为业务模块所共同调用的逻辑或责任封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可操作性和可维护性。

三、AOP的相关术语

1.通知(Advice):

  所谓通知指的就是指拦截到连接点之后要执行的代码(比如我们拦截到用户点击登录事件,然后我们需要判断登录的账号密码是否合法,是否合法的逻辑就在Advice的方法中实现,也就是拦截到连接到后要做什么处理,都在这里实现),通知分为前置、后置、异常、最终、环绕通知五类。

2.连接点(Joinpoint):

  用于定义通知(Advice)应该切入到哪些连接点(JoinPoint)上。不同的通知通常需要切入到不同的连接点上,一般为一个方法。

3.切入点(Pointcut):

  通知定义了切面要发生的“故事”和时间(定义了what和when),那么切入点就定义了“故事”发生的地点(where),例如某个类或方法的名称,spring中允许我们方便的用正则表达式来指定。

4.切面(Aspect):

  通知和切入点共同组成了切面,切面可能包含很多切入点

5.织入(Weaving):

  组装切面来创建一个被通知对象。

完成时机:可以在编译时完成(例如使用AspectJ编译器),也可以在运行时完成。Spring和其他纯Java AOP框架一样,在运行时完成织入。

6.引入(Introduction)

  引入允许我们向现有的类添加新的方法和属性(Spring提供了一个方法注入的功能)

7.目标(Target)

  即被通知的对象,如果没有AOP,那么它的逻辑将要交叉别的事务逻辑,有了AOP之后它可以只关注自己要做的事(AOP让他做爱做的事)

8.代理(proxy)

  应用通知的对象

三、基于XML配置的AOP实例

  上面的概念主要用于理解,网上很多,可以自己去理解。下面主要来用实例演示SpringAOP。

1.添加所需的jar包(可以使用maven依赖仓库帮你下载,我这里就自己一个个下载的)

      


  注意:不能少了aspectjweaver包,否则会报错

2.创建项目(SpringAOPTest)

代码没什么业务逻辑只是一些打印操作,这里把代码贴出来。

HelloWorld(处理业务逻辑)

package com.xxx.aop;
/**
*@description
*@author create by xiaoxsen
*@date   2017年8月14日---下午1:59:17
*/
public class HelloWorld {
public void print()
{
System.out.println("helloworld");
}
}


Logger(横切面关注点)

package com.xxx.aop;
/**
*@author create by xiaoxsen
*@date   2017年8月14日---下午12:28:05
*/
public class Logger {

public void printBefore(){
System.out.println("执行前CurrentTime: "+System.currentTimeMillis());
}
public void printAfter(){
System.out.println("执行后CurrentTime: "+System.currentTimeMillis());
}
}


Test(测试类)

package com.xxx.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
*@description
*@author create by xiaoxsen
*@date   2017年8月14日---下午2:00:18
*/
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld h = (HelloWorld) context.getBean("helloworld");
h.print();
}
}


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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd"> <bean id="helloworld" class="com.xxx.aop.HelloWorld"></bean>
<bean id="logger" class="com.xxx.aop.Logger"></bean>
<!-- aop配置 -->
<aop:config>
<!-- 切入点配置 -->
<aop:pointcut expression="execution(* com.xxx.aop.HelloWorld.* (..))" id="addPrint"/><!--织入到HelloWorld的所有方法,方法可以自己定制-->
<aop:aspect id="log" ref="logger">
<!-- 通知配置 -->
<aop:before method="printBefore" pointcut-ref="addPrint"/>
<aop:after method="printAfter" pointcut-ref="addPrint"/>
</aop:aspect>

</aop:config>

</beans>


运行结果:

执行前CurrentTime: 1502692311935
helloworld
执行后CurrentTime: 1502692311951


四、基于注解的AOP实例

HelloWorld(处理业务逻辑)

package com.xxx.aop;

import org.springframework.stereotype.Service;

/**
*@description
*@author create by xiaoxsen
*@date   2017年8月14日---下午1:59:17
*/
@Service
public class HelloWorld {
public void print()
{
System.out.println("helloworld");
}
}


Logger(横切面关注点)

package com.xxx.aop;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

/**
*@author create by xiaoxsen
*@date   2017年8月14日---下午12:28:05
*/
//指定切面的优先级,当有多个切面时,数值越小优先级越高
@Order(1)
//把这个类声明为一个切面:需要把该类放入到IOC容器中。再声明为一个切面.
@Aspect
@Component
public class Logger {
/**
* 声明切入点表达式,一般在该方法中不再添加其他代码。
* 使用@Pointcut来声明切入点表达式。
* 后面的通知直接使用方法名来引用当前的切入点表达式。
* 后面表达式的方法就是目标方法
*/
@Pointcut("execution(* com.xxx.aop.HelloWorld.* (..))")
public void declareJoinPointExpression() {}

/**
*前置通知,在目标方法开始之前执行。
*@Before("execution(*)")这样写可以指定特定的方法比如(public void com.xxx.aop.HelloWorld.print())。
* @param joinpoint
*/
@Before("declareJoinPointExpression()")
public void printBefore(){
System.out.println("执行前CurrentTime: "+System.currentTimeMillis());
}
/**
*前置通知,在目标方法开始之后执行。
*@after("execution(*)")这样写可以指定特定的方法比如(public void com.xxx.aop.HelloWorld.print())。
* @param joinpoint
*/
@After("declareJoinPointExpression()")
public void printAfter(){
System.out.println("执行后CurrentTime: "+System.currentTimeMillis());
}
}


Test(测试类)

package com.xxx.aop;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
/**
*@description
*@author create by xiaoxsen
*@date   2017年8月14日---下午2:00:18
*/
public class Test {
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
HelloWorld h = (HelloWorld) context.getBean(HelloWorld.class);
h.print();
}
}


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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" >
<!-- 配置自动扫描包 -->
<context:component-scan base-package="com.xxx.aop"></context:component-scan>
<!-- 使AspectJ注解起作用:自动为匹配的类生产代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>


运行结果:

执行前CurrentTime: 1502703074952
helloworld
执行后CurrentTime: 1502703074970


注意:基于注解时需要在xml文件中添加如下代码:

<context:component-scan base-package="com.xxx.aop"></context:component-scan>
<!-- 使AspectJ注解起作用:自动为匹配的类生产代理对象 -->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>


然后aspectjweaver.jar需要更新到最近的版本,不然会因为jdk版本高,aspectjweaver.jar不是最近的造成异常。

五、总结

  Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

  1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

  2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB,在xml中配置就好。

通过上面的示例发现SpringAOP编程我们需要处理的就是:

  1、定义普通业务组件(比如HelloWorld类)

  2、定义切入点,一个切入点可能横切多个业务组件(比如Logger类)

  3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: