Spring文档学习

1.IOC控制反转

IOC理论推导

以前的方法

1、先写一个UserDao接口

public interface UserDao {
   public void getUser();
}

2、再去写Dao的实现类

public class UserDaoImpl implements UserDao {
   @Override
   public void getUser() {
       System.out.println("获取用户数据");
  }
}

3、然后去写UserService的接口

public interface UserService {
   public void getUser();
}

4、最后写Service的实现类

public class UserServiceImpl implements UserService {
   private UserDao userDao = new UserDaoImpl();

   @Override
   public void getUser() {
       userDao.getUser();
  }
}

5、测试一下

@Test
public void test(){
   UserService service = new UserServiceImpl();
   service.getUser();
}

如果想要增加实现类的话,每次都需要在UserServiceImpl中进行修改,耦合性太高

使用set方法注入

public class UserServiceImpl implements UserService {
   private UserDao userDao;
// 利用set实现
   public void setUserDao(UserDao userDao) {
       this.userDao = userDao;
  }

   @Override
   public void getUser() {
       userDao.getUser();
  }
}

进行测试:

@Test
public void test(){
   UserServiceImpl service = new UserServiceImpl();
   service.setUserDao( new UserDaoMySqlImpl() );
   service.getUser();
    
   //那我们现在又想用Oracle去实现呢
   service.setUserDao( new UserDaoOracleImpl() );
   service.getUser();
}

控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法

IOC是什么

**Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。**如何理解好Ioc呢?理解好Ioc的关键是要明确“谁控制谁,控制什么,为何是反转(有反转就应该有正转了)

●**谁控制谁,控制什么:**传统Java SE程序设计,我们直接在对象内部通过new进行创建对象,是程序主动去创建依赖对象;而IoC是有专门一个容器来创建这些对象,即由Ioc容器来控制对象的创建;谁控制谁?当然是IoC 容器控制了对象;控制什么?那就是主要控制了外部资源获取(不只是对象包括比如文件等)

●**为何是反转,哪些方面反转了:**有反转就有正转,传统应用程序是由我们自己在对象中主动控制去直接获取依赖对象,也就是正转;而反转则是由容器来帮忙创建及注入依赖对象;为何是反转?因为由容器帮我们查找及注入依赖对象,对象只是被动的接受依赖对象,所以是反转;哪些方面反转了?依赖对象的获取被反转了

传统方式:

IOC方式:

Spring所倡导的开发方式就是如此,所有的类都会在spring容器中登记,告诉spring你是个什么东西,你需要什么东西,然后spring会在系统运行到适当的时候,把你要的东西主动给你,同时也把你交给其他需要你的东西。所有的类的创建、销毁都由 spring来控制,也就是说控制对象生存周期的不再是引用它的对象,而是spring。对于某个具体的对象而言,以前是它控制其他对象,现在是所有对象都被spring控制,所以这叫控制反转。

IOC能做什么

传统应用程序都是由我们在类内部主动创建依赖对象,从而导致类与类之间高耦合,难于测试;有了IoC容器后,把创建和查找依赖对象的控制权交给了容器,由容器进行注入组合对象,所以对象与对象之间是 松散耦合,这样也方便测试,利于功能复用,更重要的是使得程序的整个体系结构变得非常灵活。

由IoC容器帮对象找相应的依赖对象并注入,而不是由对象主动去找。

IOC和DI

组件之间依赖关系由容器在运行期决定,形象的说,即由容器动态的将某个依赖关系注入到组件之中。**依赖注入的目的并非为软件系统带来更多功能,而是为了提升组件重用的频率,并为系统搭建一个灵活、可扩展的平台。**通过依赖注入机制,我们只需要通过简单的配置,而无需任何代码就可指定目标需要的资源,完成自身的业务逻辑,而不需要关心具体的资源来自何处,由谁实现。

理解DI的关键是:“谁依赖谁,为什么需要依赖,谁注入谁,注入了什么”

谁依赖于谁:当然是应用程序依赖于IoC容器

●**为什么需要依赖:**应用程序需要IoC容器来提供对象需要的外部资源;

谁注入谁:很明显是IoC容器注入应用程序某个对象,应用程序依赖的对象

●注入了什么:就是注入某个对象所需要的外部资源(包括对象、资源、常量数据)

2.HelloSpring

<dependency>
   <groupId>org.springframeworkgroupId>
   <artifactId>spring-webmvcartifactId>
   <version>5.1.13.RELEASEversion>
dependency>

1、编写一个Hello实体类

public class Hello {   private String name;   public String getName() {       return name;  }   public void setName(String name) {       this.name = name;  }   public void show(){       System.out.println("Hello,"+ name );  }}

2、编写我们的spring文件 , 这里我们命名为beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans.xsd">      <bean id="hello" class="pojo.Hello">       <property name="name" value="Spring"/>   bean>beans>

3.进行测试

@Testpublic void test(){   //解析beans.xml文件 , 生成管理相应的Bean对象   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");   //getBean : 参数即为spring配置文件中bean的id .   Hello hello = (Hello) context.getBean("hello");   hello.show();}

3.依赖注入

1.set注入

要求被注入的属性 , 必须有set方法 , set方法的方法名由set + 属性首字母大写 , 如果属性是boolean类型 , 没有set方法 , 是 is

pojo类:

Address.java

public class Address {      private String address;      public String getAddress() {         return address;    }      public void setAddress(String address) {         this.address = address;    } }

Student.java

package pojo;  import java.util.List; import java.util.Map; import java.util.Properties; import java.util.Set;  public class Student {      private String name;     private Address address;     private String[] books;     private List<String> hobbys;     private Map<String,String> card;     private Set<String> games;     private String wife;     private Properties info;      public void setName(String name) {         this.name = name;    }      public void setAddress(Address address) {         this.address = address;    }      public void setBooks(String[] books) {         this.books = books;    }      public void setHobbys(List<String> hobbys) {         this.hobbys = hobbys;    }      public void setCard(Map<String, String> card) {         this.card = card;    }      public void setGames(Set<String> games) {         this.games = games;    }      public void setWife(String wife) {         this.wife = wife;    }      public void setInfo(Properties info) {         this.info = info;    }      public void show(){         System.out.println("name="+ name                 + ",address="+ address.getAddress()                 + ",books="        );         for (String book:books){             System.out.print("<<"+book+">>\t");        }         System.out.println("\n爱好:"+hobbys);          System.out.println("card:"+card);          System.out.println("games:"+games);          System.out.println("wife:"+wife);          System.out.println("info:"+info);     } }

(1)常量注入

<bean id="student" class="pojo.Student">     <property name="name" value="小明"/> bean>

测试:

 @Test public void test01(){     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");      Student student = (Student) context.getBean("student");      System.out.println(student.getName());  }

(2)Bean注入

 <bean id="addr" class="pojo.Address">     <property name="address" value="重庆"/> bean>  <bean id="student" class="pojo.Student">     <property name="name" value="小明"/>          <property name="address" ref="addr"/>  bean>

(3)数组注入

 <bean id="student" class="pojo.Student">     <property name="name" value="小明"/>     <property name="address" ref="addr"/>     <property name="books">         <array>             <value>西游记value>             <value>红楼梦value>             <value>水浒传value>         array>     property> bean>

(4)List注入

 <property name="hobbys">     <list>         <value>听歌value>         <value>看电影value>         <value>爬山value>     list> property>

(5)Map注入

 <property name="card">     <map>         <entry key="中国邮政" value="456456456465456"/>         <entry key="建设" value="1456682255511"/>     map> property>

(6)set注入

 <property name="games">     <set>         <value>LOLvalue>         <value>BOBvalue>         <value>COCvalue>     set> property>

(7)Null注入

 <property name="wife"><null/>property>

(8)Properties注入

 <property name="info">     <props>         <prop key="学号">20190604prop>         <prop key="性别">prop>         <prop key="姓名">小明prop>     props> property>
2.P命名和C命名注入

User.java

public class User {     private String name;     private int age;      public void setName(String name) {         this.name = name;    }      public void setAge(int age) {         this.age = age;    }      @Override     public String toString() {         return "User{" +                 "name='" + name + '\'' +                 ", age=" + age +                 '}';    } }

1、P命名空间注入 : 需要在头文件中加入约束文件

导入约束 : xmlns:p="http://www.springframework.org/schema/p"   <bean id="user" class="pojo.User" p:name="张三" p:age="18"/>

2、c 命名空间注入 : 需要在头文件中加入约束文件(构造器注入)

导入约束 : xmlns:c="http://www.springframework.org/schema/c"  <bean id="user" class="pojo.User" c:name="张三" c:age="18"/>

需要在User.java中加入有参构造

测试:

 @Test public void test02(){     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");     User user = (User) context.getBean("user");     System.out.println(user); }

4.Bean的作用域

在Spring中,那些组成应用程序的主体及由Spring IoC容器所管理的对象,被称之为bean。简单地讲,bean就是由IoC容器初始化、装配及管理的对象。

几种作用域中,request、session作用域仅在基于web的应用中使用(不必关心你所采用的是什么web应用框架),只能用在基于web的Spring ApplicationContext环境。

Singleton

当一个bean的作用域为Singleton,那么Spring IoC容器中只会存在一个共享的bean实例,并且所有对bean的请求,只要id与该bean定义相匹配,则只会返回bean的同一实例。Singleton是单例类型,就是在创建起容器时就同时自动创建了一个bean的对象,不管你是否使用,他都存在了,每次获取到的对象都是同一个对象。注意,Singleton作用域是Spring中的缺省作用域。要在XML中将bean定义成singleton,可以这样配置:

<bean id="ServiceImpl" class="service.ServiceImpl" scope="singleton">

测试:

 @Test public void test03(){     ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");     User user = (User) context.getBean("user");     User user2 = (User) context.getBean("user");     System.out.println(user==user2);
Prototype

当一个bean的作用域为Prototype,表示一个bean定义对应多个对象实例。Prototype作用域的bean会导致在每次对该bean请求(将其注入到另一个bean中,或者以程序的方式调用容器的getBean()方法)时都会创建一个新的bean实例。Prototype是原型类型,它在我们创建容器的时候并没有实例化,而是当我们获取bean的时候才会去创建一个对象,而且我们每次获取到的对象都不是同一个对象。根据经验,对有状态的bean应该使用prototype作用域,而对无状态的bean则应该使用singleton作用域。在XML中将bean定义成prototype,可以这样配置:

 <bean id="account" class="com.foo.DefaultAccount" scope="prototype"/>    或者 <bean id="account" class="com.foo.DefaultAccount" singleton="false"/>
Request

当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,它们依据某个bean定义创建而成。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

 

针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全新的LoginAction bean实例,且该loginAction bean实例仅在当前HTTP request内有效,因此可以根据需要放心的更改所建实例的内部状态,而其他请求中根据loginAction bean定义创建的实例,将不会看到这些特定于某个请求的状态变化。当处理请求结束,request作用域的bean实例将被销毁。

Session

当一个bean的作用域为Session,表示在一个HTTP Session中,一个bean定义对应一个实例。该作用域仅在基于web的Spring ApplicationContext情形下有效。考虑下面bean定义:

 <bean id="userPreferences" class="com.foo.UserPreferences" scope="session"/>

针对某个HTTP Session,Spring容器会根据userPreferences bean定义创建一个全新的userPreferences bean实例,且该userPreferences bean仅在当前HTTP Session内有效。与request作用域一样,可以根据需要放心的更改所创建实例的内部状态,而别的HTTP Session中根据userPreferences创建的实例,将不会看到这些特定于某个HTTP Session的状态变化。当HTTP Session最终被废弃的时候,在该HTTP Session作用域内的bean也会被废弃掉。

5.自动装配

  • 自动装配是使用spring满足bean依赖的一种方法
  • spring会在应用上下文中为某个bean寻找其依赖的bean。

Spring中bean有三种装配机制,分别是:

  1. 在xml中显式配置;
  2. 在java中显式配置;
  3. 隐式的bean发现机制和自动装配。

Spring的自动装配需要从两个角度来实现,或者说是两个操作:

  1. 组件扫描(component scanning):spring会自动发现应用上下文中所创建的bean;
  2. 自动装配(autowiring):spring自动满足bean之间的依赖,也就是我们说的IoC/DI;

组件扫描和自动装配组合发挥巨大威力,使得显式的配置降低到最少。

推荐不使用自动装配xml配置 , 而使用注解 .

测试环境搭建:

1.新建一个项目

2.新建两个实体类,Cat和Dog,且都有一个叫的方法

public class Cat {   public void shout() {       System.out.println("miao~");  }}
public class Dog {   public void shout() {       System.out.println("wang~");  }}

3、新建一个用户类 User

public class User {   private Cat cat;   private Dog dog;   private String people;}

4、编写Spring配置文件

<beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans.xsd">   <bean id="dog" class="pojo.Dog"/>   <bean id="cat" class="pojo.Cat"/>   <bean id="user" class="pojo.User">       <property name="cat" ref="cat"/>       <property name="dog" ref="dog"/>       <property name="people" value="张三"/>   bean>beans>

5、测试

public class MyTest {   @Test   public void testMethodAutowire() {       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");       User user = (User) context.getBean("user");       user.getCat().shout();       user.getDog().shout();  }}
按名称自动装配

由于在手动配置xml过程中,常常发生字母缺漏和大小写等错误,而无法对其进行检查,使得开发效率降低。

采用自动装配将避免这些错误,并且使配置简单化。

测试:

1、修改bean配置,增加一个属性 autowire=“byName”

<bean id="user" class="pojo.User" autowire="byName">   <property name="people" value="qinjiang"/>bean>

2、再次测试,结果依旧成功输出!

3、我们将 cat 的bean id修改为 catXXX

4、再次测试, 执行时报空指针java.lang.NullPointerException。因为按byName规则找不对应set方法,真正的setCat就没执行,对象就没有初始化,所以调用时就会报空指针错误。

小结:

当一个bean节点带有 autowire byName的属性时。

  1. 将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
  2. 去spring容器中寻找是否有此字符串名称id的对象。
  3. 如果有,就取出注入;如果没有,就报空指针异常
按类型自动装配

使用autowire byType首先需要保证:同一类型的对象,在spring容器中唯一。如果不唯一,会报不唯一的异常。【NoUniqueBeanDefinitionException】

测试:

1、将user的bean配置修改一下 : autowire="byType"

2、测试,正常输出

3、在注册一个cat 的bean对象!

<bean id="dog" class="pojo.Dog"/><bean id="cat" class="pojo.Cat"/><bean id="cat2" class="pojo.Cat"/><bean id="user" class="pojo.User" autowire="byType">   <property name="people" value="张三"/>bean>

4、测试,报错:NoUniqueBeanDefinitionException

5、删掉cat2,将cat的bean名称改掉!测试!因为是按类型装配,所以并不会报异常,也不影响最后的结果。甚至将id属性去掉,也不影响结果。

这就是按照类型自动装配!

使用注解

准备工作:

1、在spring配置文件中引入context文件头

xmlns:context="http://www.springframework.org/schema/context"http://www.springframework.org/schema/contexthttp://www.springframework.org/schema/context/spring-context.xsd

2、开启属性注解支持!

<context:annotation-config/>

(1)@Autowired

  • @Autowired是按类型自动转配的,不支持id匹配
  • 需要导入 spring-aop的包!

测试:

1、将User类中的set方法去掉,使用@Autowired注解

public class User {       @Autowired   private Cat cat;   @Autowired   private Dog dog;       private String people;   public Cat getCat() {       return cat;  }   public Dog getDog() {       return dog;  }   public String getStr() {       return str;  }}

2、此时配置文件内容

<context:annotation-config/><bean id="dog" class="pojo.Dog"/><bean id="cat" class="pojo.Cat"/><bean id="user" class="pojo.User"/>

3、测试,成功输出结果!

注:@Autowired(required=false) 说明:false,对象可以为null;true,对象必须存对象,不能为null。

//如果允许对象为null,设置required = false,默认为true@Autowired(required = false)private Cat cat;

(2)@Qualifier

  • @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
  • @Qualifier不能单独使用

测试实验步骤:

1、配置文件修改内容,保证类型存在对象。且名字不为类的默认名字!

<bean id="dog1" class="pojo.Dog"/><bean id="dog2" class="pojo.Dog"/><bean id="cat1" class="pojo.Cat"/><bean id="cat2" class="pojo.Cat"/>

2、没有加Qualifier测试,直接报错

【No qualifying bean of type ‘pojo.Dog’ available: expected single matching bean but found 2: dog1,dog2】

3、在属性上添加Qualifier注解

@Autowired@Qualifier(value = "cat2")private Cat cat;@Autowired@Qualifier(value = "dog2")private Dog dog;

测试,成功输出!

(3)@Resource

  • @Resource如有指定的name属性,先按该属性进行byName方式查找装配;
  • 其次再进行默认的byName方式进行装配;
  • 如果以上都不成功,则按byType的方式自动装配。
  • 都不成功,则报异常。

实体类:

public class User {   //如果允许对象为null,设置required = false,默认为true   @Resource(name = "cat2")   private Cat cat;   @Resource   private Dog dog;   private String people;}

applicationContext.xml

<bean id="dog" class="pojo.Dog"/><bean id="cat1" class="pojo.Cat"/><bean id="cat2" class="pojo.Cat"/><bean id="user" class="pojo.User"/>

测试:结果OK

小结

@Autowired与@Resource异同:

1、@Autowired与@Resource都可以用来装配bean。都可以写在字段上,或写在setter方法上。

2、@Autowired默认按类型装配(属于spring规范),默认情况下必须要求依赖对象必须存在,如果要允许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用

3、@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。

它们的作用相同都是用注解方式注入对象,但执行顺序不同。@Autowired先byType,@Resource先byName

6.使用注解开发

在spring4之后,想要使用注解形式,必须得要引入aop的包

在配置文件当中,还得要引入一个context约束

<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">beans>
Bean的实现

我们之前都是使用 bean 的标签进行bean注入,但是实际开发中,我们一般都会使用注解!

1、配置扫描哪些包下的注解

<context:component-scan base-package="pojo"/>

2、在指定包下编写类,增加注解

@Component("user")// 相当于配置文件中 public class User {   public String name = "张三";}

3、测试

@Testpublic void test(){   ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");   User user = (User) applicationContext.getBean("user");   System.out.println(user.name);}
属性注入

使用注解注入属性

1、可以不用提供set方法,直接在变量名上添加@value(“值”)

@Component("user")// 相当于配置文件中 public class User {   @Value("秦疆")   // 相当于配置文件中    public String name;}

2、如果提供了set方法,在set方法上添加@value(“值”);

@Component("user")public class User {   public String name;   @Value("秦疆")   public void setName(String name) {       this.name = name;  }}
衍生注解

@Component三个衍生注解

为了更好的进行分层,Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层
作用域

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收
@Controller("user")@Scope("prototype")public class User {   @Value("秦疆")   public String name;}
小结

XML与注解比较

  • XML可以适用任何场景 ,结构清晰,维护方便
  • 注解不是自己提供的类使用不了,开发简单方便

xml与注解整合开发 :推荐最佳实践

  • xml管理Bean
  • 注解完成属性注入
  • 使用过程中, 可以不用扫描,扫描是为了类上的注解
<context:annotation-config/>  

作用:

  • 进行注解驱动注册,从而使注解生效
  • 用于激活那些已经在spring容器里注册过的bean上面的注解,也就是显示的向Spring注册
  • 如果不扫描包,就需要手动配置bean
  • 如果不加注解驱动,则注入的值为null!

7.代理模式

AOP的底层机制就是动态代理!

代理模式:

  • 静态代理
  • 动态代理
静态代理

静态代理角色分析

  • 抽象角色 : 一般使用接口或者抽象类来实现
  • 真实角色 : 被代理的角色
  • 代理角色 : 代理真实角色 ; 代理真实角色后 , 一般会做一些附属的操作 .
  • 客户 : 使用代理角色来进行一些操作 .

代码实现

Rent . java 即抽象角色

//抽象角色:租房public interface Rent {   public void rent();}

Host . java 即真实角色

//真实角色: 房东,房东要出租房子public class Host implements Rent{   public void rent() {       System.out.println("房屋出租");  }}

Proxy . java 即代理角色

//代理角色:中介public class Proxy implements Rent {   private Host host;   public Proxy() { }   public Proxy(Host host) {       this.host = host;  }   //租房   public void rent(){       seeHouse();       host.rent();       fare();  }   //看房   public void seeHouse(){       System.out.println("带房客看房");  }   //收中介费   public void fare(){       System.out.println("收中介费");  }}

Client . java 即客户

//客户类,一般客户都会去找代理!public class Client {   public static void main(String[] args) {       //房东要租房       Host host = new Host();       //中介帮助房东       Proxy proxy = new Proxy(host);       //你去找中介!       proxy.rent();  }}

静态代理的好处:

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .

我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

静态代理再理解

1、创建一个抽象角色,比如咋们平时做的用户业务,抽象起来就是增删改查!

//抽象角色:增删改查业务public interface UserService {   void add();   void delete();   void update();   void query();}

2、我们需要一个真实对象来完成这些增删改查操作

//真实对象,完成增删改查操作的人public class UserServiceImpl implements UserService {   public void add() {       System.out.println("增加了一个用户");  }   public void delete() {       System.out.println("删除了一个用户");  }   public void update() {       System.out.println("更新了一个用户");  }   public void query() {       System.out.println("查询了一个用户");  }}

3、需求来了,现在我们需要增加一个日志功能,怎么实现!

  • 思路1 :在实现类上增加代码 【麻烦!】
  • 思路2:使用代理来做,能够不改变原来的业务情况下,实现此功能就是最好的了!

4、设置一个代理类来处理日志!代理角色

//代理角色,在这里面增加日志的实现public class UserServiceProxy implements UserService {   private UserServiceImpl userService;   public void setUserService(UserServiceImpl userService) {       this.userService = userService;  }   public void add() {       log("add");       userService.add();  }   public void delete() {       log("delete");       userService.delete();  }   public void update() {       log("update");       userService.update();  }   public void query() {       log("query");       userService.query();  }   public void log(String msg){       System.out.println("执行了"+msg+"方法");  }}

5、测试访问类:

public class Client {   public static void main(String[] args) {       //真实业务       UserServiceImpl userService = new UserServiceImpl();       //代理类       UserServiceProxy proxy = new UserServiceProxy();       //使用代理类实现日志功能!       proxy.setUserService(userService);       proxy.add();  }}
动态代理
  • 动态代理的角色和静态代理的一样 .

  • 动态代理的代理类是动态生成的 . 静态代理的代理类是我们提前写好的

  • 动态代理分为两类 : 一类是基于接口动态代理 , 一类是基于类的动态代理

    • 基于接口的动态代理----JDK动态代理
    • 基于类的动态代理–cglib
    • 现在用的比较多的是 javasist 来生成动态代理 . 百度一下javasist
    • 我们这里使用JDK的原生代码来实现,其余的道理都是一样的!、

JDK的动态代理需要了解两个类

核心 : InvocationHandler 和 Proxy

(1)【InvocationHandler:调用处理程序】

Object invoke(Object proxy, 方法 method, Object[] args)//参数//proxy - 调用该方法的代理实例//method -所述方法对应于调用代理实例上的接口方法的实例。方法对象的声明类将是该方法声明的接口,它可以是代理类继承该方法的代理接口的超级接口。//args -包含的方法调用传递代理实例的参数值的对象的阵列,或null如果接口方法没有参数。原始类型的参数包含在适当的原始包装器类的实例中,例如java.lang.Integer或java.lang.Boolean 。

(2)【Proxy : 代理】

//生成代理类public Object getProxy(){   return Proxy.newProxyInstance(this.getClass().getClassLoader(),                                 rent.getClass().getInterfaces(),this);}

代码实现

Rent . java 即抽象角色

//抽象角色:租房public interface Rent {   public void rent();}

Host . java 即真实角色

//真实角色: 房东,房东要出租房子public class Host implements Rent{   public void rent() {       System.out.println("房屋出租");  }}

ProxyInvocationHandler. java 即代理角色

public class ProxyInvocationHandler implements InvocationHandler {   private Rent rent;   public void setRent(Rent rent) {       this.rent = rent;  }   //生成代理类,重点是第二个参数,获取要代理的抽象角色!之前都是一个角色,现在可以代理一类角色   public Object getProxy(){       return Proxy.newProxyInstance(this.getClass().getClassLoader(),               rent.getClass().getInterfaces(),this);  }   // proxy : 代理类 method : 代理类的调用处理程序的方法对象.   // 处理代理实例上的方法调用并返回结果   @Override   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {       seeHouse();       //核心:本质利用反射实现!       Object result = method.invoke(rent, args);       fare();       return result;  }   //看房   public void seeHouse(){       System.out.println("带房客看房");  }   //收中介费   public void fare(){       System.out.println("收中介费");  }}

Client . java

//租客public class Client {   public static void main(String[] args) {       //真实角色       Host host = new Host();       //代理实例的调用处理程序       ProxyInvocationHandler pih = new ProxyInvocationHandler();       pih.setRent(host); //将真实角色放置进去!       Rent proxy = (Rent)pih.getProxy(); //动态生成对应的代理类!       proxy.rent();  }}

核心:一个动态代理 , 一般代理某一类业务 , 一个动态代理可以代理多个类,代理的是接口!

小结

静态代理有的它都有,静态代理没有的,它也有!

  • 可以使得我们的真实角色更加纯粹 . 不再去关注一些公共的事情 .
  • 公共的业务由代理来完成 . 实现了业务的分工 ,
  • 公共业务发生扩展时变得更加集中和方便 .
  • 一个动态代理 , 一般代理某一类业务
  • 一个动态代理可以代理多个类,代理的是接口

8.AOP

AOP简介

AOP是能够让我们在不影响原有功能的前提下,为软件横向扩展功能。 那么横向扩展怎么理解呢,我们在WEB项目开发中,通常都遵守三层原则,包括控制层(Controller)->业务层(Service)->数据层(dao),那么从这个结构下来的为纵向,它具体的某一层就是我们所说的横向。我们的AOP就是可以作用于这某一个横向模块当中的所有方法。

Spring中AOP代理由Spring的IOC容器负责生成、管理,其依赖关系也由IOC容器负责管理。因此,AOP代理可以直接使用容器中的其它bean实例作为目标,这种关系可由IOC容器的依赖注入提供。Spring创建代理的规则为:

1、默认使用Java动态代理来创建AOP代理,这样就可以为任何接口实例创建代理了

2、当需要代理的类不是代理接口的时候,Spring会切换为使用CGLIB代理,也可强制使用CGLIB

AOP编程其实是很简单的事情,纵观AOP编程,程序员只需要参与三个部分:

1、定义普通业务组件

2、定义切入点,一个切入点可能横切多个业务组件

3、定义增强处理,增强处理就是在AOP框架为普通业务组件织入的处理动作

  • 切面:拦截器类,其中会定义切点以及通知
  • 切点:具体拦截的某个业务点。
  • 通知:切面当中的方法,声明通知方法在目标业务层的执行位置,通知类型如下:
    • 前置通知:@Before 在目标业务方法执行之前执行
    • 后置通知:@After 在目标业务方法执行之后执行
    • 返回通知:@AfterReturning 在目标业务方法返回结果之后执行
    • 异常通知:@AfterThrowing 在目标业务方法抛出异常之后
    • 环绕通知:@Around 功能强大,可代替以上四种通知,还可以控制目标业务方法是否执行以及何时执行

所以进行AOP编程的关键就是定义切入点和定义增强处理,一旦定义了合适的切入点和增强处理,AOP框架将自动生成AOP代理,即:代理对象的方法=增强处理+被代理对象的方法。

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

Aop在spring中的作用

提供声明式事务;允许用户自定义切面

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。

即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

Spring实现Aop

【重点】使用AOP织入,需要导入一个依赖包!

<dependency>   <groupId>org.aspectjgroupId>   <artifactId>aspectjweaverartifactId>   <version>1.9.4version>dependency>
(1)通过SpringAPI实现

首先编写我们的业务接口和实现类

public interface UserService {   public void add();   public void delete();   public void update();   public void search();}
public class UserServiceImpl implements UserService{   @Override   public void add() {       System.out.println("增加用户");  }   @Override   public void delete() {       System.out.println("删除用户");  }   @Override   public void update() {       System.out.println("更新用户");  }   @Override   public void search() {       System.out.println("查询用户");  }}

然后去写我们的增强类 , 我们编写两个 , 一个前置增强 一个后置增强

public class Log implements MethodBeforeAdvice {   //method : 要执行的目标对象的方法   //objects : 被调用的方法的参数   //Object : 目标对象   @Override   public void before(Method method, Object[] objects, Object o) throws Throwable {       System.out.println( o.getClass().getName() + "的" + method.getName() + "方法被执行了");  }}
public class AfterLog implements AfterReturningAdvice {   //returnValue 返回值   //method被调用的方法   //args 被调用的方法的对象的参数   //target 被调用的目标对象   @Override   public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {       System.out.println("执行了" + target.getClass().getName()       +"的"+method.getName()+"方法,"       +"返回值:"+returnValue);  }}

最后去spring的文件中注册 , 并实现aop切入实现 , 注意导入约束 .

<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"      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">      <bean id="userService" class="service.UserServiceImpl"/>   <bean id="log" class="log.Log"/>   <bean id="afterLog" class="log.AfterLog"/>      <aop:config>              <aop:pointcut id="pointcut" expression="execution(* service.UserServiceImpl.*(..))"/>              <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>       <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>   aop:config>beans>

测试

public class MyTest {   @Test   public void test(){       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");       UserService userService = (UserService) context.getBean("userService");       userService.search();  }}
(2)自定义类实现

目标业务类不变依旧是userServiceImpl

第一步 : 写我们自己的一个切入类

public class DiyPointcut {   public void before(){       System.out.println("---------方法执行前---------");  }   public void after(){       System.out.println("---------方法执行后---------");  }   }

去spring中配置

<bean id="diy" class="config.DiyPointcut"/><aop:config>      <aop:aspect ref="diy">       <aop:pointcut id="diyPonitcut" expression="execution(* service.UserServiceImpl.*(..))"/>       <aop:before pointcut-ref="diyPonitcut" method="before"/>       <aop:after pointcut-ref="diyPonitcut" method="after"/>   aop:aspect>aop:config>

测试:

public class MyTest {   @Test   public void test(){       ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");       UserService userService = (UserService) context.getBean("userService");       userService.add();  }}
(3)使用注解实现

第一步:编写一个注解实现的增强类

package config;import org.aspectj.lang.ProceedingJoinPoint;import org.aspectj.lang.annotation.After;import org.aspectj.lang.annotation.Around;import org.aspectj.lang.annotation.Aspect;import org.aspectj.lang.annotation.Before;@Aspectpublic class AnnotationPointcut {   @Before("execution(* service.UserServiceImpl.*(..))")   public void before(){       System.out.println("---------方法执行前---------");  }   @After("execution(* service.UserServiceImpl.*(..))")   public void after(){       System.out.println("---------方法执行后---------");  }   @Around("execution(* service.UserServiceImpl.*(..))")   public void around(ProceedingJoinPoint jp) throws Throwable {       System.out.println("环绕前");       System.out.println("签名:"+jp.getSignature());       //执行目标方法proceed       Object proceed = jp.proceed();       System.out.println("环绕后");       System.out.println(proceed);  }}

第二步:在Spring配置文件中,注册bean,并增加支持注解的配置

<bean id="annotationPointcut" class="config.AnnotationPointcut"/><aop:aspectj-autoproxy/>

aop:aspectj-autoproxy:说明

通过aop命名空间的声明自动为spring容器中那些配置@aspectJ切面的bean创建代理,织入切面。当然,spring 在内部依旧采用AnnotationAwareAspectJAutoProxyCreator进行自动代理的创建工作,但具体实现的细节已经被隐藏起来了有一个proxy-target-class属性,默认为false,表示使用jdk动态代理织入增强,当配为时,表示使用CGLib动态代理技术织入增强。不过即使proxy-target-class设置为false,如果目标类没有声明接口,则spring将自动使用CGLib动态代理。

9.整合Mybatis

导入jar包

junit

<dependency>   <groupId>junitgroupId>   <artifactId>junitartifactId>   <version>4.12version>dependency>

mybatis

<dependency>   <groupId>org.mybatisgroupId>   <artifactId>mybatisartifactId>   <version>3.5.2version>dependency>

mysql-connector-java

<dependency>   <groupId>mysqlgroupId>   <artifactId>mysql-connector-javaartifactId>   <version>5.1.47version>dependency>

spring相关

<dependency>   <groupId>org.springframeworkgroupId>   <artifactId>spring-webmvcartifactId>   <version>5.1.10.RELEASEversion>dependency><dependency>   <groupId>org.springframeworkgroupId>   <artifactId>spring-jdbcartifactId>   <version>5.1.10.RELEASEversion>dependency>

aspectJ AOP 织入器

<dependency>   <groupId>org.aspectjgroupId>   <artifactId>aspectjweaverartifactId>   <version>1.9.4version>dependency>

mybatis-spring整合包 【重点】

<dependency>   <groupId>org.mybatisgroupId>   <artifactId>mybatis-springartifactId>   <version>2.0.2version>dependency>

配置Maven静态资源过滤问题!

<build>   <resources>       <resource>           <directory>src/main/javadirectory>           <includes>               <include>**/*.propertiesinclude>               <include>**/*.xmlinclude>           includes>           <filtering>truefiltering>       resource>   resources>build>

要和 Spring 一起使用 MyBatis,需要在 Spring 应用上下文中定义至少两样东西:一个 SqlSessionFactory 和至少一个数据映射器类

在 MyBatis-Spring 中,可使用SqlSessionFactoryBean来创建 SqlSessionFactory。要配置这个工厂 bean,只需要把下面代码放在 Spring 的 XML 配置文件中:

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource" />bean>

注意:SqlSessionFactory需要一个 DataSource(数据源)。这可以是任意的 DataSource,只需要和配置其它 Spring 数据库连接一样配置它就可以了。

在基础的 MyBatis 用法中,是通过 SqlSessionFactoryBuilder 来创建 SqlSessionFactory 的。而在 MyBatis-Spring 中,则使用 SqlSessionFactoryBean 来创建。

在 MyBatis 中,你可以使用 SqlSessionFactory 来创建 SqlSession。一旦你获得一个 session 之后,你可以使用它来执行映射了的语句,提交或回滚连接,最后,当不再需要它的时候,你可以关闭 session。

SqlSessionFactory有一个唯一的必要属性:用于 JDBC 的 DataSource。这可以是任意的 DataSource 对象,它的配置方法和其它 Spring 数据库连接是一样的。

一个常用的属性是 configLocation,它用来指定 MyBatis 的 XML 配置文件路径(mybatis_config.xml)。它在需要修改 MyBatis 的基础配置非常有用。通常,基础配置指的是 < settings> 或 < typeAliases>元素。

需要注意的是,这个配置文件并不需要是一个完整的 MyBatis 配置。确切地说,任何环境配置(),数据源()和 MyBatis 的事务管理器()都会被忽略。SqlSessionFactoryBean 会创建它自有的 MyBatis 环境配置(Environment),并按要求设置自定义环境的值

SqlSessionTemplate 是 MyBatis-Spring 的核心。作为 SqlSession 的一个实现,这意味着可以使用它无缝代替你代码中已经在使用的 SqlSession

模板可以参与到 Spring 的事务管理中,并且由于其是线程安全的,可以供多个映射器类使用,你应该总是用 SqlSessionTemplate 来替换 MyBatis 默认的 DefaultSqlSession 实现。在同一应用程序中的不同类之间混杂使用可能会引起数据一致性的问题

可以使用 SqlSessionFactory 作为构造方法的参数来创建 SqlSessionTemplate 对象。

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate"> <constructor-arg index="0" ref="sqlSessionFactory" />bean>

现在,这个 bean 就可以直接注入到你的 DAO bean 中了。你需要在你的 bean 中添加一个 SqlSession 属性,就像下面这样:

public class UserDaoImpl implements UserDao {  //注入sqlSession private SqlSession sqlSession; public void setSqlSession(SqlSession sqlSession) {   this.sqlSession = sqlSession;} public User getUser(String userId) {   return sqlSession.getMapper...;}}

按下面这样,注入 SqlSessionTemplate:

<bean id="userDao" class="org.mybatis.spring.sample.dao.UserDaoImpl"> <property name="sqlSession" ref="sqlSession" />bean>
整合实现一

1、引入Spring配置文件beans.xml

<beans xmlns="http://www.springframework.org/schema/beans"      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"      xsi:schemaLocation="http://www.springframework.org/schema/beans       http://www.springframework.org/schema/beans/spring-beans.xsd">

2、配置数据源替换mybaits的数据源

<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">   <property name="driverClassName" value="com.mysql.jdbc.Driver"/>   <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&useUnicode=true&characterEncoding=utf8"/>   <property name="username" value="root"/>   <property name="password" value="123456"/>bean>

3、配置SqlSessionFactory,关联MyBatis

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">   <property name="dataSource" ref="dataSource"/>      <property name="configLocation" value="classpath:mybatis-config.xml"/>   <property name="mapperLocations" value="classpath:dao/*.xml"/>bean>

4、注册sqlSessionTemplate,关联sqlSessionFactory

<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">      <constructor-arg index="0" ref="sqlSessionFactory"/>bean>

5、增加Dao接口的实现类;私有化sqlSessionTemplate

public class UserDaoImpl implements UserMapper {   //sqlSession不用我们自己创建了,Spring来管理   private SqlSessionTemplate sqlSession;   public void setSqlSession(SqlSessionTemplate sqlSession) {       this.sqlSession = sqlSession;  }   public List selectUser() {       UserMapper mapper = sqlSession.getMapper(UserMapper.class);       return mapper.selectUser();  }   }

6、注册bean实现

<bean id="userDao" class="dao.UserDaoImpl">   <property name="sqlSession" ref="sqlSession"/>bean>

7、测试

@Testpublic void test2(){ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");UserMapper mapper = (UserMapper) context.getBean("userDao");List<User> user = mapper.selectUser();System.out.println(user);}

结果成功输出!现在我们的Mybatis配置文件的状态!发现都可以被Spring整合!

DOCTYPE configuration       PUBLIC "-//mybatis.org//DTD Config 3.0//EN"       "http://mybatis.org/dtd/mybatis-3-config.dtd"><configuration>   <typeAliases>       <package name="pojo"/>   typeAliases>configuration>
整合实现二

dao继承Support类 , 直接利用 getSqlSession() 获得 , 然后直接注入SqlSessionFactory . 比起方式1 , 不需要管理SqlSessionTemplate , 而且对事务的支持更加友好 . 可跟踪源码查看

Spring文档学习_第1张图片

1、将我们上面写的UserDaoImpl修改一下

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {   public List<User> selectUser() {       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);       return mapper.selectUser();  }}

2、修改bean的配置

<bean id="userDao" class="dao.UserDaoImpl">   <property name="sqlSessionFactory" ref="sqlSessionFactory" />bean>

3、测试

@Testpublic void test2(){   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");   UserMapper mapper = (UserMapper) context.getBean("userDao");   List<User> user = mapper.selectUser();   System.out.println(user);}

声明式事务

事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

  1. 原子性(atomicity)

    • 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
  2. 一致性(consistency)

    • 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
  3. 隔离性(isolation)

    • 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
  4. 持久性(durability)

    • 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

代码测试

在之前的案例中,我们给userDao接口新增两个方法,删除和增加用户;

//添加一个用户int addUser(User user);//根据id删除用户int deleteUser(int id);

mapper文件,我们故意把 deletes 写错,测试

<insert id="addUser" parameterType="com.kuang.pojo.User">insert into user (id,name,pwd) values (#{id},#{name},#{pwd})insert><delete id="deleteUser" parameterType="int">deletes from user where id = #{id}delete>

编写接口的实现类,在实现类中,我们去操作一波

public class UserDaoImpl extends SqlSessionDaoSupport implements UserMapper {   //增加一些操作   public List selectUser() {       User user = new User(4,"小明","123456");       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);       mapper.addUser(user);       mapper.deleteUser(4);       return mapper.selectUser();  }   //新增   public int addUser(User user) {       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);       return mapper.addUser(user);  }   //删除   public int deleteUser(int id) {       UserMapper mapper = getSqlSession().getMapper(UserMapper.class);       return mapper.deleteUser(id);  }}

测试:

@Testpublic void test2(){   ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");   UserMapper mapper = (UserMapper) context.getBean("userDao");   List<User> user = mapper.selectUser();   System.out.println(user);}

报错:sql异常,delete写错了

结果 :插入成功!

没有进行事务的管理;我们想让他们都成功才成功,有一个失败,就都失败,我们就应该需要事务!

Spring中的事务管理

Spring在不同的事务管理API之上定义了一个抽象层,使得开发人员不必了解底层的事务管理API就可以使用Spring的事务管理机制。Spring支持编程式事务管理声明式的事务管理

编程式事务管理

  • 将事务管理代码嵌到业务方法中来控制事务的提交和回滚
  • 缺点:必须在每个事务操作业务逻辑中包含额外的事务管理代码

声明式事务管理

  • 一般情况下比编程式事务好用。
  • 将事务管理代码从业务方法中分离出来,以声明的方式来实现事务管理。
  • 将事务管理作为横切关注点,通过aop方法模块化。Spring中通过Spring AOP框架支持声明式事务管理。

使用Spring管理事务,注意头文件的约束导入 : tx

xmlns:tx="http://www.springframework.org/schema/tx"http://www.springframework.org/schema/txhttp://www.springframework.org/schema/tx/spring-tx.xsd">

事务管理器

  • 无论使用Spring的哪种事务管理策略(编程式或者声明式)事务管理器都是必须的。
  • 就是 Spring的核心事务管理抽象,管理封装了一组独立于技术的方法。

JDBC事务

<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">       <property name="dataSource" ref="dataSource" />bean>

配置好事务管理器后我们需要去配置事务的通知

<tx:advice id="txAdvice" transaction-manager="transactionManager">   <tx:attributes>              <tx:method name="add" propagation="REQUIRED"/>       <tx:method name="delete" propagation="REQUIRED"/>       <tx:method name="update" propagation="REQUIRED"/>       <tx:method name="search*" propagation="REQUIRED"/>       <tx:method name="get" read-only="true"/>       <tx:method name="*" propagation="REQUIRED"/>   tx:attributes>tx:advice>

spring事务传播特性:

事务传播行为就是多个事务方法相互调用时,事务如何在这些方法间传播。spring支持7种事务传播行为:

  • propagation_requierd:如果当前没有事务,就新建一个事务,如果已存在一个事务中,加入到这个事务中,这是最常见的选择。
  • propagation_supports:支持当前事务,如果没有当前事务,就以非事务方法执行。
  • propagation_mandatory:使用当前事务,如果没有当前事务,就抛出异常。
  • propagation_required_new:新建事务,如果当前存在事务,把当前事务挂起。
  • propagation_not_supported:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。
  • propagation_never:以非事务方式执行操作,如果当前事务存在则抛出异常。
  • propagation_nested:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则执行与propagation_required类似的操作

Spring 默认的事务传播行为是 PROPAGATION_REQUIRED,它适合于绝大多数的情况。

假设 ServiveX#methodX() 都工作在事务环境下(即都被 Spring 事务增强了),假设程序中存在如下的调用链:Service1#method1()->Service2#method2()->Service3#method3(),那么这 3 个服务类的 3 个方法通过 Spring 的事务传播机制都工作在同一个事务中。

就好比,我们刚才的几个方法存在调用,所以会被放在一组事务当中!

配置AOP

导入aop的头文件!

<aop:config>   <aop:pointcut id="txPointcut" expression="execution(* com.kuang.dao.*.*(..))"/>   <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"/>aop:config>

你可能感兴趣的:(新手,java,spring,java,后端)