蒲良鹭 - 技术
type: channel
link: http://www.pulianglu.cn/index.php/category/default/
description: 只是一个默认分类
items: JDK动态代理与CGlib动态代理
items: 关于left join的一些事
items: typecho使用腾讯cos存储保存图片
items: Spring IOC 容器源码分析

JDK动态代理与CGlib动态代理
type: item
date: 2022-05-25T23:56:00+08:00
description: 众所周知，Spring会基于代理的类去动态的选择使用JDK创建代理对象还是CGLIB（当然也可以配置全都使用CGlib），这取决于被代理的对象是类还是接口。java动态代理是利用反射机制生成一个实现代理接口的匿名类，在调用具体方法前调用InvokeHandler来处理。CGlib动态代理是利用asm开源包，对代理对象类的class文件加载进来，通过修改其字节码生成子类来处理（字节码增强）。为什么JDK动态代理只能代理接口，不能直接代理类？先定义一个接口（JDK动态代理只能代理接口） public interface UserService { /** * test * * @return */ String query(); } 接口的实现类public class UserServiceImpl implements UserService{ @Override public String query() { System.out.println("query"); return null; } } 再定义一个InvocationHandler接口的实现类：public class UserServiceInvocationHandler implements InvocationHandler { /** * 持有目标对象 */ private Object target; public UserServiceInvocationHandler(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("invocation handler"); // 通过反射调用目标对象的方法 return method.invoke(target, args); } } 通过Proxy.newInstance()创建代理对象public class MainApplication { public static void main(String[] args) { // 指明一个类加载器，要操作class文件，怎么少得了类加载器呢 ClassLoader classLoader = MainApplication.class.getClassLoader(); // 为代理对象指定要是实现哪些接口，这里我们要为UserServiceImpl这个目标对象创建动态代理，所以需要为代理对象指定实现UserService接口 Class[] classes = new Class[]{UserService.class}; // 初始化一个InvocationHandler，并初始化InvocationHandler中的目标对象 InvocationHandler invocationHandler = new UserServiceInvocationHandler(new UserServiceImpl()); // 创建动态代理 UserService userService = (UserService) Proxy.newProxyInstance(classLoader, classes, invocationHandler); // 执行代理对象的方法，通过观察控制台的结果，判断我们是否对目标对象(UserServiceImpl)的方法进行了增强 userService.query(); } } 执行结果invocation handler query阅读到这里，我们基本知道了JDK动态代理的写法，功能虽然实现了，但是对于喜欢研究源码的同学，可能会思考：为什么调用Proxy.newProxyInstance()方法后，就产生了代理对象，对目标对象的方法进行了增强？原理是什么呢？​ 那接下来，我们就想着能不能获取到代理对象的class文件。下面用几种方式来获取class文件手动写到磁盘在调用Proxy.newProxyInstance()方法时，最终会调用到ProxyGenerator.generateProxyClass()方法，该方法的作用就是生成代理对象的class文件，返回值是一个byte[]数组。所以我们可以将生成的byte[]数组通过输出流，将内容写出到磁盘。public static void main(String[] args) throws IOException { String proxyName = "com.sun.proxy.$Proxy0"; Class…

关于left join的一些事
type: item
date: 2022-05-12T22:57:00+08:00
description: 先做个题吧我们先建两个表吧。一个叫classes，一个叫students。DROP TABLE IF EXISTS `classes`; CREATE TABLE `classes` ( `id` int(11) NOT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `classes` VALUES (1, '一班'); INSERT INTO `classes` VALUES (2, '二班'); INSERT INTO `classes` VALUES (3, '三班'); CREATE TABLE `students` ( `id` int(11) NOT NULL, `class_id` int(11) NULL DEFAULT NULL, `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, `gender` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL, PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic; INSERT INTO `students` VALUES (1, 1, '小明', 'M'); INSERT INTO `students` VALUES (2, 1, '小红', 'F'); INSERT INTO `students` VALUES (3, 1, '小军 ', 'M'); INSERT INTO `students` VALUES (4, 1, '小米', 'F'); INSERT INTO `students` VALUES (5, 2, '小白', 'F'); INSERT INTO `students` VALUES (6, 2, '小兵', 'M'); INSERT INTO `students` VALUES (7, 2, '小林', 'M'); INSERT INTO `students` VALUES (8, 3, '小新', 'F'); INSERT INTO `students` VALUES (9, 3, '小王', 'M'); INSERT INTO `students` VALUES (10, 3, '小丽', 'F');现在有两个需求：找出每个班级的名称及其对应的女同学数量找出一班的同学总数第一题正确答案是：SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id and s.gender = 'F' group by c.name第二题正确答案是：SELECT c.name, count(s.name) as num FROM classes c left join students s on s.class_id = c.id where c.name = '一班' group by c.name你写对了吗？我们注意到，第一个过滤条件是放在on后面，第二个是放在where后面，为什么呢？left join的本质left join 就是以左表为基准，遍历每一行，然后根据on后面的条件，去匹配右表的数据，有几行就加几行，一行都没有，就只放一个左表的数据，右表值为空。这样就生成了一张临时表数据，然后再通过where后面的条件来筛选临时表中的数据，最后返回。下面用伪代码表示一下for (int i = 0 ; i < classes.size(); ++i){ int b = false; for(int j= 0; j < students.size(); ++j){ //处理on后面的条件 if(classes[i].id = students.[j].id ){ //往temp放一条数据 temp.add(); //处理过了，就后面就不放空数据了 b = true; …

typecho使用腾讯cos存储保存图片
type: item
date: 2022-04-17T17:19:00+08:00
description: 1.配置腾讯云cos1.1 开通服务首先登录腾讯,开通腾讯云cos服务，一开始会有50G的免费额度1.2 配置环境创建一个存储桶，访问权限选择 ‘共有读私有写’，然后访问 密钥管理 -访问密钥 -API密钥管理-新建密钥，创建一个密钥。至此，腾讯云cos配置完成2. 安装并设置cosUploadV5插件2.1 安装插件安装cosUploadV5，并启用，2.2 设置插件在腾讯云控制台 个人API密钥 页面里获取 APPID、SecretId、SecretKey内容；需要注意的是APPID和bucket名称，例:在v5版本中存储桶名称显示如test-1251801571，插件设置中APPID应输入1251801571,Bucket名称应输入test

Spring IOC 容器源码分析
type: item
date: 2022-03-03T23:31:00+08:00
description: Spring 最重要的概念是 IOC 和 AOP，本篇文章其实就是要带领大家来分析下 Spring 的 IOC 容器。既然大家平时都要用到 Spring，怎么可以不好好了解 Spring 呢？阅读本文并不能让你成为 Spring 专家，不过一定有助于大家理解 Spring 的很多概念，帮助大家排查应用中和 Spring 相关的一些问题。本文采用的源码版本是 4.3.11.RELEASE，算是 5.0.x 前比较新的版本了。为了降低难度，本文所说的所有的内容都是基于 xml 的配置的方式，实际使用已经很少人这么做了，至少不是纯 xml 配置，不过从理解源码的角度来看用这种方式来说无疑是最合适的。阅读建议：读者至少需要知道怎么配置 Spring，了解 Spring 中的各种概念，少部分内容我还假设读者使用过 SpringMVC。本文要说的 IOC 总体来说有两处地方最重要，一个是创建 Bean 容器，一个是初始化 Bean，如果读者觉得一次性看完本文压力有点大，那么可以按这个思路分两次消化。读者不一定对 Spring 容器的源码感兴趣，也许附录部分介绍的知识对读者有些许作用。希望通过本文可以让读者不惧怕阅读 Spring 源码，也希望大家能反馈表述错误或不合理的地方。引言先看下最基本的启动 Spring 容器的例子：public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("classpath:applicationfile.xml"); } 以上代码就可以利用配置文件来启动一个 Spring 容器了，请使用 maven 的小伙伴直接在 dependencies 中加上以下依赖即可，个人比较反对那些不知道要添加什么依赖，然后把 Spring 的所有相关的东西都加进来的方式。<dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.11.RELEASE</version> </dependency> spring-context 会自动将 spring-core、spring-beans、spring-aop、spring-expression 这几个基础 jar 包带进来。 多说一句，很多开发者入门就直接接触的 SpringMVC，对 Spring 其实不是很了解，Spring 是渐进式的工具，并不具有很强的侵入性，它的模块也划分得很合理，即使你的应用不是 web 应用，或者之前完全没有使用到 Spring，而你就想用 Spring 的依赖注入这个功能，其实完全是可以的，它的引入不会对其他的组件产生冲突。废话说完，我们继续。ApplicationContext context = new ClassPathXmlApplicationContext(...) 其实很好理解，从名字上就可以猜出一二，就是在 ClassPath 中寻找 xml 配置文件，根据 xml 文件内容来构建 ApplicationContext。当然，除了 ClassPathXmlApplicationContext 以外，我们也还有其他构建 ApplicationContext 的方案可供选择，我们先来看看大体的继承结构是怎么样的： 读者可以大致看一下类名，源码分析的时候不至于找不着看哪个类，因为 Spring 为了适应各种使用场景，提供的各个接口都可能有很多的实现类。对于我们来说，就是揪着一个完整的分支看完。 当然，读本文的时候读者也不必太担心，每个代码块分析的时候，我都会告诉读者我们在说哪个类第几行。 我们可以看到，ClassPathXmlApplicationContext 兜兜转转了好久才到 ApplicationContext 接口，同样的，我们也可以使用绿颜色的 FileSystemXmlApplicationContext 和 AnnotationConfigApplicationContext 这两个类。1、FileSystemXmlApplicationContext 的构造函数需要一个 xml 配置文件在系统中的路径，其他和 ClassPathXmlApplicationContext 基本上一样。2、AnnotationConfigApplicationContext 是基于注解来使用的，它不需要配置文件，采用 java 配置类和各种注解来配置，是比较简单的方式，也是大势所趋吧。不过本文旨在帮助大家理解整个构建流程，所以决定使用 Clas…