spring框架快速入门笔记

论坛 期权论坛 编程之家     
选择匿名的用户   2021-6-2 17:13   1865   0

[TOC]

Spring概述

Spring体系结构

  1. core container
    • beans与core 它们提供spring框架最基本功能,包含ioc与di
    • context 上下文对象,基于beans与cores:做了一些封装,让我们可以更方便的使用实现ioc
    • spel它是sprng提供的一个表达式语言
  2. Data access/integration
    • 数据访问
    • 集成
  3. Web
    • Spring本身提供spring mvc
    • 也可以与其它的web层进行集成
  4. AOP
    • AOP大部分情况下是使用动态代理来实现的。
  5. Test
    • 使用spring可以方便的进行测试

Spring框架优点

  • 方便解耦,简化开发 Spring就是一个大工厂,可以将所有对象创建和依赖关系维护,交给Spring管理
  • AOP编程的支持 Spring提供面向切面编程,可以方便的实现对程序进行权限拦截、运行监控等功能
  • 声明式事务的支持 只需要通过配置就可以完成对事务的管理,而无需手动编程
  • 方便程序的测试 Spring对Junit4支持,可以通过注解方便的测试Spring程序
  • 方便集成各种优秀框架 Spring不排斥各种优秀的开源框架,其内部提供了对各种优秀框架(如:Struts、Hibernate、MyBatis、Quartz等)的直接支持
  • 降低JavaEE API的使用难度 Spring 对JavaEE开发中非常难用的一些API(JDBC、JavaMail、远程调用等),都提供了封装,使这些API应用难度大大降低

IOC与DI

Ioc它是什么,解决什么问题,它的原理是如何实现?

IOC:inversion of Controller:控制反转:它主要解决代码之间耦合度过高的问题。

在程序中所说的IOC其实简单说,就是原来由我们自己实例化的对象交给spring容器来实始化。这时对象的实始化的权利就会反转。

DI:dependency injection:依赖注入:为对象的属性赋值,清除对象与对象之间的依赖关系。

在spring框架负责创建Bean对象时,动态将依赖对象注入到Bean组件。

Bean获取与实例化

ApplicationContext与BeanFactory关系

BeanFactory它采取延迟加载的方案,只有真正在getBean()时才会实例化Bean。

ApplicationContext它是扩展BeanFactory接口。在开发中我们一般使用的是ApplicationContext,真正使用的是其实现类。

  • FileSystemXmlAppliCationContext 根据文件路径获取
  • ClassPathXmlApplicationContext 根据类路径获取

AppliCationContext它会在配置文件加载时,就会初始化Bean,并且ApplicationContext它提供不同的应用层的Context实现。例如在web开发中可以使用WebApplicationContext.

Bean的实例化方式

无参数构造 对于这种方式,注意Bean类中必须提供无参数构造。

静态工厂方法 需要创建一个工厂类,在工厂类中提供一个static返回bean对象的方法就可以。

实例工厂方法 需要创建一个工厂类,在工厂类中提供一个非static的创建bean对象的方法,在配置文件中需要将工厂配置,还需要配置bean

<!-- 在开发中可以使用id或name id它遵守的xml规范,名称中不能包含特殊符号 name它是为了更好的扩展,在name中可以包含一些特殊符号,例如"/" -->
<bean name="bean1" class="com.lbb.bean.Bean1"></bean>
 
<bean name="bean2" class="com.lbb.bean.Bean2Factory" factory-method="getBean2"></bean>

<bean name="bean3Factory" class="com.lbb.bean.Bean3Factory"></bean>
<bean name="bean3" factory-bean="bean3Factory" factory-method="createBean3" scope="prototype"></bean>
复制代码
public class Bean2Factory {
 public static Bean2 getBean2(){
  return new Bean2();
 }
}

public class Bean3Factory {
 public Bean3 createBean3(){
  return new Bean3();
 }
}
复制代码

Bean的作用域

在bean声明时它有一个scope属性,它是用于描述bean的作用域。可取值有

  1. singleton:单例 代表在spring ioc容器中只有一个Bean实例 (默认的scope)
  2. prototype多例 每一次从spring容器中获取时,都会返回一个新的实例
  3. request 用在web开发中,将bean对象request.setAttribute()存储到request域中(一次请求一次?)
  4. session 用在web开发中,将bean对象session.setAttribute()存储到session域中(一次会话一次?)
  5. 还有一个globalSession:用于分布式开发的时候全局Session。

Bean的生命周期

  1. instantiate bean对象实例化 . populate properties 封装属性 . 如果Bean实现BeanNameAware执行setBeanName . 如果Bean实现BeanFactoryAwar或ApplicationContextAwar设置工厂setBeanFactory或上下文对象setApplicationContext . 如果存在类实现BeanPostProcessor(后处理Bean),执行postProcessBeforeInitialization . 如果Bean实现InitializingBean执行afterPropertiesSet . 调用自定义的init-method方法 . 如果存在类实现BeanPostProcessor(处理Bean),执行postProcessAfterInitialization . 执行业务处理
  2. 如果Bean实现DisposableBean执行destroy
  3. 调用自定义的destroy-method

对于bean的生命周期方法

第三步与第四步是让Bean了解spring容器。

第五步与第八步 可以针对指定的Bean进行功能增强,这时一般会使用动态代理。

第六步与第十步:通过实现指定的接口来完成init与destroy操作 但是在开发中一般不使用第6步与第10步,原因是我们可以使用第7步与第11步来完成。

第7步与第11步的初始化与销毁操作它无耦合,推荐使用的。但是必须在配置文件中指定初始化与销毁的方法。

<bean name="beanLifeTest" class="com.lbb.bean.BeanLife" init-method="myInit" destroy-method="myDestroy">
  <property name="name" value="张三"></property>
</bean>
<bean class="com.lbb.bean.MyBeanPostProcess"></bean>
复制代码
package com.lbb.bean;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class BeanLife implements BeanNameAware,ApplicationContextAware,InitializingBean,
         DisposableBean,TestBeanLife{

 private String name;
 
 public BeanLife() {
  System.out.println("第一步:实例化BeanLife对象");
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
  System.out.println("第二步:属性name的注入" + name);
 }

 @Override
 public void setBeanName(String name) {
  System.out.println("第三步:得到bean的id或name值:" + name);
 }

 @Override
 public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
  System.out.println("第四步:得到ApplicationContext对象:" + applicationContext);
 }

 @Override
 public void afterPropertiesSet() throws Exception {
  System.out.println("第六步:属性注入完成后");
 }
 
 public void myInit() {
  System.out.println("第七步:自定义的init方法");
 }
 
 @Override
 public void test(){
  System.out.println("第九步:test--------------------test");
 }

 @Override
 public void destroy() throws Exception {
  System.out.println("第十步:执行destroy方法");
 }
 
 public void myDestroy() {
  System.out.println("第十一步:执行自定义的销毁方法");
 }
}
复制代码
package com.lbb.bean;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcess implements BeanPostProcessor{

 @Override
 public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
  System.out.println("第八步:beanPostProcessor的after方法");
  
  
  if (beanName.equals("beanLifeTest")) {
   // 使用动态代理可以处理bean的功能增强
   Object proxy = Proxy.newProxyInstance(bean.getClass().getClassLoader(), bean.getClass().getInterfaces(),
     new InvocationHandler() {

      @Override
      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
       if (method.getName().equals("test")) {
        System.out.println("增强Test");
       }
       return method.invoke(bean, args);
      }
      
     });
   return proxy;
  }
  
  
  return bean;
 }

 @Override
 public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
  System.out.println("第五步:beanPostProcessor的before方法");
  System.out.println("处理的bean是" + bean + " 它的名称是" + beanName);
  return bean;
 }

}
复制代码

总结:对于bean的生命周期,我们需要关注的主要有两个方法。

  1. 增强bean的功能可以使用后处理Bean, BeanPostProcessor . 如果需要初始化或销毁操作我们可以使用init-method destroy-method

注意:destroy-method只对scope=singleton有效果。(调用application.close方法才可以)

Bean的属性注入

 <!-- 使用构造方法对car的属性进行注入   不常用-->
 <bean name="car" class="com.lbb.di.Car">
  <constructor-arg index="0" type="java.lang.String" value="奔驰"></constructor-arg>
  <constructor-arg index="1" type="java.lang.Double" value="1000.0"></constructor-arg>
 </bean>
  
 <bean name="car2" class="com.lbb.di.Car">
  <constructor-arg type="java.lang.Double" value="2000.0"></constructor-arg>
  <constructor-arg type="java.lang.String" value="宝马"></constructor-arg>
 </bean>
 
 <!-- 使用setter方法对car的属性进行注入 常用 -->
 <bean name="car3" class="com.lbb.di.Car">
  <property name="name" value="吉普"></property>
  <property name="price" value="2222"></property>
 </bean>
 
 <bean name="person4" class="com.lbb.di.Person">
  <property name="name" value="张三"></property>
  <property name="car" ref="car1"></property>
 </bean>
 
 <!-- 集合属性注入(数组注入的方法与list一样) -->
 <bean name="collectionDemo" class="com.lbb.di.CollectionDemo">
  <property name="list">
   <list>
    <value>张三</value>
    <value>10</value>
    <ref bean="car2"/>
   </list>
  </property>
  
  <property name="set">
   <set>
    <value>李四</value>
    <value>20</value>
    <ref bean="person4"/>
   </set>
  </property>
  
  <property name="map">
   <map>
    <entry key="name" value="张三"></entry>
    <entry key-ref="person4" value-ref="car1"></entry>
   </map>
  </property>
  
  <property name="properties">
   <props>
    <prop key="姓名">张三</prop>
    <prop key="年龄">30</prop>
   </props>
  </property>
 </bean>
复制代码

名称空间p和c的使用

Spring2.0以后提供了xml命名空间。 首先它们不是真正的名称空间,是虚拟的。它是嵌入到spring内核中的。 使用p名称空间可以解决我们setter注入时<property>简化 使用c名称空间可以解决我们构造器注入时<constructor-arg>简化

spring expression language 是在spring3.0以后的版本提供 它类似于ognl或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">

 <!-- <bean id="dog1" class="cn.itheima.namsapce.Dog"> <property name="name" 
  value="吉娃娃" /> <property name="color" value="黄色"></property> </bean> -->
 <!-- 使用p名称空间来简化 上述操作 -->
 <bean id="dog1" class="cn.itheima.namsapce.Dog" p:name="博美"
  p:color="白色"></bean>

 <!-- <bean id="dog2" class="cn.itheima.namsapce.Dog"> <constructor-arg index="0" 
  value="黑色" /> <constructor-arg index="1" value="大丹" /> </bean> -->
 <!-- 使用c名称空间来简化 上述操作 -->
 <bean id="dog2" class="cn.itheima.namsapce.Dog" c:name="大丹"
  c:color="黑色"></bean>

 <bean id="person" class="cn.itheima.namsapce.Person" p:name="张三"
  p:dog-ref="dog2" p:age="20">
 </bean>

 <bean id="person1" class="cn.itheima.namsapce.Person">
  <property name="name" value="#{person.name}" />
  <!-- <property name="dog" ref="dog1"/> -->
  <property name="dog" value="#{dog1}"></property>
  <property name="age" value="#{person.getAge()+10}"></property>
 </bean>

</beans>
复制代码

Spring注解开发

想要使用注解开发,首先导入spring-aop-....jar包,使用<context:component-sacn base-package=””>配置包扫描。

常用注解

  • @Component
  • @Repository 用于DAO层
  • @Service 用于service层
  • @Controller 用于表现层 对于我们的bean所处在的位置可以选择上述三个注解来应用,如果你的bean不明确位置,就可以使用@Component.
  • @Value
  • @Autowired:它默认是根据类型进行注入。如果与@Qualifier一起使用,就可以根据名称来进行注入。
  • @Resource 注意:@Value @Autowired它们可以修饰属性,也可以修饰setter方法,如果写在属性上,就不需要提供setter方法。
  • @Scope它以描述bean的作用域。
  • @PostConstruct:它相当于init-method=”myInit
  • @PreDestroy:它相当于是destroy-method=”myDestroy”。注意:对于销毁的方法它只对bean的scope=singleton有效。

Spring在web开发中的应用

  1. 作用:能让我们直接在web应用中直接使用Spring,而不是每个用到Spring的地方都去创建Spring容器(application)
  2. 原理:导入Spring-web.jar之后,当我们在web.xml文件中配置Spring的ContextLoaderListener(这个Listener实际上是一个ServletContextListener)后,项目一启动这个listener里面的代码就会执行,在listener里面就会帮我们去加载Spring的核心配置文件applicationContext.xml,创建一个WebApplicationContext(ApplicationContext的实现类), 并且将WebApplicationContext对象保存在ServletContext里面,以后我们使用要从Spring容器里面获取对象的时候,直接从ServletContext对象里面取出WebApplicationContext对象就可以,就不需要每次都去创建WebApplicationContext,加载配置文件了。

实现步骤 在web项目中要使用spring需要导入一个jar包spring-web-...jar

在web.xml文件中配置Listener。

<listener>
   <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
复制代码

这个ContextLoaderListener它实现了ServletContextListener。在这个listener中,当服务器启动时,将ApplicationContext对象,其实是它的一个实现类WebApplicationContext对象存入到了ServletContext中。

我们还需要在web.xml文件中配置applicationContext.xml文件的位置。

默认情况下会在WEB-INF目录 下查找applicationContext.xml, 如果applicationContext.xml文件不是在默认位置,我们可以在web.xml文件中配置。

<context-param>
   <param-name>contextConfigLocation</param-name>
   <param-value>classpath:applicationContext.xml</param-value>
</context-param>
复制代码

Spring整合junit4测试

导包spring-test-...jar

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class AnnotationTest {
 
 @Autowired
 private IUserService userService;
 
 @Test
 public void test1(){
  
 }
}
复制代码

Spring AOP

AOP概述

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

AOP是一个概念,并没有设定具体语言的实现,它能克服那些只有单继承特性语言的缺点,spring2.0之后整合AspectJ第三方AOP技术。

AspectJ是一个面向切面的框架,它扩展了Java语言。AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。

主要功能:日志记录,性能统计,安全控制,事务处理,异常处理等等。

主要意图:将日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,我们希望可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

AOP与OOP区别

OOP(面向对象编程)针对业务处理过程的实体及其属性和行为进行抽象封装,以获得更加清晰高效的逻辑单元划分。

而AOP则是针对业务处理过程中的切面进行提取,它所面对的是处理过程中的某个步骤或阶段,以获得逻辑过程中各部分之间低耦合性的隔离效果。这两种设计思想在目标上有着本质的差异。

换而言之,OOD/OOP面向名词领域,AOP面向动词领域。

AOP相关术语

  • 目标对象target 指的是需要被增强的对象,由于spring aop是通过代理模式实现,从而这个对象永远是被代理对象。
  • 连接点(join point) 所谓连接点是指那些被拦截到的点,在spring中这些点指的是方法,因为spring只支持方法类型的连接点。
  • 切入点(pointcut) 表示一组 joint point,这些 joint point 或是通过逻辑关系组合起来,或是通过通配、正则表达式等方式集中起来,它定义了相应的 Advice 将要发生的地方 简单说切入点是指我们要对哪些连接点进行拦截的定义。
  • 通知(advice) 所谓通知是指拦截到连接点之后所要做的事情就是通知,通知分为前置通知,后置通知,异常通知,最终通知,环绕通知。 Advice 定义了在 pointcut 里面定义的程序点具体要做的操作。
  • 引介introduction 引介是一种特殊的通知,在不修改类代码的前提下,introduction可以在运行期为类动态地添加一些方法或属性。
  • 切面aspect 是切入点和通知的结合。
  • 织入weaving 织入是一个过程,是将切面应用到目标对象从而创建出AOP代理对象的过程,织入可以在编译期,类装载期,运行期进行。 Spring采用动态织入,而aspectj采用静态织入。
  • 代理Proxy 一个类被AOP织入增强后,就产生一个结果代理类。

AOP底层实现

AOP分为静态AOP和动态AOP。静态AOP是指AspectJ实现的AOP,他是将切面代码直接编译到Java类文件中。动态AOP是指将切面代码进行动态织入实现的AOP。 Spring的AOP为动态AOP,实现的技术为: JDK提供的动态代理技术CGLIB(动态字节码增强技术)

JDK动态代理

在运行 ,在JVM内部动态生成class字节码对象(Class对象)。Jdk动态代理只针对于接口操作

CGLIB动态代理

CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。CGLIB包的底层是通过使用一个小而快的字节码处理框架ASM,来转换字节码并生成新的类。

如果你要单独使用CGLIB,那么需要导入cglib的jar包还需要一个asm相关jar包,但是spring框架的spring-core.jar包中已经集成了cglib与asm。

注意:jdk的动态代理只可以为接口去完成操作,而cglib它可以为没有实现接口的类去做代理,也可以为实现接口的类去做代理。

问题:spring采用的是哪一种动态机制 如果目标对象,有接口,优先使用jdk动态代理。 如果目标对象,无接口,使用cglib动态代理。

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

//创始一个使用jdk的proxy完成动态代理工具
public class JDKProxyFactory implements InvocationHandler {

 private Object target;
 public JDKProxyFactory(Object target) {
  this.target = target;
 }
 // 创建代理对象
 public Object createProxy() {

  // 使用Proxy完成代理对象创建
  // 1.得到目标对象的ClassLoader
  ClassLoader loader = target.getClass().getClassLoader();
  // 2.得到目标对象的实现接口的Class[]
  Class[] interfaces = target.getClass().getInterfaces();
  // 3.第三个参数需要一个实现了InvocationHandler接口的对象
  return Proxy.newProxyInstance(loader, interfaces, this);
 }
 // 在代理实例上处理方法调用并返回结果。
 // 参数1 就是代理对象,一般不使用
 // 参数2它调用的方法的Method对象
 // 参数3调用的方法的参数
 @Override
 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

  // 功能增强操作
  System.out.println("日志操作....");

  return method.invoke(target, args);
 }
}



import java.lang.reflect.Method;

import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;

public class CglibProxyFactory implements MethodInterceptor {

 // 得到目标对象
 private Object target;
 // 使用构造方法传递目标对象
 public CglibProxyFactory(Object target) {
  this.target = target;
 }

 // 创建代理对象
 public Object createProxy() {
  // 1.创建Enhancer
  Enhancer enhance = new Enhancer();

  // 2.传递目标对象的Class
  enhance.setSuperclass(target.getClass());

  // 3.设置回调操作 (相当于InvocationHandler)
  enhance.setCallback(this);

  return enhance.create();
 }

 // 相当于InvocationHandler中的invoke
 @Override
 public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methdoProxy) throws Throwable {
  System.out.println("日志操作....");
  return method.invoke(target, args); // 与jdk的proxy中操作类似
  // return methdoProxy.invokeSuper(proxy, args);
 }
}



public class ProxyTest {

 // 测试jdk的动态代理
 @Test
 public void test1() {
  // 1.创建目标对象
  IUserService userService = new UserServiceImpl();

  // 2.通过JKDProxyFactory完成代理对象创建
  JDKProxyFactory factory = new JDKProxyFactory(userService);

  IUserService userServiceProxy = (IUserService) factory.createProxy();

  userServiceProxy.login("tom","123");
 }
 
 //测试cglib动态代理
 @Test
 public void test2(){
  //创建目标对象
  IUserService userService = new UserServiceImpl();
  
  //2.使用CglibProxyFactory创建代理对象
  CglibProxyFactory factory=new CglibProxyFactory(userService);
  
  UserServiceImpl userServiceProxy = (UserServiceImpl) factory.createProxy();
  
  userServiceProxy.regist();
 }
}
复制代码

Spring AOP编程

Spring的传统aop编程

在传统的spring aop开发中它支持增强(advice)有五种:

  1. 前置通知 目标方法执行前增强 org.springframework.aop.MethodBeforeAdvice . 后置通知 目标方法执行后增强 org.springframework.aop.AfterReturningAdvice . 环绕通知 目标方法执行前后进行增强 org.aopalliance.intercept.MethodInterceptor . 异常抛出通知 目标方法抛出异常后的增强 org.springframework.aop.ThrowsAdvice . 引介通知 在目标类中添加一些新的方法或属性(不讲解)org.springframework.aop.IntroductionInterceptor

经典的基于代理的AOP开发(了解)

基本的jar包:1. bean2. core3. context4. expression5. aop6. com.springsource.org.aopalliance

代码示例

public class OrderServiceImpl implements IOrderService {
 @Override
 public void addOrder() {
  System.out.println("orderService add...");
 }

 @Override
 public void updateOrder() {
  System.out.println("orderService update...");
 }
}

public class OrderHelper implements MethodBeforeAdvice, AfterReturningAdvice, MethodInterceptor {
 @Override
 public void before(Method method, Object[] args, Object target) throws Throwable {
  System.out.println("前置通知...");
 }

 @Override
 public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
  System.out.println("后置通知...");
 }

 @Override
 public Object invoke(MethodInvocation mi) throws Throwable {
  System.out.println("环绕前....");
  Object value = mi.proceed();
  System.out.println("环绕后....");
  return value;
 }
}

 <!-- 目标target -->
 <bean id="orderService" class="com.lbb.aop.OrderServiceImpl"></bean>
 <!-- 通知advice -->
 <bean id="orderServiceAdvice" class="com.lbb.aop.OrderHelper"></bean>
 <!-- 定义切点 pointcut -->
 <!-- <bean id="orderServicePointCut" class="org.springframework.aop.support.NameMatchMethodPointcut">
  <property name="mappedNames">
   <list>
    <value>add</value>
    <value>update</value>
   </list>
  </property>
 </bean>  -->
 <bean id="orderServicePointCut" class="org.springframework.aop.support.JdkRegexpMethodPointcut">
  <property name="pattern" value=".*Order"></property>
 </bean>
 <!-- 切面aspect=pointcut+advice -->
 <bean id="orderServiceAspect" class="org.springframework.aop.support.DefaultPointcutAdvisor">
  <property name="advice" ref="orderServiceAdvice"/>
  <property name="pointcut" ref="orderServicePointCut"/>  
 </bean> 
 <!-- 代理 proxy -->
 <bean id="orderServiceProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
  <property name="target" ref="orderService"/>
  <property name="interceptorNames" value="orderServiceAspect"/>
  <property name="proxyInterfaces" value="com.lbb.aop.IOrderService"/>
 </bean>
 
 <!-- <bean class="org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"></bean> -->
 <!--对于基于代理的AOP的简化记的注释掉这个  @Qualifier("orderServiceProxy") -->
复制代码

基于aspectJ切点传统开发

注意1:需要在xml配置文件中导入aop声明 注意2:因为我们使用aspectj的切面声明方式 需要在导入aspectj的jar包com.springsource.org.aspectj.weaver

 <!-- 目标target -->
 <bean id="orderService" class="cn.lbb.aop.OrderServiceImpl"></bean>
 <!-- 通知advice -->
 <bean id="orderServiceAdvice" class="cn.lbb.aop.OrderHelper"></bean>

 <!-- 使用aop标签来完成切面与切点声明 -->
 <aop:config>
  <!-- 定义切点 -->
  <aop:pointcut expression="execution(* cn.lbb.aop.IOrderService.*(..))"
   id="orderServicePointCut" />
  <!-- 定义切面 -->
  <aop:advisor advice-ref="orderServiceAdvice" pointcut-ref="orderServicePointCut" />
  <!-- <aop:aspect></aop:aspect> aspectj框架它定义切面使用的 -->
 </aop:config>
复制代码

在配置文件中配置切面(切面=切点+通知)

<aop:config>来声明要对aop进行配置 <aop:pointcut>它是用于声明切点(简单说就是对哪些方法进行拦截) <aop:advisor> 定义传统的aop的切面,传统的aop切面它只能包含一个切点与一个增强 <aop:aspect>定义aspectj框架的切面.,它可以包含多个切点与多个通知

切点表达式写法

这个语法源于aspectJ的语法,spring中aop开发,对aspectj不是完全支持,只支持部分语法。

在开发中使用的比较多的是execution语法。

关于execution语法常用 1. execution(public * *()) 所有的public的方法 2. execution(* cn.itheima.aop.*.*(..)) 所有的aop包下的所有类的方法(不包含子包) . execution(* cn.itheima.aop..*.*(..)) 所有的aop包及其子包下的所有类的方法 . execution(* cn.itheima.aop.IOrderService.*(..)) IOrderService接口中定义的所有方法 . execution(* cn.itheima.aop.IOrderService+.*(..)) 匹配实现特定接口所有类的方法 . execution(* save*(..)) 区配所有的以save开头的方法

Spring整合aspectj框架实现的aop

在现在的开发中使用这种方案比较多. 在spring2.0以后它支持jdk1.5注解,而整合aspectj后可以使用aspectj语法,可以简化开发。

Aspect:切面 =切点+通知(多个切点与多个通知的组合) AspectJ 它是一个第三方框架,spring从2.0后可以使用aspectJ框架的部分语法.

AspectJ框架它定义的通知类型有6种

  1. 前置通知Before 相当于BeforeAdvice . 后置通知AfterReturning 相当于AfterReturningAdvice . 环绕通知 Around 相当于MethodInterceptor . 抛出通知AfterThrowing 相当于ThrowAdvice . 引介通知DeclareParents 相当于IntroductionInterceptor . 最终通知After 不管是否异常,该通知都会执行 相比spring 的传统AOP Advice多了一个最终通知

基于xml配置声明式事务管理方案

开发步骤

第一步:创建目标(target)

第二步:创建通知(增强 advice) 注意:在aspectj中它的增强可以不实现任何接口,只需要定义出增强功能(方法)

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;

//advice 通知
public class UserServiceHelper {

 // 前置通知
 public void before(JoinPoint jp) {
  System.out.println("拦截的目标类:" + jp.getSignature().getDeclaringTypeName());
  System.out.println("拦截的方法名称:" + jp.getSignature().getName());
  System.out.println("前置通知");
 }

 // 前置通知
 public void before1() {
  System.out.println("前置通知");
 }

 // 后置通知
 public void afterReturning(JoinPoint jp, Object val) {
  System.out.println("目标方法返回值:" + val);
  System.out.println("后置通知");
 }

 // 环绕通知
 public Object around(ProceedingJoinPoint pjp) throws Throwable {
  System.out.println("环绕前....");

  Object value = pjp.proceed(); // 执行目标行为

  System.out.println("环绕后....");

  return value;
 }

 // 异常抛出通知
 public void afterThrowing(JoinPoint jp,Throwable ex) {
  System.out.println("发现了异常。。。。"+ex);
 }

 // 最终通知
 public void after(JoinPoint jp) {
  System.out.println(jp.getSignature().getName());
  System.out.println("最终通知");
 }
}
复制代码

第三步:在spring的xml 配置文件中来配置 <aop:config>下的<aop:aspect>是aspectJ框架用来声明切面的。

<!-- target -->
 <bean id="userService" class="com.lbb.aspectj.UserServiceImpl"/>
 <!-- advice-->
 <bean id="userServiceAdvice" class="com.lbb.aspectj.UserServiceHelper"/>
 
 <!-- 使用aop:config来声明  使用aop:aspect来配置切面 -->
 <aop:config proxy-target-class="true">
  <aop:aspect ref="userServiceAdvice">
   <aop:pointcut expression="execution(* *.del(..))" id="delPointCut"/>
   <aop:before method="before" pointcut-ref="delPointCut"/>
   <aop:before method="before1" pointcut-ref="delPointCut"/>
   <aop:after-returning method="afterReturning" pointcut-ref="delPointCut" returning="val"/>
   <aop:around method="around"  pointcut-ref="delPointCut"/>
   <aop:after-throwing method="afterThrowing" pointcut-ref="delPointCut" throwing="ex"/>
   <aop:after method="after" pointcut-ref="delPointCut"/>
  </aop:aspect>
 </aop:config>
复制代码

注意:异常抛出:目标行为只有抛出了异常后才会执行这个增强方法。最终通知:无论是否有异常,最终通知都会执行。

Proxy-target-class的值默认是false,它代表有接口使用proxy代理。如果现在对目标要使用cglib代理(不考虑是否有接口),只需要将proxy-target-class设置为true。

基于annotation方案

在spring的配置文件中配置扫描注解

第一步:编写目标 在spring的配置文件中配置扫描注解<context:component-scan base-package="cn.lbb" />

第二步:编写增强(advice) 使用@Aspect来声明切面 使用@Before来声明前置通知 注意:必须在spring的配置文件中开启aspectJ注解自动代理功能。<aop:aspectj-autoproxy proxy-target-class="true"/>Proxy-target-class默认值是false,代表的是如果目标是有接口的使用proxy代理,如果没有接口使用cglib。proxy-target-class=true,不管目标是否有接口,都会使用cglib进行代理。

<context:component-scan base-package="cn.lbb" />
<!-- 开启aspectj注解自动代理 -->
<aop:aspectj-autoproxy proxy-target-class="true"/>


import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

//通知
@Component
@Aspect // 声明当前的bean就是一个切面
public class CustomerServiceHelper {
 
 @Pointcut("execution(* *.s*(..))")
 private void mypointcut(){}
 
 @Pointcut("execution(* *.update(..))")
 private void mypointcut1(){}

 // 前置通知
 @Before("mypointcut()||mypointcut1()")
 public void before() {
  System.out.println("前置通知...");
 }

 // 后置通知
 @AfterReturning(value = "execution(* *.update(..))", returning = "value")
 public void afterReturning(JoinPoint jp, Object value) {
  System.out.println("后置通知,目标方法的返回是" + value);
 }

 // 环绕通知
 @Around("mypointcut()")
 public Object around(ProceedingJoinPoint pjp) throws Throwable {
  System.out.println("环绕前...");
  Object value = pjp.proceed();
  System.out.println("环绕后");
  return value;
 }

 // 异常抛出通知
 @AfterThrowing(value = "mypointcut()", throwing = "ex")
 public void afterThrowing(JoinPoint jp, Throwable ex) {
  System.out.println("异常抛出通知:" + ex);
 }

 // 最终通知
 @After("mypointcut()")
 public void after() {
  System.out.println("最终通知");
 }
}
复制代码

Spring jdbc Template

Spring提供了一个jdbc模板,它类似于dbutils工具。

导入:jdbc与tx的相关包

快速入门

@Test
 public void test1(){
  DriverManagerDataSource dataSource = new DriverManagerDataSource();
  
  dataSource.setDriverClassName("com.mysql.jdbc.Driver");
  dataSource.setUrl("jdbc:mysql://localhost:3366/springtest");
  dataSource.setUsername("root");
  dataSource.setPassword("123");
  
  JdbcTemplate jdbcTemplate = new JdbcTemplate();
  jdbcTemplate.setDataSource(dataSource);
  
  jdbcTemplate.execute("update t_user set name = '张三' where id=1");
 }
复制代码

配置spring内置的连接池DriverManagerDataSource或者C3P0开源连接池配置

<!-- 
 <bean name="driverManagerDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
  <property name="url" value="jdbc:mysql://localhost:3366/springtest"/>
  <property name="username" value="root"/>
  <property name="password" value="123"/>
 </bean>
  -->
  
  <context:property-placeholder location="classpath:db.properties"/>
  <bean name="c3p0DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
  <property name="driverClass" value="${jdbc.driverClass}"/>
  <property name="jdbcUrl" value="${jdbc.url}"/>
  <property name="user" value="${jdbc.username}"/>
  <property name="password" value="${jdbc.password}"/>
 </bean>
 
 <bean name="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
  <property name="dataSource" ref="c3p0DataSource"/>
 </bean>



@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations="classpath:applicationContext.xml")
public class Test2 {

 @Autowired
 private JdbcTemplate jdbcTemplate;
 
 @Test
 public void test1(){
  jdbcTemplate.execute("update t_user set name = '李四' where id=2");
 }
 
 @Test
 public void test2(){
  jdbcTemplate.update("update t_user set name = ? where id=?","tom",1);
 }
 
 @Test
 public void test3(){
  jdbcTemplate.update("insert into t_user values(null,?,?,?)","赵六",30,"男");
 }
 
 @Test
 public void test4(){
  jdbcTemplate.update("delete from t_user where id = ?",4);
 }
 
 @Test
 public void test5(){
  String name = jdbcTemplate.queryForObject("select name from t_user where id = ?",String.class,1);
  System.out.println(name);
 }
 
 @Test
 public void test6(){
  Integer count = jdbcTemplate.queryForObject("select count(*) from t_user",Integer.class);
  System.out.println(count);
 }
 
 @Test
 public void test7(){
  User user = jdbcTemplate.queryForObject("select * from t_user where id = ?",new RowMapper<User>(){

   @Override
   public User mapRow(ResultSet rs, int rowNum) throws SQLException {
    User user = new User();
    user.setId(rs.getInt("id"));
    user.setId(rs.getInt("age"));
    user.setName(rs.getString("name"));
    user.setSex(rs.getString("sex"));
    return user;
   }
   
  },1);
  System.out.println(user);
 }
 
 @Test
 public void test8(){
  List<User> user = jdbcTemplate.query("select * from t_user",new RowMapper<User>(){

   @Override
   public User mapRow(ResultSet rs, int rowNum) throws SQLException {
    User user = new User();
    user.setId(rs.getInt("id"));
    user.setId(rs.getInt("age"));
    user.setName(rs.getString("name"));
    user.setSex(rs.getString("sex"));
    return user;
   }
   
  });
  System.out.println(user);
 }
 
 @Test
 public void test9(){
  User user = jdbcTemplate.queryForObject("select * from t_user where id = ?",new BeanPropertyRowMapper<User>(User.class),1);
  System.out.println(user);
  
  List<User> users = jdbcTemplate.query("select * from t_user",new BeanPropertyRowMapper<User>(User.class));
  System.out.println(users);
 }
}
复制代码

Spring 事务管理

转账案例 我们让dao去extends JdbcDaoSupport类,这个类中它创建了JdbcTempate,前提是我们需要注入一个dataSource。

<bean id="accountService" class="com.lbb.service.AccountServiceImpl">
 <property name="userDao" ref="accountDao"></property>
</bean>
 
<bean id="accountDao" class="com.lbb.dao.AccountDaoImpl">
 <property name="dataSource" ref="c3p0DataSource"></property>
</bean>
复制代码
// 转出
 @Override
 public void accountOut(String outname, double money) {
  this.getJdbcTemplate().update("update account set money=money-? where name=?", money, outname);
 }
复制代码

Spring事务管理机制

Spring事务管理的四个优点

  1. 提供一致的对于不同的事务管理的API . 支持声明式事务管理(重点) . 编程事务管理(在开发中应用比较少) . 优秀的整合与Spring的数据访问

我们重点讲解spring的事务管理的相关的API,还有声明式事务管理 Spring事务管理主要提供了三个接口来完成

  1. org.springframework.transaction.PlatformTransactionManager 这是一个事务管理器,可以来选择相关的平台(jdbc hibernate jpa…) . TransactionDefinition 它定义事务的一些相关信息 例如 隔离 传播 超时 只读 . TransactionStatus 它主要描述事务具体的运行状态

PlatformTransactionManager:平台事务管理器

在不同的持久化层解决技术它的事务代码不一样。

**JDBC开发**
Connection con=……;
con.setAutoCommit(false);//开启事务
con.rollback();
con.commit();

**Hibernate开发**
Session session=….;
Transaction t=session.beginTransaction();
t.commit();
t.rollback();
复制代码

它的子类

  • DataSourceTransactionManager 主要针对于JdbcTemplate开发 MyBatis开发
  • HibernateTransactionManasger主要针对于Hibernate开发
  • JpaTransactionManager 主要针对于JPA开发。

TransactionDefinition:它描述的是事务的定义信息

在TransactionDefinition中定义了大量的常量

隔离

事务的四个特性 ACID 原子性 一致性 隔离性 持久性。

不考虑事务隔离性有什么问题? 脏读,不可重复读 虚读。

ISOLATION_DEFUALT 它使用后端数据库的默认隔离级别(spring中选项) ISOLATION_READ_UNCOMMITTED 不能解决问题,会发生脏读 不可重复读 虚读 ISOLATION_READ_COMMITTED 可以解决脏读 会产生不可重复读与虚读。 ISOLATION_REPEATABLE_READ 可以解决脏读,不可重复读 解决不了虚读 ISOLATION_SERIALIZABLE 串行化,可以解决所有问题

对于不现的数据库,它的底层默认事务隔离级别不一样。 Oracle数据库它默认的是read_committed Mysql数据库它默认的是repeatable_read.

超时:默认值是-1 它使用的是数据库默认的超时时间。

只读:它的值有两个true/false,如果选择true一般是在select操作时

它解决的是两个被事务管理的方法互相调用问题。它与数据库没关系,是程序内部维护的问题。

以下定义了事务的传播行为 以上操作中最常用的三种 PROPAGATION_REQUIRED 默认值 两个操作处于同一个事务,如果之前没有事务,新建一个事务 PROPAGATION_REQUIRES_NEW 两个操作处于不同的事务 PROPAGATION_NESTED 它是一种嵌套事务,它是使用SavePoint来实现的。事务回滚时可以回滚到指定的savepoint,注意:它只对DataSourceTransactionManager有作用

以下了解 PROPAGATION_SUPPORTS 支持当前事务,如果不存在,就不使用事务 PROPAGATION_MANDATORY 支持当前事务,如果不存在,抛出异常 PROPAGATION_NOT_SUPPORTED 以非事务运行,如果有事务存在,挂起当前事务 PROPAGATION_NEVER 以非事务运行,如果有事务存在,抛出异常

TransactionStatus

它定义了事务状态信息,在事务运行过程中,得到某个时间点的状态。

声明式事务管理

  1. 编码方案 不建议使用,它具有侵入性。在原有的业务代码基础上去添加事务管理代码 . 声明式事务控制,基于AOP对目标进行代理,添加around环绕通知。这种方案,它不具有侵入性,不需要修改原来的业务代码

基于xml配置声明式事务管理方案

Spring为我们提供了一个TransactionInterceptor来完成增强,对于这个增强,我们可以使用spring为我们提供的一个标签<tx:advice>来完成操作。因为使用的是传统的spring的advice,需要使用<aop:advisor>

<!-- 配置事务管理器 -->
 <bean id="transactionManager"
  class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
  <property name="dataSource" ref="c3p0DataSource"></property>
 </bean>
 
 <!-- 配置通知 -->
 <tx:advice id="txAdvice" transaction-manager="transactionManager">
  <tx:attributes>
   <!-- 
    name:必须的,对哪些方法进行事务控制
     isolation 可选 设置事务隔离级别 默认是DEFAULT 
     propagation:可选 设置事务传播 默认值 REQUIRED
     timeout 可选 超时时间 默认值-1 
     read-only 可选 默认值是false 如果不是只读,它可以对insert update delete操作,如果是只读不可以。
     rollback-for 可选 可以设置一个异常,如果产生这个异常,触发事务回滚
     no-rolback-for 可选 可以设置一个异常,如果产生这个异常,不会触发事务回滚
    -->
   <tx:method name="account" />
      
  </tx:attributes>
 </tx:advice>
 
 <!-- 配置切面 -->
 <aop:config>
  <aop:pointcut expression="execution(* com.lbb.service.IAccountService.account(..))" id="txPointcut"/>
  <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>
 </aop:config>
复制代码

基于annotation声明式事务管理方案

可以使用@Transaction来在类或方法上添加声明式事务管理。

注意:需要在applicationContext.xml文件中使用<tx:annotation-driven transaction-manager="transactionManager"/>相当于开启注解事务控制。

总结

转载于:https://juejin.im/post/5ad17fb46fb9a028df230cce

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:3875789
帖子:775174
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP