在经过了对spring框架基本开发的了解以及对spring boot流程的学习,《精通spring4.x…》这本书正式开始了spring的讲解,我也跟随着这本书的脚步进行学习。
首先需要学习的是spring的IoC技术,IoC全称是Inverse of Control,是spring容器的内核。
即是将一个接口具体实现类的控制权从调用的类中移除,交给第三方来控制。如果从电影剧本的角度即是说,演员,剧本,角色的对应由导演来控制。在spring中就是spring容器借用Bean配置来进行控制。
后来由于这个概念不够直观引入了DI(Dependency Injection)依赖注入的概念,即是说调用类对某一接口实现类的依赖关系由第三方注入。
IoC可以划分为三种类型:构造函数注入,属性注入,接口注入。Spring支持构造函数注入和属性注入。
通过调用类的构造函数,将接口类通过构造函数给出。
用电影的例子就是,导演分配角色的演员,而后在剧本的构造函数中将这个演员注入。
通过调用类的函数,将接口类通过函数给出。
用电影的例子就是不是这个角色在所有的场景都需要,所以这时候可以通过setter方法来注入演员。
将调用类所有依赖注入的方法抽取到一个接口中,调用类通过实现该接口的注入方法来达成注入。
虽然如同上述一般操作后,调用类之间完成了解耦,然而这些代码在第三个类中依然存在。就好像导演要控制其他两个类,如果这时候有个第三方的代理机构只是让导演,剧本,演员各司其职,那么三个类都完成了解耦。那么spring就是这样一个容器,通过new XmlBeanFactory)
就可以启动容器并且完成装配。
java语序通过程序化的方式间接对Class进行操作。
可以通过一例子来说明。通常我们要实现一个类并赋予初值可以使用构造函数直接赋值,或是创建后用setter方法来赋值,这就属于直接调用目标类。
于此相对,我们可以创建一个ClassLoader用来获取当前线程,而后通过指定的全限定类名来装载目标类的反射实例。之后我们就可以利用反射类的各个对象来操作类,从而达到创建等方法的实现的目标。
类装载器就是寻找类的节码文件并构造出类在jvm内部表示对象的组件。其经历以下步骤把一个类装入jvm:
JVM在运行时会有三个ClassLoader,其中他们有父子关系:
JVM有全盘负责委托机制,是指当一个ClassLoader载入一个类时,其所依赖和引用的类也由同一个ClassLoader载入;先委托父装载器寻找目标类,只有找不到才从自己的路径中查找并转载目标类。
这个机制也是导致NoSUchMethodError问题的原因,如果在类路径有多个不同版本的类包就有可能导致错误。
即是可以从Class对象中获取构造函数,成员变量方法类等反射对象即是包括了private或protected成员变量和方法的但是要申明setAccessible(boolean access)
。
主要有:
实例如下:
package com.smart.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Field;
public class ReflectTest{
public static Car initByDefaultConst() throws Throwable{
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.smart.reflect.Car");
Constructor cons = clazz.getDeclaredConstructor((Class[])null);
Car car = (Car)cons.newInstance();
Method setBrand = clazz.getMethod("setBrand", String.class);
setBrand.invoke(car, "XXX");
Method setColor = clazz.getDeclaredMethod("setColor", String.class);
setColor.invoke(car, "Dark");
Method setMaxSpeed = clazz.getDeclaredMethod("setMaxSpeed", int.class);
setMaxSpeed.invoke(car, 200);
return car;
}
public static void main(String[] args) throws Throwable{
Car car = initByDefaultConst();
car.introduce();
}
}
jdk所提供的访问资源的类并不能很好地满足各种底层资源的访问需求,比如从类路径或web容器的上下文中获取资源的能力。
为此,Spring设计了一个Resource接口,它为应用提供了更强的底层资源访问能力。其主要方法如下:
bollean exists()
:资源是否存在
bollean isOpen()
:资源是否打开
URL getURL() throws IOException
:如果底层资源可以表示程URL,则该方法返回对应的URL对象。
FIle getFile() throws IOException
:如果底层资源对应一个文件则该方法返回对应的File对象。
InputStream getInputStream() throws IOException
:返回资源对应的输入流。
在获取资源后,用户就可以通过Reaource接口定义的多个方法来访问文件其他信息,如getFileName()方法来获取文件名,通过getInputStream()方法来获取文件的输入流等。
注意在资源配置文件在项目发布时会被打包,所以不能使用getFile()方法了,而要用getInputSteam(),这个问题码一下。
为了访问不同类型的资源,必须使用相应的Resource实现类,spring提供了强大的加载资源机制来简化了这一过程。仅通过资源地址的特殊标识就可以访问相应的资源,还支持ant风格的资源地址。
有如下较常见的地址前缀:
classpath:
/classpath:/
:这两个都是相对于类的根路径开始。资源文件可以在文件系统中也可以在jar或zip的类包中。特别注意classpath*:
可以扫描所有的同名包并加载所需。file:
:使用URLResource从文件系统目录中装载资源,可以是相对路径,也可以是绝对路径。http://
:使用UrlResource从web服务器中装载资源。ftp://
:使用UrlResource从ftp服务器中装载资源ant是一个允许通配符的格式
spring定义了一套资源加载的接口其关系如下:
PathMatchingResourcePatternResolver->
ResourcePatternResolver->
ResourceLoader->
Resource
这其中REsourceLoader接口是不支持ant的,但是ResourcePatternResolver扩展了其接口,而PathMathingResourcePatternResolver是spring的标准实现类,例子如下
package com.smart.resource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.testng.annotations.*;
import static org.testng.Assert.*;
public class PatternResolverTest{
@Test
public void getResources() throws Throwable{
ResourcePatternResolver resolver =
new PathMatchingResourcePatternResolver();
Resource resources[] = resolver.getResources("classpath*:com/smart/**/*.xml");
assertNotNull(resources);
for(Resource resource:resources) {
System.out.println(resource.getDescription());
}
}
}