Spring详解

6 篇文章 0 订阅

Spring技术(框架)

一、Spring引言

Spring框架:

​      集众多优秀的设计模式为一体的开源、轻量级项目管理框架。JavaEE轻量级解决方案。

​ 工厂、单例、代理、策略、模板。。。。。 Spring 3.X

轻量级项目管理框架:

	轻量级:代码的侵入性比较小。
	struts、mybatis框架 ---> MVC分层中某一层技术存在明显的不足,具体的业务替换。
	Spring项目管理框架:单层框架进行优化,使用优秀的设计模式揉合现有的单层框架进行整合管理。
	
	Spring框架: 管理 项目中组件的创建使用以及销毁。  对象---> dao service action 组件对象
			    Spring是一个容器。
			    Spring通常不对实体类进行管理创建。

工厂设计模式

概念:通过工厂类,完成对象的创建或者生产实例化。

好处:替代原始的直接new对象,通过反射来动态创建类的对象,系统–解耦和。 扩展性,开闭原则。

步骤:

1. 完成基础类的开发 (接口 实现类 工厂类)
	2. 使用配置文件配置类信息  key=value
	3. 调用工厂来创建对象 避免了new关键字 解耦和

Spring工厂类 + 配置文件 来完成用户自定义对象或者组件的创建(解耦和)

二、第一个Spring程序(Hello World)

1.搭建环境

引入jar包 Spring不提供三方jar 详见资料

2.配置文件

Spring: 位置随意,名称随意。 学习阶段建议:applicationContextxml

3.初始化配置信息

​ 不需要任何初始化配置。

Spring程序 — 核心API

1.Spring的工厂类
	ApplicationContext  工厂类  接口!!!
	
	工厂类实现:(了解)
		ClassPathXmlApplicationContext("配置文件路径");     使用类路径完成配置信息的载入
		
		WebXmlApplicationContext(路径);  应用才Web环境下读取xml配置文件
		FileSystemXmlApplicationContext(路径) 应用在文件系统中
		
	工厂一定是一个重量级的资源,工厂只有一个。  线程安全,占用内存较多。
	
	创建:ApplicationContext ctx = new ClassPathXmlApplicationContext("文件路径");

Spring代码步骤

  1. 基础代码书写
  2. 配置文件配置
//id唯一标示   class 权限定名
<bean id="user"  class="first.User"></bean> 
         
<bean id="userDAO"  class="first.UserDAOImpl"></bean>
  1. 通过Spring的工厂类来创建对象
@Test
	public void test1(){
		//工厂类的创建
		ApplicationContext ctx = 
            new  ClassPathXmlApplicationContext("/first/applicationContext.xml"); //路径
		User user = (User) ctx.getBean("user"); //user 来源于配置文件的 id
		UserDAO userDAO = (UserDAO) ctx.getBean("userDAO"); //userDAO 来源于配置文件的 id
		System.out.println(user);
		System.out.println(userDAO);
	}


三、注入(Injection)

可以使用配置文件来为创建出来的对象进行赋值 — 注入
关注语法如何实现,并不关系项目如何使用。

<!-- 可以通过配置文件为创建的对象 赋值  语法讲解 -->     
<bean id="user"  class="first.User">
    <property name="id" value="1"></property>
    <property name="name">
        <value>jinqiu</value>
    </property>
</bean> 

1. Set注入

1. JDK中所存在的数据类型

1.1 八种基本类型 + String 详细见上图

1.2 数组

User : private String s[];

<property name="s">
    <array>
        <value>1</value>
        <value>2</value>
        <value>3</value>
    </array>
</property>

1.3 集合 Set List Map

User : private Set<Object> set;

<property name="set">
    <set>
        <value>1</value>
        <value>2</value>
        <value>3</value>
    </set>
</property>
    
User : private List<Object> list;   
    
<property name="list">
    <list>
        <value>1</value>
        <set>
            <value>1</value>
        </set>
    </list>
</property>
    
注意: 所有的数据的存储不一定都是 <value></value> 具体由实际存储的数据本身来决定
 
 User : private Map<String,String> maps;  
 <key></key> 只是标识了里面的数据是键 键具体是什么样的数据值要视情况而定(基本<value> 其他对应的标签)

    <property name="maps">
        <map>
            <entry>
                <key><value>1</value></key>
                <value>1</value>
            </entry>
            <entry>
                <key><value>2</value></key>
                <value>2</value>
            </entry>
        </map>
    </property>
    
特殊 : Properties类型 : Map<String,String>
    
    <property name="maps">
        <props>
			<prop key="1">1</prop>
             <prop key="2">2</prop>
        </props>
    </property>

用户自定义类型

通常所指的实体类 —> UserDAOImpl UserServiceImpl

遵循之前的赋值思想:

​ UserServiceImpl 中去使用 UserDAOImpl的实现对象 注入 的赋值思想

​ (先创建UserDAO 将其注入到UserService)

​ 在UserServiceImpl 定义成员变量 UserDAO set/get方法 ,使用配置文件对于成员变量进行赋值。

在这里插入图片描述

<!-- 需要去创建UserDAOImpl的实现 -->
<bean id="uDAO"  class="first.UserDAOImpl"></bean>   

<!-- 需要去创建UserServiceImpl的实现  Service中需要使用 UserDAOImpl的实现-->  
<!-- UserDAO定义为成员变量 使用set注入赋值 -->    
<bean id="userService"  class="first.UserServiceImpl">
    <property name="userDAO">
        <ref local="uDAO"></ref>  <!-- 注入赋值 uDAO  就是  UserDAOImpl 中id值  -->  
    </property>
</bean>    

注入的应用:解耦和 — 使用配置文件严格遵循开闭原则

Spring框架如何实现注入
1. 使用bean标签完成声明
	<bean id="uDAO"  class="first.UserDAOImpl"></bean>
	<bean id="userService"  class="first.UserServiceImpl"></bean>

2. 定义成员变量
	private UserDAO userDAO; set、get

    Spring工厂: 读取配置文件中class,使用反射获得类对象Class,先创建类的对象。

3. Spring调用类中的Set方法为成员变量进行赋值操作。
	<bean id="userService"  class="first.UserServiceImpl">
        	<property name="userDAO">
        		<ref local="uDAO"></ref>
        	</property>
     </bean>

2. 构造注入(了解)

使用构造方法完成对于成员变量的赋值操作

  1. 定义构造方法(有参构造)
  2. 配置文件的配置
<!-- 使用构造方法注入 -->  
<bean id="user" class="first.User">
    <!-- 调用指定的构造方法 -->
    <constructor-arg type="String">
        <value>1</value>
    </constructor-arg>
    <constructor-arg type="int">
        <value>2</value>
    </constructor-arg>
</bean>   

<constructor-arg></constructor-arg> 按照参数列表的顺序一一赋值
type="参数类型"
根据配置文件自动去匹配对应的构造方法

四、Spring工厂 FactoryBean技术

1、使用Spring工厂核心目的:创建对象!

1. 简单对象
  1.1 书写对应类
  1.2 配置文件的配置
  1.3 配置文件中为对象赋值
  		注入: Set注入、构造注入     
  		JDK类型的数据 或者 用户自定义对象类型。
  		
2. 复杂对象 
	不能使用new关键字或者通过构造方法直接创建,创建过程中往往需要多个参数配置或者多个复杂步骤。
	例如: Connection   SqlSessionFactory  接口对象

1、 复杂对象创建的代码类 实现 FactoryBean接口

public class ConnectionFactoryBean implements FactoryBean<Connection>{
	@Override
	//获得对象  创建复杂对象的代码
	public Connection getObject() throws Exception {
		Class.forName("oracle.jdbc.OracleDriver");
		Connection connection = 
            	DriverManager.getConnection("jdbc:oracle:thin:@localhost:1521:XE", "hr", "hr");
		return connection;
	}
	@Override
	//获得复杂对象的class对象
	public Class getObjectType() {
		return Connection.class;
	}
	@Override
	//是否是单例       true单例 只创建一次           false不是单例可以多次创建
	public boolean isSingleton() {
		return false;
	}
}

2、完成Spring applicationContext.xml的配置

 <bean id="conn" class="factorybean.ConnectionFactoryBean"></bean>     

3、测试

@Test
public void test1(){
    ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
    //如果实现了 FactoryBean接口 类的创建并不是接口实现类对象(ConnectionFactoryBean) 
    //而是其中的getObject方法的返回值对象  Connection
    Connection conn = (Connection) ctx.getBean("conn"); //复杂对象
    //获取 ConnectionFactoryBean  基本没人用
    ConnectionFactoryBean cfb = (ConnectionFactoryBean) ctx.getBean("&conn"); //具体的实现类对象
    System.out.println(conn);
    System.out.println(cfb);
}

注意:

1.  创建简单对象的时候,getBean("id") 返回值是配置文件中 class 的具体实例对象
	2.  复杂对象中使用getBean("id") 获得的是类中 getObject方法的返回值(复杂对象)
	3.  如果真想获取class中的实例对象  getBean("&id"). --- 很少使用

第二讲

一、控制简单对象的创建次数

Spring工厂创建简单对象,由配置文件指定对象是否是单例
 	默认单例 : 节省资源 !!!

公用资源对象: DAO实现类对象、Service实现类对象 
私有资源对象: Struts2(Action)、Cart
由对象本身的特性决定

多例: <bean id="userDAO" class="first.UserDAOImpl" scope="prototype"></bean>    

单例: <bean id="userDAO" class="first.UserDAOImpl" scope="singleton"></bean>    

二、IOC 和 DI

1. IOC (Inversion of Control) 控制反转

面向对象编程中一种设计原则,降低计算机系统中的耦合度。

控制: 在对象的创建的过程中,对于成员变量的赋值权的控制。
控制权反转 : 由代码的直接书写赋值,交由了Spring的配置文件。
工厂+反射


<bean id="uDAO"  class="first.UserDAOImpl"></bean>   
<bean id="userService"  class="first.UserServiceImpl">
    <property name="userDAO">
        <ref local="uDAO"></ref>
    </property>
</bean>

2. DI(Dependency Injection) 依赖注入

一个类的实例对象需要使用另一个类的实例对象,将依赖对象转换成成员变量使用Spring的配置文件进行赋值创建。

1. Service层需要使用DAO对象,Service依赖于DAO对象,将DAO定义成Service中的成员变量使用配置文件进行赋值创建。

2. 复杂对象创建 例如 Connection 常用的参数 driver url ... 遵循 IOC DI 将其定义成成员变量使用配置文件依赖注入赋值。

三、Spring容器的特性

1. 工厂创建对象的生命周期

  1.1 创建
  		Spring工厂创建的同时会将配置文件中所有的对象全部创建。
  		ApplicationContext ctx = new ClassPathXmlApplicationContext("路径");

  		使用工厂.getBean其他对象的时候,所有对象的构造方法都会被调用。  
  		饿汉式: 当首次运行会创建所有的对象,提升系统的运行效率。(减少对象的创建时间)
  1.2 销毁
  		close(); 具体实现类中存在。   Spring销毁所有对象伴随工厂。
  		
  1.3 用户可以进行初始化以及销毁方法的自定义
  		<bean id="userDAO" class="first.UserDAOImpl" scope="singleton" 
            init-method="MyInit" destroy-method="MyDestroy"></bean>   
            
         用户自定义:MyInit  MyDestroy
         在配置文件中进行对应的信息配置。

2.配置文件参数化 PropertyPlaceHolder

将配置文件中有可能需要经常改变的内容(字符串)转移至更小的配置文件中。

<context:property-placeholder location="classpath:/jdbc.properties" />
<bean id="conn" class="first.ConnectionFactoryBean">
    <property name="driverClassName" value="${driverClassName}"></property>
    <property name="url" value="${url}"></property>
    <property name="user" value="${user}"></property>
    <property name="password" value="${password}"></property>
</bean>    


1. context 全新的标签  引入标签库
	参考beans中的bean的配置
	修改五个地方
<beans xmlns="http://www.springframework.org/schema/beans"
	   xmlns:context="http://www.springframework.org/schema/context" 
                *                                             *
       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-3.2.xsd 
                           http://www.springframework.org/schema/context  
                                                                   *
                           http://www.springframework.org/schema/context/spring-context-3.2.xsd 
                                                                   *              *      ">
 2. location="classpath:/jdbc.properties"
     classpath: 只要用来屏蔽路径差异

四、AOP 面向切面编程

代理设计模式

1. 静态代理设计模式

在这里插入图片描述


将核心业务代码 和 额外功能代码 分离开发。

在这里插入图片描述

概念: 通过代理类(Proxy)为原始类(Impl)增加额外功能代码。

目的:拆分核心功能和额外功能,保证不去频繁修改核心功能代码,提高代码的维护性。

注意: 代理类 同样需要 实现 原始接口。

代理类 = 使用原始类 + 额外功能 + 实现和原始类相同的接口

静态代理存在一些设计上没有避免的问题:
	1. 代码的冗余 ,一个实现类最少有一个代理类。  ---> 实现类数量的增加
	2. 代理类功能冗余。 一个相同的功能使用并不灵活。
	3. 本质开发繁琐,替换额外功能开发新的代理类。
2. Spring中的动态代理设计模式

概念:使用代理类为原始类添加额外功能。

避免对于代码的频繁修改,冗余问题。

准备: 引入Jar包

com.springsource.org.aopalliance-1.0.0.jar
com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
1. 创建开发原始对象
1. 创建UserService  UserServiceImpl

2. 配置
	<bean id="userService" class="dynamicproxy.UserServiceImpl"></bean>   
2. 额外功能 Advice接口(通知) (开发代理类)

使用Advice的子接口

1. MethodBeforeAdvice  	在执行原始方法之前  			   (前置通知)
2. AfterReturningAdvice	在执行原始方法之后 			   (后置通知)
3. MethodInterceptor   	在执行原始方法之前之后执行		(环绕通知)
4. ThrowAdvice         	在原始方法运行发生异常时候处理    (异常通知)


完成了代理类的开发:
<bean id="userServiceProxy" class="dynamicproxy.UserServiceProxy"></bean>      
3. 定义切入点 PointCut

决定额外功能所添加的位置

<aop:config>
     <!-- 定义切入点    id pointCut标示         
    expression 切入的位置    execution(* *(..)) 所有声明内容都添加-->
    <aop:pointcut id="userServicePc" expression="execution(* *(..))"></aop:pointcut>
</aop:config> 

execution()    * *(..)   所有的方法都添加额外功能(前置通知)  
1. 方法切入点
	表达式 execution() 函数 
         *               *(..)     所有
    修饰符 返回值类型  方法名(参数表){实现}
    public  void      login(){}
       *  login(..)
           
    //寻找一个参数是User的方法 自定义对象
      * login(dynamicproxy.User)  权限定名
      * login(String)
      * login(String,String)
      * login(String,..)
           
 2. 类切入点
 
 	方法来源于类
 	
 	public  void      dynamicproxy.UserServiceImpl.login(){}
    修饰符返回值           包..方法(参数)
        *                *.UserServiceImpl.*(..)   //所有UserServiceImpl下的方法
             
     * *.UserServiceImpl.login(..)  // 类下的所有login方法
        
 3. 包切入点   
 
 	public  void      dynamicproxy.UserServiceImpl.login(){}
    修饰符返回值           包..方法(参数)
        *                dynamicproxy.*.*(..)     //包下的所有类的所有方法
        
        *                dynamicproxy..*.*(..)    //包后多了一个. 子包
        				// dynamicproxy.sub   可以的    dynamicproxy.sub.xx  不可以
        
4. 组装 (整合 切入点 + 代理通知)
<aop:config>
    <!-- 定义切入点    id pointCut标示         
     expression 切入的位置    execution(* *(..)) 所有声明内容都添加-->
    <aop:pointcut id="userServicePc" expression="execution(* *(..))"></aop:pointcut>
    <!-- 切入点 + 代理通知 -->
    <aop:advisor advice-ref="userServiceProxy"  pointcut-ref="userServicePc"></aop:advisor>
</aop:config> 

注意:
	1.Spring会运行时动态创建代理对象。

	2.Spring创建对象 原始类的id 还是 代理类的id?
		原始类的id Spring工厂会在创建对象的时候为原始类根据配置信息动态生成代理类。 
		ApplicationContext ctx = new 	ClassPathXmlApplicationContext("路径");
		UserService userService = (UserService) ctx.getBean("userService");

第三讲

一、切入点

1. 切入点函数

1. execution()
2. args()
作用: 主要用于匹配函数中的参数作为切入点
语法: args(String)   ----> execution(* *(String))   
3. within()
作用: 匹配包或者类作为切入点
execution(*..方法(..))   
    
within(*.UserServiceImpl)
within(dynamicproxy.*)
within(dynamicproxy..*)
4. @annotation()

作用:判断方法是否存在某个特定注解,如果存在则加入切入点。

需要一个注解:自定义注解!!!

@Target(ElementType.METHOD)   //注解应用在方法上
@Retention(RetentionPolicy.RUNTIME) //RUNTIME 运行时依然有效
public @interface MyAnn {
	//String aaa() default "";  //属性   default 默认值
}
使用: 对应方法添加自定义注解  @MyAnn
    
配置:  @annotation(注解权限定名)
补充: 支持逻辑运算—切入点函数 and or not
函数之间的运算
expression="args(String) or @annotation(dynamicproxy.MyAnn)"
注意:相同函数之间不可以使用 and  (类似于数据库中的and or)

2. 额外功能通知 Adivce接口

1. MethodBeforeAdivce  前置通知
	@Override
	public void before(Method method, Object[] args, Object obj)
			throws Throwable {
		System.out.println("-----Proxy------"); //会在原始类之前执行
	}
	method:原始方法声明
	args:原始类方法中的参数数组
	obj:原始类对象
	
2. AfterReturningAdvice 后置通知
	@Override
	public void afterReturning(Object ref, Method method, Object[] args,
			Object obj) throws Throwable {
		// TODO Auto-generated method stub
	}
	其他对象和上一个方法没有区别
	Object ref: 原始类方法的返回值  		注意:如果原始方法返回值 void   ref=null;

3. MethodInterceptor   环绕通知
	@Override
	public Object invoke(MethodInvocation mi) throws Throwable {
		System.out.println("----前----");
		Object proceed = mi.proceed(); //执行后续的原始类方法 获得方法的返回值
		System.out.println("----后----");
		return proceed;
	}
4. ThrowsAdvice
	ThrowsAdvice:标示性接口 接口中没有必须实现的方法。 (Seriazable对象序列化)
	
	public void afterThrowing(Exception ex){
		System.out.println(ex);
	}
	//ex 产生的异常

二、AOP的实现原理

面向对象编程:类—>对象,通过对象之间的相互调用。

面向切面编程:

​     切面 : 切入点 + 额外功能 的 组合。

如何实现:Spring 动态代理实现。 代理类为原始类添加额外功能

动态代理

代理类 = 原始对象 + 额外功能 + 实现相同接口

直接写出来代理类 = 静态代理

使用方法在程序运行时动态生成代理类 = 动态代理

JDK 动态代理(接口)

​ 代理类 = Proxy.newProxyInstance( 类加载器 ,原始对象的接口 , 额外功能 );

类加载器: ClassLoader .class—> JVM中运行, 代理类是程序运行的时候动态生成,代理类是没有.class就没有默认的类加载器。(随便借一个)

@Test
public void test1(){
    final UserService uS = new UserServiceImpl();

    //额外功能 InvocationHandler接口
    InvocationHandler ih = new InvocationHandler(){
        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable {
            System.out.println("-----InvocationHandler-----");
            //调用UserServiceImpl 中的方法
            //uS 原始类对象     method原始类对象方法
            Object invoke = method.invoke(uS, args);
            return invoke;
        }
    };

    UserService userService = (UserService)
        Proxy.newProxyInstance(TestJDKProxy.class.getClassLoader(), 
                               uS.getClass().getInterfaces() , ih);
    userService.login();
}

在这里插入图片描述

Cglib动态代理(动态字节码添加)(实现类)
public class UserService{
    public void login(){
        //代码
    }
}
new UserService().login();

代理类中的方法 和 接口 以及原始类中的方法完全一样,代理类要去继承原始类,

继承:同样可以保证代理类和原始类中的方法一致。

final UserService1 userService1 = new UserService1();
//cglib核心对象
Enhancer enhancer = new Enhancer();

//代理类继承原始类保证方法一致   
enhancer.setSuperclass(userService1.getClass());

//额外方法 import org.springframework.cglib.proxy.InvocationHandler;
InvocationHandler ih = new InvocationHandler(){};
//添加额外功能
enhancer.setCallback(ih);

UserService1 us = (UserService1) enhancer.create();
us.login();

BeanPostProcessor 为什么可以通过id获取代理类对象

BeanPostProcessor:Spring工厂对于原始对象再加工。

在这里插入图片描述


Spring + MyBatis 整合

1.整合思路

1. 环境Jar包
	Spring --- jar    Mybatis --- jar   JDBC --- jar    Spring+MyBatis 整合时使用的jar
	
2. 整合

	Spring  轻量级的项目管理框架             项目管理 组件的创建
	MyBatis 持久层框架 替换JDBC             
		核心对象的创建交由Spring进行管理

3. MyBatis核心对象
	DAO实例对象
	SqlSession
	SqlSessionFactory
			创建SqlSessionFactory ---> 依赖对象  MyBatis配置文件
					1. 数据的连接  dataSource 数据源    
                      2. mapper.xml注册
                      
 4. 提取核心内容交由Spring的配置文件来完对于SqlSessionFactory的创建
 		SqlSessionFactory实现类   实现  FactoryBean<SqlSessionFactory>  大家都一样
 		Spring配置 ---> Spring  和 MyBatis整合jar包 
 		<bean id="sqlSessionFactory" 
    		class="Spring 和 MyBatis整合jar包帮你定义好的SqlSessionFactory的创建"/>
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"></bean>

配置文件:

<!-- 配置文件参数化 -->                   
<context:property-placeholder location="classpath:/jdbc.properties" />

<!-- dataSource :sqlSessionFactory的数据源 数据库连接 -->   
<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="${driverClassName}"></property>
    <property name="url" value="${url}"></property>
    <property name="username" value="${user}"></property>
    <property name="password" value="${password}"></property>
</bean>  

<!-- 创建MyBatis中 sqlSessionFactory -->                    
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <!--dataSource 连接 -->               
    <property name="dataSource" ref="dataSource"></property>
    <!-- Mapper.xml的注册 -->               
    <property name="mapperLocations" value="classpath:com/baizhi/mapper/*Mapper.xml"></property>
    <!-- typeAliasesPackage   包下所有的实体类自动别名     类名 User / user -->
    <property name="typeAliasesPackage" value="com.baizhi.entity"></property>
</bean>     

<!-- DAO对象依然使用 Spring工厂创建  IOC   DAO创建依赖于sqlSessionFactory-->  
<!-- mapper.xml  转换  java对象  -->    
<!-- mapperScanner创建所有配置过的mapper.xml对象 -->
<bean id="mapperScanner" class="org.mybatis.spring.mapper.MapperScannerConfigurer">
    <!-- DAO创建依赖于sqlSessionFactory--> 
    <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
    <!-- sqlSessionFactory Mapper注册的是所有 包中的所有   value=“包名”--> 
    <property name="basePackage" value="com.baizhi.dao"></property>
</bean> 
<!-- UserDAO  Spring默认接口首字字母小写   bean userDAO-->      


<bean id="userService" class="com.baizhi.service.UserServiceImpl">
    <!-- 依赖注入思想  userDAO id   所有的DAO都在mapperScanner
          命名规范:UserDAO  Spring默认接口首字字母小写   bean id="userDAO"
          ref="userDAO" 从mybatisScanner获取已经创建好的对象
     -->
    <property name="userDAO" ref="userDAO"></property>
</bean>


注意: Spring允许配置文件的拆分
<import resource="classpath:/Spring_MyBatis.xml"></import>

2. MyBatis + Spring 事务控制

事务:控制原子操作要么一起彻底成功,要么一起彻底失败

JDBC : Connection MyBatis: SqlSession

整合中连接在哪?

dataSource 数据源—>连接池 Service需要使用连接控制事务

  1. 将dataSource 定义成成员变量 使用依赖注入赋值
  2. 使用dataSource 获取连接 控制事务

Service 和 DAO 需要获取相同的连接对象?

JDBC :线程绑定。

Spring帮你去完成 ----> 同步标记

 <!-- 事务管理 : 保证Service 和 DAO 获取相同连接对象  类似于线程邦定    使用同步标记 -->
<bean id="transactionManager" 
      class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
      <property name="dataSource" ref="dataSource"></property>
</bean>
dataSource 交给 DataSourceTransactionManager 管理
获取连接不再是使用原始的dataSource,使用管理DataSourceTransactionManager

Service中定义成员变量
private DataSourceTransactionManager transactionManager;
Set/Get 方法

Connection conn = DataSourceTransactionManager.getDataSource().getConnection();

AOP面向切面编程 — 解决Service中的冗余代码(额外功能)

处理事务的环绕通知不需要自己开发,使用Spring中的配置。

AOP开发步骤
1.原始类接口 2.Adivce <tx:advice> 3.定义切点<aop:pointcut> 4.组装
    
<!-- tx事务 advice通知接口  额外方法  环绕通知控制事务 -->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!-- 细粒度的声明 ServiceImpl中哪些具体方法需要使用事务环绕通知 -->
    <tx:attributes>
        <tx:method name="r*"></tx:method>
    </tx:attributes>
</tx:advice>

<!-- 定义切入点 组装切面 -->
<aop:config>
    <!-- 定义切点 -->
    <aop:pointcut id="pc" expression="within(com.baizhi.service.*ServiceImpl)"></aop:pointcut>
    <!-- 组装切面 -->
    <aop:advisor advice-ref="txAdvice" pointcut-ref="pc"></aop:advisor>
</aop:config>

3. 事务的传播属性

现有的业务场景过于简单,Service就是调用DAO中的方法,Service调用Service中的方法。

业务方法在相互调用的时候如何保持一致?有时候需要明确区分独立事务?

事务的传播属性: 内外方法调用时候事务是否相互影响

<tx:method name="r*" propagation=""></tx:method>
	REQUIRED : 表示需要事务 (默认值 相同事务)
		如果外部没有事务,则内部开启新的事务,如果外部存在事务,则内外事务融合。
	
	REQUIRES_NEW : 每次开启新的事务 (各自独立事务)
		外部没有事务,内部需要开启新事务,如果外部存在事务则挂起外部事务,开启内部新事务,执行完毕再次唤		醒外部的事务。

	SUPPORTS : 表示支持事务(外部是一个DML操作嵌套一个查询但是查询再次嵌套DML)
		外部没有事务则内部独立,如果外部存在事务可以和内部(第三次嵌套)的事务融合


	NOT_SUPPORTED : 内部一定不会和外部事务融合。

	NEVER : 不支持事务

	ManDatory : 强制事务	

	NESTED(听说过有这么个属性就行)
	

4. 事务的隔离属性

脏读 : 一个事务读取到了另一个事务还未提交的数据。

幻读 : T1读取了两次数据,在前后读取的过程中T2对数据进行了操作(DML),T1前后数据不一致。

不可重复读 : 以为读取的是对的,T1事务读取数据,T2事务对数据进行了修改,实际数据已经发生了改变。

数据库存在默认隔离级别:避免以上错误读取。    关系型
		四个: Read uncommitted (读未提交) 所有的都可能发生
			  Read committed   (读提交)   避免脏读
			  Repeatable Read  (重复读)   避免脏读、不可重复读
			  Serializeable    (序列化)   避免脏读、不可重复读、幻读
			  
			  Oracle 默认 Read committed 可以改变 最高允许Serializeable
			  MySQL  默认 Repeatable Read
			  
isolation="Default"  默认使用数据库自己的隔离级别

5.事务中的其他属性

1. timeout="-1" 超时属性 默认-1 无限等待 (秒)
		事务的超时:事务的挂起时间  
2. read-only=true / false   默认false
		read-only=true : 该方法就是查询方法不存在DML操作  建议如果是单纯的查询操作最好添加
3. 参数:异常的权限定名  填父类包括子类      Spring默认提交策略 
	rollback-for=""       发生对应异常直接回滚数据
	no-rollback-for=""    发生对应异常直接提交数据

Spring + Struts2 整合

1.整合思路

1. 导入 jar包  struts.xml文件

2. 核心对象的创建交由Spring完成

	 自定义Action
	 XXXAction组件  ---> Spring 完成创建
	 
	 核心过滤器  web.xml中的配置的Filter  (是由服务器创建)
	 还是使用web.xml配置即可(不发生改变)

3. 交由Spring创建的核心对象只有Action

		Spring的配置文件创建 Action对象
		<bean id="XXX" class="" scope="Action多例对象">
			<Service业务对象的依赖注入 ref="">
		</bean>
          
         localhost:8989/项目名/namespace/action_name  ---> class="Spring Bean id"
         Struts.xml
         <package name="" extends="" namespace="">
            	<action name=""  class="XXX"  method=""></action>
         </package>
            
         jar包:struts-spring-plugin.jar 引入jar包 默认class中直接去寻找Spring配置中的bean id

2.如何去创建Action对象? — 项目启动的时候创建Spring工厂

ApplicationContext ctx = new ClassPathXmlApplicationContext("/applicationContext.xml");
Action action = (Action) ctx.getBean("action bean id");

原来从未手动创建过Action对象

Action由Spring创建, 需要在启动服务器的时候去创建Spring工厂。
谁能在服务器启动的时候去创建工厂呢? ---- 监听 ServletContext 全局唯一。
传你的配置文件给Spring的工厂创建方法即可。
写在哪? ---web.xml中
	<listener>
  		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  	</listener>
  	<context-param>
  		<param-name>contextConfigLocation</param-name>
  		<param-value>classpath:/applicationContext.xml</param-value>
  	</context-param>

3. Spring + Struts2+MyBatis 步骤

1. 环境 jar包 以及配置文件
	applicationContext.xml   struts.xml
	
2. Spring + MyBatis
	2.1 建表
    2.2 实体类
    2.3 DAO接口定义
    2.4 Mapper.xml的开发
    2.5 Service业务接口的定义
    2.6 Service业务方法的实现
    2.7 配置Spring的xml文件
    		a. DataSource的数据源
             b. 配置文件参数化 <context:property-placeholder/>
             c. sqlSessionFactory 核心工厂的创建
             	注入属性
                	DataSource  数据源
                	mapperLocations  注册所有的mapper
                    typeAliasesPackage  包中所有实体类别名
             d. MapperScannerConfigurer  创建所有的UserDAO实现类
             	注入属性
                	sqlSessionFactoryBeanName    工厂
                     basePackage                 接口包 --所有的对象都是接口首字母小写
             e. transactionManager     事务管理器 保证连接相同
             	注入属性
                	DataSource
             f. 声明 事务的环绕通知
             		<tx:advice id="txAdvice" transaction-manager="transactionManager">
                        <!--细粒度的声明 ServiceImpl中哪些具体方法需要使用事务环绕通知 -->
                        <tx:attributes>
                            <!-- 添加事务的属性  传播属性 内外相互影响吗? -->
                            <tx:method name="r*" read-only="true"></tx:method>
                        </tx:attributes>
                    </tx:advice>
			 g. 定义切入点 组装切面 ---- 事务
             
              h. 使用bean创建Service对象  使用属性就是依赖注入DAO
3. struts2 + Spring
	3.1 配置 web.xml
			Struts中的核心过滤器 StrutsPrepareAndExecuteFilter
             Listener监听---> 工厂的启动创建
             
         <listener>
  		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
         </listener>
         <context-param>
                    <param-name>contextConfigLocation</param-name>
        		<param-value>classpath:/applicationContext.xml</param-value>
         </context-param>

	3.2 Action的开发
	3.3 Spring配置文件 创建Action对象  <bean scope="prototype">多例对象
	3.4 配置struts.xml  <action name=""  class="spring bean id">	

4. Spring的注解开发

Spring配置文件中依然存在大量的配置冗余,2.5X建议使用注解开发简化配置文件。

在配置文件开启注解扫描

<context:component-scan base-package="com"></context:component-scan>
                                     扫描的包

1、@Component 实例化相关注解

 替换bean标签配置
 
 类上添加了 @Component  Spring工厂会创建该类的对象 默认 id 类名首字母小写
 
 @Component("userService")  加给类(组件) Action DAO Service   (如果集成MyBatis DAO就可以忽略)
 @Component(value="userService")
     
 子分支
	DAO		:  @Repository()
 	Service	:  @Service("userService")
	Action  :  @Controller("userAction")


@Scope("prototype")  多例
默认 singleton 单例

2、注入相关

@Autowired  : Spring    使用在成员变量或者set方法上
    一般都是用在成员变量上 用来替换配置中的set注入  set、get可以省略

    @Autowired    根据成员变量的类型自动选择注入
    private UserDAO userDAO;

    @Autowired
    @Qualifier(value="") 指定注入数据的内容
    private UserDAO userDAO;

@Resource : JavaEE规范中  先根据成员变量的名称,再根据成员变量的类型匹配。

3、事务相关

@Transactional
   类 或者 方法上  控制事务   声明的环绕通知以及切点 组装切面可以省略
   
   注解生效
   <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>


  事务存在传播属性 注解中的属性
  @Transactional(propagation=Propagation.SUPPORTS)

4、测试相关

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class TestMyBatis {
	@Autowired
	private UserService userService;
	
	@Test
	public void test3(){	
		//创建工厂
		//bean id 获取对象
		List<User> queryAllUser = userService.queryAllUser();
		for (User user : queryAllUser) {
			System.out.println(user);
		}
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值