前面提到过动态代理的有两种简单的实现方式,就是JDK动态代理和CGLIB。
不懂先看前文:SpringAOP的实现之代理模式
自己写嘛当然是乞丐版了,为了实现简单 但是又具有一定的通用性(拒绝对被代理类有实现接口的强硬要求)。所以采用CGLIB的方式实现AOP。
工程目录结构。
首先要实现注解一样的 AOP,要定义与AOP相关的注解。
定义2个AOP相关的注解标签。
//作用在类上
@Target(ElementType.TYPE)
//运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Aspect {
//初期版本仅仅支持按照注解分类AOP
//比如只切Controller Controller.class
Class<? extends Annotation> value();
}
如果对一个代理类有多个AOP,那么需要用户规定代理执行的顺序,那么就需要Order注解。
/**
* 定义切面顺序
*/
//作用在类上
@Target(ElementType.TYPE)
//运行时有效
@Retention(RetentionPolicy.RUNTIME)
public @interface Order {
//越小优先级别越高
int value();
}
定义好相关注解,需要定义AOP的基本骨架。这里用到了类似于模板模式。 这个骨架定义了究竟支持哪几种通知方式(前置,后置等等)。
模板模式的妙用:Spring源码(3)-Spring中的ApplicationContext
通过此骨架的定义,可以让子类按需实现自己想要实现的通知。
public abstract class DefaultAspect {
/**
* @param targetClass 被代理的目标类型
* @param method 被代理的方法
* @param args 方法的参数
* @throws Throwable
*/
public void before(Class<?> targetClass, Method method,Object[] args) throws Throwable {}
/**
*
* @param targetClass 被代理的目标类型
* @param method 被代理的方法
* @param args 方法的参数
* @param returnValue 返回值
* @return 修改后返回值
* @throws Throwable
*/
public Object afterReturning(Class<?> targetClass,Method method
,Object[] args,Object returnValue) throws Throwable{
return returnValue;
}
/**
*
* @param targetClass 被代理的目标类型
* @param method 被代理的方法
* @param args 方法的参数
* @param e 异常
* @throws Throwable 抛出被代理方法的 异常
*/
public void afterThrowing(Class<?> targetClass,
Method method,Object[] args,Throwable e) throws Throwable{}
}
只支持了前置通知,方法返回后通知,异常时通知。Aroud可以将这三个联合起来就行。
因为三个方法在抽象类里面都是默认的空实现,不会让用户强制实现,可以按需实现。
在SpringAOP的实现之代理模式提到,使用CGLIB就需要实现MethodInterceptor去复用自己的增强逻辑,而在这里也是一样的。但是又有点不一样,因为我们可能(一定)会遇到一个被代理类有多个代理,所以这里是一对多的关系。因此不能照搬原来的代码。需要做出额外的操作来辅助完成多个AOP增强一个被代理类。
所以这里需要声明两个属性。
//被代理的class
private Class<?> targetClass;
//切面信息集合 照顾多个AOP的 情况
private List<AspectInfo> sortedAspectInfoList;
而且,多个AOP的执行顺序是类似于同心圆的穿梭操作。所以多个before和多个after执行的顺序不尽相同。需要做额外的工作。
上面出现的切面信息类应该定义如下。
package com.framework.aop;
/**
* @Desc
* @Author FuYouJ
* @date 2020/5/31 21:55
*/
import com.framework.aop.aspect.DefaultAspect;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 接受注解的值
*/
@Data
public class AspectInfo {
private int orderIndex;
private DefaultAspect aspectObject;
public AspectInfo() {
}
public AspectInfo(int orderIndex, DefaultAspect aspectObject) {
this.orderIndex = orderIndex;
this.aspectObject = aspectObject;
}
}
创建一个AspectListExecutor类继承于MethodInterceptor,该类主要的工作就是往被代理的对象添加横切逻辑。
@Data
@NoArgsConstructor
public class AspectListExecutor implements MethodInterceptor {
//被代理的class
private Class<?> targetClass;
// targetCalss切面信息集合 照顾多个AOP的 情况
private List<AspectInfo> sortedAspectInfoList;
public AspectListExecutor(Class<?> targetClass,List<AspectInfo> aspectInfos){
this.targetClass = targetClass;
//直接先排序再赋值
this.sortedAspectInfoList = sort(aspectInfos);
}
//因为需要按照order的顺序执行aop,所以需要对 List排序。
private List<AspectInfo> sort(List<AspectInfo> aspectInfos) {
//升序排列
Collections.sort(aspectInfos, new Comparator<AspectInfo>() {
@Override
public int compare(AspectInfo o1, AspectInfo o2) {
return o1.getOrderIndex() - o2.getOrderIndex();
}
});
return aspectInfos;
}
/**
* 按照oder的顺序升序执行
* @param method
* @param args
*/
private void invokeBeforeAdvices(Method method, Object[] args) throws Throwable {
for (AspectInfo info : sortedAspectInfoList) {
info.getAspectObject().before(targetClass,method,args);
}
}
/**
* 发生异常的时候执行 降序
* @param method
* @param args
* @param e
*/
private void invokeAfterThrowingAdvices(Method method, Object[] args, Exception e) throws Throwable {
for (int i = sortedAspectInfoList.size() -1; i >=0 ; i--) {
sortedAspectInfoList.get(i).getAspectObject()
.afterThrowing(targetClass,method,args,e);
}
}
/**
* 如果代理方法正常返回,降序执行afterAdvice
* @param method
* @param args
* @param returnValue
* @return
*/
private Object invokeAfterReturningAdvices(Method method, Object[] args, Object returnValue) throws Throwable {
Object res = null;
for (int i = sortedAspectInfoList.size()-1; i >=0 ; i--) {
res = sortedAspectInfoList.get(i).getAspectObject().afterReturning(targetClass, method, args, returnValue);
}
return res;
}
//织入逻辑的方法
/**
*
* @param o 被增强的对象
* @param method 需要拦截的方法
* @param args 方法参数
* @param methodProxy 代理方法
* @return 返回值
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
Object returnValue = null;
if (ValidationUtil.isEmpty(sortedAspectInfoList)){
//就算为空也要执行原来的方法啊
return methodProxy.invokeSuper(o,args);
}
//1.按照order的顺序执行完毕所有切面的before方法
invokeBeforeAdvices(method,args);
try {
//2。 执行被代理的方法
returnValue = methodProxy.invokeSuper(o,args);
//3. 如果被代理方法正常返回,按照order的顺序 逆序执行afterReturning
returnValue = invokeAfterReturningAdvices(method,args,returnValue);
}catch (Exception e){
//执行异常时
invokeAfterThrowingAdvices(method,args,e);
}
return returnValue;
}
有了一个能往被代理对象里面织入逻辑的武器,还需要使用这个武器的使用者才可以。在CGLIB已经有这样的使用者。就是Enhancer,它可以根据MethodInterceptor和目标类生成代理对象。
创建一个工具类,利用Enhancer创建一个代理现象。
public class ProxyCreator {
/**
*创建代理 对象并返回
* @param targetClass
* @param interceptor
* @return
*/
public static Object createProxy(Class<?> targetClass, MethodInterceptor interceptor){
Object o = Enhancer.create(targetClass, interceptor);
return o;
}
}
有了以上工作,下面只需要从容器中筛选所有对象,用代理对象替换容器里面的被代理对象。 容器的实现
这里不赘述。后面我会新开文章专门写实现容器。这里 直接贴出 容器的实现代码 。
容器必须是单例,单例模式我以前写过一个不是很好的文章,有心的可以看看帮助理解。
JAVA中N种单例模式简单概括(防反射,克隆,序列化,实例化,多线程安全)
容器实现代码
/**
* 容器类 单例模式 (线程安全,防止反射 ,序列化)
*/
@NoArgsConstructor(access = AccessLevel.PRIVATE)
@Slf4j
public class BeanContainer {
//判断实例是否已经被加载过
private boolean loaded = false;
private final ConcurrentHashMap<Class<?>,Object> beanMap = new ConcurrentHashMap<>();
/**
* 加载 bean的注解列表
* 当一个类被一下几个注解修饰的时候,那么就归容器管理
*/
private static final List<Class<? extends Annotation>> BEAN_ANNOTATION
= Arrays.asList(Component.class, Controller.class
,Service.class, Repository.class
,Aspect.class);
/**
* 获取容器实例
* @return
*/
public static BeanContainer getInstance(){
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder{
//一个实例
HOLDER;
//一个成员
private BeanContainer instance;
private ContainerHolder(){
instance = new BeanContainer();
}
}
/**
* 扫描加载所有的bean
*避免 多线程同时加载
*/
public synchronized void loadBeans(String packageName){
if (isLoaded()){
log.warn("容器已经被加载过了");
return;
}
Set<Class<?>> classSet = ClassUtil.extractPackageClass(packageName);
if (ValidationUtil.isEmpty(classSet)) {
log.warn("没有提取到任何的类"+packageName);
return;
}
for (Class<?> clazz : classSet) {
for (Class<? extends Annotation> annotation : BEAN_ANNOTATION) {
//如果有这个注解
if (clazz.isAnnotationPresent(annotation)) {
//将m目标类本省作为建 ,目标类的实例作为 值,放在容器中
beanMap.put(clazz,ClassUtil.newInstance(clazz,true));
}
}
}
loaded = true;
}
public boolean isLoaded() {
return loaded;
}
public void setLoaded(boolean loaded) {
this.loaded = loaded;
}
public int size(){
return beanMap.size();
}
//增加一个实例
public Object addBean(Class<?>clazz, Object bean){
return beanMap.put(clazz,bean);
}
//删除
public Object remove(Class<?>clazz){
return beanMap.remove(clazz);
}
/**
* 类获取实例
* @param clazz
* @return
*/
public Object getBean(Class<?>clazz){
return beanMap.get(clazz);
}
/**
* 获取所有的 beanclss集合
* @return
*/
public Set<Class<?>> getClasses(){
return beanMap.keySet();
}
/**
* 获取所有实例
* @return
*/
public Set<Object> getBeans(){
return new HashSet<>(beanMap.values());
}
/**
* 获取某注解 的class
* @param annotation
* @return
*/
public Set<Class<?>> getClassesByAnnotation(Class<? extends Annotation> annotation){
//获取所有class
Set<Class<?>> keySet = getClasses();
if (ValidationUtil.isEmpty(keySet)){
log.warn("容器为空");
return null;
}
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> clazz : keySet) {
if (clazz.isAnnotationPresent(annotation)){
classSet.add(clazz);
}
}
return classSet.size() > 0?classSet:null;
}
public Set<Class<?>> getClassesBySuper(Class<?> interfaceOrClass){
//获取所有class
Set<Class<?>> keySet = getClasses();
if (ValidationUtil.isEmpty(keySet)){
log.warn("容器为空");
return null;
}
//通过接口筛选
Set<Class<?>> classSet = new HashSet<>();
for (Class<?> clazz : keySet) {
//判断是否是我的子类
if (interfaceOrClass.isAssignableFrom(clazz)){
classSet.add(clazz);
//TODO
}
}
return classSet.size()>0?classSet:null;
}
}
依赖注入代码
/**
* 依赖注入的服务
*/
@Slf4j
public class DependencyInjector {
private BeanContainer beanContainer;
public DependencyInjector(){
beanContainer = BeanContainer.getInstance();
}
/**
* 执行IOC
*/
public void doIoc(){
//遍历容器class对象
if (ValidationUtil.isEmpty(beanContainer.getClasses())){
log.warn("容器为空");
return;
}
for (Class<?> clazz : beanContainer.getClasses()) {
//遍历每个对象的 成员变量
Field[] fields = clazz.getDeclaredFields();
if (ValidationUtil.isEmpty(fields)){
continue;
}
//找到自动注入的注解
for (Field field : fields) {
if (field.isAnnotationPresent(Autowired.class)) {
Autowired autowired = field.getAnnotation(Autowired.class);
String autowiredValue = autowired.value();
//获取类型
Class<?> fieldClass = field.getType();
//再容器中找到
Object fieldValue = getFiledInstance(fieldClass,autowiredValue);
if (fieldValue == null){
throw new RuntimeException("注入失败,容器中不存在该类型,名字:"+autowiredValue);
}else {
//注入
Object targetBean = beanContainer.getBean(clazz);
//通过反射注入
ClassUtil.setField(field,targetBean,fieldValue,true);
}
}
}
}
}
/**
* 根据class对象 获取容器管理的类或者实现类
* @param fieldClass
* @param autowired
* @return
*/
private Object getFiledInstance(Class<?> fieldClass, String autowired) {
Object fieldValue = beanContainer.getBean(fieldClass);
//入如果直接获取到了就返回
if (fieldValue != null) {
return fieldValue;
}else {
//找实现类
Class<?> implClass = getImplementClass(fieldClass,autowired);
if (implClass != null){
return beanContainer.getBean(implClass);
}else {
return null;
}
}
}
/**
* 获取接口的实现类
* @param fieldClass
* @param autowired
* @return
*/
private Class<?> getImplementClass(Class<?> fieldClass, String autowired) {
Set<Class<?>> classsSet = beanContainer.getClassesBySuper(fieldClass);
if (ValidationUtil.isEmpty(classsSet) == false){
//如果有多个实现类呢? 通过 Qualifier 设置名称
if (ValidationUtil.isEmpty(autowired)){
//说明没有精确指定
if (classsSet.size() == 1){
return classsSet.iterator().next();
}else {
//抛出异常
throw new RuntimeException("发现多个继承类!却又没有指定名字"+fieldClass.getName());
}
}else {
for (Class<?> clazz : classsSet) {
//如果类的类名等于指定的名字
if (autowired.equals(clazz.getSimpleName())){
return clazz;
}
}
}
}
return null;
}
}
需要从容器里面筛选出符合要被代理的对象,对其替换。
public class AspectWeaver {
private BeanContainer beanContainer;
//获取容器的单例
public AspectWeaver(){
beanContainer = BeanContainer.getInstance();
}
//AOP主要方法
public void doAop(){
//获取所有的切面类
//1.获取所有的切面类
Set<Class<?>> aspectSet = beanContainer.getClassesByAnnotation(Aspect.class);
//没有需要代理的类,也就是没有切面类。直接返回 此方法是非空验证,可自行实现。
if (ValidationUtil.isEmpty(aspectSet)){ return; }
//2.将切面类按照不同的织入目标进行切分 比如切Controller的在一边,切Service的 在一边。用Map保存 比如 controller 有多个切面类要去增强他。所以用List保存
Map<Class<? extends Annotation>, List<AspectInfo>> categorizedMap = new HashMap<>();
//遍历 所有的切面类
for (Class<?> aspectClass : aspectSet) {
//对切面类进行校验,校验切面的一般规范。比如 不能切自己(死循环)
boolean b = verifyAspect(aspectClass);
if (b){
//对于校验通过的根据要切的目标去归类保存
//比如Service的 切面增强 有一个列表
//比如Controller 切面增强 有一个列表
//列表是因为 往往 不止一个切面类要去增强某被代理类
categorizeAspect(categorizedMap,aspectClass);
}else {
//对于不符合切面规范的,抛出异常
throw new RuntimeException("当前切面类必须持有@Aspect 和 @Order 和继承自DefaultAspect");
}
}
//Map都是空的 也没啥做的 直接返回 无事可做
if (ValidationUtil.isEmpty(categorizedMap)){return;}
//注意注意 这里map百年里的是KEY 假设 整个应用程序所有的切面只对service和controller进行增强,所以此时的key就是service和controller
//然后根据key获取符合条件的切面类列表,全部进行织入。
for (Class<? extends Annotation> aClass : categorizedMap.keySet()) {
//执行织入 代理对象替换了容器的被代理对象
//这样字从 容器取得对象就是已经增强后的对象了
List<AspectInfo> aspectInfoList = categorizedMap.get(aClass);
//对controller 和service织入
weaveByCategory(aClass, aspectInfoList);
}
}
/**
* 验证切面 类是否满足要求
* 必须要有两个切面标签
* 必须继承自DefaultAspect.class 必须按照这个骨架执行啊
* Aspect的属性不能是他本身
* @param aspectClass
* @return
*/
private boolean verifyAspect(Class<?> aspectClass) {
return aspectClass.isAnnotationPresent(Aspect.class)
&& aspectClass.isAnnotationPresent(Order.class)
&& DefaultAspect.class.isAssignableFrom(aspectClass)
&& aspectClass.getAnnotation(Aspect.class).value() != Aspect.class;
}
/**
*将切面类 按照不同的切面目标进行分类
* @param categorizedMap
* @param aspectClass
*/
private void categorizeAspect(Map<Class<? extends Annotation>, List<AspectInfo>> categorizedMap, Class<?> aspectClass) {
Order orderTag = aspectClass.getAnnotation(Order.class);
Aspect aspectTag = aspectClass.getAnnotation(Aspect.class);
DefaultAspect aspect = (DefaultAspect) beanContainer.getBean(aspectClass);
AspectInfo aspectInfo = new AspectInfo(orderTag.value(),aspect);
if (categorizedMap.containsKey(aspectTag.value()) == false) {
//如果第一次出现
List<AspectInfo> aspectInfoList = new ArrayList<>();
aspectInfoList.add(aspectInfo);
categorizedMap.put(aspectTag.value(),aspectInfoList);
}else {
//已有列表
categorizedMap.get(aspectTag.value())
.add(aspectInfo);
}
}
/**
*
* @param aspectAnnotationClass 要被切的注解所对应的类 比如@service的xxxxService
* @param aspectInfos 他所对应的集合
*/
private void weaveByCategory(Class<? extends Annotation> aspectAnnotationClass, List<AspectInfo> aspectInfos) {
//获取被例如 @service标记的所有类
Set<Class<?>> aspectClassSet = beanContainer.getClassesByAnnotation(aspectAnnotationClass);
if (ValidationUtil.isEmpty(aspectClassSet)){return;}
//遍历 为每个被代理的类生成动态代理对象
for (Class<?> targetClass : aspectClassSet) {
AspectListExecutor aspectListExecutor = new AspectListExecutor(targetClass,aspectInfos);
Object proxyBean = ProxyCreator.createProxy(targetClass, aspectListExecutor);
//将代理对象放到容器 替换被代理的实例
beanContainer.addBean(targetClass,proxyBean);
}
}
}
以上就是一个简单版本的根据注解来分类切面的实现。
编写一个测试的controller,具有返回值。
@Controller
public class TestAopController {
public int say(){
System.out.println("我是controller的本地方法");
return 1;
}
}
为他编写两个AOP类。顺序分别是0和1
package com.fuyouj.aspect.test;
import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* @Desc
* @Author FuYouJ
* @date 2020/6/2 0:40
*/
@Aspect(value = Controller.class)
@Order(0)
@Slf4j
public class TestAopOne extends DefaultAspect {
@Override
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
log.info("我是切面逻辑before order0,我增强的目标类是{},我增强的目标方法是,方法参数{}"
,targetClass.getSimpleName(),method.getName(),args);
}
@Override
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
Integer res = (Integer) returnValue;
log.info("我是切面逻辑after order0,我增强的目标类是{},我增强的目标方法是,方法参数{}"
,targetClass.getSimpleName(),method.getName(),args);
res++;
return res;
}
}
package com.fuyouj.aspect.test;
import com.framework.aop.annotation.Aspect;
import com.framework.aop.annotation.Order;
import com.framework.aop.aspect.DefaultAspect;
import com.framework.core.annotation.Controller;
import lombok.extern.slf4j.Slf4j;
import java.lang.reflect.Method;
/**
* @Desc
* @Author FuYouJ
* @date 2020/6/2 0:40
*/
@Aspect(value = Controller.class)
@Order(1)
@Slf4j
public class TestAopTwo extends DefaultAspect {
@Override
public void before(Class<?> targetClass, Method method, Object[] args) throws Throwable {
log.info("我是切面逻辑before order1,我增强的目标类是{},我增强的目标方法是{},方法参数{}"
,targetClass.getSimpleName(),method.getName(),args);
}
@Override
public Object afterReturning(Class<?> targetClass, Method method, Object[] args, Object returnValue) throws Throwable {
Integer res = (Integer) returnValue;
log.info("我是切面逻辑after order1,我增强的目标类是{},我增强的目标方法是{},方法参数{}"
,targetClass.getSimpleName(),method.getName(),args);
res++;
return res;
}
}
编写测试类
public class TestAop {
@Test
public void testAopOne(){
BeanContainer beanContainer = BeanContainer.getInstance();
//加载了所有类
beanContainer.loadBeans("com.fuyouj");
//AOP
new AspectWeaver().doAop();
// ioc
//这里的细节 先AOP,再自动注入。
new DependencyInjector().doIoc();
TestAopController controller = (TestAopController) beanContainer.getBean(TestAopController.class);
int say = controller.say();
System.out.println(say+"======");
}
}
按照逻辑,俩个切面类的顺序分别是0和1,所以 执行顺序因该是
0的beore,1的before,1的after,0的after.并且原来额返回结果经过两次修改,应该是2。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface Controller {
}
后序预告
AOP2.0: 支持execution表达式
自己手写一个Spring框架