借鉴:http://jinnianshilongnian.iteye.com/blog/1508018

基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Trasactional注解的事务管理,但在通过基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务是有区别的,我们接下来看看到底有哪些区别。

一、基础工作

实例SpringMVC + Spring4.3.8 + Mybatis3.2.6 + Logback 的项目,如下所示:

  1. 将xml声明式事务删除

    1. <!-- 切面 -->
    2. <tx:advice id="txAdvice" transaction-manager="transactionManager">
    3. <tx:attributes >
    4. <tx:method name="delete*" propagation="REQUIRED" />
    5. <tx:method name="insert*" propagation="REQUIRED" />
    6. <tx:method name="update*" propagation="REQUIRED" />
    7. <tx:method name="*" propagation="SUPPORTS" read-only="true"/>
    8. </tx:attributes>
    9. </tx:advice>
    10. <aop:config >
    11. <aop:pointcut expression="execution(* com.only.mate.service.*.*(..))" id="serviceCutPoint"/>
    12. <aop:advisor advice-ref="txAdvice" pointcut-ref="serviceCutPoint"/>
    13. </aop:config>
  2. 并添加注解式事务支持
    1. <!-- 编程式即采用注解的方式,事务扫描开始(开启注解@Tranctional) -->
    2. <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false" />
  3. 在我们的UserService接口上添加 @Transactional 使该方法开启事务
    1. package com.only.mate.service;
    2.  
    3. import org.springframework.transaction.annotation.Transactional;
    4.  
    5. import com.only.mate.entity.User;
    6.  
    7. public interface UserService {
    8. public User findOne(String username);
         @Transactional
    9. public void save(User user);
    10. }
    1. package com.only.mate.service.impl;
    2.  
    3. import org.springframework.beans.factory.annotation.Autowired;
    4. import org.springframework.stereotype.Service;
    5. import org.springframework.transaction.annotation.Transactional;
    6.  
    7. import com.only.mate.entity.User;
    8. import com.only.mate.repository.UserMapper;
    9. import com.only.mate.service.UserService;
    10.  
    11. @Service
    12. public class UserServiceImpl implements UserService {
    13. @Autowired
    14. private UserMapper userMapper;
    15.  
    16. @Override
    17. public User findOne(String username) {
    18. return userMapper.getUserByUserName(username);
    19. }
    20.  
    21. @Override
    22. public void save(User user) {
    23. userMapper.insert(user);
    24. if("zhangsan".equals(user.getUserName())){
    25. throw new RuntimeException();
    26. }
    27. }
    28. }
  4. 开启Logback日志的sql输出

  准备完毕

二、基于JDK动态代理

  1. <!-- 编程式即采用注解的方式,事务扫描开始(开启注解@Tranctional) -->
  2. <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="false" />

@Transactional注解放置在接口(抽象类或抽象方法)和具体类(实现类或实现方法)上都可以,具体解析请看"七、问题"。

运行访问,核心日志如下:

  1. 2017-11-08 16:36:52.231 耗时:1392016 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Acquired Connection [com.mysql.jdbc.JDBC4Connection@4f5672d1] for JDBC transaction
  2. 2017-11-08 16:36:52.235 耗时:1392020 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f5672d1] to manual commit
  3. Creating a new SqlSession
  4. Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ac24ea4]
  5. JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f5672d1] will be managed by Spring
  6. ==> Preparing: insert into user (id, username, password, sex, age, telphone, address) values (?, ?, ?, ?, ?, ?, ?)
  7. ==> Parameters: null, zhangsan(String), 123(String), 男(String), 13(Integer), 14444444444(String), 12313213123123(String)
  8. <== Updates: 1
  9. Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ac24ea4]
  10. Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ac24ea4]
  11. Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ac24ea4]
  12. 2017-11-08 16:36:52.304 耗时:1392089 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Initiating transaction rollback
  13. 2017-11-08 16:36:52.304 耗时:1392089 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@4f5672d1]
  14. 2017-11-08 16:36:52.357 耗时:1392142 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@4f5672d1] after transaction
  15. 2017-11-08 16:36:52.357 耗时:1392142 日志来自:o.springframework.jdbc.datasource.DataSourceUtils 日志类型: DEBUG 日志内容:Returning JDBC Connection to DataSource

到此我们可以看到事务起作用了,事务有回滚,也就是说即使把@Transactional放到接口上 基于JDK动态代理也是可以工作的。

三、基于CGLIB类代理

  1. <!-- 编程式即采用注解的方式,事务扫描开始(开启注解@Tranctional) -->
  2. <tx:annotation-driven transaction-manager="transactionManager" proxy-target-class="true" />

该配置方式是基于CGLIB类代理

运行访问,核心日志如下:

  1. 2017-11-08 16:44:58.079 耗时:8198 日志来自:org.springframework.web.servlet.DispatcherServlet 日志类型: DEBUG 日志内容:DispatcherServlet with name 'springServlet' processing POST request for [/SpringMVC/user/save]
  2. 2017-11-08 16:44:58.088 耗时:8207 日志来自:o.s.w.s.m.a.DefaultAnnotationHandlerMapping 日志类型: DEBUG 日志内容:Mapping [/user/save] to HandlerExecutionChain with handler [com.only.mate.controller.UserController@4731f9a] and 1 interceptor
  3. 2017-11-08 16:44:58.111 耗时:8230 日志来自:org.springframework.web.cors.DefaultCorsProcessor 日志类型: DEBUG 日志内容:Skip CORS processing: request is from same origin
  4. 2017-11-08 16:44:58.119 耗时:8238 日志来自:o.s.w.bind.annotation.support.HandlerMethodInvoker 日志类型: DEBUG 日志内容:Invoking request handler method: public java.util.Map com.only.mate.controller.UserController.save(javax.servlet.http.HttpServletRequest,javax.servlet.http.HttpServletResponse)
  5. 2017-11-08 16:44:58.121 耗时:8240 日志来自:com.only.mate.controller.UserController 日志类型: DEBUG 日志内容:注册,用户名zhangsan,密码123
  6. Creating a new SqlSession
  7. SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@158e6b7f] was not registered for synchronization because synchronization is not active
  8. 2017-11-08 16:44:58.164 耗时:8283 日志来自:o.springframework.jdbc.datasource.DataSourceUtils 日志类型: DEBUG 日志内容:Fetching JDBC Connection from DataSource
  9. 2017-11-08 16:44:58.453 耗时:8572 日志来自:com.alibaba.druid.pool.DruidDataSource 日志类型: INFO 日志内容:{dataSource-1} inited
  10. JDBC Connection [com.mysql.jdbc.JDBC4Connection@39d16e44] will not be managed by Spring
  11. ==> Preparing: insert into user (id, username, password, sex, age, telphone, address) values (?, ?, ?, ?, ?, ?, ?)
  12. ==> Parameters: null, zhangsan(String), 123(String), 男(String), 13(Integer), 14444444444(String), 12313213123123(String)
  13. <== Updates: 1
  14. Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@158e6b7f]
  15. 2017-11-08 16:44:58.556 耗时:8675 日志来自:o.springframework.jdbc.datasource.DataSourceUtils 日志类型: DEBUG 日志内容:Returning JDBC Connection to DataSource

到此我们可以看到事务没有起作用,事务没有回滚。

只有将注解放在具体类上或具体类的实现方法上才会起作用。

  1. package com.only.mate.service;
  2.  
  3. import com.only.mate.entity.User;
  4.  
  5. public interface UserService {
  6. public User findOne(String username);
  7. public void save(User user);
  8. }
  1. package com.only.mate.service.impl;
  2.  
  3. import org.springframework.beans.factory.annotation.Autowired;
  4. import org.springframework.stereotype.Service;
  5. import org.springframework.transaction.annotation.Transactional;
  6.  
  7. import com.only.mate.entity.User;
  8. import com.only.mate.repository.UserMapper;
  9. import com.only.mate.service.UserService;
  10.  
  11. @Service
  12. public class UserServiceImpl implements UserService {
  13. @Autowired
  14. private UserMapper userMapper;
  15.  
  16. @Override
  17. public User findOne(String username) {
  18. return userMapper.getUserByUserName(username);
  19. }
  20.  
  21. @Override
    @Transactional
  22. public void save(User user) {
  23. userMapper.insert(user);
  24. if("zhangsan".equals(user.getUserName())){
  25. throw new RuntimeException();
  26. }
  27. }
  28. }

运行访问,核心日志如下:

  1. 2017-11-08 16:49:54.992 耗时:3784 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Acquired Connection [com.mysql.jdbc.JDBC4Connection@51e88ff6] for JDBC transaction
  2. 2017-11-08 16:49:54.997 耗时:3789 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Switching JDBC Connection [com.mysql.jdbc.JDBC4Connection@51e88ff6] to manual commit
  3. Creating a new SqlSession
  4. Registering transaction synchronization for SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ff32a24]
  5. JDBC Connection [com.mysql.jdbc.JDBC4Connection@51e88ff6] will be managed by Spring
  6. ==> Preparing: insert into user (id, username, password, sex, age, telphone, address) values (?, ?, ?, ?, ?, ?, ?)
  7. ==> Parameters: null, zhangsan(String), 123(String), 男(String), 13(Integer), 14444444444(String), 12313213123123(String)
  8. <== Updates: 1
  9. Releasing transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ff32a24]
  10. Transaction synchronization deregistering SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ff32a24]
  11. Transaction synchronization closing SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@2ff32a24]
  12. 2017-11-08 16:49:55.070 耗时:3862 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Initiating transaction rollback
  13. 2017-11-08 16:49:55.070 耗时:3862 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Rolling back JDBC transaction on Connection [com.mysql.jdbc.JDBC4Connection@51e88ff6]
  14. 2017-11-08 16:49:55.124 耗时:3916 日志来自:o.s.jdbc.datasource.DataSourceTransactionManager 日志类型: DEBUG 日志内容:Releasing JDBC Connection [com.mysql.jdbc.JDBC4Connection@51e88ff6] after transaction
  15. 2017-11-08 16:49:55.124 耗时:3916 日志来自:o.springframework.jdbc.datasource.DataSourceUtils 日志类型: DEBUG 日志内容:Returning JDBC Connection to DataSource

运行测试类,将发现成功了。

还有一种情况如下:

  1. package com.only.mate.service.impl;
  2.  
  3. @Service
  4. public class StudentServiceImpl extends UserServiceImpl implements StudentService {
  5. @Autowired
  6. private UserMapper userMapper;
  7.  
  8. @Override
  9. public User findOne(String username) {
  10. return userMapper.getUserByUserName(username);
  11. }
  12. //没有@Transactional 
  13. @Override
  14. public void save(User user) {
  15. userMapper.insert(user);
  16. if("zhangsan".equals(user.getUserName())){
  17. throw new RuntimeException();
  18. }
  19. }
  20. }

这种情况也将无法织入事务。

四、基于aspectj的

  1. <tx:annotation-driven transaction-manager="transactionManager" mode="aspectj" proxy-target-class="true"/>

在此就不演示了,我们主要分析基于JDK动态代理和CGLIB类代理两种的区别。

五、结论

基于JDK动态代理 ,可以将@Transactional放置在接口(抽象类或抽象方法)和具体类实现类或实现方法)上。

基于CGLIB类代理,只能将@Transactional放置在具体类实现类或实现方法)上。

因此 在实际开发时全部将@Transactional放到具体类上,而不是接口上。

六、分析

1、  JDK动态代理

1.1、Spring使用JdkDynamicAopProxy实现代理:

  1. package org.springframework.aop.framework;
  2. final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {
  3. //注意此处的method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的)
  4. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  5. }
  6. }

注意此处的method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的)

1.2、如果<tx:annotation-driven 中 proxy-target-class="true" ,Spring将使用CGLIB动态代理,而内部通过Cglib2AopProxy实现代理,而内部通过DynamicAdvisedInterceptor进行拦截:

  1. package org.springframework.aop.framework;
  2. final class Cglib2AopProxy implements AopProxy, Serializable {
  3. private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
  4. //注意此处的method 一定是具体类上的method(因此只用放置在具体类上的@Transactional是可以发现的)
  5. public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  6. }
  7. }
  8. }

1.3、Spring使用AnnotationTransactionAttributeSource通过查找一个类或方法是否有@Transactional注解事务来返回TransactionAttribute(表示开启事务):

  1. package org.springframework.transaction.annotation;
  2. public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
  3. protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
  4. for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
  5. TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
  6. if (attr != null) {
  7. return attr;
  8. }
  9. }
  10. return null;
  11. }
  12. }

而AnnotationTransactionAttributeSource又使用SpringTransactionAnnotationParser来解析是否有@Transactional注解:

  1. package org.springframework.transaction.annotation;
  2.  
  3. public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
  4.  
  5. public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
  6. Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
  7. if (ann != null) {
  8. return parseTransactionAnnotation(ann);
  9. }
  10. else {
  11. return null;
  12. }
  13. }
  14.  
  15. public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
  16.  
  17. }
  18.  
  19. }

  此处使用AnnotationUtils.getAnnotation(ae, Transactional.class); 这个方法只能发现当前方法/类上的注解,不能发现父类的注解。 Spring还提供了一个 AnnotationUtils.findAnnotation()方法 可以发现父类/父接口中的注解(但spring没有使用该接口)。

  如果Spring此处换成AnnotationUtils.findAnnotation(),将可以发现父类/父接口中的注解。

七、问题

  我们之前说过,基于JDK动态代理时, method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的),但现在我们放在具体类上,那么Spring是如何发现的呢??

  TransactionAttribute是通过AnnotationTransactionAttributeSource返回的(具体看步骤1.3),而AnnotationTransactionAttributeSource 继承AbstractFallbackTransactionAttributeSource

  1. package org.springframework.transaction.interceptor;
  2. public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
  3.  
  4. public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
  5. //第一次 会委托给computeTransactionAttribute
  6. }
  7.  
  8. //计算TransactionAttribute的
  9. private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
  10.  
  11. //省略
  12.  
  13. // Ignore CGLIB subclasses - introspect the actual user class.
  14. Class<?> userClass = ClassUtils.getUserClass(targetClass);
  15. // The method may be on an interface, but we need attributes from the target class.
  16. // If the target class is null, the method will be unchanged.
  17. //①此处将查找当前类覆盖的方法
  18. Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
  19. // If we are dealing with method with generic parameters, find the original method.
  20. specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
  21.  
  22. // First try is the method in the target class.
  23. TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
  24. if (txAtt != null) {
  25. return txAtt;
  26. }
  27.  
  28. //找类上边的注解
  29. // Second try is the transaction attribute on the target class.
  30. txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
  31. if (txAtt != null) {
  32. return txAtt;
  33. }
  34. //②如果子类覆盖的方法没有 再直接找当前传过来的
  35. if (specificMethod != method) {
  36. // Fallback is to look at the original method.
  37. txAtt = findTransactionAttribute(method);
  38. if (txAtt != null) {
  39. return txAtt;
  40. }
  41. // Last fallback is the class of the original method.
  42. return findTransactionAttribute(method.getDeclaringClass());
  43. }
  44. return null;
  45. }
  46. }

//①此处将查找子类覆盖的方法

Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);

  1. // ClassUtils.getMostSpecificMethod
  2. public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
  3. Method specificMethod = null;
  4. if (method != null && isOverridable(method, targetClass) &&
  5. targetClass != null && !targetClass.equals(method.getDeclaringClass())) {
  6. try {
          //可以看出将找到当前类的那个方法。因此我们放置在UserService save方法上的@Transactional起作用了。
  7. specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());
  8. } catch (AccessControlException ex) {
  9. // security settings are disallowing reflective access; leave
  10. // 'specificMethod' null and fall back to 'method' below
  11. }
  12. }
  13. return (specificMethod != null ? specificMethod : method);
  14. }

因此,建议大家使用基于Schema风格的事务(不用考虑这么多问题,也不用考虑是类还是方法)。而@Transactional建议放置到具体类上,不要放置到接口。

Spring -- <tx:annotation-driven>注解基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)的区别。的更多相关文章

  1. 基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  2. Spring <tx:annotation-driven>注解 JDK动态代理和CGLIB动态代理 区别。

    基于JDK动态代理和CGLIB动态代理的实现Spring注解管理事务(@Trasactional)到底有什么区别. 我还是喜欢基于Schema风格的Spring事务管理,但也有很多人在用基于@Tras ...

  3. java的静态代理、jdk动态代理和cglib动态代理

    Java的代理就是客户端不再直接和委托类打交道,而是通过一个中间层来访问,这个中间层就是代理.使用代理有两个好处,一是可以隐藏委托类的实现:二是可以实现客户与委托类之间的解耦,在不修改委托类代码的情况 ...

  4. 代理模式之静态代理,JDK动态代理和cglib动态代理

    代理模式,顾名思义,就是通过代理去完成某些功能.比如,你需要购买火车票,不想跑那么远到火车站售票窗口买,可以去附近的火车票代售点买,或者到携程等第三方网站买.这个时候,我们就把火车站叫做目标对象或者委 ...

  5. JDK动态代理和CGLib动态代理简单演示

    JDK1.3之后,Java提供了动态代理的技术,允许开发者在运行期间创建接口的代理实例. 一.首先我们进行JDK动态代理的演示. 现在我们有一个简单的业务接口Saying,如下: package te ...

  6. jdk动态代理和cglib动态代理底层实现原理详细解析(cglib动态代理篇)

    代理模式是一种很常见的模式,本文主要分析cglib动态代理的过程 1. 举例 使用cglib代理需要引入两个包,maven的话包引入如下 <!-- https://mvnrepository.c ...

  7. jdk动态代理和cglib动态代理的区别

    一.原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件 ...

  8. jdk 动态代理和 cglib 动态代理

    原理区别: java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理. 而cglib动态代理是利用asm开源包,对代理对象类的class文件加载 ...

  9. 动态代理:jdk动态代理和cglib动态代理

    /** * 动态代理类:先参考代理模式随笔,了解代理模式的概念,分为jdk动态代理和cglib,jdk动态代理是通过实现接口的方式创建代理类的,cglib是通过继承类的方式实现的代理类的 * jdk动 ...

随机推荐

  1. 一般xcode报错

    下面这图报找不到FMDtabaseQueue,其实是把文件拖入xcode的时候xcode没有把.m文件添加进工程路径 解决办法:build phases-> compile sources &q ...

  2. 周强 201771010141《面向对象程序设计(java)》第四周学习总结

    实验目的与要求 (1) 理解用户自定义类的定义: (2) 掌握对象的声明: (3) 学会使用构造函数初始化对象: (4) 使用类属性与方法的使用掌握使用: (5) 掌握package和import语句 ...

  3. Python全栈之路----hash函数

    hash函数: 一般翻译为“散列”或“哈希”,就是把任意长度的输入,通过散列算法,变换成固定长度的输出,该输出就是散列值.这种转换是一种压缩映射,也就是,散列值的空间通常远小于输入的空间,不同的输入可 ...

  4. Json序列化,有多对一和多对多关系时出现的问题

    /** * 这是一张 单表中的双向一对多,多对一 关系.自己跟自己一对多多对一 */ @Entity @Table(name="tb_test") public class Cre ...

  5. 用python 实现一个栈

    前言 Python本身已有顺序表(List.Tupple)的实现,所以这里从栈开始. 什么是栈 想象一摞被堆起来的书,这就是栈.这堆书的特点是,最后被堆进去的书,永远在最上面.从这堆书里面取一本书出来 ...

  6. javascript将浮点数转换成整数

    Summary 临时我就想到3个方法而已.假设读者想到其它好用方法,也能够交流一下 parseInt 位运算符 Math.floor Math.ceil Description 一.parseInt ...

  7. CANVAS笔记

    bglayer.add(bgimage) /*后面的层会覆盖前面的,所以要放在上面的,就要后面才添加!*/ bglayer.add(this.shape1) Layer.destroy() layer ...

  8. “数据上帝” Jeff Hammerbacher

    出生于1983年的数学天才Jeff Hammerbacher在23岁时加入了Facebook,一手组建起数据分析队伍.他是“数据科学”(data science)一词的提出者之一,被人们称为“数据上帝 ...

  9. OpenLDAP一登录系统就修改密码

    1:修改配置文件 在前面打开注释 moduleload ppolicy.la modulepath /usr/lib/openldap modulepath /usr/lib64/openldap   ...

  10. LOJ 3059 「HNOI2019」序列——贪心与前后缀的思路+线段树上二分

    题目:https://loj.ac/problem/3059 一段 A 选一个 B 的话, B 是这段 A 的平均值.因为 \( \sum (A_i-B)^2 = \sum A_i^2 - 2*B \ ...