Spring静态Bean的原理

最近遇到一个spring static bean的坑,我们知道使用Java Config的方式定义一个Bean 非常简单,只需在Configuration的method上加上 @Bean 注解即可。 但是这里有个例外,假如你的Bean不是一个普通的Bean,而是一个BeanFactoryPostProcessor就需要使用static方法来定义这个Bean。 否则你会得到一个警告: @Bean method TestConfig.customEditorConfigurer is non-static and returns an object assignable to Spring's BeanFactoryPostProcessor interface. This will result in a failure to process annotations such as @Autowired, @Resource and @PostConstruct within the method's declaring @Configuration class. Add the 'static' modifier to this method to avoid these container lifecycle issues; see @Bean javadoc for complete details. 也就是说,如果你的bean是一个BFPP,必须定义为static,否则,使用@Autowired, @Resource and @PostConstruct 会有问题。 来看 @Bean注解源码里的注释: 1234567891011121314Special consideration must be taken for @Bean methods that return Spring BeanFactoryPostProcessor (BFPP) types. Because BFPP objects must be instantiated very early in the container lifecycle,they can interfere with processing of annotations such as @Autowired, @Value, and @PostConstruct within @Configuration classes. To avoid these lifecycle issues, mark BFPP-returning @Bean methods as static. For example: @Bean public static PropertySourcesPlaceholderConfigurer pspc() { // instantiate, configure and return pspc ... } By marking this method as static, it can be invoked without causing instantiation of its declaring @Configuration class, thus avoiding the above-mentioned lifecycle conflicts. Note however that static @Bean methods will not be enhanced for scoping and AOP semantics as mentioned above. This works out in BFPP cases, as they are not typically referenced by other @Bean methods. As a reminder, a WARN-level log message will be issued for any non-static @Bean methods having a return type assignable to BeanFactoryPostProcessor. 因为BFPP都需要在在Spring容器的早期进行实例化,因为他们会干扰正常的Bean实例化中处理 @Autowired @Value @PostConstruct ,这篇Blog尝试寻找一下Static Bean背后的原理。

源码解析

Spring 事件驱动的原理

Spring事件驱动Spring 事件驱动的代码都位于spring-context 模块的event包中,主要包括:事件(Event)发布者() Publisher) ,订阅者(Listener)组成。 事件ApplicationEventjava的所有事件对象一般都是java.util.EventObject的子类,Spring的整个继承体系如下:

源码解析

Spring @Transactional是如何工作的?

Spring事务使用Spring配置事务还是挺简单的,第一步创建事务管理器TransactionManager,然后在配置中增加一个@EnableTransactionManagement就可以启用Spring事务了,所以关键类就是@EnableTransactionManagement 1234@Bean public DataSourceTransactionManager transactionManager() { return new DataSourceTransactionManager(dataSource()); } 我们可以看到@EnableTransactionManagement 上实际上是import了TransactionManagementConfigurationSelector类,在这个Selector中实际import了两个配置类: AutoProxyRegistrar ProxyTransactionManagementConfiguration 1234567891011121314@Override protected String[] selectImports(AdviceMode adviceMode) { switch (adviceMode) { case PROXY: return new String[] {AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName()}; case ASPECTJ: return new String[] {determineTransactionAspectClass()}; default: return null; } } 下面我们来根据这个入口来分析一下Spring是如何处理事务的:

源码解析

Java中final关键字总结

final在java中的用法有很多,可以修饰field,可以修饰Method,可以修饰Class,而且final在多线程环境中保证了对象状态的不变性,下面就系统的总结一下Java中final关键字的用法 修饰Variable/field 修饰primitive变量,变量一旦赋值就不再可变。 final修饰基本数据类型变量和String类型时,类似于C++的const 3种变量会被隐式的定义为final:3.1. 接口中的field是final的3.2. Java7中出现的try with resource语句中的变量是隐式的final类型,如下面的代码,inputStream虽然未被声明为final,但是如果试图在try块里面重新对inputStream赋值的话,就会产生编译异常,不能给final变量赋值 12345try (FileInputStream inputStream = new FileInputStream("text.txt")){inputStream = new FileInputStream("");} catch (Exception e) { e.printStackTrace();} 修饰引用实例类型变量,变量被赋值后,变量指向的引用的值可以变,但是不能重新指向新的引用,即final只关心引用本身,而不关心final引用的内容。 12345678public static void main(String[] args) { final User user = new User("xuan1",23); System.out.println(user.getAge()); //输出23 user.setAge(24); System.out.println(user.getAge()); //输出24 user = new User("xuan2",25); //编译错误,提示不能赋值给final变量 System.out.println(user.getAge());} 修饰实例成员变量时,必须在定义的时候初始化:直接赋值,构造器初始化,或代码块中初始化,或的意思是这三种方式只能选择一种,否则编译报错。 修饰静态成员变量时,必须在变量定义的时候初始化:直接赋值,静态代码块中赋值 Tips: 有一种特殊情况:System.in,System.out,System.err 是静态域但是没有在定义的时候或者静态代码块中初始化,而是使用了set方法来设置值。 JDK8以前内部类访问外部类的变量时要求变量为Final类型,JDK8之后,只要求外部类为事实不可变变量,不一定要加上final

java

Java中的null引用

空指针也许是java中最常见的异常,到处都埋藏着NullpointerException,最近就遇到一个NullPointException,如下: 1int lastMonthTotalScore = integralOperationReadMapper.getSumByIntegralIdAndDate(integralId, lastMonthDate); 一个很常见的情况,mybatis查询的一个列的和,此时Debug时 integralId、lastMonthDate 都不为空,自动注入的 integralOperationReadMapper也不为空但是Console却实实在在的打出了这一行有一个NullPointerException,此时没有注意到Wrapper类自动转换基本数据类型的情形。 getSumByIntegralIdAndDate 方法返回的是NULL,自动拆箱的时候的要将一个NULL转换为基本数据类型就出错了…o(╯□╰)o 现在总结几个NULL的经验。 1.不用null来返回方法的返回值 不要用null来舒适化变量,方法不要返回null、这样会造成null的传播,在每一个调用的地方都需要检查null 例如: 12345public String doSomething(int id){ String name = findName(id); ... return name;} 这样如果findName如果返回为null,那么null就由findname游走到了doSomething。比如在findname中,如果没有找到对应的Id的姓名,就应该表明是没找到,而不是出错了。 善于运用Java的异常。 1234567public String findName() throws NotFoundException { if (...) { return ...; } else { throw new NotFoundException(); } }

java

Executor任务执行框架的应用

最近一段时间没有写东西了,看大名鼎鼎的Brian Goetz写的Java Concurrency in Practice时候,看到任务执行框架Executor Framework的时候,觉得纸上得来终觉浅,索性写点东西加深一下印象。 在JDK1.5中,Java平台中增加了一个并发包java.util.concurrent,这个包中包含了Executor Framework,而且还包含了很多并发包,比如并发HashMapConcurrentHashMap、阻塞队列BlockQueue、栅栏的实现CyclicBarrier、信号量Semaphore、异步任务FutureTask等等。在处理多线程任务的时候,使用Executor和task要优于使用线程,这也不是我说的,是Effect Java的作者 Joshua Bloach说的,下面来阐述一下为什么。 并发任务执行当要执行一个并发任务的时候,通常有两种方式,一种是串行的处理方式,一种是并行的处理,显然,串行的方式只能一次处理一个任务,当程序在执行当前的任务的时候,就说明接下来到来的任务请求都要等待当前的任务执行完毕才能获得CPU去执行任务,这种方式虽然不会犯错,但是效率太低。那么,如果每一个任务到来都分配一个新的任务呢,这种方式貌似很好,但是: 如果任务请求量非常大的时候会出现一定的问题,因为它没有限制可以创建的线程的数量. 线程生命周期的开销很高 线程的创建和销毁不是没有代价的,根据平台的不同,开销不同,但是不要忘记,线程的创建是需要时间的。 活跃的线程会消耗系统资源 活跃的线程很消耗系统资源,尤其是内存,如果可运行的线程数量多于处理器核心数,那么多余的线程将闲置,但是闲置的线程仍然是消耗系统资源的,尤其 是内存,给GC回收垃圾带来压力,而且线程间在进行竞争的时候也会消耗大量的资源 平台可创建的线程数量是有限的 也就是说,如果创建的线程超出了平台的限制那么,JVM就可能抛出OutofMemoryError的异常

java

Java中使用DES对称加解密

DESDES(Data Encryption Standard),即数据加密算法。是IBM公司于1975年研究成功并公开发表的。DES算法的入口参数有三个:Key、Data、Mode。其中Key为8个字节共64位,是DES算法的工作密钥;Data也为8个字节64位,是要被加密或被解密的数据;Mode为DES的工作方式,有两种:加密或解密。 安卓端对请求Web服务器请求字符串进行加密加密公共方法: 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112package com.sz.kcygl.common.DESUtil;import java.security.Key;import java.security.SecureRandom;import java.security.spec.AlgorithmParameterSpec;import javax.crypto.Cipher;import javax.crypto.SecretKeyFactory;import javax.crypto.spec.DESKeySpec;import javax.crypto.spec.IvParameterSpec;import com.sun.org.apache.xml.internal.security.utils.Base64;public class DESUtil { public static final String ALGORITHM_DES = "DES/CBC/PKCS5Padding"; /** * DES算法,加密 * * @param data * 待加密字符串 * @param key * 加密私钥,长度不能够小于8位 * @return 加密后的字节数组,一般结合Base64编码使用 * @throws CryptException * 异常 */ public static String encode(String key, String data) throws Exception { return encode(key, data.getBytes()); } /** * DES算法,加密 * * @param data * 待加密字符串 * @param key * 加密私钥,长度不能够小于8位 * @return 加密后的字节数组,一般结合Base64编码使用 * @throws CryptException * 异常 */ public static String encode(String key, byte[] data) throws Exception { try { DESKeySpec dks = new DESKeySpec(key.getBytes()); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); // key的长度不能够小于8位字节 Key secretKey = keyFactory.generateSecret(dks); Cipher cipher = Cipher.getInstance(ALGORITHM_DES); IvParameterSpec iv = new IvParameterSpec("12345678".getBytes()); AlgorithmParameterSpec paramSpec = iv; cipher.init(Cipher.ENCRYPT_MODE, secretKey, paramSpec); byte[] bytes = cipher.doFinal(data); return Base64.encode(bytes); } catch (Exception e) { throw new Exception(e); } } /** * DES算法,解密 * * @param data * 待解密字符串 * @param key * 解密私钥,长度不能够小于8位 * @return 解密后的字节数组 * @throws Exception * 异常 */ public static byte[] decode(String key, byte[] data) throws Exception { try { SecureRandom sr = new SecureRandom(); DESKeySpec dks = new DESKeySpec(key.getBytes()); SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES"); // key的长度不能够小于8位字节 Key secretKey = keyFactory.generateSecret(dks); Cipher cipher = Cipher.getInstance(ALGORITHM_DES); IvParameterSpec iv = new IvParameterSpec("12345678".getBytes()); AlgorithmParameterSpec paramSpec = iv; cipher.init(Cipher.DECRYPT_MODE, secretKey, paramSpec); return cipher.doFinal(data); } catch (Exception e) { // e.printStackTrace(); throw new Exception(e); } } /** * 获取编码后的值 * * @param key * @param data * @return * @throws Exception * @throws Exception */ public static String decodeValue(String key, String data) throws Exception { byte[] datas; String value = null; datas = decode(key, Base64.decode(data)); value = new String(datas); if (value.equals("")) { throw new Exception(); } return value; }}

java

根据WebMagic写的一个爬取煎蛋网的小爬虫

之前研究jsoup,想用jsoup写一个小爬虫,爬煎蛋网的无聊图,我也是够无聊的 =.=,挖了个坑过了半个月还没填上,昨天上知乎的时候,发现有更加好用的爬虫框架WebMagic(知乎,果然让人发现更大的世界),先用WebMagic实现一下我的小爬虫,好啦,填坑开始… 这里用到webmagic,就把webmagic介绍,使用方法都放出来,没用过的先熟悉一下。 这里是WebMagic中文使用文档,一点即达 @.@ 介绍文档已经很详细了,下面开始,生产爬虫

java

本站由 Hank Zhao 使用 Stellar 主题创建。
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。
本站总访问量