[适合初中级Java程序员修炼手册从0搭建整个Web项目](五)(下)

简介: 前言“文本已收录至我的GitHub仓库,欢迎Star:https://github.com/bin392328206种一棵树最好的时间是十年前,其次是现在


AopProxy


新建创建代理的顶层接口AopProxy

package com.xiaoliuliu.six.finger.web.spring.aop;
/**
 * @author 小六六
 * @version 1.0
 * @date 2020/10/26 17:41
 */
public interface AopProxy {
    Object getProxy();
    Object getProxy(ClassLoader classLoader);
}
复制代码


Spring选择代理创建逻辑是,如果被代理的类有实现接口用原生JDK的动态代理,否则使用Cglib的动态代理。所以AopProxy有两个子类JdkDynamicAopProxy和CglibAopProxy来实现这两种创建逻辑。

JdkDynamicAopProxy是利用JDK动态代理来创建代理的,因此需实现InvocationHandler接口。

package com.xiaoliuliu.six.finger.web.spring.aop;
import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInvocation;
import com.xiaoliuliu.six.finger.web.spring.aop.support.AdvisedSupport;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.List;
/**
 * @author 小六六
 * @version 1.0
 * @date 2020/10/26 17:42
 */
public class JdkDynamicAopProxy implements AopProxy, InvocationHandler {
    private AdvisedSupport advised;
    public JdkDynamicAopProxy(AdvisedSupport config) {
        this.advised = config;
    }
    @Override
    public Object getProxy() {
        return getProxy(this.advised.getTargetClass().getClassLoader());
    }
    @Override
    public Object getProxy(ClassLoader classLoader) {
        //JDK的动态代理方式
        return Proxy.newProxyInstance(classLoader, this.advised.getTargetClass().getInterfaces(), this);
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //获取拦截器链
        List<Object> interceptorsAndDynamicMethodMatchers =
                this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
        //外层拦截器,用于控制拦截器链的执行
        MethodInvocation invocation = new MethodInvocation(
                proxy,
                this.advised.getTarget(),
                method,
                args,
                this.advised.getTargetClass(),
                interceptorsAndDynamicMethodMatchers
        );
        //开始连接器链的调用
        return invocation.proceed();
    }
}
复制代码


还又我们的cglib也是经常用的

package com.xiaoliuliu.six.finger.web.spring.aop;
import com.xiaoliuliu.six.finger.web.spring.aop.intercept.MethodInvocation;
import com.xiaoliuliu.six.finger.web.spring.aop.support.AdvisedSupport;
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;
/**
 * @author 小六六
 * @version 1.0
 * @date 2020/10/12 11:03
 * cglib的动态代理
 */
public class CglibAopProxy implements AopProxy, MethodInterceptor {
    private AdvisedSupport advised;
    public CglibAopProxy(AdvisedSupport config) {
        this.advised = config;
    }
    @Override
    public Object getProxy() {
        return getProxy(this.advised.getTargetClass().getClassLoader());
    }
    @Override
    public Object getProxy(ClassLoader classLoader) {
        //cglib的方式
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(this.advised.getTargetClass());
        enhancer.setCallback(this);
        return  enhancer.create();
    }
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        //获取拦截器链
        List<Object> interceptorsAndDynamicMethodMatchers =
                this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, this.advised.getTargetClass());
        //外层拦截器,用于控制拦截器链的执行
        MethodInvocation invocation = new MethodInvocation(
                proxy,
                this.advised.getTarget(),
                method,
                args,
                this.advised.getTargetClass(),
                interceptorsAndDynamicMethodMatchers
        );
        //开始连接器链的调用
        return invocation.proceed();
    }
}
复制代码


成员变量AdvisedSupport封装了创建代理所需要的一切资源,从上面的代码就可以看出它至少封装了被代理的目标实例、拦截器链等,实际上它还负责解析AOP配置和创建拦截器。

package com.xiaoliuliu.six.finger.web.spring.aop.support;
import com.xiaoliuliu.six.finger.web.spring.aop.aspect.AfterReturningAdviceInterceptor;
import com.xiaoliuliu.six.finger.web.spring.aop.aspect.AfterThrowingAdviceInterceptor;
import com.xiaoliuliu.six.finger.web.spring.aop.aspect.MethodBeforeAdviceInterceptor;
import com.xiaoliuliu.six.finger.web.spring.aop.config.AopConfig;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
 * @author 小六六
 * @version 1.0
 * @date 2020/10/26 17:43
 */
public class AdvisedSupport {
    /**被代理的类class*/
    private Class<?> targetClass;
    /**被代理的对象实力*/
    private Object target;
    /**被代理的方法对应的拦截器集合*/
    private Map<Method, List<Object>> methodCache;
    /**AOP外部配置*/
    private AopConfig config;
    /**切点正则表达式*/
    private Pattern pointCutClassPattern;
    public AdvisedSupport(AopConfig config) {
        this.config = config;
    }
    public Class<?> getTargetClass() {
        return this.targetClass;
    }
    public Object getTarget() {
        return this.target;
    }
    /**
     * 获取拦截器
     */
    public List<Object> getInterceptorsAndDynamicInterceptionAdvice(Method method, Class<?> targetClass) throws Exception {
        List<Object> cached = methodCache.get(method);
        if (cached == null) {
            Method m = targetClass.getMethod(method.getName(), method.getParameterTypes());
            cached = methodCache.get(m);
            this.methodCache.put(m, cached);
        }
        return cached;
    }
    public void setTargetClass(Class<?> targetClass) {
        this.targetClass = targetClass;
        parse();
    }
    /**
     * 解析AOP配置,创建拦截器
     */
    private void parse() {
        //编译切点表达式为正则
        String pointCut = config.getPointCut()
                .replaceAll("\\.", "\\\\.")
                .replaceAll("\\\\.\\*", ".*")
                .replaceAll("\\(", "\\\\(")
                .replaceAll("\\)", "\\\\)");
        //pointCut=public .* com.lqb.demo.service..*Service..*(.*)
        String pointCutForClassRegex = pointCut.substring(0, pointCut.lastIndexOf("\\(") - 4);
        pointCutClassPattern = Pattern.compile("class " + pointCutForClassRegex.substring(
                pointCutForClassRegex.lastIndexOf(" ") + 1));
        try {
            //保存切面的所有通知方法
            Map<String, Method> aspectMethods = new HashMap<>();
            Class aspectClass = Class.forName(this.config.getAspectClass());
            for (Method m : aspectClass.getMethods()) {
                aspectMethods.put(m.getName(), m);
            }
            //遍历被代理类的所有方法,为符合切点表达式的方法创建拦截器
            methodCache = new HashMap<>();
            Pattern pattern = Pattern.compile(pointCut);
            for (Method m : this.targetClass.getMethods()) {
                String methodString = m.toString();
                //为了能正确匹配这里去除函数签名尾部的throws xxxException
                if (methodString.contains("throws")) {
                    methodString = methodString.substring(0, methodString.lastIndexOf("throws")).trim();
                }
                Matcher matcher = pattern.matcher(methodString);
                if (matcher.matches()) {
                    //执行器链
                    List<Object> advices = new LinkedList<>();
                    //创建前置拦截器
                    if (!(null == config.getAspectBefore() || "".equals(config.getAspectBefore()))) {
                        //创建一个Advivce
                        MethodBeforeAdviceInterceptor beforeAdvice = new MethodBeforeAdviceInterceptor(
                                aspectMethods.get(config.getAspectBefore()),
                                aspectClass.newInstance()
                        );
                        advices.add(beforeAdvice);
                    }
                    //创建后置拦截器
                    if (!(null == config.getAspectAfter() || "".equals(config.getAspectAfter()))) {
                        AfterReturningAdviceInterceptor returningAdvice = new AfterReturningAdviceInterceptor(
                                aspectMethods.get(config.getAspectAfter()),
                                aspectClass.newInstance()
                        );
                        advices.add(returningAdvice);
                    }
                    //创建异常拦截器
                    if (!(null == config.getAspectAfterThrow() || "".equals(config.getAspectAfterThrow()))) {
                        AfterThrowingAdviceInterceptor throwingAdvice = new AfterThrowingAdviceInterceptor(
                                aspectMethods.get(config.getAspectAfterThrow()),
                                aspectClass.newInstance()
                        );
                        throwingAdvice.setThrowName(config.getAspectAfterThrowingName());
                        advices.add(throwingAdvice);
                    }
                    //保存被代理方法和执行器链的对应关系
                    methodCache.put(m, advices);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public void setTarget(Object target) {
        this.target = target;
    }
    /**
     * 判断一个类是否需要被代理
     */
    public boolean pointCutMatch() {
        return pointCutClassPattern.matcher(this.targetClass.toString()).matches();
    }
}
复制代码


AopConfig保存了配置好切面、切点以及通知。

package com.xiaoliuliu.six.finger.web.spring.aop.config;
import lombok.Data;
/**
 * @author 小六六
 * @version 1.0
 * @date 2020/10/26 17:43
 */
@Data
public class AopConfig {
    //切点表达式
    private String pointCut;
    //前置通知方法
    private String aspectBefore;
    //后置通知方法
    private String aspectAfter;
    //切面类
    private String aspectClass;
    //异常通知方法
    private String aspectAfterThrow;
    //抛出的异常类型
    private String aspectAfterThrowingName;
}
复制代码


测试

至于测试的话就是像前面一样,改造一下

网络异常,图片无法展示
|


注入aop就好了。


遇到得问题

其实这边小六六有一个坑,但是自己没搞定,是这样的哈

网络异常,图片无法展示
|


我这个测试类的时候,我们要注入的这个UserServiceImpl 应该是代理对象,但是我们通过动态代理之后生成的代理对象一直是报错的。

网络异常,图片无法展示
|


但是他的拦截器链是成了。但是aop代理对象一直报错。

网络异常,图片无法展示
|


所以那个注入就是一直报空了,老郁闷了,不懂为啥。


结尾


其实spring的aop 说到底就是动态代理+我们各种前中后处理器(责任链模式的实现)。虽然没有完全做出来但是对于spring的aop还是有点帮助的,大家可以参考下。

相关文章
|
2月前
|
Java 程序员
JAVA程序员的进阶之路:掌握URL与URLConnection,轻松玩转网络资源!
在Java编程中,网络资源的获取与处理至关重要。本文介绍了如何使用URL与URLConnection高效、准确地获取网络资源。首先,通过`java.net.URL`类定位网络资源;其次,利用`URLConnection`类实现资源的读取与写入。文章还提供了最佳实践,包括异常处理、连接池、超时设置和请求头与响应头的合理配置,帮助Java程序员提升技能,应对复杂网络编程场景。
71 9
|
7天前
|
Java Maven
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
在Java项目中,启动jar包时遇到“no main manifest attribute”错误,且打包大小明显偏小。常见原因包括:1) Maven配置中跳过主程序打包;2) 缺少Manifest文件或Main-Class属性。解决方案如下:
java项目中jar启动执行日志报错:no main manifest attribute, in /www/wwwroot/snow-server/z-server.jar-jar打包的大小明显小于正常大小如何解决
|
4天前
|
存储 Java BI
java怎么统计每个项目下的每个类别的数据
通过本文,我们详细介绍了如何在Java中统计每个项目下的每个类别的数据,包括数据模型设计、数据存储和统计方法。通过定义 `Category`和 `Project`类,并使用 `ProjectManager`类进行管理,可以轻松实现项目和类别的数据统计。希望本文能够帮助您理解和实现类似的统计需求。
40 17
|
26天前
|
NoSQL Java 关系型数据库
Liunx部署java项目Tomcat、Redis、Mysql教程
本文详细介绍了如何在 Linux 服务器上安装和配置 Tomcat、MySQL 和 Redis,并部署 Java 项目。通过这些步骤,您可以搭建一个高效稳定的 Java 应用运行环境。希望本文能为您在实际操作中提供有价值的参考。
118 26
|
1月前
|
Java 开发者 微服务
Spring Boot 入门:简化 Java Web 开发的强大工具
Spring Boot 是一个开源的 Java 基础框架,用于创建独立、生产级别的基于Spring框架的应用程序。它旨在简化Spring应用的初始搭建以及开发过程。
58 6
Spring Boot 入门:简化 Java Web 开发的强大工具
|
2月前
|
XML Java 测试技术
从零开始学 Maven:简化 Java 项目的构建与管理
Maven 是一个由 Apache 软件基金会开发的项目管理和构建自动化工具。它主要用在 Java 项目中,但也可以用于其他类型的项目。
61 1
从零开始学 Maven:简化 Java 项目的构建与管理
|
2月前
|
Java
Java项目中高精度数值计算:为何BigDecimal优于Double
在Java项目开发中,涉及金额计算、面积计算等高精度数值操作时,应选择 `BigDecimal` 而非 `Double`。`BigDecimal` 提供任意精度的小数运算、多种舍入模式和良好的可读性,确保计算结果的准确性和可靠性。例如,在金额计算中,`BigDecimal` 可以精确到小数点后两位,而 `Double` 可能因精度问题导致结果不准确。
|
2月前
|
Java Android开发
Eclipse 创建 Java 项目
Eclipse 创建 Java 项目
51 4
|
2月前
|
SQL 存储 Java
面向 Java 程序员的 SQLite 替代品
SQLite 是轻量级数据库,适用于小微型应用,但其对外部数据源支持较弱、无存储过程等问题影响了开发效率。esProc SPL 是一个纯 Java 开发的免费开源工具,支持标准 JDBC 接口,提供丰富的数据源访问、强大的流程控制和高效的数据处理能力,尤其适合 Java 和安卓开发。SPL 代码简洁易懂,支持热切换,可大幅提高开发效率。
|
2月前
|
Java Maven Spring
Java Web 应用中,资源文件的位置和加载方式
在Java Web应用中,资源文件如配置文件、静态文件等通常放置在特定目录下,如WEB-INF或classes。通过类加载器或Servlet上下文路径可实现资源的加载与访问。正确管理资源位置与加载方式对应用的稳定性和可维护性至关重要。
63 6