Spring

Spring

1.简介

spring理念:使现有的技术更加容易使用,本身是一个大杂烩,整合了现有的技术框架

导入依赖

 <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.10.RELEASE</version>
        </dependency>
    </dependencies>

优点

  • Spring是一个开源免费的框架(容器)!
  • Spring是一个轻量级的、非入侵式的框架!
  • 控制反转(IOC),面向切面编程(AOP)!
  • 支持事务的处理,对框架整合的支持!

2.IOC

  • Ioc本质
    控制反转IOC(Inversion of Control),是一种设计思想,
    DI(依赖注入)是实现IOC的一种方法
    ,也有人认为DI只是IOC的另一种说法.
    没有Ioc的程序中,我们使用面向对象编程,对象的创建与对象间的依赖关系
    完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建
    转移给第三方,所谓控制反转就是:获得依赖对象的方式反转了.

  • 采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用
    注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在
    实现类中,从而达到零配置的目的.

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定
对象的方式.在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入(Dependency Injection,DI).

3.HelloSpring

1.导入Spring相关jar包

2.编写相关代码

2.1编写一个Hello实体类

public class Hello {
    private String str;

    public String getStr() {
        return str;
    }

    public void setStr(String str) {
        this.str = str;
    }

    @Override
    public String toString() {
        return "Hello{" +
                "str='" + str + '\'' +
                '}';
    }
}


2.2、编写我们的spring文件 , 这里我们命名为beans.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"
      xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd">

   <!--bean就是java对象 , 由Spring创建和管理-->
   <bean id="hello" class="com.prq.pojo.Hello">
       <property name="name" value="Spring"/>
   </bean>

</beans>

3、我们可以去进行测试了 .

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

4.Spring配置

    <!--导入-->
    <import resource="beans1.xml"/>
     <!--第一种,下标赋值!-->
    <bean id="user" class="com.prq.pojo.User">
      <constructor-arg index="0" value="狂神说java"/>
    </bean>

    <!--第二种方式:通过类型创建,不建议使用!-->
    <bean id="user" class="com.prq.pojo.User">
        <constructor-arg type="java.lang.String" value="qinjiang"/>
    </bean>

    <!--第三种,直接通过参数名来设置-->
    <bean id="user" class="com.prq.pojo.User">
        <constructor-arg name="name" value="秦疆"/>
    </bean>

    <!--别名,如果添加了别名,我们也可以使用别名获取到这个对象-->
      <alias name="user" alias="userNew"/>

    <!--
    id : bean的唯一标识符,也就是相当于我们学的对象名
    class : bean 对象所对应的全限定名 : 报名 + 类名
    name : 也是别名而且name 可以同时取多个别名
    -->
    <bean id="userT" class="com.prq.pojo.UserT" name="user2 u2,u3;u4;u5,u">
        <property name="name" value="西部开源"/>
    </bean>

5.DI注入

 <!--第一种,普通值注入,value-->
        <property name="name" value="秦疆"/>
        <!--第二种,bean注入,ref-->
        <property name="address" ref="address"/>
        <!--数组注入-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>水浒传</value>
                <value>三国演义</value>
            </array>
        </property>
        <!--List注入-->
        <property name="hobbys">
            <list>
                <value>听歌</value>
                <value>敲代码</value>
                <value>看电影</value>
            </list>
        </property>
        <!--Map注入-->
        <property name="card">
            <map>
                <entry key="身份证" value="111111222222223333"/>
                <entry key="银行卡" value="1321223112232123231"/>
            </map>
        </property>
        <!--Set注入-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>COC</value>
                <value>BOB</value>
            </set>
        </property>
        <!--null注入-->
        <property name="wife">
            <null/>
        </property>
        <!--Properties注入
         key=value
         key=value
         key=value
        -->
        <property name="info">
            <props>
                <prop key="driver">20190525</prop>
                <prop key="url"></prop>
                <prop key="username">root</prop>
                <prop key="password">123456</prop>
            </props>
        </property>

p命名空间注入 c命名空间注入

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

    <!--p命名空间注入,可以直接注入属性的值:property-->
    <bean id="user" class="com.prq.pojo.User" p:name="秦疆" p:age="18"/>

    <!--c命名空间注入,通过构造器注入:construct-args-->
    <bean id="user2" class="com.prq.pojo.User" c:age="18" c:name="狂神" scope="prototype"/>
    <!--
    单例模式(Spring默认机制)
    原型模式:每次从容器中get的时候都会产生一个新对象!
    -->

Bean的作用域

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

Singleton

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

Prototype

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

Request

  • 当一个bean的作用域为Request,表示在一次HTTP请求中,一个bean定义对应一个实例;即每个HTTP请求都会有各自的bean实例,
    它们依据某个bean定义创建而成。该作用域仅在基于
    web的Spring ApplicationContext情形下有效
  • 针对每次HTTP请求,Spring容器会根据loginAction bean的定义创建一个全
    新的LoginAction bean实例,且该loginAction bean
    实例仅在当前HTTP request内有效,因此可以根据需要放心
    的更改所建实例的内部状态,而其他请求中根据loginAction
    bean定义创建的实例,将不会看到这些特定于某个请求的
    状态变化。当处理请求结束,request作用域的bean实例将
    被销毁。

Session

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

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

  • 自动装配是使用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;
      组件扫描和自动装配组合发挥巨大威力,使得显示的配置降低到最少。
      
      当一个bean节点带有 autowire byName的属性时。
      1.将查找其类中所有的set方法名,例如setCat,获得将set去掉并且首字母小写的字符串,即cat。
      2.去spring容器中寻找是否有此字符串名称id的对象。
      3.如果有,就取出注入;如果没有,就报空指针异常。
    

使用注解

 <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
        https://www.springframework.org/schema/context/spring-context.xsd">
    <!--开启注解的支持-->
        <context:annotation-config/>
    <!--
    byName:会自动在容器上下文中查找,
    和自己对象set方法后面的值对应的beanid!
    byType会自动在容器上下文中查找,
    和自己对象属性类型相同的bean!
    -->
    
    <!--
        byName的时候,需要保证所有bean的id唯一,
        并且这个bean需要和自动注入的属性的set方法一致!
        byType的时候,需要保证所有bean的class唯一,
        并且这个bean需要和自动注入的属性的类型一致!
    -->

@Autowired

直接在属性上使用即可!也可以在set方式上使用
使用Autowired我们就可以不用编写set方法了,
前提是你这个自动装配的属性在IOC(Spring)容器中存在,
且符合符合名字byname!

如果显示定义了Autowired的属性为false,
说明这个对象可以为null,否则不允许为空

如果@Autowired自动装配的环境比较复杂,
自动装配无法通过一个注解[@Autowired]完成的时候、
我们可以使用@Qualifier(value="xxx")去配置
@Autowired的使用,指定一个唯一的bean对象注入!

注解说明

  • @Autowired:自动装配通过类型、名字
    如果Autowired不能唯一自动装配上属性,则需要通过@Qualifier(value = “xxx”)
  • @Nullable
    字段标记了这个注解,说明这个字段可以为null
  • @Resource :自动装配通过名字。类型
  • @Component : 组件,放在类上,说明这个类被Spring管理了,就是bean!
  • @Qualifier : @Autowired是根据类型自动装配的,加上@Qualifier则可以根据byName的方式自动装配
    @Qualifier不能单独使用。

@Resource和@Autowired的区别

  • 1.都是用来自动装配的,都可以放在属性字段上,或者set方法上
  • 2.@Autowired默认按类型装配(属于spring规范)
    ,默认情况下必须要求依赖对象必须存在,如果要允
    许null 值,可以设置它的required属性为false,如:@Autowired(required=false) ,如果我们想使用名称装配可以结合@Qualifier注解进行使用
  • 3.@Autowired通过byType的方式实现,
    而且必须要求这个对象存在!【常用】
    @Resource默认通过byname的方式实现,
    如果找不到名字则通过byType实现!
    如果两个都找不到的情况下,就报错!【常用】
  • 4.@Resource(属于J2EE复返),默认按照名称进行装配,名称可以通过name属性进行指定。如果没有指定name属性,当注解写在字段上时,默认取字段名进行按照名称查找,如果注解写在setter方法上默认取属性名进行装配。当找不到与名称匹配的bean时才按照类型进行装配。但是需要注意的是,如果name属性一旦指定,就只会按照名称进行装配。
  • 5.执行顺序不同:@Autowired通过byType的方式实现。
    @Resource默认通过byname的方式实现

7.使用注解开发

  • 在spring4之后,想要使用注解形式,必须得要引入aop的包
  • 在配置文件当中,还得要引入一个context约束
<?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">

</beans>

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

//这里这个注解的意思,就是说明这个类被Spring接管了,注册到了容器
@Component
public class User {

    private String name;

    public String getName() {
        return name;
    }

    @Value("秦疆")
    public void setName(String name) {
        this.name = name;
    }

    @Override
    public String toString() {
        return "User{" +
                "name='" + name + '\'' +
                '}';
    }
}
//这个也会被Spring容器托管,注册到容器中,因为他本来就是一个@Component
// @Configuration代表这是一个配置类,就和我们之前看的beans、xml
@Configuration
@ComponentScan("com.prq.pojo")
@Import(KuangConfig2.class)
public class KuangConfig {

    //注册一个bean,就相当于我们之前写的一个bean标签
    //这个方法的名字,就相当于bean标签中的id属性
    //这个方法的返回值,就相当于bean标签中的class属性
    @Bean
    public User user(){
        return new User();//就是返回要注入到bean的对象!
    }
}

@Component三个衍生注解

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

  • @Controller:web层
  • @Service:service层
  • @Repository:dao层

写上这些注解,就相当于将这个类交给Spring管理装配了!

@scope

  • singleton:默认的,Spring会采用单例模式创建这个对象。关闭工厂 ,所有的对象都会销毁。
  • prototype:多例模式。关闭工厂 ,所有的对象不会销毁。内部的垃圾回收机制会回收

xml与注解

xml与注解
1.xml更加万能,适用于任何场合!维护简单方便
2.注解 不是自己类使用不了,维护相对复杂

xml与注解最佳实践

1.xml用来管理bean
2.注解只负责完成属性的注入;
3.我们使用的过程中,只需要注意一个问题:必须让注解生效,
就需要开启注解的支持   
<!--指定要扫描的包,这个包下的注解就会生效-->
<context:component-scan base-package="com.prq"/>
<context:annotation-config/>

作用:

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

8.代理模式

  • 静态代理
  • 动态代理

静态代理角色分析

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

静态代理的好处:

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

缺点 :

  • 类多了 , 多了代理类 , 工作量变大了 . 开发效率降低 .
  • 我们想要静态代理的好处,又不想要静态代理的缺点,所以 , 就有了动态代理 !

动态代理
动态代理的角色和静态代理的一样 .

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

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

核心 : InvocationHandler 和 Proxy , 打开JDK帮助文档看看
【InvocationHandler:调用处理程序】

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

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

动态代理的好处

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

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

9.什么是AOP

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

Aop在Spring中的作用

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

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

使用Spring实现Aop

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

第一种方式

  • 通过 Spring API 实现

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

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切入实现 , 注意导入约束 .

<?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"
      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-->
   <bean id="userService" class="com.prq.service.UserServiceImpl"/>
   <bean id="log" class="com.prq.log.Log"/>
   <bean id="afterLog" class="com.prq.log.AfterLog"/>

   <!--aop的配置-->
   <aop:config>
       <!--切入点 expression:表达式匹配要执行的方法-->
       <aop:pointcut id="pointcut" expression="execution(* com.prq.service.UserServiceImpl.*(..))"/>
       <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
       <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();
  }
}

Aop的重要性 : 很重要 . 一定要理解其中的思路 , 主要是思想的理解这一块 .

Spring的Aop就是将公共的业务 (日志 , 安全等)
和领域业务结合起来 , 当执行领域业务时 , 将会把
公共业务加进来 . 实现公共业务的重复利用 . 领域
业务更纯粹 , 程序猿专注领域业务 , 其本质还是动态代理 .

第二种方式

  • 自定义类来实现Aop

目标业务类不变依旧是userServiceImpl
第一步 : 写我们自己的一个切入类

public class DiyPointcut {

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

去spring中配置

<!--注册bean-->
<bean id="diy" class="com.kuang.config.DiyPointcut"/>

<!--aop的配置-->
<aop:config>
   <!--第二种方式:使用AOP的标签实现-->
   <aop:aspect ref="diy">
       <aop:pointcut id="diyPonitcut" expression="execution(* com.kuang.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();
  }
}

第三种方式

  • 使用注解实现

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

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;

@Aspect
public class AnnotationPointcut {
   @Before("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void before(){
       System.out.println("---------方法执行前---------");
  }

   @After("execution(* com.kuang.service.UserServiceImpl.*(..))")
   public void after(){
       System.out.println("---------方法执行后---------");
  }

   @Around("execution(* com.kuang.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="com.kuang.config.AnnotationPointcut"/>
<aop:aspectj-autoproxy/>

aop:aspectj-autoproxy:说明

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

10.声明式事务

回顾事务

  • 事务在项目开发过程非常重要,涉及到数据的一致性的问题,不容马虎!
  • 事务管理是企业级应用程序开发中必备技术,用来确保数据
    的完整性和一致性。
  • 事务就是把一系列的动作当成一个独立的工作单元,这些动作要么全部完成,要么全部不起作用。

事务四个属性ACID

  1. 原子性(atomicity)
  • 事务是原子性操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用
  1. 一致性(consistency)
  • 一旦所有事务动作完成,事务就要被提交。数据和资源处于一种满足业务规则的一致性状态中
  1. 隔离性(isolation)
  • 可能多个事务会同时处理相同的数据,因此每个事务都应该与其他事务隔离开来,防止数据损坏
  1. 持久性(durability)
  • 事务一旦完成,无论系统发生什么错误,结果都不会受到影响。通常情况下,事务的结果被写到持久化存储器中

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

事务管理器

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

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 的事务传播机制都工作在同一个事务中。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值