spring 回顾
主要就是它的IOC理念
即:把对象的创建、初始化、销毁等工作交给spring容器来做
依赖jar
通过spring的IOC容器获取bean,执行对应的方法
圈中的内容会启动spring容器,加载spring配置文件,加载过程中会初始化配置文件中对应的bean(用bean的类中的构造函数初始化了对象)
关于注解请看:
http://www.cnblogs.com/luotuoke/p/4544144.html
说明:
1、 启动spring容器
2、 spring容器内部会自动创建有注释(@Servic,@Controller,@Repository,@Component)的对象
3、 当spring容器解析到
启动依赖注入的注解解析器:
4、 看哪些bean的属性上面是否有Resource(@Autowired)注解
5、 如果属性上面有该注解,再次检查是否有name属性
6、 如果没有name属性,则会把该注解标注的属性的名称获取到和spring容器中的id做匹配,如果匹配成功,则赋值,如果匹配不成功,则按照类型进行匹配,如果匹配成功,则赋值,如果匹配不成功,则报错。
7、 如果有name属性,则把name属性的值解析出来和spring容器中的id做匹配,如果匹配成功,则赋值,如果匹配不成功,则报错。
8、 从上述的步骤可以看出注解的效率比较低,xml的效率比较高,注解书写比较简单,xml书写比较复杂。
@Service 用于标注业务层组件,注意得注释在实现类中,不要注释在接口上。在action中引用时用的是接口,这种bean默认是“singleton”的。
例如:
@Autowired 后不需要getter()和setter()方法,spring也能够自动注入
当然除了注解方式的生成bean和注解,也可以通过配置文件方式
在spring的相关配置文件中如下写法:
由于上面的类HelloWorldFactory中的getInstance方法是静态的所以不用实例化对象去调用,用类名可直接调用
当想要执行对应的上面构造的对象的方法时,根据bean的Id来获取对象然后
执行此对象的方法
凡是有scope=prototype的,都会出现延迟加载(加载spring容器时对应的bean类不会去构造对象),并且lazy-init的false失效
如果把lazy-init设置为true,则当spring容器启动的时候,检测不到任何错误,这样会存在很大的安全性隐患,所以一般情况下应该设置lazy-init为default/false。但是如果一个bean中有一个属性,该属性含有大量的数据,
这个时候不希望该bean过早的停留在内存 中。这个时候需要用到lazy-init为true。
在默认情况下放入到spring中的bean是单例的
将来service层和dao层所有的类将放入到spring容器中,所以默认情况下这两个层的类的实例都是单例的,所以不能把数据声明到属性中。如果声明在属性中,将会成为共享的。
可以想象 ,javabean等封装数据的类是不能放在spring容器中的。
action的视图层类来接收数据的类,为啥要设计成多例(@scope),原因就在于此吧。
1, 加载spring配置文件时,调用默认的构造函数为每个bean创建一个对象(在非layz-init=true时)。
2, 如果含有di(对象的属性赋值操作),为初始化的对象赋值
3, Bean中声明了init-method方法,执行此方法
4, 执行getBean()
5, 执行方法
说明:
1、 init方法是由spring内部执行的
2、 只有当spring容器关闭以后才能执行destroy方法,spring容器一般情况下是不会关闭的。只有当web容器销毁掉的时候才可能关闭掉,所以只要一个对象在spring容器中,在spring容器关闭之前,会一直保留。
3、 如果一个bean的配置是scope为”prototype”,则spring容器不负责销毁。
与注解方式的对比:
当getBean(”person”)时就会去访问容器在扫描类时在内存中解析到的<bean id=”person” >(解析的过程中会根据注释的Component在内存中生成bean,然后根据resource找到bean并对其相关联的属性进行赋值)
的对象,然后就可访问其方法了。
流程分析
1、 启动spring容器
2、 Spring容器解析类扫描的注解解析器,在base-package指定的包及子包中查找所有的类
3、 查看哪些类上面是否含有@Component注解
4、 如果该注解的value的属性的值为空,则把类名的第一个字母变成小写,作为id值,放入到spring容器中
5、 如果该注解的value的属性的值不为空,则用value的属性的值作为id值,放入到spring容器中
6、 再次查找在spring容器中的类的所有的属性,按照@Resource的规则给属性赋值
说明
使用了类扫描机制的做法,配置文件中的配置很简单了,但是效率越来越低。
spring的东西貌似很多的样子
简略的写一下 面试可能会问的东西:
IOC:控制反转(Inversion of Control),将对象的创建,初始化,销毁等工作都交给spring容器;
你在spring的配置文件(applicationContext.xml)中配置了对应的bean那么就会在加载spring配置文件的时候就去实例化这个bean的对象(注解方式则不用去配置,会根据注解去实例化)(排除延迟加载的情况scope=”prototype”或lazy-init=”true”)。
spring容器实例化bean的三种方式
构造函数
静态工厂是调用的静态方法 所以不需要工厂的对象
实例工厂要创建对象得调用非静态放,所以先得到工厂的对象 用这个对象去调用方法
DI:主要体现在依赖注入(给属性赋值)方面: 如果要注入的属性是对象,在bean中配置property=”beanid”,利用java的反射机制调用要注入的属性的set方法赋值,或者直接根据注释注入属性对象
把action调用service,service调用dao等对象的创建交给spring来完成
非注解方式的话:
注解方式的话:
实现了完全的面向接口编程,在代码端没有必要关心一个接口的实现类是什么。
Aop: 经典的面向切面编程,其底层原理是动态代理。
在开发中的一些日志记录,事务,异常处理等都可以看成是aop的切面。
以spring的声明自己的事务处理为例子,beginTrasaction();就是一个前置通知,commit()就是一个后置通知,其中还有环绕通知,能够控制切入点(目标方法)是否执行。异常通知等。
切面<aop:aspect,,,>中配置的方法会伴随着切入点表达式<aop pointcut,expression=”packge.*.*”,>中的方法。
配置事物管理器:对目标方法进行事物管理,当在执行目标方法时,出现异常,则目标方法中的操作数据库的语句是不会提交的。
事物实例:
为什么要用事物举例;
应用层面的事务是在业务级的,这种事务数据库管不着。要是一个应用没有事务,自己点点刷刷是没有问题的,但真正到生产环境中就不一定行了。
比如你去银行取钱,银行把钱给你,然后在你帐户中把你的余额修改,这是一个事务的,能保证你拿到钱后去你再去查你的帐户,你能立刻看到你的账户余
额减少,如果没有事务,你可能去查看到的余额没有变,你会有什么感觉。这只是很简章的一方面。
可以用作权限,可以决定目标方法是否执行
ProceedingJoinPoint join
Object obj = join.proceed();//决定代理对象的调用方法(连接点)是否执行,,obj为连接点的返回值。
aop的优劣
(1).好处:
1).在编写service的时候,不用管譬如权限之类的业务要求
2).切面负责除了业务逻辑的其他内容,而且切面中的通知(权限控制)和业务逻辑方法没有任何关系,做到了完全松耦合
3).最后产生的代理对象吧通知和目标结合在一起
(2).不好之处
切入点和通知相互交织,有复杂的嫌疑。并不像struts那样具有像栈一样的模块
SSH的整合:与hibernate,在spring配置文件中配置产生dataSource的bean(有数据库配置信息)
配置产生sessionFactory的bean,sessionFactory依赖dataSource
在dao实现类声明sessionFactory对象(这个dao继承了HibernateDaoSuport)
与struts的结合 ,action的产生交给了spring容器;
1、 服务器启动通过web.xml的ContextLoaderListener监听器类,
1) 并且调用ContextLoaderListener监听器的contextInitialized()方法,在其中调用了createContextLoader()方法实例化了一个ContextLoader的contextLoader对象。
2) 用这个ContextLoader对象去加载spring的配置文件
首先在web.xml中找<context-param><param-name>contextConfigLocation</param-name><param-value>classpath:spring/applicationContext.xml
如果有此配置则根据此配置来加载spring配置文件,没有则默认去加载web-info/applicationContext.xml(此方法不推荐)
3) 继续用contextLoader对象调用它的initWebApplicationContext()方法,
在此方法中用createWebApplicationContext()创建了WebApplicationContext容器(spring容器),并将此容器放到了servletContext域中(web应用域)。
在此容器中实例化dao,service等一些实例化对象。
-----------------------===================================
通过StrutsPrepareAndExecuteFilter过滤器
调用此过滤器的init()方法//在此init方法中用InitOperations的对象init去调用InitOperations的initDispatcher()方法
此init()方法执行过程中加载了default.properties文件(包含一些常量信息比如同一编码,默认utf-8,还有文件上传的默认大小为2M,都有默认值,但都是可以改变的)
struts-default.xml(配置了启动struts容器时默认加载的一些jar包,结果集类型定义,默认拦截器的声明,拦截器栈的声明,要加载好多bean这也是struts容器启动慢的原因)、
struts-plugin.xml(改写了action的生成方式,改为按照StrutsSpringObjectFactory 里的buildAction方式生成action)、这里的声明将action的创建交给了spring容器
struts.xml文件
2、 当url请求时通过StrutsPrepareAndExecuteFilter过滤器
调用都doFiler()方法执行过程中
首先利用容器启动时创建的对象prepare和execute
1) 调用(default.priperties)的配置(文件中的struts编码默认UTF-8)设置编码
2) 创建了值栈valuestack
3) 初始化值栈,将request,session,application对象放入到值栈中的map栈中
4) 还创建了一个actionContext,将valuestack值栈中的map栈赋值给actionContext的context,所以值栈中map栈与actionContext中的context存在着同一个引用。
5) 将actionContext放入到了当前线程(threadLocal)中,保证线程的一致性
6) 创建action代理(与struts有所不同)
7) 创建action,将action放到栈顶
8) 执行action代理的invoke()方法
9) 顺序执行配置的所有拦截器
10)执行所需的action中的方法,执行结果集(反馈给浏览器),倒序执行拦截器
11)清空struts容器中的数据环境(清空了actionContext中的所有数据)
当结合spring容器后,创建action的方式发生了改变,
当用户请求道action时,
根据struts.xml中对应action配置的class=””值去在spring容器找bean的name值(这个actionbean配置了scope=”prototype”,一般此项必须配置,不然可能形成数据共享,这样确保多个action操作的不是同一个实例),创建action对象
其他过程不变