注解:
1.配置类的注解
• @Configuration:相当于applicationContext.xml文件,弄成一个配置类 • @PropertySource("classpath:db.properties"):加载配置文件,里面写配置文件的地址。相当于<context:property-placeholder location="classpath:db.properties"/> • @Value(${ }):在里面获取配置文件的key • @Bean:相当于applicationContext.xml文件里的标签<bean/>,可以直接在该注解里写id • @Qualifier("author1"):可以指定id(在方法的参数中使用) • @EnableAspectJAutoProxy():开启Aop自动代理功能 这个注解就类似于在xml文件中配置的<aop:config
2.注入到spring的注解
• @Controller:一般加在视图层,也就是servlet • @Service:一般加在业务层,也就是Service • @Repository:一般加在dao层 • @Component:对于一些身份不好归类的Bean,使用这个注解 • @ComponentScan(basePackages = "com.pan"):上面四个注解是不会自动注入到Spring容器中的,需要通过@ComponentScan来进行组件扫描.<context:component-scan base-package="com.pan"/>
3.生命周期的初始化,销毁注解
• PostConstruct():初始化注解 • PreDestory():销毁注解
4.提取注入到spring容器的bean
• @Autowired:按类型查找 • @Qualifier(""):指定名称查找,限定要自动注入的bean的id,和上面的一起使用 • @Resource:jdk提供的,先按名称查找,找不到在按类型查找 • @Value:注入简单类型数据
5.Aop的注解
• @Aspect:表示当前类是一个切面类,意味着这个类既有切点,又有通知 • @EnableAspectJAutoProxy():开启Aop自动代理功能 这个注解就类似于在xml文件中配置的<aop:config proxy-target-class="false"> • @Pointcut("execution(* com.pan.spring_config.service.impl.UserServiceImpl.*(..))") • @Before("") • @After("") • @AfterThrowing(value = "pc()",throwing = "e") • @AfterReturning(value = "pc()",returning = "r") • @Around("pc()")
6.事务的注解
• @Transactional:在该方法上添加事务,事务注解 • @Transactional(propagation = Propagation.REQUIRED,timeout = 6000,readOnly = true):参数可写可不写,该注解表示创建方法事务 • @EnableTransactionManagement:开启事务管理器
7.条件注解
• @Conditional():条件注解,用来判断是否运行该bean • @Profile():在里面写条件,封装了@Conditional
一.了解
spring:是xml+工厂模式+反射机制
1.jar
前言:如果你只是想用Spring的IoC功能, 仅需要引入: spring-context即可。 将这个jar包添加到classpath当中。如果采用maven只需要引入spring-context的依赖即可。 引入spring-context.jar,也会关联引入spring-aop,spring-expression,spring-beans和spring-core的jar
2.OCP开闭原则:
3.DIP依赖倒置原则:
4.控制反转
重点:控制反转是思想,DI(依赖注入)是实现
5.BeanFactory和FactoryBean区别(面试)
二.Spring特点
特点:
1.轻量级,由20多个模块构成,每一个jar包都很小,对代码无污染(因为只是存放你的bean,但是对你要写的代码没有任何的要求)
2.IOC:控制反转,面向接口编程,使用接口,就是面向灵活,项目的可扩展性,可维护性很高
3.AOP:面向切面编程:就是将公共的,通用的,重复的代码单独开发,在需要的时候反织回去,底层的原理就是动态代理
4.整合其它框架
1.作用:降低代码的耦合度
2.八大模块
三.GoF之工厂模式
spring:底层是是xml+工厂模式+反射机制
简单工厂模式:工厂类中的方法是静态方法,直接通过类名可以直接调用
工厂方法模式:工厂类的方法是实例方法,需要通过对象来调用
四.Ioc
概念:控制反转IOC(Inversion of Control)是一个概念,是一种思想。由Spring容器进行对象的创建和依赖注入,程序员在使用时直接取出使用。控制权从程序员手中夺走,由spring容器说的算
重点:控制反转是思想,DI(依赖注入)是实现
1.在配置文件中将bean注入到spring容器中
<bean id="stu" class="com.pan.entry.Student">
<property name="name" value="欧克">
</bean>
2.从spring容器中获取bean
**注意:ClassPathXmlApplicationContext可以配置多个spring配置文件**
执行以下代码的时候,xml文件中的bean就开始初始化对象了,而不是getBean之后才初始化
理解:正转:由程序员进行对象的创建和依赖注入,由程序员说的算
Student stu=new Student();
stu.setName("欧克");
注意点:
1.如果不是在项目里的spring.xml,可以通过下面的实现类来获取盘符下的文件
2.BeanFactory:Bean工厂
2.原理
3.log4j2日志
Spring的底层就是应用log4j2日志的,所以我们要启动一下
第一步:先引入log4j2的依赖
第二步:配置log4j2.xml配置文件
log4j依赖:
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.12</version>
</dependency>
log4j配置文件
# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# Console output...
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] - %m%n
使用
4.Ioc的实现:
A.引入Spring依赖spring-context(必须要加入此依赖才能执行FactoryBean的接口,是Spring的基本依赖)
B.创建Spring配置文件,在配置文件中,向Spring容器注册一个Bean(当启动容器时,创建该对象,实例化该对象了)
1.基于配置文件.xml的IOC
1.Spring的实例化
1.默认的构造方法实例化对象
public class User {
}
<bean id="user" class="com.pan.entity.User"/>
2.使用工厂实例化(工厂方法模式)
public class BeanFactory {
User user(){
return new User();
}
}
<!-- 根据工厂实例-->
<bean id="BeanFactory" class="com.pan.utils.BeanFactory"/>
<bean id="User" factory-bean="BeanFactory" factory-method="user"/>
3.使用静态工厂实例化(简单工厂模式)
public class BeanFactory01 {
public static User user(){
return new User();
}
}
<!-- 静态工厂实例-->
<bean id="user01" class="com.pan.utils.BeanFactory01" factory-method="user"/>
注意:工厂实例化中的factory-bean和factory-method是我们自己定义的,在spring中当你编写的类直接实现FactoryBean接口之后,factory-bean和factory-method就不需要指定了
4.FactoryBean
作用:创建比较复杂的Bean
step01-先导入spring-context依赖
step02-写一个UserDao类
public class Userdao {
public Userdao(){
}
public static Userdao getInstance(){
return new Userdao();
}
}
step03-实现FactoryBean接口
public class UserDaoFactoryBean implements FactoryBean {
//返回的是获得一个对象 的方法,也可以直接new一个对象
@Override
public Object getObject() throws Exception {
return Userdao.getInstance();
}
//返回的是一个class,也可以不写
@Override
public Class<?> getObjectType() {
return Userdao.class;
}
// false表示不是单例
@Override
public boolean isSingleton() {
return false;
}
}
step04-在xml写入Bean
<!-- 会注入两个bean到spring容器,UserDao和UserDaoFactoryBean-->
<bean class="com.pan.factory.UserDaoFactoryBean" id="userdao"/>
step05-测试
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
Userdao userdao = (Userdao) ctx.getBean("userdao");
Userdao userdao2 = (Userdao) ctx.getBean("userdao");
System.out.println(userdao);
System.out.println(userdao == userdao2);//false,因为不是单例
// 获取到工厂bean的实例
UserDaoFactoryBean bean = (UserDaoFactoryBean) ctx.getBean("&userdao");
System.out.println(bean);
实例-时间类型的转换
2.使用setter注入:
分为简单类型注入和引用类型注入,简单类型注入值使用value,引用类型使用ref属性
(使用setter注入必须提供无参构造方法,必须提供set方法,name属性写的是set方法后面的属性名)
```xml
//简单类型注入
<bean id="stu" class="com.pan.entry.Student">
<property name="name" value="胖">
</bean>
//引用类型注入
<bean id="school" class="com.pan.entry.School">
<property name="name" value="清华大学"></property>
<property name="address" value="海淀区"></property>
</bean>
<bean id="stu" class="com.pan.entry.Student">
<property name="name" value="张三"></property>
<property name="age" value="21"></property>
<property name="school" ref="school"></property>
</bean>
```
**注意:简单数据类型用的是valve属性赋值,date虽然是简单类型,当时我们通常用ref,因为data需要simpledataeformat来转换**
注意:简单数据类型用的是valve属性赋值,date虽然是简单类型,当时我们通常用ref,因为data需要simpledataeformat来转换
3.使用构造方法注入(了解)
1.使用带参构造方法注入参数值
<!--创建学校的对象,使用构造方法参数名称注入值-->
<bean id="school" class="com.pan.entry.School">
<constructor-arg name="address" value="海淀区"></constructor-arg>
<constructor-arg name="name" value="清华大学"></constructor-arg>
</bean>
2.使用带参构造方法的下标注入值
<!--创建学生对象,使用构造方法的参数的下标注入值-->
<bean id="stu" class="com.pan.entry.School">
<constructor-arg index="0" value="钱七"></constructor-arg>
<constructor-arg index="2" ref="school"></constructor-arg>
<constructor-arg index="1" value="22"></constructor-arg>
</bean>
3.使用带参构造方法默认顺序注入值
<!--创建学生对象,使用默认的构造方法的参数顺序-->
<bean id="stuSequence" class="com.pan.entry.School">
<constructor-arg value="陈十"></constructor-arg>
<constructor-arg value="22"></constructor-arg>
<constructor-arg ref="school"></constructor-arg>
</bean>
4.引入外部Propertis文件(context命名空间)
context命名空间
注意:username会先找系统的值,所以我们正常都是加前缀db.username或者jdbc.username
5.Bean是单例的
注意:scope是多个值来的,我们加入spring-mvcjar的时候就会有request,session
1.scope的八种属性
基本属性引入(默认情况下,注入到Spring容器中的Bean都是单例的)
scope属性可以修改的
singleton:表示这个Bean是单例的
prototype:表示多例的
request:在web环境中,每一次请求中,多次拿到这个bean,都是同一个
session:在web环境中,每一次会话中,多次拿到这个bean,都是同一个
application:在web环境中,每一次应用实例中,多次拿到这个bean,都是同一个
2.自定义属性(了解)
6.Bean的生命周期(面试)
Bean的生命周期之五步:
第一步:实例化bean
第二步:给对象属性赋值
第三步:初始化方法
第四步:使用bean
第五步:销毁方法
1.生命周期之五步
step01-创建一个实体类
public class User {
private String username;
public User(){
System.out.println("01.无参数构造方法执行");
}
public void setUsername(String username){
System.out.println("02.给对象属性赋值");
this.username=username;
}
public void init(){
System.out.println("03.初始化Bean");
}
public void destory(){
System.out.println("05.销毁Bean");
}
}
step02-在spring配置文件中将实体类注入到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="user" class="com.pan.springlife.entity.User" init-method="init" destroy-method="destory">
<property name="username" value="ok"/>
</bean>
</beans>
step03-测试
@Test
void contextLoads() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
User user = applicationContext.getBean("user", User.class);
System.out.println("04.使用bean"+user);
applicationContext.close();
}
2.生命周期之七步
step01-定义一个实体类
public class User {
private String name;
public User() {
System.out.println("第一步:执行了构造方法");
}
//名字任意取
public void init(){
System.out.println("第三步:初始化方法");
}
//名字任意取
public void destory(){
System.out.println("第五步:销毁方法执行");
}
public String getName() {
return name;
}
public void setName(String name) {
System.out.println("第二步:给对象的属性赋值");
this.name = name;
}
}
step02-在xml中定义初始化方法和销毁方法
<!-- lazy-init延迟初始化-->
init-method:初始化方法
destroy-method:销毁
<bean class="com.pan.com.pan.entry.User" id="user" init-method="init" destroy- method="destory" lazy-init="false"></bean>
<!--Bean后处理器必须要放在需要调用bean后处理器的配置文件上,否则不起作用-->
<bean class="com.pan.springlife.config.LogBeanPostProcessor"/>
step03-Bean后处理器
/**
* 日志Bean后处理器
*/
public class LogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行Bean后处理器的Before方法");
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("执行Bean后处理器的After方法");
return null;
}
}
3.生命周期之十步
Bean的生命周期之十步:
第一步:实例化bean
第二步:给对象属性赋值
第三步:实现相关的Aware接口
第四步:Bean后处理器Before
第五步:实现InitializingBean接口
第六步:初始化方法
第七步:Bean后处理器After
第八步:使用bean
第九步:实现DisposableBean接口
第十步:销毁方法
step01-User实体类实现Aware,InitializingBean,DisposableBean接口,调用它们的方法
public class User implements BeanNameAware, BeanFactoryAware, BeanClassLoaderAware, InitializingBean, DisposableBean {
private String username;
public User(){
System.out.println("01.无参数构造方法执行");
}
public void setUsername(String username){
System.out.println("02.给对象属性赋值");
this.username=username;
}
public void initBean(){
System.out.println("03.初始化Bean");
}
public void destroyBean(){
System.out.println("05.销毁Bean");
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("Bean加载器"+classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("生产这个Bean的工厂对象"+beanFactory);
}
@Override
public void setBeanName(String s) {
System.out.println("这个bean的名字"+s);
}
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean执行");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean执行");
}
}
2.基于注解的IOC(DI)
1.基于注解开发一定要记住要添加包扫描:
1.在spring核心配置文件里添加包扫描
<!--添加包扫描-->
<context:component-scan base-package="com.pan"></context:component-scan>
2.在配置类中使用注解添加包扫描
@ComponentScan(basePackages = "com.pan")
2.开发用到的注解
1.创建对象的注解:
1.@Component:可以创建任意对象,创建对象的默认名称是类名的驼峰命名法,也可以指定对象的名称@Component("stu")
2.@Controller:专门用来创建控制层的对象,可以接收用户的请求,可以返回处理结果给客户端
3.@Service:专门用来创建业务逻辑层,负责向下访问数据访问层,处理完后结果返回给界面层
4.@Repository:专门用来创建数据访问层的对象,负责数据库中的增删改查所有操作
2.依赖注入的注解:
简单类型(8中基本类型+String)值类型的注入
1.@Value(""):用来给简单类型注入值
引用类型的注入(相当于xml的ref引用类型注入)
1.@Autowired:使用类型注入值,从整个Bean工厂中搜索同源类型的对象进行注入
同源类型:是指当你在一个类中标记了该注解时,会直接在spring容器中寻找有没有该类被注入进来,并调用
2.@Autowired,@Qualifier("名称"):使用名称注入值,从整个Bean工厂中搜索相同名称的对象进行注入
@Component("schoolNew") //交给spring去创建对象
public class School {
//引用类型按类型名称注入
@Autowired
@Qualifier("schoolNew")
private School school;
注意
在项目开发的时候,一个配置文件存在很多的隐患,而且都是多人合作开发的,所以需要拆分配置文件
1.按层拆:根据数据访问层,逻辑业务层,界面层进行拆分。
2.按功能拆:根据每一个类进行对应的功能进行拆分
当项目写完后,在进行整合
<import resource="applicatoinContext_mapper.xml"></import>
<import resource="applicatoinContext_service.xml"></import>
<import resource="applicatoinContext_controller.xml"></import>
3.java配置类
实例:书籍类里包含着作者类
step01-Author类
package com.pan.entry;
public class Author {
private String name;
public Author() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
Book类
package com.pan.entry;
public class Book {
private String name;
private Author author;
public Book() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Author getAuthor() {
return author;
}
public void setAuthor(Author author) {
this.author = author;
}
}
step02-JavaConfig配置类
package com.pan.config;
import com.pan.dao.Userdao;
import com.pan.entry.Author;
import com.pan.entry.Book;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
//这个是我们的java配置类,这个类相当于applicationContext.xml
/*
@Configuration:这个注解是标记当前类是配置类
如果配置类上包含@Configuration注解,那么在配置类内部,方法之间相互调用时,如果方法上包含@Bean注解,
则这个方法不会被直接调用而是在先去Spring容器中查找该方法对应的Bean,如果存在直接使用Spring容器中的Bean
@Component:不会拦截去Spring容器中查找Bean,是直接调用该方法
*/
@Configuration
public class JavaConfig {
/*
@Bean:相当于application里的<bean/>
*/
@Bean("userDao2")
Userdao userDao(){
return new Userdao();
}
@Bean
Author author(){
Author author = new Author();
author.setName("潘权荣");
return author;
}
@Bean
Author author1(){
Author author = new Author();
author.setName("潘权荣1");
return author;
}
/*
由于这个方法是由Spring容器去调用的,所以我们可以直接为这个方法添加参数,
这些参数的值,将来Spring容器会自动去容器中查找,然后完成Bean的注入
注意:Spring容器查找参数Bean的时候,是按照类型去查找的,不是按名字
@Qualifier("author1"):可以指定id
*/
@Bean
Book book(@Qualifier("author1")Author author){
Book book = new Book();
book.setName("三国杀");
book.setAuthor(author);
return book;
}
// @Bean
// Book book(){
// Book book = new Book();
// book.setName("三国杀");
// book.setAuthor(author());
// return book;
// }
}
4.组件扫描注入-实例
实例:通过Dao,Service,Servlet来讲述组件注解
step01-Dao层
package com.pan.dao;
import org.springframework.stereotype.Repository;
/*
这样就可以不需要在配置类中一个个加到里面去,
可以直接在类上加注解,自动将当前类注册到Spring容器中
@Controller:一般加在视图层,也就是servlet
@Service:一般加在业务层,也就是Service
@Repository:一般加在dao层
@Component:对于一些身份不好归类的Bean,使用这个注解
*/
//表示将当前类注册到Spring容器中去,默认情况下,Bean的名称是类名首字母小写,也可以在@Repository("ok")配置
@Repository
public class UserDao {
public String sayHello(){
return "hello";
}
}
step02-service层
package com.pan.service;
import com.pan.dao.UserDao;
import org.springframework.stereotype.Service;
@Service
public class UserService {
private UserDao userDao;
/*
Spring容器在初始化UserService的时候,需要调用到当前这个构造方法,
但是这个构造方法有参数,spring容器会自动去查找到这个参数,并注入进来
*/
public UserService(UserDao userDao){
this.userDao=userDao;
}
public String sayHello(){
return userDao.sayHello();
}
}
step03-servlet
package com.pan.servlet;
import com.pan.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
@Controller
public class UserServlet {
@Autowired
private UserService userService;
/*
@Autowired:当存在多个构造方法的时候可以通过Autowired注解来指定使用哪一个构造方法来完成Bean的构造
也表示去spring容器查找一个UserService类型的Bean,并赋值给当前变量
*/
// @Autowired
// public UserServlet(UserService userService){
// this.userService=userService;
// }
public String sayHello(){
return userService.sayHello();
}
}
step04-JavaConfig配置类
package com.pan.config;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
/*
刚刚那四个注解是不会自动注入到Spring容器中的,
需要通过@ComponentScan来进行组件扫描
*/
@Configuration
@ComponentScan(basePackages = "com.pan")
public class JavaConfig {
}
step05-测试
import com.pan.config.JavaConfig;
import com.pan.config.MyBeanFactory;
import com.pan.servlet.UserServlet;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
UserServlet u = ctx.getBean(UserServlet.class);
System.out.println(u.sayHello());
}
}
注意:@Autowired:当存在多个构造方法的时候可以通过Autowired注解来指定使用哪一个构造方法来完成Bean的构造,也表示去spring容器查找一个UserService类型的Bean,并赋值给当前变量
5.加载配置文件
例如:获取配置文件中的value
step01-先定义一个类
package com.pan.model;
public class DataSource {
private String url;
private String username;
private String password;
@Override
public String toString() {
return "DataSource{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
step02-JavaConfig配置类
package com.pan.config;
import com.pan.model.DataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
//用来加载配置文件
@PropertySource("db.properties")
public class JavaConfig01 {
//用来注入properties配置文件中的内容
@Value("${db.url}")
String url;
@Value("${db.username}")
String username;
@Value("${db.password}")
String password;
@Bean
DataSource dataSource(){
DataSource dataSource = new DataSource();
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
}
step03-测试
import com.pan.config.JavaConfig01;
import com.pan.model.DataSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo03 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig01.class);
DataSource ds = ctx.getBean(DataSource.class);
System.out.println(ds.toString());
}
}
6.配置类的整合
step01-导入依赖
<dependencies>
<!--QueryRunner,封装jdbc的工具-->
<dependency>
<groupId>commons-dbutils</groupId>
<artifactId>commons-dbutils</artifactId>
<version>1.7</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
#mysql连接
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
#德鲁伊数据源
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
#测试工具
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
step02-User类
public class User {
private String username;
private Integer money;
public User() {
}
@Override
public String toString() {
return "User{" +
"username='" + username + '\'' +
", money=" + money +
'}';
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public Integer getMoney() {
return money;
}
public void setMoney(Integer money) {
this.money = money;
}
}
step03-dao层
@Repository("userDaoImpl")
public class UserDaoImpl implements Userdao {
@Autowired
@Qualifier("queryRunner")
private QueryRunner queryRunner;
@Override
public List<User> selectAll() {
List<User> userList = null;
try {
userList = queryRunner.query("select * from user", new BeanListHandler<User>(User.class));
} catch (SQLException e) {
e.printStackTrace();
}
return userList;
}
}
step04-service层
@Service
public class UserServiceImpl implements UserService {
@Autowired
@Qualifier("userDaoImpl")
private Userdao userdao;
@Override
public List<User> selectAll() {
return userdao.selectAll();
}
}
step05-配置类和配置文件
db.driverClassName=com.mysql.cj.jdbc.Driver
db.username=root
db.password=123456
db.url=jdbc:mysql:///student02?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&setUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false
db.maxWait=60000
db.initialSize=100
db.maxActive=200
db.minIdle=10
@Configuration
@ComponentScan(basePackages = "com.pan")
@PropertySource("classpath:db.properties")
public class UserConfig {
@Value("${db.driverClassName}")
private String driverClassName;
@Value("${db.url}")
private String url;
@Value("${db.username}")
private String username;
@Value("${db.password}")
private String password;
@Bean
DataSource dataSource(){
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(driverClassName);
dataSource.setUrl(url);
dataSource.setUsername(username);
dataSource.setPassword(password);
return dataSource;
}
@Bean
QueryRunner queryRunner(@Qualifier("dataSource") DataSource dataSource){
return new QueryRunner(dataSource);
}
}
step06-测试
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(UserConfig.class);
UserServiceImpl userServiceImpl = applicationContext.getBean("userServiceImpl", UserServiceImpl.class);
userServiceImpl.selectAll().forEach(user -> {
System.out.println(user);
});
4.Spring整合Jdbc
step01-导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.15</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
</dependencies>
db.driverClassName=com.mysql.cj.jdbc.Driver
db.username=root
db.password=123456
db.url=jdbc:mysql:///student02?serverTimezone=Asia/Shanghai&characterEncoding=utf-8&setUnicode=true&zeroDateTimeBehavior=convertToNull&useSSL=false
db.maxWait=60000
db.initialSize=100
db.maxActive=200
db.minIdle=10
step02-建立实体类
public class User {
private String username;
private Integer money;
public User() {
}
}
step03-Dao层
public interface UserDao {
//根据名字查询
User selectByUsername(String username);
//查询所有
List<User> selectAll();
//根据名字删除
int deleteByUserName(String username);
//查询数量
int selectCount();
}
public class UserDaoImpl implements UserDao {
private JdbcTemplate jdbcTemplate;
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
@Override
public User selectByUsername(String username) {
User user = null;
try {
user = jdbcTemplate.queryForObject("select * from user where username=?", new BeanPropertyRowMapper<User>(User.class), username);
} catch (DataAccessException e) {
e.printStackTrace();
}
return user;
}
@Override
public List<User> selectAll() {
List<User> userList = jdbcTemplate.query("select * from user", new BeanPropertyRowMapper<>(User.class));
return userList;
}
@Override
public int deleteByUserName(String username) {
int update = jdbcTemplate.update("delete from USER where username=?", username);
return update;
}
@Override
public int selectCount() {
Integer integer = jdbcTemplate.queryForObject("select count(1) from user", Integer.class);
return integer;
}
}
step04-Service
public interface UserService {
User selectByUsername(String username);
List<User> selectAll();
int deleteByUserName(String username);
int selectCount();
}
public class UserServiceImpl implements UserService {
private UserDao userDao;
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Override
public User selectByUsername(String username) {
return userDao.selectByUsername(username);
}
@Override
public List<User> selectAll() {
return userDao.selectAll();
}
@Override
public int deleteByUserName(String username) {
return userDao.deleteByUserName(username);
}
@Override
public int selectCount() {
return userDao.selectCount();
}
}
step05-spring核心配置文件或者java配置类
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.引入db.properties配置文件-->
<context:property-placeholder location="classpath:db.properties"/>
<!-- 2.引入数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="driverClassName" value="${db.driverClassName}"/>
<property name="url" value="${db.url}"/>
<property name="password" value="${db.password}"/>
<property name="username" value="${db.username}"/>
</bean>
<!-- 3.配置JdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="userDao" class="com.pan.dao.impl.UserDaoImpl">
<property name="jdbcTemplate" ref="jdbcTemplate"></property>
</bean>
<bean id="userService" class="com.pan.service.impl.UserServiceImpl">
<property name="userDao" ref="userDao"/>
</bean>
</beans>
package com.pan.p2.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.pan.p2.dao.UserDao;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import javax.sql.DataSource;
@Configuration
@PropertySource("classpath:db.properties")
public class JavaConfig {
@Value("${db.username}")
String username;
@Value("${db.password}")
String password;
@Value("${db.url}")
String url;
@Value("${db.driverClassName}")
String driverClassName;
@Bean
DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUrl(url);
ds.setUsername(username);
ds.setPassword(password);
ds.setDriverClassName(driverClassName);
return ds;
}
@Bean
JdbcTemplate jdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
@Bean
UserDao userDao(){
UserDao userDao = new UserDao();
userDao.setJdbcTemplate(jdbcTemplate());
return userDao;
}
@Bean
UserService userService(){
UserService userService=new UserService();
userService.setUserDao(userDao())
return userService;
}
}
5.条件注解
1.实例:通过windows系统和linux系统获取对应的命令符号
A.接口
package com.pan.condition;
public interface ShowCmd {
String cmd();
}
B.windows类
package com.pan.condition;
public class WindowsShowCmd implements ShowCmd {
@Override
public String cmd() {
return "dir";
}
}
C.linux类
package com.pan.condition;
public class LInusShowCmd implements ShowCmd {
@Override
public String cmd() {
return "ls";
}
}
D.window条件类
package com.pan.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class WindowsCondition implements Condition {
//这个方法如果返回的是true。就表示当前系统是windows
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
//获取操作系统的名字
String osName = context.getEnvironment().getProperty("os.name");
return osName.toLowerCase().contains("win");
}
}
E.linux条件类
package com.pan.condition;
import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;
public class LinuxCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
String osName = context.getEnvironment().getProperty("os.name");
return osName.toLowerCase().contains("linux");
}
}
F.JavaConfig配置类
package com.pan.config;
import com.pan.condition.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;
@Configuration
public class JavaConfig {
@Bean
//如果此条件满足,则这个bean会被注入到Spring容器中,则不会被存入Spring容器中
@Conditional(WindowsCondition.class)
ShowCmd windowCmd(){
return new WindowsShowCmd();
}
@Bean
@Conditional(LinuxCondition.class)
ShowCmd linusCmd(){
return new LInusShowCmd();
}
}
G.测试
package com.pan.test;
import com.pan.condition.ShowCmd;
import com.pan.config.JavaConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
ShowCmd sh = ctx.getBean(ShowCmd.class);
System.out.println(sh.cmd());
}
}
2.条件注解的封装——多环境切换
实例:根据开发和生产环境之间的切换
A.定义一个数据源类,为了账户和地址不同
package com.pan.model;
public class DataSource {
private String url;
private String username;
private String password;
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
@Override
public String toString() {
return "DataSource{" +
"url='" + url + '\'' +
", username='" + username + '\'' +
", password='" + password + '\'' +
'}';
}
}
B.JavaConfig配置类
package com.pan.config;
import com.pan.model.DataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
@Configuration
public class DsConfig {
/*
开发环境数据源
*/
@Bean
//@Profile封装了Condition注解,这个是spring提供的多环境切换的场景,不用像上面那样需要设定条件类
@Profile("dev")
DataSource devDs(){
DataSource ds = new DataSource();
ds.setUrl("jdbc:mysql:///dev");
ds.setPassword("123");
ds.setUsername("欧克");
return ds;
}
/*
生产环境数据源
*/
@Bean
@Profile("prod")
DataSource prodDs(){
DataSource ds = new DataSource();
ds.setUrl("jdbc:mysql://121.121.121.1:3306/prod");
ds.setUsername("潘权荣");
ds.setPassword("123");
return ds;
}
}
C.测试
package com.pan.test;
import com.pan.config.DsConfig;
import com.pan.model.DataSource;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo01 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
//设置当前系统环境是开发
ctx.getEnvironment().addActiveProfile("dev");
//设置配置类
ctx.register(DsConfig.class);
//刷新容器
ctx.refresh();
DataSource ds = ctx.getBean(DataSource.class);
System.out.println(ds);
}
}
6.Aware-查询bean
概念: 想要获取到容器中的配置、获取到容器中的 Bean 等等。在这 种情况下,就需要 Spring 容器中的 Bean 真正的意识到 Spring 容器的存在,才能要到这些东西
例如:BeanFactoryAware接口
package com.pan.config;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.stereotype.Component;
@Component
public class MyBeanFactory implements BeanFactoryAware {
private static BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
MyBeanFactory.beanFactory=beanFactory;
}
public static <T> T getBean(String beanName){
return (T) beanFactory.getBean(beanName);
}
}
import com.pan.config.JavaConfig;
import com.pan.config.MyBeanFactory;
import com.pan.servlet.UserServlet;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
//就是通过aware来找到你想要的bean,然后可以通过该bean来获取她的参数或方法
UserServlet userServlet = MyBeanFactory.getBean("userServlet");
System.out.println(userServlet.sayHello());
}
}
五.GoF之代理模式
1.静态代理
是在代理对象上写了被代理对象实现接口的方法,为什么是接口的方法呢,那是因为会有很多的代理对象工作(方法一致)。然后通过:接口 对象名=new 实现类(),通过多态性的机制来调用方法。
2.动态代理
1.JDK动态代理
注意:jdk动态代理,实际上就是动态的给Calculator这个接口生成一个实现类
jdk动态代理:只能给接口生成实现类,对于没有接口的类,无法使用jdk代理类的
实例:计算机
step01-接口
package com.pan.dynamic_proxy.jdk;
public interface Calculator {
int add(int a,int b);
int minus(int a,int b);
}
step02-实现类
package com.pan.dynamic_proxy.jdk;
public class CalculatorImpl implements Calculator {
@Override
public int add(int a, int b) {
System.out.println(a+"+"+b+"="+(a+b));
return a+b;
}
@Override
public int minus(int a, int b) {
System.out.println(a+"-"+b+"="+(a-b));
return a-b;
}
}
step03-Java代码增加动态
package com.pan.dynamic_proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* jdk动态代理,实际上就是动态的给Calculator这个接口生成一个实现类
* jdk动态代理:也只能给接口生成实现类,对于没有接口的类,无法使用jdk代理类的
*
* public class A implements Calculator{
* CalculatorImpl impl = new CalculatorImpl();
* int add(int a ,int b){
* //记录开始时间
* int o=impl.add(a,b);
* //记录结束时间
* //打印
* return o;
* }
* }
*/
public class Demo01 {
public static void main(String[] args) {
CalculatorImpl calculatorImpl = new CalculatorImpl();
ClassLoader loader = Demo01.class.getClassLoader();
Class[] interfaces = {Calculator.class};
InvocationHandler handler= new InvocationHandler() {
/**
*
* @param proxy 这个是具体的代理对象
* @param method 代理的方法
* @param args 代理的方法的参数
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
long startTime = System.currentTimeMillis();
//如果第一个参数写的是接口也可以,但是呢,需要写在一个方法里,传一个实例对象过来,因为第一个参数的意思就是,在执行该对象的时候,一同执行添加的新事务,新功能
Object invoke = method.invoke(calculatorImpl, args);
long endTime = System.currentTimeMillis();
System.out.println("执行时间"+(endTime-startTime));
return 99;
}
};
//调用这个方法可以获取一个动态代理对象
/**
* 1.类加载器
* 2.生成的动态代理对象所实现的接口
* 3.动态代理对象具体的处理逻辑
*/
//o就是Calculator这个接口动态生成的一个对象,上面有写内部意思
Calculator o = (Calculator) Proxy.newProxyInstance(loader, interfaces, handler);
// o.add方法执行的逻辑在invoke方法里面,并不直接执行com.pan.dynamic_proxy.jdk.CalculatorImpl里的add方法,也需要调用invoke方法前面与后面的事务
//在invoke方法中,动态的添加操作,只能在com.pan.dynamic_proxy.jdk.CalculatorImpl上面或者下面操作,
// 并不是在com.pan.dynamic_proxy.jdk.CalculatorImpl里面进行添加操作
System.out.println(o.add(1, 2));
}
}
注意:当要在业务对象中添加切面对象的时候,我们不需要自己手写一个代理类了,而是直接调用ProxyFactoryBean来绑定,就能够在实现业务的时候调用事务的操作(事务是写在切面对象中的)
如果还有疑惑那么请去查看: Spring(荣姐) · 语雀 (yuque.com)
2.CGLIB动态代理
注意:CGLIB动态代理:既可以代理接口(为接口生成实现类),也可以代理普通类(为类生成继承类)
需要的依赖:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
step01-自定义类
package com.pan.dynamic_proxy.cglib;
import com.pan.dynamic_proxy.jdk.Calculator;
public class CalculatorImpl{
public int add(int a, int b) {
System.out.println(a+"+"+b+"="+(a+b));
return a+b;
}
public int minus(int a, int b) {
System.out.println(a+"-"+b+"="+(a-b));
return a-b;
}
}
step02-实现CGLIB依赖的MethodInterceptor接口(拦截器)
package com.pan.dynamic_proxy.cglib;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 定义一个计算机的拦截器
*
* 这是一个方法的拦截器,当执行计算器的方法时候,会被自动拦截下来
*/
public class CalculatorInterceptor implements MethodInterceptor {
/**
* 这个方法类似于JDK动态代理中的invoke方法,要额外加进来的代码,就放在这个里边
* @param o 代理对象,是动态生成子类的对象
* @param method 代理方法
* @param objects 代理参数
* @param methodProxy 方法代理对象,相当于在jdk代理类中额外new的一个CalculatorImpl对象
* @return
* @throws Throwable
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
long startTime = System.currentTimeMillis();
//调用父类中的方法,父类就是CalculatorImpl,子类是一个动态生成的类
Object o1 = methodProxy.invokeSuper(o, objects);
long endTime = System.currentTimeMillis();
System.out.println(endTime-startTime);
return o1;
}
}
step03-增强器(一个是设置代理对象,一个是写拦截器类)
package com.pan.dynamic_proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
/**
* CGLIB动态代理:既可以代理接口(为接口生成实现类),也可以代理普通类(为类生成继承类)
*/
public class Demo02 {
public static void main(String[] args) {
//用于来创建代理对象
//创建字节增强器
Enhancer enhancer = new Enhancer();
//设置代理类,父类
enhancer.setSuperclass(CalculatorImpl.class);
enhancer.setCallback(new CalculatorInterceptor());
//这里拿到的并不是CalculatorImpl这个类的对象本身,而是他的子类的一个对象
CalculatorImpl calculatorImpl = (CalculatorImpl) enhancer.create();
calculatorImpl.minus(5,6);
}
}
六.Aop
1.Spring支持的AOP的实现(了解)
Spring支持AOP的编程,常用的有以下几种:
1)Before通知:在目标方法被调用前调用,涉及接口org.springframework.aop.MethodBeforeAdvice;
2)After通知:在目标方法被调用后调用,涉及接口为org.springframework.aop.AfterReturningAdvice;
3)Throws通知:目标方法抛出异常时调用,涉及接口org.springframework.aop.ThrowsAdvice;
4)Around通知:拦截对目标对象方法调用,涉及接口为org.aopalliance.intercept.MethodInterceptor。
2.Aop常用的术语
1)连接点:就是目标方法.因为在目标方法中要实现目标方法的功能和切面功能.
2)切入点(Pointcut):指定切入的位置,多个连接点构成切入点.切入点可以是一个目标方法,可以是一个类中的所有方法,可以是某个包下的所有类中的方法.
3)通知/增强(Advice):可以为切入点添加额外功能:前置通知,后置通知,异常通知,环绕通知等
4)目标对象:代理的目标对象
5)代理对象:被AOP织入通知后,产生的结果类
6)切面:由切点和通知组成,将横切逻辑织入切面所指定的连接点中。就是那些重复的,公共的,通用的功能称为切面,例如:日志,事务,权限.
3.AspectJ框架
1.AspectJ框架是一个优秀面向切面的框架,它扩展了java语言,提供了强大的切面实现
2.AspectJ常见通知类型
1)前置通知@Before
2)后置通知@AfterReturning
3)环绕通知@Around
4)最终通知@After:无论是否执行成功都会执行
5)定义切入点@Pointcut(了解)
3.AspectJ的切入点表达式(掌握)
规范的公式:
execution(访问权限 方法返回值 方法声明(参数) 异常类型)
简化后的公式:
execution( 方法返回值 方法声明(参数) )
用到的符号:
* 任意(通配符)
.. 如果出现在方法的参数中,则代表任意参数
如果出现在路径中,则代表本路径及其所有的子路径
示例:
execution(public * *(..) ) :任意的公共方法
execution(* set*(..)): 任何一个以“set”开始的方法
execution(* com.xyz.service.impl.*.*(..)) :任意的返回值类型,在com.xyz.service.impl包下的任意类的任意方法的任意参数
1.AOP-xml
step01-导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.22</version>
</dependency>
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.3.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>5.1.6.RELEASE</version>
</dependency>
step02-业务的接口和实现类
public interface UserService {
void save();
Integer add(int a,int b);
}
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("舒服");
}
@Override
public Integer add(int a, int b) {
return a+b;
}
}
step03-通知类
public class LogAspectj{
public void before(){
System.out.println("前置通知");
}
}
step04-配置文件
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
https://www.springframework.org/schema/aop/spring-aop.xsd">
<bean class="com.pan.spring_xml.service.impl.UserServiceImpl" id="userService"/>
<bean class="com.pan.spring_xml.aop.LogAspectj" id="logAspectj"/>
<aop:config proxy-target-class="false">
<aop:aspect ref="logAspectj">
<aop:before method="before" pointcut="execution(* com.pan.spring_xml.service.impl.UserServiceImpl.*(..))"/>
</aop:aspect>
</aop:config>
</beans>
step05-测试类
public class Test {
public static void main(String[] args) {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = (UserService) applicationContext.getBean("userService");
userService.save();
Integer add = userService.add(1, 2);
System.out.println(add);
}
}
/**
* 此类为切面类,包含各种切面方法
*/
@Aspect //交给AspectJ的框架去识别切面类
public class MyAspect {
@Before(value = "execution(public String com.bjpowernode.s01.SomeServiceImpl.*(String,int))")
public void myBefore(){
System.out.println("切面方法中的前置通知功能实现............");
}
// @Before(value = "execution(public * com.bjpowernode.s01.SomeServiceImpl.*(..))")
// public void myBefore(){
// System.out.println("切面方法中的前置通知功能实现............");
// }
4.在配置xml文件绑定
创建业务对象
<bean id="someService" class="com.bjpowernode.s01.SomeServiceImpl"></bean>
创建切面对象
<bean id="myaspect" class="com.bjpowernode.s01.MyAspect"></bean>
<!--绑定-->
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy>
#AspectJ框架切换JDK动态代理和CGLib动态代理
<aop:aspectj-autoproxy ></aop:aspectj-autoproxy> ===>默认是JDK动态代理,取时必须使用接口类型
<aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> ==>设置为CGLib子类代理,可以使用接口和实现类接
2.Spring封装动态代理——XML+自定义注解
可以自定义一个注解接口,并且pointcut里写的是:@annotation(com.pan.dynamic_proxy.spring_xml.MyAction)使用,这样只有加上MyAction注解的才会执行通知
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.METHOD)
public @interface MyAction {
}
step01-接口
public interface Calculator {
int add(int a, int b);
int minus(int a, int b);
}
step02-实现类中间可以利用注解类来设置切点,需不需要在该方法中用通知
public class CalculatorImpl implements Calculator {
@Override
@MyAction
public int add(int a, int b) {
System.out.println(a+"+"+b+"="+(a+b));
return a+b;
}
@Override
public int minus(int a, int b) {
System.out.println(a+"-"+b+"="+(a-b));
return a-b;
}
}
step03-通知类
public class LogAspect {
public void before(JoinPoint jp){
//获取被拦截下来的方法名称
String name=jp.getSignature().getName();
System.out.println(name+"方法开始执行了");
}
public void after(JoinPoint jb){
String name = jb.getSignature().getName();
System.out.println(name+"方法执行结束");
}
/**
* 拦截方法发生异常时触发
*
* 这里的参数类型就表示这个异常通知能够捕获的参数类型,如果写的是任意子类,出的错不是参数写的异常,那么将不会执行异常通知
*/
public void ex(JoinPoint jb,Exception e){
String name = jb.getSignature().getName();
System.out.println(name+"异常是"+e.getMessage());
}
// r:表示目标方法的返回值,这个参数的类型必须和方法的返回值类型相匹配,不然无法返回
public void re(JoinPoint jb,Object r){
String name = jb.getSignature().getName();
System.out.println(name+"返回"+r);
}
/**
* 环绕通知:有点像jdk动态代理的invoke
* @param pjp
* @return
*/
public Object round(ProceedingJoinPoint pjp){
try {
//表示让目标方法继续向下执行
// 这一行类似:Object result =method.invoke(被代理对象,args);
System.out.println("前置通知");
Object proceed = pjp.proceed();
System.out.println("后置通知");
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知");
}
return null;
}
}
step04-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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 虽然写的是CalculatorImpl,但是实际运行的是Calculator.class-->
<bean class="com.pan.dynamic_proxy.spring_xml.service.CalculatorImpl" id="calculator"/>
<!-- 开始配置Aop-->
<bean class="com.pan.dynamic_proxy.spring_xml.aop.LogAspect" id="logAspect"></bean>
<!-- proxy-target-class:如果是true则使用的是CGLib动态代理-->
<aop:config proxy-target-class="false">
<!--
设置了切点,那么只有在符合该方法的地方才能有通知
这个地方用来配置切点 expression:拦截规则,相当于是获取该方法,返回该方法的返回值
第一个int:表示方法的返回值
接下来是该方法的全路径
最后还要指定方法的参数类型(防止方法有重载)
-->
<!-- <aop:pointcut id="pc1" expression="execution(int com.pan.dynamic_proxy.spring_xml.service.CalculatorImpl.add(int,int))"/>-->
<!--
第一个*表示返回值任意
第二个*表示包下的所有类任意
第三个*表示方法名任意
..表示参数任意:类型任意,个数任意
-->
<!-- <aop:pointcut id="pc1" expression="execution(* com.pan.dynamic_proxy.spring_xml.service.*.*(..))"/>-->
<!-- @annotation:表示拦截所有有该注解的方法,类不行,注解是放在方法上的
@within:表示拦截所有有该注解的类中的方法,注解是放在类上的
-->
<aop:pointcut id="pc1" expression="@annotation(com.pan.dynamic_proxy.spring_xml.MyAction)"/>
<!--
这个地方是配置切面的地方,切面是切点+通知
-->
<aop:aspect ref="logAspect">
<!-- aop:before:是配置前置通知,method指的是前置通知的方法名称,pointcut-ref指引用的切点-->
<aop:before method="before" pointcut-ref="pc1"/>
<aop:after method="after" pointcut-ref="pc1"/>
<!-- throwing="e"表示名称为e的参数,里面保存着异常信息 -->
<aop:after-throwing method="ex" pointcut-ref="pc1" throwing="e"/>
<aop:after-returning method="re" pointcut-ref="pc1" returning="r"/>
<aop:around method="round" pointcut-ref="pc1"></aop:around>
</aop:aspect>
</aop:config>
</beans>
step05-测试类
/**
* Spring中,如果被代理的对象有接口,默认使用Jdk动态代理
* 如果被代理的对象没有接口,则默认使用CGLIB动态代理
*/
public class Demo03 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
//调用的是接口的实现类,在spring中重新生成一个实现类,而不是调用CalculatorImpl
Calculator calculator = (Calculator) ctx.getBean("calculator", Calculator.class);
calculator.add(1, 2);
calculator.minus(4, 3);
}
}
3.Aop-JavaConfig
step-01业务层
public interface UserService {
void save();
Integer add(int a,int b);
}
@Service("userService1")
public class UserServiceImpl implements UserService {
@Override
public void save() {
System.out.println("ok");
}
@Override
public Integer add(int a, int b) {
return a+b;
}
}
step02-Aop切面
@Aspect
//开启Aop自动代理功能 这个注解就类似于在xml文件中配置的<aop:config proxy-target-class="false">
@EnableAspectJAutoProxy()
@Component
public class AopAspectj {
@Pointcut("execution(* com.pan.spring_config.service.impl.UserServiceImpl.*(..))")
public void pc(){
}
@Before("pc()")
public void before(){
System.out.println("前置通知。。。");
}
}
step03-配置类
@Configuration
@ComponentScan(basePackages = "com.pan.spring_config")
public class JavaConfig {
}
step04-测试
public class Test02 {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(JavaConfig.class);
UserService userService = applicationContext.getBean("userService1", UserService.class);
userService.save();
}
}
4.Spring封装动态代理——JavaConfig+自定义注解
step01-接口+自定义注解
public interface Calculator {
int add(int a, int b);
int minus(int a, int b);
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyAction{
}
step02-实现类中间可以利用注解类来设置切点,需要在该方法中用通知
public class CalculatorImpl implements Calculator {
@Override
//使用aop自定义注解,因为不是每一个方法都需要调用通知,仅仅只是作为标记而已,将来代码运行的时候,去检查方法上是否有该注解,如有就拦截下来,按AOP方式进行处理
@MyAction
public int add(int a, int b) {
System.out.println(a+"+"+b+"="+(a+b));
return a+b;
}
@Override
public int minus(int a, int b) {
System.out.println(a+"-"+b+"="+(a-b));
return a-b;
}
}
step03-在通知类里设置切面=切点+通知
@Component
//开启Aop自动代理功能 这个注解就类似于在xml文件中配置的<aop:config proxy-target-class="false">
@EnableAspectJAutoProxy()
//表示当前类是一个切面类,意味着这个类既有切点,又有通知
@Aspect
public class LogAspect {
//通过定义一个空方法,然后通过@Poincut去定义一个切入点
//@Pointcut("execution(* com.pan.dynamic_proxy.spring_javaconfig.service.*.*(..))")
@Pointcut("@annotation(com.pan.dynamic_proxy.spring_xml.MyAction)")
public void pc(){
}
@Before("pc()")
public void before(JoinPoint jp){
//获取被拦截下来的方法名称
String name=jp.getSignature().getName();
System.out.println(name+"方法开始执行了");
}
@After("pc()")
public void after(JoinPoint jb){
String name = jb.getSignature().getName();
System.out.println(name+"方法执行结束");
}
@AfterThrowing(value = "pc()",throwing = "e")
public void ex(JoinPoint jb,Exception e){
String name = jb.getSignature().getName();
System.out.println(name+"异常是"+e.getMessage());
}
@AfterReturning(value = "pc()",returning = "r")
public void re(JoinPoint jb,Object r){
String name = jb.getSignature().getName();
System.out.println(name+"返回"+r);
}
/**
* 环绕通知:有点像jdk动态代理的invoke
* @param pjp
* @return
*/
@Around("pc()")
public Object round(ProceedingJoinPoint pjp){
try {
//表示让目标方法继续向下执行
// 这一行类似:Object result =method.invoke(被代理对象,args);
System.out.println("前置通知");
Object proceed = pjp.proceed();
System.out.println("后置通知");
return proceed;
} catch (Throwable throwable) {
throwable.printStackTrace();
System.out.println("异常通知");
}
return null;
}
}
step04-配置类
@Configuration
@ComponentScan(basePackages = {"com.pan.dynamic_proxy.spring_javaconfig.aop", "com.pan.dynamic_proxy.spring_javaconfig.service"})
public class JavaConfig {
}
step05-测试
public class Demo04 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
Calculator calculator = (Calculator) ctx.getBean(Calculator.class);
calculator.add(2,3);
}
}
七.事务(ACID)
1.事务的特性
A.原子性(Atomicity): 一个事务(transaction)中的所有操作,或者全部完成,或者全部不完 成,不会结束在中间某个环节。事务在执行过程中发生错误,会被回滚(Rollback)到事务开始前 的状态,就像这个事务从来没有执行过一样。即,事务不可分割、不可约简。
B.一致性(Consistency): 在事务开始之前和事务结束以后,数据库的完整性没有被破坏。这表 示写入的资料必须完全符合所有的预设约束、触发器、级联回滚等。
C.隔离性(Isolation): 数据库允许多个并发事务同时对其数据进行读写和修改的能力,隔离性可 以防止多个事务并发执行时由于交叉执行而导致数据的不一致。事务隔离分为不同级别,包括未提 交读(Read uncommitted)、提交读(read committed)、可重复读(repeatable read)和串 行化(Serializable)。
D.持久性(Durability): 事务处理结束后,对数据的修改就是永久的,即便系统故障也不会丢失。
2.隔离性级别
未提交读(Read uncommitted):在事务中修改了数据,但是还未提交,此时别人事务来查询,可以看见未提交的数据。(脏读、不可重复读、幻读)
提交读(read committed):在事务中,修改了数据,并且已经提交了,别的事务可以看见已经提交的数据。(不可重复读、幻读)
可重复读(repeatable read),默认即此。首先开启 A事务,然后开启 B 事务,B 事务中修改了数据,B 事务中的修改对 A是不可见的,也就是 A 事务开启的一瞬间,数据是什么样子,在 A 事务整个执行过程中,数据都是什么样子。(幻读)
串行化(Serializable):同一时间只能有一个事务执行。
读到别的事务未提交的数据,叫做脏读。
在同一个事务中,反复查询多条数据,得到的结果不一样,叫做不可重复读。
幻读
隔离级别指的是,当一个事务在执行的过程中,它里边关于数据的修改是否可以被别的事务看见。
上面四种隔离级别,从上往下,性能依次降低,但是隔离级别依次升高。
3.编辑式事务——事务管理和事务模板
step01-导入依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
step02-建立UserDao
package com.pan.p1.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void addMoney(String name,Double money){
jdbcTemplate.update("update user set money=money+? where username=?",money,name);
}
public void minusMoney(String name,Double money){
jdbcTemplate.update("update user set money=money-? where username=?",money,name);
}
}
step03-建立UserService
package com.pan.p1.service;
import com.pan.p1.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;
import org.springframework.transaction.support.TransactionCallbackWithoutResult;
import org.springframework.transaction.support.TransactionTemplate;
@Service
public class UserService {
//@Autowired从spring容器中获取该bean,该bean已经在xml或者配置类中注入进spring容器中了
@Autowired
UserDao userDao;
// 这个是事务接口,一般以后不会修改,不管以后用的是什么来连接数据库
@Autowired
PlatformTransactionManager platformTransactionManager;
@Autowired
TransactionTemplate transactionTemplate;
//事务管理的方式
public void transferMoney(String from,String to,Double money){
//创建事务的定义
DefaultTransactionDefinition definition = new DefaultTransactionDefinition();
//获取一个事务
TransactionStatus transaction = platformTransactionManager.getTransaction(definition);
try {
userDao.minusMoney(from,money);
// int i = 1 / 0;
userDao.addMoney(to,money);
platformTransactionManager.commit(transaction);
} catch (Exception e) {
e.printStackTrace();
platformTransactionManager.rollback(transaction);
}
}
//事务模板的方式
public void transferMoney02(String from,String to,Double money){
//事务模板
transactionTemplate.execute(new TransactionCallbackWithoutResult() {
@Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
try {
userDao.minusMoney(from,money);
// int i = 1 / 0;
userDao.addMoney(to,money);
// 事务完成
status.isCompleted();
} catch (Exception e) {
e.printStackTrace();
// 事务回滚
status.setRollbackOnly();
}
}
});
}
}
step04-在applicationContext.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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 扫描包-->
<context:component-scan base-package="com.pan.p1"/>
<!-- 引入db.properties配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 引入数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!-- 配置JdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 编程式事务:只需要提供一个事务管理器或者一个事务模板即可
spring中,统一了事务的编程方式,PlatformTransactionManager这是Spring中提供的事务管理的一个规范
-->
<!-- 配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!-- 也可以通过事务模板来实现编程式事务,不过使用事务模板也需要先配事务管理-->
<bean class="org.springframework.transaction.support.TransactionTemplate" id="transactionTemplate">
<property name="transactionManager" ref="transactionManager"/>
</bean>
</beans>
step05-测试
package com.pan.p1;
import com.pan.p1.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo01 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = ctx.getBean(UserService.class);
userService.transferMoney02("zhangsan","lisi", 100.0);
}
}
4.声明式事务——无侵入
step01-Dao层
package com.pan.p2.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void addMoney(String name,Double money){
jdbcTemplate.update("update user set money=money+? where username=?",money,name);
}
public void minusMoney(String name,Double money){
jdbcTemplate.update("update user set money=money-? where username=?",money,name);
}
}
step02-Service层
package com.pan.p2.service;
import com.pan.p2.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
//@Autowired从spring容器中获取该bean,该bean已经在xml或者配置类中注入进spring容器中了
@Autowired
UserDao userDao;
public void transferMoney(String from,String to,Double money){
userDao.minusMoney(from,money);
// int i = 1 / 0;
userDao.addMoney(to,money);
}
}
step03-配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描包-->
<context:component-scan base-package="com.pan.p2"/>
<!-- 引入db.properties配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 引入数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!-- 配置JdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
声明式事务,配置三大步骤:
1.配置事务管理器
2.配置事务各种属性
3.配置Aop
-->
<!-- 配置事务管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
<!--
事务的底层就是Aop,所以事务的配置就是Aop的配置,这里就是配置Aop的通知和增强,
也就是将来要加入进去的代码都在tx:adivice里面
这里实际上只需要配置事务的基本属性
以下代码相当于
//开启事务管理器
@EnableTransactionManagement
-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<!-- 指定事务的方法名,也就是在哪些方法上加上事务 @Transaction-->
<tx:method name="transferMoney"/>
<!-- 这个表示给以add开始的方法添加事务-->
<tx:method name="add*"/>
</tx:attributes>
</tx:advice>
<!-- 配置Aop 切面-->
<aop:config>
<aop:pointcut id="pc1" expression="execution(* com.pan.p2.service.UserService.*(..))"/>
<!-- aop:aspect:是可以在切点上写多个通知,连接多个切点,但aop:advisor只能单个通知和切点-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pc1"/>
</aop:config>
</beans>
step04-测试
package com.pan.p2;
import com.pan.p2.service.UserService;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class Demo02 {
public static void main(String[] args) {
ClassPathXmlApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext02.xml");
UserService userService = ctx.getBean(UserService.class);
userService.transferMoney("zhangsan","lisi",10.0);
}
}
5.声明式事务——有轻微侵入,配置类
step01-导入依赖
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.23</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.11</version>
</dependency>
<!-- 下面是添加通知,切点 aop的依赖-->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.9.9.1</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.9.1</version>
</dependency>
</dependencies>
step02-Dao层
package com.pan.p3.dao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
@Repository
public class UserDao {
@Autowired
JdbcTemplate jdbcTemplate;
public void addMoney(String name,Double money){
jdbcTemplate.update("update user set money=money+? where username=?",money,name);
}
public void minusMoney(String name,Double money){
jdbcTemplate.update("update user set money=money-? where username=?",money,name);
}
}
step03-Service层
package com.pan.p3.service;
import com.pan.p3.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
//@Autowired从spring容器中获取该bean,该bean已经在xml或者配置类中注入进spring容器中了
@Autowired
UserDao userDao;
@Transactional
public void transferMoney(String from,String to,Double money){
userDao.minusMoney(from,money);
// int i = 1 / 0;
userDao.addMoney(to,money);
}
}
step04-配置类
package com.pan.p3.config;
import com.alibaba.druid.pool.DruidDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
@Configuration
@ComponentScan(basePackages = "com.pan.p3")
@PropertySource("classpath:db.properties")
//开启事务管理器
@EnableTransactionManagement
public class JavaConfig {
@Value("${db.username}")
String username;
@Value("${db.password}")
String password;
@Value("${db.url}")
String url;
@Bean
DataSource dataSource(){
DruidDataSource ds = new DruidDataSource();
ds.setUsername(username);
ds.setPassword(password);
ds.setUrl(url);
return ds;
}
@Bean
JdbcTemplate jdbcTemplate(){
JdbcTemplate jdbcTemplate = new JdbcTemplate();
jdbcTemplate.setDataSource(dataSource());
return jdbcTemplate;
}
@Bean
DataSourceTransactionManager dataSourceTransactionManager(){
DataSourceTransactionManager dataSourceTransactionManager = new DataSourceTransactionManager();
dataSourceTransactionManager.setDataSource(dataSource());
return dataSourceTransactionManager;
}
}
step05-测试
package com.pan.p3;
import com.pan.p3.config.JavaConfig;
import com.pan.p3.service.UserService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Demo03 {
public static void main(String[] args) {
AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext(JavaConfig.class);
UserService userService = ctx.getBean(UserService.class);
userService.transferMoney("zhangsan","lisi",1.0);
}
}
6.声明式事务——有轻微侵入,配置文件
配置文件
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/aop https://www.springframework.org/schema/aop/spring-aop.xsd">
<!-- 扫描包-->
<context:component-scan base-package="com.pan.p3"/>
<!-- 引入db.properties配置文件-->
<context:property-placeholder location="classpath:db.properties"></context:property-placeholder>
<!-- 引入数据源-->
<bean class="com.alibaba.druid.pool.DruidDataSource" id="dataSource">
<property name="url" value="${db.url}"/>
<property name="username" value="${db.username}"/>
<property name="password" value="${db.password}"/>
</bean>
<!-- 配置JdbcTemplate-->
<bean class="org.springframework.jdbc.core.JdbcTemplate" id="jdbcTemplate">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--
声明式事务,配置三大步骤:
1.配置事务管理器
2.配置事务各种属性
3.配置Aop
-->
<!-- 配置数据管理器-->
<bean class="org.springframework.jdbc.datasource.DataSourceTransactionManager" id="transactionManager">
<property name="dataSource" ref="dataSource"></property>
</bean>
//开启事务管理器
//相当于@EnableTransactionManagement
<tx:annotation-driven/>
</beans>
7.事务传播性行为
A.概念
事务传播行为是为了解决业务层方法之间互相调用的事务问题
B.事务传播的属性propagation(一共七种)
public enum Propagation {
REQUIRED(TransactionDefinition.PROPAGATION_REQUIRED),
SUPPORTS(TransactionDefinition.PROPAGATION_SUPPORTS),
MANDATORY(TransactionDefinition.PROPAGATION_MANDATORY),
REQUIRES_NEW(TransactionDefinition.PROPAGATION_REQUIRES_NEW),
NOT_SUPPORTED(TransactionDefinition.PROPAGATION_NOT_SUPPORTED),
NEVER(TransactionDefinition.PROPAGATION_NEVER),
NESTED(TransactionDefinition.PROPAGATION_NESTED);
}
TransactionDefinition.PROPAGATION_REQUIRED 使用的最多的一个事务传播行为,我们平时经常使用的 @Transactional 注解默认使用就是这个事务传 播行为。
如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。也就是说:
如果外部方法没有开启事务的话, Propagation.REQUIRED 修饰的内部方法会新开启自己的事 务,且开启的事务相互独立,互不干扰。
如果外部方法开启事务并且被 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 修饰 的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。
2.TransactionDefinition.PROPAGATION_REQUIRES_NEW
创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干 扰。
TransactionDefinition.PROPAGATION_NESTED :
如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等 价于 TransactionDefinition.PROPAGATION_REQUIRED 。也就是说:
在外部方法未开启事务的情况下 Propagation.NESTED 和 Propagation.REQUIRED 作用相同,修 饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。
如果外部方法开启事务的话, Propagation.NESTED 修饰的内部方法属于外部事务的子事务,外 部主事务回滚的话,子事务也会回滚,而内部子事务可以单独回滚而不影响外部主事务和其他子事 务。
TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)
TransactionDefinition.PROPAGATION_SUPPORTS : 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。
TransactionDefinition.PROPAGATION_NOT_SUPPORTED : 以非事务方式运行,如果当前存在 事务,则把当前事务挂起。
TransactionDefinition.PROPAGATION_NEVER : 以非事务方式运行,如果当前存在事务,则抛 出异常。
8.事务的隔离级别
public enum Isolation {
DEFAULT(TransactionDefinition.ISOLATION_DEFAULT),
READ_UNCOMMITTED(TransactionDefinition.ISOLATION_READ_UNCOMMITTED),
READ_COMMITTED(TransactionDefinition.ISOLATION_READ_COMMITTED),
REPEATABLE_READ(TransactionDefinition.ISOLATION_REPEATABLE_READ),
SERIALIZABLE(TransactionDefinition.ISOLATION_SERIALIZABLE);
}
TransactionDefinition.ISOLATION_DEFAULT :使用后端数据库默认的隔离级别,MySQL 默 认采用的 REPEATABLE_READ 隔离级别 Oracle 默认采用的 READ_COMMITTED 隔离级别.
TransactionDefinition.ISOLATION_READ_UNCOMMITTED :最低的隔离级别,使用这个隔离级 别很少,因为它允许读取尚未提交的数据变更,可能会导致脏读、幻读或不可重复读
TransactionDefinition.ISOLATION_READ_COMMITTED : 允许读取并发事务已经提交的数 据,可以阻止脏读,但是幻读或不可重复读仍有可能发生
TransactionDefinition.ISOLATION_REPEATABLE_READ : 对同一字段的多次读取结果都是一 致的,除非数据是被本身事务自己所修改,可以阻止脏读和不可重复读,但幻读仍有可能发生。
TransactionDefinition.ISOLATION_SERIALIZABLE : 最高的隔离级别,完全服从 ACID 的隔 离级别。所有的事务依次逐个执行,这样事务之间就完全不可能产生干扰,也就是说,该级别可以 防止脏读、不可重复读以及幻读。但是这将严重影响程序的性能。通常情况下也不会用到该级别。
9.事务超时属性
所谓事务超时,就是指一个事务所允许执行的最长时间,如果超过该时间限制但事务还没有完成,则自 动回滚事务。在 TransactionDefinition 中以 int 的值来表示超时时间,其单位是秒,默认值为-1。
10.事务只读属性
对于只有读取数据查询的事务,可以指定事务类型为 readonly,即只读事务。只读事务不涉及数据的 修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。
分享一下关于事务只读属性,其他人的解答:
如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一 致性;
如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保 证整体的读一致性,否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则 该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持
11.事务回滚规则
这些规则定义了哪些异常会导致事务回滚而哪些不会?
默认情况下,事务只有遇到运行期异常 (RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,但是,在遇到检查型 (Checked)异常时不会回滚。
如果你想要回滚你定义的特定的异常类型的话,可以这样:
@Transactional(rollbackFor= MyException.class)
12.事务注意
1) @Transactional 的作用范围
方法 :推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,并且不是static方法和final修饰的方法,否则不生效。
类 :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。
接口 :不推荐在接口上使用。
八.mybatis整合
step01-需要的依赖
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.3.18</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.1</version>
</dependency>
<!--这个jar是整合jar-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
step02-spring配置文件的输写
mybatis核心配置文件可以什么都不写,也可以写一些例如日志配置的东西
<!--扫描注解包-->
<context:component-scan base-package="com.qf"/>
<!--引入数据库配置文件-->
<context:property-placeholder location="classpath:jdbcConfig.properties"/>
<!--配置数据源-->
<bean class="org.springframework.jdbc.datasource.DriverManagerDataSource" id="dataSource">
<property name="driverClassName" value="${jdbc.driver}"></property>
<property name="url" value="${jdbc.url}"></property>
<property name="username" value="${jdbc.username}"></property>
<property name="password" value="${jdbc.password}"></property>
</bean>
<!--配置SqlSessionFactory-->
<bean class="org.mybatis.spring.SqlSessionFactoryBean" id="sessionFactory">
<property name="dataSource" ref="dataSource"></property>
<!--设置包的别名-->
<property name="typeAliasesPackage" value="com.qf.entity"></property>
<!--设置核心mapper-->
<property name="configLocation" value="classpath:myBatisConfig.xml"></property>
</bean>
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer" id="mapperScannerConfigurer">
<property name="basePackage" value="com.qf.mapper"></property>
</bean>