注解、反射、动态代理

注解、反射、动态代理

涉及到的知识点注解反射动态代理AOPLombok代理模式、`builder模式```````````

Q:工作中用到过注解吗,简单介绍一下。

A:在日常开发中经常使用注解,包括Java提供的@Overried、@Deprecated,一些框架提供的如SpringMVC的@Controller、@Service、@Compontent、@Configuration、@Bean等Lombok的@Data、@Builder等,还有自己结合Spring的AOP来实现日志记录和权限校验等注解。注解的作用就是标记,根据标记的内容进行相应的处理。Java元注解就是修饰注解的注解,常用的有@Retention和@Target。

@Retention用来设置注解的声明周期有三种

  • RetentionPolicy.SOURCE:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略。比如Java原生的@Override只做检查性的操作。Lombok的@Data
  • RetentionPolicy.CLASS:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期。
  • RetentionPolicy.RUNTIME:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在。写业务代码一般是RUNTIME级别,因为我们要在运行的时候去获取元注解信息。

@Target注解用来声明这个注解可以使用的地方包括ElementType.TYPE、ElementType.FIELD、 ElementType.METHOD、 ElementType.PARAMETER、 ElementType.CONSTRUCTOR、 ElementType.LOCAL_VARIABLE、 ElementType.ANNOTATION_TYPE、 ElementType.PACKAGE、 ElementType.TYPE_PARAMETER、 ElementType.TYPE_USE。

获取运行时注解一般使用反射来获取,从Package、Class、Mtehod等类上获取常用的API

  • public T getAnnotation(Class annotationClass)

使用AOP+自定义注解实现自定义权限校验

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface VerifyAuthority {

    String value() default "";
}
@Aspect
@Component
@Slf4j
public class VerifyAuthorityAspect {
    
    @Pointcut(value = "@annotation(verifyAuthority)")
    public void pointcut(VerifyAuthority verifyAuthority) {
    }
    
    @Around(value = "pointcut(verifyAuthority)")
    public Object doAround(ProceedingJoinPoint joinPoint, VerifyAuthority verifyAuthority) throws Throwable {
        // 获取注解中的值
        String needResourcePermission = verifyAuthority.value();
        // .... 执行相应的业务逻辑
        
        return joinPoint.proceed();
    }
    
}

或者使用反射来拿到注解

@Around(value = "pointcut(verifyAuthority)")
    public Object doAround(ProceedingJoinPoint joinPoint, VerifyAuthority verifyAuthority) throws Throwable {
        // 获取方法签名
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        // 获取方法
        Method method = methodSignature.getMethod();
        // 获取方法上面的注解
        VerifyAuthority verifyAuthority = method.getAnnotation(VerifyAuthority.class);
        // 获取注解中的值
        String needResourcePermission = verifyAuthority.value();
        // .... 执行相应的业务逻辑
        
        return joinPoint.proceed();
    }

Q:那你说说你对反射的理解

A:反射能在运行状态下获取类和对象的信息,并且可以构造对象,调用对象的方法。写工具类或者框架的时候常用。反射常用的类有

  • Class
  • Method
  • Constructor
  • Filed

获取Class对象有三种方式:

  • Class.forName(“xxx.Student”):通过类的全路径名获取Class对象
  • Student.class:通过类的class属性
  • student.getClass():通过对象的getClass()函数。

代理模式:

在不改变原始类(被代理类)的代码的情况下,通过引入代理类来给原始类增加功能。代理分静态代理和动态代理。

静态代理实现方式:

  • 代理类和原始类实现相同的接口,代理类中调用原始类方法。

    缺点: 原始类没有定义接口或者原始类并不是我们开发维护的(使用的的第三方类库),我们没有办法修改原始类,给他重新定义一个接口。

  • 采用继承方式实现外部扩展,让代理类来继承原始类,然后扩展附加功能。

    缺点:代理类需要将原始类的所有方法都重新实现一遍,并且每个方法都增加类似的代码逻辑。如果要添加的附加功能的类不止一个,那么每个类都要创建一个代理类

动态代理:

事先不为每个原始类编写代理类,在运行的时候,动态的创建原始类对应的代理类,在系统中用代理类替换原始类。java已经提供了动态代理的语法,底层依赖反射语法。

public class UserControllerProxy {
    
    public Object createProxy(Object proxyObject) {
        DynamicProxyHandler dynamicProxyHandler = new DynamicProxyHandler(proxyObject);
        return Proxy.newProxyInstance(proxyObject.getClass().getClassLoader(), proxyObject.getClass().getInterfaces(), dynamicProxyHandler);
    }

    private class DynamicProxyHandler implements InvocationHandler {
        private Object proxiedObject;

        public DynamicProxyHandler(Object proxiedObject) {
            this.proxiedObject = proxiedObject;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // ... 其他逻辑
            // ...

            // 执行原来的逻辑
            Object result = method.invoke(proxiedObject, args);

            // ... 其他逻辑
            // ...

            return result;
        }
    }

    public static void main(String[] args) {
        UserControllerProxy userControllerProxy = new UserControllerProxy();
        UserControllerInterface userControllerInterface = (UserControllerInterface) userControllerProxy.createProxy(new UserController());
    }
}

Q:那你在说说建造者模式

A:使用场景-校验必填参数

如果我们创建对象的时候有些属性是必填的,要达到这个要求,可以通过构造函数设置必填项,通过set()方法设置可选项。但是下面这三种场景才是Builder模式的战场。

  • 如果必填参数很多,把必填配置放到构造函数中设置,那么构造函数的参数列表就会很长。如果必填项也通过set()方法设置,那么校验必填项逻辑就无处安放。
  • 配置项之间有依赖关系,例如获奖为true,就必须添加描述。
  • 想要创建不可变对象,在对象创建好之后就不能再修改内部属性值。

在build()方法中先做校验,通过之后再创建对象。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值