本篇博客参考《架构探险--从零开始写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框架的更多相关文章

  1. 带你学习AOP框架之Aspect.Core[1]

    在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是OOP的延续,是软件开发中的 ...

  2. Android平台免Root无侵入AOP框架Dexposed使用详解

    Dexposed是基于久负盛名的开源Xposed框架实现的一个Android平台上功能强大的无侵入式运行时AOP框架. Dexposed的AOP实现是完全非侵入式的,没有使用任何注解处理器,编织器或者 ...

  3. Android免Root无侵入AOP框架Dexposed

    Dexposed框架是阿里巴巴无线事业部近期开源的一款在Android平台下的免Root无侵入运行期AOP框架,该框架基于AOP思想,支持经典的AOP使用场景,可应用于日志记录,性能统计,安全控制,事 ...

  4. JAVA基础加强(张孝祥)_类加载器、分析代理类的作用与原理及AOP概念、分析JVM动态生成的类、实现类似Spring的可配置的AOP框架

    1.类加载器 ·简要介绍什么是类加载器,和类加载器的作用 ·Java虚拟机中可以安装多个类加载器,系统默认三个主要类加载器,每个类负责加载特定位置的类:BootStrap,ExtClassLoader ...

  5. 仿写一个简陋的 IOC/AOP 框架 mini-spring

    讲道理,感觉自己有点菜.Spring 源码看不懂,不想强行解释,等多积累些项目经验之后再看吧,但是 Spring 中的控制反转(IOC)和面向切面编程(AOP)思想很重要,为了更好的使用 Spring ...

  6. 动手造轮子:实现一个简单的 AOP 框架

    动手造轮子:实现一个简单的 AOP 框架 Intro 最近实现了一个 AOP 框架 -- FluentAspects,API 基本稳定了,写篇文章分享一下这个 AOP 框架的设计. 整体设计 概览 I ...

  7. Spring 08: AOP面向切面编程 + 手写AOP框架

    核心解读 AOP:Aspect Oriented Programming,面向切面编程 核心1:将公共的,通用的,重复的代码单独开发,在需要时反织回去 核心2:面向接口编程,即设置接口类型的变量,传入 ...

  8. 设计 REST 风格的 MVC 框架

    http://www.ibm.com/developerworks/cn/java/j-lo-restmvc/ 传统的 JavaEE MVC 框架如 Struts 等都是基于 Action 设计的后缀 ...

  9. Dora.Interception, 一个为.NET Core度身打造的AOP框架[3]:Interceptor的注册

    在<不一样的Interceptor>中我们着重介绍了Dora.Interception中最为核心的对象Interceptor,以及定义Interceptor类型的一些约定.由于Interc ...

随机推荐

  1. NodeJS、npm安装步骤和配置(windows版本)

    https://jingyan.baidu.com/article/48b37f8dd141b41a646488bc.html 上面这个链接很详细了,怕它没了自己记一遍.我的简洁一点. 1. 打开no ...

  2. Python Paramiko模块使用

    1 执行远程命令 #!/usr/bin/python import paramiko ssh = paramiko.SSHClient() ssh.set_missing_host_key_polic ...

  3. 手机端 css 样式重置

    @charset "utf-8"; body, div, ul, li, ol, h1, h2, h3, h4, h5, h6, input, textarea, select, ...

  4. highlight语法高亮推荐样式

    最近在弄一个类似博客的东西,需要高亮显示代码,所以用了highlight.js来完成 highlight提供了不同的风格我们可以通过更改css样式表来找到适合我们的. 我罗列一部分看看有哪些风格 以下 ...

  5. SQL中exists和in的区别

  6. 【串线篇】spring boot日志框架

    一.日志框架 小张:开发一个大型系统: 1.System.out.println(""):将关键数据打印在控制台:去掉?写在一个文件? 2.框架来记录系统的一些运行时信息:日志框架 ...

  7. mysql order by 自定义

    TIMESTAMPDIFF 语法: TIMESTAMPDIFF(interval,datetime_expr1,datetime_expr2). 说明: 返回日期或日期时间表达式datetime_ex ...

  8. 如何在mac上使用iMessage发送信息

    在Mac上你也可以像iPhone上一样使用iMessage 来发送iMessage 与 普通的短信息. 并且你需要在iPhone上设置中的信息的信息转发中激活对电脑的支持.此时, 你的电脑也可以向你的 ...

  9. Telegraf根据配置文件启动(Influxdb的数据收集)

    1.创建一个telegraf.config文件 telegraf -sample-config -input-filter cpu:disk:diskio:net:system:mem -output ...

  10. LDD3 第15章 内存映射和DMA

    本章内容分为三个部分: 第一部分讲述了mmap系统调用的实现过程.将设备内存直接映射到用户进程的地址空间,尽管不是所有设备都需要,但是能显著的提高设备性能. 如何跨越边界直接访问用户空间的内存页,一些 ...