Spring详细

注解:

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):同一时间只能有一个事务执行。

  1. 读到别的事务未提交的数据,叫做脏读。

  2. 在同一个事务中,反复查询多条数据,得到的结果不一样,叫做不可重复读。

  3. 幻读

隔离级别指的是,当一个事务在执行的过程中,它里边关于数据的修改是否可以被别的事务看见。

上面四种隔离级别,从上往下,性能依次降低,但是隔离级别依次升高。

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);
}
  1. TransactionDefinition.PROPAGATION_REQUIRED 使用的最多的一个事务传播行为,我们平时经常使用的 @Transactional 注解默认使用就是这个事务传 播行为。

    如果当前存在事务,则加入该事务;如果当前没有事务,则创建一个新的事务。也就是说:

    1. 如果外部方法没有开启事务的话, Propagation.REQUIRED 修饰的内部方法会新开启自己的事 务,且开启的事务相互独立,互不干扰。

    2. 如果外部方法开启事务并且被 Propagation.REQUIRED 的话,所有 Propagation.REQUIRED 修饰 的内部方法和外部方法均属于同一事务 ,只要一个方法回滚,整个事务均回滚。

2.TransactionDefinition.PROPAGATION_REQUIRES_NEW

创建一个新的事务,如果当前存在事务,则把当前事务挂起。也就是说不管外部方法是否开启事务,Propagation.REQUIRES_NEW 修饰的内部方法会新开启自己的事务,且开启的事务相互独立,互不干 扰。

  1. TransactionDefinition.PROPAGATION_NESTED :

    如果当前存在事务,则创建一个事务作为当前事务的嵌套事务来运行;如果当前没有事务,则该取值等 价于 TransactionDefinition.PROPAGATION_REQUIRED 。也就是说:

    1. 在外部方法未开启事务的情况下 Propagation.NESTED 和 Propagation.REQUIRED 作用相同,修 饰的内部方法都会新开启自己的事务,且开启的事务相互独立,互不干扰。

    2. 如果外部方法开启事务的话, Propagation.NESTED 修饰的内部方法属于外部事务的子事务,外 部主事务回滚的话,子事务也会回滚,而内部子事务可以单独回滚而不影响外部主事务和其他子事 务。

  2. TransactionDefinition.PROPAGATION_MANDATORY 如果当前存在事务,则加入该事务;如果当前没有事务,则抛出异常。(mandatory:强制性)

  3. TransactionDefinition.PROPAGATION_SUPPORTS : 如果当前存在事务,则加入该事务;如果当前没有事务,则以非事务的方式继续运行。

  4. TransactionDefinition.PROPAGATION_NOT_SUPPORTED : 以非事务方式运行,如果当前存在 事务,则把当前事务挂起。

  5. 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,即只读事务。只读事务不涉及数据的 修改,数据库会提供一些优化手段,适合用在有多条数据库查询操作的方法中。

分享一下关于事务只读属性,其他人的解答:

  1. 如果你一次执行单条查询语句,则没有必要启用事务支持,数据库默认支持 SQL 执行期间的读一 致性;

  2. 如果你一次执行多条查询语句,例如统计查询,报表查询,在这种场景下,多条查询 SQL 必须保 证整体的读一致性,否则,在前条 SQL 查询之后,后条 SQL 查询之前,数据被其他用户改变,则 该次整体的统计查询将会出现读数据不一致的状态,此时,应该启用事务支持

11.事务回滚规则

这些规则定义了哪些异常会导致事务回滚而哪些不会?

默认情况下,事务只有遇到运行期异常 (RuntimeException 的子类)时才会回滚,Error 也会导致事务回滚,但是,在遇到检查型 (Checked)异常时不会回滚。

如果你想要回滚你定义的特定的异常类型的话,可以这样:

@Transactional(rollbackFor= MyException.class)

12.事务注意

1) @Transactional 的作用范围

  1. 方法 :推荐将注解使用于方法上,不过需要注意的是:该注解只能应用到 public 方法上,并且不是static方法和final修饰的方法,否则不生效。

  2. 类 :如果这个注解使用在类上的话,表明该注解对该类中所有的 public 方法都生效。

  3. 接口 :不推荐在接口上使用。

八.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>

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值