Spring AOP 的简单实现

Spring AOP
1、什么是AOP

AOP(Aspect Oriented Programming)称为面向切面编程思想,它是对于OOP(面向对象)思想的扩展和补充。它主要用来解决一些系统层面上的问题,比如系统日志、权限、声明式事务;在不改变系统原有逻辑的基础上,增加一些额外的功能。

2、AOP 的实现方式

通过实现日志的的例子实现AOP编程。

1、基于XML的实现方式(前置通知与后置通知)

首先创建业务类接口和具体实现类(实现日志添加)
1、业务类接口

public interface IOrderService {
    void createNewOrder(int maney);
}

2、具体实现类

public class OrderServiceImpl implements IOrderService {
    @Override
    public void createNewOrder(int maney) {
        System.out.println("【业务层】创建新订单,金额为"+maney);
    }
}

3、日志类

/**
 * MethodBeforeAdvice:前置通知接口
 * AfterReturningAdvice:后置通知接口
 * MethodInterceptor: 环绕通知
 */
public class LogAdvice implements MethodBeforeAdvice, AfterReturningAdvice {
    /**
     *
     * @param method 目标方法
     * @param objects 方法参数列表
     * @param o 目标对象
     * @throws Throwable
     */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println("【前置通知】"+method.getName()+"方法执行,在"+new Date()+"开始执行");
    }

    /**
     *
     * @param o 方法返回值
     * @param method 目标方法
     * @param objects 方法参数列表
     * @param o1 目标对象
     * @throws Throwable
     */
    @Override
    public void afterReturning(Object o, Method method, Object[] objects, Object o1) throws Throwable {
        System.out.println("【后置通知】"+method.getName()+"方法执行,在"+new Date()+"执行结束");
    }
}

4、导入相关依赖(除spring核心依赖依赖外还需要导入

<!--spring aop相关依赖-->
<dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-aop</artifactId>
   <version>5.2.1.RELEASE</version>
</dependency>

5、spring-config.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--被代理对象-->
 <bean id="orderServiceTargetBean" class="com.apesource.service.impl.OrderServiceImpl"/>

    <!--通知-->
    <bean id="logAdviceBean" class="com.apesource.advice.LogAdvice"/>

    <!--代理对象-->
    <bean id="orderServiceBean" class="org.springframework.aop.framework.ProxyFactoryBean">
       <!--注入被代理对象接口-->
        <property name="proxyInterfaces">
            <value>com.apesource.service.IOrderService</value>
        </property>
        <!--注入被代理对象-->
        <property name="target">
            <ref bean="orderServiceTargetBean"/>
        </property>
        <!--注入通知-->
        <property name="interceptorNames">
            <value>logAdviceBean</value>
        </property>
    </bean>
</beans>

6、测试类

public class Test01 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        IOrderService orderService = (IOrderService) context.getBean("orderServiceBean");
        orderService.creatNewOrder(10);
    }
}
2、基于XML的实现方式(环绕通知)

1、业务类接口与实现类相同,实现方法性能的测试,性能测试类如下

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
/**
 * MethodInterceptor:环绕通知接口
 */
public class PerformanceAdvice implements MethodInterceptor {
    /**
     *
     * @param methodInvocation 目标方法
     * @return 目标方法返回值
     * @throws Throwable
     */
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("【环绕通知前置】方法性能检测开始!");
        double begin = System.currentTimeMillis();

        Object resultValue = methodInvocation.proceed();

        double end = System.currentTimeMillis();
        System.out.println("【环绕通知前置】方法性能检测结束!共耗时:"+(end+begin));
        return resultValue;
    }
}

2、spring-config1.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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--被代理对象-->
    <bean id="orderServiceBean" class="com.apesource.service.impl.OrderServiceImpl"/>
    <!--通知对象-->
    <bean id="performanceAdviceBean" class="com.apesource.advice.PerformanceAdvice"/>

    <!--切入点pointcut(按照正则表达式规则指定切入点(某些方法))-->
    <bean id="createMethodPointcut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
        <!--注入正则表达式切入规则拦截create*方法-->
        <property name="pattern" value=".*create.*"/>
    </bean>
    
    <!--advisor(高级通知)=pointcut(切入点)+advice(通知)(按照切入点进行规则的通知)-->
    <bean id="performanceAdvisorBean" class="org.springframework.aop.support.DefaultPointcutAdvisor">
        <!--注入切入点-->
        <property name="pointcut" ref="createMethodPointcut"/>
        <!--注入通知-->
        <property name="advice" ref="performanceAdviceBean"/>
    </bean>

    <!--自动代理创建-->
    <bean class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
        <!--被代理对象列表(需要为那些bean创建自动代理)-->
        <property name="beanNames">
            <list>
                <value>*ServiceBean</value>
              <!--  <value>自动需要创建的Bean</value> --> 
            </list>
        </property>
        <!--通知列表(需要执行的通知)-->
        <property name="interceptorNames">
            <list>
                <!--执行先后顺序与放置顺序相同-->
                <value>LogAdviceBean</value>
				<!--<value>performanceAdviceBean</value>-->
                <value>performanceAdvisorBean</value>
            </list>
        </property>
    </bean>
    
</beans>

3、测试类

public static void main(String[] args) {
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-config1.xml");
    IOrderService orderService = (IOrderService) context.getBean("orderServiceBean");
    orderService.createNewOrder(10);
}
3、使用Aspectj实现切面,使用Spring AOP进行配置

1、业务类接口

public interface IUserService {
    int createNewUser();
    void loginOut();
}

2、实现类

@Service("userServiceBean")
public class UserServiceImpl implements IUserService {
    @Override
    public int createNewUser() {
       // int a = 12/0;// 用于测试异常通知
        System.out.println("【业务层】创建新的用户");
        return 1001;
    }
    @Override
    public void loginOut() {
        System.out.println("【业务层】用户退出登录");
    }
}

3、切面类:

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
/**
 * 使用Aspectj实现切面
 */
public class LogAspect {
    /**
     * 实现前置通知
     */
    public void beforeAspect(JoinPoint joinPoint){
        System.out.println("【前置通知】开始。。。。。");
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("目标方法"+joinPoint.getSignature().getName());
        System.out.println("目标方法参数"+joinPoint.getArgs());
        System.out.println("【前置通知】结束。。。。。");
    }

    /**
     * 实现后置通知:方法正常执行后,有返回值,执行该后置通知;如果该方法执行出现异常,则不执行该后置通知
     * @param joinPoint
     * @param returnVal 返回值
     */
    public void afterAspect(JoinPoint joinPoint, Object returnVal){
        System.out.println("【后置有返回值通知】开始。。。。。");
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("目标方法"+joinPoint.getSignature().getName());
        System.out.println("目标方法参数"+joinPoint.getArgs());
        System.out.println("目标方法返回值"+returnVal);
        System.out.println("【后置有返回值通知】结束。。。。。");
    }

    /**
     * 后置通知
     */
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("【后置无返回值通知】开始。。。。。");
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("目标方法"+joinPoint.getSignature().getName());
        System.out.println("目标方法参数"+joinPoint.getArgs());
        System.out.println("【后置无返回值通知】结束。。。。。");
    }

    /**
     * 异常通知:方法出现异常时,执行该通知
     * @param joinPoint
     * @param e 异常对象
     */
    public void exceptionAdvice(JoinPoint joinPoint, Exception e){
        System.out.println("【异常通知】开始。。。。。");
        System.out.println("异常通知"+e.getMessage());
        System.out.println("【异常通知】结束。。。。。");
    }

    /**
     * 环绕通知
     * @param joinpoint
     * @return 目标方法返回值
     * @throws Throwable
     */
    public Object aroundAdvice(ProceedingJoinPoint joinpoint) throws Throwable {
        System.out.println("。。。。。【环绕通知前置】。。。。。");
        Object returnVal = joinpoint.proceed();// 执行目标方法
        System.out.println("。。。。。【环绕通知后置】。。。。。");
        return returnVal;
    }
}

4、spring-config2.xml配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--业务组件-->
    <bean id="userServiceBean" class="com.apesource.service.impl.UserServiceImpl"/>
    <!--日志切面bean-->
    <bean class="com.apesource.aspectj.LogAspect" id="logAspectBean"/>

    <!--使用Aspectj实现切面,使用Spring AOP进行配置-->
    <aop:config>
        <!--配置切面-->
        <!--注入切面bean-->
        <aop:aspect ref="logAspectBean">
            
            <!--定义切面(pointcut)通过expression表达式来寻找特定方法(切入点)-->
            <aop:pointcut id="serviceMethodPointcut" expression="execution(* com.apesource.service.impl.*.create*(..) )"/>
            
            <!--配置前置通知-->
            <!--在pointcut切入点(serviceMethodPointcut)查找到的方法执行“前”,执行当前LogAspectBean的doBefore-->
            <aop:before method="beforeAspect" pointcut-ref="serviceMethodPointcut"/>
           
            <!--后置通知-->
            <!--returning属性:配置当前方法中用来接收返回值的参数名-->
            <aop:after-returning returning="returnVal" method="afterAspect"  pointcut-ref="serviceMethodPointcut"/>
           
            <aop:after method="afterAdvice" pointcut-ref="serviceMethodPointcut"/>
            <!--配置“异常通知”-->
            <!--throwing属性:配置当前方法中用来接收当前异常的参数名-->
            <aop:after-throwing method="exceptionAdvice" throwing="e" pointcut-ref="serviceMethodPointcut"/>
          
            <!--配置环绕通知-->
            <aop:around method="aroundAdvice" pointcut-ref="serviceMethodPointcut"/>
        
        </aop:aspect>
    </aop:config>
</beans>

5、测试类

public class Test03 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config2.xml");
        IUserService userService = (IUserService) context.getBean("userServiceBean");
        userService.createNewUser();
        userService.loginOut();
    }
}
3、使用Aspectj实现切面,使用注解实现

1、实体类使用userServiceImpl
2、切面类

/**
 * 使用Aspectj实现切面(注解实现)
 */
@Aspect//声明当前类为Aspect切面,并交给Spring容器管理
@Component
public class LogAnnotationAspect {
    private static final String POINTCUT_EXPRESSION = "execution(* com.apesource.service.impl.*.create*(..) )";// 切入点
    /**
     * 实现前置通知
     */
    @Before(POINTCUT_EXPRESSION)
    public void beforeAspect(JoinPoint joinPoint){
        System.out.println("【前置通知】开始。。。。。");
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("目标方法"+joinPoint.getSignature().getName());
        System.out.println("目标方法参数"+joinPoint.getArgs());
        System.out.println("【前置通知】结束。。。。。");
    }

    /**
     * 实现后置通知:方法正常执行后,有返回值,执行该后置通知;如果该方法执行出现异常,则不执行该后置通知
     * @param joinPoint
     * @param returnVal 返回值
     */
    @AfterReturning(value =POINTCUT_EXPRESSION,returning = "returnVal")
    public void afterAspect(JoinPoint joinPoint, Object returnVal){
        System.out.println("【后置有返回值通知】开始。。。。。");
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("目标方法"+joinPoint.getSignature().getName());
        System.out.println("目标方法参数"+joinPoint.getArgs());
        System.out.println("目标方法返回值"+returnVal);
        System.out.println("【后置有返回值通知】结束。。。。。");
    }

    /**
     * 后置通知
     */
    @After(POINTCUT_EXPRESSION)
    public void afterAdvice(JoinPoint joinPoint){
        System.out.println("【后置无返回值通知】开始。。。。。");
        System.out.println("目标对象"+joinPoint.getTarget());
        System.out.println("目标方法"+joinPoint.getSignature().getName());
        System.out.println("目标方法参数"+joinPoint.getArgs());
        System.out.println("【后置无返回值通知】结束。。。。。");
    }

    /**
     * 异常通知:方法出现异常时,执行该通知
     * @param joinPoint
     * @param e 异常对象
     */
    @AfterThrowing(value =POINTCUT_EXPRESSION, throwing = "e")
    public void exceptionAdvice(JoinPoint joinPoint, Exception e){
        System.out.println("【异常通知】开始。。。。。");
        //System.out.println("异常通知"+e.getMessage());
        System.out.println("【异常通知】结束。。。。。");
    }

    /**
     * 环绕通知
     * @param joinpoint
     * @return 目标方法返回值
     * @throws Throwable
     */
    @Around(POINTCUT_EXPRESSION)
    public Object aroundAdvice(ProceedingJoinPoint joinpoint) throws Throwable {
        System.out.println("。。。。。【环绕通知前置】。。。。。");
        Object returnVal = joinpoint.proceed();// 执行目标方法
        System.out.println("。。。。。【环绕通知后置】。。。。。");
        return returnVal;
    }
}

3、spring-config3.xml配置,首先导入aop和context命名空间。

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                          http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd
                          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
        <!--自动创建代理对象-->
        <aop:aspectj-autoproxy/>
        <!--扫描包-->
        <context:component-scan base-package="com.apesource"/>
</beans>

4、测试类

public class Test04 {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-config3.xml");
        IUserService userService = (IUserService) context.getBean("userServiceBean");
        userService.createNewUser();
        userService.loginOut();
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值