Spring概述
* 简单了解框架
- 框架,即framework。其实就是某种应用的半成品,就是一组组件,供你选用完成你自己的系统。
- 框架是对特定应用领域中的应用系统的部分设计和实现的整体结构。
- 因为软件系统发展到今天已经很复杂了,特别是服务器端软件,涉及到的知识,内容,问题太多。在某些方面使用别人成熟的框架,就相当于让别人帮你完成一些基础工作,你只需要集中精力完成系统的业务逻辑设计。而且框架一般是成熟,稳健的,他可以处理系统很多细节问题,比如,事务处理,安全性,数据流控制等问题。还有框架一般都经过很多人使用,所以结构很好,所以扩展性也很好,而且它是不断升级的,你可以直接享受别人升级代码带来的好处。
1.1 Spring概述
-
Spring是一个开源框架
-
Spring为简化企业级开发而生,使用Spring,JavaBean就可以实现很多以前要靠EJB才能实现的功能。同样的功能,在EJB中要通过繁琐的配置和复杂的代码才能够实现,而在Spring中却非常的优雅和简洁。
-
Spring是一个IOC(DI)和AOP容器框架。
-
Spring的优良传统:
- 非侵入式:基于Spring开发的应用中的对象可以不依赖于Spring的API
- 依赖注入:DI——Dependency Injection,反转控制(IOC)最经典的实现
- 面向切面编程:Aspect Oriented Programming——AOP
- 容器:Spring是一个容器,因为它包含并且管理应用对象的生命周期
- 组件化:Spring实现了使用简单的组件配置组合成一个复杂的应用。在 Spring 中可以使用XML和Java注解组合这些对象。
- 一站式:在IOC和AOP的基础上可以整合各种企业应用的开源框架和优秀的第三方类库(实际上Spring 自身也提供了表述层的SpringMVC和持久层的Spring JDBC)。
-
Spring 模块:
1.2 搭建spring运行时环境
-
导入jar包
Spring自身jar包spring-framework-4.0.0.RELEASE\libs目录下
spring-beans-4.0.0.RELEASE.jar
spring-context-4.0.0.RELE2ASE.jar
spring-core-4.0.0.RELEASE.jar
spring-expression-4.0.0.RELEASE.jarcommons-logging-1.1.1.jar
-
在Spring Tool Suite工具中通过如下步骤创建Spring的配置文件
① File->New->Spring Bean Configuration File
② 为文件取名字 例如:applicationContext.xml
1.3 hello spring
目标:使用Spring创建对象,为属性赋值
-
创建Student类
public class Student { private Integer id; private String name; private String gender; public void setId(Integer id) { this.id = id; } public void setName(String name) { this.name = name; } public void setGender(String gender) { this.gender = gender; } }
-
创建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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <bean id="student" class="com.lipu.spring.hello.Student"> <property name="id" value="1001"></property> <property name="name" value="小明"></property> <property name="gender" value="男"></property> </bean> </beans>
-
测试:通过Spring的IOC容器创建Student类实例
package com.lipu.spring.hello; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Test { public static void main(String[] args) { ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml"); Student student = applicationContext.getBean("student",Student.class); System.out.println(student); } }
IOC容器和Bean的配置
2.1 IOC简介
-
IOC(Inversion of control): 反转控制
- 在应用程序中的组件需要获取资源时,传统的方式是组件主动的从容器中获取所需要的资源,在这样的模式下开发人员往往需要知道在具体容器中特定资源的获取方式,增加了学习成本,同时降低了开发效率。
- 反转控制的思想完全颠覆了应用程序组件获取资源的传统方式:反转了资源的获取方向——改由容器主动的将资源推送给需要的组件,开发人员不需要知道容器是如何创建资源对象的,只需要提供接收资源的方式即可,极大的降低了学习成本,提高了开发的效率。这种行为也称为查找的被动形式。
-
(Dependency Injection): 依赖注入
- IOC的另一种表述方式:即组件以一些预先定义好的方式(例如:setter 方法)接受来自于容器的资源注入。相对于IOC而言,这种表述更直接。
- 总结: IOC 就是一种反转控制的思想, 而DI是对IOC的一种具体实现。
-
IOC容器在spring中的实现
前提: Spring中有IOC思想, IOC思想必须基于 IOC容器来完成, 而IOC容器在最底层实质上就是一个对象工厂.
-
在通过IOC容器读取Bean的实例之前,需要先将IOC容器本身实例化。
-
Spring提供了IOC容器的两种实现方式
① BeanFactory:IOC容器的基本实现,是Spring内部的基础设施,是面向Spring本身的,不是提供给开发人员使用的。
② ApplicationContext:BeanFactory的子接口,提供了更多高级特性。面向Spring的使用者,几乎所有场合都使用ApplicationContext而不是底层的BeanFactory。
-
-
ApplicationContext的主要实现类
- ClassPathXmlApplicationContext: 对应类路径下xml格式的配置文件
- FileSystemXmlApplicationContext: 对应文件系统中的xml格式的配置文件
- 在初始化时就创建单例的bean,也可以通过配置的方式指定创建的Bean是多实例的。
-
ConfigurationApplicationContext
- 是ApplicationContext的子接口,包含一些扩展方法
- refresh()和close()让ApplicationContext具有启动、关闭和刷新上下文的能力。
-
WebApplicationContext
专门为WEB应用而准备的,它允许从相对于WEB根目录的路径中完成初始化工作
-
容器结构图
2.2 IOC操作Bean管理(基于xml方式)
-
基于xml方式创建对象
<bean id = "Book" class = "com.lipu.spring.Book"></bean>
- 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
- 在bean标签里有很多属性常用以下两种
id属性:唯一标识符
class属性:类全路径(包类路径) - 创建对象时,默认也是使用无参构造方式完成对象创建。
- 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
-
基于xml方式注入属性
- DI依赖注入,就是注入属性
-
第一种注入方式:使用set
①创建类,定义属性和对应的set方法public class Book { private String title; private String author; public void setTitle(String title) { this.title = title; } public void setAuthor(String author) { this.author = author; } }
② 在spring配置文件配置对象创建,配置属性注入
<!-- 使用set方法注入属性 --> <bean id = "book" class="com.lipu.spring.hello.Book"> <!-- 使用property完成属性注入 name:类里面属性名称 author:向属性注入的值 --> <property name = "title" value = "易筋经"></property> <property name = "author" value = "达摩老祖"></property> </bean>
-
第二种注入方式:使用有参构造
① 创造类,定义属性,创造属性对应的有参构造方法
public class Order { private String name; private String address; public Order(String name, String address) { super(); this.name = name; this.address = address; } }
② 在spring配置文件中进行配置
<!-- 有参构造注入属性 --> <bean id="order" class="com.lipu.spring.hello.Order"> <constructor-arg name = "name" value = "电脑"></constructor-arg> <constructor-arg name = "address" value = "china"></constructor-arg> </bean>
-
p名称空间注入(了解)
使用p名称空间注入,可以简化xml配置方式
① 在配置文件中添加p名称空间
<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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
② 进行属性注入,在bean标签里进行操作
<!-- 使用p名称空间注入 --> <bean id = "book2" class = "com.lipu.spring.hello.Book" p:title="九阳神功" p:author="无名氏"></bean>
2.3 IOC操作bean管理(xml注入其他类型属性)
-
字面量
①注入null
<!-- null值 --> <property name = "title"> <null></null> </property>
② 属性值包含特殊符号
<!-- 属性值包含特殊符号 1. 把<>进行转义: < > 2. 把带特殊符号的值写到CDATA中 --> <property name = "author"> <value><![CDATA[<<南京>>]]></value> </property>
-
注入属性-外部bean
① 创建UserService类和UserDao类
② 在service类中调用dao类
UserService:
public class UserService { private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void show() { System.out.println("service..."); userDao.show(); } }
UserDao:
public class UserDao { public void show() { System.out.println("Dao..."); } }
③ 在spring配置文件中进行配置
<bean id = "userService" class = "com.lipu.spring.xmlinjection.UserService"> <property name = "userDao" ref = "userDao"></property> </bean> <bean id = "userDao" class = "com.lipu.spring.xmlinjection.UserDao"> </bean>
-
注入属性-内部bean
①一对多关系:部门和员工
Dept类:
public class Dept { private String dname; public void setDname(String dname) { this.dname = dname; } }
Emp类:
public class Emp { private String ename; private Dept dept; public void setEname(String ename) { this.ename = ename; } public void setDept(Dept dept) { this.dept = dept; } }
② 在spring配置文件中进行配置
<!-- 注入属性-内部bean --> <bean id = "emp" class = "com.lipu.spring.xmlinjection.Emp"> <property name = "ename" value = "张三"></property> <property name = "dept"> <!-- 内部bean --> <bean class = "com.lipu.spring.xmlinjection.Dept"> <property name = "dname" value = "开发部"></property> </bean> </property> </bean>
-
注入属性-级联赋值
-
第一种写法
同外部bean<略>
-
第二种写法
<!-- 注入属性-级联赋值内部bean --> <bean id = "emp2" class = "com.lipu.spring.xmlinjection.Emp"> <property name = "ename" value = "李四"></property> <!-- 必须要先复制dept,在复制dept内的属性 --> <property name = "dept" ref = "dept"></property> <!-- Ept类中需要有getDept()方法 --> <property name = "dept.dname" value = "保安部"></property> </bean> <bean id = "dept" class = "com.lipu.spring.xmlinjection.Dept"></bean>
-
2.4 IOC操作bean管理(xml注入集合属性)
-
数组、list、map、set集合中注入字面量(内部bean)
<!-- 数组、list、map、set --> <bean id = "student" class = "com.lipu.spring.collectionInjection.Student"> <!-- 数组注入 --> <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 = "map"> <map> <entry key = "java" value = "Java"></entry> <entry key = "php" value = "Php"></entry> </map> </property> <!-- set注入 --> <property name = "set"> <set> <value>MySQL</value> <value>Redis</value> </set> </property> </bean>
-
把集合注入部分提取出来
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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"> <util:list id = "bookList"> <value>九阳神功</value> <value>九阴真经</value> </util:list> <property name = "list" ref = "bookList"></property>
2.4 IOC操作bean管理(FactoryBean)
-
Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
-
普通 bean:在配置文件中定义 bean 类型就是返回类型
-
工厂 bean:在配置文件定义 bean 类型可以和返回类型不一样
第一步 创建类,让这个类作为工厂 bean,实现接口 FactoryBean
第二步 实现接口里面的方法,在实现的方法中定义返回的 bean 类型
public class MyBean implements FactoryBean<Course> { @Override public Course getObject() throws Exception { Course course = new Course("java"); return course; } @Override public Class<?> getObjectType() { // TODO Auto-generated method stub return null; } @Override public boolean isSingleton() { // TODO Auto-generated method stub return false; } }
2.4 IOC操作bean管理(Bean作用域)
-
在 Spring 里面,设置创建 bean 实例是单实例还是多实例
-
Spring 里面,默认情况下, bean 是单实例对象
-
如何设置单实例还是多实例
(1)在 spring 配置文件 bean 标签里面有属性( scope)用于设置单实例还是多实例
(2) scope 属性值
第一个值 默认值, singleton,表示是单实例对象
第二个值 prototype,表示是多实例对象
(3) singleton 和 prototype 区别
第一 singleton 单实例, prototype 多实例
第二 设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象
设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建 对象,在调用getBean 方法时候创建多实例对象
-
bean的后置处理器(生命周期分为七部)
① 通过构造器创建 bean 实例(无参数构造)
② 为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
③ 把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization
④ 调用 bean 的初始化的方法(需要进行配置初始化的方法)
⑤ 把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization
⑥ bean 可以使用了(对象获取到了)
⑦ 当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
-
添加后置处理器
public class myBeanPost implements BeanPostProcessor { @Override public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { System.out.println("在bean初始化之前执行"); return bean; } @Override public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException { System.out.println("在bean初始化之后执行"); return bean; } }
配置xml
<bean id = "myBeanPost" class = "com.lipu.spring.factorybeantest.MyBeanPost"></bean>
2.4 IOC操作bean管理(Bean生命周期)
-
生命周期: 从对象创建到对象销毁的过程
-
bean 生命周期(五步)
① 通过构造器创造bean实例(无参构造器)
② 为bean的属性设置值和对其他属性的引用(调用set方法)
③ 调用bean的初始化方法(需要进行配置初始化的方法)
④ bean的使用过程(获取到了对象)<bean id = "myBean" class = "com.lipu.spring.factorybeantest.MyBean" init-method="initMethod" destroy-method="destroyMethod">
⑤ 当容器关闭时,调用bean的销毁方法(需要进行配置bean的销毁方法)
<bean id = "myBean" class = "com.lipu.spring.factorybeantest.MyBean" init-method="initMethod" destroy-method="destroyMethod">
2.4 IOC操作bean管理(xml自动装配)
-
什么是自动装配
根据指定装配规则(属性名称或者属性类型), Spring 自动将匹配的属性值进行注入
-
根据属性名称自动注入
<!--实现自动装配 bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值: byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 byType 根据属性类型注入 --> <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName">
-
根据类型自动注入
<!--实现自动装配 bean 标签属性 autowire,配置自动装配 autowire 属性常用两个值: byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样 byType 根据属性类型注入 --> <bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType">
2.4 IOC操作bean管理(外部属性文件)
-
直接配置数据库信息
① 配置德鲁伊连接池
<!--直接配置连接池--> <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>
② 引入德鲁伊连接池以来jar包
-
把外部properties文件引入到xml配置文件中
① 引入context命名空间
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
② 在spring配置文件中使用标签引入外部属性文件
<!-- 引入外部属性文件 --> <context:property-placeholder location="classpath:jdbc.properties"/>
③ 配置数据库连接池
<!-- 配置连接池 --> <bean id = "dataSource" class = "com.alibaba.druid.pool.DruidDataSource"> <property name = "driverClassName" value = "${jdbc.driverClass}"></property> <property name = "url" value = "${jdbc.url}"></property> <property name = "username" value = "${jdbc.username}"></property> <property name = "password" value = "${jdbc.password}"></property> </bean>
2.4 IOC操作bean管理(基于注解方式)
-
什么是注解
注解是代码特殊标记,格式: @注解名称(属性名称=属性值, 属性名称=属性值…)
使用注解,注解作用在类上面,方法上面,属性上面
使用注解目的:简化 xml 配置
-
Spring 针对 Bean 管理中创建对象提供注解
@Component @Service @Controller @Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
-
基于注解方式实现对象创建
第一步:导入依赖
spring-aop-4.0.0.RELEASE.jar
第二步:开启组件扫描
<!--开启组件扫描 1 如果扫描多个包,多个包使用逗号隔开 2 扫描包上层目录 --> <context:component-scan base-package="com.lipu.spring.annotationtest"></context:component-scan>
细节一:include
<!--示例 1 use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter context:include-filter ,设置扫描哪些内容 --> <context:component-scan base-package="com.lipu.spring.annotationtest" use-default-filters="false"> <context:include-filter type="annotation" expression = "org.springframework.stereotype.Controller"/> </context:component-scan>
细节二:exclude
<!-- 示例 2 下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描 --> <context:component-scan base-package="com.lipu.spring.annotationtest" use-default-filters="false"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan>
第三步:创建类,在类上面添加创建对象注解
//在注解里面 value 属性值可以省略不写, //默认值是类名称,首字母小写 //UserService -- userService @Component(value = "userService") //等同于 <bean id = "userService" class = "..."></bean> public class UserService { public void show() { System.out.println("show user Service"); } }
-
基于注解方式实现属性注入
(1) @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(); } }
(2) @Qualifier:根据名称进行注入
这个@Qualifier 注解的使用,和上面@Autowired 一起使用
//定义 dao 类型属性 //不需要添加 set 方法 //添加注入属性注解 @Autowired //根据类型进行注入 @Qualifier(value = "userDaoImpl1") //根据名称进行注入 private UserDao userDao;
(3) @Resource:可以根据类型注入,可以根据名称注入
//@Resource //根据类型进行注入 @Resource(name = "userDaoImpl1") //根据名称进行注入 private UserDao userDao;
(4) @Value:注入普通类型属性
@Value(value = "abc") private String name;
-
完全注解开发
(1)创建配置类,替代 xml 配置文件
@Configuration //作为配置类,替代 xml 配置文件 @ComponentScan(basePackages = {"com.atguigu"}) public class SpringConfig { }
AOP
AOP简介
-
什么是 AOP
(1)面向切面编程(方面), 利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
-
AOP(底层原理):AOP 底层使用动态代理
- 第一种 有接口情况,使用 JDK 动态代理
- 第二种 没有接口情况,使用 CGLIB 动态代理
JDK动态代理
共同接口
public interface UserDao {
public Integer add(Integer a,Integer b);
}
被代理类(实现共同借口)
public class UserDaoImpl implements UserDao {
@Override
public Integer add(Integer a, Integer b) {
return a + b;
}
}
代理类及测试:
public class UserProxy{
public static void main(String[] args) {
UserDaoImpl userDaoImpl = new UserDaoImpl();
MyInvocationHandler myproxy = new MyInvocationHandler(userDaoImpl);
UserDao dao = (UserDao)Proxy.newProxyInstance(UserProxy.class.getClassLoader(),new Class[] {UserDao.class},myproxy);
int result = dao.add(3, 1);
System.out.println(result);
}
}
class MyInvocationHandler implements InvocationHandler {
Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("前处理");
Object result = method.invoke(obj, args);
System.out.println("后置处理");
return result;
}
}
AOP术语
-
连接点
类里面哪些方法可以被增强,这些方法被称为连接点 -
切入点
实际被增强的方法,被称为切入点 -
通知(增强)
实际增强的逻辑部分,被称为通知(增强)通知有多种类型:
前置通知
后置通知
环绕通知
异常通知
最终通知 -
切面
动作,指把通知应用到切入点的过程
AOP操作
-
准备工作
spring框架一般基于AspectJ实现操作的,AspectJ不是Spring的组成部分,独立的AOP框架,一般把AspectJ和Spring一起使用,进行AOP操作。
-
基于AspectJ实现AOP操作
基于xml配置文件实现
基于注解方式实现
-
在项目里引入AOP相关依赖
-
切入点表达式:
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: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.. (…)) -
AOP操作(AspectJ注解)
-
使用注解创建类,在类里面定义方法:
//被增强的类 @Component public class User { public void show() { System.out.println("user show"); } }
-
使用注解创建增强类,编写增强类逻辑
//增强的类 @Component @Aspect//生成代理对象 @Order(1)//设置增强类优先级,数字越小优先级越高 public class UserProxy{...}
-
进行通知的配置:
①. 在spring的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: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/spring-beans.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd"> <!--开启注解扫描--> <context:component-scan base-package="com.lipu.spring.aoptest"></context:component-scan> </beans>
② 在增强类和被增强类上添加注解
③ 在增强类上添加注解@Aspect
@Aspect//生成代理对象 public class UserProxy{
④ 在配置文件中生开启生成代理对象
<!-- 开启自动代理 --> <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
-
配置不同类型的通知,在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//前置通知 @Before(value = "execution(* com.lipu.spring.aoptest.User.show(..))") public void before() {//前置通知 System.out.println("before..."); } //后置通知 @AfterReturning(value="execution(* com.lipu.spring.aoptest.User.show(..))") public void afterRuturing() {//前置通知 System.out.println("afterRuturing..."); } //最终通知 @After(value="execution(* com.lipu.spring.aoptest.User.show(..))") public void after() {//前置通知 System.out.println("after..."); } //异常通知 @AfterThrowing(value="execution(* com.lipu.spring.aoptest.User.show(..))") public void afterThrowing() {//前置通知 System.out.println("afterThrowing..."); } //异常通知 @Around(value="execution(* com.lipu.spring.aoptest.User.show(..))") public void round(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {//前置通知 System.out.println("环绕前"); proceedingJoinPoint.proceed(); System.out.println("环绕后"); }
-
相同的切入点抽取
//相同切入点抽取 @Pointcut(value = "execution(* com.atguigu.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.lipu.spring.aoptest"})//开启注解扫描 @EnableAspectJAutoProxy(proxyTargetClass = true)//开启自动代理 public class ConfigAop { }
-
-
AOP操作(AspectJ配置文件)
-
创建两个类,增强类和被增强类,创建方法
-
在spring配置文件中创建两个类对象
<!-- 创建增强类和被增强类 --> <bean id = "book" class = "com.lipu.spring.aoptest.Book"></bean> <bean id = "bookProxy" class = "com.lipu.spring.aoptest.BookProxy"></bean>
-
在spring配置文件中配置切入点
<!-- 配置aop增强切入点 --> <aop:config> <!-- 配置aop切入点 --> <aop:pointcut expression="execution(* com.lipu.spring.aoptest.Book.*(..))" id="p"/> <!-- 配置切面 --> <aop:aspect ref="bookProxy"> <!-- 增强作用在具体的方法上 --> <aop:before method="before" pointcut-ref="p"/> </aop:aspect> </aop:config>
-
JdbcTemplate
JdbcTemplate简介及准备工作
Spring 框架对 JDBC 进行封装,使用 JdbcTemplate 方便实现对数据库操作
-
准备工作
- 引入相关jar包
- 在spring配置文件配置数据库连接池
- 配置JdbcTemplate对象,注入DataSource
- 创建Service类,注入Dao类,在Dao类中注入JdbcTemplate对象
JdbcTemplate操作数据库
增删改
一、基于xml配置
-
创建数据库
use test; drop table if exists book; create table book( `id` int primary key auto_increment, `title` varchar(100) unique, `author` varchar(50) );
-
对应数据库创建实体类
//对应数据库中的book表 public class Book { private Integer id; private String title; private String author; //构造方法,getter、setter、tostring略 }
-
编写Service和Dao类
BookDao类:
public interface BookDao { int add(Book book); }
BookDaoImpl类:
public class BookDaoImpl implements BookDao{ private JdbcTemplate jdbcTemplate; public int add(Book book) { String sql = "INSERT INTO book(`title`,`author`) VALUES(?, ?)"; return jdbcTemplate.update(sql, book.getTitle(),book.getAuthor()); } public void setJdbcTemplate(JdbcTemplate jdbcTemplate) { this.jdbcTemplate = jdbcTemplate; } }
BookService类:
public class BookService { private BookDao bookDao; public int addBook(Book book) { return bookDao.add(book); } public void setBookDao(BookDao bookDao) { this.bookDao = bookDao; } }
-
配置xml
jdbc.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:jdbc="http://www.springframework.org/schema/jdbc" xsi:schemaLocation="http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-4.0.xsd 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"> <!-- 使用xml配置方式进行数据库操作配置 --> <!-- 加载数据库配置文件,classpath:文件位于src目录下 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 创建DataSource对象,并赋值属性 --> <bean id = "dataSource" class = "com.alibaba.druid.pool.DruidDataSource"> <!-- 注入数据库连接的四个必要属性 --> <property name="url" value = "${jdbc.url}"></property> <property name="username" value = "${jdbc.username}"></property> <property name = "password" value = "${jdbc.password}"></property> <property name = "driverClassName" value = "${jdbc.driverClass}"></property> </bean> <!-- 创建JdbcTemplate对象,注入DataSource属性 --> <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"> <property name="dataSource" ref="dataSource"></property> </bean> <!-- 创建BookDaoImpl对象,注入JdbcTemplate属性 --> <bean id = "bookDaoImpl" class = "com.lipu.spring.jdbctemplate.BookDaoImpl"> <property name = "jdbcTemplate" ref="jdbcTemplate"></property> </bean> <!-- 创建BookService类,注入BookDaoImpl属性 --> <bean id = "bookService" class = "com.lipu.spring.jdbctemplate.BookService"> <property name = "bookDao" ref="bookDaoImpl"></property> </bean> </beans>
jdbc.properties
jdbc.username=root jdbc.password=root jdbc.url=jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&serverTimezone=UTC&useSSL=false jdbc.driverClass=com.mysql.cj.jdbc.Driver
-
编写测试类
@RunWith(SpringJUnit4ClassRunner.class)//开启junit测试 @ContextConfiguration("classpath:jdbc.xml")//加载配置xml文件 public class TestJdbcTemplate { @Autowired//junit测试中自动扫描注解??? private BookService bookService; @Test public void testAdd() { Book book = new Book(null, "三国演义","罗贯中"); bookService.addBook(book); } }
二、完全注解开发
-
创建数据库(同上)
-
创建数据库对应的实体类(同上)
-
编写Service和Dao类
BookDao:
public interface BookDao { public abstract int add(Book book); }
BookDaoImpl:
@Repository(value = "bookDaoImpl")//持久层注解 public class BookDaoImpl implements BookDao { @Autowired//根据类型自动注入 @Qualifier(value = "jdbcTemplate")//根据名称自动注入,需要配合Autowired一起使用 private JdbcTemplate jdbcTemplate; public int add(Book book) { String sql = "INSERT INTO book(`title`,`author`) VALUES(?, ?)"; return jdbcTemplate.update(sql,book.getTitle(),book.getAuthor()); } }
BookService:
@Service("bookService")//<bean id = "bookService" class = "..."></bean> public class BookService { @Autowired//根据类型自动注入 @Qualifier(value = "bookDaoImpl")//根据名称进行自动注入,需要配合Autowired一起使用 private BookDao bookDao; public int addBook(Book book) { return bookDao.add(book); } }
-
编写配置类:
@Configuration//定义该类为配置类 @ComponentScan(basePackages = {"com.lipu.spring.jdbctemplate2"})//开启自动扫描组件 public class ConfigJdbc { //创建数据库连接池 @Bean public DataSource getDataSource() { //加载druid数据库连接的配置文件 InputStream is = this.getClass().getClassLoader().getResourceAsStream("druid.properties"); Properties prop = new Properties(); //创建数据库连接池 DataSource dataSource = null; try { prop.load(is); dataSource = DruidDataSourceFactory.createDataSource(prop); } catch (Exception e) { // TODO: handle exception } return dataSource; } //创建JdbcTemplate对象 @Bean(name = "jdbcTemplate") public JdbcTemplate getJdbcTemplate(DataSource dataSource) { //注入dataSource JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate; } //创建事务管理器,暂时可能不用 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) { //注入dataSource DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager(dataSource); return dataSourceTransactionManager; } }
查
主要变动在Dao层实现类中,所以只记录Dao层的变化
-
查询单行单列值
BookDaoImpl:@Override public Object queryForSingleValue() { String sql = "select count(*) from book"; return jdbcTemplate.queryForObject(sql, Integer.class); }
-
查询一行
@Override public Book queryForOne(Integer bookId) { RowMapper<Book> rowMapper = new BeanPropertyRowMapper<Book>(Book.class); String sql = "SELECT `id`,`title`,`author` FROM book WHERE id = ?"; Book book = jdbcTemplate.queryForObject(sql, rowMapper, bookId); return book; }
-
查询多行
@Override
public List queryForList() {
String sql = “SELECTid
,title
,author
FROM book”;
RowMapper rowMapper = new BeanPropertyRowMapper(Book.class);
List books = jdbcTemplate.query(sql, rowMapper);
return books;
}
批量操作
主要变化仍然在DaoImpl层,故只记录变化处
@Override
public int[] batchAdd(List<Object[]> books) {
String sql = "INSERT INTO book(`title`,`author`) VALUES(?, ?)";
int[] result = jdbcTemplate.batchUpdate(sql, books);
return result;
}
事务操作
-
事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操作都失败
-
事务四个特性(ACID)
(1)原子性
(2)一致性
(3)隔离性
(4)持久性 -
事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
-
在 Spring 进行事务管理操作有两种方式:编程式事务管理和声明式事务管理(使用)
-
声明式事务管理
(1)基于注解方式(使用)
(2)基于 xml 配置文件方式 编程式事务管理和声明式事务管理(使用)
-
在 Spring 进行声明式事务管理,底层使用 AOP 原理
-
Spring 事务管理 API
事务操作( XML 声明式事务管理)
-
在 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:tx="http://www.springframework.org/schema/tx" 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-4.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!-- xml配置 --> <!-- 引入数据库配置 --> <context:property-placeholder location = "classpath:jdbc.properties"/> <!-- 创建DataSource,初始化数据库连接四个要素 --> <bean id = "dataSource" class = "com.alibaba.druid.pool.DruidDataSource"> <property name="username" value="${jdbc.username}"></property> <property name="password" value="${jdbc.password}"></property> <property name="url" value="${jdbc.url}"></property> <property name="driverClassName" value="${jdbc.driverClass}"></property> </bean> <!-- 创建JdbcTemplate,并且注入dataSource --> <bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate"> <property name = "dataSource" ref = "dataSource"></property> </bean> <!-- 创建CountDaoImpl,注入jdbcTemplate --> <bean id = "countDaoImpl" class = "com.lipu.spring.transactiontest.CountDaoImpl"> <property name = "jdbcTemplate" ref = "jdbcTemplate"></property> </bean> <!-- 创建CountService,注入CountDaoImpl --> <bean id = "countService" class = "com.lipu.spring.transactiontest.CountService"> <property name = "countDao" ref = "countDaoImpl"></property> </bean> <!-- 创建事务管理器 --> <bean id = "transactionManager" class = "org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!-- 注入数据源 --> <property name="dataSource" ref = "dataSource"></property> </bean> <!-- 配置通知 --> <tx:advice id="txadvice"> <!-- 配置事务参数 --> <tx:attributes> <!-- 指定在哪种规则的方法上添加事务 --> <tx:method name="transferCount" propagation="REQUIRED"/> </tx:attributes> </tx:advice> <!-- 配置切入点和切面 --> <aop:config> <!-- 配置切入点 --> <aop:pointcut expression="execution(* com.lipu.spring.transactiontest.CountService.*(..))" id="pointcut"/> <!-- 配置切面 --> <aop:advisor advice-ref="txadvice" pointcut-ref="pointcut"/> </aop:config> </beans>
事务操作(注解声明式事务管理)
-
创建配置类,使用配置类替代 xml 配置文件,开启事务注解
@Configuration//配置类 @ComponentScan(basePackages = {"com.lipu.spring.transactiontest2"})//开启组件扫描,定义扫描包 @EnableTransactionManagement//开启事务性 public class Config { //创建DataSource @Bean public DataSource getDataSource() { //读取jdbc配置文件 InputStream is = this.getClass().getClassLoader().getResourceAsStream("druid.properties"); Properties prop = new Properties(); //创建dataSource DataSource dataSource = null; try { prop.load(is); dataSource = DruidDataSourceFactory.createDataSource(prop); } catch (Exception e) { e.printStackTrace(); } return dataSource; } //创建JdbcTemplate类 @Bean public JdbcTemplate getJdbcTemplate(DataSource dataSource) { JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource); return jdbcTemplate; } //创建事务管理器 @Bean public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource) { DataSourceTransactionManager transactionManager = new DataSourceTransactionManager(dataSource); return transactionManager; } }
-
在 service 类上面( 或者 service 类里面方法上面)添加事务注解
(1) @Transactional,这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务
@Service(value="countService") @Transactional(propagation = Propagation.REQUIRED )//设置事务 public class CountService { @Autowired//自动注入 private CountDao countDao; public void setCountDao(CountDao countDao) { this.countDao = countDao; } public void transferCount() { countDao.addBlance("小明"); System.out.println("用来模拟异常情况"); int i = 10/0; countDao.subBlance("小红"); } }
===============================================================================
初学spring框架,资料来源于尚硅谷公开课:尚硅谷-Spring5框架2020最新版教程(idea版)