自定义Aspect风格的AOP框架
本篇博客参考《架构探险--从零开始写java web框架》4.3章节
1代理接口:
package smart.myaop.framework;
public interface Proxy {
/**
* 执行链式调用
*/
Object doProxy(ProxyChain proxyChain) throws Throwable;
}
2代理链(责任链模式,同一个对象可以被多个Proxy层层代理):
package smart.myaop.framework;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
/**
* 代理链
*/
public class ProxyChain {
private final Class<?> targetClass; //目标类
private final Object targetObject; //目标对象
private final Method targetMethod; //目标方法
private final MethodProxy methodProxy; //方法代理,cglib提供的方法代理对象
private final Object[] methodParams; //方法参数
private List<Proxy> proxyList = new ArrayList<>(); //代理列表
private int proxyIndex = 0; //代理索引
public ProxyChain(Class<?> targetClass, Object targetObject, Method targetMethod, MethodProxy methodProxy, Object[] methodParams, List<Proxy> proxyList) {
this.targetClass = targetClass;
this.targetObject = targetObject;
this.targetMethod = targetMethod;
this.methodProxy = methodProxy;
this.methodParams = methodParams;
this.proxyList = proxyList;
}
public Class<?> getTargetClass() {
return targetClass;
}
public Method getTargetMethod() {
return targetMethod;
}
public Object[] getMethodParams() {
return methodParams;
}
/**
* 在Proxy接口的实现中提供相应横切逻辑并调用doProxyChain方法
* methodProxy的invokeSuper方法执行目标对象的业务逻辑
* @return
* @throws Throwable
*/
public Object doProxyChain() throws Throwable {
Object methodResult;
if(proxyIndex < proxyList.size()) {
methodResult = proxyList.get(proxyIndex++).doProxy(this);
} else {
methodResult = methodProxy.invokeSuper(targetObject, methodParams);
}
return methodResult;
}
}
3创建代理对象的工具类:
package smart.myaop.framework;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
import java.util.List;
/**
* 代理管理器,输入目标类和一组proxy接口实现,创建一个代理对象并输出
* 由切面类来调用ProxyManager创建代理链,切面类在目标方法调用前后进行增强
*
* 在框架里使用ProxyManager创建代理对象并放入ioc容器,然后将代理对象注入到其它对象中
*/
public class ProxyManager {
public static <T> T createProxy(final Class<?> targetClass, final List<Proxy> proxyList) {
return (T) Enhancer.create(targetClass, new MethodInterceptor() {
@Override
public Object intercept(Object targetObject, Method targetMethod, Object[] methodParams, MethodProxy methodProxy) throws Throwable {
return new ProxyChain(targetClass, targetObject, targetMethod, methodProxy, methodParams, proxyList);
}
});
}
}
4Proxy接口的抽象实现,模板方法模式:
package smart.myaop.framework;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.lang.reflect.Method;
/**
* 切面代理
* 该抽象类提供模板方法,由其子类扩展相应的抽象方法
*/
public abstract class AspectProxy implements Proxy {
private static final Logger logger = LoggerFactory.getLogger(AspectProxy.class);
@Override
public Object doProxy(ProxyChain proxyChain) throws Throwable {
Object result = null;
Class<?> cls = proxyChain.getTargetClass();
Method method = proxyChain.getTargetMethod();
Object[] params = proxyChain.getMethodParams();
/**
* 从proxyChain中获取目标类,目标方法和目标参数,通过try ... catch ...finally代码块调用代理框架
*/
begin();
try {
if(intercept(cls, method, params)) {
before(cls, method, params);
result = proxyChain.doProxyChain();
after(cls, method, result);
}
} catch (Exception e) {
logger.error("proxy failure", e);
error(cls, method, params, e);
} finally {
end();
}
return result;
}
/**
* 下面几个都是钩子方法,可在子类中有选择性的实现,可以有选择性的实现,所以不定义成抽象方法
*/
public boolean intercept(Class<?> cls, Method method, Object[] params) {
return true;
}
public void before(Class<?> cls, Method method, Object[] params) {}
public void after(Class<?> cls, Method method, Object result) {}
public void begin() {}
public void end() {}
public void error(Class<?> cls, Method method, Object[] params, Exception e) {}
}
5举例某个具体的Proxy实现:
package smart.myaop;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import smart.myaop.framework.AspectProxy;
import java.lang.reflect.Method;
/**
* 该示例类继承AspectProxy类,指定拦截Controller所有方法,并在方法前后加日志并记录执行时间
*/
@Aspect(Controller.class)
public class ControllerAspect extends AspectProxy {
private static final Logger logger = LoggerFactory.getLogger(ControllerAspect.class);
private long begin;
@Override
public void before(Class<?> cls, Method method, Object[] params) {
logger.debug("----------begin----------");
logger.debug(String.format("class: %s"), cls.getName());
logger.debug(String.format("method: %s"), method.getName());
begin = System.currentTimeMillis();
}
@Override
public void after(Class<?> cls, Method method, Object result) {
logger.debug(String.format("time: %dms"), System.currentTimeMillis() - begin);
logger.debug("----------end----------");
}
}
6自定义注解@Aspect,作为代理标记:
package smart.myaop;
import java.lang.annotation.*;
/**
* 切面注解
*/
@Target(ElementType.TYPE) //只能用在类上
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
/**
* 注解,注解类,用来定义注解
* @return
*/
Class<? extends Annotation> value();
}
7初始化框架并创建代理对象放入ioc容器:
package smart.myaop;
import smart.myaop.framework.AspectProxy;
import smart.myaop.framework.Proxy;
import smart.myaop.framework.ProxyManager;
import java.lang.annotation.Annotation;
import java.util.*;
/**
* 初始化时获取所有目标类和其被拦截的切面类实例,获取AspectProxy抽象类的所有子类和@Aspect注解的所有类
* 调用ProxyManager#createProxy方法创建代理对象放入ioc容器
* AopHelper在框架初始化时调用,要在初始化bean之后,ioc容器初始化之前调用,这样ioc容器做属性注入时候才能拿到相应的代理对象
*/
public class AopHelper {
private static Set<Class<?>> allClassSet = new HashSet<>(); //项目启动时把指定路径下的所有class文件加载为class对象集合,过程略
static {
try {
Map<Class<?>, Set<Class<?>>> proxyMap = createProxyMap();
Map<Class<?>, List<Proxy>> targetMap = createTargetMap(proxyMap);
for (Map.Entry<Class<?>, List<Proxy>> targetEntry : targetMap.entrySet()) {
Class<?> targetClass = targetEntry.getKey();
List<Proxy> proxyList = targetEntry.getValue();
Object proxy = ProxyManager.createProxy(targetClass, proxyList);
//todo 放入targetClass为键,proxy为值放入ioc容器,在ioc容器做属性注入的时候通过class对象拿到的就是代理对象了
}
} catch (Exception e) {
System.out.println("aop failure");
e.printStackTrace();
}
}
/**
* 目标类与代理对象列表之间的映射关系,如一个业务类被多个@Aspect注解修饰的AspectProxy子类代理,这里得到这样的1对n映射关系
* @param proxyMap
* @return
* @throws Exception
*/
private static Map<Class<?>, List<Proxy>> createTargetMap(Map<Class<?>, Set<Class<?>>> proxyMap) throws Exception {
Map<Class<?>, List<Proxy>> targetMap = new HashMap<>();
for (Map.Entry<Class<?>, Set<Class<?>>> proxyEntry : proxyMap.entrySet()) {
Class<?> proxyClass = proxyEntry.getKey();
Set<Class<?>> targetClassSet = proxyEntry.getValue();
for (Class<?> targetClass : targetClassSet) {
Proxy proxy = (Proxy) proxyClass.newInstance();
if(targetMap.containsKey(targetClass)) {
targetMap.get(targetClass).add(proxy);
} else {
List<Proxy> proxyList = new ArrayList<>();
proxyList.add(proxy);
targetMap.put(targetClass, proxyList);
}
}
}
return targetMap;
}
/**
* 给createTargetMap方法用
* 代理类(切面类)与目标类集合之间的一对多映射关系
* 在全部class对象集合中搜索满足1是AspectProxy子类,2被@Aspect注解,这样的类(代理类),根据@Aspect注解指定的注解属性去获取该注解对应的目标类集合
* 然后建立代理类与目标类集合之间的映射关系,据此分析出目标类与代理对象列表之间的映射关系
* @return
* @throws Exception
*/
private static Map<Class<?>, Set<Class<?>>> createProxyMap() throws Exception {
Map<Class<?>, Set<Class<?>>> proxyMap = new HashMap<>();
Set<Class<?>> proxyClassSet = new HashSet<>();
for (Class<?> aClass : allClassSet) {
if(AspectProxy.class.isAssignableFrom(aClass) && !AspectProxy.class.equals(aClass)) {
proxyClassSet.add(aClass); //获取AspectProxy子类class对象集合
}
}
for (Class<?> aClass : proxyClassSet) {
if(aClass.isAnnotationPresent(Aspect.class)) {
Aspect aspect = aClass.getAnnotation(Aspect.class);
Set<Class<?>> targetClassSet = createTargetClassSet(aspect);
proxyMap.put(aClass, targetClassSet);
}
}
return proxyMap;
}
/**
* 给createProxyMap方法用
* 获取被指定aspect注解的所有类对象
* @param aspect
* @return
* @throws Exception
*/
private static Set<Class<?>> createTargetClassSet(Aspect aspect) throws Exception {
Set<Class<?>> targetClassSet = new HashSet<>();
Class<? extends Annotation> annotation = aspect.value();
if(annotation != null && annotation.equals((Aspect.class))) {
for (Class<?> aClass : allClassSet) {
if(aClass.isAnnotationPresent(annotation)) {
targetClassSet.add(aClass); //这里从所有的class集合中挑出被annotation类型注解的class对象集合
}
}
}
return targetClassSet;
}
}
注意:代码从1写到7,从7到1理解有助于了解整体工作流程,整个用了责任链模式、模板方法模式,CGLIB动态代理。书中叫Proxy和ProxyChain叫做代理和代理链,改叫增强和增强链更容易理解,每一个Proxy就是对目标类的方法的一次功能增强。
使用举例:
可以提供一个@Login注解,可以用在方法或类上,然后实现一个AuthzAnnotationAspect继承AspectProxy,用@Aspect(Controller.class)注解,在before方法中判断目标方法或目标类是否被@Login注解了,如果是,使用Shiro的代码判断用户是否登录:
PrincipalCollection principals = SecurityUtils.getSubject().getPrincipals();
if(principals == null || principals.isEmpty()) {
throw new Exception(“当前用户未登录!”);
}
自定义Aspect风格的AOP框架的更多相关文章
- 带你学习AOP框架之Aspect.Core[1]
在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的 ...
- Android平台免Root无侵入AOP框架Dexposed使用详解
Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...
- Android免Root无侵入AOP框架Dexposed
Dexposed框架是阿里巴巴无线事业部近期开源的一款在Android平台下的免Root无侵入运行期AOP框架,该框架基于AOP思想,支持经典的AOP使用场景,可应用于日志记录,性能统计,安全控制,事 ...
- JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架
1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...
- 仿写一个简陋的 IOC/AOP 框架 mini-spring
讲道理,感觉自己有点菜.Spring 源码看不懂,不想强行解释,等多积累些项目经验之后再看吧,但是 Spring 中的控制反转(IOC)和面向切面编程(AOP)思想很重要,为了更好的使用 Spring ...
- 动手造轮子:实现一个简单的 AOP 框架
动手造轮子:实现一个简单的 AOP 框架 Intro 最近实现了一个 AOP 框架 -- FluentAspects,API 基本稳定了,写篇文章分享一下这个 AOP 框架的设计. 整体设计 概览 I ...
- Spring 08: AOP面向切面编程 + 手写AOP框架
核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...
- 设计 REST 风格的 MVC 框架
http://www.ibm.com/developerworks/cn/java/j-lo-restmvc/ 传统的 JavaEE MVC 框架如 Struts 等都是基于 Action 设计的后缀 ...
- Dora.Interception, 一个为.NET Core度身打造的AOP框架[3]:Interceptor的注册
在<不一样的Interceptor>中我们着重介绍了Dora.Interception中最为核心的对象Interceptor,以及定义Interceptor类型的一些约定.由于Interc ...
随机推荐
- 【问题解决方案】GitHub上克隆项目到本地
说明: 克隆线上项目到本地,可以直接clone克隆,不必再自己建文件夹啊,初始化啊. 命令: git clone + 想要克隆的项目的地址 END
- Thymeleaf简介
Thymeleaf Thymeleaf简介 Thymeleaf是一个流行的模板引擎,该模板引擎采用Java语言开发,模板引擎是一个技术名词,是跨领域跨平台的概念,在Java语言体系下有模板引擎,在C# ...
- Tensorflow学习笔记3:卷积神经网络实现手写字符识别
# -*- coding:utf-8 -*- import tensorflow as tf from tensorflow.examples.tutorials.mnist import input ...
- hdu 4655 Cut Pieces(想法题)
Cut Pieces Time Limit: 4000/2000 MS (Java/Others) Memory Limit: 131072/65536 K (Java/Others) Tota ...
- 2018-09-10-weekly
Algorithm 删除链表的倒数第N个节点 What:给定一个链表,删除链表的倒数第 n 个节点,并且返回链表的头结点. How:这是一道典型的利用双指针法解题.首先让指针first指向头节点,然后 ...
- php 调用远程url
// ; Whether to allow the treatment of URLs (like http:// or ftp://) as files. // ; http://php.net/a ...
- 进程间的mutex
设两个进程共用一个临界资源的互斥信号量mutex=1,当mutex=-1时表示(). 一个进程进入了临界区,另一个进程等待 没有一个进程进入临界区 两个进程都进入临界区 两个进程都在等待 互斥信号量不 ...
- redis不重启之rdb数据切换到aof数据
温馨提示: 要实现不重启从rdb切换到aof,redis的版本必须要在2.2以上. [root@pyyuc /data ::]#redis-server -v Redis server v= sha= ...
- pip安装包出现timeout的解决办法
今天安装django时老是出现timeout WARNING: Retrying (Retry(total=4, connect=None, read=None, redirect=None, sta ...
- Callable使用示例
之前工作中也有使用过Callable,但是却没有使用Thread对象去操作过,今晚 小组培训,涨知识了,现特意记录一下,以免忘记. 先看一下Thread的构造器 可以看到,Thread类并没有提供参数 ...