切面中使用反射技术

package cn.com.demo.abby.common.aspect;

import cn.com.swancloud.aliyun.Authentication;
import com.aliyuncs.DefaultAcsClient;
import com.aliyuncs.IAcsClient;
import com.aliyuncs.profile.DefaultProfile;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.lang.reflect.Field;
import java.util.*;

/**
 * @Date: 2019/1/16 16:13
 * 生成认证对象切面
 */
@Aspect
@Component
public class AuthenticationAspect {

    private final String regionId = "regionId";
    private final String AccessKey_ID = "AccessKey_ID";
    private final String AccessKey_Secret = "AccessKey_Secret";

    /**
     * 对指定包,进行横切,创建一个横切的对象方法
     */
    @Pointcut("execution(* cn.com.demo.abby.service..*.*(..))")
    public void executionPoint() {}

    @Before("executionPoint()")
    public void before(JoinPoint joinPoint) {
        try {
            Object[] o = joinPoint.getArgs();
            for (int i = 0; i < o.length; i++) {

                //1.加载类
                String name = o[i].getClass().getName();
                Class clz = Class.forName(name);

                //2.获取当前类包含其父类的全部属性
                List<Field> fieldList = new ArrayList<>();
                while(clz != null){
                    fieldList.addAll(Arrays.asList(clz.getDeclaredFields()));
                    clz = clz.getSuperclass();
                }

                //3.匹配到iAcsClient属性,赋值
                for(Field field : fieldList){
                    if(("iAcsClient").equals(field.getName())){
                        //4.如果此属性为非public属性的情况时,需要设置属性可达,否则会抛出IllegalAccessException异常
                        field.setAccessible(true);
                        //5.生成认证对象
                        //5.1获取指定属性值
                        Map<String, String> fieldsValue = getFieldsValue(fieldList, o[i]);
                        //5.2创建 Profile。生成 IClientProfile 的对象 profile,该对象存放 AccessKeyID 和 AccessKeySecret 和默认的地域信息
                        DefaultProfile profile = DefaultProfile.getProfile(fieldsValue.get(regionId), fieldsValue.get(AccessKey_ID), fieldsValue.get(AccessKey_Secret));
                        //5.3创建 Client。从 IClientProfile 类中再生成 IAcsClient 的对象 client,后续获得 response 都需要从 IClientProfile 中获得。
                        IAcsClient client = new DefaultAcsClient(profile);
                        //6.赋值
                        field.set(o[i],client);
                    }
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private Map<String,String> getFieldsValue(List<Field> fieldList,Object object) throws Exception {
        Map<String,String> result = new HashMap<>();
        for(Field field : fieldList){
            if(("regionId").equals(field.getName())){
                //如果此属性为非public属性的情况时,需要设置属性可达,否则会抛出IllegalAccessException异常
                field.setAccessible(true);
                //获取属性值存入Map
                result.put(regionId,field.get(object).toString());
            }
            if(("authentication").equals(field.getName())){
                field.setAccessible(true);
                //获取属性值存入Map
                Authentication authentication = (Authentication)field.get(object);
                result.put(AccessKey_ID,authentication.getAccessKey_ID());
                result.put(AccessKey_Secret,authentication.getAccessKey_Secret());
            }
        }
        return result;
    }
}

 

<think>首先,用户的问题是关于面向切面编程(AOP)中反射使用方式。具体问题是:“我想了解面向切面编程中反射使用方式 请问面向切面编程 AOP 如何使用反射实现”。 我需要参考提供的引用内容。引用包括: - [^1]: 定义了AOP,提到它是通过预编译方式和运行期动态代理实现的。 - [^2]: 讨论了一篇文章关于Spring AOP,包括动态代理实现。 - [^3]: 详细介绍了Spring AOP,包括动态代理的实现原理。 - [^4]: 解释了AOP的实现,提到横向抽取关注点。 用户的问题焦点是反射在AOP中的应用。反射Java中的一个特性,允许在运行时检查和修改类、方法、字段等。 在AOP中,反射通常用于动态代理的实现。动态代理是AOP的核心技术之一,它允许在运行时创建代理对象来拦截方法调用。 从引用中: - [^2] 提到“动态代理方式实现AOP”。 - [^3] 详细描述了“动态代理”,并区分了JDK Proxy和CGLIB。 - [^4] 提到AOP通过动态代理实现。 反射在动态代理中的作用: - 在JDK动态代理中,使用`java.lang.reflect.Proxy`类,它依赖于反射来创建代理实例。 - 在CGLIB中,它使用字节码生成,但也可能间接使用反射。 具体到反射使用: - 反射用于动态调用方法、获取方法信息等。 - 在AOP中,代理对象拦截方法调用时,使用反射来调用原始方法或应用通知。 我的回答需要: - 使用中文。 - 结构清晰,逐步解决问题。 - 保证回答真实可靠。 - 在行内数学表达式中使用`$...$`,但用户的问题中没有数学内容,所以可能不需要。 - 独立公式使用`$$...$$`并单独成段,但可能不适用。 - 生成相关问题。 - 在引用段落末尾添加引用标识。 回答结构: 1. **解释AOP和反射的基本概念**:简要回顾AOP是什么,以及反射是什么。 2. **描述AOP如何实现**:重点在动态代理中使用反射。 3. **具体反射使用方式**:详细说明在动态代理中反射如何被应用。 4. **举例说明**:可能用伪代码或简单例子。 5. **生成相关问题**:在最后添加相关问题。 参考引用: - 从[^1]:AOP通过预编译方式和运行期动态代理实现。 - 从[^2]和[^3]:动态代理是AOP的实现方式。 - 从[^3]:Spring AOP使用JDK动态代理或CGLIB。 反射在JDK动态代理中的使用: - `Proxy.newProxyInstance()`方法使用反射来创建代理对象。 - `InvocationHandler`接口的`invoke`方法使用反射调用原始方法。 例如: ```java public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 在方法调用前执行通知(如日志) Object result = method.invoke(target, args); // 使用反射调用原始方法 // 在方法调用后执行通知 return result; } ``` 这里,`method.invoke(target, args)` 就是使用反射调用目标对象的方法。 在CGLIB中,它使用ASM库生成字节码,可能不直接使用反射,但原理类似。 确保回答真实:基于标准Java和Spring知识。 最后,生成相关问题。 添加引用:在回答中引用相关段落时,末尾添加[^n]。 例如,如果提到动态代理实现,可以引用[^2]或[^3]。 完整回答大纲: - 引言:重述问题,并简要回答。 - 主体:分步解释。 - 步骤1: AOP和反射概述。 - 步骤2: AOP实现机制:动态代理。 - 步骤3: 反射在动态代理中的具体使用。 - 步骤4: 示例(可选)。 - 结尾:总结。 - 相关问题。 语言:中文。</think>### 面向切面编程(AOP)中反射使用方式 面向切面编程(AOP)是一种编程范式,旨在将横切关注点(如日志、事务、安全等)从业务逻辑中分离出来,以提高代码的可维护性和复用性。在AOP的实现中,反射(Reflection)扮演着关键角色,尤其是在动态代理机制中。反射允许程序在运行时动态地获取和操作类、方法、字段等信息,从而实现方法的拦截和增强。下面我将逐步解释AOP如何使用反射实现,确保内容真实可靠,并基于Spring AOP框架(Java生态中最常见的AOP实现)进行说明。 #### 步骤1: AOP和反射的基本概念回顾 - **AOP核心**:AOP通过“切面”(Aspect)来封装横切关注点。切面由“切点”(Pointcut,定义在何处拦截)和“通知”(Advice,定义拦截后执行的动作)组成。目标是减少代码侵入性,例如,在不修改业务代码的情况下添加日志[^1]。 - **反射的作用**:反射Java语言的一个特性,它允许程序在运行时动态加载类、调用方法、访问字段等。在AOP中,反射用于动态创建代理对象、拦截方法调用,并执行通知逻辑。这使得AOP能在运行时“织入”(Weave)额外行为,而无需修改源代码[^3]。 #### 步骤2: AOP的实现机制:动态代理依赖反射 AOP的实现主要依赖动态代理(Dynamic Proxy),而动态代理的核心就是反射。动态代理分为两种方式: - **JDK动态代理**:基于Java的`java.lang.reflect.Proxy`类,适用于接口代理。它使用反射动态生成代理类实例。 - **CGLIB动态代理**:基于字节码生成库(如CGLIB),适用于类代理(即使目标类未实现接口)。它通过生成子类来代理,内部也间接使用反射来调用方法。 在Spring AOP中,默认优先使用JDK动态代理(如果目标对象实现了接口),否则回退到CGLIB。反射在这里用于: - 动态识别目标类的方法。 - 在运行时创建代理对象,拦截方法调用。 - 执行通知(如前置通知、后置通知)[^2][^3]。 #### 步骤3: 反射在动态代理中的具体使用方式 反射在AOP的动态代理中主要通过`InvocationHandler`(JDK代理)或`MethodInterceptor`(CGLIB代理)来实现。以下是详细步骤和代码示例: 1. **创建代理对象使用反射**: - 在JDK动态代理中,`Proxy.newProxyInstance()`方法使用反射加载目标类的接口信息,并动态生成代理类。 - 示例代码(简化版,基于JDK代理): ```java import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class JdkProxyExample { public static void main(String[] args) { // 目标对象(业务逻辑) UserService target = new UserServiceImpl(); // 创建代理对象使用反射动态生成 UserService proxy = (UserService) Proxy.newProxyInstance( target.getClass().getClassLoader(), // 反射获取类加载器 target.getClass().getInterfaces(), // 反射获取接口数组 new InvocationHandler() { @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { // 使用反射调用方法前执行通知(如日志) System.out.println("前置通知: 方法 " + method.getName() + " 被调用"); // 反射调用原始方法:method.invoke() 使用反射执行目标方法 Object result = method.invoke(target, args); // 方法调用后执行通知 System.out.println("后置通知: 方法执行完成"); return result; } } ); // 调用代理方法,实际会触发反射逻辑 proxy.addUser(); } } ``` - 关键点:`method.invoke(target, args)` 通过反射调用目标对象的原始方法。`method` 对象是通过反射从目标类获取的,它封装了方法信息(如名称、参数类型)。这使得AOP能在运行时动态“插入”通知逻辑[^3][^4]。 2. **在通知中应用反射**: - 通知(Advice)如`@Before`或`@Around`注解的方法,在Spring AOP中会被代理拦截。反射用于: - 获取方法元数据:例如,通过`Method`对象获取方法名、参数等,用于日志或权限检查。 - 动态调用:在环绕通知中,反射允许灵活地控制方法执行(如跳过、修改参数或返回值)。 - 示例(Spring AOP 环绕通知): ```java import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAspect { @Around("execution(* com.example.service.*.*(..))") // 切点定义 public Object logMethod(ProceedingJoinPoint joinPoint) throws Throwable { // 使用反射获取方法信息 MethodSignature signature = (MethodSignature) joinPoint.getSignature(); Method method = signature.getMethod(); System.out.println("拦截方法: " + method.getName()); // 反射调用目标方法:joinPoint.proceed() 内部使用反射执行 Object result = joinPoint.proceed(); System.out.println("方法执行结果: " + result); return result; } } ``` - 这里,`ProceedingJoinPoint.proceed()` 底层通过反射调用目标方法,体现了AOP与反射的紧密集成[^2][^4]。 3. **反射在性能与安全方面的考虑**: - **性能开销**:反射调用比直接调用慢,因为涉及动态解析。在高性能场景中,Spring AOP通过缓存代理类或使用CGLIB(字节码增强)来优化,减少反射调用次数。 - **安全性**:反射可以绕过访问控制(如调用私有方法),因此AOP框架通常结合安全管理器(如Spring Security)来限制反射操作。 - 总体而言,反射使AOP实现“运行时织入”,而无需静态编译,但开发者需权衡灵活性与性能[^3][^4]。 #### 步骤4: 总结 在面向切面编程中,反射是实现动态代理的核心技术。它允许AOP框架在运行时动态创建代理对象、拦截方法调用,并执行通知逻辑。关键是通过反射的`Method.invoke()`来调用原始方法,实现业务逻辑与横切关注点的解耦。Spring AOP等框架优化了反射使用,使其在大多数应用场景中高效可靠。如果您使用Java,可以结合Spring Boot轻松实践AOP的反射机制[^1][^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值