InvocationHandler

InvocationHandler 详细介绍

InvocationHandler 是 Java 反射机制中的一个接口,属于 java.lang.reflect 包。它的主要作用是为动态代理提供处理方法调用的能力。当使用 Java 动态代理时,可以通过实现 InvocationHandler 接口来定义在代理对象上调用方法时的行为。

核心方法
  • invoke(Object proxy, Method method, Object[] args):
  • proxy: 代表被代理的对象。
  • method: 被调用的方法。
  • args: 方法参数。
  • 该方法会在每次调用代理对象的方法时被触发,你可以在这里添加额外的逻辑,比如日志、事务管理等。

    AOP(面向切面编程)

    AOP(Aspect-Oriented Programming)是一种编程范式,它允许你将关注点分离到不同的“切面”中。与传统 OOP 不同,AOP 可以让你将横切关注点(如日志、安全性、事务管理等)从业务逻辑中分离出来,从而提高代码的可维护性和可重用性。

    AOP 和 InvocationHandler 的关系
  • 在 Java 中,Spring AOP 实际上是基于动态代理实现的,其中就使用了 InvocationHandler。当你创建一个带有切面的 Spring Bean 时,Spring 会生成一个代理类,这个类会实现目标接口,并在其内部使用 InvocationHandler 来拦截方法调用。

  • 因此,当你使用 Spring AOP 定义一个切面并应用于某个服务时,实际上是在背后利用了 InvocationHandler 来执行横切逻辑。

  • 示例代码

    以下是一个简单示例,包括自定义的 InvocationHandler 和如何通过 Spring AOP 使用它:

    自定义 InvocationHandler 示例
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    // 定义接口
    interface HelloService {
        void sayHello(String name);
    }
    
    // 实现接口
    class HelloServiceImpl implements HelloService {
        @Override
        public void sayHello(String name) {
            System.out.println("Hello, " + name);
        }
    }
    
    // 自定义 InvocationHandler
    class MyInvocationHandler implements InvocationHandler {
        private final Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // 前置增强:打印日志
            System.out.println("Before method: " + method.getName());
            
            // 调用目标方法
            Object result = method.invoke(target, args);
            
            // 后置增强:打印结束信息
            System.out.println("After method: " + method.getName());
            
            return result;
        }
    }
    
    public class Main {
        public static void main(String[] args) {
            HelloService helloService = new HelloServiceImpl();
            
            // 创建动态代理实例
            HelloService proxyInstance = (HelloService) Proxy.newProxyInstance(
                    helloService.getClass().getClassLoader(),
                    helloService.getClass().getInterfaces(),
                    new MyInvocationHandler(helloService)
            );
    
            // 调用代理实例的方法
            proxyInstance.sayHello("World");
        }
    }
    
    输出结果
    Before method: sayHello
    Hello, World
    After method: sayHello
    
    使用 Spring AOP 示例

    如果你想要通过 Spring AOP 来实现类似功能,可以按照下面步骤进行:

    1. 添加依赖(以 Maven 为例):
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-aop</artifactId>
    </dependency>
    
    1. 创建服务和切面:
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.springframework.stereotype.Service;
    
    @Service
    public class HelloServiceImpl implements HelloService {
        
        @Override
        public void sayHello(String name) {
            System.out.println("Hello, " + name);
        }
    }
    
    @Aspect
    @Component
    class LoggingAspect {
    
        @Before("execution(* com.example.HelloService.*(..))")
        public void logBefore() {
            System.out.println("Before executing the method");
        }
    }
    
    1. 启动 Spring Boot 应用程序,并调用服务:
    @SpringBootApplication
    public class Application {
    
       public static void main(String[] args) {
           ApplicationContext context = SpringApplication.run(Application.class, args);
           HelloService helloService = context.getBean(HelloService.class);
           helloService.sayHello("World");
       }
    }
    
    总结
  • InvocationHandler 用于创建动态代理并处理方法调用。
  • AOP 是一种编程范式,通过切面将横切关注点从业务逻辑中分离出来。
  • 在 Spring 中,AOP 是基于动态代理实现的,因此可以看作是对 InvocationHandler 的高级封装。
  • MethodInterceptor 和 InvocationHandler 的关系

    MethodInterceptor和InvocationHandler都是用于实现动态代理的工具,但它们在实现方式、应用场景以及效率上有所不同。以下是对两者关系的详细分析:

    一、定义与实现方式
    1. InvocationHandler
  • InvocationHandler是Java动态代理的一种方式,定义在java.lang.reflect包中。
  • 通过实现InvocationHandler接口,开发者可以定义在代理对象上调用方法时要执行的逻辑。
  • InvocationHandler接口包含一个invoke()方法,该方法在代理对象的方法被调用时被触发。
    1. MethodInterceptor
  • MethodInterceptor是CGLIB库(Code Generation Library)提供的一种动态代理方式。
  • CGLIB是一个用于生成Java字节码的代码生成库,它允许在运行时对类进行代理,而不仅仅是对接口。
  • MethodInterceptor接口定义了一个intercept()方法,该方法在代理对象的方法被调用时被触发。
  • 二、应用场景
    1. InvocationHandler
      主要用于对接口进行代理。
      由于Java的动态代理机制是基于接口的,因此InvocationHandler适用于那些需要代理的类实现了特定接口的场景。
    2. MethodInterceptor
      可以对类进行代理,而无需实现特定接口。
      由于CGLIB通过继承目标类来生成代理,因此MethodInterceptor适用于那些无法或不想实现接口的类。
    三、效率与性能
    1. InvocationHandler
      由于Java的动态代理机制相对简单且直接基于接口,因此InvocationHandler通常具有较高的效率。
    2. MethodInterceptor
      由于CGLIB需要生成不同类型的字节码,并且需要生成一些运行时对象,因此MethodInterceptor的效率相对较低。
      但是,这种性能差异在大多数情况下可能并不显著,具体取决于应用程序的复杂性和性能要求。
    四、总结
    1. InvocationHandler和MethodInterceptor都是实现动态代理的有效工具,但它们在实现方式、应用场景以及效率上有所不同。
    2. 如果只需要对接口进行代理,并且对效率有较高的要求,那么可以选择使用InvocationHandler。
    3. 如果需要对类进行代理,或者需要更多的控制被拦截的方法,那么可以选择使用MethodInterceptor。

    综上所述,MethodInterceptor和InvocationHandler各有优劣,开发者应根据具体需求和场景选择合适的动态代理方式。

    find 命令

    find 命令是 Unix/Linux 系统中用于查找文件和目录的强大工具。它可以在指定的目录及其子目录中搜索符合条件的文件,并对这些文件执行操作。

    基本语法
    find [path] [expression]
    
  • path: 要搜索的路径,可以是一个或多个目录。如果不指定,默认是在当前目录。
  • expression: 用于定义查找条件,如名称、类型、大小等。
  • 常用选项和表达式
    1. 基本查找

    2. 查找特定名称的文件:

      find /path/to/search -name "filename"
      
    3. 查找特定扩展名的文件:

      find /path/to/search -name "*.txt"
      
    4. 忽略大小写

      使用 -iname 选项可以忽略大小写:

      find /path/to/search -iname "*.txt"
      
    5. 按类型查找

      可以使用 -type 选项来限制查找结果:

    6. 查找普通文件:

      find /path/to/search -type f
      
    7. 查找目录:

      find /path/to/search -type d
      
    8. 查找符号链接:

      find /path/to/search -type l
      
    9. 按时间查找

      使用 -mtime, -atime, 和 -ctime 来根据时间过滤:

    10. 查找最近修改过的文件(例如:7天内):

      find /path/to/search -mtime -7 
      
    11. 查找超过30天未被访问过的文件:

      find /path/to/search -atime +30 
      
    12. 按大小查找

      使用 -size 选项来根据文件大小进行筛选:

    13. 找到大于100MB的文件:
      find /path/to/search -size +100M 
      
    14. 组合条件

      可以使用逻辑运算符组合多个条件:

    15. -and: 与(默认)
    16. -or: 或
    17. !: 非
    18. 示例:找到所有 .txt 文件并且大小大于1MB:

      find /path/to/search -name "*.txt" ! -size +1M 
      
    19. 执行命令

    通过 -exec 选项可以对找到的每个文件执行命令:

    find /path/to/search -name "*.tmp" -exec rm {} \;
    

    这里 {} 是占位符,代表当前找到的每个文件,\; 表示命令结束。

    1. 打印结果

    使用以下选项来控制输出格式:

  • 默认情况下,结果会逐行显示。

  • 如果需要以更易读的方式显示,可以加上 -print

  • find /path/to/search -name "*.log" -print 
    
    1. 限制深度

    使用 -maxdepth-mindepth 限制搜索深度:

    find /path/to/search -maxdepth 2 
    

    这将只搜索到指定深度为2层级。

    示例
    1. /home/user/ 下查找所有 .jpg 文件并删除它们:
    find /home/user/ -name "*.jpg" -exec rm {} \;
    
    1. 找出 /var/log/ 中最后修改时间在10天前的日志文件:
    find /var/log/ -mtime +10 
    
    1. 查找到大于500KB且以 .pdf 结尾的所有文档,并列出详细信息:
    find . \( -name "*.pdf" \)  \( ! –size –500k \) –ls 
    
    总结

    find 命令非常灵活和强大,能够满足多种需求。通过合理地组合各种参数和表达式,可以高效地管理和处理大量数据。在实际使用中,根据具体需求调整路径和条件,以达到最佳效果。

    mysql 创建用户方式

    在 MySQL 中,创建用户是数据库管理的重要任务之一。MySQL 提供了多种方式来创建用户,包括使用 SQL 语句、图形界面工具(如 MySQL Workbench)等。下面将详细介绍通过 SQL 语句创建用户的步骤,并给出示例。

    创建用户的基本步骤
    1. 连接到 MySQL 数据库:使用具有足够权限的账户(如 root 用户)登录到 MySQL。
    2. 执行 CREATE USER 语句:使用 SQL 命令创建新用户。
    3. 授予权限:为新用户分配所需的权限,以便他们可以执行特定操作。
    创建用户的 SQL 语法
    CREATE USER 'username'@'host' IDENTIFIED BY 'password';
    
  • username: 要创建的用户名。
  • host: 用户可以从哪个主机连接到数据库。常见值有:
  • 'localhost':仅允许本地连接。
  • '%':允许任何主机连接。
  • password: 用户密码。
  • 示例
    第一步:连接到 MySQL

    首先,打开命令行并输入以下命令以登录到 MySQL:

    mysql -u root -p
    

    系统会提示你输入 root 用户的密码。

    第二步:创建一个新用户

    假设我们要创建一个名为 newuser 的新用户,该用户可以从任何主机访问,并且其密码为 password123

    CREATE USER 'newuser'@'%' IDENTIFIED BY 'password123';
    
    第三步:授予权限

    接下来,我们需要为该用户授予必要的权限。例如,如果我们希望 newuser 可以对数据库进行所有操作,可以使用以下命令:

    GRANT ALL PRIVILEGES ON *.* TO 'newuser'@'%';
    

    这条命令表示给予 newuser 对所有数据库和表格的所有权限。如果只想授权某个特定数据库,例如名为 mydatabase 的数据库,可以这样做:

    GRANT ALL PRIVILEGES ON mydatabase.* TO 'newuser'@'%';
    
    第四步:刷新权限

    为了确保新的权限生效,可以运行以下命令:

    FLUSH PRIVILEGES;
    
    完整示例代码

    结合上述步骤,完整示例如下:

    -- 登录到 MySQL (在命令行中)
    mysql -u root -p;
    
    -- 创建新用户
    CREATE USER 'newuser'@'%' IDENTIFIED BY 'password123';
    
    -- 授予所有权限(或指定数据库)
    GRANT ALL PRIVILEGES ON *.* TO 'newuser'@'%';
    
    -- 刷新权限
    FLUSH PRIVILEGES;
    
    注意事项
    1. 安全性: 使用强密码以保护你的数据库安全。
    2. 最小权限原则: 尽量不要给予过多不必要的权限,只授予必需的最低限度权利,以减少潜在风险。
    3. 主机限制: 如果你只希望特定 IP 或域名能够访问该账户,请替换 % 为具体地址,如 '192.168.1.%''example.com'

    作者:路上阡陌

    物联沃分享整理
    物联沃-IOTWORD物联网 » Java学习笔记(九)

    发表回复