IoC的概念和DI的概念
IoC:Inverse of Convert,控制反轉,比較難理解的概念,以後補充 DI:Dependency Injection,依賴注入,讓調用類對某一接口實現的依賴關係由第三方(容器和協作類)注入,以移除調用類對某一接口實現類的依賴。
IoC的三種類型
- 構造函數注入
- 屬性注入
- 接口注入
構造函數注入
這種利用構造器的初始化,注入接口的實現類。這種方式有點像面向對象的組合使用。
//MoAttack.java
public class MoAttack {
private GeLi geli;
//通過構造函數參數注入革離的飾演者
public MoAttack(GeLi geli) {
this.geli = geli;
}
public void cityGateAsk() {
geli.responseAsk("墨者革離");
}
}
//Director.java
//導演安排角色扮演者
public class Director {
public void direct() {
//指定角色飾演者
GeLi geli = new LiuDeHua();
//將具體飾演者到劇本中
MoAttack moAttack = new MoAttack(geli);
moAttack.cityGateAsk();
}
}
屬性注入
通過setter方法,在有需要的時候再注入接口實現類。
//MoAttack.java
public class MoAttack {
private GeLi geli;
//屬性注入
public void setGeLi(GeLi geli) {
this.geli = geli;
}
public void cityGateAsk() {
geli.responseAsk("墨者革離");
}
}
//Director.java
public class Director {
public void direct() {
Geli geli = new LiuDeHua();
MoAttack moAttack = new MoAttack();
moAttack.setGeli(geli);
moAttack.cityGateAsk();
}
}
构造器注入和setter方法注入区别:构造器注入不可缺少,否则编译直接出错;setter方法注入,若为空,则会在运行期间出现空指针异常,无法正常运行。
可以通过在xml配置文件中增加«context:annotation-config/» 给setter注入添加一个require的注解,避免空指针异常。
接口注入
將調用類的所有依賴注入的方法抽取到一個接口,調用類實現該接口進行依賴注入。
實現的效果,其實與屬性注入並無區別,反而顯得比屬性注入稍麻煩。
//interface
public interface ActorArrangeable {
void injectGeLi(GeLi geli);
}
//MoAttack.java
public class MoAttack implements ActorArrangeable {
private GeLi geli;
public void injectGeLi(Geli geli) {
this.geli = geli;
}
public void cityGateAsk() {
geli.responseAsk("墨者革離");
}
}
//Director.java
public class Director {
public void direct() {
MoAttack moAttack = new MoAttack();
GeLi geli = new LiuDeHua();
moAttack.injectGeLi(geli);
moAttack.cityGateAsk();
}
}
Spring容器完成依賴關係的注入
Spring通過配置文件或註解描述類和類之間的依賴關係,自動完成類的初始化和依賴注入工作,其底層工作原理是java的反射機制。
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w4.org/2001/XMLSchema-instance"
xmlns:p="http://www.springframework.org/schema/p"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframeworl.org/schema/beans/spring-beans-4.0.xsd">
<!-- 實現類的實例化 -->
<bean id="geli" class="LiuDeHua"/>
<bean id="moAttack" class="com.smart.ioc.MoAttack"
p:geli-ref="geli"/><!--通過geli-ref建立依賴關係-->
</beans>
最後可以通過new XmlBeanFactory(“beans.xml”)等方式啓動容器,Spring會根據配置文件的描述信息,自動實例化Bean並完成依賴關係的裝配,從容器中即可返回準備就緒的Bean實例,後續可以直接使用。
注解方式注入
@Component注册组建类,spring自动创建对应的bean
@Configuration注册一个配置类,使用@ComponentScan告诉spring容器需要扫描的包并自动装配
@Autowired自动注入
@RunWith(SpringJUnit4ClassRunner.class)便于测试的时候自动创建Spring的上下文
@ContextConfiguration(classes=xxx.class)告诉spring加载什么配置文件
使用Java Config,只需要创建一个配置类,在配置类中编写方法,返回要注入的对象(可以为第三方库的对象),并给方法加上@Bean注解,告诉Spring为返回的对象创建实例。
以下图来自bridgeforyou,侵权即删。

java反射機制
//示例
//Car.java
package com.smart.reflect;
public class Car {
private String brand;
private String color;
private int maxSpeed;
public Car() {}
public Car(String brand, String color, int maxSpeed) {
this.brand = brand;
this.color = color;
this.maxSpeed = maxSpeed;
}
public void introduce() {
System.out.println("brand: "+brand+" color: "+color+" maxSpeed: "+maxSpeed);
}
//getter 和 setter方法
...
}
//ReflectTest.java
package com.smart.reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
public class ReflectTest {
public static Car initByDefaultConst() throws Throwable {
//通過類加載器獲取Car類對象
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Class clazz = loader.loadClass("com.smart.reflect.Car");
//獲取類的默認構造器對象並通過它實例化Car
Constructor cons = clazz.getDeclaredConstructor((Class[])null);
Car car = (Car)cons.newInstance();
//通過反射方法設置屬性
Method setBrand = clazz.getMethod("setBrand", String.class);
setBrand.invoke(car, "bif");
Method setColor = clazz.getMethod("setColor", String.class);
setColor.invoke(car, "black");
Method setMaxSpeed = clazz.getMethod("setMaxSpeed", int.class);
setMaxSpeed.invoke(car, 200);
return car;
}
public static void main(String[] args) throws Throwable {
Car car = initByDefaultConst();
car.introduce();
}
}
ClassLoader
類加載器的作用是尋找類的字節碼,構造出類在jvm中對象的組建。
类装载器把一个类装入JVM中,需要经过以下步骤:
- 装载:查找和导入Class文件
- 链接:校验、准备和解析步骤
- 校验:检查载入Class文件数据的正确性
- 准备:给类的静态变量分配存储空间
- 解析:将符号引用转换成直接引用
- 初始化:对类的静态变量、静态代码块执行初始化工作
JVM运行时会产生3个ClassLoader:根装载器, ExtClassLoader, APPClassLoader。
- 根装载器:采用C++语言编写,在java中不可见,负责装载JRE的核心类库
- ExtClassLoader:为ClassLoader的子类,负责装载JRE扩展目录ext中JAR类包
- AppClassLoader:为ClassLoader的子类,负责装载Classpath路径下的类包
(这三类装载其存在父子层级关系,根装载器为ExtClassLoader的父装载器,ExtClassLoader为APPClassLoader的父装载器)
public class ClassLoaderTest {
public static void main(String[] args) {
ClassLoader loader = Thread.currentThread().getContextClassLoader();
System.out.println("current loader: "+ loader);
System.out.println("parent loader: "+ loader.getParent());
System.out.println("grandparent loader: "+loader.getParent().getParent());
}
}
/*
current loader: sun.misc.Launcher$AppClassLoader@18b4aac2
parent loader: sun.misc.Launcher$ExtClassLoader@677327b6
grandparent loader: null
*/
Class类没有public的构造方法,Class对象是在装载类是有JVM通过调用类装载器中的defineClass()方法自动构造。
反射类
- Constructor:类的构造函数反射类,通过Class对象getConstructors()获取所有构造函数反射对象数组,getConstructor(Class… parameterTypes)获取特定参数的构造函数反射对象。构造器对象的newInstance()方法可以创建一个实例对象。
- Method:类方法的反射类,Class对象getDeclaredMethods()获取所有方法反射类对象数组Method[]。再通过invoke方法进行调用。
- Field:类的成员变量的反射类,Class对象getDeclaredFields方法获取类的成员变量反射对象数组。通过Field的set方法对成员变量进行值设置。
Spring资源访问
Resource资源抽象接口
接口主要方法:
- boolean exists():资源是否存在
- boolean isOpen():资源是否打开
- URL getURL() throws IOException:返回对应的URL对象
- File getFile() throw IOException:返回对应的File对象
- InputStream getInputStream() throws IOException:返回资源对应的输入流
具体的实现有ByteArrayResource(二进制数组资源),ClassPathResource(类路径下的资源),FileSystemResource(文件系统资源),InputStreamResource(以输入流返回表示资源),ServletContextResource(为访问Web容器上下文中的资源设计的类,以web应用为根目录),UrlResource(疯转Java中URL,可以访问任意资源)
public class ResourceTest {
public static void main(String[] args) {
try {
String filePath = "/home/xxx/chapter/Web/WEB-INF/classes/conf/file1.txt";
Resource res1 = new FileSystemResource(filePath);
//以类路径方式加载文件
Resource res2 = new ClassPathResource("conf/file1.txt");
InputStream ins1 = res1.getInputStream();
InputStream ins2 = res2.getInputStream();
System.out.println("res1:"+res1.getFilename());
System.out.println("res2:"+res2.getFilename());
} catch(IOException e) {
e.printStackTrace();
}
}
}
资源地址表达式

classpath:与classpath*: 的区别:前者只会在第一个加载的包下去查找,而后者会扫描所有这些JAR包和类路径下出现的包名。
资源加载器(ResourceLoader)
ResourceLoader接口定义了一个getResource(String location)方法,ResourcePatternResolver实现ResourceLoader接口,并且定义一个新的方法getResources()方法。Spring则提供了一个标准实现类PathMatchingResourcePatternResolver
ResourcePatternResolver resolver = new PathMatchResourcePatternResolver();
//加载所有类包com.baobaotao(及子包)下的以xml为后缀的资源
Resource[] resources = resolver.getResources("classpath*:com/baobaotao/**/*.xml");
for(Resource resource: resources) {
System.out.println(resource.getDescription());
}
BeanFactory和ApplicationContext
BeanFactory是Spring最核心的接口,提供高级IoC配置机制,实现不同类型的Java对象,底层是基于Java的反射机制实现;ApplicationContext则是基于BeanFactory上建立的,便于创建应用,面向开发者。

除了常见ClassPathXmlApplicationContext加载xml的配置文件之外,spring还提供一种java config的配置方式,使用注解和类编写方式,可以让开发者更好的控制。
@Configuration//表示一个配置信息提供类
public class Beans {
@Bean//定义一个Bean
public Car buildCar() {
Car car = new Car();
car.setBrand("CA72");
car.setMaxSpeed(200);
return car;
}
}
启动Configuration注解的配置类
public class AnnotationApplicationContext {
public static void main(String[] args) {
//加载带有Configuration的POJO装载Bean的配置
ApplicationContext ctx = new AnnotationConfigApplicationContext(Beans.class);
Car car = ctx.getBean("car", Car.class);
}
}
也可以通过web.xml方式来启动@Configuration配置类
<!--指定context参数-->
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<!--指定标注@Configuration的配置类-->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>com.baobaotao.AppConfig1,com.baobaotao.AppConfig2</param-value>
</context-param>
<!--ContextLoaderListener将根据上面配置启动Spring容器-->
<listener>
<listener-class>
org.springframeworkwork.web.context.ContextLoaderListener
</listener-class>
</listener>
Bean的生命周期


spring容器,Bean配置信息,Bean实现类及应用程序的关系

Spring的技术内幕
spring的AbstractApplicationContext为ApplicationContext的抽象类,该抽象类的refresh()方法定义容器在加载配置文件后各种处理过程。
//refresh方法内部:
//1.初始化Bean Factory
ConfigurableListableBean beanFactory = getBeanFactory();
...
//2.调用工厂后处理器
invokeBeanFactoryPostProcessors();
//3.注册bean后处理器
registerBeanPostProcessor();
//4.初始化消息源
initMessageSource();
//5.初始化应用上下文事件广播器
initApplicationEventMulticaster();
//6.初始化其他特殊的Bean:交给具体子类实现
onRefresh();
//7.注册事件监视器
registerListeners();
//8.初始化所有单实例的Bean,将其放到缓存中,使用懒初始化模式的Bean例外
finishBeanFactoryInitialization(beanFactory);
//9.完成刷新并发布容器刷新时间
finishRefresh();

以下摘录自公众号《Java后端技术》中一篇关于Spring的面试
IOC
IOC(Inversion Of Controll,控制反转)是一种设计思想,将原本在程序中手动创建对象的控制权,交由给Spring框架来管理。IOC容器是Spring用来实现IOC的载体,IOC容器实际上就是一个Map(key, value),Map中存放的是各种对象。
这样可以很大程度上简化应用的开发,把应用从复杂的依赖关系中解放出来。IOC容器就像是一个工厂,当需要创建一个对象,只需要配置好配置文件/注解即可,不用考虑对象是如何被创建出来的,大大增加了项目的可维护性且降低了开发难度。
AOP
AOP(Aspect-Oriented Programming,面向切面编程)能够将那些与业务无关,却为业务模块所共同调用的逻辑或责任(例如事务处理、日志管理、权限控制等)封装起来,便于减少系统的重复代码,降低模块间的耦合度,并有利于未来的可扩展性和可维护性。使用AOP之后我们可以把一些通用功能抽象出来,在需要用到的地方直接使用即可,这样可以大大简化代码量,提高了系统的扩展性。
Spring AOP是基于动态代理的,如果要代理的对象实现了某个接口,那么Spring AOP就会使用JDK动态代理去创建代理对象;而对于没有实现接口的对象,就无法使用JDK动态代理,转而使用CGlib动态代理生成一个被代理对象的子类来作为代理。
Spring AOP / AspectJ AOP 的区别?
Spring AOP属于运行时增强,而AspectJ是编译时增强。
Spring AOP基于代理(Proxying),而AspectJ基于字节码操作(Bytecode Manipulation)。
AspectJ相比于Spring AOP功能更加强大,但是Spring AOP相对来说更简单。如果切面比较少,那么两者性能差异不大。但是,当切面太多的话,最好选择AspectJ,它比SpringAOP快很多。