学习Java框架 Spring

断了今生、忘了曾经 提交于 2020-01-14 02:36:59
学习Java框架 Spring

Java Spring框架

  • 简介
    • Spring是一个开源框架、一个轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。相当于一个容器 工厂, 可以将所有对象创建和依赖关系维护, 交由 Spring 管理 。
  • 体系结构
    • 在这里插入图片描述如果作为一个整体,这些模块为你提供了开发企业应用所需的一切。但你不必将应用完全基于Spring框架。你可以自由地挑选适合你的应用的模块而忽略其余的模块。

Spring简单使用

步骤·:

  • 1、导包 引入spring依赖的jar文件
    在这里插入图片描述
    • 解压后的文件夹:
      在这里插入图片描述
      • docs :API 和开发规范.
      • libs :jar 包和源码.
      • schema :约束.
    • jar 包
      • spring-beans-4.3.6.jar 所有应用都要用到的,它包含访问配置文件、创建和管理 bean(java对象)及进行 Inversion of Control / Dependency Injection(属性赋值)(IoC/DI)操作相关的所有类
      • spring-context-4.3.6.jar Spring 供在基础 IoC 功能上的扩展服务,此外还 供许多企业级服务的支持 , 如邮件服务(SpringMail)、 任务调度(SpringTest)、JNDI 定位、EJB 集成、远程访问、缓存以及各种视图 层框架(springMVC)的封装等。
      • spring-core-4.3.6.jar 包含 Spring 框架基本的核心工具类,Spring 其它组件要都要使用到这个包里 的类 , 是其它组件的基本核心。
      • spring-expression-4.3.6.jar Spring 表达式语言
      • commons-logging-1.2.jar 第三方的主要用于处理日志
  • 2、创建一个对象 并提供set和get方法 比如User
  • 3、xml配置注册对象到容器
    • 建立xml文件,(applicationContext.xml)
    • 配置文件头信息
    • 注册对象:< bean id=“user” class=“beike.spring.pojo.User”></ bean> 相当于直接new User() 无参 图:
      在这里插入图片描述
    • 属性解析:
      • 将User对象交给spring容器管理 ,其中name任意,class为全包名
      • class属性:被管理对象的完整类名
      • name属性:给被管理的对象起个名字,根据该名称获得对象可以重复,可以使用特殊字符
      • id属性:与name属性一模一样名称不可重复,不能使用特殊字符 所以尽量使用name属性
  • 4、代码测试
    • 在这里插入图片描述

Spring的工厂(容器) 加载的三种方式

  • 方法一 在类路径下寻找配置文件来实例化容器 即src目录下的 等
    • //如果只有一个配置文件,一次加载一个容器 ApplicationContext applicationContext = new ClassPathXmlApplicationContext(“applicationContext.xml”);
    • // 如果有多个配置文件,定义成数组一次可以加载多个 spring 容器 ApplicationContext ac = new ClassPathXmlApplicationContext(new String[] {“com/offcn/applicationContext.xml” , “com/offcn/beans.xml”});
    • //或者是使用通配符加载: ApplicationContext ctx=new ClassPathXmlApplicationContext(“com/offcn/*.xml”);
  • 方法二 从硬盘绝对路径下加载配置文件
    • ApplicationContext ctx = new FileSystemXmlApplicationContext(“recourse/applicationContext.xml”); //当前项目路径加载单个配置文件
    • ApplicationContext ctx = new FileSystemXmlApplicationContext(“D:/project/bean.xml”);//根据具体路径加载文件
    • // 如果有多个配置文件,定义成数组一次可以加载多个 spring 容器 ApplicationContext ac = new FileSystemXmlApplicationContext(new String[] {“D:\workspaceSpring\SpringBean\beans.xml”});
  • 方法三 (过时) 使用 BeanFactory
    • BeanFactory beanfactory = new XmlBeanFactory(new FileSystemResource(“D:\dev\work\springDemo\recourse\applicationContext.xml”));
  • 注意点:
    • BeanFactory :是在 getBean 的时候才会生成类的实例
    • AlicationContext 默认在加载 applicationContext.xml(容器启动)时候就会创建类的实例

IOC:控制反转 由 Spring IOC 容器来负责对象的生命周期和对象之间的关系

Spring创建对象的三种方式 (无参示例):

  • 方式一: 空参构造方式(最主要方式)
    • xml文件beans里面配置 :< bean id=“user” class=“beike.spring.pojo.User”></ bean>
  • 方式二: 使用静态工厂方法实例化 ( 简单工厂模式 )
    • < !-- 通过工厂的静态方法来获取对象 -->
      < bean name=“userStaticFactory” class=“com.ujiuye.day04.ioc.factory.UserStaticFactory”
      factory-method=“getUser”></ bean>
  • 方式三: 使用实例工厂方法实例化 ( 工厂方法模式 )
    • < !-- 通过工厂的非静态方法获取对象 -->
      < bean name=“UserFactory” class=“com.ujiuye.day04.ioc.factory.UserFactory”></ bean>
      < bean name=“getUserByUserFactory” factory-bean=“UserFactory”
      factory-method=“getUser”></ bean>
  • 注意:class 一定要是类,不能使用接口,因为接口不能被实例化。

关于配置文件解析(applicationContext.xml)

  • bean元素属性
    -

DI:即依赖注入, 就是由IOC容器在运行期间, 动态地将某种依赖关系注入到对象之中。

属性注入 :

1、set方法注入 (前提是set注入之前该对象提供setter方法) 子标签:property
2、构造函数注入 (前提是在类中提供匹配的构造方法) 子标签:constructor-arg
3、使用注解实现注入 使用 @Autowired 或 @Resource 注解方式进行装配
4、注解实现装配Bean
  • 1、set方法注入使用示例

    • Car类
      在这里插入图片描述
    • xml文件配置
      在这里插入图片描述
  • 2、构造函数注入使用示例

    • User类
      -
    • xml文件配置 在这里插入图片描述
  • 3、复杂类型注入: 数组、List集合、Map集合、 Properties配置文件

    • ComplexBean类:在这里插入图片描述
    • xml文件 在这里插入图片描述在这里插入图片描述在这里插入图片描述
  • 4、使用注解实现注入 使用 @Autowired 或 @Resource 进行装配使用步骤

    • 1、引入context命名空间 需要在xml配置文件中配置以下信息 在这里插入图片描述

      • 注意:< context:annotation-config/> 标签 这个配置隐式注册了多个对注解进行解析处理的处理器:
        • 对注解进行解析处理的处理器
        • AutowiredAnnotationBeanPostProcessor
        • CommonAnnotationBeanPostProcessor
        • PersistenceAnnotationBeanPostProcessor
        • RequiredAnnotationBeanPostProces- sor
    • 2、使用注解@autowired 或者注解@Resource 标注在属性上 或者 标注在 set 方法上
      在这里插入图片描述

    • 3、代码测试在这里插入图片描述

  • 关于注解@autowired 和 注解@Resource 的用法:

  • @autowired

    • 使用注解 @autowired 标注在属性上
      在这里插入图片描述
      • 获取该注解标注的字段的类型—Car类型
      • 以该类型为条件到spring容器(beans.xml文件)中去查找bean的id节点的类型是 Car 类型 。 比如说:文件上有< bean name=“car” class=“com.ujiuye.day04.annotation.pojo.Car”>
      • 找到以后,获取该节点对应的对象,利用反射直接为personDao变量赋值
        所以注解 @autowired 标注在属性上可以不用set方法
    • 使用注解 @autowired 标注在 set 方法上 在这里插入图片描述
      • 获取 setCar() 方法的参数的类型 —Car 类型
      • 以该类型为条件到spring容器(beans.xml)中去查找bean的id节点的类 型是Car 类型 .
      • 找到以后 , 获取该节点对应的对象 , 把该对象作为实参传递给该 setCar(Car car) 的形参 .
  • @Resource

    • 使用注解 @Resource 标注在属性上 在这里插入图片描述
      • 如果没有指定 name 属性 则获取该注解标注的字段值—car3
        • 以该字段值为条件到 spring 容器 (beans.xml) 中去查找 bean 的 id 节点的值是 car3 的节点
        • 找到以后,获取该节点对应的对象, 利用反射直接为car3变量 赋值
        • 如果没有找到.并且按照默认的名称找不到依赖对象时, @Resource 注解会回退到按类型装配
        • 获取该注解标注的字段类型 --Car 以该类型为条件到spring容器(xml文件)中去查找bean的节点的 类型是 Car 类型的对象 找到以后,获取该节点对应的对象,利用反射直接为car3 变量 赋值
      • 如果指定 name 属性 只能按名称装配
        • 获取name属性的值
        • 以该值为条件到spring容器(beans.xml)中去查找bean的id节点的值
        • 找到以后 , 获取该节点对应的对象 , 把该对象作为实参传递给该 setCar(Car Car) 的形参 .
        • 如果不存在该名称 , 抛出异常
  • 使用注解 @Resource 标注在 set 方法上
    在这里插入图片描述
    • 如果没有指定name属性
      • 获取setCar()方法的属性名—car
      • 以该属性名为条件到spring容器(xml文件)中去查找bean的id节点的值是 car 的节点
      • 找到以后 , 获取该节点对应的对象 , 把该对象作为实参传递给该 setPersonDao( PersonDao personDao) 的形参 .
      • 如果没有找到.并且按照默认的名称找不到依赖对象时,@Resource注解会回退到按类型装配
      • 获取setPersonDao()方法的参数类型—PersonDao
      • 以该类型为条件到spring容器(beans.xml)中去查找bean的节点的类型是 PersonDao 类型对象
      • 找到以后 , 获取该节点对应的对象 , 把该对象作为实参传递给该 setPersonDao(
        PersonDao personDao) 方法的形参
  • 如果指定 name 属性 只能按名称装配
    • 获取name属性的值
    • 以该值为条件到spring容器(beans.xml)中去查找bean的id节点的值
    • 找到以后 , 获取该节点对应的对象 , 把该对象作为实参传递给该 setCar(Car Car) 的形参 .
    • 如果不存在该名称 , 抛出异常

注解实现装配Bean :

  • 简介:
    • 1、Spring3.0 为我们引入了组件自动扫包 机制,它可以在类路径底下寻找标注了 @ Component、@Service、@Controller、@Repository 注解的类,并把这些类纳入进 spring 容器中管理
      • 2、功能介绍
        • @Service 用于标注业务层组件.
        • @Controller 用于标注控制层组件(如 struts 中的 action)
        • @Repository 用于标注数据访问组件,即 DAO 组件。
        • @Component 泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。
使用步骤:
  • 1、引入context命名空间 需要在xml配置文件中配置以下信息:
    在这里插入图片描述
  • 2、将相应的注解标注在实现类的上面
    在这里插入图片描述
  • 3、代码测试
    在这里插入图片描述

AOP:是Spring框架面向切面的编程思想

  • AOP采用一种称为“横切”的技术,将涉及多业务流程的通用功能抽取并单独封装,形成独立的切面,在合适的时机将这些切面横向切入到业务流程指定的位置中

AOP 代理模式 :JDK动态代理(主要使用)、 cglib代理

  • 简介:
    • 代理模式 : 代理模式的英文叫做 Proxy 或 Surrogate,中文都可译为”代理“,所谓代理, 就是一个人或者一个机构代表另一个人或者另一个机构采取行动。在一些情况下,一个客 户不想或者不能够直接引用一个对象,而代理对象可以在客户端和目标对象之间起到中介 的作用 .
    • 使用代理对象访问目标对象(优势:目标对象专注于实现目标对象需要的业务操作,代理对象用来添加其他的操作)
    • 注意:开发时尽量使用接口的编程,
      • (1) 对接口创建代理优于对类创建代理,因为会产生更加松耦合的系统。
      • (2) 标记为 final 的方法不能够被通知。spring 是为目标类产生子类。任何需要被通知的方法都被复写,将通知织入。final 方法是不允许重写的
      • (3) spring 只支持方法连接点,不支持属性的连接点
  • JDK 动态代理 :必须要实现接口,才能产生代理对象,如果没有接口不能使用动态代理技术
  • cglib代理 (没有实现任何接口):第三方代理技术,可以对任何类实现代理,
    原理:对目标对象进行继承代理,如果目标被final修饰,则无法被cglib代理。

准备通知:

  • 在这里插入图片描述

AOP 配置

  • 要进行 AOP 编程,首先我们要在 spring 的配置文件中引入 aop 命名空间:
    在这里插入图片描述
  • Spring 提供了两种切面使用方式,实际工作中我们可以选用其中一种:
    • 基于 XML 配置方式进行 AOP 开发
    • 基于注解方式进行 AOP 开发

AOP(基于xml文件配置):

  • 1、导入spring相关的jar包
    • 在这里插入图片描述
  • 2、准备目标对象
      • 在这里插入图片描述
  • 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 id="userService" class="beike.spring.day2.UserServiceImpl"></bean>
</beans>

  • 测试:
public class App {
	public static void main(String[] args) {
		// 直接访问目标对象
		// 使用代理对象访问目标对象,当在 spring 的容器中添加 <aop>
		ApplicationContext ac = new ClassPathXmlApplicationContext("beans.xml");
		UserService userService = (UserService)ac.getBean("userService");
		userService.saveUser(" 超级强 ", "123");
		userService.updateUser(" 超级强 ", "123");
		userService.deleteUser(" 超级强 ");
		String str = userService.findUser();
		System.out.println("str:"+str);
	}
}
  • 3、准备通知
  • .
    • 任何通知方法可以将第一个参数定义为org.aspectj.lang.JoinPoint类型 (
    • 环绕通知需要定义第一个参数为ProceedingJoinPoint类型, 它是 JoinPoint 的一个子类)。
    • JoinPoint 接口提供了一系列有用的方法, * 比如 getArgs()(返回方法参数)、
    • getThis()(返回代理对象)、
    • getTarget()(返回目标)、
    • getSignature()(返回正在被通知的方法相关信息)、
    • toString() (打印出正在被通知的方法的有用信息)。

前置通知

  • **前置通知示例:**继续使用刚刚的例子,新增切面类BeforeSecurity
public class BeforeSecurity {
	public void checkSecurity(JoinPoint joinPoint){
		System.out.println(" 正在执行验证 ...");
		Object [] args = joinPoint.getArgs();
		if(args!=null && args.length>0){
			for(Object o:args){
				System.out.println(" 参数:"+o);
			}
		}
		System.out.println(" 代理对象:"+joinPoint.getThis(). getClass());
		System.out.println(" 目标对象:"+joinPoint.getTarget(). getClass());
		System.out.println(" 访问目标对象方法的名称:"+joinPoint. getSignature().getName());
		System.out.println("spring容器中定义切入点的表达式: "+joinPoint.toString());
	}
}
  • 前置通知xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<bean id="userService" class="beike.spring.day2.UserServiceImpl"></bean>
	<bean id=" beforeSecurity " class="beike.spring.day2.BeforeSecurity"></bean>
	<!-- spring的aop编程,所有的操作(切面、通知、切入点)都要放置到 aop:config -->
	<aop:config>
<!--
				声明切入点
				id:切入点的惟一标识 expression:切入点的表达式语言,指定项目中哪个类哪个方法作为切入点 -->
				<aop:pointcut id="save" expression="execution(* beike.spring.day2.UserServiceImpl.saveUser(..))" />
				<aop:pointcut id="update" expression="execution(* beike.spring.day2.UserServiceImpl.updateUser(..))" />

          <!--定义个切面,此时切面的类具有了灵魂 id:惟一标识 ref:注入对象-->
          <aop:aspect id="aa" ref=" beforeSecurity ">
				
				<!--
				定义通知(切入点要做的事情) 前置通知:在访问目标对象方法之前,先执行通知定义的方法
				特点:如果代理对象(切面)中的方法(通知)抛出异常,此时不会执行目标对象通知
				* pointcut-ref:注入切入点,这样才能让切入点关联
				* method: 指定切面中定义的通知的方法 -->
                <aop:before pointcut-ref="save" method="checkSecurity"/>
                <aop:before pointcut-ref="update" method="checkSecurity"/>
          </aop:aspect>
    </aop:config>
</beans>
  • 重新运行

后置通知

  • 后置通知和前置通知的配置是一样的,后置通知为:< aop:after-returning />
    新增切面类AfterSecurity
public class AfterSecurity {
	public void checkSecurity(JoinPoint joinPoint,Object returnValue){
		System.out.println(" 正在执行验证 ...");
		Object [] args = joinPoint.getArgs();
		if(args!=null && args.length>0){
			for(Object o:args){
				System.out.println(" 参数:"+o);
			}
		}
		System.out.println(" 代理对象:"+joinPoint.getThis(). getClass());
		System.out.println(" 目标对象:"+joinPoint.getTarget(). getClass());
		System.out.println(" 访问目标对象方法的名称:"+joinPoint. getSignature().getName());
		System.out.println("spring 容 器 中 定 义 切 入 点 的 表 达 式: "+joinPoint.toString());
		System.out.println(" 目标对象方法的返回值:"+returnValue);
	}
}

  • 后置通知的xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">
	<bean id="userService" class="beike.spring.day2.UserServiceImpl"></bean>
	<bean id="afterSecurity" class="beike.spring.day2.AfterSecurity"></bean>

	<!-- spring的aop编程,所有的操作(切面、通知、切入点)都要放置到 aop:config -->
	<aop:config>
<!--
				声明切入点
				id:切入点的惟一标识 expression:切入点的表达式语言,指定项目中哪个类哪个方法作为切入点 -->
				<aop:pointcut id="save" expression="execution(* beike.spring.day2.UserServiceImpl.saveUser(..))" />
				<aop:pointcut id="find" expression="execution(* beike.spring.day2.UserServiceImpl.findUser(..))" />

          <!--定义个切面,此时切面的类具有了灵魂 id:惟一标识 ref:注入对象-->
          <aop:aspect id="aa" ref="afterSecurity">
				
	<!--
     后置通知:在访问目标对象方法之后,再执行通知定义的方法
特点:
1:如果在目标对象中抛出异常,此时不会执行通知
     2:因为是先执行目标对象中的方法,再执行通知,所以能不能在通知中获取目标对象的方法的返回值?能
     第一步:在spring容器中定义:returning="returnValue"
     第二步:在通知的方法中的第二个参数可以指定 Object 类型,
     例如 public void checkSecurity(JoinPoint joinPoint,Object returnValue){}
     总结: 参数一定要放在第二个
          参数一定一个 Object 参数的属性名称一定要
          参数的位置类型与 spring 容器中定义的 returning 相一致
     -->                
                
                <aop:after-returning pointcut-ref="save" method="checkSecurity" returning="returnValue"/>
				<aop:after-returning pointcut-ref="find" method="checkSecurity" returning="returnValue"/>
          </aop:aspect>
    </aop:config>
</beans>

异常通知

  • 新增切面类 Security
public class ThrowsSecurity {
	public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){
		System.out.println(" 正在执行验证 ...");
		Object [] args = joinPoint.getArgs();
		if(args!=null && args.length>0){
			for(Object o:args){
				System.out.println(" 参数:"+o);
			}
		}
		System.out.println(" 代理对象:"+joinPoint.getThis(). getClass());
		System.out.println(" 目标对象:"+joinPoint.getTarget(). getClass());
		System.out.println(" 访问目标对象方法的名称:"+joinPoint. getSignature().getName());
		System.out.println("spring容器中定义切入点的表达式: "+joinPoint.toString());
		System.out.println(" 目标对象方法抛出的异常是: "+throwingValue);
	}
}

异常通知的xml容器中配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

	<bean id="userService" class="beike.spring.day2.UserServiceImpl"></bean>
	<bean id="throwsSecurity" class="beike.spring.day2.ThrowsSecurity"></bean>
	
	<!-- spring的aop编程,所有的操作(切面、通知、切入点)都要放置到 aop:config -->
	<aop:config>
<!--
				声明切入点
				id:切入点的惟一标识 expression:切入点的表达式语言,指定项目中哪个类哪个方法作为切入点 -->
				<aop:pointcut id="save" expression="execution(* beike.spring.day2.UserServiceImpl.saveUser(..))" />
				<aop:pointcut id="find" expression="execution(* beike.spring.day2.UserServiceImpl.findUser(..))" />

          <!--定义个切面,此时切面的类具有了灵魂 id:惟一标识 ref:注入对象-->
          <aop:aspect id="aa" ref="throwsSecurity">
				
				<!--
				异常通知:在访问目标对象方法之后,前提是目标对象
				方法中抛出异常,此时才会执行通知定义的方法
				 特点:
				1:只有目标对象方法中抛出异常,此时才会执行通知
				2:在通知的方法中捕获异常
					第一步:在 spring 容器中定义
					第二步:在通知的方法中的第二个参数的位置,可以指定,例如 public void checkSecurity(JoinPoint joinPoint,Throwable throwingValue){
						* 要求一:获取目标对象抛出的异常的参数要放置在第二个参数的位置
						* 要求二:类型必须指定Throwable 类型
						* 要求三:Throwable 对应的属性值要和 spring 容器中定义的 throwing="throwingValue" 值要相匹配
                  -->
                
                <aop:after-throwing pointcut-ref="save" method="checkSecurity" throwing="throwingValue"/>
                <aop:after-throwing pointcut-ref="find" method="checkSecurity" throwing="throwingValue"/>
          </aop:aspect>
    </aop:config>
</beans>
  • 在saveUser()中制造异常:int i=1/0; 运行测试类

环绕通知

  • 新增切面类AroundSecurity
package beike.spring.day2;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
public class AroundSecurity {
	/*
	 * 环绕通知类型
	 * 通知的第一个参数必须是 ProceedingJoinPoint 类型。
	 * 在通知体内,调用 ProceedingJoinPoint的proceed()方法会导致 后台的连接点方法执行。
	 * proceed 方法也可能会被调用并且传入一个 Object[]对象-该数组中的 值将被作为方法执行时的参数。
	 * 环绕通知将通知的方法的返回值要定义成 Object 类型,只有这样才能将目标对象方法的返回值,传递给客户端 
	 */
	public void checkSecurity(ProceedingJoinPoint joinPoint){
		System.out.println(" 正在执行环绕验证 ...");
		Object [] args = joinPoint.getArgs();
		if(args!=null && args.length>0){
			for(Object o:args){
				System.out.println("参数:"+o);
			}
		}
		System.out.println(" 代理对象:"+joinPoint.getThis(). getClass());
		System.out.println(" 目标对象:"+joinPoint.getTarget(). getClass());
		System.out.println(" 访问目标对象方法的名称:"+joinPoint. getSignature().getName());
		System.out.println("spring容器中定义切入点的表达式: "+joinPoint.toString());
		 //在调用joinPoint.proceed()方法之前面执行的相当于后置通知
		 Object value = null;
         try {
                value = joinPoint.proceed();
         } catch (Throwable e) {
                e.printStackTrace();
         }
         //在调用joinPoint.proceed()方法之后面执行的相当于后置通知
         System.out.println("环绕通知:在目标执行后调用:"+joinPoint. getTarget().getClass()+"获取返回结果 :"+value);
	}
}

  • 环绕通知的xml文件配置:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
       http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context-4.3.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

	<bean id="userService" class="beike.spring.day2.UserServiceImpl"></bean>
	<bean id="aroundSecurity" class="beike.spring.day2.AroundSecurity"></bean>
	
	<!-- spring的aop编程,所有的操作(切面、通知、切入点)都要放置到 aop:config -->
	<aop:config>
<!--
				声明切入点
				id:切入点的惟一标识 expression:切入点的表达式语言,指定项目中哪个类哪个方法作为切入点 -->
				<aop:pointcut id="save" expression="execution(* beike.spring.day2.UserServiceImpl.saveUser(..))" />
				<aop:pointcut id="find" expression="execution(* beike.spring.day2.UserServiceImpl.findUser(..))" />

          <!--定义个切面,此时切面的类具有了灵魂 id:惟一标识 ref:注入对象-->
          <aop:aspect id="aa" ref="aroundSecurity">
				
				<!--
			    环绕通知:
                最后一种通知是环绕通知。环绕通知在一个目标对象方法执行之前和之后执行。
			    它使得通知有机会 在一个方法执行之前和执行之后运 行。而且它可以决定这个方法在什么时候执行,如何执行,甚至是否执行。
			    环绕通知经常在某线程安全的环境下,你需要在一个 方法执行之前和之后共享某种状态的时候使用。
			    请尽量使用最简单的满足你需求的通知。(比如如果 简单的前置通知也可以适用的情况下不要使用环绕通知)。
                -->
				
				<aop:around pointcut-ref="save" method="checkSecurity"/>
                <aop:around pointcut-ref="find" method="checkSecurity"/>
          </aop:aspect>
    </aop:config>
</beans>

AOP(基于注解文件配置):

  • 注解说明:
    在这里插入图片描述
使用注解实现步骤:
  • 为了在 Spring 配置中使用 @AspectJ 切面,你首先必须启用 Spring 对 @AspectJ 切面配置的支持,并确保自动代理
    • 在这里插入图片描述
  • 1、导包
  • 2、准备目标对象
    - 在这里插入图片描述
  • 3、准备通知
package com.ujiuye.day05.aop.aop_annotation.security;

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

import java.util.Arrays;

@Component
@Aspect  // 表示在 spring 容器中定义:<aop:aspect id=" " ref=" ">
public class Security {
    /**
     * 声明定义目标对象方法的切入点(指的是方法)
     * 要求:
     * 1、方法的修饰符任意
     * 2、要求方法没有返回值
     * 3、方法的名称任意(一个类中,不允许出现同名方法,表明的切入点的id惟一)
     * 4、方法没有参数
     * 5、方法体为空
     */
    // 相当于 spring 容器中定义:<aop:pointcut id="save"
    // expression="execution(*  。。saveUser(..))" />
    @Pointcut(value = "execution(* com.ujiuye.day05.aop.aop_annotation.service.UserServiceImpl.saveUser(..))")
    public  void save(){}

    /*
	 * 相当于spring容器中定义:
	<aop:before pointcut-ref="save" method="checkSecurity"/>
	*/
    @Before(value="save()")
    public void checkSecurity() {
        System.out.println(" 正在执行前置通知安全校验....");
    }
    @After(value="save()")
    public void checkAfter() {
        System.out.println(" 已通过....");
    }
    @AfterThrowing(pointcut = "execution(* com.ujiuye.day05.aop.aop_annotation.service.UserServiceImpl.saveUser(..))",
            throwing = "e")
    public void afterThrowing(JoinPoint jp, RuntimeException e) {
        System.out.println(jp.getSignature().getName() + " 方法发生异常:" + e);
        System.out.println("异常信息已记录日志!");
    }
    @Around("execution(* com.ujiuye.day05.aop.aop_annotation.service.UserServiceImpl.*(..))")
    public Object aroundLogger(ProceedingJoinPoint jp)  {
        System.out.println("调用 " + jp.getTarget() + " 的 " + jp.getSignature().getName()
                + " 方法。方法入参:" + Arrays.toString(jp.getArgs()));
        try {
            Object result = jp.proceed();
            System.out.println("调用 " + jp.getTarget() + " 的 "
                    + jp.getSignature().getName() + " 方法。方法返回值:" + result);
            return result;
        } catch (Throwable e) {
            System.out.println(jp.getSignature().getName() + " 方法发生异常:" + e);

        } finally {
            System.out.println(jp.getSignature().getName() + " 方法结束执行。");
        }
        return null;
    }

}


  • 4、配置xml文件
    - 在这里插入图片描述
  • 5、代码测试
    - 在这里插入图片描述

Spring 事务处理 :

  • 事务是一组操作的执行单元,相对于数据库操作来讲,事务管理的是一组 SQL 指令, 比如增加,修改,删除等, 仅用四个词解释事务(ACID)
    • atomic( 原子性 ): 要么都发生,要么都不发生。
    • consistent( 一致性 ): 数据应该不被破坏。(例如A、B两人总金额100元,发生转账操作总金额还是100元(即事务处理后数据一致!))
    • Isolate( 隔离性 ): 用户间操作不相混淆
    • Durable( 持久性 ): 永久保存 , 例如保存到数据库中等
简单使用步骤:
  • 1、配置xml文件 spring-config.xml
  • 2、数据库层

spring-config.xml代码:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd">

    <!-- 启用 Spring 对 @AspectJ 切面配置的支持 -->
    <aop:aspectj-autoproxy />

    <context:annotation-config />
    <context:component-scan base-package="com.ujiue.dao"></context:component-scan>
    <!-- 配置 DBCP 连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <!-- 连接池启动时的初始值 -->
        <property name="initialSize" value="1"/>
        <!-- 连接池的最大值 -->
        <property name="maxActive" value="500"/>
        <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用
        不到的连接慢慢释放一部分,一直减少到 maxIdle 为止 -->
        <property name="maxIdle" value="2"/>
        <!-- 最小空闲值 . 当空闲的连接数少于该值时,连接池就会预申请 一些连接,以避免洪峰来时再申请而造成的性能开销 -->
        <property name="minIdle" value="1"/>
    </bean>

    <!-- 创建 spring  供的 Jdbc 模板,用来操作数据库 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
</beans>

数据库层代码:

 @Override
    public Student getById(int sid) {

        RowMapper<Student> mapper=new RowMapper<Student>() {
            @Override
            public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                Student student=new Student();
                student.setSid(resultSet.getInt("sid"));
                student.setSname(resultSet.getString("sname"));
                return student;
            }
        };
        String sql="SELECT * FROM student WHERE sid=?";
        Object[] param={sid};
        return jdbcTemplate.queryForObject(sql,param,mapper);
    }

    @Override
    public List<Student> list() {
        RowMapper<Student> mapper=new RowMapper<Student>() {
            @Override
            public Student mapRow(ResultSet resultSet, int i) throws SQLException {
                Student student=new Student();
                student.setSid(resultSet.getInt("sid"));
                student.setSname(resultSet.getString("sname"));
                return student;
            }
        };
        String sql="SELECT * FROM student";
        return jdbcTemplate.query(sql,mapper);
    }

Jdbc事务深入:

传播行为 : 定义关于客户端和被调用方法的事物边界
  • 事务的传播级别定义的是事务的控制范围,主要是父子事务之间的相互影响关系;
  • 事务的隔离级别定义的是事务读写的控制范围,主要是两个事务之间的相互影响关系
  • 传播级别:REQUIRED、SUPPORTS、REQUIRES_NEW 等 7 种,其默认为 REQUIRED。
  • 七个事务传播行为
    • 在这里插入图片描述
隔离级别
  • 在这里插入图片描述

注意:

  • 如果在 Dao中或者在 Service抛出异常,需要抛出运行时异常。 这里:任何 RuntimeException 将触发事务回滚但是任何捕获了异常即(try catch) 将 不触发事务回滚
  • 在开发过程中,要想对事务进行控制,如果需要抛出异常的时候,需要在 Service 层或者 Dao 层的方法中抛出运行时异常,这样才能进行事务控制
添加声明式事务处理(XML)操作:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <!-- 启用 Spring 对 @AspectJ 切面配置的支持 -->
    <aop:aspectj-autoproxy />

    <context:annotation-config />
    <context:component-scan base-package="com.ujiuye.springJdbc.jdbcTest02"/>

    <!-- 配置 DBCP 连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mythird-day06?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <!-- 连接池启动时的初始值 -->
        <property name="initialSize" value="1"/>
        <!-- 连接池的最大值 -->
        <property name="maxActive" value="500"/>
        <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用
        不到的连接慢慢释放一部分,一直减少到 maxIdle 为止 -->
        <property name="maxIdle" value="2"/>
        <!-- 最小空闲值 . 当空闲的连接数少于该值时,连接池就会预申请 一些连接,以避免洪峰来时再申请而造成的性能开销 -->
        <property name="minIdle" value="1"/>
    </bean>


    <!-- 创建 spring  供的 Jdbc 模板,用来操作数据库 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>


    <!-- 添加声明式事务处理(XML)begin -->
    <!--
    声明事务管理器
    ( 相当于切面,也就是说在切面的类中定义的是事务控制 )
     -->
    <bean id="trManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
    <!--
	tx:advice:定义通知(通知关联事务,通知的方法要放置到切面的类中)
		* id:通知方法的在容器中的惟一标识
		* transaction-manager:事务管理,指定对哪个事务进行管理
	tx:method: 对切入点方法的细化
		* name:表示切入点中的方法名称
	* 实例:
		* name="saveAccount":表示业务层的saveAccount的方法 (1)
		* name="save*":表示业务层的以save开头的方法(2)
		* name="*":表示业务层的所有的方法(3)
		* 执行的优先级:(1)>(2)>(3)
	* isolation="DEFAULT":事务的隔离级别,数据库用什么隔离 级别,该方法就使用什么隔离级别
	* propagation="REQUIRED":如果业务方法存在一个事务中,
	       直接使用这个事务,如果业务方法运行不存在一个事务,自己会开启一个新的事 务
	* read-only="false":可写数据库,比如增、删、改的操作 read-only=”true”:只读数据库,比如查询的操作
	-->
    <tx:advice id="trManagerAdvice" transaction-manager="trManager">
        <tx:attributes>
            <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="*" read-only="true"/>
        </tx:attributes>
    </tx:advice>

    <!-- 定义切入点点,而且使得切入点要关联通知 -->
    <aop:config>
        <!--aop:pointcut: 定义切入点,(业务层的方法就是切入点)-->
        <aop:pointcut id="servicePointCut"
                      expression="execution(* com.ujiuye.springJdbc.jdbcTest02..*.*(..))" />
    <!-- execution(* com.ujiuye.springJdbc.jdbcTest02..*.*(..))
        任意返回值类型   包名jdbcTest02下任意类任意方法 任意参数       -->

        <!--aop:advisor:配置切入点要关联通知(事务控制业务层的方法) -->
        <aop:advisor advice-ref="trManagerAdvice" pointcut-ref="servicePointCut"/>
    </aop:config>
</beans>
使用注解(@Transactional) 声明事务处理

xml文件配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
		http://www.springframework.org/schema/beans/spring-beans-4.3.xsd
		http://www.springframework.org/schema/context
		http://www.springframework.org/schema/context/spring-context-4.3.xsd
		http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
       http://www.springframework.org/schema/tx
       http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

    <!-- 启用 Spring 对 @AspectJ 切面配置的支持 -->
    <aop:aspectj-autoproxy />

    <context:annotation-config />
    <context:component-scan base-package="com.ujiuye.springJdbc.jdbcTest03"/>

    <!-- 配置 DBCP 连接池 -->
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
        <property name="url" value="jdbc:mysql://localhost:3306/mythird-day06?useUnicode=true&amp;characterEncoding=utf8"></property>
        <property name="username" value="root"></property>
        <property name="password" value="root"></property>
        <!-- 连接池启动时的初始值 -->
        <property name="initialSize" value="1"/>
        <!-- 连接池的最大值 -->
        <property name="maxActive" value="500"/>
        <!-- 最大空闲值.当经过一个高峰时间后,连接池可以慢慢将已经用
        不到的连接慢慢释放一部分,一直减少到 maxIdle 为止 -->
        <property name="maxIdle" value="2"/>
        <!-- 最小空闲值 . 当空闲的连接数少于该值时,连接池就会预申请 一些连接,以避免洪峰来时再申请而造成的性能开销 -->
        <property name="minIdle" value="1"/>
    </bean>

    <!-- 创建 spring  供的 Jdbc 模板,用来操作数据库 -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!-- 添加声明式事务处理(XML)begin -->
    <!--
    声明事务管理器
    ( 相当于切面,也就是说在切面的类中定义的是事务控制 )
     -->
    <bean id="trManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
    </bean>
   
    <!-- 主要是这句 注解需要在业务层的类的上面或者方法的上面添加一个注解@ Transcational -->
    <tx:annotation-driven transaction-manager="trManager"/>
    <!-- 添加声明式事务处理(注解的方式) end-->
</beans>

在业务层的类中定义

package com.ujiuye.springJdbc.jdbcTest03.service;


import com.ujiuye.springJdbc.jdbcTest03.accountDao.AccountDao;
import com.ujiuye.springJdbc.jdbcTest03.accountDao.InAccountDao;
import com.ujiuye.springJdbc.jdbcTest03.pojo.Account;
import com.ujiuye.springJdbc.jdbcTest03.pojo.InAccount;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
/**
 * @Transcational 注解
 * 放置到类的上面,默认对类中的所有方法都有效,而且默认是可写的操作
 * 放置到方法的上面:此时方法级别的事务,会覆盖类级别的事务
 * 注解方式总结:
 * 1:在业务层的类上面定义:@Transactional(readOnly=true),表示业务层的所有方法都只读。
 * 2:在业务层类的方法上定义
 *  @Transactional(isolation=Isolation. DEFAULT,propagation=Propagation.REQUIRED,readOnly=false):
 *      表示方法可写, 例如新增、删除、修改的方法上:
 * 3:查询的方法无需定义 @Transactional,因为表示只读即可 *
 * 4、方法级别的事务,会覆盖类级别的事务
 * 5:注解 @Transactional 放置到类的上面,默认对类中的所有方法都有效,
 *  如果不添加任何属性,此时默认是可写的操作,即类中的所有方法都可写。

 *相当于 spring 容器中定义:
 *  <tx:advice id="trManagerAdvice" transaction-manager="trManager">
        <tx:attributes>
            <tx:method name="save*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="update*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="delete*" isolation="DEFAULT" propagation="REQUIRED" read-only="false"/>
            <tx:method name="*" read-only="true"/>
        </tx:attributes>
    </tx:advice>
	 定义切入点点,而且使得切入点要关联通知
	<aop:config>
	//        aop:pointcut: 定义切入点,(业务层的方法就是切入点)
	    <aop:pointcut id="servicePointCut" expression="execution(* beike.spring.trasiction..*.*(..))" />
	//        aop:advisor:配置切入点要关联通知(事务控制业务层的方法)
	    <aop:advisor advice-ref="trManagerAdvice" pointcut-ref="servicePointCut"/>
	</aop:config>
*/

@Transactional(readOnly = true)
@Service("accountService")
public class AccountServiceImpl implements AccountService {
    @Resource
    private InAccountDao inAccountDao;
    @Resource
    private AccountDao accountDao;

    //方法级别的事务,会覆盖类级别的事务
    @Override
    @Transactional(isolation= Isolation.DEFAULT, propagation= Propagation.REQUIRED,readOnly=false)
    public int saveAccount(InAccount inAccount) {
        inAccountDao.save(inAccount);
        Account account = accountDao.findAccountById(inAccount.getAccountid());
        account.setBalance( account.getBalance()+inAccount.getInbalance());
        return accountDao.updateAccount(account);
    }
}
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!