Spring的AOP面向切面编程

本文详细介绍了Spring的AOP编程,包括AOP的概述、组成要素、原理,以及Spring对AOP的支持。通过实例展示了如何使用AOP进行日志记录和权限控制,强调了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接口,具有登陆与注册两个方法:

public interface User {
    void login();
    void register();
}

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

public class UserImpl implements User {

    private String role;

    public String getRole() {
        return role;
    }

    public void setRole(String role) {
        this.role = role;
    }

    @Override
    public void login() {
        System.out.println("完成登录的业务方法!");
    }

    @Override
    public void register()  {
        System.out.println("完成注册的业务方法!");
    }
}

创建一个通知类对象MyAdvice:

public class MyAdvice {

    public void beforelog()
    {
        System.out.println("这是前置通知的日志。");
    }

    public void afterlog()
    {
        System.out.println("这是后置通知的日志。");
    }

    public void aroundlog(ProceedingJoinPoint p) throws Throwable
    {
        System.out.println("环绕前的日志!");
        p.proceed();  //让原来的业务方法执行!
        System.out.println("环绕后的日志!");
    }


    public void throwlog()
    {
        System.out.println("这是异常通知的日志");
    }
}

在applicationContext.xml文件配置头部:

<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"
    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">

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

    <!-- 创建1个业务实现类对象 -->
    <bean id="user" class="com.sunsharing.entity.UserImpl">
    </bean>

    <!--创建1个自定义通知类对象 -->
    <bean id="myadvice" class="com.sunsharing.entity.MyAdvice">

    </bean>

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

         <aop:after method="afterlog" pointcut-ref="mypc"/>

         <aop:around method="aroundlog" pointcut-ref="mypc"/>

         <aop:after-throwing method="throwlog" pointcut-ref="mypc"/>
      </aop:aspect>
    </aop:config>

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

import com.sunsharing.entity.User;

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class TestUser {

    @org.junit.Test
    public void test() {
        BeanFactory fac = new ClassPathXmlApplicationContext("applicationContext.xml");
        User user = (User) fac.getBean("user");
        user.login();
    }
}

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

@Override
    public void register()  {
        throw new RuntimeException("这是注册的异常!");
        //System.out.println("完成注册的业务方法!");
    }

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

<aop:pointcut expression="execution(* com.sunsharing.entity.UserImpl.register(..))" id="mypc"/>

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

五、AOP的权限功能

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

package com.sunsharing.entity;

import java.util.Calendar;

import org.aspectj.lang.ProceedingJoinPoint;

public class MyRegAdvice
{
    //这是环绕通知
    public void checkday(ProceedingJoinPoint p) throws Throwable
    {
        Calendar c = Calendar.getInstance();

        int day = c.get(Calendar.DAY_OF_WEEK);
        if(day==5)//周日是第一天为1,因此周四是5,亲测!
        {
            System.out.println("周四无法注册!");
            return;
        }

        p.proceed(); //其他情况,允许往前走。
    }
}

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

<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"
    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">

    <!-- 创建1个业务实现类对象 -->
    <bean id="user" class="com.sunsharing.entity.UserImpl">

    </bean>

    <!--创建1个自定义通知类对象 -->
    <bean id="myadvice" class="com.sunsharing.entity.MyRegAdvice">

    </bean>

    <!-- 配置 织入关系 -->
    <aop:config>
    <!-- 定义1条切线 -->
      <aop:pointcut expression="execution(* com.sunsharing.entity.UserImpl.reg*(..))" id="mypc"/>
      <!--配置切面 ,引用自定义通知对象-->
      <aop:aspect ref="myadvice">
        <aop:around method="checkday" pointcut-ref="mypc"/>
      </aop:aspect>
    </aop:config>
</beans>

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值