Spring基础部分(ioc,aop,事物)

该篇文章是Spring基础部分,包括了ioc容器,aop以及事物的使用。

因为记录的是我自己个人学习的笔记,所以可能会不全面,主要是让自己梳理知识点,也欢迎大家留言讨论。


两个ioc容器,beanFactory和ApplicationContext。

beanFactory目前基本不用了,不过占用资源较少,在移动端有用到,存在也是为了向以前的版本做兼容。

常用实现类是XmlBeanFactory

ApplicationContext现在基本都是用这个容器(官方极力推荐)。

常用实现类是

FileSystemXmlApplicationContext,写xml路径要写绝对路径

ClassPathXmlApplicationContext,不用绝对路径

WebXmlApplicationContext

默认情况下ioc容器产生的类都是单列的。

<bean id="helloWorld" class="com.tutorialspoint.HelloWorld" scope="prototype">

若需要多例,添加scope,属性为prototype,单例是singleton



当对象的创建与销毁时,需要调用方法的话,只需要在bean标签上添加init-method 或 destroy-method 属性即可



1.默认的装配方式是调用无参构造

2.beanFactory和ApplicationContext的区别,装配bean的时候前者延迟加载

3.bean后处理器---->BeanPostProcessor

4.bean的生命周期

1. 执行无参构造

2. 执行setter

3. 获取bean的id (需要bean实现BeanNameAware)

4. 获取beanFactory(需要bean实现BeanFactoryAware)

5. 执行---before--方法 (需要注册bean后处理器)

6. bean初始化完毕之前(需要bean实现InitializingBean)

7. bean初始化完毕之后(xml文件中设置的init方法)

8. 执行---after--方法 (需要注册bean后处理器)

9. 执行业务方法(自己的)

10. 实现接口的销毁之前(需要bean实现DisposableBean)

11. 销毁之前(xml文件中设置destroy方法)

5.bean的id和name属性的作用是相同的。尽量使用id属性!!!


6.构造注入和设值注入---->设置注入知道就行,构造注入工作基本不用

list、set、array、map、properties的注入
map和properties比较特殊:
<property name="myMap">
   <map>
      <entry key="name" value="zhangsan"/>
      <entry key="sex" value="男"/>
   </map>
</property>
<property name="myProperties">
   <props>
      <prop key="name">张三</prop>
      <prop key="sex">男</prop>
   </props>
</property>
这两个有一点点区别。虽然都是键值对。
但是properties的key的value都是字符串的
而map的键值对可以是引用的

7.p命名空间设值注入和c命名空间构造注入---->基本不用,用的很少

8.com.demo 和com.demo.* 的区别,前者是导入其下所有的类以及包,后者只导入其下的包

9.引用类型--域属性--对象属性 不同叫法罢了!!

10.@Component @Service @Controller @Repository

都是注解,指由spring容器创建该bean
@Scope定义bean的作用域,有singleton和propotype
@Value注入bean的普通属性
@Autowired值注入bean的域属性(引用类型、对象属性。。)
@Qualifier也是注入域属性,但是需要和@Autowired一起使用!有byName和byType两种!(很少用的)
@Resource也是自动注入和@Autowired功能一样!(JDK自带,需要1.6版本以上。一般都不用这个吧?)
@PostConstruct和@PreDestroy 定义bean的生命始末,
一般写在某个方法上,在特定的声明周期会调用该方法
JavaConfig注解,注解到类上,@Configuration 表示该类代替Spring容器来管理bean

该类下面有一系列的方法,要什么bean就返回什么类型的bean,并用注解@Bean,例如

@Configuration
public class MyJavaConfig{
   @Bean(name="myStudent",autowire=Autowire.BY_TYPE)
   public Student getMyStudent(){
      return new Student();
   }
}
之后还要在Spring配置文件中添加组件扫描器,扫描该javaconfig类所在的包!!

然后加载该配置文件即可!!
感觉毫无用处!一般不会用这种,了解即可!

11.Sping测试包,添加test的jar包

在类上添加两个注解
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = "classpath:xxxx")

12.xml的优先级高于注解(xml配置更改只需重启服务器,而注解式开发的如果修改了注解则需要对整个程序进行重新编译,后期web项目非常麻烦。可以按这么理解来记忆)

13.spring的aop编程需要用到的两个包aop联盟包和spring的aop的包

14.aop编程术语

1. 切面(aspect)

通知说明了干什么和什么时候干(什么时候通过方法名中的before,after,around等就能知道),而切入点说明了在哪干(指定到底是哪个方法),这就是一个完整的切面定义

2. 织入(weaving)

将切面与目标对象方法进行aop整合的过程

3. 连接点(join point)

表示可以进行aop织入的地方,比如一个方法执行之前,之后,抛出异常时等。这些点就是指连接点,可想而知,一个类,连接点是非常多的。

4. 切入点(point cut)

表示进行aop织入的点,通常来说是有一些规则过滤的,比如定义了一些规则后。

本来这个类有10个连接点,但是能够进行aop进行织入的切入点可能就3个。

一般是用execution表达式,注解等等。

可以看这个:http://www.cnblogs.com/powerwu/articles/5177662.html

这篇博文对切入点的过滤条件非常详细

一般来说如果是通知的话,一般就不写什么切入点的过滤规则了,通知是对某一个类的所有方法都进行aop织入了!!

而顾问就比较细粒度,会定义切入点表达式,从而匹配需要进行aop织入的方法!

5. 目标对象(target object)

要进行aop织入的类

6. 通知(advise)

7. 顾问(advisor)

通知和顾问都是切面的实现形式,其中通知可以完成对目标对象方法简单织入功能。

而顾问包装了通知,可以让我们对通知实现更加精细化的管理,让我们可以指定具体的切入点。

使用通知这种切面对目标对象的方法进行织入的缺点是是显而易见的,因为他会对目标对象中的所有方法进行织入。

15.四个常用的advise

1. 前置通知--->自定义通知类实现MethodBeforeAdvice接口,并实现其方法

2. 后置通知--->自定义通知类实现AfterReturningAdvice接口,并实现其方法。(能得到返回值单不能修改)

3. 环绕通知--->自定义通知类实现MethodInterceptor接口,该接口是在aop联盟包中的,执行业务代码用proceed()方法,环绕通知可以修改返回值。

4. 异常通知--->自定义通知类实现ThrowsAdvice接口,该接口没有方法,需要我们在该接口的注释部分查看。可以重载多个异常,按异常的大小先手顺序排列。

public void afterThrowing(Exception1 ex)
public void afterThrowing(Exception2 ex)
public void afterThrowing(Exception3 ex)

16.xml文件的配置:

注册目标对象,注册切面对象,注册proxyFactoryBean对象,并将其依赖关系弄好就ok了。
每有一个代理对象,就需要在xml文件中写一个proxyFactoryBean对象,多个通知也要写多个。
这种比较麻烦,后期可以用注解形式。可以实现自动代理,解决代码臃肿问题。

17.如果目标对象有接口,默认使用JDK的proxy动态代理。

若目标类没有实现接口,则默认使用cglib代理。
cglib即可进行类代理,也可以进行实现了接口的类代理。
若要强制使用cglib代理。则需要在proxyFactotyBean属性中↓
配置optimize属性为true
或者设置proxyTargetClass属性为true

18.顾问(advisor) 也是切面,不过是更加细粒度的切面,可以对方法进行匹配。

进行更细粒度的织入常用的两种:

1.NameMatchMethodPointcutAdvisor

匹配方法名称,这个就是单单指类中的方法名称!用*代表通配符

2.RegexpMethodPointcutAdvisor 

正则匹配方法,这个匹配的是全限定性方法名。

例如在com.demo.test包下有MyTest类下右test01方法,则该方法的名称就是,com.demo.test.MyTest.test01

19.自动代理,常用的两个类:

(用了自动代理就方便很多了,底层实现都是bean后处理器。自动代理类是实现了beanPostProcessor接口)

1.DefaultAdvisorAutoProxyCreator

存在3个问题:
1)不能选择目标对象
2)不能选择切面类型,切面只能是advisor
3)不能选择advisor,所有的advisor都将作为切面织入到目标对象中

2.BeanNameAutoProxyCreator(功能更加强大!!)

需要配置两个属性:
1.beanNames --->目标对象
2.interceptorNames--->切面对象(可以选择advise也可以选择advisor)

20.AspectJ和Spring都是框架,前者比较小,仅仅是aop的框架。虽然两者都实现了aop的编程思想。

AspectJ更加优秀、便捷。所以Spring将其引入到了自己的框架中,在Spring的环境中如果进行aop的编程,其实就是用的AspectJ进行aop编程。

21.使用Spring-AspectJ整合,自定义advisor类,基于注解实现

目标对象可以再xml中注册或者@Component ,然后写上<aop:aspectj-autoproxy/>就可以了!
package aspectJ.annotation;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAdvisor {

    @Before(value = "args(str)")//这里的参数就是切入点的方法参数,前置可以使用,后置也可以使用,但是不能修改!
    //@Before("MyPointCut()")   使用自定义切入点。
    public void myBefor(String str){
        System.out.println("前置通知"+str);
    }

    @AfterReturning(value = "args(num)")
    public void myAfterReturning(Integer num){
        System.out.println("后置通知"+num);
    }

    @Around(value = "args(money)")//后置通知加这个ProceedingJoinPoint joinPoint
    public void myAround(ProceedingJoinPoint joinPoint,Double money) throws Throwable {
        System.out.println("环绕之前"+money);
        joinPoint.proceed();
        System.out.println("环绕之后"+money);
    }

    //要捕获什么异常就这边定义,execution表达式定义切入点
    @AfterThrowing(throwing = "e",value = "execution(* aspectJ.annotation.MyServiceImpl.doFirst(..))")
    public void myAfterThrowing(ArrayIndexOutOfBoundsException e){
        System.out.println("数组越界异常");
    }
    //要捕获什么异常就这边定义
    @AfterThrowing(throwing = "e1",value = "execution(* aspectJ.annotation.MyServiceImpl.doFirst(..))")
    public void myAfterThrowing(ClassNotFoundException e1){
        System.out.println("类没有找到异常通知");
    }

    @After("execution(public * aspectJ.annotation.MyServiceImpl.*ir*(..))")
    public void myFinally(){
        System.out.println("最终会执行的通知!");
    }

    //定义切入点!!
    @Pointcut("execution(* aspectJ.annotation.MyServiceImpl.doFirst(..))")
    public void MyPointCut(){}
}

22.aspectJ的5个通知:

1. @Before

2. @AfterReturning

3. @Around (ProceedingJoinPoint joinPoint)

4. @AfterThrowing

5. @After (最终都会执行,类似try-catch-finally)

23.aspectJ的2个注解

1. @Aspect (表示该类是切面)

2. @Pointcut (自定义切点)

24.使用Spring-AspectJ整合,自定义advisor类,基于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"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:context="http://www.springframework.org/schema/context"
       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
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:component-scan base-package="aspectJ.xml"/>

    <aop:config>

        <aop:pointcut id="doFirst" expression="execution(* aspectJ.xml.MyServiceImpl.doFirst(..))"/>
        <aop:pointcut id="doSecond" expression="execution(* aspectJ.xml.MyServiceImpl.doSecond(..))"/>
        <aop:pointcut id="doThird" expression="execution(* aspectJ.xml.MyServiceImpl.doThird(..))"/>

        <aop:aspect ref="myAdvisor">
            <aop:before method="myBefore" pointcut-ref="doFirst"/>
            <aop:after-returning method="myAfterReturning" returning="num" pointcut-ref="doSecond"/>
            <aop:around method="myAround" pointcut-ref="doThird"/>
            <aop:after method="myFinally" pointcut-ref="doSecond"/>
            <aop:after-throwing method="myAfterThrowing" throwing="e" pointcut-ref="doFirst"/>
        </aop:aspect>
    </aop:config>
</beans>

反正我不会用这种,太麻烦了!!还是注解的形式比较方便!!

推荐两篇关于AOP概念的博客:

http://blog.csdn.net/qukaiwei/article/details/50367761

http://blog.csdn.net/j080624/article/details/53996875

25.事物!!@Transactional


事物的四个特性(ACID):

⑴ 原子性(Atomicity)

  原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,这和前面两篇博客介绍事务的功能是一样的概念,因此事务的操作如果成功就必须要完全应用到数据库,如果操作失败则不能对数据库有任何影响。

⑵ 一致性(Consistency)

  一致性是指事务必须使数据库从一个一致性状态变换到另一个一致性状态,也就是说一个事务执行之前和执行之后都必须处于一致性状态。
  拿转账来说,假设用户A和用户B两者的钱加起来一共是5000,那么不管A和B之间如何转账,转几次账,事务结束后两个用户的钱相加起来应该还得是5000,这就是事务的一致性。

⑶ 隔离性(Isolation)

  隔离性是当多个用户并发访问数据库时,比如操作同一张表时,数据库为每一个用户开启的事务,不能被其他事务的操作所干扰,多个并发事务之间要相互隔离。
  即要达到这么一种效果:对于任意两个并发的事务T1和T2,在事务T1看来,T2要么在T1开始之前就已经结束,要么在T1结束之后才开始,这样每个事务都感觉不到有其他事务在并发地执行。
  关于事务的隔离性数据库提供了多种隔离级别,稍后会介绍到。

⑷ 持久性(Durability)

  持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的,即便是在数据库系统遇到故障的情况下也不会丢失提交事务的操作。
  例如我们在使用JDBC操作数据库时,在提交事务方法后,提示用户事务操作完成,当我们程序执行完成直到看到提示后,就可以认定事务以及正确提交,即使这时候数据库出现了问题,也必须要将我们的事务完全执行完成,否则就会造成我们看到提示事务处理完毕,但是数据库因为故障而没有执行事务的重大错误。

事物的传播特性和隔离级别:http://www.linuxidc.com/Linux/2011-12/50178.htm
事物的4中隔离级别用故事来理解:http://blog.csdn.net/qq_33290787/article/details/51924963

26.在项目中事物的使用:

首当其冲的是要在xml中配置PlatformTransactionManager平台事物管理器。
这是一个接口,下面有两个常用的实现类。

1. HibernateTransactionManager(hibernate框架用的)

2. DataSourceTransactionManager(jdbc或mybatis使用)

例如在xml中像这样配置:
<!-- (事务管理)transaction manager, use JtaTransactionManager for global tx -->
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>

然后开启事物注解驱动:
<!-- 开启事务注解驱动 -->
<tx:annotation-driven transaction-manager="transactionManager"/>

如果不用这个驱动,则需要配:
<bean id="myProxy" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean">
<property name="" value=""/>
<property name="" value=""/>
<property name="" value=""/>
</bean>

配置一堆东西,而且这个proxyFactoryBean只能对应一个目标对象,很麻烦,而且用的时候还是用代理对象,不适合逻辑!!肯定不用这种啊!!还是直接开启事物注解驱动来的爽!

配置好了以后,就可以再项目中使用@Transactional注解了!一般我们用在Service层
也可以用在Controller层,但是具体怎么弄,这边先不做介绍。后面的博文会介绍。

@Transactional的参数介绍:参考博文:http://blog.csdn.net/bao19901210/article/details/41724355




propagation enum: Propagation 可选的事务传播行为设置,默认就行
isolation enum: Isolation 可选的事务隔离级别设置,默认就行
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组,一般设置这个
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组

noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组

属性 类型 描述
value String 可选的限定描述符,指定使用的事务管理器若只有一个默认就是直接调用了,一般也不用设置
propagation enum: Propagation 可选的事务传播行为设置,默认就行
isolation enum: Isolation 可选的事务隔离级别设置,默认就行
readOnly boolean 读写或只读事务,默认读写
timeout int (in seconds granularity) 事务超时时间设置
rollbackFor Class对象数组,必须继承自Throwable 导致事务回滚的异常类数组,这是我们需要设置的
rollbackForClassName 类名数组,必须继承自Throwable 导致事务回滚的异常类名字数组,2选1
noRollbackFor Class对象数组,必须继承自Throwable 不会导致事务回滚的异常类数组
noRollbackForClassName 类名数组,必须继承自Throwable 不会导致事务回滚的异常类名字数组
Spring默认的回滚是RuntimiException,而Exception默认是不回滚的,所有@Transactional一般要设置回滚的异常处理。

还有一种是基于aspectJ的事物处理,感觉也挺麻烦,但是是基于xml的配置,这边贴一下网上的配置:

    <!-- 事物切面配置 -->
    <tx:advice id="advice" transaction-manager="transactionManager">
        <tx:attributes>
            <tx:method name="update*" propagation="REQUIRED" read-only="false" rollback-for="java.lang.Exception"/>
            <tx:method name="insert" propagation="REQUIRED" read-only="false"/>
        </tx:attributes>
    </tx:advice>

    <aop:config>
        <aop:pointcut id="testService" expression="execution (* com.baobao.service.MyBatisService.*(..))"/>
        <aop:advisor advice-ref="advice" pointcut-ref="testService"/>
    </aop:config>

好处就是好管理,我觉得用注解就能满足我们大部分需求了。各位看自己的需求吧!

27.Spring中Web的使用以及一些需要注意的地方:

Servlet是单列的!

如何在servlet中使用容器中的对象,例如在servlet中调用UserService来执行addUser方法

1.获取容器中的对象:ApplicationContext ac=new ClassPathXmlApplicationContext("applicationContext.xml");

2.获取UserService:UserService us=(UserService)ac.getBean("UserService");

3.执行:us.addUser(user);

注意:为了避免资源的浪费,容器的创建放在ServletContextListener的实现类的contextInitialized方法中,ServletContext这个接口的实现类在整个项目启动的时候就只有一个,所以当监听到创建的事件时就我们就创建容器,并把容器放入到application作用域中!!全局作用域,这样,整个项目的容器就只有一份,并且生命周期和整个项目是一致的。☆☆☆☆☆

ServletContext接口的介绍:http://blog.csdn.net/gavin_john/article/details/51399425

Spring web模块有一个类已经帮我们实现了ServletContextListener接口,那就是这个ContextLoaderListener这个类

<!--注册ServletContext监听器,完成两件事
        1.在ServletContext被创建时,创建Spring容器对象
        2.将创建好的Spring容器对象放入到ServletContext的域属性空间
-->
<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring-mvc.xml</param-value>
</context-param>
非常的方便!!只需指定下contextConfigLocation是哪个即可!!!

利用工具类来获取容器中的对象:

WebApplicationContextUtils,需要传入ServletContext对象,该对象可以通过Servlet的this.getServletContext()来获取

不过这个方式也比较麻烦。后面在SpringMVC部分会有更简单的方式


以上就是Spring基础部分的ioc,aop,以及事物的使用。其他部分会在其他博文中。


猜你喜欢

转载自blog.csdn.net/ljqwstc/article/details/78192442