注解、反射、动态代理
涉及到的知识点注解
、反射
、动态代理
,AOP
、Lombok
,代理模式
、`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()方法中先做校验,通过之后再创建对象。