动态代理实现Spring Aop

Posted 邱慕夏

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了动态代理实现Spring Aop相关的知识,希望对你有一定的参考价值。

引言


我们在前两篇文章中,都为这篇做了铺垫,我们现在来做这样一件事情,在业务逻辑中添加Aop的非业务逻辑。



AopClinetTest:

package com.tgb.client;

import com.tgb.config.BeanFactory;
import com.tgb.config.ClassPathXmlApplicationContext;
import com.tgb.dao.UserDao;
import com.tgb.domain.User;
/**
 * AOP效果测试
* @ClassName: AopClientTest 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author [qmx] 
* @date 
*
 */
public class AopClientTest {

	public static void main(String[] args) throws Exception {
		//初始化容器对象
		BeanFactory factory = new ClassPathXmlApplicationContext();

		User user = new User();
		user.setUserName("hanyankun");
		//获取容器中userDao对象
		UserDao userDao = (UserDao)factory.getBean("UserDao");
		
		userDao.update(user);

	}
}

AspectCacheBean:


package com.tgb.config;

import com.tgb.util.MethodCatch;
/**
* @ClassName: AspectCachBean 
* @Description: 
* 缓存服务类,实现了对缓存的,前置,后置, 保存的方法
* @author [qmx] 
* @date 
*
 */
public class AspectCachBean {
	
	@MethodCatch(methodName="cacheBefore")
	public  void cacheBefore(Object proxy) {

		System.out.println("---这是切入 类AspectCachBean  cacheBefore()-方法--");
	}

	@MethodCatch(methodName="cacheAfter")
	public static void cacheAfter(Object proxy) {
		System.out.println("---这是切入 类AspectCachBean  cacheAfter()-方法--");
	}
	@MethodCatch(methodName="cacheSave")
	public void cacheSave(Object proxy){
		System.out.println("---这是切入 类AspectCachBean  cacheSave()-方法--");
	}
}

AspectCertifiyBean:

package com.tgb.config;

import com.tgb.util.MethodCatch;
/**
 * 
* @ClassName: AspectCertifiyBean 
* @Description: 认证服务类,提供了认证前, 认证后,认证保存的方法 
* @author [qmx] 
* @date  
*
 */
public class AspectCertifiyBean {

	@MethodCatch(methodName = "certifiyBefore")
	public void certifiyBefore(Object proxy) {

		System.out.println("---这是切入 类AspectCertifiyBean  certifiyBefore()-方法--");
	}

	@MethodCatch(methodName = "certifyAfter")
	public  void certifyAfter(Object proxy) {
		System.out.println("---这是切入 类AspectCertifiyBean  certifyAfter()-方法--");
	}

	@MethodCatch(methodName = "certifySave")
	public void certifySave(Object proxy) {
		System.out.println("---这是切入 类AspectCertifiyBean  certifySave()-方法--");
	}
}

BeanFactory:


package com.tgb.config;

import java.util.List;
import java.util.Map;
/**
 * 
* @ClassName: ContainerBeans 
* @Description: 容器接口,提供容器公共服务方法, 增加,删除,遍历,获取对象,遍历类型,容器大小等方法
* @author [qmx] 
* @date 
*
 */
public interface BeanFactory {
	/**
	 * 获取容器中指定对象
	 * 
	 * @param id
	 *            对象名称如: getBean("user")
	 * @return
	 */
	public Object getBean(String id);

	/**
	 * 容器中放入对象
	 * 
	 * @param k
	 * @param v
	 * @return
	 */
	public Object put(String k, Object v);

	/**
	 * 打印容器中所有对象类型
	 */
	public void printTypeName();

	/**
	 * 获取容器中所有对象 返回类型 Map<string(对象类型),Object(对象)>
	 * 
	 * @return Map<string(对象类型),Object(对象)>
	 */
	public Map<String, Object> getAllBean();
	
	/**
	 * 获取容器所有bean
	 */
	public void printAllbeanId();

	public void remove(String id);

	/**
	 * 容器中对象的数量
	 * @return
	 */
	public int size();
}

ClassPathXmlApplicationContext:


package com.tgb.config;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeSet;

import net.sf.cglib.asm.ClassVisitor;

import org.jdom.Document;
import org.jdom.Element;
import org.jdom.input.SAXBuilder;

/***
 * 
 * 
 * @author  容器组装类, 装载 业务容器 和 切入容器。 分别将其中的 颗粒装载到各自的 beans 中
 */
public class ClassPathXmlApplicationContext implements BeanFactory {

	// 业务容器beans
	private Map<String, Object> containerBeans = new HashMap<String, Object>();

	// 切面容器beans
	private Map<String, Object> aspectBeans = new HashMap<String, Object>();

	// 设置是否需要aop
	private boolean isAop = true;

	/*
	 * 构造函数加载个容器内对象
	 */
	public ClassPathXmlApplicationContext() throws Exception {
		SAXBuilder sb = new SAXBuilder();

		// 扫描业务容器对象
		Document containerDoc = sb.build(Thread.currentThread().getContextClassLoader()
				.getResourceAsStream("beans.xml"));

		// 扫描切入容器对象
		Document aspectDoc = sb.build(Thread.currentThread().getContextClassLoader()
				.getResourceAsStream("aspecbeans.xml"));

		// 设置切面容器
		getAspectBeans(aspectDoc);

		// 设置业务容器bean
		getContainerBeans(containerDoc);
	}

	/***
	 * 设置业务容器装配
	 * @param doc
	 * @throws ClassNotFoundException
	 * @throws IllegalAccessException
	 * @throws InstantiationException
	 * @throws SecurityException
	 * @throws NoSuchMethodException
	 * @throws InvocationTargetException
	 * @throws IllegalArgumentException
	 */
	private void getContainerBeans(Document doc) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException, NoSuchMethodException,
			SecurityException, IllegalArgumentException, InvocationTargetException {

		//读取文档根目录
		Element root = doc.getRootElement();
		//获取aop节点信息
		Element aopElement = (Element) root.getChildren("aop").get(0);
		//aop节点属性
		isAop = Boolean.parseBoolean(aopElement.getAttributeValue("isaop"));
		//获取bean节点
		List list = root.getChildren("bean");
		// 前置增强节点
		List aopBeforeList = root.getChildren("aspectbefore");
		//后置节点
		List aopAfterList = root.getChildren("aspectafter");
		//辨别增强节点是否有配置,放入bean关系容器中
		if (aopBeforeList != null) {
			containerBeans.put("aspectbefore", aopBeforeList);

		}
		if (aopAfterList != null) {
			containerBeans.put("aspectafter", aopAfterList);
		}
		//调用装备对象方法,装配业务对象
		putAllBeans(list, containerBeans);
		
	}

	/**
	 * 设置切入容器装配对象
	 * 
	 * @param doc
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 * @throws InvocationTargetException 
	 * @throws IllegalArgumentException 
	 * @throws SecurityException 
	 * @throws NoSuchMethodException 
	 */
	private void getAspectBeans(Document doc) throws InstantiationException,
			IllegalAccessException, ClassNotFoundException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {

		//读取文档根目录
		Element root = doc.getRootElement();
		List aspectElements = root.getChildren("aspectbean");//读取切入配置文件
		putAllBeans(aspectElements,aspectBeans);

	}

	/**
	 * 对象装配方法
	 * @param list 读取的配置文件
	 * @param allBeans 设置装配的容器对象
	 * @throws InstantiationException
	 * @throws IllegalAccessException
	 * @throws ClassNotFoundException
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public void putAllBeans(List list, Map<String, Object> allBeans)
			throws InstantiationException, IllegalAccessException,
			ClassNotFoundException, NoSuchMethodException, SecurityException,
			IllegalArgumentException, InvocationTargetException {

		for (int i = 0; i < list.size(); i++) {
			//获取传入父亲节点中的每个子节点,为行element
			Element element = (Element) list.get(i);
			//获取子节点中的id属性
			String id = element.getAttributeValue("id");
			//获取子节点中的class属性
			String clazz = element.getAttributeValue("class");
			//实例化class
			Object o = Class.forName(clazz).newInstance();

			//将实例化的class放入容器
			allBeans.put(id, o);

			//for循环获取 bean中的 属性property
			for (Element propertyElement : (List<Element>) element
					.getChildren("property")) {
				//获取property属性中的name属性
				String name = propertyElement.getAttributeValue("name"); // userDAO
				
				//获取property属性中的ref属性
				String bean = propertyElement.getAttributeValue("ref"); // 
				
				//获取子属性的试题
				Object beanObject = allBeans.get(bean);// UserDAOImpl

				//调用 依赖实体中的set方法(为子实体的方法)
				String methodName = "set" + name.substring(0, 1).toUpperCase()
						+ name.substring(1);

				Method m = o.getClass().getMethod(methodName,
						beanObject.getClass().getInterfaces()[0]);
				m.invoke(o, beanObject);
			}

		}
	}

	
	/**
	 * 获取容器中指定对象
	 * 
	 * @param id
	 *            对象名称如: getBean("user")
	 * @return
	 */
	public Object getBean(String id) {

		if (!isAop) {
			return containerBeans.get(id);
		}
		return new JDKDynamicProxy(containerBeans.get(id), aspectBeans, containerBeans)
				.getProxy();
	}

	/**
	 * 容器中放入对象
	 * 
	 * @param k
	 * @param v
	 * @return
	 */
	public Object put(String k, Object v) {

		return containerBeans.put(k, v);
	}

	/**
	 * 打印容器中所有对象类型
	 */
	public void printTypeName() {

		Set<String> hashSet = new HashSet<String>();
		Set<Entry<String, Object>> entryset = containerBeans.entrySet();
		{
			Iterator iterator = entryset.iterator();
			while (iterator.hasNext()) {
				Entry<String, Object> entry = (Entry<String, Object>) iterator.next();
				hashSet.add(entry.getValue().getClass().getSimpleName());
			}

		}
		for (String setType : hashSet) {
			System.out.println(setType);
		}

	}

	/**
	 * 获取容器中所有对象
	 * 
	 * @return Map<string(对象类型),Object(对象)>
	 */
	public Map<String, Object> getAllBean() {
		Map<String, Object> beanList = new HashMap<String, Object>();
		Iterator iterator = containerBeans.entrySet().iterator();
		while (iterator.hasNext()) {
			Entry<String, Object> entry = (Entry<String, Object>) iterator.next();
			beanList.put(entry.getValue().getClass().getSimpleName(), entry.getValue());
		}
		return beanList;

	}

	/***
	 * 删除指定对象
	 */
	public void remove(String id) {
		containerBeans.remove(id);

	}

	/***
	 * 打印所有注入对象
	 */
	public void printAllbeanId() {
		Set<Entry<String, Object>> entryset = containerBeans.entrySet();

		Set<String> linkSet = new TreeSet<String>();
		{
			Iterator iterator = entryset.iterator();
			while (iterator.hasNext()) {
				Entry<String, Object> entry = (Entry<String, Object>) iterator.next();
				linkSet.add(entry.getKey());
				// System.out.println(entry.getKey());
			}

			System.out.println(linkSet.toString());
			System.out.println("容器中的对象个数是" + size() + "个");
		}

	}

	/**
	 * 获取容器中对象的个数
	 */
	public int size() {
		return containerBeans.size();
	}
}

JDKDynamicProxy:


package com.tgb.config;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

import org.jdom.Element;

import com.tgb.util.MethodCatch;

/* @ClassName: JDKDynamicProxy 
* @Description: AOP实现对业务容器 的增强,对业务容器中每个对象增强 服务类中的方法,根据 关系容器配置,
* 实现特性方法增强
* @author [qmx] 
*/ 
public class JDKDynamicProxy implements InvocationHandler {

	private Object target;

	private Map<String, Object> aspectBeans; // 切入容器
	private Map<String, Object> containerBeans;// 业务容器

	/**
	 * 代理类获取代理对象,业务容器,切面容器
	 * 
	 * @param target
	 *            被代理对象
	 * @param aspectBeans
	 *            切面容器
	 * @param containerBeans
	 *            业务容器
	 */
	public JDKDynamicProxy(Object target, Map<String, Object> aspectBeans,
			Map<String, Object> containerBeans) {
		this.target = target;
		this.aspectBeans = aspectBeans;
		this.containerBeans = containerBeans;
	}

	@SuppressWarnings("unchecked")
	public <T> T getProxy() {
		return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target
				.getClass().getInterfaces(), this);
	}

	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

		List beforeList = (List) containerBeans.get("aspectbefore");//获取关系容器中的配置
		invokeAspectName(beforeList, method, args);// 调用切面类中匹配方法
		Object result = method.invoke(target, args);// 调用 被代理类本身方法

		return result;
	}

	/**
	 * 拦截方法匹配
	 * 
	 * @param beforeList
	 *            拦截器的所有对象
	 * @param method
	 * @param args
	 * @throws NoSuchMethodException
	 * @throws SecurityException
	 * @throws IllegalAccessException
	 * @throws IllegalArgumentException
	 * @throws InvocationTargetException
	 */
	public void invokeAspectName(List beforeList, Method method, Object[] args)
			throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		
		//if判断获取的集合是否为空,若为空则执行所有服务对象的所有方法
		if (beforeList != null && beforeList.size() != 0) {

			//for循环遍历 集合对象中的对象,获取每个对象,执行该对象中的方法
			for (int i = 0; i < beforeList.size(); i++) {

				Element element = (Element) beforeList.get(i); //获取每个对象
				String aspectClass = element.getAttributeValue("aspectId");// 获取容器中切入类名称
				String aspectName = element.getAttributeValue("aspectMethod");// 执行的切入方法
				//若对象为空,则说明
				if (aspectBeans.get(aspectClass) == null) {
					System.out.println("未找到该切入类,请查看配置的切入类名称是否正确");
					return;
				}
				Class clazz = aspectBeans.get(aspectClass).getClass(); // 获取切入类
				String elementMethod = element.getAttributeValue("method");// 获取被切入类方法

				// 未声明切入方法,则执行所有切入方法
				if (aspectName == null) {

					if (method.getName() != null) {
						if (method.getName().equals(elementMethod)) {
							getAllMethod(clazz, aspectClass, args);
							

						}
						
					}
					aspactAllClass(aspectClass, args==null? new Object[1]: args);
				} else {
					//if判断是否 声明l切入方法,若是则执行指定切入方法,否则执行所有方法
					if (method.getName().equals(elementMethod)) {
						Method jinectmethod = clazz.getMethod(aspectName, Object.class); // 反射调用切入类方法
						jinectmethod.invoke(aspectBeans.get(aspectClass), args==null? new Object[1]: args);
					}
					aspactAllClass(aspectClass, args==null? new Object[1]: args);
				}

			}

		}
		//传入集合对象为空,则执行多有对象的所有方法
		else {

			Iterator aspectClass = aspectBeans.entrySet().iterator();
			while (aspectClass.hasNext()) {
				Entry<String, Object> entry = (Entry<String, Object>) aspectClass.next();
				Class clazz = entry.getValue().getClass();// 获取切入类
				Method[] methods = clazz.getMethods();
				for (int j = 0; j < methods.length; j++) {
					// 读取切入类注解中的所有方法
					if (methods[j].isAnnotationPresent(MethodCatch.class) == true) {
						MethodCatch methodCatch = methods[j]
								.getAnnotation(MethodCatch.class);// 获取注解类
						Method jinectmethod = clazz.getMethod(methodCatch.methodName(),
								Object.class); // 反射调用切入类方法
						jinectmethod.invoke(entry.getValue(), args==null? new Object[1]: args);
					}

				}
			}
		}
	}

	/**
	 * 
	* @Title: aspactAllClass 
	* @Description:  执行容器中所有服务类中的方法(除了指定的对象不执行,其他的都执行)
	* @param @param aspectId
	* @param @param args
	* @param @throws NoSuchMethodException
	* @param @throws SecurityException
	* @param @throws IllegalAccessException
	* @param @throws IllegalArgumentException
	* @param @throws InvocationTargetException    设定文件 
	* @return void    返回类型 
	* @throws
	 */
	public void aspactAllClass(String aspectId, Object[] args)
			throws NoSuchMethodException, SecurityException, IllegalAccessException,
			IllegalArgumentException, InvocationTargetException {
		Iterator aspectClass = aspectBeans.entrySet().iterator();
		
		//while循环获取服务集合中的所有对象,调用该对象中的方法
		while (aspectClass.hasNext()) {
			Entry<String, Object> entry = (Entry<String, Object>) aspectClass.next();
			
			if (!aspectId.equals(entry.getKey())) {
				Class clazz = entry.getValue().getClass();// 获取切入类
				Method[] methods = clazz.getMethods();
				for (int j = 0; j < methods.length; j++) {
					// 读取切入类注解中的所有方法
					if (methods[j].isAnnotationPresent(MethodCatch.class) == true) {
						MethodCatch methodCatch = methods[j]
								.getAnnotation(MethodCatch.class);// 获取注解类
						Method jinectmethod = clazz.getMethod(methodCatch.methodName(),
								Object.class); // 反射调用切入类方法
						jinectmethod.invoke(entry.getValue(), args==null? new Object[1]: args);
					}

				}
			}
		}
	}

	/**
	 * 执行某个服务类中的所有方法
	* @Title: getAllMethod 
	* @Description: TODO(这里用一句话描述这个方法的作用) 
	* @param @param clazz
	* @param @param aspectClass
	* @param @param args
	* @param @throws IllegalAccessException
	* @param @throws IllegalArgumentException
	* @param @throws InvocationTargetException
	* @param @throws NoSuchMethodException
	* @param @throws SecurityException    设定文件 
	* @return void    返回类型 
	* @throws
	 */
	public void getAllMethod(Class clazz, String aspectClass, Object[] args)
			throws IllegalAccessException, IllegalArgumentException,
			InvocationTargetException, NoSuchMethodException, SecurityException {
		Method[] methods = clazz.getMethods();
		for (int j = 0; j < methods.length; j++) {

			if (methods[j].isAnnotationPresent(MethodCatch.class) == true) {
				MethodCatch methodCatch = methods[j].getAnnotation(MethodCatch.class);// 获取注解类
				Method jinectmethod = clazz.getMethod(methodCatch.methodName(),
						Object.class); // 反射调用切入类方法
				jinectmethod.invoke(aspectBeans.get(aspectClass), args==null? new Object[1]: args);
			}

		}
	}
}

UserDao:


package com.tgb.dao;

import com.tgb.domain.User;

/**
 * 用于操作用户信息类。
* @ClassName: UserDao 
* @Description: TODO(这里用一句话描述这个类的作用) 
* @author [qmx] 
* @date 
*
 */
public interface UserDao {

	void save(User user);
	
	void update(User user);
	
	public void delete( User user);
	
}

UserDaoImpl:


package com.tgb.daoImpl;

import com.tgb.dao.UserDao;
import com.tgb.domain.User;

/**
 * 用户操作类,用于添加用户信息
 * 
 * @ClassName: UserDaoImpl
 * @Description: TODO(这里用一句话描述这个类的作用)
 * @author [qmx]
 * @date 
 * 
 */
public class UserDaoImpl implements UserDao {

	@Override
	public void save(User user) {
		System.out.println("这是业务类 " + this.getClass()
				+ "-----的 userDao.save()方法-----");

	}

	@Override
	public void update(User user) {
		System.out.println("这是业务类 " + this.getClass()
				+ "-----的 userDao.update()方法-----");

	}

	@Override
	public void delete(User user) {
		System.out.println("这是业务类 " + this.getClass()
				+ "-----的 userDao.delete()方法-----");

	}

}

User:


package com.tgb.domain;

public class User {

	private String userName;
	private String password;
	public String getUserName() {
		return userName;
	}
	public void setUserName(String userName) {
		this.userName = userName;
	}
	public String getPassword() {
		return password;
	}
	public void setPassword(String password) {
		this.password = password;
	}
	
	
}

MethodCatch:


package com.tgb.util;

/**
 * 自定义注解类,用于类方法上读取方法名称
 */
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface MethodCatch {

	/**
	 * 用于 获取方法名称
	* @Title: methodName 
	* @Description: 
	* @param @return    设定文件 
	* @return String    返回类型 
	* @throws
	 */
	public String methodName();
}

aspectbeans.xml:


<beans>
<!-- 服务类配置 -->
	<aspectbean id="aspectCachBean" class="com.tgb.config.AspectCachBean"></aspectbean>
	<aspectbean id="aspectCertifiyBean" class="com.tgb.config.AspectCertifiyBean"></aspectbean>
</beans>

beans.xml:

<beans>
<!-- 业务类 -->
	<bean id="UserDao" class="com.tgb.daoImpl.UserDaoImpl" />
		
	<!-- 是否启用aop -->
	<aop isaop="true"></aop>

<!-- 关系配置 -->
 <!-- aspectMethod="save"  -->
	<aspectbefore  aspectId="aspectCachBean" method="update"  aspectMethod="cacheAfter" ></aspectbefore> 
	<aspectbefore  aspectId="aspectCertifiyBean" method="update"  aspectMethod="certifyAfter" ></aspectbefore> 
</beans>






以上是关于动态代理实现Spring Aop的主要内容,如果未能解决你的问题,请参考以下文章

Spring总结七:AOP动态代理的实现

详解 spring AOP 动态代理

Spring之AOP

JavaEE--Spring_AOP

Spring AOP详解

用spring aop实现动态代理的例子