使用Spring代理工厂实现AOP 切面

横切关注点:跨越应用程序多个模块的方法或功能。(软件系统,可以看做由一组关注点即业务或功能或方法组成。其中,直接的业务关注点是直切关注点,而为直切关注点服务的,就是横切关注点。)即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。

切面(ASPECT):横切关注点被模块化的特殊对象。即,它是一个类。

通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

目标(Target):被通知对象。

代理(Proxy):向目标对象应用通知之后创建的对象。

切入点(PointCut):切面通知执行的“地点”的定义。

连接点(JointPoint):与切入点匹配的执行点。

下面示意图:

SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

1.引入spring-aop

新建 一个Maven项目,在项目中引入Spring核心库与AOP,修改pom.xml文件,在dependencies中增加如下节点:

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>4.3.0.RELEASE</version>
    </dependency>

2.定义通知(Advice)

前置通知

package org.shangjack.spring.aop.aop05

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

/**
 * 前置通知
 */
public class BeforeAdvice implements MethodBeforeAdvice {

    /**
     * method 方法信息
     * args 参数
     * target 被代理的目标对象
     */
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----------------前置通知-----------------");
    }
}

后置通知

package org.shangjack.spring.aop.aop05;

import java.lang.reflect.Method;

import org.springframework.aop.AfterReturningAdvice;

/**
 * 后置通知
 *
 */
public class AfterAdvice implements AfterReturningAdvice {

    /*
     * returnValue 返回值
     * method 被调用的方法
     * args 方法参数
     * target 被代理对象
     */
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("-----------------后置通知-----------------");
    }

}

环绕通知

package org.shangjack.spring.aop.aop05;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 环绕通知
 * 方法拦截器
 *
 */
public class SurroundAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation i) throws Throwable {
        //前置横切逻辑
        System.out.println("方法" + i.getMethod() + " 被调用在对象" + i.getThis() + "上,参数 " + i.getArguments());
        //方法调用
        Object ret = i.proceed();
        //后置横切逻辑
        System.out.println("返回值:"+ ret);
        return ret;
    }
}

3.创建代理工厂、设置被代理对象、添加通知。

package org.shangjack.spring.aop.aop05

import org.springframework.aop.framework.ProxyFactory;

public class Test {

    @org.junit.Test
    public void test01()
    {
        //实例化Spring代理工厂
        ProxyFactory factory=new ProxyFactory();
        //设置被代理的对象
        factory.setTarget(new Math());
        //添加通知,横切逻辑
        factory.addAdvice(new BeforeAdvice());
        factory.addAdvice(new AfterAdvice());
        factory.addAdvice(new SurroundAdvice());
        //从代理工厂中获得代理对象
        IMath math=(IMath) factory.getProxy();
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
    @org.junit.Test
    public void test02()
    {
        //message.message();
    }
}

运行结果

4.封装代理创建逻辑

在上面的示例中如果要代理不同的对象需要反复创建ProxyFactory对象,代码会冗余。同样以实现方法耗时为示例代码如下:

4.1创建一个环绕通知:

package org.shangjack.spring.aop.aop05;

import java.util.Random;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

/**
 * 用于完成计算方法执行时长的环绕通知
 */
public class TimeSpanAdvice implements MethodInterceptor {

    public Object invoke(MethodInvocation invocation) throws Throwable {
        // 被织入的横切内容,开始时间 before
        long start = System.currentTimeMillis();
        lazy();

        //方法调用
        Object result = invocation.proceed();

        // 被织入的横切内容,结束时间
        Long span = System.currentTimeMillis() - start;
        System.out.println("共用时:" + span);

        return result;
    }

    // 模拟延时
    public void lazy() {
        try {
            int n = (int) new Random().nextInt(500);
            Thread.sleep(n);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

4.2封装动态代理类

package org.shangjack.spring.aop.aop05;

import org.springframework.aop.framework.ProxyFactory;

/**
 * 动态代理类
 *
 */
public abstract class DynamicProxy {
    /**
     * 获得代理对象
     * @param object 被代理的对象
     * @return 代理对象
     */
    public static Object getProxy(Object object){
        //实例化Spring代理工厂
        ProxyFactory factory=new ProxyFactory();
        //设置被代理的对象
        factory.setTarget(object);
        //添加通知,横切逻辑
        factory.addAdvice(new TimeSpanAdvice());
        return factory.getProxy();
    }
}

4.3测试运行

package org.shangjack.spring.aop.aop05;

import org.springframework.aop.framework.ProxyFactory;

public class DynaminProxyTest {

    @org.junit.Test
    public void test01()
    {
        //从代理工厂中获得代理对象
        IMath math=(IMath) DynamicProxy.getProxy(new Math());
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
    @org.junit.Test
    public void test02()
    {
        IMessage message=(IMessage) DynamicProxy.getProxy(new Message());
        message.message();
    }
}

4.4封装动态代理类:

package org.shangjack.spring.aop.aop05;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.framework.ProxyFactory;

public class SpringProxy<T> implements MethodInterceptor {

    /**获得代理后的对象*/
    public T getProxyObject(Object target){
        //代理工厂
        ProxyFactory proxy=new ProxyFactory();
        //添加被代理的对象
        proxy.setTarget(target);
        //添加环绕通知
        proxy.addAdvice(this);
        //获得代理后的对象
        return (T) proxy.getProxy();
    }

    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        before();
        //调用方法获得结果
        Object result=methodInvocation.proceed();
        after(result);
        return result;
    }

    public void before(){
        System.out.println("调用方法前");
    }
    public void after(Object result){
        System.out.println("调用方法后"+result);
    }
}

通过反射创建对象:

Class<T> entityClass = (Class<T>) ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[0];
T entity = entityClass.newInstance();

测试运行

package org.shangjack.spring.aop.aop05;

public class SpringProxyTest {

    @org.junit.Test
    public void test01()
    {
        SpringProxy<Math> proxy = new SpringProxy();

        //从代理工厂中获得代理对象
        IMath math= proxy.getProxyObject(new Math());
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

5.使用IOC配置的方式实现AOP

5.1引入Spring IOC的核心jar包,方法与前面相同。

<dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>4.3.0.RELEASE</version>
    </dependency>

5.2创建IOC的配置文件beans.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:p="http://www.springframework.org/schema/p"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <!-- 被代理的目标对象 -->
    <bean id="target" class="org.shangjack.spring.aop.aop06.Math"></bean>
    <!--通知、横切逻辑-->
    <bean id="advice" class="org.shangjack.spring.aop.aop06.AfterAdvice"></bean>
    <!--代理对象 -->
    <!--interceptorNames 通知数组 -->
    <!--p:target-ref 被代理的对象-->
    <!--p:proxyTargetClass 被代理对象是否为一个类,如果是则使用cglib,否则使用jdk动态代理  -->
    <bean id="proxy" class="org.springframework.aop.framework.ProxyFactoryBean"
        p:interceptorNames="advice"
        p:target-ref="target"
        p:proxyTargetClass="true"></bean>
</beans>

5.3获得代理类的实例并测试运行

package org.shangjack.spring.aop.aop06;

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

public class Test {

    @org.junit.Test
    public void test01()
    {
        //容器
        ApplicationContext ctx=new ClassPathXmlApplicationContext("beans.xml");
        //从代理工厂中获得代理对象
        IMath math=(IMath)ctx.getBean("proxy");
        int n1=100,n2=5;
        math.add(n1, n2);
        math.sub(n1, n2);
        math.mut(n1, n2);
        math.div(n1, n2);
    }
}

5.4、小结

这里有个值得注意的问题:从容器中获得proxy对象时应该是org.springframework.aop.framework.ProxyFactoryBean类型的对象(如下代码所示),但这里直接就转换成IMath类型了,这是因为:ProxyFactoryBean本质上是一个用来生产Proxy的FactoryBean。如果容器中的某个对象持有某个FactoryBean的引用它取得的不是FactoryBean本身而是 FactoryBean的getObject()方法所返回的对象。所以如果容器中某个对象依赖于ProxyFactoryBean那么它将会使用到 ProxyFactoryBean的getObject()方法所返回的代理对象这就是ProxyFactryBean得以在容器中使用的原因。

ProxyFactoryBean message=new ProxyFactoryBean();
message.setTarget(new Message());
message.addAdvice(new SurroundAdvice());
((IMessage)message.getObject()).message();

  • 7
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

shangjg3

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值