【转】Spring的AOP面向切面编程

一、AOP的概述

AOP是基于反射与动态代理的实现,java有反射的中间层,让AOP的实现更加简单,而C++想实现相比会较为困难。

AOP为:(aspect oriented programming),面向切面编程。是对OOP的补充,弥补OOP的不足。
作用:在不修改源码的前提下,给项目增加新的公共功能,如:权限,日志,事务(数据库中同时成功或同时失败的操作的集合)。

二、AOP的组成要数

分别有以下几点:

  • advice:通知。即新增的功能。比如写日志。
  • target:目标。通知加入的目标类
  • joinpoint:切点。通知加入的目标类的具体位置
  • pointcut:切线。所有切点的集合
  • aspect:切面。切线+通知
  • weaving:动词。织入。
  • introduction:在不修改代码的前提下,引入可以在运行期为类动态地添加一些方法或字段。

三、AOP原理

基于设计模式之“代理模式”。agent/proxy。
代码实现上面分为:静态代理 和 动态代理。
代理模式的原理:代理对象和被代理对象实现同样的业务接口,由代理对象代替其完成业务行为,并增加“附加行为”。

例如:我想去动物园买票,传统方式是直接到动物园门口购票。而现在我可以找个人代替我买票,那个人就是代理人,而他也要向我索取金钱(成本+代理费。。)之类的附加行为,就是代理业务。
而现在的AOP就是那个代理人,替我们去做事的代理人。

注意:spring实现aop采用的”动态代理”的方式。动态代理意思是,去帮你做事的代理对象不是固定的,是用Spring动态创建的

四、Spring对AOP的支持

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

AOP通知日志的简单实例,创建一个User接口,具有登陆与注册两个方法:

<span style="color:#000000"><code><span style="color:#000088 !important">public</span> <span style="color:#000088 !important">interface</span> User {
    <span style="color:#000088 !important">void</span> login();
    <span style="color:#000088 !important">void</span> register();
}</code></span>

创建一个代理对象UserImpl,实现User接口:

<span style="color:#000000"><code><span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> <span style="color:#4f4f4f !important">UserImpl</span> <span style="color:#000088 !important">implements</span> <span style="color:#4f4f4f !important">User</span> {

    <span style="color:#000088 !important">private</span> String role;

    <span style="color:#000088 !important">public</span> String <span style="color:#009900 !important">getRole</span>() {
        <span style="color:#000088 !important">return</span> role;
    }

    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">setRole</span>(String role) {
        <span style="color:#000088 !important">this</span>.role = role;
    }

    <span style="color:#9b859d !important">@Override</span>
    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">login</span>() {
        System.out.println(<span style="color:#009900 !important">"完成登录的业务方法!"</span>);
    }

    <span style="color:#9b859d !important">@Override</span>
    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">register</span>()  {
        System.out.println(<span style="color:#009900 !important">"完成注册的业务方法!"</span>);
    }
}</code></span>

创建一个通知类对象MyAdvice:

<span style="color:#000000"><code><span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> MyAdvice {

    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">beforelog</span>()
    {
        System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"这是前置通知的日志。"</span>);
    }

    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">afterlog</span>()
    {
        System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"这是后置通知的日志。"</span>);
    }

    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">aroundlog</span>(ProceedingJoinPoint p) throws Throwable
    {
        System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"环绕前的日志!"</span>);
        p.proceed();  <span style="color:#880000 !important"><em>//让原来的业务方法执行!</em></span>
        System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"环绕后的日志!"</span>);
    }


    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">throwlog</span>()
    {
        System.<span style="color:#000088 !important">out</span>.println(<span style="color:#009900 !important">"这是异常通知的日志"</span>);
    }
}</code></span>

在applicationContext.xml文件配置头部:

<span style="color:#000000"><code><beans xmlns=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans"</span>
    xmlns:xsi=<span style="color:#009900 !important">"http://www.w3.org/2001/XMLSchema-instance"</span>
    xmlns:aop=<span style="color:#009900 !important">"http://www.springframework.org/schema/aop"</span>
    xsi:schemaLocation=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans</span>
    http:<span style="color:#880000 !important"><em>//www.springframework.org/schema/beans/spring-beans-3.0.xsd</em></span>
    http:<span style="color:#880000 !important"><em>//www.springframework.org/schema/aop</em></span>
    http:<span style="color:#880000 !important"><em>//www.springframework.org/schema/aop/spring-aop-3.0.xsd"></em></span></code></span>

接着配置其代理对象,和通知类对象及织入关系

<span style="color:#000000"><code>    <span style="color:#880000 !important"><em><!-- 创建1个业务实现类对象 --></em></span>
    <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"user"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.UserImpl"</span>></span>
    <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>

    <span style="color:#880000 !important"><em><!--创建1个自定义通知类对象 --></em></span>
    <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"myadvice"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.MyAdvice"</span>></span>

    <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>

    <span style="color:#880000 !important"><em><!-- 配置 织入关系 --></em></span>
    <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:config</span>></span>
    <span style="color:#880000 !important"><em><!-- 定义1条切线 --></em></span>
      <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:pointcut</span> <span style="color:#4f4f4f !important">expression</span>=<span style="color:#009900 !important">"execution(* com.sunsharing.entity.UserImpl.login(..))"</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"mypc"</span>/></span> <span style="color:#880000 !important"><em><!--这里监听的是login方法--></em></span>
      <span style="color:#880000 !important"><em><!--配置切面 ,引用自定义通知对象--></em></span>
      <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:aspect</span> <span style="color:#4f4f4f !important">ref</span>=<span style="color:#009900 !important">"myadvice"</span>></span>
         <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:before</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"beforelog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span> 

         <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:after</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"afterlog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>

         <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:around</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"aroundlog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>

         <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:after-throwing</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"throwlog"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
      <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:aspect</span>></span>
    <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:config</span>></span></code></span>

最后,在TestUser测试类中进行日志测试:

<span style="color:#000000"><code class="language-java"><span style="color:#000088 !important">import</span> com.sunsharing.entity.User;

<span style="color:#000088 !important">import</span> org.springframework.beans.factory.BeanFactory;
<span style="color:#000088 !important">import</span> org.springframework.context.support.ClassPathXmlApplicationContext;

<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> <span style="color:#4f4f4f !important">TestUser</span> {

    <span style="color:#9b859d !important">@org</span>.junit.Test
    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">test</span>() {
        BeanFactory fac = <span style="color:#000088 !important">new</span> ClassPathXmlApplicationContext(<span style="color:#009900 !important">"applicationContext.xml"</span>);
        User user = (User) fac.getBean(<span style="color:#009900 !important">"user"</span>);
        user.login();
    }
}</code></span>

输出结果:
这里写图片描述
其中还有个抛出异常的日志监听,在register方法抛出个自定义异常试试。

<span style="color:#000000"><code><span style="color:#9b859d !important">@Override</span>
    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">register</span>()  {
        <span style="color:#000088 !important">throw</span> <span style="color:#000088 !important">new</span> RuntimeException(<span style="color:#009900 !important">"这是注册的异常!"</span>);
        <span style="color:#880000 !important"><em>//System.out.println("完成注册的业务方法!");</em></span>
    }</code></span>

然后把applicationContext中的监听方法改为rigister

<span style="color:#000000"><code><span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:pointcut</span> <span style="color:#4f4f4f !important">expression</span>=<span style="color:#009900 !important">"execution(* com.sunsharing.entity.UserImpl.register(..))"</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"mypc"</span>/></span></code></span>
  •  

测试类中运行后,输出的结果为:
这里写图片描述
以上是日志功能的实现,接下来再讲个权限功能的实现

五、AOP的权限功能

沿用以上的例子,加个周四无法注册的权限控制。
创建一个MyRegAdvice的自定义通知类:

<span style="color:#000000"><code><span style="color:#000088 !important">package</span> com.sunsharing.entity;

<span style="color:#000088 !important">import</span> java.util.Calendar;

<span style="color:#000088 !important">import</span> org.aspectj.lang.ProceedingJoinPoint;

<span style="color:#000088 !important">public</span> <span style="color:#000088 !important">class</span> <span style="color:#4f4f4f !important">MyRegAdvice</span>
{
    <span style="color:#880000 !important"><em>//这是环绕通知</em></span>
    <span style="color:#000088 !important">public</span> <span style="color:#000088 !important">void</span> <span style="color:#009900 !important">checkday</span>(ProceedingJoinPoint p) <span style="color:#000088 !important">throws</span> Throwable
    {
        Calendar c = Calendar.getInstance();

        <span style="color:#000088 !important">int</span> day = c.get(Calendar.DAY_OF_WEEK);
        <span style="color:#000088 !important">if</span>(day==<span style="color:#006666 !important">5</span>)<span style="color:#880000 !important"><em>//周日是第一天为1,因此周四是5,亲测!</em></span>
        {
            System.out.println(<span style="color:#009900 !important">"周四无法注册!"</span>);
            <span style="color:#000088 !important">return</span>;
        }

        p.proceed(); <span style="color:#880000 !important"><em>//其他情况,允许往前走。</em></span>
    }
}</code></span>

创建一个applicationContext_register.xml配置文件:

<span style="color:#000000"><code><span style="color:#006666 !important"><<span style="color:#4f4f4f !important">beans</span> <span style="color:#4f4f4f !important">xmlns</span>=<span style="color:#009900 !important">"http://www.springframework.org/schema/beans"</span>
    <span style="color:#4f4f4f !important">xmlns:xsi</span>=<span style="color:#009900 !important">"http://www.w3.org/2001/XMLSchema-instance"</span>
    <span style="color:#4f4f4f !important">xmlns:aop</span>=<span style="color:#009900 !important">"http://www.springframework.org/schema/aop"</span>
    <span style="color:#4f4f4f !important">xsi:schemaLocation</span>=<span style="color:#009900 !important">"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"</span>></span>

    <span style="color:#880000 !important"><em><!-- 创建1个业务实现类对象 --></em></span>
    <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"user"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.UserImpl"</span>></span>

    <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>

    <span style="color:#880000 !important"><em><!--创建1个自定义通知类对象 --></em></span>
    <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">bean</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"myadvice"</span> <span style="color:#4f4f4f !important">class</span>=<span style="color:#009900 !important">"com.sunsharing.entity.MyRegAdvice"</span>></span>

    <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">bean</span>></span>

    <span style="color:#880000 !important"><em><!-- 配置 织入关系 --></em></span>
    <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:config</span>></span>
    <span style="color:#880000 !important"><em><!-- 定义1条切线 --></em></span>
      <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:pointcut</span> <span style="color:#4f4f4f !important">expression</span>=<span style="color:#009900 !important">"execution(* com.sunsharing.entity.UserImpl.reg*(..))"</span> <span style="color:#4f4f4f !important">id</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
      <span style="color:#880000 !important"><em><!--配置切面 ,引用自定义通知对象--></em></span>
      <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:aspect</span> <span style="color:#4f4f4f !important">ref</span>=<span style="color:#009900 !important">"myadvice"</span>></span>
        <span style="color:#006666 !important"><<span style="color:#4f4f4f !important">aop:around</span> <span style="color:#4f4f4f !important">method</span>=<span style="color:#009900 !important">"checkday"</span> <span style="color:#4f4f4f !important">pointcut-ref</span>=<span style="color:#009900 !important">"mypc"</span>/></span>
      <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:aspect</span>></span>
    <span style="color:#006666 !important"></<span style="color:#4f4f4f !important">aop:config</span>></span>
<span style="color:#006666 !important"></<span style="color:#4f4f4f !important">beans</span>></span></code></span>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28

最后在测试类进行测试,测试类完全不用改动。
输出结果为:
这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值