当前位置: 首页 > news >正文

做美图+网站有哪些青岛官网优化

做美图+网站有哪些,青岛官网优化,子页网站设计,设计培训文章目录 1. JDK动态代理和CGLIB动态代理的区别1.1 适用范围1.2 生成的代理类1.3 调用方式 2. 问题引入3. 创建工程验证 Spring 默认采用的动态代理机制3.1 引入 Maven 依赖3.2 UserController.java3.3 UserService.java3.4 UserServiceImpl.java#xff08;save方法添加了Tra… 文章目录 1. JDK动态代理和CGLIB动态代理的区别1.1 适用范围1.2 生成的代理类1.3 调用方式 2. 问题引入3. 创建工程验证 Spring 默认采用的动态代理机制3.1 引入 Maven 依赖3.2 UserController.java3.3 UserService.java3.4 UserServiceImpl.javasave方法添加了Transactional注解3.5 User.java3.6 编写配置文件 4. 测试结果及测试结果分析4.1 测试结果4.2 测试结果分析4.3 Spring Framework工程和SpringBoot工程中分别使用哪种动态代理4.4 源码分析4.5 手动指定 AOP 使用哪种动态代理 5. 为什么SpringBoot默认会选用CGLIB动态代理JDK动态代理的局限性5.1 使用JDK动态代理时目标对象必须要实现接口5.2 使用JDK动态代理时必须要用接口来接收5.3 使用JDK动态代理时获取不到实现类方法上的注解 6. 补充调试时this指针指的是哪个对象7. 总结8. 参考资料 1. JDK动态代理和CGLIB动态代理的区别 1.1 适用范围 如果要使用 JDK 动态代理必须要保证目标类实现了接口如果要使用 CGLIB 动态代理目标类不能使用 final 关键字进行修饰也就是说目标类是可继承的 1.2 生成的代理类 JDK 动态代理生成的代理对象会实现目标类实现的所有接口这也是 JDK 动态代理为什么要保证目标类实现了接口的原因CGLIB 动态代理生成的代理对象会继承目标类所以说目标类不能使用 final 关键字进行修饰因为一个类加了final 关键字就不能被继承了 1.3 调用方式 JDK 动态代理是采用反射的方式来调用目标方法而 CGLIB 是通过子类直接调用父类的方式来调用目标方法 2. 问题引入 Spring 框架在创建代理对象时默认情况下如果目标对象实现了至少一个接口那么会使用 JDK 动态代理如果目标对象没有实现任何接口则会使用 CGLIB 动态代理 以上内容相信大家都非常熟悉然而事实真的是这样吗我们创建一个工程来验证一下 3. 创建工程验证 Spring 默认采用的动态代理机制 工程环境SpringBoot 3.0.2 JDK 17.0.7 3.1 引入 Maven 依赖 为了方便测试我们引入以下依赖 Spring WebMyBatis FrameworkaspectjweaverMySQL 连接驱动 dependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId /dependencydependencygroupIdorg.mybatis.spring.boot/groupIdartifactIdmybatis-spring-boot-starter/artifactIdversion3.0.2/version /dependencydependencygroupIdorg.aspectj/groupIdartifactIdaspectjweaver/artifactId /dependencydependencygroupIdcom.mysql/groupIdartifactIdmysql-connector-j/artifactIdscoperuntime/scope /dependency我们创建几个简单的类验证 Spring 默认采用的动态代理机制 3.2 UserController.java import cn.edu.scau.pojo.User; import cn.edu.scau.service.UserService; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController;RestController RequestMapping(/user) public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService userService;}PostMapping(/save)public void save(RequestBody User user) {userService.save(user);}}3.3 UserService.java import cn.edu.scau.pojo.User;public interface UserService {void save(User user);}3.4 UserServiceImpl.javasave方法添加了Transactional注解 import cn.edu.scau.pojo.User; import cn.edu.scau.service.UserService; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional;Service public class UserServiceImpl implements UserService {Transactionalpublic void save(User user) {System.out.println(保存用户 user);}}3.5 User.java public class User {}3.6 编写配置文件 application.yml spring:datasource:url: jdbc:mysql://localhost:3306/blog?useUnicodetruecharacterEncodingutf-8useSSLfalseserverTimezoneAsia/ShanghaiallowPublicKeyRetrievaltrueusername: rootpassword: 123456driver-class-name: com.mysql.cj.jdbc.Driver4. 测试结果及测试结果分析 4.1 测试结果 我们在 controller 层打上断点以 debug 的方法访问接口 可以看到UserService 的类名中出现了 CGLIB 关键字说明采用的是 CGLIB 动态代理 怎么回事呢明明我的 UserServiceImpl 是实现了 UserService 接口的为什么会使用 CGLIB 动态代理呢 4.2 测试结果分析 在 Spring Framework 中是使用 EnableAspectJAutoProxy 注解来开启 Spring AOP 相关功能的 我们查看一下EnableAspectJAutoProxy 注解类的源码 在这里插入图片描述 通过源码的注释我们可以了解到在 Spring Framework 中proxyTargetClass 的默认取值是 falsefalse 表示不启用 CGLIB 动态代理也就是说Spring Framework 默认采用的还是使用 JDK 动态代理 难道说Spring 源码的注释写错了 为了进一步进行测试我们在项目的启动类上手动指定使用 JDK 动态代理 EnableAspectJAutoProxy(proxyTargetClass false)可以发现Spring 默认使用的就是 JDK 动态代理 打断点后再次进行测试发现使用的还是 CGLIB 动态代理 难道 EnableAspectJAutoProxy 的 proxyTargetClass 属性无效了 4.3 Spring Framework工程和SpringBoot工程中分别使用哪种动态代理 等一等我们是不是遗漏了什么 我们的测试工程使用的 SpringBoot 环境那如果不用 SpringBoot只用 Spring Framework 会怎么样呢 测试过程与上述测试相同就不再赘述了 测试结果表明在 Spring Framework 中如果类实现了接口默认还是使用 JDK 动态代理 4.4 源码分析 结果上面的分析很有可能是 SpringBoot 修改了 Spring AOP 的相关配置那我们就来一波源码分析看一下 SpringBoot 内部到底做了什么 源码分析找对入口很重要。那这次的入口在哪里呢 SpringBootApplication 是一个组合注解该注解中使用 EnableAutoConfiguration 实现了大量的自动装配 EnableAutoConfiguration 也是一个组合注解在该注解上被标志了 Import AutoConfigurationImportSelector 类实现了 DeferredImportSelector 接口 DeferredImportSelector 接口中有一个 getImportGroup 方法AutoConfigurationImportSelector 类重写了该方法 getImportGroup 方法返回了AutoConfigurationGroup 类AutoConfigurationGroup 是 AutoConfigurationImportSelector 中的一个私有内部类实现了 DeferredImportSelector.Group 接口 SpringBoot 就是通过 AutoConfigurationImportSelector.AutoConfigurationGroup 类的 process 方法来导入自动配置类的 我们在 process 方法打上断点重新启动 SpringBoot 测试工程 通过调试信息我们可以看到与 AOP 相关的自动配置是通过 org.springframework.boot.autoconfigure.aop.AopAutoConfiguration 类来进行配置的 我们查看 AopAutoConfiguration 类的源码发现如果我们没有在配置文件中指定使用哪种代理默认就会使用 CGLIB 动态代理 4.5 手动指定 AOP 使用哪种动态代理 通过源码我们也就可以知道在 SpringBoot 中如果需要指定 AOP 使用哪种动态代理需要通过 spring.aop.proxy-target-class 这个配置项来修改 在 application.yml 文件中通过 spring.aop.proxy-target-class 属性来配置 在编写文件时我们也可以看到proxy-target-class 属性默认为 true 我们改为 false 之后再次启动测试工程发现已经使用了 JDK 动态代理 5. 为什么SpringBoot默认会选用CGLIB动态代理JDK动态代理的局限性 为什么 SpringBoot 默认会选用 CGLIB 动态代理呢当然是因为 JDK 动态代理存在一定的局限性 5.1 使用JDK动态代理时目标对象必须要实现接口 如果要使用 JDK 动态代理必须要保证目标类实现了接口这是由 JDK 动态代理的实现原理决定的 5.2 使用JDK动态代理时必须要用接口来接收 我们现在有 UserServiceImpl 和 UserService 两个类此时需要在 UserContoller 中注入 UserService 类 我们通常都习惯这样写代码 Autowired private UserService userService;在这种情况下无论是使用 JDK 动态代理还是 CGLIB 动态代理都不会出现问题 但是如果代码是这样的呢 Autowired private UserServiceImpl userService;这个时候如果我们是使用 JDK 动态代理那在启动时就会报错 The bean is of type ‘jdk.proxy2.$Proxy59’ and implements: cn.edu.scau.service.UserService org.springframework.aop.SpringProxy org.springframework.aop.framework.Advised org.springframework.core.DecoratingProxy Expected a bean of type ‘cn.edu.scau.service.impl.UserServiceImpl’ which implements: cn.edu.scau.service.UserService Action: Consider injecting the bean as one of its interfaces or forcing the use of CGLib-based proxies by setting proxyTargetClasstrue on EnableAsync and/or EnableCaching. 因为 JDK 动态代理是基于接口的代理生成的对象只能赋值给接口变量 而 CGLIB 就不存在这个问题因为 CGLIB 是通过生成目标对象子类来实现的代理对象无论是赋值给接口还是实现类两者都是代理对象的父类使用 CGLIB 动态代理可以避免类型转换导致的错误 5.3 使用JDK动态代理时获取不到实现类方法上的注解 我们在 UserServiceImpl 类的 save 方法上添加一个自定义的 Log 注解 import java.lang.annotation.*;Target({ElementType.PARAMETER, ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) Documented public interface Log {/*** 操作名称*/String name() default ;}同时对方法上带有 Log 注解的类进行增强 import cn.edu.scau.annotation.Log; import jakarta.servlet.http.HttpServletRequest; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.Signature; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.stereotype.Component; import org.springframework.web.context.request.RequestAttributes; import org.springframework.web.context.request.RequestContextHolder; import org.springframework.web.context.request.ServletRequestAttributes;import java.lang.reflect.Method; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter;/*** 切面类*/ Component Aspect public class SystemAspect {Pointcut(annotation(cn.edu.scau.annotation.Log))private void pointcut() {}Around(pointcut())public Object around(ProceedingJoinPoint joinPoint) throws Throwable {// 通过解析 session 或 token 获取用户名// 获取被增强类和方法的信息Signature signature joinPoint.getSignature();MethodSignature methodSignature (MethodSignature) signature;// 获取被增强的方法对象Method method methodSignature.getMethod();// 从方法中解析注解if (method ! null) {Log logAnnotation method.getAnnotation(Log.class);System.out.println(模块名称 logAnnotation.name());}// 方法名字String name null;if (method ! null) {name method.getName();}System.out.println(方法名 name);// 通过工具类获取Request对象RequestAttributes requestAttributes RequestContextHolder.getRequestAttributes();ServletRequestAttributes servletRequestAttributes (ServletRequestAttributes) requestAttributes;HttpServletRequest request null;if (servletRequestAttributes ! null) {request servletRequestAttributes.getRequest();}// 访问的URLString url null;if (request ! null) {url request.getRequestURI();}System.out.println(访问的URL url);// 请求方式String methodName null;if (request ! null) {methodName request.getMethod();}System.out.println(请求方式 methodName);// 登录IPString ipAddress null;if (request ! null) {ipAddress getIpAddress(request);}System.out.println(登录IP ipAddress);// 操作时间System.out.println(操作时间 LocalDateTime.now().format(DateTimeFormatter.ofPattern(yyyy-MM-dd HH:mm:ss)));// 将操作日志保存到数据库return joinPoint.proceed();}/*** 获取 IP 地址** param request HttpServletRequest* return String*/public String getIpAddress(HttpServletRequest request) {String ip request.getHeader(x-forwarded-for);if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) {ip request.getHeader(Proxy-Client-IP);}if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) {ip request.getHeader(WL-Proxy-Client-IP);}if (ip null || ip.isEmpty() || unknown.equalsIgnoreCase(ip)) {ip request.getRemoteAddr();}return 0:0:0:0:0:0:0:1.equals(ip) ? 127.0.0.1 : ip;}}以 debug 的方式启动项目在切面中发现获取到的注解数量为 0获取不到任何注解因为 UserService 接口上并没有注解当然我们也可以在接口上添加注解但是这不符合我们的编码习惯 method.getAnnotations()改用 CGLIB 动态代理后再次以 debug 的方式启动项目在切面中发现获取到的注解数量为 2获取到了方法上的所有注解 6. 补充调试时this指针指的是哪个对象 哪个对象调用了this所在的代码块this 指针就指向哪个对象 如果我们在打断点调试时输出 this会发现 this 指针指向的不是代理对象为什么呢 因为代理对象只是对目标对象进行了增强真正调用目标方法还是需要目标对象 以 CGLIB 动态代理为例代理对象会保留目标对象的引用代理对象的简单结构如下 我们记住哪个对象调用了 this 所在的代码块this 指针就指向哪个对象 在进行断点调试时this 所在的代码块指向的对象就是目标对象而不是代理对象 7. 总结 在 Spring Framework 工程中 AOP 默认使用 JDK 动态代理为了解决使用 JDK 动态代理可能导致的类型转化异常问题SpringBoot 默认使用 CGLIB 动态代理在 SpringBoot 中如果需要默认使用 JDK 动态代理可以通过配置项 spring.aop.proxy-target-classfalse 来进行修改在启动类上通过 EnableAspectJAutoProxy(proxyTargetClass false) 配置已无效 8. 参考资料 小米二面为什么SpringBoot使用cglib作为默认动态代理 AOP使用jdk动态代理会有什么问题 _哔哩哔哩_bilibili什么鬼弃用JDK动态代理Spring5 默认使用 CGLIB 了|jdk|aop|boot|proxy|spring|framework_网易订阅 (163.com)腾讯二面Spring AOP底层实现原理是怎么样的JDK和CGlib动态代理有什么区别 _哔哩哔哩_bilibili
http://www.dnsts.com.cn/news/263717.html

相关文章:

  • 潍坊优化网站多久可以拿证
  • 有的网站打不开 但别人电脑能打开纪检网站建设计划书
  • 网站建设前期情况说明博客群wordpress
  • 网站建设更新不及时亚马逊雨林的资料
  • 东莞网站设计找哪里四平市住房和畅想建设局网站
  • 北京工信部网站监控网站模版
  • 移动端网站怎么制作高埗仿做网站
  • wordpress 标签id南昌专业网站优化推广
  • 手机网站开发屏幕尺寸一般是多少抖音代运营直播
  • 网站建设维护管理软件公司备案的网站被别的公司盗用
  • 网站淘宝推广怎么做买手表去哪个网站买是正品的
  • 自己买一个服务器怎么做网站网站建设费用的会计分录
  • 温州手机网站制作哪家好摄影做网站
  • 张家港营销型网站建设上海推广网站
  • 网站上怎么做推广比较好呢网站制作需要学什么
  • 沙田网站建设广东官网网站建设哪家好
  • 网站的内容建设网站建设服务属于信息技术服务吗
  • 网站建设方案硬件支撑网站通栏图片代码
  • 订单查询网站怎么做手机上怎么制作网页
  • 手机网站建设的整体流程图网站登录人太多进不去怎么办
  • 大岭山营销型网站建设网站制作 外包
  • 微网站与app的区别北京专业网站营销
  • 网站迁移到别的服务器要怎么做阿里云 wordpress 插件
  • 茶社网站开发与设计的开题报告手机网站前端写法
  • 一级a做爰片免费网站孕交视频教程手机端企业网站怎么做
  • 网站绑定别名好吗西安网站制作托
  • 一个网站项目几个人做北京快三下载官方网站
  • 网站里的图片切换怎么做有网站教做水电资料吗
  • 建设企业网站企业网上银行对公东莞建站网站建设产品推广
  • 网站用ps如何做wordpress菜单底部导航代码