Spring笔记整理(二)之AOP_声明式事务_JdbcTemplate

Spring笔记整理(二)之AOP

IOC回顾

spirng整合连接池

把别人编写的类交给spring管理

  1. 在外部创建一个jdbc.properties配置文件 这个里面配置各个数据库的信息

  2. 在spirng的配置文件中,加载外部的jdbc.properties文件

    <context:property-placeholder location="classpath:jdbc.properties"/>
    
  3. 通过spirng的ioc和di设定连接池的核心对象以及对象的核心属性

    ps: ${} 是spring提供的的表达式 针对的就是spring加载了properties之后的数据

    <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource"> 
        <property name="driverClass" value="${jdbc.driver}"></property>
        <property name="jdbcUrl" value="${jdbc.url}"></property>
        <property name="user" value="${jdbc.username}"></property>
        <property name="password" value="${jdbc.password}"></property>
    </bean>
    

spring的IOC的注解配置

别人的类还用xml,自己的类都用注解

条件
  1. 导包 spring-aop.jar

  2. 开启扫描器

    <context:component-scan base-package="cn.dmdream"></context:component-scan>
    
常用注解
  1. @Component(“bean的id名称”)
    定义在类上 扫描到就会创建该类的实例对象
    1. @Controller(“bean的id名称”) web层的类上
    2. @service(“bean的id名称”) service层的类上
    3. @Repository(“bean的id名称”) dao层的类上
  2. @Value(“属性值”)
    定义在属性字段上的 针对的是基本类型和String类型
    • set的方法可以省略掉
  3. @Autowired
    定义在属性字段上的 针对是对象类型
    • set的方法可以省略掉
    • 会自动去spring容器中找该类的实例对象赋值
  4. @Qualifier(“指定的对象名称”)
    定义在属性字段上的 指定使用该类型下面的哪一个实体对象
    • 必须要和@Autowired一起使用
  5. @Scope(“singleton或者prototype”)
全注解的配置
  • 条件: 注解类

    package cn.dmdream.springconfig;
    
    import javax.sql.DataSource;
    
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.PropertySource;
    import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
    
    import com.mchange.v2.c3p0.ComboPooledDataSource;
    
    @Configuration // 表示该类是一个注解类
    @ComponentScan(basePackages="cn.dmdream")  //<context:component-scan base-package="cn.dmdream"></context:component-scan>
    @PropertySource(value="classpath:jdbc.properties")//<context:property-placeholder location="classpath:jdbc.properties"/>
    //@Import(value = {DataSourceconfig.class})
    public class SpringConfig
    {
    	@Value("${jdbc.driver}")
    	private String driver;
    	@Value("${jdbc.url}")
    	private String url;
    	@Value("${jdbc.username}")
    	private String username;
    	@Value("${jdbc.password}")
    	private String password;
    	
    	// 自己创建c3p0连接池 给spring容器
    	@Bean(name="c3p0")  //<bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    	public DataSource createDataSourceC3p0() throws Exception
    	{
    		ComboPooledDataSource ds = new ComboPooledDataSource();
    		ds.setDriverClass(driver);
    		ds.setJdbcUrl(url);
    		ds.setUser(username);
    		ds.setPassword(password);
    		return ds;
    	}
    	
    	// 4.3以前 要给spring配置一个解析器 来解析 ${}
    	@Bean
    	public static PropertySourcesPlaceholderConfigurer createPropertySourcesPlaceholderConfigurer()
    	{
    		return new PropertySourcesPlaceholderConfigurer();
    	}
    }
    
    

    jdbc.properties

    jdbc.driver=com.mysql.jdbc.Driver
    jdbc.url=jdbc:mysql://cloud.dmdream.cn:3306/hibernate
    jdbc.username=root
    jdbc.password=1234
    
  • 加载不是配置文件了 而是注解类

    测试类Demo代码

    package cn.dmdream.test;
    
    import java.sql.Connection;
    import java.sql.SQLException;
    
    import javax.sql.DataSource;
    
    import org.junit.Test;
    import org.springframework.context.ApplicationContext;
    import org.springframework.context.annotation.AnnotationConfigApplicationContext;
    
    import cn.dmdream.springconfig.SpringConfig;
    
    public class SpringConfigTest 
    {
    	@Test
    	public void test() throws SQLException
    	{
    		// 加载注解类
    	   ApplicationContext context=new AnnotationConfigApplicationContext(SpringConfig.class);
    	  /* UserService userService=(UserService)context.getBean("userService");
    	   userService.save();*/
    	   DataSource ds =(DataSource)context.getBean("c3p0");
    	   Connection con = ds.getConnection();
    	   System.out.println(con);
    	}
    }
    
    

spring整合junit

  1. 导包
    • spring-test.jar
    • spring-aop.jar
    • junit.jar
  2. 告诉spirng配置文件的位置
    • @ContextConfiguration(value=“classpath:applicationContext.xml”)
  3. 告诉spirng谁加载配置文件
    • @RunWith(value =SpringJUnit4ClassRunner.class)
  4. 分层测试

AOP

一、Spring的AOP概述

  • AOP: 面相切面编程思想

    简单理解: 将一些共性的内容进行抽取,在需要用到的地方,以动态代理的方式进行插入
    在不修改源码的基础上,还能对源码进行前后的增强

  • 底层的技术: 动态代理

  • spring就是把动态代理进行层层封装 诞生出了aop思想

AOP的应用
  • 权限拦截
  • 日志的输出
  • 性能的检测
  • 事务管理
aop思想的底层技术: 动态代理

举例

  • 目标对象 save
  • 代理对象 对目标对象save方法进行增强

两种动态代理的方式

1.JDk动态代理

不用导包,jdk提供好了

proxy

条件: 目标类必须得有接口

@Test
public void test2()
{
    // 目标类
    final UserImpl user=new UserImpl();


    // jdk的动态代理
    // 参数一: 和目标类一样的类加载器
    // 参数二: 和目标类一样的接口 
    // 参数三: 增强的业务
    User userproxy=(User)Proxy.newProxyInstance(
            user.getClass().getClassLoader(), 
            user.getClass().getInterfaces(),
            new InvocationHandler() {
                @Override //增强的业务
                // 参数一: 固定值
                // 参数二: 要增强的方法 (原有的方法)
                // 参数三: 方法运行时候需要的参数
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

                    if("save".equals(method.getName()))
                    {
                        // 让原有方法执行
                        // 本身应该调用这个方法的对象
                        System.out.println("之前增强了...");
                        method.invoke(user, args);
                        System.out.println("之后增强了...");
                    }else
                    {
                        // 执行原来的方法
                        method.invoke(user, args);
                    }

                    return null;
                }
            });

    userproxy.save(); // 只要执行,就会执行增强业务方法invoke方法,这个方法里面就是对save方法的增强
    userproxy.delete(); // 只要执行,就会执行增强业务方法invoke方法,这个方法里面就是对delete方法的增强
}

User&Impl

package cn.dmdream.domain;

public interface User
{
	public void save();
	public void delete();
	public void update();
	public void find();
}

// 带接口的--为了演示jdk动态代理
public class UserImpl implements User
{

	@Override
	public void save() {
		System.out.println("普通的保存方法...");
		
	}

	@Override
	public void delete() {
		System.out.println("普通的删除方法...");
		
	}

	@Override
	public void update() {
		System.out.println("普通的修改方法...");
		
	}

	@Override
	public void find() {
		System.out.println("普通的查询方法...");
		
	}

}

2.CGLIB动态代理

第三方 单用它就必须导包

enhance

条件: 只要有一个目标类即可增强

@Test
public void test3()
{
    // 目标类---没有接口
    final Person person=new Person();

    // CGLIB的方式
    // 参数一: 目标类的字节码文件类型  因为用于继承
    // 参数二: 增强的业务逻辑
    Person p=(Person)Enhancer.create(Person.class,new MethodInterceptor() {

        @Override
        // 参数一: 代理对象的类型 固定值
        // 参数二: 目标类要增强的方法
        // 参数三: 方法运行时期需要的参数
        // 参数四: 代理方法 忽略
        public Object intercept(Object proxy, Method method, Object[] args, MethodProxy arg3) throws Throwable {

            if("delete".equals(method.getName()))
            {
                System.out.println("之前增强....");
                method.invoke(person, args);
                System.out.println("之后增强....");
            }else
            {
                // 让原有方法执行
                method.invoke(person, args);
            }

            return null;
        }
    });

    p.delete(); // 执行了它  增强业务intercept方法也会执行 这个方法里面就是对delete的增强
    p.find();// 执行了它  增强业务intercept方法也会执行 这个方法里面就是对find的增强
}

Person

package cn.dmdream.domain;
// 没有接口--为了演示cglib的动态代理
public class Person 
{
	public void save() {
		System.out.println("普通的保存方法...");
	}
	public void delete() {
		System.out.println("普通的删除方法...");
	}
	public void update() {
		System.out.println("普通的修改方法...");
	}
	public void find() {
		System.out.println("普通的查询方法...");
	}
}

3.spring使用的代理方式
  • 如果目标类有接口 会默认使用jdk的动态代理
  • 如果目标类没有接口 会默认使用CGLIB的动态代理

二、Spring AOP快速入门(全xml配置)

AOP的术语
  • 目标对象 要被增强的对象
  • 代理对象 增强以后的对象
  • 连接点: 可以被增强的方法
    • 例如:person里面的save() delete() update() find() 都可以被增强 统称为连接点
  • 切入点: 要被增强的方法
    • 例如: person里面要开始增强delete方法了 那么delete方法就是切入点
  • 通知/增强: 做了增强的那段代码方法
    • 只要在原有方法之前和之后进行了增强的代码方法,我们就可以把这些增强的代码方法称之为通知或者增强
  • 切面: 切入点+通知/增强=切面
    • 里面有要被增强的方法+增强的方法
  • 织入: 切入点集成到切面的这一个过程,就叫做织入过程
基于aspectj的AOP的快速入门
  1. 导包

    • 1 AOP联盟 (AOP的一套规范(接口))—从依赖包导入
    • 2 Spring-aop.jar(实现了AOP的一套规范)
    • 3 aspectj.jar(第三方的–实现了AOP的一套规范)
    • 4 spring-aspects.jar (spring整合aspectj)—从核心包导入
  2. 开发步骤

    需求: 对person中的save方法进行增强

    1. 确定目标类 (目标类中有切入点 —要被增强的方法)

       <!-- 目标类 (有切入点 有被增强的方法save) -->
      <bean id="person" class="cn.dmdream.domain.Person"></bean>
      
    2. 确定切面类 (里面有通知/增强 ----增强的那段代码方法)

      <!-- 切面类(有通知/增强  有增强方法) -->
      <bean id="myAspect" class="cn.dmdream.aspectj.MyAspect"></bean>
      
    3. 配置织入过程 (将增强方法和被增强方法进行结合)

      <!-- 配置织入(增强方法和被增强方法集成在一起) -->
      <aop:config>
          <aop:aspect ref="myAspect"><!-- 指定切面类 -->
              <!-- 原始方式 -->
              <aop:before method="beforeMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
              <aop:after-returning method="aftereturningMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
              <aop:after-returning method="aftereturningMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
              <!-- 便捷方式 -->
              <!-- 定义切入点 -->
              <aop:pointcut expression="execution(void cn.dmdream.domain.Person.save())" id="pointcut1"/>
              <aop:pointcut expression="execution(void cn.dmdream.domain.Person.delete())" id="pointcut2"/>
              <aop:pointcut expression="execution(* cn.dmdream.domain.Person.up*(..))" id="pointcut3"/>
              <aop:pointcut expression="execution(* cn.dmdream.domain.*.find(..))" id="pointcut4"/>
              <!-- 通过切入点织入 -->
              <aop:before method="beforeMethod" pointcut-ref="pointcut1"/>
              <aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut1"/>
              <aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut2"/>
              <aop:around method="aroundMethod" pointcut-ref="pointcut3"/>
              <aop:after-throwing method="throwingMethod" pointcut-ref="pointcut4"/>
              <aop:after method="afterMethod" pointcut-ref="pointcut4"/>
              
          </aop:aspect>
      </aop:config>
      
    4. AOP的xsd约束(完整版例子)

      <?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:context="http://www.springframework.org/schema/context"
          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/context 
              http://www.springframework.org/schema/context/spring-context.xsd
              http://www.springframework.org/schema/aop
              http://www.springframework.org/schema/aop/spring-aop.xsd">
          
           <!-- 目标类 (有切入点 有被增强的方法save) -->
          <bean id="person" class="cn.dmdream.domain.Person"></bean>
          
          <!-- 切面类(有通知/增强  有增强方法) -->
          <bean id="myAspect" class="cn.dmdream.aspectj.MyAspect"></bean>
          
          <!-- 配置织入(增强方法和被增强方法集成在一起) -->
          <aop:config>
          	<aop:aspect ref="myAspect">
          		<!-- 定义切入点 -->
          		<aop:pointcut expression="execution(void cn.dmdream.domain.Person.save())" id="pointcut1"/>
          		<aop:pointcut expression="execution(void cn.dmdream.domain.Person.delete())" id="pointcut2"/>
          		<aop:pointcut expression="execution(* cn.dmdream.domain.Person.up*(..))" id="pointcut3"/>
          		<aop:pointcut expression="execution(* cn.dmdream.domain.*.find(..))" id="pointcut4"/>
          		<!-- 原始方式 -->
          		<!-- <aop:before method="beforeMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
          		<aop:after-returning method="aftereturningMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/> -->
          		<!-- <aop:after-returning method="aftereturningMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/> -->
          		<!--便捷方式 -->
          		<aop:before method="beforeMethod" pointcut-ref="pointcut1"/>
          		<aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut1"/>
          		<aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut2"/>
          		<aop:around method="aroundMethod" pointcut-ref="pointcut3"/>
          		<aop:after-throwing method="throwingMethod" pointcut-ref="pointcut4"/>
          		<aop:after method="afterMethod" pointcut-ref="pointcut4"/>
          	</aop:aspect>
          </aop:config>
              
       </beans>
      
细节1-切入点语法表达式的抽取

方式一:不定义切入点

 <!-- 目标类 (有切入点 有被增强的方法save) -->
<bean id="person" class="cn.dmdream.domain.Person"></bean>

<!-- 切面类(有通知/增强  有增强方法) -->
<bean id="myAspect" class="cn.dmdream.aspectj.MyAspect"></bean>

<!-- 配置织入(增强方法和被增强方法集成在一起) -->
<aop:config>
    <aop:aspect ref="myAspect">
        <!-- 原始方式 -->
        <aop:before method="beforeMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
        <aop:after-returning method="aftereturningMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
        <aop:after-returning method="aftereturningMethod" pointcut="execution(void cn.dmdream.domain.Person.save())"/>
    </aop:aspect>
</aop:config>

方式二:自定义切入点

<!-- 便捷方式 -->
<!-- 定义切入点 -->
<aop:pointcut expression="execution(void cn.dmdream.domain.Person.save())" id="pointcut1"/>
<aop:pointcut expression="execution(void cn.dmdream.domain.Person.delete())" id="pointcut2"/>
<aop:pointcut expression="execution(* cn.dmdream.domain.Person.up*(..))" id="pointcut3"/>
<aop:pointcut expression="execution(* cn.dmdream.domain.*.find(..))" id="pointcut4"/>
<!-- 通过切入点织入 -->
<aop:before method="beforeMethod" pointcut-ref="pointcut1"/>
<aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut1"/>
<aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut2"/>
<aop:around method="aroundMethod" pointcut-ref="pointcut3"/>
<aop:after-throwing method="throwingMethod" pointcut-ref="pointcut4"/>
<aop:after method="afterMethod" pointcut-ref="pointcut4"/>
细节2-切入点语法表达式的书写
execution(void cn.dmdream.domain.Person.save())

void      cn.dmdream.domain.    Person.      save()
返回值     包名/子包名	       类名	        方法名

*: 代表所有
..: 代表上一层 或者是 方法参数的全匹配

* cn.dmdream.domain.Person.save(..)//最常用

* cn.dmdream.domain.Person.sa*(..)

* cn.dmdream.domain.*.*(..)

* cn.dmdream..*.*(..)

* cn.dmdream..Person.save(..)//最常用
细节3-通知类型

切入点配置和织入过程详细见上面的配置文件

  • 前置通知 在之前增强 权限拦截

    <aop:pointcut expression="execution(void cn.dmdream.domain.Person.save())" id="pointcut1"/>
    <aop:before method="beforeMethod" pointcut-ref="pointcut1"/>
    
    // 增强方法
    public void beforeMethod()
    {
        System.out.println("-----beforeMethod-----");
    }
    
  • 后置通知 在之后增强 日志输出

    <aop:pointcut expression="execution(void cn.dmdream.domain.Person.delete())" id="pointcut2"/>
    <aop:after-returning method="aftereturningMethod" pointcut-ref="pointcut2"/>
    
    // 增强方法
    public void aftereturningMethod()
    {
        System.out.println("----aftereturningMethod---");
    }
    
  • 环绕通知 在之前和之后都增强 性能检测

    <aop:pointcut expression="execution(* cn.dmdream.domain.Person.up*(..))" id="pointcut3"/>
    <aop:around method="aroundMethod" pointcut-ref="pointcut3"/>
    
    // 增强方法
    public void aroundMethod(ProceedingJoinPoint pdp) throws Throwable // 正在要执行的原有方法
    {
        System.out.println("之前...");
        // 原有方法执行一下
        pdp.proceed(); // method.invoke();
        System.out.println("之后...");
    }
    
  • 异常通知 catch{}

    <aop:pointcut expression="execution(* cn.dmdream.domain.Person.up*(..))" id="pointcut3"/>
    <aop:after-throwing method="throwingMethod" pointcut-ref="pointcut4"/>
    
    // 增强方法
    public void throwingMethod()
    {
        System.out.println("----throwingMethod----");
    }
    
  • 最终通知 finally {}

    <aop:pointcut expression="execution(* cn.dmdream.domain.*.find(..))" id="pointcut4"/>
    <aop:after method="afterMethod" pointcut-ref="pointcut4"/>
    
    // 增强方法
    public void afterMethod()
    {
        System.out.println("--不管你有没有异常,我都出来了---");
    }
    

三、Spring AOP的注解方式

半xml和半注解的方式

别人类用xml <–> 自己的类用注解

如何使用

  1. 导包 spring-aop.jar

  2. 开启注解扫描器

    <?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:context="http://www.springframework.org/schema/context"
        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/context 
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop
            http://www.springframework.org/schema/aop/spring-aop.xsd">
        
        	<!-- 开启注解扫描器 -->
        	<context:component-scan base-package="cn.dmdream"></context:component-scan>
       		<!-- 开启注解的动态代理方式  为了识别@Before @AfterReturning -->
       		 <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
            
     </beans>
    

自己的类

  1. Person @Componect(“person”) 目标类
  2. MyAspect @Componect(“myAspect”) @Aspect切面类

使用注解替代aop织入的xml配置

  1. 确定谁是切面类 @Aspect

    // 切面类--有增强方法
    @Component("myAspect")
    @Aspect
    public class MyAspect{
        ...
    }
    
  2. 切面类下面的增强方法就能使用注解来配置了

    条件:先开启动态代理的注解方式

    <!-- 开启注解的动态代理方式  为了识别@Before @AfterReturning -->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
Demo代码
  1. 主配置文件(见上面)

  2. 目标类

    package cn.dmdream.domain;
    import org.springframework.stereotype.Component;
    // 没有接口--为了演示cglib的动态代理
    @Component("person")
    public class Person 
    {
    	public void save() {
    		System.out.println("普通的保存方法...");	
    	}
    	public void delete() {
    		System.out.println("普通的删除方法...");
    	}
    	public void update() {
    		System.out.println("普通的修改方法...");
    	}
    	public void find() {
    		System.out.println("普通的查询方法...");
    	}
    }
    
  3. 切面类

    package cn.dmdream.aspectj;
    
    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.springframework.stereotype.Component;
    
    // 切面类--有增强方法
    @Component("myAspect")
    @Aspect
    public class MyAspect 
    {
    	// 增强方法
    	@Before(value ="execution(* cn.dmdream.domain.Person.save(..))")
    	public void beforeMethod()
    	{
    		System.out.println("-----beforeMethod-----");
    	}
    	
    	// 增强方法
    	@AfterReturning(value="execution(* cn.dmdream.domain.Person.delete(..))")
    	public void aftereturningMethod()
    	{
    		System.out.println("----aftereturningMethod---");
    	}
    	
    	// 增强方法
    	@Around(value="execution(* cn.dmdream.domain.Person.find(..))")
    	public void aroundMethod(ProceedingJoinPoint pdp) throws Throwable // 正在要执行的原有方法
    	{
    		System.out.println("之前...");
    		// 原有方法执行一下
    		pdp.proceed(); // method.invoke();
    		System.out.println("之后...");
    	}
    	
    	// 增强方法
    	@AfterThrowing(value="execution(* cn.dmdream.domain.Person.update(..))")
    	public void throwingMethod()
    	{
    		System.out.println("----throwingMethod----");
    	}
    	
    	// 增强方法
    	@After(value="execution(* cn.dmdream.domain.Person.update(..))")
    	public void afterMethod()
    	{
    		System.out.println("--不管你有没有异常,我都出来了---");
    	}
    }
    
    
  4. 测试类

    @ContextConfiguration("classpath:applicationContext.xml")
    @RunWith(SpringJUnit4ClassRunner.class)
    public class SpringJunit 
    {
    	@Autowired
    	private Person person;
    	
    	@Test
    	public void test()
    	{
    		person.update();
    		
    	}
    }
    
全注解方式
  • 得有一个注解类

    package cn.dmdream.springconfig;
    
    import org.springframework.context.annotation.ComponentScan;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.context.annotation.EnableAspectJAutoProxy;
    
    @Configuration // 当前类是注解类
    @ComponentScan(basePackages="cn.dmdream") //<context:component-scan base-package="cn.dmdream"></context:component-scan>
    @EnableAspectJAutoProxy   //<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    public class SpringConfig {
    		
    	// 目前没有别人的类
    }
    
  • 目标类和切面类与上面 半xml半注解方式 相同

  • 测试类

    package cn.dmdream.demo;
    
    import org.junit.Test;
    import org.junit.runner.RunWith;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.test.context.ContextConfiguration;
    import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
    
    import cn.dmdream.domain.Person;
    import cn.dmdream.springconfig.SpringConfig;
    
    //@ContextConfiguration("classpath:applicationContext.xml") // 加载配置文件
    @ContextConfiguration(classes=SpringConfig.class) //加载注解类
    @RunWith(SpringJUnit4ClassRunner.class)
    public class SpringJunit 
    {
    	@Autowired
    	private Person person;
    	
    	@Test
    	public void test()
    	{
    		person.save();
    		
    	}
    }
    
    

四、AOP总结&回顾

Spring的AOP
	 底层:动态代理
	 思想:将一些共性的内容进行抽取 在需要用到的地方以动态代理的形式进行插入
	 在不修改源码的基础上 还能对源码进行前后的增强
	
底层:企业开发不会自己写 因为全都封装好了
				
继承   httpServletRequest request; 是tomcat帮你们创建出来的
    缺点: 需要知道要继承的父类是谁	

装饰者模式
	缺点: 需要有接口  save()   100个方法
	      这个接口下除了要增强的方法以外,别的方法也得实现

动态代理
	jdk的动态代理  save()   100个方法
		  需要有接口 可以指定只增强这个接口下的哪个方法
	
	cglib的动态代理
		 不需要有接口 也可以指定增强方法
		 缺点:代码写的多 

Spring得到Junit测试不需要开启注解扫描器
spting-test.jar spring-aop.jar junit4.jar

AOP的回顾:
	  1 导包
		AOP联盟
		spring-aop.jar
		aspectj.jar
		spring-aspects.jar

	 2 编写
		 1 确定目标类 (里面有切入点 要被增强的方法)

		 2 确定切面类 (里面有通知/增强  有增强方法)

		 3 织入的配置  (把增强方法指定在切入点之前,之后,环绕,异常,最终执行)

Spring声明式事务

一、spring的JdbcTemplate

jdbcTemplate是spring提供的dao层用来和数据库数据交互的技术

回顾dao层操作数据库数据的技术
  • jdbc+c3p0 任何代码要想操作数据库的数据都得遵循jdbc规范
  • dbutils apache组织提供的对jdbc+c3p0的封装
  • hibernate 对jdbc+c3p0的封装
  • jdbctemplate spring对jdbc+c3p0的封装
  • hibernateTemplate spring对hibernate又封装一次
  • mybatis 对jdbc+c3p0的封装
  • SqlMapClientTemplate sping对mybatis又封装一次

执行效率:A mybatis B dbutils C hibernateTemplate

jdbctemplate和dbutils比较
  • dbutils:apache公司

    QueryRunner qr=new QueryRunner();
    qr.setDataSource(连接池);
    String sql="crud";
    
    qr.update();
    qr.query();
    
  • jdbctemplate:Spring公司

    jdbctemplate qr=new jdbctemplate();
    qr.setDataSource(连接池);
    String sql="crud";
    
    qr.update();
    qr.query();
    
jdbctemplate的开发步骤
1.导包
  • spring-jdbc.jar
  • spring-tx.jar
2.配置文件

applicationContext.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: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/context
        http://www.springframework.org/schema/context/spring-context.xsd">

	<!-- spring加载src下的properties文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <!-- c3p0 -->
    <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="c3p0"></property>
	</bean>
	<!-- accountDao -->
	<bean id="accountDao" class="cn.dmdream.daoimpl.AccountDaoImpl">
		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
	</bean>
	<!-- accountService -->
	<bean id="accountService" class="cn.dmdream.serviceImpl.AccountServiceImpl">
		<property name="accountDao" ref="accountDao"></property>
	</bean>
</beans>

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://cloud.dmdream.cn:3306/spring04?characterEncoding=UTF8
jdbc.username=root
jdbc.password=xxxx


#jdbc.driver=com.mysql.jdbc.Driver
#jdbc.url=jdbc:oracle://localhost:3306/hibernate
#jdbc.username=root
#jdbc.password=1234


#jdbc.driver=com.mysql.jdbc.Driver
#jdbc.url=jdbc:db2://localhost:3306/hibernate
#jdbc.username=root
#jdbc.password=1234
3.JdbcTemplat入门代码
package cn.dmdream.jdbctempalte;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;

import com.mchange.v2.c3p0.ComboPooledDataSource;

public class Demo
{
	@Test // 硬编码的方式
	public void test1() throws Exception
	{
		// c3p0
		ComboPooledDataSource ds = new ComboPooledDataSource();  //ioc
		ds.setDriverClass("com.mysql.jdbc.Driver");
		ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring04");
		ds.setUser("root");									// di
		ds.setPassword("1234");
		
		
		JdbcTemplate jdbcTemplate = new JdbcTemplate();   
		jdbcTemplate.setDataSource(ds);					
		String sql="insert into account values(?,?)";
		jdbcTemplate.update(sql, "jack",1000);
		
	}
	
	@Test // ioc+di
	public void test2()
	{
		ApplicationContext context=new ClassPathXmlApplicationContext("applicationContext.xml");
		JdbcTemplate jdbcTemplate =(JdbcTemplate)context.getBean("jdbcTemplate");
		String sql="insert into account values(?,?)";
		jdbcTemplate.update(sql, "rose",1000);
	}
}

4.Service代码略
5.Dao层实现类(完整代码)
package cn.dmdream.daoimpl;

import java.util.List;

import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

import cn.dmdream.dao.AccountDao;
import cn.dmdream.domain.Account;

public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao
{
	/*继承DaoSupport
	 * 
	 *  1 在appliactionContext.xml文件中 使用set方式注入了  但是AccountDaoImpl没有set方法
	 *  2 如果自己里面没有找到 要去父类里面找 setJdbcTempalte() 找到了 注入给父类的jdbcTempalte
	 *  3 父类如果有了 自己子类为什么不能用jdbcTempalte 因为父类private私有化了
	 *  4 但是父类里面有一个getJdbcTempalte 相当于子类里面也有一个 
	 *  5 就可以在子类中使用getJdbcTempalte的方法 获取到注入好的JdbcTempalte
	 * */
	

	@Override
	public void save() {
		String sql = "insert into Account values(?,?)";
		
		this.getJdbcTemplate().update(sql,"周沛3",10000);
		
/*		String sql="insert into account values(?,?)";
		getJdbcTemplate().update(sql,"Tom",100000);*/
	}

	@Override
	public void delete() {
		String sql = "delete from Account where username=?";
		this.getJdbcTemplate().update(sql,"zhoupei");
		
	}

	@Override
	public void update() {
		String sql = "update Account set money=? where username=?";
		this.getJdbcTemplate().update(sql, 1000000,"周沛");
		
	}

	@Override
	public void findAll() {
		String sql = "select * from Account";
		List<Account> query = this.getJdbcTemplate().query(sql,new BeanPropertyRowMapper<Account>(Account.class));
		
		for (Account account : query) {
			System.out.println(account);
		}
		/*String sql="select * from account";
		List<Account> list = getJdbcTemplate().query(sql, new BeanPropertyRowMapper(Account.class));
		for (Account account : list) {
			System.out.println(account);
		}*/
		
	}

	@Override
	public void findByname() {
		String sql = "select * from Account where username = ?";
		Account account = this.getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class),"周沛");
		
		System.out.println(account);
		
		/*String sql="select * from account where username=?";
		Account account= jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class),"jack");
		System.out.println(account);*/
	}

	@Override
	public void findCount() {
		String sql = "select count(*) from Account";
		
		Long long1 = this.getJdbcTemplate().queryForObject(sql, long.class);
		System.out.println(long1);
		
		/*String sql="select count(*) from account";
		Long l = jdbcTemplate.queryForObject(sql, long.class);
		System.out.println(l);*/		
	}

	@Override
	protected void checkDaoConfig() throws IllegalArgumentException {
		// TODO Auto-generated method stub
		
	}

	/*
	 * 
	 * set方式注入--第一种
	 * 
	 * private JdbcTemplate jdbcTemplate;
	
	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
		this.jdbcTemplate = jdbcTemplate;
	}

	@Override
	public void save() {
		
		String sql="insert into account values(?,?)";
		jdbcTemplate.update(sql,"Tom",100000);
	}

	@Override
	public void delete() {
		String sql="delete from account where username=?";
		jdbcTemplate.update(sql,"Tom");
		
	}

	@Override
	public void update() {
		String sql="update account set money=? where username=?";
		jdbcTemplate.update(sql, 999,"rose");
		
	}
	@Override // 自己做实现
	public void findAll() {
		String sql="select * from account";
		List<Account> list = jdbcTemplate.query(sql, new RowMapper<Account>(){
			@Override
			public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
				// 自己实现
				Account account = new Account();
				account.setUsername(rs.getString("username"));
				account.setMoney(rs.getDouble("money"));
				return account;
			}});
		
		for (Account account : list) {
			System.out.println(account);
		}
	}

	@Override
	public void findAll()
	{
		String sql="select * from account";
		List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Account.class));
		for (Account account : list) {
			System.out.println(account);
		}
	}

	@Override
	public void findByname() {
		String sql="select * from account where username=?";
		Account account= jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class),"jack");
		System.out.println(account);
	}

	@Override
	public void findCount() {
		String sql="select count(*) from account";
		Long l = jdbcTemplate.queryForObject(sql, long.class);
		System.out.println(l);
	}*/
}

6.测试类
package cn.dmdream.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.dmdream.service.AccountService;

@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunit 
{
	@Autowired
	private AccountService accountService;
	
	
	@Test
	public void test()
	{
		//accountService.save(); //增
		//accountService.delete(); //删
		//accountService.update(); // 改
		//accountService.findAll(); //全查--list
		//accountService.findByname();//查单个对象
		accountService.findCount(); //查总个数
	}
}

jdbctemplate的2种注入方式

参考类cn.dmdream.daoimpl.AccountDaoImpl

PS:hibernateTempalte也有这2中方式

  • set方式注入

    能使用注解

    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao
    {
    
    	/*
    	 * 
    	 * set方式注入--第一种
    	 * 
    	 * */
        private JdbcTemplate jdbcTemplate;
    	
    	public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
    		this.jdbcTemplate = jdbcTemplate;
    	}
    
    	@Override
    	public void save() {
    		
    		String sql="insert into account values(?,?)";
    		jdbcTemplate.update(sql,"Tom",100000);
    	}
    
    	@Override
    	public void delete() {
    		String sql="delete from account where username=?";
    		jdbcTemplate.update(sql,"Tom");
    		
    	}
    
    	@Override
    	public void update() {
    		String sql="update account set money=? where username=?";
    		jdbcTemplate.update(sql, 999,"rose");
    		
    	}
    	@Override // 自己做实现
    	public void findAll() {
    		String sql="select * from account";
    		List<Account> list = jdbcTemplate.query(sql, new RowMapper<Account>(){
    			@Override
    			public Account mapRow(ResultSet rs, int rowNum) throws SQLException {
    				// 自己实现
    				Account account = new Account();
    				account.setUsername(rs.getString("username"));
    				account.setMoney(rs.getDouble("money"));
    				return account;
    			}});
    		
    		for (Account account : list) {
    			System.out.println(account);
    		}
    	}
    
    	@Override
    	public void findAll()
    	{
    		String sql="select * from account";
    		List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Account.class));
    		for (Account account : list) {
    			System.out.println(account);
    		}
    	}
    
    	@Override
    	public void findByname() {
    		String sql="select * from account where username=?";
    		Account account= jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class),"jack");
    		System.out.println(account);
    	}
    
    	@Override
    	public void findCount() {
    		String sql="select count(*) from account";
    		Long l = jdbcTemplate.queryForObject(sql, long.class);
    		System.out.println(l);
    	}
    }
    
  • 继承的方式

    不能使用注解

    让jdbctemplate继承JdbcDaoSupport,说明

    1. 在appliactionContext.xml文件中 使用set方式注入了 但是AccountDaoImpl没有set方法
    2. 如果自己里面没有找到 要去父类里面找 setJdbcTempalte() 找到了 注入给父类的jdbcTempalte
    3. 父类如果有了 自己子类为什么不能用jdbcTempalte 因为父类private私有化了
    4. 但是父类里面有一个公有的getJdbcTempalte 相当于子类里面也有一个
    5. 就可以在子类中使用getJdbcTempalte的方法 获取到注入好的JdbcTempalte
    public class AccountDaoImpl extends JdbcDaoSupport implements AccountDao
    {
    	/*继承DaoSupport
    	 * 
    	 *  1 在appliactionContext.xml文件中 使用set方式注入了  但是AccountDaoImpl没有set方法
    	 *  2 如果自己里面没有找到 要去父类里面找 setJdbcTempalte() 找到了 注入给父类的jdbcTempalte
    	 *  3 父类如果有了 自己子类为什么不能用jdbcTempalte 因为父类private私有化了
    	 *  4 但是父类里面有一个getJdbcTempalte 相当于子类里面也有一个 
    	 *  5 就可以在子类中使用getJdbcTempalte的方法 获取到注入好的JdbcTempalte
    	 * */
    
    	@Override
    	public void save() {
    		String sql = "insert into Account values(?,?)";
    		this.getJdbcTemplate().update(sql,"周沛3",10000);
    	}
    
    	@Override
    	public void delete() {
    		String sql = "delete from Account where username=?";
    		this.getJdbcTemplate().update(sql,"zhoupei");
    	}
    
    	@Override
    	public void update() {
    		String sql = "update Account set money=? where username=?";
    		this.getJdbcTemplate().update(sql, 1000000,"周沛");
    	}
    
    	@Override
    	public void findAll() {
    		String sql = "select * from Account";
    		List<Account> query = this.getJdbcTemplate().query(sql,new BeanPropertyRowMapper<Account>(Account.class));
    		
    		for (Account account : query) {
    			System.out.println(account);
    		}
    	}
    
    	@Override
    	public void findByname() {
    		String sql = "select * from Account where username = ?";
    		Account account = this.getJdbcTemplate().queryForObject(sql, new BeanPropertyRowMapper<Account>(Account.class),"周沛");
    		System.out.println(account);
    	}
    
    	@Override
    	public void findCount() {
    		String sql = "select count(*) from Account";
    		Long long1 = this.getJdbcTemplate().queryForObject(sql, long.class);
    		System.out.println(long1);
    	}
    
    	@Override
    	protected void checkDaoConfig() throws IllegalArgumentException {
    	}
    }
    
使用jdbctempate对数据库数据进行crud操作一些要点
//查询的得到结果集返回
list List<Account> list = jdbcTemplate.query(sql, new BeanPropertyRowMapper(Account.class));
//单个对象查询 
Account account= jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Account.class),"jack");
//聚合查 
Long l = jdbcTemplate.queryForObject(sql, long.class);

二、回顾Spring导包

  • 环境
    • bean
    • cores
    • context
    • expression
  • 日志
    • commons-logging
    • log4j
  • jdbctemplate
    • spring-jdbc
    • spring-tx
    • mysql-connector(依赖)
    • c3p0(依赖)
  • SpringJunit
    • spring-aop
    • spring-test
    • junit
  • 事务
    • spring-tx

三、spring的声明式事务

事务回顾
  • 事务有什么特性:ACID

    • 原子性:一件完成的事情,要不全部成功 要不全部失败

      转账: 加钱 减钱

    • 一致性:事务的前后,数据总数不发生变化

      jack 1000   rose 1000   2000
      jack 500    rose 1500   2000
      
    • 持久性:只要事务已完成(提交),数据就会到数据库中

    • 隔离性:事务具备隔离性,如果没有隔离性,会发送读取数据的问题

  • 不具备隔离性的话,会发生什么问题

    读取数据的问题

    • 脏读:在一个事务中,读取到了另一个事务还没有提交的数据

      必须杜绝的 PS:所有的数据库都自动避免的脏读

    • (不可)重复读:在一个事务中,2次读取到的数据内容不一致(update)

      可以允许

      一个事务读到了另外一个事务提交的数据 ,造成了前后两次查询结果不一致。

    • 虚读/幻读:在一个事务中,2次读取到的数据内容不一致(insert)

      可以允许

  • 通过调整数据库的 隔离级别 ,避免以上问题的发生

    设置隔离级别

    • read uncommitted(读未提交) 效率最高 安全最低

      引发问题: 脏读

    • read committed(读已提交) oracle

      解决: 脏读 , 引发: 不可重复读

    • repeatable read(可重复读) mysql

      解决: 脏读 、 不可重复读 , 未解决: 幻读

    • serializable(序列化) 安全最高 效率最低

      解决: 脏读、 不可重复读 、 幻读。

  • 事务的编写

    1. 获取连接

    2. 通过连接开启事务

      con.setAutoCommit(false);
      con.commit();
      con.rollback();
      
过渡——使用AOP来操作事务

如何让spring提供的事务方法在指定的save之前执行,在save之后执行?——AOP

开启事务(spring提供的方法) 增强的方法
save() 切入点
提交事务(spring提供的方法) 增强的方法
  • 以前的事务都得自己来编写操作,那spring给我们提供好了一套操作事务的封装,只要拿过来用即可
  • spring给我们提供了2种操作事务的方式
    • API的方式 —硬编码方式
    • 配置文件的方式
API的方式

硬编码方式 可以将提供好的API以代码的方式进行事务的控制 (没人用) 纯编码方式

PlatformTransactionManager

平台事务管理器 spring提供接口 封装事务的方法

  • 提交方法

  • 回滚方法

  • 我们要用只能找这个接口的实现类来用

    1. DataSourceTransactionManager,作用于:dbutils jdbcTempalte connnection

      是切面类 里面有提交方法 回滚方法 (通知/增强)

    2. 相比于 HibernateTransactionManager,作用于: hibernate hibernateTemplate session

      Hibernate版

TransactionDefinition

事务的定义信息对象

如何设定/获取:

  • 事务的隔离级别 (默认获取的就是数据库的隔离级别)

  • 事务的超时时间 (默认 time=-1 永不超时 )

  • 事务的是否只读 (默认 rederOnly=false 可以做任何的crud操作)

  • 事务的传播行为:

    解决的事情: 事务嵌套事务的问题

    应用场景: service:  a() b() 
    a方法有事务 b方法也有事务 在a方法中调用b方法 问:使用谁的事务?
    
    REQUIRED(默认值): 查看当前是否有事务,如果有事务使用当前的事务,如果没事务给当前创建一个事务
    a方法调用b方法:
     b先看看a有没有事务,如果a没有事务,就为创建一个事务,大家一起玩
                如果a有事务,就放弃自己的事务,加入到a的事务中,一起玩
    
    让所有方法都在一个事务中
    
    SUPPORT: 查看当前是否有事务,如果有事务就使用当前的事务,如果没事务就使用非事务
    
    a方法调用b方法:
        b先看看a有没有事务,如果a有事务,加入到a的事务中一起玩
                如果a没有事务,就全部放弃事务,使用非事务执行
    
TransactionStatus

运行状态对象

  • 时时查看事务的运行状态信息

  • 查看当前事务是否完成

  • 查看是否为新的事务

  • 查看是否回滚

    。。。

配置文件/注解的方式

xml方式 (重点) 底层就是API的封装 直接以xml方式告诉给sping即可

xml配置的方式

需求: jack给rose转账

1.导包

  • spring-tx.jar
  • 事务依赖aop
    • AOP联盟
    • spring-aop.jar
    • aspectj.jar
    • spring-aspects.jar

2.编码

  1. 确定目标类 (TranFerServiceImpl --tranfer)

  2. 确定切面类 (用人家Spring提供的 因为spring有一个类,里面都是事务的方法)

    • DataSourceTransactionManager

      • 提交
      • 回滚
      <!-- 切面类 -->
      <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
              <property name="dataSource" ref="c3p0"></property>
      </bean>
      
    • 增强方法:都是关于事务的

    • 细节: 得为配了这些事务增强方法的tanfer配置一些事务参数

      1. 是否只读
      2. 超时时间
      3. 传播行为
      <!-- 配置DataSourceTransactionManager里面事务方法的一些参数
           不写 该方法使用的事务参数都是默认值-->
      <tx:advice transaction-manager="transactionManager" id="txadvice">
          <tx:attributes>
          <!-- 指定方法名称:是业务核心方法 
              read-only:是否是只读事务。默认false,不只读。
              isolation:指定事务的隔离级别。默认值是使用数据库的默认隔离级别。 
              propagation:指定事务的传播行为。
              timeout:指定超时时间。默认值为:-1。永不超时。
              rollback-for:用于指定一个异常,当执行产生该异常时,事务回滚。产生其他异常,事务不回滚。没有默认值,任何异常都回滚。
              no-rollback-for:用于指定一个异常,当产生该异常时,事务不回滚,产生其他异常时,事务回滚。没有默认值,任何异常都回滚。
              -->
              <tx:method name="*" read-only="false" propagation="REQUIRED"/>
              <tx:method name="find*" read-only="true" propagation="SUPPORTS"/>
              <tx:method name="tranfer"/>
          </tx:attributes>
      </tx:advice>
      
  3. 配置织入

    • 专门针对事务的标签

    • 指定那个事务用于那个切入点

      <!-- 织入 -->
      <aop:config>
          <aop:pointcut expression="execution(* cn.dmdream.serviceimpl.TranFerServiceImpl.tranfer(..))" id="pointcut"/>
          <!-- 针对事务的配置标签 -->
          <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut"/>
      </aop:config>
      
  4. 完整配置文件

    <?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:context="http://www.springframework.org/schema/context"
        xmlns:tx="http://www.springframework.org/schema/tx"
        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/context
            http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/tx
            http://www.springframework.org/schema/tx/spring-tx.xsd
            http://www.springframework.org/schema/aop 
            http://www.springframework.org/schema/aop/spring-aop.xsd">
    
    	<!-- spring加载src下的properties文件 -->
    	<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    
        <!-- c3p0 -->
        <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
    		<property name="driverClass" value="${jdbc.driver}"></property>
    		<property name="jdbcUrl" value="${jdbc.url}"></property>
    		<property name="user" value="${jdbc.username}"></property>
    		<property name="password" value="${jdbc.password}"></property>
    	</bean>
    	<!-- jdbcTemplate -->
    	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
    		<property name="dataSource" ref="c3p0"></property>
    	</bean>
    
    	<!-- tranFerDao -->
    	<bean id="tranFerDao" class="cn.dmdream.daoimpl.TranFerDaoImpl">
    		<property name="jdbcTemplate" ref="jdbcTemplate"></property>
    	</bean>
    	<!-- tranFerService 目标类 -->
    	<bean id="tranFerService" class="cn.dmdream.serviceimpl.TranFerServiceImpl">
    		<property name="tranFerDao" ref="tranFerDao"></property>
    	</bean>
    	
    	<!-- 切面类 -->
    	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
    			<property name="dataSource" ref="c3p0"></property>
    	</bean>
    	<!-- 配置DataSourceTransactionManager里面事务方法的一些参数
    		 不写 该方法使用的事务参数都是默认值
    	 -->
    	<tx:advice transaction-manager="transactionManager" id="txadvice">
    			<tx:attributes>
    				<tx:method name="tranfer" />
    			</tx:attributes>
    	</tx:advice>
    	
    	<!-- 织入 -->
    	<aop:config>
    		<aop:pointcut expression="execution(* cn.dmdream.serviceimpl.TranFerServiceImpl.tranfer(..))" id="pointcut"/>
    		<!-- 针对事务的配置标签 -->
    		<aop:advisor advice-ref="txadvice" pointcut-ref="pointcut"/>
    	</aop:config>
    
    </beans>
    
简短总结
导包:
   spring-tx.jar


配置在指定的方法前后用事务 (AOP)

AOP导包:
     AOP联盟
     spring-aop.jar
     spring-aspets.jar
     aspectj.jar


配置:
  1 目标类:tranFerService  里面有切入点 (tranfer)

  2 切面类  DataSourceTransactionManager   里面有事务的增强方法
    细节1:为使用 DataSourceTransactionManager里面的事务方法配置一些事务的参数

  3 配置织入
    细节2: 使用事务单独的标签来配置
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut1"/>
半注解的方式

xml+注解(企业开发)

别人的 用xml 自己的 用注解

  • 开启注解扫描

    <!-- 开启注解扫描器 -->
    <context:component-scan base-package="cn.dmdream"></context:component-scan>
    
  • 事务的注解2步

    1. 指定开启事务的注解 告诉spirng使用的事务方法是谁的方法

      <!-- 开启事务的注解配置 告诉使用的是哪个类下的事务,PlatformTransactionManager是DataSourceTransactionManager的父类 -->
      <tx:annotation-driven transaction-manager="platformTransactionManager"/>
      
    2. 将platformTransactionManager里面的事务方法给目标类的切入点方法增强

      @Transactional//(切入点)在方法上或则是类上配置
      

      比如:给Service中的方法增强

      @Service("tranFerService")
      @Transactional
      public class TranFerServiceImpl implements TranFerService{}
      
  • 以上的两步操作代替了

    1. 事务(增强方法)配置

      <!-- platformTransactionManager是切面类 -->
      <tx:advice transaction-manager="platformTransactionManager" id="txadvice">
          <tx:attributes>
              <!-- 给使用事物的这个方法配置一些事务的参数 -->
              <tx:method name="*" />
          </tx:attributes>
      </tx:advice>
      
    2. 配置织入过程

      <aop:config>
              <!-- 配置方法和切入点 -->
              <aop:pointcut expression="execution(* cn.dmdream.service.TransferServiceImpl.transfer(..))" id="pointcut1"/>
              <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut1" />
      </aop:config>
      
Demo参考

完整配置文件applicationContext.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:context="http://www.springframework.org/schema/context"
    xmlns:tx="http://www.springframework.org/schema/tx"
    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/context
        http://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/tx
        http://www.springframework.org/schema/tx/spring-tx.xsd
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop.xsd">

<!-- 别人的类 -->
<!-- spring加载src下的properties文件 -->
	<context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>

    <!-- c3p0 -->
    <bean id="c3p0" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="user" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	
	<!-- jdbcTemplate -->
	<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
		<property name="dataSource" ref="c3p0"></property>
	</bean>
	
	<!-- 切面类 -->
	<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
			<property name="dataSource" ref="c3p0"></property>
	</bean>

<!-- 自己的类 -->
	
	<!-- 开启注解扫描器 -->
	<context:component-scan base-package="cn.dmdream"></context:component-scan>

	<!-- 开启事务的注解配置 告诉使用的是哪个类下的事务 -->
	<tx:annotation-driven transaction-manager="transactionManager"/>
		
</beans>

jdbc.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://cloud.dmdream.cn:3306/spring04?characterEncoding=UTF8
jdbc.username=root
jdbc.password=xxxx

Dao省略

Service

package cn.dmdream.serviceimpl;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import cn.dmdream.dao.TranFerDao;
import cn.dmdream.service.TranFerService;

@Service("tranFerService")
@Transactional
public class TranFerServiceImpl implements TranFerService
{
	@Autowired
	private TranFerDao tranFerDao;
	
	public void tranfer(String toUser,String inUser,double money) 
	{
		// 减钱
		tranFerDao.toMoney(toUser,money);
		//int i=1/0;
		// 加钱
		tranFerDao.inMoney(inUser,money);
		
	}
	
}

测试类

package cn.dmdream.test;

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;

import cn.dmdream.service.TranFerService;

@ContextConfiguration("classpath:applicationContext.xml")
@RunWith(SpringJUnit4ClassRunner.class)
public class SpringJunit 
{
	@Autowired
	private TranFerService tranFerService;
	@Test
	public void test()
	{
		tranFerService.tranfer("周沛", "周沛3", 10000);
	}
}

全注解的方式

了解即可

条件: 需要注解类

  • @EnableTransactionManagement

    代替了 <tx:annotation-driven transaction-manager="platformTransactionManager"/>

  • 在方法的形参使用注解@Qualifier(“c3p0”)

    public JdbcTemplate createJdbcTemplate(@Qualifier(“c3p0”) DataSource ds) // 使用注解注入对象

注解类完整代码

其它设置和半xml方式一样

SpringConfig.java

package cn.dmdream.springconfig;

import java.beans.PropertyVetoException;

import javax.sql.DataSource;

import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.mchange.v2.c3p0.ComboPooledDataSource;

@Configuration
@ComponentScan(basePackages="cn.dmdream")
@EnableTransactionManagement  //<tx:annotation-driven transaction-manager="transactionManager"/>
public class SpringConfig 
{
	// 创建出来c3p0 给spring
	@Bean(name="c3p0")
	public  DataSource createDataSourceC3p0() throws PropertyVetoException
	{
		ComboPooledDataSource ds = new ComboPooledDataSource();  //ioc
		ds.setDriverClass("com.mysql.jdbc.Driver");
		ds.setJdbcUrl("jdbc:mysql://localhost:3306/spring04");
		ds.setUser("root");									// di
		ds.setPassword("1234");
		return ds;
	}
	
	@Bean(name="jdbcTemplate")
	public  JdbcTemplate createJdbcTemplate(@Qualifier("c3p0") DataSource ds)  // 使用注解问spring要
	{
		JdbcTemplate jdbcTemplate = new JdbcTemplate(); 
		jdbcTemplate.setDataSource(ds);
		return jdbcTemplate;
	}
	
	
	@Bean(name="transactionManager")
	public  DataSourceTransactionManager createDataSourceTransactionManager(@Qualifier("c3p0") DataSource ds)  // 使用注解问spring要
	{
		DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
		dataSourceTransactionManager.setDataSource(ds);
		return dataSourceTransactionManager;
	}
}


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值