Spring AOP

代理模式

什么是代理

举个实际的例子:像卖电脑这样一件事情,有很多的电脑生成厂商,他们卖出去电脑,都需要前期的宣传营销,然后把各自的电脑卖出去,然后都需要售后服务。而宣传营销和售后服务对于每一个厂商来说都是一样的(大致可以这样理解),只有各自生成的电脑不一样。为了节约成本,就产生了代理这样一个东西,每一个电脑生成厂商都把宣传营销和售后服务交给他们的代理去完成,电脑生产商只关心生成就可以了,这样可以极大的提高效率,和节约成本。
在实际的软件开发过程中,像性能监控和事务管理,也是同样的道理

代理模式有什么作用

为其他对象提供一种代理以控制对这个对象的访问。在某些情况下,一个客户不想或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用

静态代理

  1. 什么是静态代理
    所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了
  2. 静态代理的优缺点
    1) 缺点:

    • 代理对象的一个接口只服务于一种类型的对象,如果要代理的方法很多,势必要为每一种方法都进行代理,静态代理在程序规模稍大时就无法胜任了。
    • 如果接口增加一个方法,除了所有实现类需要实现这个方法外,所有代理类也需要实现此方法。增加了代码维护的复杂度。
  3. 静态代理的实现

java动态代理

动态代理

动态代理类的源码是在程序运行期间由JVM根据反射等机制动态的生成,所以不存在代理类的字节码文件。代理类和委托类的关系是在程序运行时确定

动态代理的实现
  • java.lang.reflect.Proxy
    这是 Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象
// 方法 1: 该方法用于获取指定代理对象所关联的调用处理器  
static InvocationHandler getInvocationHandler(Object proxy)   

// 方法 2:该方法用于获取关联于指定类装载器和一组接口的动态代理类的类对象  
static Class getProxyClass(ClassLoader loader, Class[] interfaces)   

// 方法 3:该方法用于判断指定类对象是否是一个动态代理类  
static boolean isProxyClass(Class cl)   

// 方法 4:该方法用于为指定类装载器、一组接口及调用处理器生成动态代理类实例  
static Object newProxyInstance(ClassLoader loader, Class[] interfaces, InvocationHandler h) 
  • java.lang.reflect.InvocationHandler
    这是调用处理器接口,它自定义了一个 invoke 方法,用于集中处理在动态代理类对象上的方法调用,通常在该方法中实现对委托类的代理访问。每次生成动态代理类对象时都要指定一个对应的调用处理器对象。
 /* 该方法负责集中处理动态代理类上的所有方法调用。第一个参数既是代理类实例,第二个参数是被调用的方法对象  
 ** 第三个方法是调用参数。调用处理器根据这三个参数进行预处理或分派到委托类实例上反射执行  
 */
 Object invoke(Object proxy, Method method, Object[] args) 
  • 实现举例
    1) 目标接口
public interface HelloWorld {

    /**
     * Say hello.
     */
    void sayHello();

    void nihao();
}

2) 实现类

public class HelloWorldImpl implements HelloWorld {

    /**
     * TODO 简单描述该方法的实现功能(可选).
     * @see com.lei.test.HelloWorld#sayHello()
     */
    @Override
    public void sayHello() {
        System.out.println("HelloWorld!");
    }

    /**
     * TODO 简单描述该方法的实现功能(可选).
     * @see com.lei.test.HelloWorld#nihao()
     */
    @Override
    public void nihao() {
        System.out.println("----nihao !");
    }

}

3) 代理类

public class HelloWorldHandler implements InvocationHandler {

    /** The object. */
    private Object object;

    /**
     * Creates a new instance of HelloWorldHandler.
     *
     * @param object the object
     */

    public HelloWorldHandler(Object object) {
        super();
        this.object = object;
    }

    /**
     * TODO 简单描述该方法的实现功能(可选).
     *
     * @param proxy the proxy
     * @param method the method
     * @param args the args
     * @return the object
     * @throws Throwable the throwable
     * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        Object result = null;
        // 调用之前
        doBefore();
        // 调用原始对象的方法
        result = method.invoke(object, args);
        // 调用之后
        doAfter();
        return result;
    }

    /**
     * Do before.
     */
    private void doBefore() {
        System.out.println("before method invoke");
    }

    /**
     * Do after.
     */
    private void doAfter() {
        System.out.println("after method invoke");
    }

}

4) 测试

public class HelloWorldTest {

    public static void main(String[] args) {
        HelloWorld helloWorld = new HelloWorldImpl();

        // 创建动态代理对象
        HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(helloWorld.getClass().getClassLoader(),
                                                               helloWorld.getClass().getInterfaces(), new HelloWorldHandler(helloWorld));
        proxy.sayHello();

        proxy.nihao();
    }
}

Spring AOP

什么是AOP

SpringAOP作为面向对象编程的一种补充,广泛应用于处理一些具有横切性质的系统级服务,如事务管理、安全检查、缓存、对象池管理等。
AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理则可分为静态代理和动态代理两大类.其中静态代理是指使用 AOP 框架提供的命令进行编译,从而在编译阶段就可生成 AOP 代理类,因此也称为编译时增强;而动态代理则在运行时借助于 JDK 动态代理、CGLIB 等在内存中“临时”生成 AOP 动态代理类,因此也被称为运行时增强.

为什么使用AOP

减少重复代码、降低耦合

Sring AOP 的重要概念

  • 切面:通知和切入点的结合
  • 通知:就是你想要的功能,也就是上说的安全、事物、日子等。你给先定义好,然后再想用的地方用一下。包含Aspect的一段处理代码。
    通知类型:前置通知[Before advice]、正常返回通知[After returning advice]、异常返回通知[After throwing advice]、返回通知[After (finally) advice]、环绕通知[Around advice]

  • 切入点:上面说的连接点的基础上,来定义切入点,你的一个类里,有15个方法,那就有十几个连接点了对吧,但是你并不想在所有方法附件都使用通知(使用叫织入,下面再说),你只是想让其中几个,在调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法,让切点来筛选连接点,选中那几个你想要的方法。
    切入点常用的是表达式形式,具体语法参照:http://www.360doc.com/content/13/1212/09/14416931_336521220.shtml

  • 目标对象:目标类,也就是要被通知的对象,也就是真正的业务逻辑
  • 连接点:就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的钱、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。其他如AspectJ还可以让你在构造器或属性注入时都行,不过那不是咱们关注的,只要记住,和方法有关的前前后后都是连接点

Spring AOP的实现

xml配置方式
  • 引入jar包
 compile group: 'org.aspectj', name: 'aspectjrt', version: '1.8.6'
 compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.6'
  • 切面代码
/**
 * Project Name:manager-web<br>
 * File Name:ExecutionTimeLog.java<br>
 * Package Name:cn.com.duiba.tuia.manager.aop<br>
 * Date:2016年5月31日下午2:14:12<br>
 * Copyright (c) 2016, duiba.com.cn All Rights Reserved.
 */

package cn.com.duiba.tuia.manager.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import cn.com.duiba.tuia.manager.utils.TimeProfileUtils;

/**
 * ClassName: ExecutionTimeLog <br/>
 * Function: TODO ADD FUNCTION. <br/>
 * Reason: TODO ADD REASON(可选). <br/>
 * date: 2016年5月31日 下午2:14:12 <br/>
 *
 * @author leiliang
 * @version
 * @since JDK 1.7
 */
public class ExecutionTimeLog {

    /** The logger. */
    protected Logger logger = LoggerFactory.getLogger(getClass());

    /** controller层日志开启标志位(true:开启, flase:关闭不打). */
    private boolean  CONTROLLER_TIME_LOG_SIGN;

    /** service层日志开启标志位(true:开启, flase:关闭不打). */
    private boolean  SERVICE_TIME_LOG_SIGN;

    /** dao层日志开启标志位(true:开启, flase:关闭不打). */
    private boolean  DAO_TIME_LOG_SIGN;

    /**
     * Controller around.
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    public void controllerAround(ProceedingJoinPoint joinPoint) throws Throwable {
        around(joinPoint, CONTROLLER_TIME_LOG_SIGN);
    }

    /**
     * Service around.
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    public Object serviceAround(ProceedingJoinPoint joinPoint) throws Throwable {
        return around(joinPoint, SERVICE_TIME_LOG_SIGN);
    }

    /**
     * Dao around.
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    public Object daoAround(ProceedingJoinPoint joinPoint) throws Throwable {
        return around(joinPoint, DAO_TIME_LOG_SIGN);
    }

    /**
     * Around.
     *
     * @param joinPoint the join point
     * @param sign the sign
     * @return the object
     * @throws Throwable the throwable
     */
    private Object around(ProceedingJoinPoint joinPoint, boolean sign) throws Throwable {
        if (sign) {
            Signature signature = joinPoint.getSignature();
            String fullName = signature.getDeclaringTypeName();
            TimeProfileUtils.enter(fullName.substring(fullName.lastIndexOf(".") + 1) + " " + signature.getName());
        }
        Object ret = joinPoint.proceed();
        if (sign) {
            TimeProfileUtils.release();
        }
        return ret;
    }

    /**
     * cONTROLLER_TIME_LOG_SIGN.
     *
     * @param cONTROLLER_TIME_LOG_SIGN the cONTROLLER_TIME_LOG_SIGN to set
     * @since JDK 1.7
     */
    public void setCONTROLLER_TIME_LOG_SIGN(boolean cONTROLLER_TIME_LOG_SIGN) {
        CONTROLLER_TIME_LOG_SIGN = cONTROLLER_TIME_LOG_SIGN;
    }

    /**
     * sERVICE_TIME_LOG_SIGN.
     *
     * @param sERVICE_TIME_LOG_SIGN the sERVICE_TIME_LOG_SIGN to set
     * @since JDK 1.7
     */
    public void setSERVICE_TIME_LOG_SIGN(boolean sERVICE_TIME_LOG_SIGN) {
        SERVICE_TIME_LOG_SIGN = sERVICE_TIME_LOG_SIGN;
    }

    /**
     * dAO_TIME_LOG_SIGN.
     *
     * @param dAO_TIME_LOG_SIGN the dAO_TIME_LOG_SIGN to set
     * @since JDK 1.7
     */
    public void setDAO_TIME_LOG_SIGN(boolean dAO_TIME_LOG_SIGN) {
        DAO_TIME_LOG_SIGN = dAO_TIME_LOG_SIGN;
    }
}
  • 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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
           http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context-3.0.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
           http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://code.alibabatech.com/schema/dubbo
           http://code.alibabatech.com/schema/dubbo/dubbo.xsd">

    <aop:config>
        <aop:aspect id="executionTimeLogAspect" ref="executionTimeLog">
            <!-- 切面 -->
            <aop:pointcut id="pointController"
                expression="execution(public * cn.com.duiba.tuia.manager.controller..*.*(..))" />
            <aop:pointcut id="pointService"
                expression="execution(public * cn.com.duiba.tuia.manager.service..*.*(..)) or execution(public * cn.com.duiba.tuia.manager.common.*.*(..))" />
            <aop:pointcut id="poinDao"
                expression="execution(public * cn.com.duiba.tuia.manager.dao..*.*(..))" />
            <!-- 通知 -->
            <aop:around pointcut-ref="pointController" method="controllerAround" />
            <aop:around pointcut-ref="pointService" method="serviceAround" />
            <aop:around pointcut-ref="poinDao" method="daoAround" />
        </aop:aspect>
    </aop:config>
    <!-- 切面bean -->
    <bean id="executionTimeLog" class="cn.com.duiba.tuia.manager.aop.ExecutionTimeLog">
        <property name="CONTROLLER_TIME_LOG_SIGN" value="${manager.controller.time.log.sign}" />
        <property name="SERVICE_TIME_LOG_SIGN" value="${manager.service.time.log.sign}" />
        <property name="DAO_TIME_LOG_SIGN" value="${manager.dao.time.log.sign}" />
    </bean>

</beans>

注解方式
  • 引入jar包
 compile group: 'org.aspectj', name: 'aspectjrt', version: '1.8.6'
 compile group: 'org.aspectj', name: 'aspectjweaver', version: '1.8.6'
  • 切面代码
/**
 * Project Name:tuia-web<br>
 * File Name:ExecutionTimeLog.java<br>
 * Package Name:cn.com.duiba.tuia.aop<br>
 * Date:2016年5月26日下午3:23:34<br>
 * Copyright (c) 2016, duiba.com.cn All Rights Reserved.
 */

package cn.com.duiba.tuia.aop;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import cn.com.duiba.tuia.tool.TimeProfileTool;

/**
 * ClassName: ExecutionTimeLog <br/>
 * Function: 计算执行时间日志aop <br/>
 * Reason: TODO ADD REASON(可选). <br/>
 * date: 2016年5月26日 下午3:23:34 <br/>
 *
 * @author leiliang
 * @version
 * @since JDK 1.7
 */
@Component
@Aspect
// 切面
public class ExecutionTimeLog {

    /** The logger. */
    protected Logger logger = LoggerFactory.getLogger(getClass());

    /** controller层日志开启标志位(true:开启, flase:关闭不打). */
    @Value("${adv.controller.time.log.sign}")
    private boolean  CONTROLLER_TIME_LOG_SIGN;

    /** service层日志开启标志位(true:开启, flase:关闭不打). */
    @Value("${adv.service.time.log.sign}")
    private boolean  SERVICE_TIME_LOG_SIGN;

    /** dao层日志开启标志位(true:开启, flase:关闭不打). */
    @Value("${adv.dao.time.log.sign}")
    private boolean  DAO_TIME_LOG_SIGN;

    /**
     * Controller aspect.
     */
    // 切入点
    @Pointcut("execution(public * cn.com.duiba.tuia.controller..*.*(..))")
    public void controllerAspect() {
    }

    /**
     * Service aspect.
     */
    @Pointcut("execution(public * cn.com.duiba.tuia.service..*.*(..)) or execution(public * cn.com.duiba.tuia.cache.*.*(..)) or execution(public * cn.com.duiba.tuia.task.*.*(..))")
    public void serviceAspect() {
    }

    /**
     * Dao aspect.
     */
    @Pointcut("execution(public * cn.com.duiba.tuia.dao..*.*(..))")
    public void daoAspect() {
    }

    /**
     * Controller around.
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    // 环绕通知
    @Around("controllerAspect()")
    public void controllerAround(ProceedingJoinPoint joinPoint) throws Throwable {
        around(joinPoint, CONTROLLER_TIME_LOG_SIGN);
    }

    /**
     * Service around.
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    @Around("serviceAspect()")
    public Object serviceAround(ProceedingJoinPoint joinPoint) throws Throwable {
        return around(joinPoint, SERVICE_TIME_LOG_SIGN);
    }

    /**
     * Dao around.
     *
     * @param joinPoint the join point
     * @return the object
     * @throws Throwable the throwable
     */
    @Around("daoAspect()")
    public Object daoAround(ProceedingJoinPoint joinPoint) throws Throwable {
        return around(joinPoint, DAO_TIME_LOG_SIGN);
    }

    /**
     * Around.
     *
     * @param joinPoint the join point
     * @param sign the sign
     * @return the object
     * @throws Throwable the throwable
     */
    private Object around(ProceedingJoinPoint joinPoint, boolean sign) throws Throwable {
        if (sign) {
            Signature signature = joinPoint.getSignature();
            String fullName = signature.getDeclaringTypeName();
            TimeProfileTool.enter(fullName.substring(fullName.lastIndexOf(".") + 1) + " " + signature.getName());
        }
        Object ret = joinPoint.proceed();
        if (sign) {
            TimeProfileTool.release();
        }
        return ret;
    }
}
  • 0
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值