Spring框架知识点

2023-05-16

1. Spring概述

1.1 什么是框架?

框架(Framework):框(指其约束性)架(指其支撑性),在软件设计中指为解决一个开放性问题而 设计的具有一定约束性的支撑结构。在此结构上可以根据具体问题扩展、安插更多的组成部分,从而更 迅速和方便地构建完整的解决问题的方案。

1、框架本身一般不完整到可以解决特定问题

2、框架天生就是为扩展而设计的

3、框架里面可以为后续扩展的组件提供很多辅助性、支撑性的方便易用的实用工具(utilities), 也就是说框架时常配套了一些帮助解决某类问题的库(libraries)或工具(tools)。

如何学习框架呢?

1、知道框架能做什么

2、学习框架的语法,一般框架完成一个功能需要一定的步骤

3、框架的内部实现原理(扩展)

4、尝试实现一个框架(提升)

1.2 Spring是什么

Spring官网 Spring | Home

Spring具有控制反转(IoC)和面向切面(AOP)两大核心。Java Spring 框架通过声明式方式灵活地进 行事务的管理,提高开发效率和质量。

Spring 框架还是一个超级粘合平台,除了自己提供功能外,还提供粘合其他技术 和框架的能力。

1.3 Spring的优势

1、方便解耦,简化开发

2、方便集成各种优秀框架

3、降低 Java EE API 的使用难度

Spring 对 Java EE 开发中非常难用的一些 API(JDBC、JavaMail、远程调用等)都提供了封装, 使这些 API 应用的难度大大降低

4、方便程序的测试

5、AOP 编程的支持

6、声明式事务的支持

只需要通过配置就可以完成对事务的管理,而无须手动编程。

1.4 Spring的体系结构

Spring 为我们提供了一站式解决方案,但Spring 是模块化的,允许咱们挑选和选择适用于项目的模块, 不需要把剩余部分也引入。

Spring 框架提供约 20 个模块,可以根据应用程序的要求来选择。

2、Spring核心之IoC控制反转

2.1 IoC的概念

Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想

IoC 是指在程序开发中,实例的创建不再由调用者管理,而是由 Spring 容器创建,因此,控制权由程序代码转移到了 Spring 容器中,控 制权发生了反转,这就是 Spring 的 IoC 思想。

例如:Javaweb中servlet并没有new就可以使用里面的dopostdoget方法,这就是tomcat服务器创建的对象,就是这种思想

2.2 Spring入门案例

2.2.1 创建maven项目

2.2.2 pom.xml文件添加依赖和插件

2.2.3 创建一个实体类

2.3.4 创建Spring的配置文件application.xml

2.3.5 使用Spring容器创建对象

2.3.6 获取Spring容器

Spring 提供了两种 IoC 容器,分别为 BeanFactory 和 ApplicationContext(BeanFactory的子类)

BeanFactory beanFactory = new XmlBeanFactory(new FileSystemResource(Spring配置文件 的名称));

ApplicationContext applicationContext=new ClassPathXmlApplicationContext(Spring配 置文件的名称);   

ApplicationContext applicationContext = new FileSystemXmlApplicationContext(String configLocation);

2.3.7 通过上下文对象获取容器中的对象

容器中其他API

//容器中其他API
        //容器中对象的个数
        int beanDefinitionCount = applicationContext.getBeanDefinitionCount();
        System.out.println("容器中对象的个数:"+beanDefinitionCount);
        //容器中所有对象的名字
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        System.out.println("容器中对象的名字为:");
        for (String name : beanDefinitionNames) {
            System.out.println(name);
        }

 2.3.8 创建非自定义对象

<!--
        创建非自定义对象
    -->
    <bean id="data1" class="java.util.Date"></bean>

//获取日期对象(非自定义)
        Object data1 = applicationContext.getBean("data1");
        System.out.println(data1);

2.3.9 bean标签的属性 

public void init(){
        System.out.println("--------------init--------------");
    }

    public void destroy(){
        System.out.println("-----------------destroy--------------");
    }

<bean id="team1" class="com.kkb.pojo.Team" scope="singleton" lazy-init="true" init-method="init" destroy-method="destroy"></bean>
    <bean id="team2" class="com.kkb.pojo.Team" scope="prototype"></bean>

@Test
    public void test02(){
        String springConfig="application.xml";
        ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext(springConfig);
        Team team1 = (Team) applicationContext.getBean("team1");
        Team team11 = (Team) applicationContext.getBean("team1");
        System.out.println(team1);
        System.out.println(team11);
        Team team2 = (Team) applicationContext.getBean("team2");
        Team team22 = (Team) applicationContext.getBean("team2");
        System.out.println(team2);
        System.out.println(team22);
        applicationContext.close();//关闭容器
    }

结果:

team-默认的构造方法 id=null,name=null,location=null
--------------init--------------
com.kkb.pojo.Team@32502377
com.kkb.pojo.Team@32502377
team-默认的构造方法 id=null,name=null,location=null
team-默认的构造方法 id=null,name=null,location=null
com.kkb.pojo.Team@2c1b194a
com.kkb.pojo.Team@4dbb42b7
-----------------destroy--------------

2.3 Spring容器创建对象的方式

2.3.1 使用默认的构造方法

2.3.2 使用带参数的构造方法

public Team(Integer id, String name, String location) {
        this.id = id;
        this.name = name;
        this.location = location;
        System.out.println("team-带参数的构造方法 id="+id+",name="+name+",location="+location);
    }

 <!--spring容器创建对象的方式:
    1、通过默认构造方法
    测试类:
    测试结果:
    2、通过带参数的构造方法
    3、通过工厂方法:实例方法,静态方法-->
    <!--1、通过默认构造方法-->
    <bean id="team1" class="com.kkb.pojo.Team"></bean>
    <!-- 2、通过带参数的构造方法-->
    <bean id="team2" class="com.kkb.pojo.Team">
        <!--name:表示参数的名称-->
        <constructor-arg name="id" value="1001"/>
        <constructor-arg name="name" value="公牛"/>
        <constructor-arg name="location" value="纽约"/>
    </bean>
    <bean id="team3" class="com.kkb.pojo.Team">
        <!--index:表示参数的下标索引-->
        <constructor-arg index="0" value="1002"/>
        <constructor-arg index="1" value="篮网"/>
        <constructor-arg index="2" value="华盛顿"/>
    </bean>
 @Test
    public void  CreateTypeTest01(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("createType.xml");
    }

结果:

team-默认的构造方法 id=null,name=null,location=null
team-带参数的构造方法 id=1001,name=公牛,location=纽约
team-带参数的构造方法 id=1002,name=篮网,location=华盛顿

2.3.3 使用工厂类

package com.kkb.pojo;
public class MyFactory {
    /**
     * 实例方法
     * @return
     */
    public Team instanceFun(){
        System.out.println("MyFactory------instanceFun");
        return new Team(1003,"湖人","洛杉矶");
    }
    /**
     * 静态方法
     * @return
     */
    public static Team staticFun(){
        System.out.println("MyFactory------staticFun");
        return new Team(1004,"小牛","达拉斯");
    }
    public static void main(String[] args) {
        MyFactory.staticFun();
        MyFactory factory=new MyFactory();
        factory.instanceFun();
    }
}
<!--3、通过工厂方法:
3.1 静态方法
Team team1 = MyFactory.staticFun();-->
    <bean id="staticTeam" class="com.kkb.pojo.MyFactory" factory-method="staticFun"></bean>

    <!--3、通过工厂方法:
3.2 实例方法
MyFactory factory=new MyFactory();
Team team = factory.instanceFun();-->
    <bean id="factory" class="com.kkb.pojo.MyFactory"></bean>
    <bean id="instanceTeam" factory-bean="factory" factory-method="instanceFun"></bean>

结果:

MyFactory------staticFun
team-带参数的构造方法 id=1004,name=小牛,location=达拉斯
MyFactory------instanceFun
team-带参数的构造方法 id=1003,name=湖人,location=洛杉矶

2.4 基于XML的DI(依赖注入)

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

IoC 是一个概念,是一种思想,其实现方式多种多样。依赖注入就是其中用的比较多的一种方式。

Ioc和DI是同一个概念的不同角度描述。IoC是一种思想,概念,DI是实现它的手段。Spring框架使用依 赖注入实现IoC.

Spring 容器是一个超级大工厂,负责创建、管理所有的 Java 对象,这些 Java 对象被称为 Bean。 Spring 容器管理着容器中 Bean 之间的依赖关系,Spring 使用“依赖注入”的方式来管理 Bean 之间的依 赖关系。使用 IoC 实现对象之间的解耦和。

2.4.1 注入分类


2.4.1.1 通过set方法(常用)

set 注入也叫设值注入是指,通过 setter 方法传入被调用者的实例。这种注入方式简单、直观,因而在 Spring 的依赖注入中大量使用。

<bean id="teamDao" class="com.kkb.dao.TeamDao"></bean>
    <bean id="teamService" class="com.kkb.service.TeamService">
        <!--使用set方法注入属性值-->
        <property name="teamDao" ref="teamDao"></property>
    </bean>
public void setTeamDao(TeamDao teamDao) {
    this.teamDao = teamDao;
}

2.4.1.2 通过构造方法

构造注入是指,在构造调用者实例的同时,完成被调用者的实例化。使用构造器设置依赖关系。

<bean id="teamService2" class="com.kkb.service.TeamService">
        <!--使用构造方法注入属性值-->
        <constructor-arg name="teamDao" ref="teamDao"></constructor-arg>
    </bean>
 public TeamService(TeamDao teamDao) {
        this.teamDao = teamDao;
    }

2.4.1.3. 自动注入

设置 autowire 属性值,为引用类型属性进行隐式自动注入(默认是不自动注入引用类型属 性)。根据 自动注入判断标准的不同,

可以分为两种:

byName:根据名称自动注入

byType: 根据类型自动注入

1、 byName

当配置文件中被调用者 bean 的 id 值与代码中调用者 bean 类的属性名相同时,可使用byName 方式,

<bean id="teamService3" class="com.kkb.service.TeamService" autowire="byName">
    <!--使用byName自动注入-->
</bean>

2、 byType

配置文件中被调用者 bean 的 class 属性指定的类,要与代码中调用 者 bean 类的某引用类型属性类型同源      ⭐:只能有一个相同属性的类

<bean id="teamService4" class="com.kkb.service.TeamService" autowire="byType">
        <!--使用Type自动注入-->
    </bean>

2.5、基于注解实现IoC--重要

对于 DI 使用注解,将不再需要在 Spring 配置文件中声明 bean 实例。Spring 中使用注解,需要在原有 Spring 运行环境基础上再做一些改变。

2.5.1 声明Bean的注解 @Component

在类上添加注解@Component表示该类创建对象的权限交给Spring容器。注解的value属性用于指定 bean的id值,value可以省略。

@Component 不指定 value 属性,bean 的 id 是类名的首字母小写

除此之外,Spring中还提供了其他3个用于创建对象的注解:

@Repository : 用于dao实现类的的注解

@Service: 用户service实现类的注解

@Controller: 用于controller实现类的注解

@Repository,@Service,@Controller 是对@Component 注解的细化,标注不同层的对象。即持久 层对象,业务层对象,控制层对象。

2.5.2 包扫描

需要在 Spring 配置文件中配置组件扫描器,用于在指定的基本包中扫描注解。如果没有报扫描,添加的 创建对象的注解不生效。

如果要扫描的包有多个,可以有以下方式扫描: 1、使用多个context:component-scan指定不同的包路径

2、指定 base-package的值使用分隔符

分隔符可以使用逗号(,)分号(;)还可以使用空格,不建议使用空格。

 3、base-package是指定到父包名

但不建议使用顶级的父包,扫描的路径比较多,导致容器启动时间变慢。指定到目标包和合适的。也就 是注解所在包全路径。

2.5.3 属性注入@Vaule

需要在属性上使用注解@Value,该注解的 value 属性用于指定要注入的值。使用该注解完成属性注入 时,类中无需 setter。当然,若属性有 setter,则也可将其加到 setter 上。

2.5.4 byType自动注入@Autowired 

需要在引用属性上使用注解@Autowired,该注解默认使用按类型自动装配 Bean 的方式。

2.5.5 byName自动注入@Autowired和@Qualifier

@Qualifier 的 value 属性用于指定要匹配 的 Bean 的 id 值。类中无需 set 方法,也可加到 set 方法上。

@Autowired 还有一个属性 required,默认值为 true,表示当匹配失败后,会终止程序运行。若将其值 设置为 false,则匹配失败,将被忽略,未匹配的属性值为 null。

 2.5.6 自动注入@Resource

Spring提供了对 jdk中@Resource注解的支持。@Resource 注解既可以按名称匹配Bean,也可以按类 型匹配 Bean。默认是按名称注入。使用该注解,要求 JDK 必须是 6 及以上版本。@Resource 可在属性 上,也可在 set 方法上。

4、Spring核心之AOP

4.1 什么是AOP

AOP为Aspect Oriented Programming的缩写,意思为面向切面编程,是通过预编译方式和运行期动态 代理实现程序功能的统一维护的一种技术。

好处:

1、减少代码的重复,提高开发效率,便于维护。

2、专注核心业务的开发。

通过spring工厂自动实现将服务性代码以切面的方式加入到核心业务代码中。

4.2 AOP的实现机制-动态代理

4.2.1 什么是代理模式

代理:自己不做,找人帮你做。

代理模式:在一个原有功能的基础上添加新的功能。

分类:静态代理和动态代理。

4.3静态代理

4.3.1 原有方式:核心业务和服务方法都编写在一起

package com.kkb.service;
public class TeamService {
public void add(){
try {
System.out.println("开始事务");
System.out.println("TeamService---- add----");// 核心业务
System.out.println("提交事务");
} catch (Exception e) {
e.printStackTrace();
System.out.println("回滚事务");
}
}
}

4.3.2 基于类的静态代理

将服务性代码分离出来,核心业务--保存业务中只有保存功能

 弊端:代理类只能代理一个类

 4.3.3 基于接口的静态代理

为核心业务(保存add)创建一个接口,通过接口暴露被代理的方法

要求:代理类和被代理类都实现了同一个接口

 

 4.3.4 提取出切面代码,作为AOP接口

 共有4个位置可以将切面代码编织进入核心业务代码中。

 总结静态代理:

1)可以做到在不修改目标对象的功能前提下,对目标对象功能扩展。

2)缺点: 因为代理对象,需要与目标对象实现一样的接口。所以会有很多代理类,类太多。 一旦接口增加方法,目标对象与代理对象都要维护。

4.4 动态代理

静态代理:要求代理类一定存在,

动态代理:程序运行的时候,根据要被代理的对象动态生成代理类。

类型:

1、基于JDK的动态代理

2、基于CGLIB的动态代理

4.4.1 基于JDK的动态代理

直接编写测试类

public static void main1(String[] args) {
        //目标对象--被代理对象
        TeamService teamService=new TeamService();
        //返回代理对象 基于JDK的动态代理
        IService proxyService= (IService) Proxy.newProxyInstance(
                teamService.getClass().getClassLoader(),
                teamService.getClass().getInterfaces(),
                new InvocationHandler() {//回调函数 编写代理规则
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            System.out.println("开始事务");
                            Object invoke = method.invoke(teamService, args);//核心业务
                            System.out.println("提交事务");
                            return invoke;
                        }catch (Exception e){
                            System.out.println("回滚事务");
                            e.printStackTrace();
                            throw e;
                        }finally {
                            System.out.println("finally---------");
                        }
                    }
                }
        );
        //代理对象干活
        proxyService.add();
        System.out.println(teamService.getClass());
        System.out.println(proxyService.getClass()+"------");
    }

结构化设计

public class ProxyFactory {

    private IService service;//目标对象
    private AOP aop;//切面

    public ProxyFactory(IService service, AOP aop) {
        this.service = service;
        this.aop = aop;
    }

    /**
     * 获取动态代理的示例
     * @return
     */
    public Object getProxyInstance() {
        return Proxy.newProxyInstance(
                service.getClass().getClassLoader(),
                service.getClass().getInterfaces(),
                new InvocationHandler() {//回调函数 编写代理规则
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        try {
                            aop.before();
                            Object invoke = method.invoke(service, args);//核心业务
                            aop.after();
                            return invoke;
                        }catch (Exception e){
                            aop.exception();
                            e.printStackTrace();
                            throw e;
                        }finally {
                            aop.myFinally();
                        }
                    }
                }
        );
    }
}
public static void main(String[] args) {
        //目标对象--被代理对象
        TeamService teamService=new TeamService();
        //切面
        AOP tranAop=new TranAOP();
        AOP logAop=new LogAop();
        //获取代理对象
        IService service= (IService) new ProxyFactory(teamService,tranAop).getProxyInstance();
        IService service1= (IService) new ProxyFactory(service,logAop).getProxyInstance();
        service1.add();//核心业务+服务代码混合在一起的完整的业务员方法
    }

4.4.2 基于CGLIB的动态代理

如果想代理没有 实现接口的类,就可以使用CGLIB实现。

4.4.2.1 直接编写测试类

public static void main1(String[] args) {
        //目标对象:没有接口
        NBAService nbaService=new NBAService();
        //创建代理对象:选择cglib动态代理
        NBAService proxyService= (NBAService) Enhancer.create(nbaService.getClass(),
                new MethodInterceptor() {//回调函数编写代理规则
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                try {
                    System.out.println("开始事务");
                    Object invoke = methodProxy.invokeSuper(o, objects);//核心
                    System.out.println("提交事务");
                    return invoke;
                }catch (Exception e){
                    System.out.println("事务回滚");
                    throw e;
                }finally {
                    System.out.println("finally------------");
                }
            }
        });
        //代理对象干活
       int res=proxyService.add("huren",1001);
        System.out.println(res);
    }

4.4.2.2 结构化设计方式

public class CglibProxyFactory {
    //目标对象
    private NBAService nbaService;//没有实现接口的
    //切面
    private AOP aop;//切面

    /**
     * 创建代理对象
     * @param nbaService
     * @param aop
     * @return
     */
    public Object getProxyInstance(NBAService nbaService,AOP aop){
       return  Enhancer.create(nbaService.getClass(),
                new MethodInterceptor() {
                    @Override
                    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                        try {
                            aop.before();
                            Object o1 = methodProxy.invokeSuper(o, objects);
                            aop.after();
                            return o1;
                        }catch (Exception e){
                            aop.exception();
                            throw  e;
                        }                        finally {
                            System.out.println("finally-----------");
                        }
                    }
                });
    }
}
public static void main(String[] args) {
        //目标对象:没有接口
        NBAService nbaService=new NBAService();
        //创建切面
        AOP tranAop=new TranAOP();
        //创建代理对象:选择cglib动态代理
        NBAService proxyInstance = (NBAService) new CglibProxyFactory().getProxyInstance(nbaService, tranAop);
        int res=proxyInstance.add("huren",1001);
        System.out.println(res);
    }

4.5 SpringAOP

4.5.1 Spring AOP相关概念

Spring的AOP实现底层就是对上面的动态代理的代码进行了封装,封装后我们只需要对需要关注的部分 进行代码编写,并通过配置的方式完成指定目标的方法增强。 我们先来介绍AOP的相关术语:

Target(目标对象)

要被增强的对象,一般是业务逻辑类的对象。

Proxy(代理)

一个类被 AOP 织入增强后,就产生一个结果代理类。

Aspect(切面)

表示增强的功能,就是一些代码完成的某个功能,非业务功能。是切入点和通知的结合

Joinpoint(连接点)

所谓连接点是指那些被拦截到的点。在Spring中,这些点指的是方法(一般是类中的业务方法),因为 Spring只支持方法类型的连接点。

Pointcut(切入点)

切入点指声明的一个或多个连接点的集合。通过切入点指定一组方法。 被标记为 final 的方法是不能作为连接点与切入点的。因为最终的是不能被修改的,不能被增强的。

Advice(通知/增强)

通知的类型:前置通知,后置通知,异常通知,最终通知,环绕通知。

切入点定义切入的位置,通知定义切入的时间。

Weaving(织入).

是指把增强应用到目标对象来创建新的代理对象的过程。 spring 采用动态代理织入,而 AspectJ 采用编 译期织入和类装载期织入。

切面的三个关键因素:

1、切面的功能--切面能干啥

2、切面的执行位置--使用Pointcut表示切面执行的位置

3、切面的执行时间--使用Advice表示时间,在目标方法之前还是之后执行。

4.5.2 AspectJ 对 AOP 的实现

所以,Spring 又将 AspectJ 的对于 AOP 的实现也引入到了自己的框架中。

在 Spring 中使用 AOP 开发时,一般使用 AspectJ 的实现方式

4.5.2 .1 AspectJ的通知类型

AspectJ 中常用的通知有5种类型: 1. 前置通知 2. 后置通知 3. 环绕通知 4. 异常通知 5. 最终通知

4.5.2.2 AspectJ的切入点表达式

execution(访问权限 方法返回值    方法声明(参数) 异常类型) 

PS:表达式中黑色文字表示可省略部分,各部分间用空格分开

 4.5.3 注解方式实现AOP

开发阶段:关注核心业务和AOP代码

运行阶段:spring框架会在运行的时候将核心业务和AOP代码通过动态代理的方式编织在一起

代理方式的选择:是否实现了接口:有接口就选择JDK动态代理;没有就选择CGLIB动态代理。

1、创建项目引入依赖

<dependencies>
        <!--spring 核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、创建spring配置文件引入约束

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/springbeans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--在beans标签中 引入AOP和context约束-->
</beans>

3、创建核心业务类

public interface IService {
    void add(int id,String name);
    boolean update(int num);
}
@Service
public class TeamService implements IService{
    @Override
    public void add(int id, String name) {
        //id=10/0;
        System.out.println("TeamService-------------add----------");
    }

    @Override
    public boolean update(int num) {
        System.out.println("TeamService-------------update----------");
        if (num>666){
            return true;
        }
        return false;
    }
}

4、定义切面类

/**
 * 切面类
 */
@Component//切面对象的创建权限依然交给Spring容器
@Aspect//aspectj  框架的注解  表示当前类为切面类
public class MyAspect {

    /**
     * Pointcut  切入点表达式  一般为私有
     * 可以在其他通知的value属性值中直接使用方法
     */
    @Pointcut("execution(* com.kkb.service..*.*(..))")
    private void pointCut(){

    }

    @Pointcut("execution(* com.kkb.service..*.add*(..))")
    private void pointCut2(){

    }

    @Before("pointCut()")
    public void before(){
        System.out.println("前置通知:在目标方法执行之前被调用的通知");
    }

    /**
     * AfterReturning 注解声明后置通知
     * value:切入点表达式
     * returning:属性表示  返回的结果 ,如果需要的话可以在后置通知的方法中修改结果
     */
    @AfterReturning(value = "pointCut2()",returning = "result")
    public Object after(Object result){
        if (result!=null){
            boolean res= (boolean) result;
            if (res){
                result=false;
            }
        }
        System.out.println("后置通知:在目标方法执行之后被调用的通知,result:"+result);
        return result;
    }


    /**
     * Around  环绕通知
     * ProceedingJoinPoint 中的proceed表示目标方法被执行
     */
    @Around(value = "pointCut2()")
    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("环绕方法-----------目标方法执行之前");
        pjp.proceed();
        System.out.println("环绕方法-----------目标方法执行之后");
    }


    /**
     * AfterThrowing 注解声明异常通知
     * value:切入点表达式
     * returning:属性表示  返回的结果 ,如果需要的话可以在后置通知的方法中修改结果
     */
    @AfterThrowing(value = "pointCut2()",throwing = "ex")
    public void exception(JoinPoint jp,Throwable ex){
        //一般会把异常发生时间 位置 原因记录下来
        System.out.println("异常通知:在目标方法执行出现异常的时候才会别调用的通知,否则不执行");
        System.out.println(jp.getSignature()+"方法出现异常"+ex.getMessage());
    }

    /**
     * After  最终通知
     */
    @After(value = "pointCut2()")
    public void myFinally(){
        System.out.println("最终通知:无论是否出现异常都是最后被调用的通知");
    }
}

在定义好切面 Aspect 后,需要通知 Spring 容器,让容器生成“目标类+ 切面”的代理对象。这个代理是 由容器自动生成的。只需要在 Spring 配置文件中注册一个基于 aspectj 的自动代理生成器,其就会自动 扫描到@Aspect 注解,并按通知类型与切入点,将其织入,并生成代理。

5、spring.xml配置文件中开启包扫描和 注册aspectj的自动代理

<!--包扫描-->
<context:component-scan base-package="com.kkb.service,com.kkb.aop"/>
<!--开启注解AOP的使用-->
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!--aop:aspectj-autoproxy的底层是由 AnnotationAwareAspectJAutoProxyCreator 实现的,
是基于 AspectJ 的注解适配自动代理生成器。
其工作原理是,aop:aspectj-autoproxy通过扫描找到@Aspect 定义的切面类,再由切面类根据切入点找
到目标类的目标方法,再由通知类型找到切入的时间点。-->

6.测试类:

@Test
    public void  test01(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("spring.xml");
        TeamService teamservice = (TeamService) applicationContext.getBean("teamService");
        teamservice.add(1001,"湖人队");
        System.out.println("------------");
        boolean update = teamservice.update(888);
        System.out.println("修改结果:"+update);

    }

4.5.4 XML方式实现AOP

/**
 * 切面类
 */
@Component//切面对象的创建权限依然交给Spring容器
@Aspect//aspectj  框架的注解  表示当前类为切面类
public class AOP {

    /**
     * Pointcut  切入点表达式  一般为私有
     * 可以在其他通知的value属性值中直接使用方法
     */
    @Pointcut("execution(* com.kkb.service..*.*(..))")
    private void pointCut(){

    }

    @Pointcut("execution(* com.kkb.service..*.add*(..))")
    private void pointCut2(){

    }

    @Before("pointCut()")
    public void before(){
        System.out.println("AOP前置通知:在目标方法执行之前被调用的通知");
    }


    public void after(Object result){
        System.out.println("AOP后置通知:在目标方法执行之后被调用的通知,result:"+result);

    }



    public void around(ProceedingJoinPoint pjp) throws Throwable {
        System.out.println("AOP环绕方法-----------目标方法执行之前");
        pjp.proceed();
        System.out.println("AOP环绕方法-----------目标方法执行之后");
    }



    public void exception(JoinPoint jp,Throwable ex){
        //一般会把异常发生时间 位置 原因记录下来
        System.out.println("AOP异常通知:在目标方法执行出现异常的时候才会别调用的通知,否则不执行");
        System.out.println(jp.getSignature()+"方法出现异常"+ex.getMessage());
    }


    public void myFinally(){
        System.out.println("AOP最终通知:无论是否出现异常都是最后被调用的通知");
    }
}

配置文件

 <!--xml方式实现AOP-->
    <aop:config>
        <aop:pointcut id="pt1" expression="execution(* com.kkb.service..*.*(..))"/>
        <aop:pointcut id="pt2" expression="execution(* com.kkb.service..*.add*(..))"/>
        <aop:aspect ref="AOP">
            <aop:before method="before" pointcut-ref="pt1"></aop:before>
            <aop:after-returning method="after" pointcut-ref="pt1" returning="result"></aop:after-returning>
            <aop:after-throwing method="exception" pointcut-ref="pt1" throwing="ex"></aop:after-throwing>
            <aop:after method="myFinally" pointcut-ref="pt1"></aop:after>
            <aop:around method="around" pointcut-ref="pt1"></aop:around>
        </aop:aspect>
    </aop:config>

5、Spring整合JDBC

5.1 使用spring-jdbc操作数据库

1、创建项目引入依赖

<dependencies>
        <!--spring 核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.2.13.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.23</version>
        </dependency>
        <dependency>
            <groupId>com.mchange</groupId>
            <artifactId>c3p0</artifactId>
            <version>0.9.5.2</version>
        </dependency>
        <!--测试依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!--编译插件-->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.0</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>

2、测试连接

package com.kkb.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import org.junit.Test;
import org.springframework.jdbc.core.JdbcTemplate;

import java.beans.PropertyVetoException;

public class Test01 {
    @Test
    public void test01() throws PropertyVetoException {
        //创建数据源
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springJDBC?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        //使用JDBCTemplete
        JdbcTemplate template=new JdbcTemplate(dataSource);
        String sql="insert into team(tname,location) value(?,?)";
        int update = template.update(sql, "篮网", "华盛顿");
        System.out.println("插入:"+update);

    }
}

5.2. Spring管理JdbcTemplate

spring整合jdbc的时候我们都是直接让我的dao继承Spring提供的JdbcDaoSupport类,该类中提供了 JdbcTemplate模板可用。

public class TeamDao extends JdbcDaoSupport {

    public int getCount(){
        String sql="select count(tid) from team";
        return this.getJdbcTemplate().queryForObject(sql,Integer.class);
    }

    public Map<String,Object> getMany(){
        String sql="select max(tid),min(tid) from team";
        return this.getJdbcTemplate().queryForMap(sql);
    }

    /**
     * 自己封装一个处理多种结果的方法
     */
    public Team handleResult(ResultSet resultSet) throws SQLException {
        Team team = new Team();
        team.setId(resultSet.getInt("tid"));
        team.setName(resultSet.getString("tname"));
        team.setLocation(resultSet.getString("location"));
        return team;
    }

    public int insert(Team team){
        String sql="insert into team(tname,location) value(?,?)";
        return this.getJdbcTemplate().update(sql,team.getName(),team.getLocation());
    }

    public int update(Team team){
        String sql="update team set tname=?,location=? where tid=?";
        return this.getJdbcTemplate().update(sql,team.getName(),team.getLocation(),team.getId());
    }

    public int del(int id){
        String sql="delete from team where tid=?";
        return this.getJdbcTemplate().update(sql,id);
    }

    public Team findById(int id){
        String sql="select * from team where tid=?";
        return this.getJdbcTemplate().queryForObject(sql, new Object[]{id}, new RowMapper<Team>() {
            @Override
            public Team mapRow(ResultSet resultSet, int i) throws SQLException {
                /*Team team = new Team();
                team.setId(resultSet.getInt("tid"));
                team.setName(resultSet.getString("tname"));
                team.setLocation(resultSet.getString("location"));
                return team;*/
                return handleResult(resultSet);
            }
        });
    }


    public List<Team> findByAll(){
        String sql="select * from team ";
        return this.getJdbcTemplate().query(sql, new RowMapper<Team>() {
            @Override
            public Team mapRow(ResultSet resultSet, int i) throws SQLException {
                /*Team team = new Team();
                team.setId(resultSet.getInt("tid"));
                team.setName(resultSet.getString("tname"));
                team.setLocation(resultSet.getString("location"));
                return team;*/
                return handleResult(resultSet);
            }
        });
    }
}

spring的配置文件application.xml中需要创建数据源和给TeamDao中的jdbcTemplate赋值

<!--创建JDBCTemplate的数据源-->
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="driverClass" value="com.mysql.cj.jdbc.Driver"></property>
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/springJDBC?serverTimezone=UTC&amp;characterEncoding=utf8&amp;useUnicode=true&amp;useSSL=false"></property>
        <property name="user" value="root"></property>
        <property name="password" value="123456"></property>
    </bean>

    <!--创建JdbcTemplate-->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <bean id="teamDao" class="com.kkb.dao.TeamDao">
        <property name="jdbcTemplate" ref="jdbcTemplate"></property>
    </bean>

测试:

public class Test01 {

    @Test
    public void testMany(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        Map<String, Object> many = teamDao.getMany();
        Set<Map.Entry<String, Object>> entries = many.entrySet();
        for (Map.Entry<String, Object> entry : entries) {
            System.out.println(entry.getKey()+"-----------"+entry.getValue());
        }
    }

    @Test
    public void testGetCount(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        int count = teamDao.getCount();
        System.out.println(count);
    }


    @Test
    public void testFindAll(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        List<Team> byAll = teamDao.findByAll();
        System.out.println(byAll);
    }

    @Test
    public void testFindById(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        Team team = teamDao.findById(1);
        System.out.println(team);
    }



    @Test
    public void testDel(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        int insert = teamDao.del(5);
        System.out.println("删除:"+insert);
    }

    @Test
    public void testUpdate(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        Team team = new Team();
        team.setName("小牛");
        team.setLocation("达拉斯");
        team.setId(3);
        int insert = teamDao.update(team);
        System.out.println("修改:"+insert);
    }

    @Test
    public void testInster(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamDao teamDao = (TeamDao) applicationContext.getBean("teamDao");
        Team team = new Team();
        team.setName("勇士");
        team.setLocation("金州");
        int insert = teamDao.insert(team);
        System.out.println("插入:"+insert);
    }


    @Test
    public void test01() throws PropertyVetoException {
        //创建数据源
        ComboPooledDataSource dataSource=new ComboPooledDataSource();
        dataSource.setDriverClass("com.mysql.cj.jdbc.Driver");
        dataSource.setJdbcUrl("jdbc:mysql://localhost:3306/springJDBC?serverTimezone=UTC&characterEncoding=utf8&useUnicode=true&useSSL=false");
        dataSource.setUser("root");
        dataSource.setPassword("123456");
        //使用JDBCTemplete
        JdbcTemplate template=new JdbcTemplate(dataSource);
        String sql="insert into team(tname,location) value(?,?)";
        int update = template.update(sql, "篮网", "华盛顿");
        System.out.println("插入:"+update);

    }
}

6、Spring事务管理

事务原本是数据库中的概念,在 Dao 层。但在实际开发中,一般将事务提升到业务层,即 Service 层。 这样做是为了能够使用事务的特性来管理具体的业务。

6.1 Spring事务管理API

Spring 的事务管理,主要用到两个事务相关的接口。

6.1.1 事务管理器接口

事务管理器是 PlatformTransactionManager 接口对象。其主要用于完成事务的提交、回滚,及获取事 务的状态信息。

PlatformTransactionManager 接口常用的实现类:

DataSourceTransactionManager:使用 JDBC 或 MyBatis 进行数据库操作时使用。

Spring的回滚方式:

Spring 事务的默认回滚方式是:发生运行时异常和 error 时回滚,发生受查(编译)异常时提交。不过, 对于受查异常,程序员也可以手工设置其回滚方式。

6.1.2 事务定义接口

事务定义接口 TransactionDefinition 中定义了事务描述相关的三类常量:事务隔离级别、事务传播行 为、事务默认超时时限,及对它们的操作

6.1.2.1 事务隔离级别常量

这些常量均是以 ISOLATION_开头。即形如 ISOLATION_XXX。

6.1.2.2 事务传播行为常量 

所谓事务传播行为是指,处于不同事务中的方法在相互调用时,执行期间事务的维护情况。如,A 事务 中的方法 doSome()调用 B 事务中的方法 doOther(),在调用执行期间事务的维护情况,就称为事务传 播行为。事务传播行为是加在方法上的。

事务传播行为常量都是以 PROPAGATION_ 开头,形如 PROPAGATION_XXX。 Propagation.REQUIRED 当前没有事务的时候,就会创建一个新的事务;如果当前有事务,就直接加入该事务,比较常用的 设置

Propagation.SUPPORTS 支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就以非事务方式执行

Propagation.MANDATORY 支持当前事务,如果当前有事务,就直接加入该事务;当前没有事务的时候,就抛出异常

Propagation.REQUIRES_NEW 创建新事务,无论当前是否有事务都会创建新的

PROPAGATION_NESTED

PROPAGATION_NEVER

PROPAGATION_NOT_SUPPORTED

6.1.2.3 默认事务超时时限

常量 TIMEOUT_DEFAULT 定义了事务底层默认的超时时限,sql 语句的执行时长。 注意,事务的超时时限起作用的条件比较多,且超时的时间计算点较复杂。

所以,该值一般就使用默认 值即可。

6.2 声明式事务控制

Spring提供的对事务的管理,就叫做声明式事务管理。

如果用户需要使用spring的声明式事务管理,在配置文件中配置即可:不想使用的时候直接移除配置。 这种方式实现了对事务控制的最大程度的解耦。

声明式事务管理,核心实现就是基于AOP

 6.3 基于注解的事务

@Service
public class TeamService {

    @Autowired
    private TeamDao teamDao;

    @Transactional(propagation = Propagation.REQUIRED,rollbackFor = {Exception.class})
    public int insert(Team team){
        int insert = teamDao.insert(team);
        System.out.println("第一执行成功");
        System.out.println(10/0);
        int insert2 = teamDao.insert(team);
        return insert+insert2;
    }
}
<context:component-scan base-package="com.kkb"></context:component-scan>
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
public class Test02 {

    @Test
    public void test01(){
        ApplicationContext applicationContext=new ClassPathXmlApplicationContext("application.xml");
        TeamService teamService = (TeamService) applicationContext.getBean("teamService");
        int insert = teamService.insert(new Team("凯尔特人", "波士顿"));

    }
}

6.4 基于XML的事务

 

 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Spring框架知识点 的相关文章

随机推荐

  • CentOS yum方式升级内核kernel

    xff08 此方法只限于CentOS派系的yum rpm 补充 xff1a 限于64Bit CentOS7的32位 xff0c 我试过用CentOS6的32位内核来升级 xff0c 可升级可重启可使用 xff0c 半个小时后删除了此系统没再
  • dlang语法的简单整理

    dlang整理 为什么使用dlang 优点 xff1a 快速 xff0c 开发高效的 xff0c 方便 xff0c 无虚拟机的 xff0c 快速的 xff0c 高性能的 垃圾回收 缺点 xff1a 语法较为复杂 xff0c 支持gc 曾经很
  • Linux基础命令-netstat显示网络状态

    Linux基础命令 ss显示socket信息 文章目录 netstat 命令介绍 语法格式 基本参数 显示各列内容分析 1 xff09 netstat a显示各列内容分析 2 xff09 netstat r显示各列内容分析 3 xff09
  • R语言igraph包的使用

    igraph包是一个用来解决图与网络问题以及对其进行可视化的包 前几天数学建模做图论的作业我就是用的这个包 xff0c 这篇博客就写一下如何解决图论中的最短路问题 xff0c 最大流问题和最小生成树问题 xff0c 以及图的可视化 需要声明
  • C++ sizeof

    sizeof xff1a 运算符 xff0c 返回类型或数据对象的长度 xff0c 单位为字节 用法 xff1a 数据类型 xff1a sizeof char xff1a 返回对应长度 普通变量 xff1a sizeof a xff1a 返
  • VisualGDB的基本使用

    在Linux下调试工程是一件很苦逼的事情 xff0c 不像在Windows下用Visual Studio那样简便 xff0c 但是最近发现一件神器可以让Linux下的程序一样可以在Windows下的Viusal Studio中调试起来 Vi
  • Unix 环境高级编程(一):开发环境

    Unix 环境高级编程 xff08 一 xff09 xff1a 开发环境 一 Unix操作系统二 Linux操作系统三 GNU编译工具 xff08 GCC xff09 1 简介2 基本用法3 文件后缀4 构建过程5 预处理指令6 预定义宏7
  • 18张含金量最高的大数据证书

    这年头从事数据行业很不赖 用人需求量之大达到创记录的水平 xff0c 薪资也水涨船高 几乎任何数据认证都会让你的薪资涨一涨 本文介绍了哪几大数据认证可以让你稳赚丰厚薪水 顶级数据技能拿顶薪 你是不是在想 xff1a 为获得那下一份数据认证付
  • XML解析——Java中XML的四种解析方式

    XML是一种通用的数据交换格式 它的平台无关性 语言无关性 系统无关性 给数据集成与交互带来了极大的方便 XML在不同的语言环境中解析方式都是一样的 只不过实现的语法不同而已 XML的解析方式分为四种 xff1a 1 DOM解析 xff1b
  • JVM调优总结 -Xms -Xmx -Xmn -Xss

    Xms 是指设定程序启动时占用内存大小 一般来讲 xff0c 大点 xff0c 程序会启动的快一点 xff0c 但是也可能会导致机器暂时间变慢 Xmx 是指设定程序运行期间最大可占用的内存大小 如果程序运行需要占用更多的内存 xff0c 超
  • Spring Boot 传参方式

    最近在搞Spring Boot的项目 xff0c 把传参方式总结一下 网上也参考一些文章 xff0c 总结的很不错 xff0c 这里借鉴一下 注解 64 RequestParam 这个注解用来绑定单个请求数据 xff0c 既可以是url中的
  • Java BigDecimal开方

    前言 一般开平方使用的是Math中的静态方法Math sqrt double a xff0c 涉及到金融计算的时候 xff0c Math sqrt double a 精度就不够了 金融领域的计算 xff0c 用的都是BigDecimal类型
  • c实现set集合

    集合有点编程语言会带有 xff0c 有的没有 但是我想redis的集合set你一定听说过或者用过 下面咱们用链表来实现set 相信有了前面的基础我们可以很容易的实现set集合 需要引入我的链表的list c和list h 头文件 span
  • Spring Boot 集成RabbitMQ

    RabbitMQ is an open source multi protocol messaging broker 前言 参照官方Messaging with RabbitMQ xff0c 记录在实战中的一些坑 搭建RabbitMQ服务
  • Java 获取接口所有实现类

    利用Spring的Bean工厂 xff0c 获取接口所有实现类 前言 在学习Spring Boot 集成RabbitMQ时 xff0c 发现定义了好几个bean xff0c 这些bean在什么地方用到呢 xff1f 查看RabbitAdmi
  • Intellij IDEA-SSH executable-Native

    Connecting to gitlab using PuTTY generated SSH key in IDEA 背景 项目开发中 xff0c 使用Gitlab搭建git服务 xff0c 做代码的版本管理 xff0c 一开始是使用htt
  • 近年作品集

    青海省电力局资产盘点机器人 一种基于地图骨架提取和启发式搜索树的路径规划算法 复杂地图 一种基于地图骨架提取和启发式搜索树构建的路径规划 简单地图
  • 【技巧】SQL中修改列名(column)

    问 xff1a 怎么修改Mysql中的表格的某列的列名 xff1f 答 xff1a 比如说我想将seat表中的seatid列名修改为 seat id 1 初状态 xff1a 输入 xff1a select from seat 查看seat
  • 【报错】resultMap认知错误

    数据库改了一个字段的名字 xff0c 后来牵扯到实体类标准化都要改 xff0c 原来以为 xff0c mybatis使用的sql语句都是通过resultMap映射后 xff0c 可以使用后面的property xff0c 因为之前colum
  • Spring框架知识点

    1 Spring概述 1 1 什么是框架 xff1f 框架 xff08 Framework xff09 xff1a 框 xff08 指其约束性 xff09 架 xff08 指其支撑性 xff09 xff0c 在软件设计中指为解决一个开放性问