一、Spring 核心
IOC(控制反转) + AOP(面向切面编程)
IOC:Inversion Of Control 控制反转
其实把对象的创建工作这个控制权交给spring。AOP:面向切面编程
其实就是在不改动原来代码的前提下, 对这份功能进行增强、扩展。
二、IOC的演变过程
三、Spring 入门案例 及 配置详解
导入jar包
我们学习的Spring的版本是4.2.9版本,完全的Spring的jar包共有60个之多,分为三个三个一组,而我们写基本的Spring程序只需要导入核心的四个jar包就可以了
core | bean | context | expression
在src下新建xml文件 ,并且导入约束
约束文件的名字一般情况下写作是applicationContext.xml,当然也可以是别的名字
找约束文件的模板:在Spring的官方提供的文档里面:Spring\docs\spring-framework-reference\html,拉到最下面的一个文件:xsd-configuration.html,里面有详细的约束。
也可以手动导入约束模板!!使用bean标签指定spring托管具体类
配置applicationContext.xml,注意这是xsd的约束,写法和dtd的约束写法不尽相同,所有的标签都写在根标签之内
<?xml version="1.0" encoding="UTF-8"?>
<!-- 根标签已经固定 -->
<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">
<!-- 告诉spring,让它托管这个类,以便以后,我们只要拿着us 来找他的工厂,就能够创建这个类的实例给我们 -->
<!-- bean : 代表的是要托管的一个类。 spring把所有被托管的类, 都看成是 bean
id : 托管的而每一个bean 都给一个标识符, 这个标识符具有唯一性。
class: 托管的每一个类的全路径地址,因为spring后面要根据这个路径通过反射来创建该类的实例
scope : 指定创建该类实例是单例还是多例, 默认是单例, singleton . 如果想创建多例, 那么指定prototype,如果是单例的情况下,是在工厂一加载的时候就会创建对象
-->
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" scope="prototype"></bean>
</beans>
- 在代码里面通过工厂获取实例对象
//1.获得工厂实例
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
//2. 要问工厂要实例,但是我们得告诉工厂要谁的实例。
UserService userService = (UserService) context.getBean("us");
userService.save();
//这行代码以后都不会写了,因为咱们以后写的是web工程, 工厂以后也不是我们创建 工厂会一直跟着项目
((AbstractApplicationContext) context).close();
- 由于spring框架里面输出日志需要第三方的日志包,所以还需要导入日志jar包,还有log4j的日志文件
官方推荐使用slf4j的日志包,更加的高效。
配置详解:
applicationContext.xml:
<?xml version="1.0" encoding="UTF-8"?>
<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">
<bean id="userService" class="com.xxx.service.impl.UserServiceImpl" scope="prototype"></bean>
</beans>
<beans> 根标签:
根标签已经固定,xsd约束里面中,所有的其他标签都写到根标签内部
<bean>标签:
bean代表要托管的一个类,spring把所有要托管的类都看做是一个bean;
id属性: bean的id属性不能够重名,具有唯一性。,否则会报错。
一般情况下,id就认为写成我们之前写的接收创建出来的对象的变量名即可。如userService
scope属性:工厂默认创建实例是单例模式singleton;多例:scope="prototype"
四、Spring实例化方式
1. 采用无参构造方式实例化(重要:常用)
默认spring实例化某一个类,调用的是该类的无参构造方法, 所以要实例化的类,需要提供无参构造。
public class UserServiceImpl implements UserService {
public UserServiceImpl() {
}
@Override
public void save() {
System.out.println("调用了UserServiceImpl 的 save方法");
}
}
配置:
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" scope="prototype"></bean>
2. 采用静态工厂方法实例化
工厂类:
注意不要忘记将获得bean的方法声明成为静态
public class StaticFactory {
public static UserService getBean(){
return new UserServiceImpl();
}
}
配置:
<!-- 以下是静态工厂实例化方式配置
这种方式要求,我们自己维护工厂,这太复杂了,第一种无参的方式,是spring自己维护工厂。
class : 指定工厂的位置
factory-method : 指定工厂的方法。
一旦我们拿着us 去问 spring的工厂要东西。 spring的工厂就会找到我们的工厂,由于我门的工厂方法也是静态的。并且也根据factory-method 属性告诉spring方法叫什么了。 所以spring直接来调用那个静态方法返回对象。现在看上去,创建这个类的实例工作,并不是spring来做。 spring只不过是一个中间人,来调用我的工厂方法而已。
-->
<bean id="us" class="com.xxx.factory.StaticFactory" factory-method="getBean"></bean>
3. 采用实例化工厂方法获取实例
工厂:
public class InstanceFactory {
public UserService getBean(){
return new UserServiceImpl();
}
}
配置:
我们必须让spring来实例化这个工厂,然后以后我们拿着us 问spring要东西的时候
它就去找刚才它做出来的那个工厂实例,然后调用getBean方法 返回对象。
<bean id="factory" class="com.xxx.factory.InstanceFactory"></bean>
<bean id="us" factory-bean="factory" factory-method="getBean"></bean>
不管是静态工厂还是实例化工厂,都有一个共同点,就是创建实例的工作还是我们自己来做,并且我们要维护自己的工厂。spring的工厂只不过是做一个跳转的作用而已。 所以后面两种方式(使用静态工厂方法获得实例、采用实例工厂方法获得实例) 几乎不会用。
五、注入 - Dependency Injection
注入的简称是DI , 全称是 dependency Injection 翻译过来是依赖注入。
- 什么是依赖注入
依赖注入其实指的就是我们的某些类里面有成员属性, 要求在创建这个类的实例同时,完成这些成员属性的赋值工作。但是现在创建实例的工作不是我们来做,是spring来做。说的通俗一点,注入其实就是告诉spring在创建实例的时候,把这些成员属性也给赋值。
1. 采用构造方法注入
public class UserServiceImpl implements UserService {
private String address;
public UserServiceImpl(String address) {
super();
this.address = address;
}
@Override
public void save() {
System.out.println("调用了UserServiceImpl的save方法==" + address);
}
}
配置:
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" scope="prototype">
<!-- 由于UserServiceImpl 有一个属性叫做 name , 我们还采用了构造方法赋值。 但是到底赋什么值, 我们也需要告诉sprig -->
<constructor-arg name="address" value="奥巴马"></constructor-arg>
<constructor-arg name="password" value="123"></constructor-arg>
</bean>
2. 采用set方法注入(重要 - 常用)
1.字符串类型
bean:需要提供set方法
private String address;
public void setAddress(String address) {
this.address = address;
}
配置:
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" scope="prototype">
<property name="address" value="深圳"></property>
</bean>
2. 注入数组
private String []address;
public void setAddress(String[] address) {
this.address = address;
}
配置:
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" scope="prototype">
<property name="address">
注入数组数据
<array>
<value>北京1</value>
<value>北京2</value>
<value>北京3</value>
<value>北京4</value>
</array>
</property>
</bean>
3. 注入list
private List<String> address;
public void setAddress(List<String> address) {
this.address = address;
}
配置:
<bean id="us" class="com.XXX.service.impl.UserServiceImpl" scope="prototype">
<property name="address">
<list>
<value>北京11</value>
<value>北京22</value>
<value>北京33</value>
<value>北京44</value>
</list>
</property>
</bean>
4. map
private Map<String , String> address;
public void setAddress(Map<String, String> address) {
this.address = address;
}
配置:
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" scope="prototype">
<property name="address">
<map>
<entry key="地址1" value="北京1"></entry>
<entry key="地址2" value="北京2"></entry>
<entry key="地址3" value="北京3"></entry>
<entry key="地址4" value="北京4"></entry>
</map>
</property>
</bean>
5.对象
类:
public class UserServiceImpl implements UserService {
private UserDao userDao ;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void save() {
System.out.println("调用了UserServiceImpl 的 save方法====" );
userDao.save();
}
}
配置
<!-- 因为要告诉spring一会注入的是这个类的实例,所以必须在这里声明这个bean -->
<bean id="userDao" class="com.xxx.dao.impl.UserDaoImpl"></bean>
<bean id="userService" class="com.xxx.service.impl.UserServiceImpl" scope="prototype">
<property name="userDao" ref="userDao"></property>
</bean>
3. 采用名称空间 & Spring EL 属性注入
使用名称空间注入需要加入一行配置:
这一行配置可以去官方给的网页资源里面去找到的
<?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:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"
xsi:schemaLocation=" http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd ">
</beans>
- c名称空间
走构造函数的方式进行数据注入,需要提供对应的构造方法
<bean id="userService" class="com.xxx.service.impl.UserServiceImpl"
scope="singleton" c:username="ceshi" c:password="123">
</bean>
- p名称空间
走set方法进行数据注入,需要提供对应的set方法
<bean id="userService" class="com.xxx.service.impl.UserServiceImpl"
scope="singleton" p:username="ceshi" p:password="123">
</bean>
- spring EL 表达式注入
这种方式需要提供无参数的构造函数
<bean id="userService" class="com.xxx.service.impl.UserServiceImpl"
scope="singleton">
<property name="username" value="#{'测试22'}"></property>
<property name="password" value="#{'112233'}"></property>
</bean>
<bean id="us" class="com.xxx.service.impl.UserServiceImpl" >
<property name="address" value="#{1>2}"/>
</bean>
整合Servlet和Spring的简单实例:
servlet代码:
public class ServletTest extends HttpServlet {
private static final long serialVersionUID = 1L;
@SuppressWarnings("resource")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("执行了doget方法");
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.save();
//((AbstractApplicationContext) applicationContext).close();
}
}
这段代码的问题:
1)每次来一个请求,都会执行doget方法,每次执行doget方法都会创建ClassPathXmlApplicationContext这个类的实例。
2)工厂实例创建的实际稍微晚了。因为请求来了才创建工厂。我们其实可以让工厂的创建稍微提前一点。
改进:
1)要想用监听器,得导入一个spring的jar包中的包:spring-web-4.2.9的包;
2)配置监听器:在web.xml中
<listener>
<!-- 配置了这个监听器就可以感知到servletContext被创建,一旦感知到servletContext被创建,就知道项目已经被部署了,
那么这个监听器就会解析xml,然后立即创建工厂 -->
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 告诉spring,我们的xml配置文件在哪里
classpath:applicationContext.xml就是告诉spring要到类路径去找这个文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
3)生成工厂:
@SuppressWarnings("resource")
protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("执行了doget方法");
WebApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(getServletContext());
UserService userService = (UserService) context.getBean("userService");
userService.save();
}
工厂创建的时候,创建之后会把工厂放到servletcontext作用域中去,所以可以这样取:
可以看看源码,上面一种方法的背后也是使用下面这种方式来取值的。