Spring5框架
Spring-IOC
IOC容器-Bean管理(基于xml方式)
-
什么是IOC容器?
(a)把对象创建和对象之间的调用过程,交给Spring进行管理
(b)使用IOC目的:为了降低耦合度 -
IOC底层原理
(a)xml解析,工厂模式,反射 -
IOC 操作 Bean 管理(基于 xml 方式)
(a)基于xml方式创建对象<bean id="user" class="com.atguigu.spring5.User"></bean>
其中id:唯一标识,class:类全路径
(b)DI:依赖注入,注入属性
(b1)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; } }
<bean id="book" class="com.atguigu.spring5.Book"> <property name="bname" value="十万个为什么"></property> <property name="bauthor" value="abc"></property> </bean>
(b2)有参构造方法注入
/** * 使用有参数构造注入 */ public class Orders { //属性 private String oname; private String address; //有参数构造 public Orders(String oname,String address) { this.oname = oname; this.address = address; } }
<bean id="orders" class="com.atguigu.spring5.Orders"> <constructor-arg name="oname" value="电脑"></constructor-arg> <constructor-arg name="address" value="China"></constructor-arg> </bean>
(b3)p空间注入(了解)
<!--1、添加p名称空间在配置文件头部--> <?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" <!--在这里添加一行p--> <!--2、在bean标签进行属性注入(算是set方式注入的简化操作)--> <bean id="book" class="com.atguigu.spring5.Book" p:bname="very" p:bauthor="good"> </bean>
(b4)注入空值和特殊符号
<bean id="book" class="com.atguigu.spring5.Book"> <!--(1)null值--> <property name="address"> <null/><!--属性里边添加一个null标签--> </property> <!--(2)特殊符号赋值--> <!--属性值包含特殊符号 a 把<>进行转义 < > b 把带特殊符号内容写到CDATA --> <property name="address"> <value><![CDATA[<<南京>>]]></value> </property> </bean>
(b5)注入属性-外部bean
public class UserService {//service类 //创建UserDao类型属性,生成set方法 private UserDao userDao; public void setUserDao(UserDao userDao) { this.userDao = userDao; } public void add() { System.out.println("service add..............."); userDao.update();//调用dao方法 } } public class UserDaoImpl implements UserDao {//dao类 @Override public void update() { System.out.println("dao update..........."); } }
<!--1 service和dao对象创建--> <bean id="userService" class="com.atguigu.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>
(b6)基于XML方式注入内部bean和级联赋值
(1)一对多关系:部门和员工
一个部门有多个员工,一个员工属于一个部门(部门是一,员工是多)
(2)在实体类之间表示一对多关系,员工表示所属部门,使用对象类型属性进行表示//部门类 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.atguigu.spring5.bean.Emp"> <!--设置两个普通属性--> <property name="ename" value="Andy"></property> <property name="gender" value="女"></property> <!--设置对象类型属性--> <property name="dept"> <bean id="dept" class="com.atguigu.spring5.bean.Dept"><!--内部bean赋值--> <property name="dname" value="宣传部门"></property> </bean> </property> </bean>
<!--方式一:级联赋值--> <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <!--设置两个普通属性--> <property name="ename" value="Andy"></property> <property name="gender" value="女"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> <property name="dname" value="公关部门"></property> </bean>
//方式二:生成dept的get方法(get方法必须有!!) public Dept getDept() { return dept; }
<!--级联赋值--> <bean id="emp" class="com.atguigu.spring5.bean.Emp"> <!--设置两个普通属性--> <property name="ename" value="jams"></property> <property name="gender" value="男"></property> <!--级联赋值--> <property name="dept" ref="dept"></property> <property name="dept.dname" value="技术部门"></property> </bean> <bean id="dept" class="com.atguigu.spring5.bean.Dept"> </bean>
(b7)IOC 操作 Bean 管理——xml 注入集合属性
//(1)创建类,定义数组、list、map、set 类型属性,生成对应 set 方法 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; 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; }
<!--(2)在 spring 配置文件进行配置--> <bean id="stu" class="com.atguigu.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>
(b8)在集合里面设置对象类型值
//学生所学多门课程 private List<Course> courseList;//创建集合 public void setCourseList(List<Course> courseList) { this.courseList = courseList; }
<!--创建多个course对象--> <bean id="course1" class="com.atguigu.spring5.collectiontype.Course"> <property name="cname" value="Spring5框架"></property> </bean> <bean id="course2" class="com.atguigu.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>
(b9)把集合注入部分提取出来
<!--第一步:在 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:util="http://www.springframework.org/schema/util" <!--添加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名称空间--> <!--第二步:使用 util 标签完成 list 集合注入提取--> <!--把集合注入部分提取出来--> <!--1 提取list集合类型属性注入--> <util:list id="bookList"> <value>易筋经</value> <value>九阴真经</value> <value>九阳神功</value> </util:list> <!--2 提取list集合类型属性注入使用--> <bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype"> <property name="list" ref="bookList"></property> </bean>
IOC容器-Bean管理(基于xml方式)②
1.IOC 操作 Bean 管理(FactoryBean)
1、Spring 有两种类型 bean,一种普通 bean,另外一种工厂 bean(FactoryBean)
2、普通 bean:在配置文件中定义 bean 类型就是返回类型
3、工厂 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;
}
}
<bean id="myBean" class="com.atguigu.spring5.factorybean.MyBean">
</bean>
@Test
public void test3() {
ApplicationContext context =
new ClassPathXmlApplicationContext("bean3.xml");
//xml里定义的是MyBean类型的,而返回值是Course类型的
Course course = context.getBean("myBean", Course.class);//返回值类型可以不是定义的bean类型!
System.out.println(course);
}
2.IOC 操作 Bean 管理(bean 作用域)
//(1)在 spring 配置文件 bean 标签里面有属性(scope)用于设置单实例还是多实例
//(2)scope 属性值 第一个值 默认值,singleton,表示是单实例对象 第二个值 prototype,表示是多实例对象
//(3)singleton 和 prototype 区别
// (a)singleton 单实例,prototype 多实例
// (b)设置 scope 值是 singleton 时候,加载 spring 配置文件时候就会创建单实例对象 ;设置 scope 值是 prototype 时候,不是在加载 spring 配置文件时候创建对象,在调用 getBean 方法时候创建多实例对象
//(4)Spring中Bean默认是单实例的
<!--设置为多实例-->
<bean id="book" class="com.atguigu.spring5.collectiontype.Book" scope="prototype">
<!--scope标签里面设置 是否为多实例-->
<property name="list" ref="bookList"></property>
</bean>
3.IOC 操作 Bean 管理(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("第五步 执行销毁的方法");
}
}
<bean id="orders" class="com.atguigu.spring5.bean.Orders" initmethod="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property>
</bean>
@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();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5xiq2yrR-1666085340080)(D:\桌面\SummerVacation\Spring5课堂笔记.assets\image-20221018152744379.png)]
bean 的后置处理器, bean 生命周期有七步 :
(1)通过构造器创建 bean 实例(无参数构造)
(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)
**(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization **
(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)
**( 5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization **
(6) bean 可以使用了(对象获取到了)
(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)
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参数配置-->
<!--配置初始化方法和销毁方法-->
<bean id="orders" class="com.atguigu.spring5.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">
<property name="oname" value="手机"></property><!--这里就是通过set方式(注入属性)赋值-->
</bean>
<!--配置后置处理器-->
<bean id="myBeanPost" class="com.atguigu.spring5.bean.MyBeanPost"></bean>
@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();
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nr8lsqgc-1666085340081)(D:\桌面\SummerVacation\Spring5课堂笔记.assets\image-20221018153221908.png)]
4.IOC 操作 Bean 管理( xml 自动装配)
1、什么是自动装配
(1)根据指定装配规则(属性名称或者属性类型), Spring 自动将匹配的属性值进行注入
2、演示自动装配过程
(1)根据属性名称自动注入
<!--实现自动装配
bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
byType 根据属性类型注入
-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byName">
<!--这里就可以不用写property标签了-->
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
(2)根据属性类型自动注入
<!--
实现自动装配
bean 标签属性 autowire,配置自动装配
autowire 属性常用两个值:
byName 根据属性名称注入 ,注入值 bean 的 id 值和类属性名称一样
byType 根据属性类型注入
-->
<bean id="emp" class="com.atguigu.spring5.autowire.Emp" autowire="byType">
<!--<property name="dept" ref="dept"></property>-->
</bean>
<bean id="dept" class="com.atguigu.spring5.autowire.Dept"></bean>
5.**IOC 操作 Bean 管理(外部属性文件) **
5.1方式一:直接配置数据库信息,(1)配置Druid(德鲁伊)连接池 (2)引入Druid(德鲁伊)连接池依赖 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>
5.2方式二:引入外部属性文件配置数据库连接池
(1)创建外部属性文件,properties 格式文件,写数据库信息(jdbc.properties)
//jdbc.properties
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/userDb
prop.userName=root
prop.password=root
(2)把外部 properties 属性文件引入到 spring 配置文件中 —— 引入 context 名称空间
使用表达式获取数据库信息
${prop.url}等等…
<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"><!--引入context名称空间-->
<!--引入外部属性文件-->
<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>
</beans>
IOC容器-Bean管理(基于注解方式)
1、什么是注解
(1)注解是代码特殊标记,格式: @注解名称(属性名称=属性值, 属性名称=属性值…)
(2)使用注解,注解作用在类上面,方法上面,属性上面
(3)使用注解目的:简化 xml 配置
2、 Spring 针对 Bean 管理中创建对象提供注解
(1) @Component
(2) @Service
(3) @Controller
(4) @Repository
上面四个注解功能是一样的,都可以用来创建 bean 实例
3、基于注解方式实现对象创建
(a)开启组件扫描
<!--
开启组件扫描
1 如果扫描多个包,多个包使用逗号隔开
2 扫描包上层目录
-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
(b)创建类,在类上面添加创建对象注解
//在注解里面 value 属性值可以省略不写,
//默认值是类名称,首字母小写
//UserService -- userService@Component(value = "userService") //<bean id="userService" class=".."/>
public class UserService {
public void add() {
System.out.println("service add.......");
}
}
(c)组件扫描的细节配置
<!--示例 1
use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter
context:include-filter ,设置扫描哪些内容
-->
<context:component-scan base-package="com.atguigu" use-defaultfilters="false">
<context:include-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--示例 2
下面配置扫描包所有内容
context:exclude-filter: 设置哪些内容不进行扫描
-->
<context:component-scan base-package="com.atguigu">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
(d)基于注解方式实现属性注入
@Autowired:根据类型注入
@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") //根据名称进行注入
private UserDao userDao;
@Resource:可以根据类型注入,可以根据名称注入
//@Resource
//根据类型进行注入
@Resource(name = "userDaoImpl1")
//根据名称进行注入
private UserDao userDao;
@Value:注入普通类型属性
@Value(value = "abc")
private String name;
(e)完全注解开发
创建配置类
@Configuration //作为配置类,替代 xml 配置文件
@ComponentScan(basePackages = {"com.atguigu"})
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();
}
Spring-AOP
1.AOP基本概念
(1)面向切面编程(方面),利用 AOP 可以对业务逻辑的各个部分进行隔离,从而使得 业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
(2)通俗描述:不通过修改源代码方式,在主干功能里面添加新功能
(3)使用登录例子说明 AOP
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5BkDgdDb-1666085340082)(D:\桌面\SummerVacation\Spring5课堂笔记.assets\image-20221018161011636.png)]
2.AOP底层原理
(1)有接口情况,使用 JDK 动态代理 ;创建接口实现类代理对象,增强类的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lBPqmn98-1666085340082)(D:\桌面\SummerVacation\Spring5课堂笔记.assets\image-20221018161303442.png)]
(2)没有接口情况,使用 CGLIB 动态代理;创建子类的代理对象,增强类的方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nNwU0o2C-1666085340083)(D:\桌面\SummerVacation\Spring5课堂笔记.assets\image-20221018161325872.png)]
3.AOP(JDK动态代理)
//(1)创建接口,定义方法
public interface UserDao {
public int add(int a,int b);
public String update(String id);
}
//(2)创建接口实现类,实现方法
public class UserDaoImpl implements UserDao {
@Override
public int add(int a, int b) {
return a+b;
}
@Override
public String update(String id) {
return id;
}
}
//(3)使用 Proxy 类创建接口代理对象
public class JDKProxy {
public static void main(String[] args) {
//创建接口实现类代理对象
Class[] interfaces = {UserDao.class};
UserDaoImpl userDao = new UserDaoImpl();
/**
第一参数,类加载器
第二参数,增强方法所在的类,这个类实现的接口,(支持多个接口)
第三参数,实现这个接口 InvocationHandler,创建代理对象,写增强的部分
*/
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术语
(1)连接点:类里面哪些方法可以被增强,这些方法称为连接点
(2)切入点:实际被真正增强的方法称为切入点
(3)通知(增强):实际增强的逻辑部分称为通知,且分为以下五种类型:
1)前置通知 2)后置通知 3)环绕通知 4)异常通知 5)最终通知
(4)切面:把通知应用到切入点过程
5.AOP操作
(1)切入点表达式作用:知道对哪个类里面的哪个方法进行增强
(2)语法结构: execution([权限修饰符] [返回类型] [类全路径] [方法名称]([参数列表]) )
(3)例子如下:
例 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(注解方式)
6.AOP 操作(AspectJ 注解)
//1、创建类,在类里面定义方法
public class User {
public void add() {
System.out.println("add.......");
}
}
//2、创建增强类(编写增强逻辑)
//(1)在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类
public class UserProxy {
public void before() {//前置通知
System.out.println("before......");
}
}
组件扫描是关于注解的
AspectJ是关于AOP注解的
<!--3、进行通知的配置-->
<?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 base-package="com.atguigu.spring5.aopanno"></context:component-scan>
<!-- 开启Aspect生成代理对象-->
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
在增强类上面添加@Aspect注解
//增强的类
@Component
@Aspect //生成代理对象
public class UserProxy {}
//被增强的类
@Component
public class User {}
//4、配置不同类型的通知
@Component
@Aspect //生成代理对象
public class UserProxy {
//前置通知
//@Before注解表示作为前置通知
@Before(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* com.atguigu.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("环绕之后.........");
}
}
其中大家都使用了execution(* com.atguigu.spring5.aopanno.User.add(…))
那么就可以用到相同的切入点抽取**@Pointcut(value = “execution(* com.atguigu.spring5.aopanno.User.add(…))”)**
//4、配置不同类型的通知
@Component
@Aspect //生成代理对象
public class UserProxy {
//相同切入点抽取
@Pointcut(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void pointdemo() {
}
//前置通知
//@Before注解表示作为前置通知
@Before(value = "pointdemo()")//相同切入点抽取使用!
public void before() {
System.out.println("before.........");
}
//后置通知(返回通知)
@AfterReturning(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void afterReturning() {
System.out.println("afterReturning.........");
}
//最终通知
@After(value = "execution(* com.atguigu.spring5.aopanno.User.add(..))")
public void after() {
System.out.println("after.........");
}
//异常通知
@AfterThrowing(value = "execution(* com.atguigu.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("环绕之后.........");
}
}
7.有多个增强类对同一个方法进行增强,设置增强类优先级
//(1)在增强类上面添加注解 @Order(数字类型值),数字类型值越小优先级越高
@Component
@Aspect
@Order(1)
public class PersonProxy{ }
8.完全注解开发
@Configuration//配置类
@ComponentScan(basePackages = {"com.atguigu"})//组件扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启Aspect
public class ConfigAop {
}
AOP(xml方式)
<!--1、创建两个类,增强类和被增强类,创建方法(同上一样)-->
<!--2、在 spring 配置文件中创建两个类对象-->
<!--创建对象-->
<bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>
<bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
<!--3、在 spring 配置文件中配置切入点-->
<!--配置 aop 增强-->
<aop:config>
<!--切入点-->
<aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/>
<!--配置切面-->
<aop:aspect ref="bookProxy">
<!--增强作用在具体的方法上-->
<aop:before method="before" pointcut-ref="p"/>
</aop:aspect>
</aop:config>
Spring-JdbcTemplate
1.配置数据库连接池
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql:///test" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
2.配置 JdbcTemplate 对象,注入 DataSource
<!-- JdbcTemplate 对象 -->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!--注入 dataSource-->
<property name="dataSource" ref="dataSource"></property><!--set方式注入-->
</bean>
3.创建 service 类,创建 dao 类,在 dao 注入 jdbcTemplate 对象
(1)开启组件扫描
<!-- 组件扫描 -->
<context:component-scan base-package="com.atguigu"></context:component-scan>
@Service //Service注解用在Service层
public class BookService {
//注入 dao
@Autowired
private BookDao bookDao;
}
@Repository //Repository注解用在Dao层
public class BookDaoImpl implements BookDao {
//注入 JdbcTemplate
@Autowired
private JdbcTemplate jdbcTemplate;
}
JdbcTemplate 操作数据库(添加)
DaoImpl需要有jdbcTemplate对象,Service需要有Dao对象
(1)对应数据库的实体类
public class Book {
private String userId;
private String username;
private String ustatus;
@Override
public String toString() {
return "Book{" +
"userId='" + userId + '\'' +
", username='" + username + '\'' +
", ustatus='" + ustatus + '\'' +
'}';
}
public String getUserId() {
return userId;
}
public void setUserId(String userId) {
this.userId = userId;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Book(String userId, String username, String ustatus) {
this.userId = userId;
this.username = username;
this.ustatus = ustatus;
}
public Book() {
}
public String getUstatus() {
return ustatus;
}
public void setUstatus(String ustatus) {
this.ustatus = ustatus;
}
}
(2)编写Service和Dao
public interface BookDao {
public void add(Book book);
void delete(String id);
void update(Book book);
int selectCount();
Book selectBookInfo(String id);
List<Book> selectAllBook();
void batchAdd(List<Object[]> batchArgs);
void batchUpdateBook(List<Object[]> batchArgs);
void batchDeleteBook(List<Object[]> batchArgs);
}
@Repository
public class BookDaoImpl implements BookDao{
@Autowired
private JdbcTemplate jdbcTemplate;
}
@Service
public class BookService {
@Autowired
private BookDao bookDao;
//添加
public void add(Book book){
bookDao.add(book);
}
//删除
public void delete(String id){
bookDao.delete(id);
}
//修改
public void update(Book book){
bookDao.update(book);
}
//查询1
public int selectCount(){
return bookDao.selectCount();
}
//查询2
public Book selectBookInfo(String id){
return bookDao.selectBookInfo(id);
}
//查询3
public List<Book> selectAllBook(){
return bookDao.selectAllBook();
}
//批量添加
public void batchAdd(List<Object[]> batchArgs){
bookDao.batchAdd(batchArgs);
}
//批量修改
public void batchUpdateBook(List<Object[]> batchArgs){
bookDao.batchUpdateBook(batchArgs);
}
//批量删除
public void batchDeleteBook(List<Object[]> batchArgs){
bookDao.batchDeleteBook(batchArgs);
}
}
@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);
}
JdbcTemplate 操作数据库(修改和删除)
使用JdbcTemplate 模板所实现的 “增删改” 都是调用了同一个 “update” 方法
//1、修改
@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);
}
//2、删除
@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);
}
JdbcTemplate 操作数据库(查询返回某个值)
//查询表记录数
@Override
public int selectCount() {
String sql = "select count(*) from t_book";
//queryForObject方法中:第一个参数代表--sql语句;第二个参数代表--返回类型class
Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
return count;
}
JdbcTemplate 操作数据库(查询返回集合)
//所用场景:查询图书列表分页、、
//查询返回集合
@Override
public List<Book> findAllBook() {
String sql = "select * from t_book";
//调用方法
List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
return bookList;
}
JdbcTemplate 操作数据库(批量操作)
//批量添加
@Override
public void batchAddBook(List<Object[]> batchArgs) {
String sql = "insert into t_book values(?,?,?)";
//batchUpdate方法 第一个参数:sql语句 第二个参数:List集合,添加多条记录数据
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);
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));
}
Spring-事务
1、什么是事务 ?
( 1)事务是数据库操作最基本单元,逻辑上一组操作,要么都成功,如果有一个失败所有操 作都失败
(2)典型场景:银行转账 lucy 转账 100 元 给 mary lucy 少 100, mary 多 100
2、事务四个特性(ACID)
(1)原子性
(2)一致性
(3)隔离性
(4)持久性
3.事务操作(模拟事务操作环境)
public interface UserDao {
/**
* 减少钱
*/
void redeceMoney();
/**
* 添加钱
*/
void addMoney();
}
@Repository
public class UserDaoImpl implements UserDao{
@Autowired
private JdbcTemplate jdbcTemplate;
/**
* 减少钱
*/
@Override
public void redeceMoney() {
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 {
@Autowired
private UserDao userDao;
public void accountMoney(){
//lucy给mary转100元
userDao.redeceMoney();
userDao.addMoney();
}
}
上边代码正常执行没有问题, 但是如果代码执行过程中出现异常,有问题,如下模拟异常!
@Service
public class UserService {
//这里执行后将会产生错误(异常),lucy 少 100后,mary不会多 100,这就不对了!!
private UserDao userDao;
//转账方法
public void accountMoney(){
userDao.reduceMoney();//lucy 少 100
//模拟异常
int x=10/0;
userDao.addMoney(); //mary 多 100
}
}
解决方式
//解决上边的异常方法——【编程式事务(传统方法)】
//转账的方法
public void accountMoney() {
try {
//第一步 开启事务
//第二步 进行业务操作
//lucy少100
userDao.reduceMoney();
//模拟异常
int i = 10/0;
//mary多100
userDao.addMoney();
//第三步 没有发生异常,提交事务
}catch(Exception e) {
//第四步 出现异常,事务回滚
}
}
4.事务管理的介绍
(1)事务添加到 JavaEE 三层结构里面 Service 层(业务逻辑层)
(2)在 Spring 进行事务管理操作 ;两种方式:编程式事务管理、声明式事务管理(推荐使用)
(3)声明式事务管理
(a)基于注解方式(推荐使用) (b)基于 xml 配置文件方式
(4)在 Spring 进行声明式事务管理,底层使用 AOP 原理
(5)Spring 事务管理 API :提供一个接口,代表事务管理器,这个接口针对不同的框架提供不同的实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-M9IhkpQ5-1666085340084)(D:\桌面\SummerVacation\Spring5课堂笔记.assets\image-20221018170920817.png)]
注解声明式事务管理
<?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.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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启组件扫描-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/userDb" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!-- 配置JdbcTemplate对象,注入DataSource-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
</beans>
其中重要的是
<!-- 创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 开启事务注解-->
<tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
1.在 Service 类上面( 或者 service 类里面方法上面)添加事务注解
(1) @Transactional,这个注解添加到类上面,也可以添加方法上面
(2)如果把这个注解添加类上面,这个类里面所有的方法都添加事务
(3)如果把这个注解添加方法上面,为这个方法添加事务
事务操作(声明式事务管理参数配置)
1.事务传播行为(propagation)
(1)REQUIRED:如果add方法本身有事务,调用update方法后,update使用当前add方法里面的事务;
如果add方法本身没有事务,调用update方法后,创建新的事务
(2)REQUIRED_NEW:使用add调用update方法,不论add方法是否有事务,都会创建新的事务。
7种:(required / supports / mandatory / requires_new / not supported / never / nested)
PROPAGATION_REQUIRED:如果当前没有事务,就创建一个新事务,如果当前存在事务,就加入该事务,这是最常见的选择,也是Spring默认的事务传播行为。(required需要,没有新建,有加入)
PROPAGATION_SUPPORTS:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就以非事务执行。(supports支持,有则加入,没有就不管了,非事务运行)
PROPAGATION_MANDATORY:支持当前事务,如果当前存在事务,就加入该事务,如果当前不存在事务,就抛出异常。(mandatory强制性,有则加入,没有异常)
PROPAGATION_REQUIRES_NEW:创建新事务,无论当前存不存在事务,都创建新事务。(requires_new需要新的,不管有没有,直接创建新事务)
PROPAGATION_NOT_SUPPORTED:以非事务方式执行操作,如果当前存在事务,就把当前事务挂起。(not supported不支持事务,存在就挂起)
PROPAGATION_NEVER:以非事务方式执行,如果当前存在事务,则抛出异常。(never不支持事务,存在就异常)
PROPAGATION_NESTED:如果当前存在事务,则在嵌套事务内执行。如果当前没有事务,则按REQUIRED属性执行。(nested存在就在嵌套的执行,没有就找是否存在外面的事务,有则加入,没有则新建)
对事务的要求程度可以从大到小排序:mandatory / supports / required / requires_new / nested / not supported / never
@Transactional(propagation = Propagation.REQUIRED,) //事务一
public void add(){
//调用update方法
update();
}
public void update(){ //事务二
}
2.事务隔离级别(isolation)
(1)事务有特性成为隔离性,多事务操作之间不会产生影响。不考虑隔离性产生很多问题
(2)有三个读问题:脏读、不可重复读、虚(幻)读
(3)脏读:一个未提交事务读取到另一个未提交事务的数据
(4)不可重复读:一个未提交事务读取到另一提交事务修改数据
(5)虚读:一个未提交事务读取到另一提交事务添加数据
(6) 解决: 通过设置事务隔离级别,解决读问题
- 读未提交(READ UNCOMMITTED)
- 读已提交(READ COMMITTED)
- 可重复读(REPEATABLE READ) 默认
- 序列化(SERIALIZABLE)
3.timeout:超时时间
(1)事务需要在一定时间内进行提交,如果不提交进行回滚 (2)默认值是 -1(不超时) ,设置时间以秒单位进行计算
4.readOnly:是否只读
(1)读:查询操作,写:添加修改删除操作
(2)readOnly 默认值 false,表示可以查询,可以添加修改删除操作
(3)设置 readOnly 值是 true,设置成 true 之后,只能查询
5.rollbackFor:回滚
设置出现哪些异常进行事务回滚
6.noRollbackFor:不回滚
设置出现哪些异常不进行事务回滚
7.最终设置
@Transactional(timeout = -1,readOnly = false,propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)
事务操作(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: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.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
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
<!-- 开启组件扫描-->
<context:component-scan base-package="com.atguigu"></context:component-scan>
<!-- 数据库连接池 -->
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"
destroy-method="close">
<property name="url" value="jdbc:mysql://localhost:3306/user_db" />
<property name="username" value="root" />
<property name="password" value="root" />
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
</bean>
<!-- 配置JdbcTemplate对象,注入DataSource-->
<bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 1.创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2.配置通知-->
<tx:advice id="txadvice">
<!-- 配置事务参数-->
<tx:attributes>
<!-- 指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED" isolation="REPEATABLE_READ"/>
<!-- <tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!-- 3.配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))" />
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
</beans>
其中重要的是
<!-- 1.创建事务管理器-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入数据源-->
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 2.配置通知-->
<tx:advice id="txadvice">
<!-- 配置事务参数-->
<tx:attributes>
<!-- 指定哪种规则的方法上面添加事务-->
<tx:method name="accountMoney" propagation="REQUIRED" isolation="REPEATABLE_READ"/>
<!-- <tx:method name="account*"/>-->
</tx:attributes>
</tx:advice>
<!-- 3.配置切入点和切面-->
<aop:config>
<!--配置切入点-->
<aop:pointcut id="pt" expression="execution(* com.atguigu.spring5.service.UserService.*(..))" />
<!--配置切面-->
<aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
</aop:config>
事务操作(完全注解声明式事务管理)
@Configuration //配置类
@ComponentScan(basePackages = {"com.atguigu"}) //组件扫描
@EnableTransactionManagement //开启事务
public class TxConfig {
/**
* 德鲁伊数据源
* 创建数据库连接池
* @return {@link DruidDataSource}
*/
@Bean
public DruidDataSource getDruidDataSource(){
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3307/user_db");
dataSource.setUsername("root");
dataSource.setPassword("lcj6445254");
return dataSource;
}
/**
* 获得jdbc模板
* 创建JdbcTemplate对象
* @param dataSource 数据源
* @return {@link JdbcTemplate}
*/
@Bean
public JdbcTemplate getJdbcTemplate(DataSource dataSource){
//到IOC容器中根据类型找DataSource
JdbcTemplate jdbcTemplate = new JdbcTemplate();
//注入DataSource
jdbcTemplate.setDataSource(dataSource);
return jdbcTemplate;
}
/**
* 事务管理器获取数据源
* 创建事务管理器
* @param dataSource 数据源
* @return {@link DataSourceTransactionManager}
*/
@Bean
public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
transactionManager.setDataSource(dataSource);
return transactionManager;
}
}