由于怕过久了对于配置的一些原理和配置上的细节会忘记,所以记录下来。
在学习完javase和javaweb的servlet部分知识后,就必须进入到框架的学习,框架可以减少很多繁琐重复的工作量,提供更强大的功能。
先来学习spring
什么是IOC
所谓控制反转,就是把对其他对象的实例化和调用,转给spring容器来做。
看下面代码:
public class TestIOC {
LoginService loginService = new LoginServiceImpl();
loginService.doLogin("张三","123");
}
上面TestIOC类需要调用LoginService类,直接实例化它,这就等于LoginService 的控制在TestIOC手里。
如果使用spring的IOC控制反转,那么代码如下:
public class TestIOC {
LoginService loginService;
loginService.doLogin("张三","123");
}
这时候实例化过程被省略,因为需要的loginService这个实例是spring容器通过配置bean文件在容器中实例化了,然后注入到LoginService这个接口成为它的实例对象。
所以new LoginServiceImpl()这个实例的创建的控制权就从业务逻辑的类中转移到spring容器中,这就是控制反转的意思。
IOC的好处就是能够面向接口编程,减少类之间的耦合,这样每个类都分模块做自己的事情。同时可以把业务分成很多的模块,然后使用容器来装载这些模块,使得它们共同工作,也方便灵活的添加需要的模块bean和排除不需要的模块bean。
什么是AOP
aop即所谓面向切面编程,意思是把你的程序执行过程从上面和下面切开,同时把需要执行的附加内容插入到这个切面中:
public void 普通方法(){
System.out.println("我是好人");
}
如上,如果要在System.out.println(“我是好人”)之前加上判断,在之后加上确认,如果没有AOP只能进入源码中修改,就像下面这样:
public void 普通方法(){
doBeforeMethod();
System.out.println("我是好人");
doAfterMethod();
}
但是使用AOP后可以把代码切出两个面,一个是在System.out.println(“我是好人”)之前,一个在之后,然后再在切面的位置添加需要的代码即可,原来的代码不变:
public void 普通方法(){
System.out.println("我是好人");
}
public void useAOP(){
切面:doBeforeMethod();
调用那个普通方法的代码...
切面:doAfterMethod();
}
从上面看到,普通方法本身不需要修改,只需要在AOP中找到切面添加相应方法即可,效果就跟把代码插进去一样。
AOP的原理是使用反射,可以用在拦截器(在执行所有内容之前和之后给你执行下某些代码)。也可以用在数据库的事务管理中,简单原理如下:
一个更新操作,需要开启事务,执行更新,提交和回滚三步:
public boolean update() {
conn.setAutoCommit(false);
try{
conn.execute("更新");
conn.commit();
} catch(异常) {
conn.rollback();
}
}
使用AOP的切面编程思想,把第一步的conn.setAutoCommit(false)和第三步的conn.commit()+conn.rollback()抽取出来,分别放到执行conn.execute(“更新”)之前和之后的两个切面中,变成了:
public boolean update() {
conn.execute("更新");
}
public boolean useAOP() {
conn.setAutoCommit(false);
try{
调用那个更新方法的代码...
conn.commit();
} catch(异常) {
conn.rollback();
}
}
调用那个更新方法的代码使用的是反射,我们发现update方法只需要关注业务,事务的开启和回滚已经使用AOP思想配置在容器中了。这样做的好处就是,如果有非常多个数据库更新,插入等操作,事务管理的代码都不用写了,在容器中配置开启就可以。
spring的如何初始化bean
一般是在路径下配置的applicationContext.xml中配置好bean,如下:
<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
xmlns:p="http://www.springframework.org/schema/p" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.3.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.3.xsd">
//看这个bean:
<bean id="demoService" class="com.pack.DemoServiceImpl">
</bean>
</beans>
上面我们在applicationContext.xml的中配置了一个,指定这个bean的id为demoService,具体的类为com.pack.DemoServiceImpl,那么,ServletContextListener接口的实现类会在spring容器启动(也就是启动你的project app)的时候把applicationContext.xml中的所有bean初始化,如同上面IOC部分提到的一样,不需要自己new一个类实例出来,容器会帮我们注入bean实例。
到此,有三个疑问没有解决:
1、 spring容器 去哪里 调用ServletContextListener接口的实现类
加载applicationContext.xml配置文件需要注册一个监听器,这个监听器需要实现ServletContextListener接口,其实只要在我们熟悉的web.xml文件中配置好:
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
ContextLoaderListener就是ServletContextListener接口的实现类,在spring-web.jar包内,spring容器就是根据web.xml中配置的ContextLoaderListener来加载applicationContext.xml配置文件
2、 ServletContextListener接口的实现类 如何 加载applicationContext.xml
3、 加载之后如何实例化和管理bean
ContextLoaderListener会监听启动WebApplicationContext,WebApplicationContext会有一系列的动作包括获取配置文件applicationContext.xml的内容并且初始化bean等(具体实现不清楚),简单原理大概如下。
String xmlPath = "WEB-INF\\applicationContext.xml";
//获取配置文件的对象等
ApplicationContext applicationContext = new ClassPathXmlApplicationContext(xmlPath);
//获得实例
DemoService demoService = (DemoService)applicationContext.getBean("id");