Spring 框架学习

spring框架的介绍

   Spring框架是一个轻量级的控制反转(IOC)和面向切面(AOP)的容器框架。

   记录了一些关于框架特点和特性以及Spring工作流程图。
比如:

  • IOC
  • AOP的底层原理
  • 属性注入
  • 注解
  • bean管理

一、 一些基本特性

轻量:大小与开销轻量级,完整的spring框架可以在大小只有1MB多的JAR文件中发布,且spring框架是非侵入式的,spring应用不依赖于spring特定类。

控制反转:Spring通过一种称为控制反转的(IOC)的技术促进松耦合。当应用了IOC,一个对象依赖的其他对象通过被动的方式传递进来,而不是对象自己创建或者查找依赖对象。不是对象从容器中查找依赖,而是容器在对象初始化的不等对象请求就主动依赖传递过去.把对象的创建不是通过new方式,而是交给spring配置创建类对象。

面向切面:Spring提供面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务进行内聚性的开发(AOP)。

容器:Spring包含并管理应用对象的配置和生命周期,可以配置每个bean如何被创建——基于一个可配置原型(prototype),你的bean可以可以创建一个单独的实例,或每次需要都生成一个新的实例——以及他们是如何相互关联。

框架:Spring框架可以将简单的组件配置,组合成为复杂的应用。在Spring应用对象被声明组合,典型是在一个XML文件里面,Spring也提供许多基础功能(事务管理,持久化框架集成,自己开发应用逻辑)


二、 Spring流程图

解析:这个图在Struts结构图的基础上加了spring流程图,在web.xml配置文件中加入了spring监听器,在Struts配置文件中添加是告知Struts2运行的时候使用spring来创建对象,spring在其中要做就是注入实例,将所有需要类的实例都由spring管理。

三、 深入了解

Spring是一站式框架

spring在Javaee三层结构:

每一层都提供不同的解决技术。

  • web层:SpringMVC
  • service层:Spring的IOC
  • DAO层:Spring的jdbcTemplate

该笔记的学习是基于spring4.x版本

业精于勤 ,荒于嬉;行成于思,毁于随

spring的IOC操作

把对象创建交给spring进行管理

ioc操作两部分:

(1)IOC的配置文件方式
(2)IOC的注解方式

IOC的底层原理

  • xml配置文件
  • dom4j解决xml
  • 工厂设计模式
  • 反射
1
2
3
4
5
6
7
public class UserService{
}
public class UserServlet{
//得到UserServlet的对象
//原始:new创建
uersFactory.getService();
}

第一步 创建xml配置文件,配置要创建对象类

1
<bean id ="userService" class="cn.itcast.Userservice"/>

第三步  创建工厂类,使用dom4j解析配置+反射

1
2
3
4
5
6
7
8
9
10
11
12
public class Userfacorty{
//返回UserService对象的方法
}
public static UserService getService(){
//使用dom4j解析xml文件
//根据id值userService,得到id值对应class属性值
String class = "class属性值";
class clazz= class.forname(classValue);
//创建类对象
Userservice service =clazz.newInstance();
return service;
}

在创建spring配置文件的时候注意,spring核心配置和位置不是固定的,建议放到src下面,官方建议的名称叫applicationContext.xml

配置的时候引入schema约束
可以在docs文档 →spring-framenwork-reference →html里找最后一个configuration.xsd里有

IOC和DI的区别

  • IOC:控制反转,把对象创建交给spring进行配置
  • DI :依赖注入,向类里面的属性中设置值
  • 关系:依赖注入不能单独存在,需要在IOC的基础上完成操作的。

Spring的Bean管理(XML配置文件)

Bean实例化的三种方式

使用类的无参数构造创建

1
2
<!-- ioc入门 -->
<bean id ="user" class="cn.itcast.ioc.User"></bean>

如果类里面没有无参数的构造,就会出现异常

使用静态工厂创建

创建类的方法,返回类的对象

1
2
3
4
5
6
public class Bean2Factory{
//静态方法,返回Bean2对象
public static Bean2 getBean2(){
return new Bean2();
}
}

1
2
<!-- 使用静态工厂创建对象 -->
<bean id="bean2" class="cn.itcast.bean.Bean2Factory" factory-method="getBean2">

使用实例工厂创建

创建不是静态的方法,返回类对象

1
2
3
4
5
6
public class Bean3Factory{
//普通的方法。返回bean3对象
public Bean3 getBean3(){
return new Bean3();
}
}

Bean的常用标签

id属性

起名称,不能包含特殊符号
根据id值得到配置对象

class属性

创建对象所在类的全路径

name属性

根据属性值得到配置对象
但在name属性里可以包含特殊符号
现在不怎么用,都是旧版本的遗留问题

scope属性

  • singleton:默认值,单例

  • prototype:多例

  • request:创建对象把对象放到request域里面
    (request的生命周期是一次访问,如果登陆信息放在request域的话,第二次访问就丢失了)

  • session:创建对象把对象放到session域里面
    (session域的生命周期是默认30分钟,也叫一次会话,当浏览器访问的时候,就创建了session,当浏览器关闭了,或者超过30分钟了,session就会关闭。

  • globalSession:创建对象把对象放到globalSession里面

属性注入介绍

创建对象的时候,向类里面属性设置值

属性注入的三种方式

  • 使用set方法
    1
    2
    3
    4
    5
    6
    public class User{
    private String name;
    public void setName(String name){
    this.name = name;
    }
    }
1
2
User user =new User();
user.setName("abcd");

使用有参数的结构注入

1
2
3
4
5
6
public class User{
private String name;
public User(String name){
this.name = name;
}
}
1
User user =new User("lucy");

使用接口注入

1
2
3
public interface Dao{
public void delete(String name);
}
1
2
3
4
5
6
public class DaoImpl implements Dao{
private String name;
public void delete (String name){
this.name = name;
}
}

注意的地方
在spring框架里面,支持前两种方式

  • set方法注入(重点)
  • 有参数结构注入

    使用有参数构造注入属性

1
2
3
4
<!-- 使用有参数构造注入属性 -->
<bean id ="demo" class ="cn.itcast.property.PropertyDemo1">
<!-- 使用有参数构造注入 -->
<constructor-arg name="username" value ="tom"></constructor-arg>
1
2
3
4
5
6
7
private String name ;
public PropertyDemo1(String user name){
this.username =username;
}
public void test1{
System.out.println("demo1----"+username);
}

使用set方法注入属性(重点)

1
2
3
4
5
private String bookname;
//set 方法
public void setBookname(String bookname){
this.bookname =bookname;
}
1
2
3
4
5
6
7
<!-- 使用set方法注入属性 -->
<bean id ="book" class ="cn.itcast.property.Book">
<!-- 注入属性
name属性值,类里面定义的属性名称
value属性:设置属性的值
-->
<property name ="bookname" value ="hamlet"></property>

注入 对象类型 属性(重点)

创建service类和dao类

  • 在service得到dao对象

具体的实现过程

  • 在service 里面把dao作为类型属性
  • 生成dao类型属性的set方法
1
2
3
4
5
6
7
8
public class UserService{
//1定义dao类型属性
private UserDao userDao;
//2生成set方法
public void setUserDao(UserDao userDao){
this.userDao =userDao;
}
}
  • 配置文件中注入关系
1
2
3
4
5
6
7
8
9
<!-- 1配置service和dao对象 -->
<bean id = "userDao" class ="cn.itcast.ioc.UserDao"></bean>
<bean id= "userService" class ="cn.itcast.ioc.Userservice">
<!-- 注入dao对象
name属性值:service类里面属性名称
但是现在不要写value属性,因为之前的是字符串,现在是对象 。
写ref属性:dao配置中的bena标签的id值
-->
<property name ="userDao" ref ="userDao"></property>

P名称空间注入

修改配置文件,加入下面这一条

1
xmls : p ="http://www.spring.framework.org/schema/p"

然后再加入这条

1
2
<!-- P名称空间注入 -->
<bean id ="person" class= "cn.itcast.property.Person" p :name="Lucy">

注入复杂类型属性

  • 数组
  • list集合
  • map集合
  • properties类型
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
<!-- 注入复杂类型属性 -->
<bean id = "person" class ="cn.itcast.property.Person">

<!-- 数组 -->
<property name="arrs">
<list>
<value>小王</value>
<value>小马</value>
<value>小宋</value>
</list>
</property>


<!-- list -->
<property name="list">
<list>
<value>小奥</value>
<value>小金</value>
<value>小普</value>
</list>
</property>


<!-- map -->
<property name="map">
<map>
<entry key="aa" value=_lucy"></entry>
<entry key="bb" value="mary"></entry>
<entry key="cc" value="tom"></entry>
</map>
</property>


<!-- properties -->
<property name="properties">
<props>
<prop key="driverclass">com.mysql.jdbc.Driver</prop>
<prop key="username">root</prop>
</props>
</property>

</bean>

Spring的bean管理(注释)

关于注解

  • 代码里特殊的标记,使用注解可以完成功能
  • 注解写法 @ 注解名称(属性名称=属性值)
  • 注解使用在类、方法属性上面

注解开发准备

  1. 导入的基本包
  1. 导入jar包的时候要多一个sop的包
  1. 创建类、创建对象

  2. 在创建Spring配置文件,引入约束
    (1)做ioc基本功能,引入约束beans
    (2)做Spring的IOC注解发开,引入新的约束

  1. 开启注解扫描

注解创建对象

  • @Component:组件(作用在类上)
  • 三个衍生注解
    @ Controller:web层
    @Service:业务层
    @Repository :持久层
    目前这个四个功能差不多,只是为了区分用途,都是创建对象

  • 创建是单实例还是多实例

      @ scope(value=“prototype”)
    

注解注入属性

第一个注解 @Autowired

  1. 创建service类,创建dao类
1
2
3
@Service (value="userService")
public class Userservice {
}
1
2
3
4
5
6
7
8
@Component (value="userDao")
//注解里的value可以不写
//写成Component("userService")也是可以的
public class UserDao {
public void add() {
System.out.println("Dao----")
}
}
  1. 在service里面定义dao类型属性
1
2
3
@Autowired
private UserDao userDao;
//使用注解的方法不需要set方法

注入属性第二个注解 @Resource

1
2
3
//name的属性值写注解创建dao对象,也就是写value值
@Resource(name="userDao")
private UserDao userDao;

Autowired和Resource的区别

  1. @Resource默认按照名称方式进行bean匹配,@Autowired默认按照类型方式进行bean匹配,就是Autowired去匹配的时候是找类,不是名称,而Resource是按照名称来寻找。

  2. Spring属于第三方的,J2EE是Java自己的东西。使用@Resource可以减少代码和Spring之间的耦合。

  3. 两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。

@Resource装配顺序:
①如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。

②如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常。

③如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。

④如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配。
@Resource的作用相当于@Autowired,只不过@Autowired按照byType自动注入。

配置文件和注解混合使用

  1. 在创建对象操作使用配置文件方式实现

    1
    2
    3
    4
    <!-- 配置对象 -->
    <bean id ="bookService" class="cn.itcast.xmlanno.BookService"></bean>
    <bean id ="ordersDao" class= "cn.itcast.xmlanno.OrdersDao"></bean>
    <bean id ="bookDao" class ="cn.itcast.xmlanno.BookDao"></bean>
  2. 注入属性的操作使用注解方式实现

1
2
3
4
5
//得到bookdao和ordersdao的对象
@Resource(name="bookDao")
private BookDao bookDao;
@Resource(name="ordersDao")
private OrdersDao ordersDao;

AOP

AOP概念

  1. aop:面向切面(方面)编程,扩展功能不修改源代码实现
  2. AOP采取横向抽取机制,取代了传统纵向继承体系重复性代码

AOP原理

下图是最原始方法和纵向抽取机制解决方法

AOP:横向抽取机制
底层使用动态代理方式实现
看下图

AOP操作术语

  • Joinpoint(连接点):
    类里面可以被增强的方法,这些方法称为连接点

  • Pointcut(切入点):
    所谓切入点是指我们要对哪些Joinpoint进行拦截的定义.

  • Advice(通知/增强):
    所谓通知是指拦截到Joinpoint之后所要做的事情就是通知.通知分为前置通知,后置通知,异常通知,最终通知,环绕通知(切面要完成的功能)

  • Aspect(切面):
    是切入点和通知(引介)的结合

  • Introduction(引介):
    引介是一种特殊的通知在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.

  • Target(目标对象):
    代理的目标对象(要增强的类)

  • Weaving(织入):
    是把增强应用到目标的过程.把advice 应用到 target的过程

  • Proxy(代理):
    一个类被AOP织入增强后,就产生一个结果代理类

AOP操作

  1. 在Spring里面进行AOP操作,使用aspectj实现
    • aspectj不是spring一部分,和spring一起使用进行aop操作
    • Spring2.0以后新增了对AspectJ支持
  2. 使用aspectj实现aop有两种方式
    • 基于aspectj的xml配置
    • 基于aspectj的注解方式

AOP操作准备(包含引入约束)

  1. 除了最基本的jar包,还需要导入跟AOP相关的jar包

    • spring-aop-4.2.2.RELEASE.jar
    • spring-aspects-4.2.2.RELEASE.jar
      后两个是要另外下载,不在spring包里面
    • aspectjweaver-1.8.7.jar
    • aopalliance-1.0.jar
  2. 创建spring核心配置文件,导入aop的约束

     在docs文档里面查找AOP的约束
    
    1
    2
    3
    4
    5
    6
    7
    <?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="
    http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here -->
    </beans>

使用表达式配置切入点

  1. 切入点:实际增强的方法

  2. 常用的表达式
    execution(<访问修饰符>?<返回类型><方法名>(<参数>)<异常>)

    1. execution(* cn.itcast.aop.Book.add(..))
      匹配cn.itcast.aop.Book.add的方法,参数用..表示
    2. execution( cn.itcast.aop.Book.(..))
      匹配所有cn.itcast.aop.Book.*中的方法
    3. execution( .*(..))
      匹配所有类中的所有方法

    4. 匹配所有save开头的方法 execution( save(..))

AOP配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<!--  配置对象 -->
<bean id ="book" class = "cn.itcast.aop.Book" ></bean>
<bean id ="myBook" class ="cn.itcast.aop.MyBook"></bean>

<!-- 2配置AOP操作 -->
<aop:config>
<!-- 2.1配置切入点 -->
<aop:pointcut expression="execution(* cn.itcast.aop.Book.add(..))" id="pointcut1"/>

<!--
注意在表达式前面第一个修饰符 * 号的后面要加空格
id值可以随便写,相当于起名字 -->

<!-- 2.2配置切面,把增强用到方法上面 -->
<aop:aspect ref="myBook">
<!-- 配置增强类型
method:增强类里面选择哪个方法做前置 -->
<aop:before method="before1" pointcut-ref="pointcut1"/>
</aop:aspect>
</aop:config>

加入其他配置

true
1
2
public void around() {
System.out.println("环绕增强");

1
2
3
4
5
<aop:aspect ref="myBook">
<!-- 配置增强类型
method:增强类里面选择哪个方法做环绕 -->
<aop:around method="around" pointcut-ref="pointcut1"/>
</aop:aspect>

输出结果 :

环绕增强

这种方法应该是属于替换了原来的方法,
但是如果修改了around的参数的话,效果很不同,就真的是环绕
如下

1
2
3
4
5
public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
System.out.println("环绕增强之前");
proceedingJoinPoint.proceed();
System.out.println("之后");
}

然后配置文件不变
输出结果:

环绕增强之前
add---------Book.java
之后

log4j

log4j介绍

  1. 通过log4j可以看到程序运行过程中更详细的信息
        经常使用log4j查看日志
  2. 使用方法
    1. 导入log4j的jar包
    2. 复制log4j的配置文件,复制到src下面
      配置文件:log4j.properties

其中log4j.properties的内容中

log4j.rootLogger=info, stdout

是代表了日志级别

  1. 设置日志级别
    1. info:看到基本信息
    2. debug:看到更详细信息

Spring整合web项目原理

基础原理

加载Spring核心配置文件

  • new对象,功能可以实现,但是效率很低

    实现思想:
    把加载配置文件和创建对象过程,在服务器启动时完成

实现原理

  • serlvletContext 对象
  • 监听器
  • 具体使用
    (1)在服务器启动时,为每一个项目创建一个servletContext对象
    (2)在servletContext 对象创建时,可以用监听器具体到servletContext对象在什么时候创建。
    (3)当使用监听器听到servletContext对象创建的时候,加载spring配置文件,创建配置对象。
    (4)把创建出来的对象放到ServletContext域对象里面(setAttribute方法)
    (5)获取对象时候,到ServletContext域得到 (getAttribute方法)

web项目演示

  1. 演示问题
    • action 调用service,service调用dao

每次访问action的时候,都会加载spring配置文件
功能没问题,可是性能不行

1
2
3
4
5
6
信息: Deployment of web application directory C:\Program Files\Java\apache-tomcat-7.0.90\webapps\manager has finished in 90 ms
action..........
21:09:55,491 INFO ClassPathXmlApplicationContext:578 - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@4a3ef897: startup date [Wed Jul 25 21:09:55 CST 2018]; root of context hierarchy
21:09:55,492 INFO XmlBeanDefinitionReader:317 - Loading XML bean definitions from class path resource [applicationContext.xml]
service..........
dao..........

  1. 解决方案
    1. 在服务器启动的时候,创建对象加载配置文件
    2. 底层使用监听器,servletContext对象
  2. spring里面不需要我们自己写代码,已经封装好了,这就是使用框架的好处
    1. 封装了一个监听器,只需要配置监听器就可以了
    2. 配置监听器之前,导入spring整合web项目jar包
      spring-web-4.2.4.RELEASE.jar
1
2
<!-- 配置监听器 --> <listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class> </listener>

指定加载spring配置文件,因为默认是找不到位置的。

1
2
3
<!-- 指定spring配置文件位置 --> <context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value> </context-param>
Just for fun!
------------- 文章已经到尾 -------------