【后端Java】Spring框架体系(一)

13 篇文章 0 订阅

Spring5框架

一、Spring框架概述

1. Spring框架简介

  • Spring 是轻量级的开源的 JavaEE 框架
  • Spring 可以解决企业应用开发的复杂性
  • Spring 有两个核心部分:IOC 和 AOP
    (1)IOC:控制反转,把创建对象过程交给 Spring 进行管理
    (2)AOP:面向切面,不修改源代码进行功能增强
  • Spring 特点
    • 方便解耦,简化开发
    • AOP 编程支持
    • 方便程序测试
    • 方便和其他框架进行整合
    • 方便进行事务操作
    • 降低 API 开发难度

二、IOC

1. IOC(Inversion Of Control)的概念

  • 控制反转,把对象创建和对象之间的调用过程,交给 Spring 进行管理
  • 使用 IOC 目的:为了耦合度降低

2. IOC 底层原理

  • xml解析、工厂模式、反射
    请添加图片描述

3. IOC 接口

  • IOC 思想基于 IOC 容器完成,IOC 容器底层就是对象工厂
  • Spring 提供 IOC 容器实现两种方式:(两个接口)
    • BeanFactory:IOC 容器基本实现,是 Spring 内部的使用接口,不提供开发人员进行使用
      • 加载配置文件时候不会创建对象,在获取对象(使用)才去创建对象
    • ApplicationContext:BeanFactory 接口的子接口,提供更多更强大的功能,一般由开发人员进行使用
      • 加载配置文件时候就会把在配置文件对象进行创建
  • ApplicationContext 接口有实现类请添加图片描述

4. IOC 操作 Bean 管理

  • 什么是 Bean 管理
    • Bean 管理指的是两个操作
      • Spring 创建对象
      • Spirng 注入属性
  • Bean 管理操作有两种方式
    • 基于 xml 配置文件方式实现
    • 基于注解方式实现

5. IOC 操作 Bean 管理(基于xml方式)

  • 基于 xml 方式创建对象
    • <bean id="user" class="com.alex.spring5.User"></bean>
    • 在 spring 配置文件中,使用 bean 标签,标签里面添加对应属性,就可以实现对象创建
    • 在 bean 标签有很多属性,介绍常用的属性
      • id 属性:唯一标识
      • class 属性:类全路径(包类路径)
    • 创建对象时候,默认也是执行无参数构造方法完成对象创建
  • 基于 xml 方式注入属性
    • DI:依赖注入,就是注入属性

    • 第一种注入方式:使用set方法进行注入

      • 创建类,定义属性和对应的set方法

        public class Book {
        	// 创建属性
        	private String bname;
        	private String bauthor;
        	// 创建属性对应的set方法
        	public void setBname(String bname) {
        		this.bname = bname;
        	}
        	public void setBauthor(String bauthor) {
        		this.bauthor = bauthor;
        	}
        }
        
      • 在Spring配置文件配置对象创建,配置属性注入(set方法注入属性)

        <bean id="book" class="com.alex.spring5.Book">
        	<!-- 
        		使用property完成属性注入
        			name:类里面属性名称
        			value:向属性注入的值
        	-->
        	<property name="bname" value="三国演义"></property>
        	<property name="bauthor" value="水浒传"></property>
        </bean>
        
    • 第二种注入方式:使用有参构造进行注入

      • 创建类,定义属性,创建属性对应有参数构造方法

        public class Book {
        	// 创建属性
        	private String bname;
        	private String bauthor;
        	// 有参构造
        	public Book(String bname, String bauthor) {
        		this.bname = bname;
        		this.bauthor = bauthor;
        	}
        }
        
      • 在Spring配置文件中进行配置(有参构造注入属性)

        <bean id="book" class="com.alex.spring5.Book">
        	<constructor-arg name="bname" value="三国演义"></constructor-arg>
        	<constructor-arg name="bauthor" value="水浒传"></constructor-arg>
        </bean>
        

6. IOC 操作 Bean 管理(xml 注入其他类型属性)

  • 字面量

    • null 值

      <!-- null 值 --> 
      <property name="address"> 
      	<null/>
      </property>
      
    • 属性值包含特殊符号

      <!-- 
      	属性值包含特殊符号
      	1 把<>进行转义 &lt; &gt;
      	2 把带特殊符号内容写到 CDATA
      -->
      <property name="address"> 
      	<value><![CDATA[<<南京>>]]></value>
      </property>
      
  • 注入属性 - 外部 bean

    • 创建两个类 service 类和 dao 类
    • 在 service 调用 dao 里面的方法
    • 在 spring 配置文件中进行配置
public class UserService {
	//创建 UserDao 类型属性,生成 set 方法
	private UserDao userDao;
	public void setUserDao(UserDao userDao) {
		this.userDao = userDao;
	}
	public void add() {
		System.out.println("service add...............");
		userDao.update();
	} 
}
<!--1 service 和 dao 对象创建--> 
<bean id="userService" class="com.alex.spring5.service.UserService">
	<!--注入 userDao 对象
		name 属性:类里面属性名称
		ref 属性:创建 userDao 对象 bean 标签 id 值
	-->
	<property name="userDao" ref="userDaoImpl"></property>
</bean> 
<bean id="userDaoImpl" class="com.atguigu.spring5.dao.UserDaoImpl"></bean>
  • 注入属性 - 内部 bean
    • 一对多关系:部门和员工
      • 一个部门有多个员工,一个员工属于一个部门
      • 部门是一,员工是多
    • 在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示
// 部门类
public class Dept {
	private String dname;
	public void setDname(String dname) {
		this.dname = dname;
	} 
}
// 员工类
public class Emp {
	private String ename;
	private String gender;
	// 员工属于某一个部门,使用对象形式表示
	private Dept dept;
	public void setDept(Dept dept) {
		this.dept = dept;
	}
	public void setEname(String ename) {
		this.ename = ename;
	}
	public void setGender(String gender) {
		this.gender = gender;
	} 
}
<!--内部 bean--> 
<bean id="emp" class="com.alex.spring5.bean.Emp">
	<!--设置两个普通属性-->
	<property name="ename" value="lucy"></property> 
	<property name="gender" value=""></property>
	<!--设置对象类型属性--> 
	<property name="dept"> 
		<bean id="dept" class="com.alex.spring5.bean.Dept"> 
			<property name="dname" value="财务部"></property>
		</bean>
	</property>
</bean>
  • 注入属性 - 级联赋值
    • 第一种方法
<!--级联赋值--> 
<bean id="emp" class="com.alex.spring5.bean.Emp">
	<!--设置两个普通属性--> 
	<property name="ename" value="lucy"></property> 
	<property name="gender" value=""></property>
	<!--级联赋值--> 
	<property name="dept" ref="dept"></property>
</bean> 
<bean id="dept" class="com.spring5.bean.Dept">
	<property name="dname" value="财务部"></property>
</bean>
  • 第二种方法
    请添加图片描述
<bean id="emp" class="com.alex.spring5.bean.Emp">
	<!--设置两个普通属性--> 
	<property name="ename" value="lucy"></property> 
	<property name="gender" value=""></property>
	<!--级联赋值--> 
	<property name="dept" ref="dept"></property>
	<property name="dept.dname" value="技术部"></property>
</bean> 
<bean id="dept" class="com.spring5.bean.Dept">
	<property name="dname" value="财务部"></property>
</bean>

7. IOC 操作 Bean 管理(xml 注入集合属性)

  • 注入数组类型属性
  • 注入 List 集合类型属性
  • 注入 Map 集合类型属性
public class Stu {
	//1 数组类型属性
	private String[] courses;
	//2 list 集合类型属性
	private List<String> list;
	//3 map 集合类型属性
	private Map<String,String> maps;
	//4 set 集合类型属性
	private Set<String> sets;

	// 学生所学多门课程
	pricate List<Course> courseList;
	public void setCourseList(List<Course> courseList) {
		this.courseList = courseList;
	}
	public void setSets(Set<String> sets) {
		this.sets = sets;
	}
	public void setCourses(String[] courses) {
		this.courses = courses;
	}
	public void setList(List<String> list) {
		this.list = list;
	}
	public void setMaps(Map<String, String> maps) {
		this.maps = maps;
	}
}
<!--1 集合类型属性注入--> 
<bean id="stu" class="com.alex.spring5.collectiontype.Stu">
	<!--数组类型属性注入--> 
	<property name="courses"> 
		<array> 
			<value>java 课程</value> 
			<value>数据库课程</value>
		</array>
	</property>
	<!--list 类型属性注入--> 
	<property name="list"> 
		<list>
			<value>张三</value> 
			<value>小三</value>
		</list>
	</property>
	<!--map 类型属性注入--> 
	<property name="maps"> 
		<map>
			<entry key="JAVA" value="java"></entry> 
			<entry key="PHP" value="php"></entry>
		</map>
	</property>
	<!--set 类型属性注入--> 
	<property name="sets"> 
		<set>
			<value>MySQL</value> 
			<value>Redis</value>
		</set>
	</property>
</bean>
  • 在集合里面设置对象类型值
<!--创建多个 course 对象--> 
<bean id="course1" class="com.alex.spring5.collectiontype.Course"> 
	<property name="cname" value="Spring5框架"></property>
</bean> 
<bean id="course2" class="com.alex.spring5.collectiontype.Course"> 
	<property name="cname" value="MyBatis框架"></property>
</bean>
<!--注入 list 集合类型,值是对象--> 
<property name="courseList"> 
	<list>
		<ref bean="course1"></ref> 
		<ref bean="course2"></ref>
	</list>
</property>
  • 把集合注入部分提取出来
<!-- (1)在Spring配置文件中引入名称空间 util -->
<?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:p="http://www.springframework.org/schema/p"
	   xmlns:util="http://www.springframework.org/schema/util"
	   xsi:schemaLocation="http://www.springframework.org/schema/beans 
		http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/util 
		http://www.springframework.org/schema/util/spring-util.xsd">

<!-- (2)使用 util 标签完成 list 集合注入提取 -->
<!-- 1 提取 list 集合类型属性注入 --> 
<util:list id="bookList">
	<value>西游记</value> 
	<value>三国演义</value> 
	<value>红楼梦</value>
</util:list>
<!-- 2 提取 list 集合类型属性注入使用 --> 
<bean id="book" class="com.alex.spring5.collectiontype.Book"> 
	<property name="list" ref="bookList"></property>
</bean>

8. IOC 操作 Bean管理(FactoryBean)

  • Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
    • 普通 bean:在配置文件中定义 bean 类型就是返回类型
    • 工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
      • 第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
      • 第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> {
	//定义返回 bean
	@Override
	public Course getObject() throws Exception {
		Course course = new Course();
		course.setCname("abc");
		return course;
	}
	@Override
	public Class<?> getObjectType() {
		return null; 
	}
	@Override
	public boolean isSingleton() {
		return false; 
	} 
}

@Test
public void test3() {
	ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
	Course course = context.getBean("myBean", Course.class);
	System.out.println(course);
}
<bean id="myBean" class="com.alex.spring5.factorybean.MyBean"></bean>

9. IOC 操作 Bean 管理(bean 作用域)

  • 在 Spring 里面,设置创建 bean 实例是单实例还是多实例
  • 在 Spring 里面,默认情况下,bean 是单实例对象
    在这里插入图片描述
  • 输出:
    • com.alex.spring5.collectiontype.Book@5d11354a
    • com.alex.spring5.collectiontype.Book@5d11354a
    • 地址相同
  • 如何设置单实例还是多实例
    • 在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
    • scope 属性值
      • 第一个值 默认值,singleton,表示是单实例对象
      • 第二个值 prototype,表示是多实例对象
<bean id="" class="com.alex.spring5.collectiontype.Book" scope="prototype">
	<property name="list" ref="bookList"></property>
</bean>
  • 输出:
    • com.alex.spring5.collectiontype.Book@5d11354a
    • com.alex.spring5.collectiontype.Book@7a13623f
    • 地址不相同
  • singleton 和 prototype 区别
    • 第一 singleton 单实例,prototype 多实例
    • 第二 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象,设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象

10. IOC 操作 Bean 管理(生命周期)

  • 生命周期:从对象创建到对象销毁的过程
  • bean生命周期
    (1)通过构造器创建bean实例(无参数构造)
    (2)为bean的属性设置值和对其他bean引用(调用set方法)
    (3)调用bean的初始化的方法(需要进行配置初始化的方法)
    (4)bean可以使用了(对象获取到了)
    (5)当容器关闭时候,调用bean的销毁的方法(需要进行配置销毁的方法)
  • 演示 bean 生命周期
public class Orders {
	//无参数构造
	public Orders() {
		System.out.println("第一步 执行无参数构造创建 bean 实例");
	}
	private String oname;
	public void setOname(String oname) {
		this.oname = oname;
		System.out.println("第二步 调用 set 方法设置属性值");
	}
	//创建执行的初始化的方法
	public void initMethod() {
		System.out.println("第三步 执行初始化的方法");
	}
	//创建执行的销毁的方法
	public void destroyMethod() {
		System.out.println("第五步 执行销毁的方法");
	} 
}

@Test
public void testBean3() {
	// ApplicationContext context =
	// new ClassPathXmlApplicationContext("bean4.xml");
	ClassPathXmlApplicationContext context =
		new ClassPathXmlApplicationContext("bean4.xml");
	Orders orders = context.getBean("orders", Orders.class);
	System.out.println("第四步 获取创建 bean 实例对象");
	System.out.println(orders);
	//手动让 bean 实例销毁
	context.close();
}

// 打印
/**
	第一步 执行无参数构造创建 bean 实例
	第二步 调用 set 方法设置属性值
	第三步 执行初始化的方法
	第四步 获取创建 bean 实例对象
	com.alex.spring5.bean.Orders@192d3742
	第五步 执行销毁的方法
*/
<bean id="orders" class="com.alex.spring5.bean.Orders" initmethod="initMethod" destroy-method="destroyMethod">
 	<property name="oname" value="手机"></property>
</bean>
  • bean 的后置处理器,bean 生命周期有七步
    • 通过构造器创建 bean 实例(无参数构造)
    • 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
    • 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
    • 调用 bean 的初始化的方法(需要进行配置初始化的方法)
    • 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
    • bean 可以使用了(对象获取到了)
    • 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
  • 演示添加后置处理器效果
// 创建类,实现接口 BeanPostProcessor,创建后置处理器
public class MyBeanPost implements BeanPostProcessor {
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
			System.out.println("在初始化之前执行的方法");
			return bean;
	}
	@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("在初始化之后执行的方法");
		return bean;
	} 
}
<!-- 配置后置处理器 -->
<bean id="myBeanPost" class="com.alex.spring5.bean.MyBeanPost"></bean>
/**
	打印:
		第一步 执行无参数构造创建 bean 实例
		第二步 调用 set 方法设置属性值
		在初始化之前执行的方法
		第三步 执行初始化的方法
		在初始化之后执行的方法
		第四步 获取创建 bean 实例对象
		com.alex.spring5.bean.Orders@192d3742
		第五步 执行销毁的方法
*/

11. IOC 操作 Bean 管理(xml 自动装配)

  • 什么是自动装配
    • 根据指定装配规则(属性名称或者属性类型),Spring 自动将匹配的属性值进行注入
  • 演示自动装配过程
    • 根据属性名称自动注入

      <!--
      	实现自动装配
      	bean 标签属性 autowire,配置自动装配
      	autowire 属性常用两个值:
      		byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
      		byType 根据属性类型注入
      -->
      <bean id="emp" class="com.alex.spring5.autowire.Emp" autowire="byName">
      	<!--<property name="dept" ref="dept"></property>-->
      </bean> 
      <bean id="dept" class="com.alex.spring5.autowire.Dept"></bean>
      
    • 根据属性类型自动注入

      <!--
      	实现自动装配
      		bean 标签属性 autowire,配置自动装配
      		autowire 属性常用两个值:
      			byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
      			byType 根据属性类型注入
      -->
      <bean id="emp" class="com.alex.spring5.autowire.Emp" autowire="byType">
      	<!--<property name="dept" ref="dept"></property>-->
      </bean> 
      <bean id="dept" class="com.alex.spring5.autowire.Dept"></bean>
      

12. IOC 操作 Bean 管理(外部属性文件)

  • 直接配置数据库信息
    (1)配置德鲁伊连接池
    (2)引入德鲁伊连接池依赖 jar 包
    在这里插入图片描述
<!--直接配置连接池--> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> 
	<property name="driverClassName" value="com.mysql.jdbc.Driver"></property> 
	<property name="url" value="jdbc:mysql://localhost:3306/userDb"></property> 
	<property name="username" value="root"></property> 
	<property name="password" value="root"></property>
</bean>
  • 引入外部属性文件配置数据库连接池
    • 创建外部属性文件,properties 格式文件,写数据库信息
      在这里插入图片描述
    • 把外部 properties 属性文件引入到 spring 配置文件中
      * 引入 context 名称空间
<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:util="http://www.springframework.org/schema/util"
	   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/util 
http://www.springframework.org/schema/util/spring-util.xsd
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd"> 
<!-- 在 spring 配置文件使用标签引入外部属性文件 -->
<!--引入外部属性文件--> 
<context:property-placeholder location="classpath:jdbc.properties"/>
<!--配置连接池--> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> 
	<property name="driverClassName" value="${prop.driverClass}"></property> 			  
	<property name="url" value="${prop.url}"></property> 
	<property name="username" value="${prop.userName}"></property> 
	<property name="password" value="${prop.password}"></property>
</bean>

13. IOC 操作 Bean 管理(基于注解方式)

  • 什么是注解
    (1)注解是代码特殊标记,格式:@注解名称(属性名称=属性值, 属性名称=属性值…)
    (2)使用注解,注解作用在类上面,方法上面,属性上面
    (3)使用注解目的:简化 xml 配置

  • Spring 针对 Bean 管理中创建对象提供注解

    • @Component
    • @Service
    • @Controller
    • @Repository
    • 上面四个注解功能是一样的,都可以用来创建 bean 实例
  • 基于注解方式实现对象创建

    • 引入依赖
      在这里插入图片描述

    • 开启组件扫描

      <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: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"> 
      <!-- 开启组件扫描
      	1 如果扫描多个包,多个包使用逗号隔开
      	2 扫描包上层目录
      -->
      <context:component-scan base-package="com.alex"></context:component-scan>
      
    • 创建类,在类上面添加创建对象注解

      //在注解里面 value 属性值可以省略不写,默认值是类名称,首字母小写
      // UserService -- userService
      @Component(value = "userService") //<bean id="userService" class=".."/>
      public class UserService {
      	public void add() {
      		System.out.println("service add.......");
      	} 
      }
      
      public void testService() {
      	ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
      	UserService userService = context.getBean("userService", UserService.class);
      	System.out.println(userService);
      	userService.add();
      }
      
  • 开启组件扫描细节配置

    <!--
    	示例 1
    	use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
    	context:include-filter ,设置扫描哪些内容
    -->
    <context:component-scan base-package="com.alex" use-defaultfilters="false"> 
    	<!-- 只扫描带Controller的类 -->		
    	<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
    <!--
    	示例 2
    	下面配置扫描包所有内容
    	context:exclude-filter: 设置哪些内容不进行扫描
    -->
    <context:component-scan base-package="com.atguigu">
    	<!-- 不扫描带Controller的类 -->	 
    	<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    
  • 基于注解方式实现属性注入

    • @Autowired:根据属性类型进行自动装配
      • 把 service 和 dao 对象创建,在 service 和 dao 类添加创建对象注解
      • 在 service 注入 dao 对象,在 service 类添加 dao 类型属性,在属性上面使用注解
    @Service
    public class UserService {
    	//定义 dao 类型属性
    	//不需要添加 set 方法
    	//添加注入属性注解
    	@Autowired
    	private UserDao userDao;
    	
    	public void add() {
    		System.out.println("service add.......");
    		userDao.add();
    	} 
    }
    
    • @Qualifier:根据名称进行注入
      • 这个@Qualifier 注解的使用,和上面@Autowired 一起使用
    //定义 dao 类型属性
    //不需要添加 set 方法
    //添加注入属性注解
    @Autowired //根据类型进行注入
    @Qualifier(value = "userDaoImpl1") //根据名称进行注入(userDao有多个实现类时使用)
    private UserDao userDao;
    
    • @Resource:可以根据类型注入,可以根据名称注入(是javax中的)
    //@Resource //根据类型进行注入
    @Resource(name = "userDaoImpl1") //根据名称进行注入
    private UserDao userDao;
    
    • @Value:注入普通类型属性
    @Value(value = "abc")
    private String name;
    
  • 完全注解开发

    • 创建配置类,替代 xml 配置文件
    @Configuration //作为配置类,替代 xml 配置文件
    @ComponentScan(basePackages = {"alex"})
    // 等价于替代
    // <context:component-scan base-package="com.alex"></context:component-scan>
    public class SpringConfig {
    }
    
    • 编写测试类
    @Test
    public void testService2() {
    	//加载配置类
    	ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    	UserService userService = context.getBean("userService", UserService.class);
    	System.out.println(userService);
    	userService.add();
    }
    

三、AOP

1. AOP(Aspect Orient Programming)概念

  • 面向切面编程(方面),利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
  • 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
  • 使用登录例子说明AOP
    请添加图片描述

2. AOP 底层原理

  • AOP底层使用动态代理
    • 有两种情况动态代理
      • 第一种 有接口情况,使用 JDK 动态代理
        • 创建接口实现类代理对象,增强类的方法
          请添加图片描述
      • 第二种 没有接口情况,使用 CGLIB 动态代理
        • 创建子类的代理对象,增强类的方法
          请添加图片描述

3. AOP(JDK动态代理)

  • 使用JDK动态代理,使用Proxy类里面的方法创建代理对象
    请添加图片描述

  • 调用newProxyInstance方法
    请添加图片描述

    • 方法有三个参数:
      • 第一参数,类加载器
      • 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口
      • 第三参数,实现这个接口InvocationHandler,创建代理对象,写增强的部分
  • 编写 JDK 动态代理代码

    • 创建接口,定义方法

      public interface UserDao{
      	public int add(int a, int b);
      	public String update(String id);
      }
      
    • 创建接口实现类,实现方法

      public class UserDaoImpl implements UserDao {
      	@Override
      	public int add(int a, int b) {
      		return a+b;
      	}
      	@Override
      	public String update(String id) {
      		return id;
      	} 
      }
      
    • 使用 Proxy 类创建接口代理对象

      public class JDKProxy {
      	public static void main(String[] args) {
      		//创建接口实现类代理对象
      		Class[] interfaces = {UserDao.class};
      		// Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new InvocationHandler() {
      			// @Override
      			// public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      			// return null;
      			// }
      		// });
      		UserDaoImpl userDao = new UserDaoImpl();
      		UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), 	interfaces, new UserDaoProxy(userDao));
      		int result = dao.add(1, 2);
      		System.out.println("result:"+result);
      	} 
      }
      
      //创建代理对象代码
      class UserDaoProxy implements InvocationHandler {
      	//1 把创建的是谁的代理对象,把谁传递过来
      	//有参数构造传递
      	private Object obj;
      	public UserDaoProxy(Object obj) {
      		this.obj = obj;
      	}
      	//增强的逻辑
      	@Override
      	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      		//方法之前
      		System.out.println("方法之前执行...."+method.getName()+" :传递的参数..."+ Arrays.toString(args));
      		//被增强的方法执行
      		Object res = method.invoke(obj, args);
      		//方法之后
      		System.out.println("方法之后执行...."+obj);
      		return res;
      	} 
      }
      

4. AOP(术语)

  • 连接点
    • 类里面哪些方法可以被增强,这些方法称为连接点
  • 切入点
    • 实际被真正增强的方法,称为切入点
  • 通知(增强)
    • 实际增强的逻辑部分称为通知(增强)
    • 通知有多种类型
      • 前置通知
      • 后置通知
      • 环绕通知
      • 异常通知
      • 最终通知
  • 切面
    • 是动作
    • 把通知应用到切入点的过程

5. AOP操作(准备工作)

  • Spring 框架一般都是基于 AspectJ 实现 AOP 操作
    • AspectJ 不是 Spring 组成部分,独立 AOP 框架,一般把 AspectJ 和 Spirng 框架一起使用,进行 AOP 操作
  • 基于 AspectJ 实现 AOP 操作
    (1)基于 xml 配置文件实现
    (2)基于注解方式实现(使用)
  • 在项目工程里面引入 AOP 相关依赖
    请添加图片描述
  • 切入点表达式
    • 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
    • 语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
    • 举例 1:对 com.atguigu.dao.BookDao 类里面的 add 进行增强
      • execution(* com.atguigu.dao.BookDao.add(…))
    • 举例 2:对 com.atguigu.dao.BookDao 类里面的所有的方法进行增强
      • execution(* com.atguigu.dao.BookDao.* (…))
    • 举例 3:对 com.atguigu.dao 包里面所有类,类里面所有方法进行增强
      • execution(* com.atguigu.dao.*.* (…))

6. AOP操作(AspectJ注解☆)

  • 创建类,在类里面定义方法
// 被增强的类
public class User {
	public void add() {
		System.out.println("add.......");
	} 
}
  • 创建增强类(编写增强逻辑)

    • 在增强类里面,创建方法,让不同方法代表不同通知类型
    //增强的类
    public class UserProxy {
    	public void before() {	//前置通知
    		System.out.println("before......");
    	} 
    }
    
  • 进行通知的配置

    • 在 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:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
    http://www.springframework.org/schema/beans/spring-beans.xsd
    http://www.springframework.org/schema/context 
    http://www.springframework.org/schema/context/spring-context.xsd
    http://www.springframework.org/schema/aop 
    http://www.springframework.org/schema/aop/spring-aop.xsd">
    <!-- 开启注解扫描 --> 
    <context:component-scan basepackage="com.alex.spring5.aopanno"></context:component-scan>
    
    • 使用注解创建 User 和 UserProxy 对象
    @Component
    public class User
    @Component
    public class UserProxy
    
    • 在增强类上面添加注解 @Aspect
    //增强的类
    @Component
    @Aspect //生成代理对象
    public class UserProxy {
    
    • 在 spring 配置文件中开启生成代理对象
    <!-- 开启 Aspect 生成代理对象--> 
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
  • 配置不同类型的通知

    • 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {
	//前置通知
	//@Before 注解表示作为前置通知
	@Before(value = "execution(* com.alex.spring5.aopanno.User.add(..))")
	public void before() {
		System.out.println("before.........");
	}
	//后置通知(返回通知)返回结果后执行 有异常不执行
	@AfterReturning(value = "execution(* com.alex.spring5.aopanno.User.add(..))")
	public void afterReturning() {
		System.out.println("afterReturning.........");
	}
	//最终通知 方法后就执行 有异常也执行
	@After(value = "execution(* com.alex.spring5.aopanno.User.add(..))")
	public void after() {
		System.out.println("after.........");
	}
	//异常通知 方法有异常才执行
	@AfterThrowing(value = "execution(* com.alex.spring5.aopanno.User.add(..))")
	public void afterThrowing() {
		System.out.println("afterThrowing.........");
	}
	//环绕通知 在方法前后执行
	@Around(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
	public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		System.out.println("环绕之前.........");
		//被增强的方法执行
		proceedingJoinPoint.proceed();
		System.out.println("环绕之后.........");
	} 
}
public void testAopAnno() {
		//加载配置类
		ApplicationContext context = new AnnotationConfigApplicationContext("bean.xml");
		User user = context.getBean("user", User.class);
		user.add();
}

// 环绕之前 ...
// before ...
// add ...
// 环绕之后 ...
// after ...
// afterReturning ...
  • 相同的切入点抽取
//相同切入点抽取
@Pointcut(value = "execution(* com.alex.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before 注解表示作为前置通知
@Before(value = "pointdemo()")
public void before() {
	System.out.println("before.........");
}
  • 有多个增强类多同一个方法进行增强,设置增强类优先级
    • 在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy
  • 完全使用注解开发
    • 创建配置类,不需要创建 xml 配置文件
@Configuration
@ComponentScan(basePackages = {"com.alex"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigAop {
}

7. AOP操作(AspectJ 配置文件)

  • 创建两个类,增强类和被增强类,创建方法
  • 在 spring 配置文件中创建两个类对象
<!--创建对象--> 
<bean id="book" class="com.spring5.aopxml.Book"></bean> 
<bean id="bookProxy" class="com.alex.spring5.aopxml.BookProxy"></bean>
  • 在 spring 配置文件中配置切入点
<!--配置 aop 增强--> 
<aop:config>
	<!--切入点--> 
	<aop:pointcut id="p" expression="execution(* com.alex.spring5.aopxml.Book.buy(..))"/>
	<!--配置切面--> 
	<aop:aspect ref="bookProxy">
		<!--增强作用在具体的方法上--> 
		<aop:before method="before" pointcut-ref="p"/>
	</aop:aspect>
</aop:config>

四、JdbcTemplate

1. JdbcTemplate 概述和准备工作

  • Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
  • 准备工作
    在这里插入图片描述
  • 在 spring 配置文件配置数据库连接池
<!-- 数据库连接池 --> 
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close"> 
	<property name="url" value="jdbc:mysql:///user_db" />
	<property name="username" value="root" />
	<property name="password" value="root" />
	<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
  • 配置 JdbcTemplate 对象,注入 DataSource
<!-- JdbcTemplate 对象 --> 
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
	<!--注入 dataSource--> 
	<property name="dataSource" ref="dataSource"></property>
</bean>
  • 创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象
<!-- 组件扫描 --> 
<context:component-scan base-package="com.alex"></context:component-scan>
// Service
@Service
public class BookService {
	//注入 dao
	@Autowired
	private BookDao bookDao; 
}
// Dao
@Repository
public class BookDaoImpl implements BookDao {
	//注入 JdbcTemplate
	@Autowired
	private JdbcTemplate jdbcTemplate; 
}

2. JdbcTemplate 操作数据库(添加)

  • 对应数据库创建实体类
    在这里插入图片描述
  • 编写 service 和 dao
    (1)在 dao 进行数据库添加操作
    (2)调用 JdbcTemplate 对象里面 update 方法实现添加操作
    在这里插入图片描述
    • 有两个参数
      • 第一个参数:sql 语句
      • 第二个参数:可变参数,设置 sql 语句值
@Repository
public class BookDaoImpl implements BookDao {
	//注入 JdbcTemplate
	@Autowired
	private JdbcTemplate jdbcTemplate;
	//添加的方法
	@Override
	public void add(Book book) {
		//1 创建 sql 语句
		String sql = "insert into t_book values(?,?,?)";
		//2 调用方法实现
		Object[] args = {book.getUserId(), book.getUsername(), book.getUstatus()};
		int update = jdbcTemplate.update(sql,args);
		System.out.println(update);
	}
}
  • 测试类
@Test
public void testJdbcTemplate() {
	ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
	BookService bookService = context.getBean("bookService", BookService.class);
	Book book = new Book();
	book.setUserId("1");
	book.setUsername("java");
	book.setUstatus("a");
	bookService.addBook(book);
}

请添加图片描述

3. JdbcTemplate 操作数据库(修改和删除)

  • 修改
@Override
public void updateBook(Book book) {
	String sql = "update t_book set username=?,ustatus=? where user_id=?";
	Object[] args = {book.getUsername(), book.getUstatus(),book.getUserId()};
	int update = jdbcTemplate.update(sql, args);
	System.out.println(update);
}
  • 删除
@Override
public void delete(String id) {
	String sql = "delete from t_book where user_id=?";
	int update = jdbcTemplate.update(sql, id);
	System.out.println(update);
}

4. JdbcTemplate 操作数据库(查询返回某个值)

  • 查询表里面有多少条记录,返回是某个值
  • 使用 JdbcTemplate 实现查询返回某个值代码
    在这里插入图片描述
    • 有两个参数
      • 第一个参数:sql 语句
      • 第二个参数:返回类型 Class
//查询表记录数
@Override
public int selectCount() {
	String sql = "select count(*) from t_book";
	Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
	return count;
}

5. JdbcTemplate 操作数据库(查询返回对象)

  • 场景:查询图书详情
  • JdbcTemplate 实现查询返回对象
    在这里插入图片描述
    • 有三个参数
      • 第一个参数:sql 语句
      • 第二个参数:RowMapper,是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
      • 第三个参数:sql 语句值(参数)
//查询返回对象
@Override
public Book findBookInfo(String id) {
	String sql = "select * from t_book where user_id=?";
	//调用方法
	Book book = jdbcTemplate.queryForObject(sql, new 
	BeanPropertyRowMapper<Book>(Book.class), id);
	return book;
}

6. JdbcTemplate 操作数据库(查询返回集合)

  • 场景:查询图书列表分页
  • 调用 JdbcTemplate 方法实现查询返回集合
    在这里插入图片描述
    • 有三个参数
      • 第一个参数:sql 语句
      • 第二个参数:RowMapper 是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
      • 第三个参数:sql 语句值
//查询返回集合
@Override
public List<Book> findAllBook() {
	String sql = "select * from t_book";
	//调用方法
	List<Book> bookList = jdbcTemplate.query(sql, new 
	BeanPropertyRowMapper<Book>(Book.class));
	return bookList;
}

7. JdbcTemplate 操作数据库(批量操作)

  • 批量操作:操作表里面多条记录
  • JdbcTemplate 实现批量添加操作
    在这里插入图片描述
    • 有两个参数
      • 第一个参数:sql 语句
      • 第二个参数:List集合,添加多条记录数据
//批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
	String sql = "insert into t_book values(?,?,?)";
	int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
	System.out.println(Arrays.toString(ints));
}

//批量添加测试
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"3","java","a"};
Object[] o2 = {"4","c++","b"};
Object[] o3 = {"5","MySQL","c"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用批量添加
bookService.batchAdd(batchArgs);

8. JdbcTemplate 操作数据库(批量修改删除)

  • JdbcTemplate 实现批量修改操作
//批量修改
@Override
public void batchUpdateBook(List<Object[]> batchArgs) {
	String sql = "update t_book set username=?,ustatus=? where user_id=?";
	int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
	System.out.println(Arrays.toString(ints));
}

//批量修改
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"java0909","a3","3"};
Object[] o2 = {"c++1010","b4","4"};
Object[] o3 = {"MySQL1111","c5","5"};
batchArgs.add(o1);
batchArgs.add(o2);
batchArgs.add(o3);
//调用方法实现批量修改
bookService.batchUpdate(batchArgs);
  • JdbcTemplate 实现批量删除操作
//批量删除
@Override
public void batchDeleteBook(List<Object[]> batchArgs) {
	String sql = "delete from t_book where user_id=?";
	int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);
	System.out.println(Arrays.toString(ints));
}

//批量删除
List<Object[]> batchArgs = new ArrayList<>();
Object[] o1 = {"3"};
Object[] o2 = {"4"};
batchArgs.add(o1);
batchArgs.add(o2);
//调用方法实现批量删除
bookService.batchDelete(batchArgs);

五、事务

1. 事务概念

  • 什么是事务?
    (1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操
    作都失败
    (2)典型场景:银行转账
    • lucy 转账 100 元 给 mary
    • lucy 少 100,mary 多 100
  • 事务四个特性(ACID)
    (1)原子性:过程不可分割
    (2)一致性:操作前后总量不变
    (3)隔离性:多事务操作时不会产生影响
    (4)持久性:提交之后表中数据真正发生变化

2. 事务操作(搭建事务操作环境)

在这里插入图片描述

  • 创建数据库表,添加记录
    在这里插入图片描述
  • 创建 service,搭建 dao,完成对象创建和注入关系
    • service 注入 dao,在 dao 注入 JdbcTemplate,在 JdbcTemplate 注入 DataSource
@Service
public class UserService {
	//注入 dao
	@Autowired
	private UserDao userDao; 
}
@Repository
public class UserDaoImpl implements UserDao {
	@Autowired
	private JdbcTemplate jdbcTemplate; 
}
  • 在 dao 创建两个方法:多钱和少钱的方法,在 service 创建方法(转账的方法)
@Repository
public class UserDaoImpl implements UserDao {
	@Autowired
	private JdbcTemplate jdbcTemplate;
	//lucy 转账 100 给 mary
	//少钱
	@Override
	public void reduceMoney() {
		String sql = "update t_account set money=money-? where username=?";
		jdbcTemplate.update(sql,100,"lucy");
	}
	//多钱
	@Override
	public void addMoney() {
		String sql = "update t_account set money=money+? where username=?";
		jdbcTemplate.update(sql,100,"mary");
	} 
}
@Service
public class UserService {
	//注入 dao
	@Autowired
	private UserDao userDao;
	//转账的方法
	public void accountMoney() {
		//lucy 少 100
		userDao.reduceMoney();
		//mary 多 100
		userDao.addMoney();
	} 
}
  • 上面代码,如果正常执行没有问题的,但是如果代码执行过程中出现异常,有问题
    在这里插入图片描述
  • 问题如何解决? – 使用事务
  • 事务操作过程
    在这里插入图片描述

3. 事务操作(Spring 事务管理介绍)

  • 事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
  • 在 Spring 进行事务管理操作
    • 有两种方式:编程式事务管理和声明式事务管理(使用)
  • 声明式事务管理
    (1)基于注解方式(使用)
    (2)基于 xml 配置文件方式
  • 在 Spring 进行声明式事务管理,底层使用 AOP 原理
  • Spring 事务管理 API
    • 提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
      在这里插入图片描述
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

柠檬小帽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值