Spring学习笔记

Spring的作用:

  • 能够降低组件之间的耦合度,实现软件之间的解耦
  • 可以使用Spring容器的众多服务,比如:事务管理器.当我们使用事务管理器时,开发人员不需要手动控制事务,也不需要处理复杂的事务传播.
  • Spring容器提供单例模式的支持
  • 容器提供了AOP技术,利用它很容易实现如权限拦截、运行期监控等.
  • 容器提供了很多辅助器,这些类能够加快应用的开发,如:JdbcTemplate、HibernateTemplate等.
  • Spring提供了主流应用框架的支持,如:集成Hibernate、JPA、Struts等,便于应用程序的开发

Spring的核心技术是IoC(Inversion of Control)和AOP(Aspect-oriented programming).

IoC还有另一个名字,叫DI(Dependency Injection),称为"依赖注入".所谓依赖注入就是指,在运行期间,由外部容器动态地将依赖对象注入到组件中.

依赖注入有三种方式:

  1. 使用构造器注入
  2. 使用属性setter注入
  3. 使用Field注入(利用注解)

Bean的装配

Spring提供三种实例化Bean的方式:

使用类构造器的方式

<bean id=”唯一标识符” class=”完整类名”></bean>

使用静态工厂的方式

<bean id="唯一标识符"

class="工厂的完整类名" factory-method="需要执行工厂的哪个方法的方法名" />

使用工厂实例的方式

<bean id=“唯一标识符1" class="工厂的完整类名"/>

<bean id="唯一标识符2" factory-bean=“唯一标识符1"

factory-method="需要执行工厂的哪个方法的方法名" />

Bean在实例化的时候默认只有一个,不管获得几次,都是同一个Bean,不过Spring提供了修改Bean作用域的属性,有几种取值:

singleton,prototype,request,session,global session.有几个都没什么用.

除了配置的注入方式以外,还可以使用注解的方式进行注入,在JAVA代码中使用@Autowired或@Resource注解方式进行装配.不过在装配之前,需要引入context命名空间,虽然Spring支持注解的解析,但是默认解析的"开关"没有打开,必须在配置文件里添加<context:annotation-config />标签,这个标签隐式的注册了Spring对注解进行解析的处理器:

AutowiredAnnotationBeanPostProcessor,CommonAnnotationBeanPostProcessor,

PersistenceAnnotationBeanPostProcessor,RequiredAnnotationBeanPostProcessor

@Autowired可以作用在构造器、字段和方法上,默认以类型进行查找,默认情况下它要求的依赖对象必须存在,如果可以允许不存在,则需要设置它的required属性值为false.如果想以名称进行查找,可以结合@Qualifier注解一起使用,如@Autowired @Qualifier("指定名称").@Qualifier注解还可以指定在构造器或者方法的参数在,如:

@Autowired
public void setPersonDao(@Qualifier("personDao") PersonDao personDao) {//用于属性的set方法上
      this.personDao = personDao;
}

@Resource可以作用在类、方法和字段上,默认以名称进行查找,如果找不到相应的Bean,则以类型进行查找.如果指定了name属性,则只按名称进行查找.

另外,Spring还支持Bean的自动装配:

<bean id=“foo” class=“...Foo”  autowire=“autowire type”>

autowire的取值包含(byType,byName,constructor,autodetecte),只用了解就行了,不常用,以免出现不可预知的后果.

此外,项目中通常会有上百个组件,如果这些组件全部采用在配置文件中通过Bean的方式来配置,则会明显增加配置文件的体积,查找和维护起来也会相关不便.所以,Spring2.5提供了自动扫描组件的方式来配置组件,它通过在类路径下寻找标了@Component、@Service、@Controller和@Repository注解的类,并把这些类纳入到Spring的容器中进行管理.

要使用自动扫描机制,需要引入context命名空间,并且需要在配置文件里面加上:

<context:component-scan base-package="包名"/>.

这个标签将扫描指定包(含子包)下的所有组件.并且把AutowiredAnnotationBeanPostProcessor 和CommonAnnotationBeanPostProcessor隐式地被包括进来以进行注解解析.

AOP--面向切面编程

AOP代理对象

如果目标对象实现了接口,则代理对象也实现同样的接口,否则使用cglib代理,则Spring有两种代理方式:

  1. 若目标对象实现了若干接口,spring使用JDK的java.lang.reflect.Proxy类代理。
  2. 若目标对象没有实现任何接口,spring使用CGLIB库生成目标对象的子类。

备注: 

对接口创建代理优于对类创建代理,因为这将产生更加松耦合的系统.

标记为final的方法无法得到通知,Spring需要为目标类产生子类,需要覆写被通知的方法,然后将通知织入.final方法不允许被覆写.

 

 

 

 

AOP中有几个概念,这些概念我们经常会用到,大数人也经常在程序中写出来,但是自己不知道:

Jointpoint(连接点)

连接点就是被拦截到的那个点,在Spring中,"点"指的是方法,因为Spring只支持方法类型的连接点.也就是说,哪个方法现在被拦截到了,我们就把这个方法称为连接点.

Pointcut(切入点)

切入点就是我们要拦截哪些连接点.比如你的工作是查水表,你负责的那个区域就是切入点,你现在正在查的那家,就是连接点.

Advice(通知)

通知就是拦截到连接点之后要做的事情,比如乘地铁,你不带包就不用安检,如果你带了包,就要安检(被拦截到了),然后X射线检查你的包就是一个通知.通知分为:前置通知,后置通知,最终通知,异常通知,环绕通知五种.

Aspect(切面)

是通知和切入点的结合,通知和切入点共同定义了关于切面的全部内容---它的功能、在何时和何地完成其功能,简单的说,通知所在的类,并且定义了切入点,那么这个类就是切面.

Target(目标对象)

需要代理的对象.

Weaving(织入)

把切面应用到目标对象来创建一个代理对象的过程就叫织入.

Introduction(引入)

引入就是在不修改类代码的前提下, Introduction可以在运行期为类动态地添加一些方法或Field.

 

 



要进行AOP编程,首先就要引入aop命名空间,Spring提供了两种切面方式,实际工作中我们可以任选其一:

  • 基于XML配置方式进行AOP开发
  • 基于注解方式进行AOP开发

切面类:

public class Security {     //切面
   public void checkSecurity(JoinPoint joinPoint){  //通知
           System.out.println("进行安全检查 ");
   }
} 

如果使用XML配置方式,需要在文件中使用<aop:config>标签,如:

<bean id="security" class="切面类" /><!-- 定义切面对象 -->
<bean id="userManager" class="目标对象类" /><!--创建接口实现类对象-->
    <aop:config>  <!--所有的切面和通知都必须定义在aop:config元素内部 -->
      <aop:aspect ref="security">  <!-- 声明切面  -->
         <!-- 声明切入点 -->
         <aop:pointcut id="userManagerPointcut" 
               expression="execution(* *.*(..))"/>
         <!--声明后置通知,在匹配的方法完全执行后运行-->
         <aop:after-returning method="checkSecurity" pointcut-ref="userManagerPointcut"/>
       </aop:aspect>
    </aop:config>
 

 

如果想在通知中获取相关数据,任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。JoinPoint 接口提供了一系列有用的方法,比如 getArgs()(返回方法参数)、 getThis()(返回代理对象)、getTarget()(返回目标)、 getSignature()(返回正在被通知的方法签名)和 toString() (打印出正在被通知的方法的有用信息)。

基于注解的方式除了在配置文件中引入aop的命名空间以外,还需要打开自动代理:

<aop:aspectj-autoproxy/>

这个标签将启用Spring对@AspectJ的支持,配置文件里面只需要声明切面对象和目标对象就行了,在类上标注@Aspect注解用以声明切面,然后在切面的方法上面标注通知或者切入点.

 

   定义切入点的几点注意:

        * 切入点使用方法定义的形式出现

        * 方法的定义

        * 方法的修饰符private修饰

        * 方法的返回值类型是void

        * 方法的名称自定义

        * 方法没有参数

        * 方法有方法体,方法体为空

在方法上使用@Pointcut定义切入点,如:

@Pointcut("execution( * cn.itcast.aop.aspectJ.before.UserManagerImpl.save*(..))")
private void  perform(){}

切入点表达式写法:

*    切入点表达式的写法

*    execution(主要)表示匹配方法执行的连接点

*    例如: * com.itcast.service..*.save*(..))

*         1 "*"  表示方法的返回类型任意

*         2 com.itcast.service..*  表示service包及其子包中所有的类

*         3 .save* 表示类中所有以save开头的方法

*         4 (..) 表示参数是任意数量

 

定义通知:

/**
 * @Before表示前置通知
 *   等价于
 *   aop:before method="checkSecurity" pointcut-ref="perform" 
 * @param joinPoint
 */
@Before("perform()||perform1()")            //可以使用多个切入点,用||隔开
public void checkSecurity(JoinPoint  joinPoint){
   System.out.println("进行安全性检查");
    
   if(joinPoint.getArgs()!=null&&joinPoint.getArgs().length>0){
       for(int i=0;i<joinPoint.getArgs().length;i++){
            System.out.println("方法的参数  "+joinPoint.getArgs()[i]);
       }
   }
    
   //获取方法的签名,方法的名称,方法的返回类型
   Signature signature=joinPoint.getSignature();
   System.out.println("方法的名称  "+signature.getName());
    
} 

 



 

事务管理

仅用四个词解释事务

 

atomic(原子性):要么都发生,要么都不发生。

consistent(一致性):数据应该不被破坏。

Isolate(隔离性):用户间操作不相混淆

Durable(持久性):永久保存,例如保存到数据库中等


 

Spring提供了两种事务管理方式:

  • 编程序事务管理
  • 声明式事务管理

 

 

编程序事务管理

编写程序式的事务管理可以清楚的定义事务的边界,可以实现细粒度的事务控制,比如可以通过程序代码来控制你的事务何时开始,何时结束等,与下面的声明式事务管理相比,它可以实现细粒度的事务控制。

 

 

声明式事务管理

如果并不需要细粒度的事务控制,可以使用声明式事务,在Spring中,只需要在Spring配置文件中做一些配置,即可将操作纳入到事务管理中,解除了和代码的耦合,这是对应用代码影响最小的选择,从这一点再次验证了Spring关于AOP的概念。当不需要事务管理的时候,可以直接从Spring配置文件中移除该设置.需要引入用于事务管理的命名空间(tx).

Spring并没有直接管理事务,而是将事务的管理委托给其他的事务管理器实现.

Spring支持的事务管理器:

 

  • org.springframework.jdbc.datasource.DataSourceTransactionManager (在单一的JDBC Datasource中的管理事务)
  • org.springframework.orm.hibernate3.HibernateTransactionManager (当持久化机制是hibernate时,用它来管理事务)
  • org.springframework.jdo.JdoTransactionManager (当持久化机制是Jdo时,用它来管理事务)
  • org.springframework.transaction.jta.JtaTransactionManager (使用一个JTA实现来管理事务。在一个事务跨越多个资源时必须使用)
  • org.springframework.orm.ojb.PersistenceBrokerTransactionManager (当apache的ojb用作持久化机制时,用它来管理事务)

基于XML文件配置事务管理器:

<!--1 配置数据源-->
<bean id="dateSource" class="org.apache.commons.dbcp.BasicDataSource">
   <property name="username" value="root"/>
   <property name="password" value="root"/>
   <property name="url" value="jdbc:mysql://localhost:3306/test"/>
   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
   <property name="maxActive" value="100"/>
    <!-- 初始化连接数 -->
   <property name="initialSize" value="5"/>
  
   <!--最大空闲数,当洪峰退去时, 连接池所放的最少连接数-->
   <property name="maxIdle" value="8"/>
   
   <!--最小空闲数,当洪峰到来时,引起的性能开销 -->
   <property name="minIdle" value="5"/>
</bean>
  
<!--2 配置JdbcTemplate模板,类似于dbutils,可数据访问操作-->  
<bean id="JdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    <!-- 给JdbcTemplate注入数据源,调用JdbcAccessor中的setDataSource(DataSource dataSource)注入数据源-->
    <property name="dataSource" ref="dateSource"/>
</bean>  
 
 
<!-- 3 声明事务管理器(实际上,事务管理器就是一个切面),事务管理器将在获取连接时,返回一个打开事务的连接 -->
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    <!-- 注入数据源,spring的jdbc事务管理器在管理事务时,依赖于JDBC的事务管理机制 -->
    <property name="dataSource" ref="dateSource"/>
</bean>
 
<!-- 4 配置通知 
     id="advice":该属性的值就是通知的唯一标识
     transaction-manager:表示通知织入哪个切面
-->
<tx:advice id="advice" transaction-manager="txManager">
  <tx:attributes>
    <!-- tx:method的属性:
          * name 是必须的,表示与事务属性关联的方法名(业务方法名),对切入点进行细化。通配符(*)可以用来指定一批关联到相同的事务属性的方法。
                    如:'get*'、'handle*'、'on*Event'等等.
          * propagation  不是必须的 ,默认值是REQUIRED 
                            表示事务传播行为, 包括REQUIRED,SUPPORTS,MANDATORY,REQUIRES_NEW,NOT_SUPPORTED,NEVER,NESTED
          * isolation    不是必须的 默认值DEFAULT 
                            表示事务隔离级别(数据库的隔离级别) 
          * timeout      不是必须的 默认值-1(永不超时)
                            表示事务超时的时间(以秒为单位) 
          
          * read-only    不是必须的 默认值false不是只读的 
                            表示事务是否只读? 
          
          * rollback-for 不是必须的   
                            表示将被触发进行回滚的 Exception(s);以逗号分开。
                            如:'com.foo.MyBusinessException,ServletException' 
          
          * no-rollback-for 不是必须的  
                              表示不被触发进行回滚的 Exception(s);以逗号分开。
                              如:'com.foo.MyBusinessException,ServletException'
                              
                              
        任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚                      
     -->
     <tx:method name="save*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
     <tx:method name="update*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
     <tx:method name="delete*" propagation="REQUIRED" isolation="DEFAULT" read-only="false"/>
     <!-- 其他的方法之只读的 -->
     <tx:method name="*" read-only="true"/>
  </tx:attributes>
</tx:advice>
 
<!-- 5 配置切入点 -->
<aop:config>
  
  <!-- 定义切入点,可以定义到类中所有的方法,之后在事务中在对方法进行细化 -->
  <aop:pointcut id="perform" expression="execution(* *.*(..))"/>
  
  <!-- 将通知和切入点关联起来-->
  <aop:advisor advice-ref="advice" pointcut-ref="perform"/>
</aop:config> 

配置完事务管理器后,再常规的配置Bean注入对象.默认只有运行时异常才将导致事务回滚.

基于注解配置事务管理器:

XML配置文件中只需要声明事务管理器,而不需要给它"灵魂",因为"灵魂"是由注解注入,所以需要注解解析器的支持:

<tx:annotation-driven transaction-manager="txManager"/>

注册对事务注解进行解析的处理器,将注解与事务管理器关联起来即可.

/**
 *   方法的事务设置将被优先执行。
 *   例如: BusinessService类在类的级别上被注解为只读事务,
 *   但是,这个类中的 save 方法的@Transactional 注解的事
 *   务设置将优先于类级别注解的事务设置。
 *   默认的 @Transactional 设置如下:
 *       事务传播设置是 PROPAGATION_REQUIRED
 *       事务隔离级别是 ISOLATION_DEFAULT
 *       事务是 读/写
 *       事务超时默认是依赖于事务系统的,或者事务超时没有被支持
 *       任何 RuntimeException 将触发事务回滚,但是任何 checked Exception 将不触发事务回滚
 */
@Transactional(readOnly=true)
public class BusinessService {
    
    @Resource(name="personDao")
    private PersonDao personDao;
    
    @Transactional(readOnly=false,isolation=Isolation.DEFAULT,propagation=Propagation.REQUIRED)
    public void save() throws Exception{
        personDao.save();
        
        this.update();
    }
    
    public void update() throws Exception{
        personDao.save();
        
        personDao.update();
    }
} 

 



 

小记:三大框架中最难的当属三大框架的整合,本文只是简单的将Spring的一些常见配置记下,诸如SpringMVC也不错,和StrutsMVC只是封装的不一样.Spring强大的声明式事务管理,常被用以业务层的封装.

时间: 2024-09-10 11:25:56

Spring学习笔记的相关文章

spring学习笔记(16)趣谈spring 事件机制[2]:多监听器流水线式顺序处理

上一篇我们使用到的ApplicationListener是无序的,结合异步调度它能满足了我们的大部分应用场景,但现在我们来个另类的需求,我们来模拟一条作业调度流水线,它不能异步,必须按照先后次序执行不同的任务才能得到我们的最终结果. 需求示例:现在假如华中科技大学的小白想要为它的智能机器人作品申报国家创新奖,需要经过学校.省级创新科研机构.国家创新科研机构逐层审核.我们尝试通过事件来实现,核心就在监听器实现SmartApplicationListener接口.示例如下: 1. 配置事件发布者小白

spring学习笔记(13)基于Schema配置AOP详解

基于Schema配置入门实例 除了基于@AspectJ注解的形式来实现AOP外,我们还可以在IOC容器中配置.先来看看一个常见的应用场景,在我们的web项目中,我们需要为service层配置事务,传统的做法是在每个业务逻辑方法重复下面配置中: Created with Raphaël 2.1.0程序开始1. 获取DAO层封装好的数据库查询API,如HIbernate中的SessionFactory/Session和mybatis中的xxxMapper2. 开启事务3. 根据入参查询数据库完成相应

spring学习笔记(10)@AspectJ研磨分析[1]入门、注解基本介绍

@AspectJ准备 AspectJ是一个面向切面的框架,它扩展了Java语言.AspectJ定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件. 在使用AspectJ之前,我们需要导入aspectJ相应的jar包,可到我的资源页http://download.csdn.net/detail/qwe6112071/9468329 中下载,而如果使用maven则可直接在pom.xml中加入如下代码: <dependency> <groupId>o

spring学习笔记(19)mysql读写分离后端AOP控制实例

在这里,我们接上一篇文章,利用JNDI访问应用服务器配置的两个数据源来模拟同时操作不同的数据库如同时操作mysql和oracle等.实际上,上个例子可能用来模拟mysql数据库主从配置读写分离更贴切些.既然如此,在本例中,我们就完成读写分离的模拟在web端的配置实例. 续上次的例子,关于JNDI数据源的配置和spring datasource的配置这里不再重复.下面着重加入AOP实现DAO层动态分库调用.可先看上篇文章<spring学习笔记(18)使用JNDI模拟访问应用服务器多数据源实例 >

spring学习笔记(21)编程式事务配置,service层概念引入

访问数据库事务导入 在我之前的文章<spring学习笔记(19)mysql读写分离后端AOP控制实例>中模拟数据库读写分离的例子,在访问数据库时使用的方法是: public <E> E add(Object object) { return (E) getSessionFactory().openSession().save(object); } 通过直接开启session而后保存对象.查询数据等操作,是没有事务的.而如果我们的项目规模变大,业务逻辑日益复杂,我们在一个方法中进行大

Spring学习笔记之依赖的注解(2)

Spring学习笔记之依赖的注解(2) 1.0 注解,不能单独存在,是Java中的一种类型 1.1 写注解 1.2 注解反射 2.0 spring的注解 spring的 @Controller@Component@Service//更多典型化注解,但是@Controller@Service建议使用 @service("personService")可以代替set get 方法,@Resource(name=personDao) @Autowired//按照类型匹配 @Qualifier

Spring学习笔记之aop动态代理(3)

Spring学习笔记之aop动态代理(3) 1.0 静态代理模式的缺点: 1.在该系统中有多少的dao就的写多少的proxy,麻烦 2.如果目标接口有方法的改动,则proxy也需要改动. PersonDao.java public interface PersonDao { public void savePerson(); } PersonDaoImpl.java public class PersonDaoImpl implements PersonDao{ public void save

Spring学习笔记2之表单数据验证、文件上传实例代码_java

在上篇文章给大家介绍了Spring学习笔记1之IOC详解尽量使用注解以及java代码,接下来本文重点给大家介绍Spring学习笔记2之表单数据验证.文件上传实例代码,具体内容,请参考本文吧! 一.表单数据验证 用户注册时,需要填写账号.密码.邮箱以及手机号,均为必填项,并且需要符合一定的格式.比如账号需要32位以内,邮箱必须符合邮箱格式,手机号必须为11位号码等.可以采用在注册时验证信息,或者专门写一个工具类用来验证:来看下在SpringMVC中如何通过简单的注释实现表单数据验证. 在javax

Spring学习笔记3之消息队列(rabbitmq)发送邮件功能_java

rabbitmq简介: MQ全称为Message Queue, 消息队列(MQ)是一种应用程序对应用程序的通信方法.应用程序通过读写出入队列的消息(针对应用程序的数据)来通信,而无需专用连接来链接它们.消息传递指的是程序之间通过在消息中发送数据进行通信,而不是通过直接调用彼此来通信,直接调用通常是用于诸如远程过程调用的技术.排队指的是应用程序通过 队列来通信.队列的使用除去了接收和发送应用程序同时执行的要求.其中较为成熟的MQ产品有IBM WEBSPHERE MQ. 本节的内容是用户注册时,将邮

最全面的Spring学习笔记

pring致力于提供一种方法管理你的业务对象.在大量Java EE的应用中,随处可见Spring.今天我将简单的介绍一下Spring这个框架. 本文适合读者: 想学Spring的Java开发者 刚用Spring不久的人 Why 为什么要使用Spring? Spring主要两个有功能为我们的业务对象管理提供了非常便捷的方法: DI(Dependency Injection,依赖注入) AOP(Aspect Oriented Programming,面向切面编程) Java Bean 每一个类实现了