很长一段时间关注在Java Web开发的方向上,说起到Jave Web开发就绕不开Spring全家桶系列,使用面向百度,谷歌的编程方法可以完成大部分的工做。可是这种不系统的了解总以为本身的知识有所欠缺。因此有了系统了解Spring的想法,了解了Spring,才可以更好的学习Spring全家桶系列,Spring的书籍也是琳琅满目,固然也能够阅读Spring官方的reference,相信那个才是最好的材料,可是鉴于英语的阅读速度有限。因此就挑选了这本《精通Spring 4.X企业应用开发实战》。这里记录一下读书笔记,主要是方便之后快速的查阅~html
以前零碎的信息了解到Spring的两大基础其实就是 IoC和AOP了,最近花了好久的时间阅读了《精通Spring 4.X企业应用开发实战》关于 DI 的这部份内容。仍是了解了很多内容,固然也产生了很多的疑问。java
IoC容器web
IoC(Inversion of Control,控制反转),另一个词DI(Dependency Injection,依赖注入),在Spring中能够把这两个词等价起来,纠结这些概念我以为没有必要,个人理解,它就是一种成熟的软件设计模式,可以实现软件开发的高内聚,低耦合。系统在改动,扩展起来可以轻松应对。网络上面不少关于这个概念的解释,他们也都举例说明了它的好处,可是都是很小的实例,不是一个复杂的系统对于减轻应对改动、扩展的功效印象并不深入。因此仍是应该潜心码代码,当本身码的代码到达必定的规模,天然而然应该就会了解到IoC的概念,设计模式们的优雅之处了。 面试
用书中的一句话来描述,某一接口具体实现类的选择控制权从调用类中移除,转交给第三方决定(一头雾水)。简单点说由Spring容器集中管理实现类,须要该实现类的时候由Spring容器根据名称或类型动态的注入该实现类。spring
从注入方法上区分,IoC主要包含构造函数注入、属性注入和接口注入;Spring支持构造函数注入和属性注入。Spring容器经过xml配置文件、注解描述、JavaConfig、Groovy DSL四种方式对实现类的信息及其它们之间的依赖关系进行描述,目前比较经常使用的应该是基于注解,而后配合JavaConfig的方式(Spring boot在我看来是一个anti-xml的产品,最近它比较流行,xml配置文件的方式在spring boot中有点显得格格不入),不过《精通Spring 4.X企业应用开发实战》这本书不太好的地方就在于,书中仍是保留了大量的基于xml配置文件的介绍,不过也是了解一下历史吧(Spring由于一直向下保持兼容,致使即便Spring boot如今流行起来,网上仍是大量的关于Spring xml配置的介绍,在推广注解、Java Config的方向上仍是阻力重重。这也是以前看到相对于Guice这种后起的IoC框架,Spring的不足之处)。编程
说到这里其实我以前面试的时候被问到一个问题:Spring有什么优缺点,咱们看Spring的书,满满的都是Spring的优势,轻量级的框架,当初就是由于EJB太笨重才有的Spring;依赖注入帮助咱们设计出低耦合的程序;AOP特性使得咱们写出异常简洁的代码,java web程序几乎离不开这个框架的身影,如今Spring已然是全家桶的解决方案,原有的SSH框架到如今的SSM轻量级框架,其中Spring,SpringMVC都属于Spring社区的,Spring的优势太多了,我也只是依照我我的理解简述了一下。缺点呢?前段时间看到一篇对比Spring和Guice的,角度挺好的,Spring虽然随着发展,拥抱了注解、java配置的形式对bean进行描述,可是依然保留了xml形式的bean定义,致使如今网上搜索资料不少都是xml形式的解答,这样使得社区变化的比较慢。可是依然推荐使用Spring,由于相对于Guice,Spring有一个更加优秀的社区,你遇到问题可以搜索到不少的Spring解答,社区也活跃。若是你使用Guice,遇到了问题社区相对于Spring活跃度就大打折扣了。设计模式
Java反射(这个知识自己很基础,听说效率不高,可是帮助你理解Spring框架,xml的bean声明都是反射生成的吧?)数组
Java中有一个特殊的类,Class,每一个Class内部都有一个Class类,是否是很拗口。可是确实是这样,这是一个特殊的类,它包含类的全部信息;全部的构造函数,全部的Field,全部的Method...能够动态的建立对象;用代码展现比较合适(很容易理解,就是异常声明的有点多)。tomcat
package com.test.spider; import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class JavaRefection { public static class MockClass { private String firstName; private String secondName; public String getFirstName() { return this.firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } @Override public String toString() { return "MockClass [firstName=" + firstName + ", secondName=" + secondName + "]"; } } public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException { Class c1 = MockClass.class; Class c2 = Class.forName("com.test.spider.JavaRefection$MockClass"); Class c3 = new MockClass().getClass(); //check that 3 methods obtain same class instance System.out.println("Whether T.class is same with Class.forName():" + String.valueOf(c1 == c2) + "\n" + "Whether Class.forName is same with object.getClass():" + String.valueOf(c2 == c3)); //using nullary constructor MockClass mockClass1 = (MockClass)c1.newInstance(); //another method, first get Constructor Constructor<MockClass> constructor = c1.getConstructor(new Class[]{}); MockClass mockClass2 = (MockClass)constructor.newInstance(new Object[]{}); //get Method Method[] methods = c1.getDeclaredMethods(); for(Method method : methods) { System.out.println(method.getName()); } //get Field Field[] fields = c1.getDeclaredFields(); for(Field field : fields) { System.out.println(field.getName()); } fields[0].setAccessible(true); fields[0].set(mockClass2, "Hello"); fields[1].setAccessible(true); fields[1].set(mockClass2, "World!"); System.out.println(mockClass2.toString()); //invoke method System.out.println(methods[1].invoke(mockClass2, new Object[]{})); System.out.println(methods[1].invoke(mockClass1, new Object[]{})); } }
用书中正式的话描述:每一个类在JVM中都拥有一个对应的java.lang.Class对象,提供类结构信息的描述。数组、枚举、注解及基本的Java类型(int,double等),甚至void都拥有对应的Class对象。Class没有public的构造方法。Class对象是在装载类时由JVM经过调用类装载器中的defineClass()方法自动构造的。网络
//for classLoader //全盘负责委托机制(委托机制防止自定义的ClassLoader恶意加载基础类) ClassLoader loader = c1.getClassLoader(); System.out.println("currentLoad:"+ loader); System.out.println("parent:" + loader.getParent()); System.out.println("grandParent:" + loader.getParent().getParent()); System.out.println(loader.getResource("java/net/URL.class")); System.out.println(loader.getResource("java/lang/String.class"));
输出:
currentLoad:sun.misc.Launcher$AppClassLoader@73d16e93 parent:sun.misc.Launcher$ExtClassLoader@6d06d69c grandParent:null jar:file:/G:/Develop/JDK1.8/jre/lib/rt.jar!/java/net/URL.class jar:file:/G:/Develop/JDK1.8/jre/lib/rt.jar!/java/lang/String.class jar:file:/G:/Develop/JDK1.8/jre/lib/rt.jar!/java/net/URL.class
ClassLoader,(1)装载:查找和导入class文件(2)连接:执行校验(检查class文件正确性)、准备(静态变量分配存储空间)和解析步骤(符号引用转换成直接饮用),其中解析步骤是可选的;(3)初始化:对类的静态变量,静态代码块执行初始化工做;
几个ClassLoader的关系,以及Class、Object、ClassLoader的关系:
介绍反射的概念仍是由于Spring中很多地方应该都用到了反射的知识,我我的理解,xml中声明bean的时候须要指明class属性,其中须要使用全类名,此时我相信Spring建立这个bean对象的时候使用的就是反射的技术。首先使用Class.forName("")获取到其类型,而后newInstance()或者获取到构造函数,使用有参数的构造函数;还有一个能想到的使用反射的地方。目前注入bean的时候能够在类的private属性上面添加@Autowired注释注入bean,即便没有相关的set方法,这里必定使用了反射技术,否则private类型是没法被外部的对象访问的,反射能够动态调整属性的访问类别,而后直接设置属性;应该还有不少地方。
IoC容器
话题继续切回IoC容器,Spring中的IoC容器,BeanFactory、ApplicationContext,ApplicationContext相比BeanFactory有更强大的功能,通常直接使用ApplicationContext;
BeanFactory
最初接触Spring的时候,不太能理解Bean是什么概念,有时候又看到JavaBean,最初JavaBean只是一个可复用的Java类,有必定的条件,好比无参构造函数、setter,getter获取属性以及可序列化,而后发展为后来的EJB(企业级JavaBean),为了简化企业级开发,就有了Spring框架,Spring里面的Bean就比较宽泛了,能被Spring容器实例化的Java类均可以成为Bean,因此几乎全部的类均可以是Bean。固然也能够不纠结这些概念。
XmlBeanDefinitionReader和DefaultListableBeanFactory,继承图如上图。(Idea真的挺好用的,Ctrl+N能够搜索到想要的类,而后生成类图,固然也能够看代码),经过Idea查看接口、类拥有什么方法最方便了,反正能够看出从BeanFactory,ListableBeanFactory,HierarchicalBeanFactory,ConfigurableBeanFactory(类装载器,属性编辑器,容器初始化后置处理器等),AutowireCapableBeanFactory功能逐渐增长的;
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="car" class="com.smart.beanfactory.Car"> <property name="brand" value="redCA72"></property> <property name="color" value="black"></property> <property name="maxSpeed" value="200"></property> </bean> </beans>
这里不得不说Idea的xml代码提示也挺好用的。
public class BeanFactoryTest { @Test public void getBean() throws Exception { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource resource = resolver.getResource("classpath:beans.xml"); System.out.println(resource.getURL()); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory); xmlBeanDefinitionReader.loadBeanDefinitions(resource); Car car = (Car)factory.getBean("car"); System.out.println(car.toString()); } }
ApplicationContext
以上是最基本的ApplicationContext接口的类图
Lifecycle这个接口好像tomcat的设计中也有相似的设计,tomcat的Lifecyle负责容器的生命周期?
主要使用的4个类
ClassPathXmlApplicationContext,构造参数中的路径classpath:... FileSystemXmlApplicationContext,构造参数中的路径file:... AnnotationConfigApplicationContext,基于@Configuration注解bean以及@Component方式声明Bean支持 GenericGroovyApplicationContext,基于Groovy声明Bean的方式
第三种目前使用的最多,SpringBoot中推崇的方式,Xml的方式遗留系统中使用较多;
WebApplicationContext
WebApplicationContext的初始化方式不一样于BeanFactory、ApplicationContext,由于其须要ServletContext实例。web.xml中配置自启动的Servlet或自定义Web容器监听器(ServletContextListener),借助而这种任何一个,完成启动Spring Web应用上下文的工做。
ContextLoaderServlet和ContextLoaderListener分别是Spring提供的用于启动WebApplicationContext的Servlet和Web容器的监听器。
二者的内部都实现了启动WebApplicationContext实例的逻辑,根据Web容器具体状况进行选择,web.xml中进行配置便可。
经过Web容器监听器引导
... <!-- 指定配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/smart-dao.xml, /WEB-INF/smart-service.xml </param-value> </context-param> <!-- 声明web容器监听器 --> <listener> <listener-class> org.springframework.web.context.ContextLoaderListener </listener-class> </listener> ...
不支持监听器的低版本web容器,采用自启动的Servlet;
... <!-- 指定配置文件 --> <context-param> <param-name>contextConfigLocation</param-name> <param-value> /WEB-INF/smart-dao.xml, /WEB-INF/smart-service.xml </param-value> </context-param> <!-- 声明自启动的servlet --> <servlet> <servlet-name>springContextLoaderServlet</servlet-name> <servlet-class>org.springframework.web.context.ContextLoaderServlet</servlet-class> <load-on-startup>1</load-on-startup> </servlet> ...
Bean的生命周期
Bean的完整生命周期从Spring容器着手实例化Bean开始,知道最终销毁Bean,其中通过了许多关键点,每一个关键点都设计特定的方法调用,方法主要分为4个种类:
1. Bean自身的方法:如调用Bean构造函数实例化Bean、调用Setter设置Bean的属性以及经过Bean的init-method和destroy-method所指定的方法
2.Bean级生命周期接口方法:如BeanAware、BeanFactoryAware、InitializingBean和DisposableBean,这些接口方法由Bean类实现
3.容器级别生命周期接口方法:InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口实现,通常称它们的实现类为“后处理器”。后处理器接口通常不禁Bean自己实现,它们独立于Bean,实现类以容器附加装置的形式注册到Spring容器中,并经过接口反射未Spring容器扫描识别。当Spring容器建立任何Bean的时候,这些后处理器就会发生做用,因此这些后处理器的影响是全局性的。
4.工厂后处理器接口方法:包括AspectJWeavingEnabler、CustomAutowireConfigurer、ConfigurationClassPostProcessor等方法。工厂后处理器也是容器级的,在应用上下文装配配置文件后当即调用。
Bean级生命周期和容器级生命周期接口是个性和共性的结合,Bean级别解决了个性化处理问题,容器级解决了某些Bean共性化处理的问题,Spring容器中能够注册多个后处理器,他们同时实现org.springframework.core.Ordered接口便可。
BeanFactory
public class BeanFactoryTest { @Test public void getBean() throws Exception { ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); Resource resource = resolver.getResource("classpath:beans.xml"); System.out.println(resource.getURL()); DefaultListableBeanFactory factory = new DefaultListableBeanFactory(); XmlBeanDefinitionReader xmlBeanDefinitionReader = new XmlBeanDefinitionReader(factory); xmlBeanDefinitionReader.loadBeanDefinitions(resource); factory.addBeanPostProcessor(new BeanPostProcessor1()); factory.addBeanPostProcessor(new BeanPostProcessor2()); factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor1()); factory.addBeanPostProcessor(new MyInstantiationAwareBeanPostProcessor2()); Car car = (Car)factory.getBean("car"); System.out.println(car.toString());
factory.destorySingletons(); } } public class Car implements BeanFactoryAware, BeanNameAware, InitializingBean, DisposableBean{ private String brand; private String color; private String maxSpeed; public String getBrand() { return brand; } public String getColor() { return color; } public String getMaxSpeed() { return maxSpeed; } public void setBrand(String brand) { this.brand = brand; } public void setColor(String color) { this.color = color; } public void setMaxSpeed(String maxSpeed) { this.maxSpeed = maxSpeed; } @Override public String toString() { return "Car{" + "brand='" + brand + '\'' + ", color='" + color + '\'' + ", maxSpeed='" + maxSpeed + '\'' + '}'; } private BeanFactory beanFactory; private String beanName; public void selfInit() { System.out.println("init-method指定的方法的调用。"); } public void selfDestory() { System.out.println("destory-method指定的方法的调用。"); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { System.out.println("调用BeanFactoryAware接口的setBeanFactory方法。"); this.beanFactory = beanFactory; } @Override public void setBeanName(String s) { System.out.println("调用BeanNameAware接口的setBeanName方法。参数:" + s); this.beanName = s; } @Override public void destroy() throws Exception { System.out.println("调用DisposableBean接口的destory方法。"); } @Override public void afterPropertiesSet() throws Exception { System.out.println("调用InitializingBean接口的afterPropertiesSet方法。"); } } public class BeanPostProcessor1 implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("调用BeanPostProcessor0接口的postProcessBeforeInitialization方法:" + s); return o; } @Override public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("调用BeanPostProcessor0接口的postProcessAfterInitialization方法:" + s); return o; } @Override public int getOrder() { return 0; } } public class BeanPostProcessor2 implements BeanPostProcessor, Ordered { @Override public Object postProcessBeforeInitialization(Object o, String s) throws BeansException { System.out.println("调用BeanPostProcessor1接口的postProcessBeforeInitialization方法:" + s); return o; } @Override public Object postProcessAfterInitialization(Object o, String s) throws BeansException { System.out.println("调用BeanPostProcessor1接口的postProcessAfterInitialization方法:" + s); return o; } @Override public int getOrder() { return 1; } } public class MyInstantiationAwareBeanPostProcessor1 extends InstantiationAwareBeanPostProcessorAdapter implements Ordered { @Override public int getOrder() { return 0; } public Object postProcessBeforeInstantiation(Class<?> var1, String var2) throws BeansException { System.out.println("调用InstantiationAwareBeanPostProcessor0接口的postProcessBeforeInstantiation方法:" + var2); return null; } public boolean postProcessAfterInstantiation(Object var1, String var2) throws BeansException { System.out.println("调用InstantiationAwareBeanPostProcessor0接口的postProcessAfterInstantiation方法:" + var2); return true; } public PropertyValues postProcessPropertyValues(PropertyValues var1, PropertyDescriptor[] var2, Object var3, String var4) throws BeansException { System.out.println("调用InstantiationAwareBeanPostProcessor0接口的postProcessPropertyValues方法:" + var4); return var1; } } public class MyInstantiationAwareBeanPostProcessor2 extends InstantiationAwareBeanPostProcessorAdapter implements Ordered { @Override public int getOrder() { return 1; } public Object postProcessBeforeInstantiation(Class<?> var1, String var2) throws BeansException { System.out.println("调用InstantiationAwareBeanPostProcessor1接口的postProcessBeforeInstantiation方法:" + var2); return null; } public boolean postProcessAfterInstantiation(Object var1, String var2) throws BeansException { System.out.println("调用InstantiationAwareBeanPostProcessor1接口的postProcessAfterInstantiation方法:" + var2); return true; } public PropertyValues postProcessPropertyValues(PropertyValues var1, PropertyDescriptor[] var2, Object var3, String var4) throws BeansException { System.out.println("调用InstantiationAwareBeanPostProcessor1接口的postProcessPropertyValues方法:" + var4); return var1; } }
Spring提倡的是非侵入式编程,以上其实框架代码和业务代码融合了;因此在应用编程的时候,尽可能少用4个Bean生命周期接口类,使用init-method,destory-method,或者支持@PostConstruct,@PreDestory的InitDestoryAnnotationBeanPostProcessor(ApplicationContext默认装载)这些不入侵代码的编程方式。
ApplicationContext
其与BeanFactory中的bean的生命周期一点不一样在于生命周期中多了一些接口调用;
另一点最大的不一样时ApplicationContext利用Java反射机制自动识别出配置文件中定义的BeanPostProcessor、InstantiationAwareBeanPostProcessor和BeanFactoryPostProcessor,而且自动将他们注册到ApplicationContext中;BeanFactory的代码中展现须要手动调用addBeanPostProcessor()等方法注册;
因此应用中使用ApplicationContext更加方便;