[适合初中级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还是有点帮助的,大家可以参考下。

相关文章
|
4月前
|
存储 前端开发 Java
【JAVA】Java 项目实战之 Java Web 在线商城项目开发实战指南
本文介绍基于Java Web的在线商城技术方案与实现,涵盖三层架构设计、MySQL数据库建模及核心功能开发。通过Spring MVC + MyBatis + Thymeleaf实现商品展示、购物车等模块,提供完整代码示例,助力掌握Java Web项目实战技能。(238字)
470 0
|
4月前
|
安全 Java API
Java Web 在线商城项目最新技术实操指南帮助开发者高效完成商城项目开发
本项目基于Spring Boot 3.2与Vue 3构建现代化在线商城,涵盖技术选型、核心功能实现、安全控制与容器化部署,助开发者掌握最新Java Web全栈开发实践。
466 1
|
5月前
|
前端开发 Java 数据库
Java 项目实战从入门到精通 :Java Web 在线商城项目开发指南
本文介绍了一个基于Java Web的在线商城项目,涵盖技术方案与应用实例。项目采用Spring、Spring MVC和MyBatis框架,结合MySQL数据库,实现商品展示、购物车、用户注册登录等核心功能。通过Spring Boot快速搭建项目结构,使用JPA进行数据持久化,并通过Thymeleaf模板展示页面。项目结构清晰,适合Java Web初学者学习与拓展。
414 1
|
5月前
|
JavaScript Java 微服务
现代化 Java Web 在线商城项目技术方案与实战开发流程及核心功能实现详解
本项目基于Spring Boot 3与Vue 3构建现代化在线商城系统,采用微服务架构,整合Spring Cloud、Redis、MySQL等技术,涵盖用户认证、商品管理、购物车功能,并支持Docker容器化部署与Kubernetes编排。提供完整CI/CD流程,助力高效开发与扩展。
626 64
|
6月前
|
缓存 NoSQL Java
Java Web 从入门到精通之苍穹外卖项目实战技巧
本项目为JavaWeb综合实战案例——苍穹外卖系统,涵盖Spring Boot 3、Spring Cloud Alibaba、Vue 3等主流技术栈,涉及用户认证、订单处理、Redis缓存、分布式事务、系统监控及Docker部署等核心功能,助你掌握企业级项目开发全流程。
697 0
|
6月前
|
安全 JavaScript Java
java Web 项目完整案例实操指南包含从搭建到部署的详细步骤及热门长尾关键词解析的实操指南
本项目为一个完整的JavaWeb应用案例,采用Spring Boot 3、Vue 3、MySQL、Redis等最新技术栈,涵盖前后端分离架构设计、RESTful API开发、JWT安全认证、Docker容器化部署等内容,适合掌握企业级Web项目全流程开发与部署。
467 0
|
3月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
213 1
|
3月前
|
JSON 网络协议 安全
【Java基础】(1)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
235 1
|
4月前
|
数据采集 存储 弹性计算
高并发Java爬虫的瓶颈分析与动态线程优化方案
高并发Java爬虫的瓶颈分析与动态线程优化方案