SSH:
Hibernate:
用来做持久层: 它将JDBC做了一个良好的封装,程序员在与数据库进行交互时可以不用书写大量的SQL语句.
Struts2:
用来做应用层: 负责控制service(业务逻辑处理类),从而控制了service的生命周期.
SSH框架流程:
JSP页面—-Struts2—-Service(业务逻辑处理类)—-Hibernate
这样层与层之间耦合性强,这时,使用spring的 IOC机制(控制反转和依赖注入)就起到了控制Action对象(Struts2中的) 和 Service类的作用,降低它们之间的耦合度.
Spring实际的使用场景是整合其它框架: 非侵入式框架,对现有存在的项目结构不会产生影响.
Spring(控制反转和依赖注入):
(1)控制反转(IOC):inversion of control
以前我们需要对象,需要我们主动的创建;
IOC: 使用者只需要知道需要什么对象,然后直接从容器中直接获取即可,不用再关注对象的创建过程,只需要关注拿到该对象之后需要做的业务.
IOC容器:
创建对象和处理对象之间的依赖关系, 工厂模式 + 反射机制
IOC加载对象的方式:
1. 通过bean 的 class来加载
2. 通过静态工厂加载
3. 通过工厂方法加载
通过IOC获取要使用的对象
所有bean对象的构造方法都是在配置文件加载之前完成,并且bean对象是一个单例,所以每次getBean获取的都是同一个对象;
如果bean是一个单例对象,一般来说,会有效的减少服务器的内存空间,Spring通过监听器来监听bean对象,如果该bean对象长期不使用,会被销毁掉.当我们再次使用bean对象时,会再次创建.
(2)依赖注入(DI):
IOC的一种特殊体现形式,配合接口,达到不同层之间的解耦
我们在获取A对象时,在A的内容必须依赖B对象,我们在获取A对象时,将B对象注入到A对象中
DI依赖注入的方式: :
1.构造器注入
2.setter注入
3.静态工厂注入
4.实例化工厂注入
项目中具体的创建注入都是Spring容器帮我们实现的,即通过控制反转和依赖注入,实现都是基于反射机制来实现的
Spring容器控制所有的Action对象和业务逻辑类的生命周期,这样Action只是充当了Service的控制工具,它只要知道这些业务实现类所提供的方法接口就行了.层与层之间完全脱耦,是程序运行起来效率更高,维护起来更方便.
以往单独使用Struts2框架时,所有业务方法类的生命周期,甚至是一些业务流程都是由Action来控制的,层与层之间耦合性太紧密了,既降低了数据访问的效率,又使业务逻辑看起来很复杂,代码量也很多.
Spring
<1>创建对象(IOC):
(1)通过静态工厂模式创建对象:
1. 根据 id 获取到工厂的类名
2. 通过类调用factory-method来指定创建bean实例的静态工厂方法
<bean id="car" class="com.xalo.action.CarFactory" factory-method="getCar">
</bean>
(2)工厂方法模式创建对象:
工厂中获取具体对象的方法不是静态方法,而是成员方法
1.定义一个工厂类(加载工厂对象)
2.通过factory-bean获取工厂对象,通过factory-method配置工厂对象要调用的方法.
<bean id="carFactory" class="com.xalo.action.CarFactory"></bean>
<bean id="car" factory-bean="carFactory" factory-method="getCar">
</bean>
(3)通过bean的class来加载:
<bean id="girl" class="com.xalo.entity.Girl"></bean>
<bean id="person" name="father,monther" class="com.xalo.entity.Person" init-method="init" destroy-method="destory"></bean>
<2>依赖注入(DI)
(1)setter注入:
1.根据property标签的name属性的值去找对应的setter方法.
例如: name= “aa” 对应的就是setAa方法.
2.由于属性注入具有可选性和灵活性高的优点,是实际上最常用的注入方式.
3.属性注入要求bean提供一个默认的构造函数,并为需要注入的属性提供对应的setter方法.spring先调用bean默认的构造函数实例化bean对象,然后通过反射机制的方法调用setter方法注入属性值.
public class SetterAction {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
//ApplicationContext.xml
<bean id="setter" class="com.xalo.action.SetterAction">
<property name="name" value="碧瑶"/>
</bean>
//DITest
public class DITest {
@Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
SetterAction setterAction = (SetterAction) context.getBean("setter");
System.out.println(setterAction.getName());
}
}
/*
*向SetterAction里面注入bean setter注入
*/
public interface ServiceInterf {
}
public class LoginService implements ServiceInterf {
public void sayHello(){
}
}
public class LoginAction {
private ServiceInterf loginService;
//setter注入需要提供setter方法.
public void setLoginService(ServiceInterf loginService) {
this.loginService = loginService;
System.out.println(loginService);
}
}
// ApplicationContext.xml
<bean id="service" class="com.xalo.service.LoginService">
</bean>
<bean id="setter" class="com.xalo.action.SetterAction">
<property name="loginService" ref="service" />
</bean>
//DITest
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
SetterAction setterAction = (SetterAction) context.getBean("setter");
//打印出来的结果是:com.xalo.service.LoginService@3d921e20
(2)构造器注入
构造器注入:保证了一些必要的属性在Bean实例化时就设置,并且确保了bean实例在实例化后就可以使用.
1.在类中,不用为属性设置setter方法,只需提供构造方法即可
2.在构造文件中配置该类bean,并配置构造器,在配置构造器中用
public class LoginAction {
private String name;
//提供构造方法
public LoginAction(String name) {
this.name = name;
System.out.println("name:" + name);
}
//ApplicationContext.xml
<bean id="action" class="com.xalo.action.LoginAction">
<constructor-arg index ="0" name="name" value="碧瑶"></constructor-arg>
</bean>
//DITest
public class DITest {
@Test
public void test() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
LoginAction action = (LoginAction) context.getBean("action");
}
/*
*向LoginAction里面注入bean 构造器注入
*/
public interface ServiceInterf {
}
public class LoginService implements ServiceInterf {
public void sayHello(){
}
}
public class LoginAction {
/*
*我们将要使用的类型声明为接口类型,当前类对象和要使用的对象就没有直接的联系.
*主要适合接口进行对接,所以两个类之间的耦合度就降低了
*(不同层之间的耦合度越低越好,同层之间的耦合度越高越好)
*/
private ServiceInterf loginService;
//提供有参的构造方法
public LoginAction(ServiceInterf loginService) {
this.loginService = loginService;
System.out.println(loginService);
}
}
//ApplicationContext.xml
<bean id="service" class="com.xalo.service.LoginService">
</bean>
<bean id="action" class="com.xalo.action.LoginAction">
<constructor-arg name="loginService" ref="service" ></constructor-arg>
</bean>
//DITest
public class DITest {
@Test
public void test() {
//加载配置文件
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
LoginAction action = (LoginAction) context.getBean("action");
}
}
Bean常用的属性:
将要使用的bean,配置在IOC容器中.
IOC: 底层就是工厂模式(map), bean是通过反射机制创建的对象
id: bean对象的唯一标识符,通过id获取对象,同一文件中id不能有重复的.
class: bean的类型.
name: 作用和id类似,但是可以有多个,用逗号隔开(基本不使用)
default- lazy-init:
true: 加载配置文件时,不加载 getBean时,才会加载. 此时该bean还是单例 懒加载
false: 加载配置文件时加载
init-method: bean初始化方法.它会在bean组装之后调用.必须无参,在对象创建好之后(构造方法之后)执行.
destroy-method: bean销毁方法 扫尾工作.在beanFactory关闭时调用,必须无参,只能用于singletonBean.
abstract:
true: 当前bean为抽象模板,不能获取对象.一般用于父类bean,因为父类bean主要是供子类bean继承使用.
false: 非抽象
factory-method: 静态工厂时,从工厂获取对象的静态方法(不常用)
factory-bean: 实例工厂时,要引入的工厂对象, 配合factory-method使用(不常用)
parent: 实际上是对依赖注入的继承关系的确定. 子类bean要使用父类bean的依赖注入的属性值和构造器参数,必须指明父类.
(1)不指定parent时:
1.可以为父类中的属性进行setter注入:
如果父类进行了setter注入,子类对象得不到该值
父类进行了构造器注入,子类对象无法使用该值
4.子类要使用父类的构造器进行注入,子类必须有一个对应的构造器,在此构造器中调用父类的构造器.
(2)指定parent时:
1.子类可以使用父类中进行setter注入的值
//2.子类要使用父类构造器中注入的值,必须在子类中定义一个对应的构造器,在此构造器中调用父类的构造器
public class ParentAction {
private Integer age;
public ParentAction() {
System.out.println("父类的构造方法");
}
public ParentAction(Integer age) {
this.age = age;
}
public Integer getAge() {
return age;
}
}
public class TestAction extends ParentAction {
public TestAction() { }
public TestAction(Integer age) {
super(age); //子类定义对应的构造器
}
// ApplicationContext.xml:
<bean id="parent" class="com.axlo.action.ParentAction">
<constructor-arg name="age" value="18"></constructor-arg>
</bean>
<bean id="action" class="com.axlo.action.TestAction" parent="parent">
</bean>
public class Test {
@org.junit.Test
public void test() {
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
TestAction testAction = context.getBean("action",TestAction.class);
System.out.println(testAction.getAge());
}
}
2.父类进行了构造器注入,如果子类没有对应的构造器方法,获取子类对象就会失败
如果子类有对应的构造器方法,但是并没有调用父类的构造器,子类对象可以正常获取,但是无法使用父类构造方法的值
如果子类既有对应的构造器方法,又调用父类的构造器,子类和父类对同一属性都进行依赖注入,父类和子类都用自己的值.
depends-on:
当前的bean依赖于 depends-on 所指定的bean,这个对象会在bean初始化之前创建.
scope: 指定当前bean的模式
singleton: 单例模式 默认: 在beanFactory作用范围内,只维护此bean的一个实例.在服务器启动时(容器初始化之前)创建.主要用与service, Dao层还有一些utils工具类等,只需要在服务器启动时初始化一次即可.
prototype: 原型模式 每次getBean获取对象时,都重新实例化一个对象.在SSH项目中主要用于action对象,这种方式一般在服务器启动时不会创建对象,在每次使用时才创建.
request: 将该对象放入request域中
session: 将对象放入 session域中
global session: 全局session
autowire: 自动装配(自动依赖注入)
先对类中的setter或者构造方法进行扫描,在配置文件中,扫描所有的bean,找到对应的id进行注入.如果没有匹配的id,就不进行注入.
优点: 配置简单,容错率低
缺点:影响项目的性能
byName: 根据id注入(setter)
byType: 根据类型注入(setter)
constructor: 既可以根据id 也可以根据类型(构造器注入)
先根据id匹配,再根据类型匹配.
no: 不使用自动注入
bean的常用的子标签: (几乎都是注入用的)
constructor-arg: 构造器注入使用
index: 要注入的构造方法的参数位置 从0开始.如果不设置index,自上而下为对应位置的参数赋值
name: 指定参数名称,构造方法的形参名称
type: 指定注入数据的类型,当使用构造器注入时,如果参数除了类型不一样,
其它都一样需要指定type来区分执行哪一个构造方法
value: 要注入的值,必须和对应的参数的类型匹配
[简单值 (四类八种 字符串)]
ref: 要注入的值是一个 JAVA bean, 写bean的 id.
如果没有index也没有name,必须按照参数的顺序进行注入
property: setter注入使用
根据property标签的name属性的值去找对应的setter方法.
name: setter方法对应的属性名
ref: 要注入的bean的id
value: 要注入的值
构造器注入,对象中需要提供有参的构造方法
集合类型的注入
<!-- 构造器注入:
1.不够灵活
2.会影响对象的创建效率-->
public class LoginAction {
private List<Integer> list;
private Map<String, String> map;
<!--构造器依赖注入,对象中需要提供有参的构造方法-->
public LoginAction(List<Integer> list){
this.list = list;
System.out.println(list);
}
public LoginAction(Map<String, String> map) {
this.map = map;
System.out.println(map);
}
}
<!--ApplicationContext.xml-->
<!--spring-framework-5.0.7.RELEASE docs spring-framework-reference pdf core.pdf 9.1.4-->
<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"
default-lazy-init="true">
<bean id="action" class="com.xalo.action.LoginAction">
<!-- 集合类型的注入 -->
<constructor-arg name="list">
<!-- 创建了一个list对象 -->
<list>
<!-- 给list中添加元素 -->
<value>1</value>
<value>2</value>
<value>3</value>
</list>
</constructor-arg>
<!--给map中添加值时,如果属性名一样,后面的值会覆盖前面的值 -->
<constructor-arg name="map">
<map>
<entry key="name" value="张小凡" />
<entry key="age" value="12" />
<entry key="name" value="道玄真人" />
</map>
</constructor-arg>
</bean>
</beans>
<!--DITest-->
public class DITest {
@Test
public void test(){
<!--加载配置文件-->
ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
LoginAction action = (LoginAction) context.getBean("action");
}
}