《精通Spring 4.X企业应用开发实战》读书笔记1-1(IoC容器和Bean)

   很长一段时间关注在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更加方便;

  

相关文章
相关标签/搜索