文档章节

Spring AOP(Aspect Oriented Programming)

无畏的老巨人
 无畏的老巨人
发布于 2017/05/09 17:59
字数 1752
阅读 29
收藏 0

概念

Spring AOP’s approach to AOP differs from that of most other AOP frameworks. The aim is not to provide the most complete AOP implementation (although Spring AOP is quite capable); it is rather to provide a close integration between AOP implementation and Spring IoC to help solve common problems in enterprise applications.

Spring Aop是Spring OOP的实现,但是不同于其他的Aop框架。目的不是提供完整的Aop实现,而是更好地衔接Spring Ioc,解决企业开发中的通用问题。 AspectJ 在Aop上功能更全面,与Spring Aop是互相补充。

Spring aop通常作用于方法上,通知方式有以下几种,使用时尽量选择细粒度的通知(一般不用around advice):

  • before前置通知
  • return 通知
  • throw 异常通知
  • after后置通知
  • around环绕通知

Spring Aop是基于代理实现的,可以使用Jdk动态代理和CGLib代理方式。

DbUtils代理使用


public class SqlNullCheckedResultSet implements InvocationHandler {
     /**
     * Maps normal method names (ie. "getBigDecimal") to the corresponding null
     * Method object (ie. getNullBigDecimal).
     */
    private static final Map<String, Method> nullMethods = new HashMap<String, Method>();

    /**
     * The {@code getNull} string prefix.
     * @since 1.4
     */
    private static final String GET_NULL_PREFIX = "getNull";

    static {
        Method[] methods = SqlNullCheckedResultSet.class.getMethods();
        for (int i = 0; i < methods.length; i++) {
            String methodName = methods[i].getName();

            if (methodName.startsWith(GET_NULL_PREFIX)) {
                String normalName = "get" + methodName.substring(GET_NULL_PREFIX.length());
                nullMethods.put(normalName, methods[i]);
            }
        }
    }
     /**
     * The factory to create proxies with.
     */
    private static final ProxyFactory factory = ProxyFactory.instance();

    /**
     * Wraps the <code>ResultSet</code> in an instance of this class.  This is
     * equivalent to:
     * <pre>
     * ProxyFactory.instance().createResultSet(new SqlNullCheckedResultSet(rs));
     * </pre>
     *
     * @param rs The <code>ResultSet</code> to wrap.
     * @return wrapped ResultSet
     */
    public static ResultSet wrap(ResultSet rs) {
        return factory.createResultSet(new SqlNullCheckedResultSet(rs));
    }

    private InputStream nullAsciiStream = null;
    private BigDecimal nullBigDecimal = null;
    private InputStream nullBinaryStream = null;
    private Blob nullBlob = null;
    private boolean nullBoolean = false;
    private byte nullByte = 0;
    private byte[] nullBytes = null;
    private Reader nullCharacterStream = null;
    private Clob nullClob = null;
    private Date nullDate = null;
    private double nullDouble = 0.0;
    private float nullFloat = 0.0f;
    private int nullInt = 0;
    private long nullLong = 0;
    private Object nullObject = null;
    private Ref nullRef = null;
    private short nullShort = 0;
    private String nullString = null;
    private Time nullTime = null;
    private Timestamp nullTimestamp = null;
    private URL nullURL = null;

    /**
     * The wrapped result.
     */
    private final ResultSet rs;

    /**
     * Constructs a new instance of
     * <code>SqlNullCheckedResultSet</code>
     * to wrap the specified <code>ResultSet</code>.
     * @param rs ResultSet to wrap
     */
    public SqlNullCheckedResultSet(ResultSet rs) {
        super();
        this.rs = rs;
    }


 /**
     * Returns the value when a SQL null is encountered as the result of
     * invoking a <code>getAsciiStream</code> method.
     *
     * @return the value
     */
    public InputStream getNullAsciiStream() {
        return this.nullAsciiStream;
    }

    /**
     * Returns the value when a SQL null is encountered as the result of
     * invoking a <code>getBigDecimal</code> method.
     *
     * @return the value
     */
    public BigDecimal getNullBigDecimal() {
        return this.nullBigDecimal;
    }
...... ......other getNull..() ...... ...... 

 @Override
    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable {
        //原来被代理方法的调用结果 如getBigDecimal方法
        Object result = method.invoke(this.rs, args);
        //如根据getBigDecimal名称取得Method对象(getNullBigDecimal)
        Method nullMethod = nullMethods.get(method.getName());

        // Check nullMethod != null first so that we don't call wasNull()
        // before a true getter method was invoked on the ResultSet.
        //如过resultSet结果集为null,调用本类中getNullBigDecimal方法,取得decimal类型默认值,否则返回真实的值
        return (nullMethod != null && this.rs.wasNull())
            ? nullMethod.invoke(this, (Object[]) null)
            : result;
    }
}

以上**wrap(ResultSet rs)**方法为转换代理对象的入口方法。

其中createResultSet方法如下,通过调用此JDK动态代理方法,返回了ResultSet接口的代理对象SqlNullCheckedResultSet 。

 /**
     * Creates a new proxy <code>ResultSet</code> object.
     * @param handler The handler that intercepts/overrides method calls.
     * @return proxied ResultSet
     */
    public ResultSet createResultSet(InvocationHandler handler) {
        return newProxyInstance(ResultSet.class, handler);
    }

 /**
     * Convenience method to generate a single-interface proxy using the handler's classloader
     *
     * @param <T> The type of object to proxy
     * @param type The type of object to proxy
     * @param handler The handler that intercepts/overrides method calls.
     * @return proxied object
     */
    public <T> T newProxyInstance(Class<T> type, InvocationHandler handler) {
        return type.cast(Proxy.newProxyInstance(handler.getClass().getClassLoader(), new Class<?>[] {type}, handler));
    }

##Mybatis Interceptor 核心Plugin类

public class Plugin implements InvocationHandler {
	private Object target;
	private Interceptor interceptor;
	private Map<Class<?>, Set<Method>> signatureMap;

	private Plugin(Object target, Interceptor interceptor,
			Map<Class<?>, Set<Method>> signatureMap) {
		this.target = target;
		this.interceptor = interceptor;
		this.signatureMap = signatureMap;
	}
    //此处生成指定接口的JDK动态代理,在mybatis中,可以代理Executor,StatementHandler,ResultHandler等接口的代理对象。
	public static Object wrap(Object target, Interceptor interceptor) {
		Map signatureMap = getSignatureMap(interceptor);
		Class type = target.getClass();
		Class[] interfaces = getAllInterfaces(type, signatureMap);
		return interfaces.length > 0 ? Proxy.newProxyInstance(type
				.getClassLoader(), interfaces, new Plugin(target, interceptor,
				signatureMap)) : target;
	}
//调用该方法调用代理对象的方法。如果有多层代理嵌套,则利用chain依次调用
	public Object invoke(Object proxy, Method method, Object[] args)
			throws Throwable {
		try {
			Set e = (Set) this.signatureMap.get(method.getDeclaringClass());
			return e != null && e.contains(method) ? this.interceptor
					.intercept(new Invocation(this.target, method, args))
					: method.invoke(this.target, args);
		} catch (Exception arg4) {
			throw ExceptionUtil.unwrapThrowable(arg4);
		}
	}

	private static Map<Class<?>, Set<Method>> getSignatureMap(
			Interceptor interceptor) {
		Intercepts interceptsAnnotation = (Intercepts) interceptor.getClass()
				.getAnnotation(Intercepts.class);
		if (interceptsAnnotation == null) {
			throw new PluginException(
					"No @Intercepts annotation was found in interceptor "
							+ interceptor.getClass().getName());
		} else {
			Signature[] sigs = interceptsAnnotation.value();
			HashMap signatureMap = new HashMap();
			Signature[] arr$ = sigs;
			int len$ = sigs.length;

			for (int i$ = 0; i$ < len$; ++i$) {
				Signature sig = arr$[i$];
				Object methods = (Set) signatureMap.get(sig.type());
				if (methods == null) {
					methods = new HashSet();
					signatureMap.put(sig.type(), methods);
				}

				try {
					Method e = sig.type().getMethod(sig.method(), sig.args());
					((Set) methods).add(e);
				} catch (NoSuchMethodException arg9) {
					throw new PluginException("Could not find method on "
							+ sig.type() + " named " + sig.method()
							+ ". Cause: " + arg9, arg9);
				}
			}

			return signatureMap;
		}
	}

	private static Class<?>[] getAllInterfaces(Class<?> type,
			Map<Class<?>, Set<Method>> signatureMap) {
		HashSet interfaces;
		for (interfaces = new HashSet(); type != null; type = type
				.getSuperclass()) {
			Class[] arr$ = type.getInterfaces();
			int len$ = arr$.length;

			for (int i$ = 0; i$ < len$; ++i$) {
				Class c = arr$[i$];
				if (signatureMap.containsKey(c)) {
					interfaces.add(c);
				}
			}
		}

		return (Class[]) interfaces.toArray(new Class[interfaces.size()]);
	}
}

Interceptor 定义

public interface Interceptor {
	Object intercept(Invocation arg0) throws Throwable;

	Object plugin(Object arg0);

	void setProperties(Properties arg0);
}

chain定义,通过plugin方法,依次代理多个拦截器实例

public class InterceptorChain {
	private final List<Interceptor> interceptors = new ArrayList();

	public Object pluginAll(Object target) {
		Interceptor interceptor;
		for (Iterator i$ = this.interceptors.iterator(); i$.hasNext(); target = interceptor
				.plugin(target)) {
			interceptor = (Interceptor) i$.next();
		}

		return target;
	}

	public void addInterceptor(Interceptor interceptor) {
		this.interceptors.add(interceptor);
	}

	public List<Interceptor> getInterceptors() {
		return Collections.unmodifiableList(this.interceptors);
	}
}

拦截器注解

@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface Intercepts {
	Signature[] value();
}

调用对象封装

public class Invocation {
	private Object target;
	private Method method;
	private Object[] args;

	public Invocation(Object target, Method method, Object[] args) {
		this.target = target;
		this.method = method;
		this.args = args;
	}

	public Object getTarget() {
		return this.target;
	}

	public Method getMethod() {
		return this.method;
	}

	public Object[] getArgs() {
		return this.args;
	}

	public Object proceed() throws InvocationTargetException,
			IllegalAccessException {
		return this.method.invoke(this.target, this.args);
	}
}

定义需要拦截的接口注解


@Retention(RetentionPolicy.RUNTIME)
@Target({ ElementType.TYPE })
public @interface Signature {
	Class<?> type();

	String method();

	Class<?>[] args();
}

自定义拦截器类

package com.ld.common.interceptor;

import java.util.List;
import java.util.Map;
import java.util.Properties;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

/**
 * mysql sql print plugin
 * @author Cruz
 *
 */


@Intercepts({@Signature(
		  type= Executor.class,
		  method = "query",
		  args = {MappedStatement.class,Object.class,RowBounds.class,ResultHandler.class})})
public class SqlOutputInterceptor implements Interceptor{
	private static final Log LOGGER = LogFactory.getLog(SqlOutputInterceptor.class);

	private Properties properties;
	/**
	 * 打印sql语句
	 */
	public void printSql(Configuration configuration,BoundSql boundSql,String sqlId,long time){
		Map parameterObject = (Map)boundSql.getParameterObject();
        List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
        String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
        if (parameterMappings.size() > 0 && parameterObject != null) {
            for(int i=0;i<parameterMappings.size();i++){
            	sql=sql.replaceFirst("\\?", "'"+(String)parameterObject.get(parameterMappings.get(i).getProperty())+"'");
            }
        }
        StringBuilder str = new StringBuilder();
        str.append(sqlId);
        str.append(":");
        str.append(sql);
        str.append("====>");
        str.append(time);
        str.append("ms");
        System.out.println(str);
        if(LOGGER.isWarnEnabled()){
        	LOGGER.warn(str);
        }
	}
	
	@Override
	public Object intercept(Invocation arg0) throws Throwable {
		MappedStatement st=(MappedStatement)arg0.getArgs()[0];
		Configuration config=st.getConfiguration();
		int len=arg0.getArgs().length;
		Object parameter=null;
		if(len>1){
			//read 
			parameter=arg0.getArgs()[1];
		}
		BoundSql boundSql=st.getBoundSql(parameter);
		String sqlId=st.getId();
		long start=System.currentTimeMillis();
		Object ret=arg0.proceed();
		long end=System.currentTimeMillis();
		//show sql
		printSql(config,boundSql,sqlId,end-start);
		return ret;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties arg0) {
		this.properties=arg0;
	}

}


此外在Configuration配置类中,定义了几个可以拦截接口的方法。 通过拦截器的链式调用,分别在接口上增加了一系列代理。

public ParameterHandler newParameterHandler(
			MappedStatement mappedStatement, Object parameterObject,
			BoundSql boundSql) {
		ParameterHandler parameterHandler = mappedStatement.getLang()
				.createParameterHandler(mappedStatement, parameterObject,
						boundSql);
		parameterHandler = (ParameterHandler) this.interceptorChain
				.pluginAll(parameterHandler);
		return parameterHandler;
	}

	public ResultSetHandler newResultSetHandler(Executor executor,
			MappedStatement mappedStatement, RowBounds rowBounds,
			ParameterHandler parameterHandler, ResultHandler resultHandler,
			BoundSql boundSql) {
		DefaultResultSetHandler resultSetHandler = new DefaultResultSetHandler(
				executor, mappedStatement, parameterHandler, resultHandler,
				boundSql, rowBounds);
		ResultSetHandler resultSetHandler1 = (ResultSetHandler) this.interceptorChain
				.pluginAll(resultSetHandler);
		return resultSetHandler1;
	}

	public StatementHandler newStatementHandler(Executor executor,
			MappedStatement mappedStatement, Object parameterObject,
			RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
		RoutingStatementHandler statementHandler = new RoutingStatementHandler(
				executor, mappedStatement, parameterObject, rowBounds,
				resultHandler, boundSql);
		StatementHandler statementHandler1 = (StatementHandler) this.interceptorChain
				.pluginAll(statementHandler);
		return statementHandler1;
	}

	public Executor newExecutor(Transaction transaction) {
		return this.newExecutor(transaction, this.defaultExecutorType);
	}

	public Executor newExecutor(Transaction transaction,
			ExecutorType executorType) {
		return this.newExecutor(transaction, executorType, false);
	}

	public Executor newExecutor(Transaction transaction,
			ExecutorType executorType, boolean autoCommit) {
		executorType = executorType == null ? this.defaultExecutorType
				: executorType;
		executorType = executorType == null ? ExecutorType.SIMPLE
				: executorType;
		Object executor;
		if (ExecutorType.BATCH == executorType) {
			executor = new BatchExecutor(this, transaction);
		} else if (ExecutorType.REUSE == executorType) {
			executor = new ReuseExecutor(this, transaction);
		} else {
			executor = new SimpleExecutor(this, transaction);
		}

		if (this.cacheEnabled) {
			executor = new CachingExecutor((Executor) executor, autoCommit);
		}

		Executor executor1 = (Executor) this.interceptorChain
				.pluginAll(executor);
		return executor1;
	}

以上介绍了常见类库中代理的用法,主要以JDK动态代理为主。在运用时,注意使用一些模式,达到松耦合目的。

© 著作权归作者所有

下一篇: CSS FAQ
无畏的老巨人

无畏的老巨人

粉丝 17
博文 89
码字总数 86791
作品 0
宁波
CTO(技术副总裁)
私信 提问
spring 入门及IOC容器

Spring 一.Spring,原意“春天”,是一种轻量级容器框架,所谓轻量级就是依赖比较少,侵入性较低,其核心就是1. IOC(Inversion of Control)/DI(Dependency Injection)IoC的核心思想是通过消...

天国使者125
2013/06/15
179
0
Spring系列教程六:AOP详细讲解

AOP 概述 什么是 AOP AOP:全称是 Aspect Oriented Programming 即:面向切面编程。 AOP技术是对OOP技术的一种延伸,AOP是面向纵向,OOP是面向横向。简单的说它就是把我们程序重复的代码抽取...

我叫小糖主
05/19
32
0
一个简单的例子实现自己的AOP

AOP是Aspect Oriented Programming的缩写,意思是面向切面编程,与OOP(Object Oriented Programming)面向对象编程对等,都是一种编程思想。 从OOP角度分析,我们关注业务的处理逻辑,是属于纵...

java工会
2018/05/16
0
0
现有AOP解决方案收集

基于.NET的AOP解决方案: Aspect# (http://aspectsharp.sourceforge.net/) is a free AOP framework for .NET. AspectDNG (http://sourceforge.net/projects/aspectdng/) is a .NET multi-l......

强子哥哥
2016/08/27
44
0
Spring之 Aspect Oriented Programming with Spring

1. Concepts Aspect-Oriented Programming (AOP) complements OOP by providing another way of thinking about program structure. While OO decomposes applications into a hierarchy of ......

leodaxin
2018/07/29
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Cloud 笔记之Spring cloud config client

观察者模式它的数据的变化是被动的。 观察者模式在java中的实现: package com.hxq.springcloud.springcloudconfigclient;import org.springframework.context.ApplicationListener;i...

xiaoxiao_go
53分钟前
4
0
CentOS7.6中安装使用fcitx框架

内容目录 一、为什么要使用fcitx?二、安装fcitx框架三、安装搜狗输入法 一、为什么要使用fcitx? Gnome3桌面自带的输入法框架为ibus,而在使用ibus时会时不时出现卡顿无法输入的现象。 搜狗和...

技术训练营
今天
4
0
《Designing.Data-Intensive.Applications》笔记 四

第九章 一致性与共识 分布式系统最重要的的抽象之一是共识(consensus):让所有的节点对某件事达成一致。 最终一致性(eventual consistency)只提供较弱的保证,需要探索更高的一致性保证(stro...

丰田破产标志
今天
7
0
docker 使用mysql

1, 进入容器 比如 myslq1 里面进行操作 docker exec -it mysql1 /bin/bash 2. 退出 容器 交互: exit 3. mysql 启动在容器里面,并且 可以本地连接mysql docker run --name mysql1 --env MY...

之渊
今天
7
0
python数据结构

1、字符串及其方法(案例来自Python-100-Days) def main(): str1 = 'hello, world!' # 通过len函数计算字符串的长度 print(len(str1)) # 13 # 获得字符串首字母大写的...

huijue
今天
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部