Spring5框架内容整理

Spring框架概述

  1. 针对bean的生命周期进行管理的轻量级容器
  2. 开源的JavaEE框架,解决企业开发的复杂性
  3. 两个主要核心:
    • IOC:控制反转,把创建对象的过程交给Spring进行管理
    • Aop:面向切面,不修改源代码进行功能增强
  4. Spring特点
    • 方便解耦,简化开发
    • Aop编程支持
    • 方便程序测试
    • 方便和其它框架进行整合
    • 方便进行事务操作
    • 降低API使用难度

下载地址:https://repo.spring.io/release/org/springframework/spring/

小案例

public class User {
    
    

    public void add(){
    
    
        System.out.println("add........");
    }
}
<?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">

    <!--配置User对象创建-->
    <bean id="user" class="com.atgw.spring5.User"></bean>

</beans>
    @Test
    public void testAdd(){
    
    

        //1 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");

        //2 获取配置创建的对象
        User user = context.getBean("user", User.class);

        System.out.println(user);
        user.add();
    }

IOC容器

  1. 控制反转,把对象创建和对象之间的调用过程交给Spring进行管理
  2. 使用IOC目的是降低耦合

底层原理

xml解析,工厂模式,反射

在这里插入图片描述


接口

  • IOC思想基于IOC容器完成,IOC容器底层就是对象工厂

  • Spring提供IOC容器实现的两种方式

    • BeanFactory:是Spring内部使用的接口,不提供开发人员使用;加载配置文件的时候不会创建对象,在获取对象时才创建
    • ApplicationContextBeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员使用;加载配置文件时就创建了对象
  • ApplicationContext的两个主要实现类

    • ClassPathXmlApplicationContext
          //类路径下的配置文件
      
    • FileSystemXmlApplicationContext
          //全类名的配置文件
      

IOC的Bean管理

什么是Bean管理(有两个操作)

  1. Spring创建对象
  2. Spring注入属性

Bean管理的操作方式

基于xml配置文件方式实现

注入属性的方法

  • 创建对象(bean标签)
  • id属性:获取对象的唯一标识
  • class属性:类全路径

创建对象的时候,默认也是执行无参数构造方法完成对象创建

  • 注入属性(property标签)–使用set方法
public class Book {
     
     

  private String bname;
  private String bauthor;

   public void setBname(String bname) {
     
     
      this.bname = bname;
   }

   public void setBauthor(String bauthor) {
     
     
      this.bauthor = bauthor;
   }

   public void testDemo(){
     
     
      System.out.println("bname ="+ bname);
   }
}
  <bean id="book" class="com.atgw.spring5.Book">
      <!--name:类里面属性名称
           vlaue: 向属性注入的值-->
       <property name="bname" value="平凡的世界"></property>
       <property name="bauthor" value="路遥"></property>
   </bean>
  • 注入属性(property标签)–有参函数构造方法
public class Order {
     
     

   private String oname;
   private String oaddress;

   public Order(String oname, String oaddress) {
     
     
       this.oname = oname;
       this.oaddress = oaddress;
   }
}
   <bean id="order" class="com.atgw.spring5.Order">
       <constructor-arg name="oname" value="abc"></constructor-arg>
       <constructor-arg name="oaddress" value="china"></constructor-arg>
   </bean>
  • 了解,p名称空间注入
  1. 添加p名称空间在配置文件中
       xmlns:p="http://www.springframework.org/schema/p"
  1. 进行属性注入
    <bean id="book" class="com.atgw.spring5.Book" p:bname="平凡的世界" p:bauthor="路遥">
    </bean>

xml注入其他类型属性

字面量

null

        <property name="bauthor">
            <null/>
        </property>

属性值包含特殊符号 <![CDATA[特殊的内容]]>

        <property name="bauthor">
            <value><![CDATA[<<南京>>]]]></value>
        </property>

外部bean对象

public interface UserDao {
     
     

    void update();
}
public class UserDaoImpl implements UserDao {
     
     
    @Override
    public void update() {
     
     
        System.out.println("update方法");
    }
}
public class UserService {
     
     

    //创建UserDao类型属性,生成set方法
    //准备在配置文件中进行set注入参数配置
    private UserDao userDao;
    public void setUserDao(UserDao userDao) {
     
     
        this.userDao = userDao;
    }

    public void add(){
     
     
        System.out.println("add方法");
        userDao.update();
    }
}

参数注入

<bean id="userservive" class="com.atgw.spring5.service.UserService">
        <!--注入userDao对象
            ref属性: 创建userDao对象bean标签id值
            -->
        <property name="userDao" ref="userDaoImpl"></property>
    </bean>

    <bean id="userDaoImpl" class="com.atgw.spring5.dao.UserDaoImpl"></bean>

测试

    @Test
    public void testAdd(){
     
     

        //1 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");

        //2 获取配置创建的对象
        UserService userService = context.getBean("userService", UserService.class);

        System.out.println(userService);
        userService.add();
    }

内部bean和级联赋值

 public class Dept {
     
     

  private String name;

  public void setName(String name) {
     
     
      this.name = name;
  }
 }
public class Empl {
     
     

    private String name;
    private String gender;
    //员工属于某一个部门,使用对象形式表示
    private Dept dept;

    public void setName(String name) {
     
     
        this.name = name;
    }

    public void setGender(String gender) {
     
     
        this.gender = gender;
    }

    public void setDept(Dept dept) {
     
     
        this.dept = dept;
    }

    public void add(){
     
     
        System.out.println(name+"::"+gender+"::"+dept);
    }
}

内部bean配置

    <bean id="empl" class="com.atgw.spring5.bean.Empl">
        <!--配置两个普通属性-->
        <property name="name" value="LiMing"></property>
        <property name="gender" value=""></property>
        <!--设置对象类型属性-->
        <property name="dept">
            <bean id="dept" class="com.atgw.spring5.bean.Dept">
                <property name="name" value="人力部"></property>
            </bean>
        </property>
    </bean>

测试

    @Test
    public void testEmpl(){
     
     

        //1 加载spring配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");

        //2 获取配置创建的对象
        Empl empl = context.getBean("empl", Empl.class);

        System.out.println(empl);
        empl.add();
    }

级联配置(第一种)

    <bean id="empl" class="com.atgw.spring5.bean.Empl">
        <!--配置两个普通属性-->
        <property name="name" value="LiMing"></property>
        <property name="gender" value=""></property>
        <!--设置对象类型属性-->
        <property name="dept" ref="dept"></property>
    </bean>
    <bean id="dept" class="com.atgw.spring5.bean.Dept">
        <property name="name" value="人力部"></property>
    </bean>

级联配置(第二种)

条件:要生成get方法

    <bean id="empl" class="com.atgw.spring5.bean.Empl">
        <!--配置两个普通属性-->
        <property name="name" value="LiMing"></property>
        <property name="gender" value=""></property>
        <!--设置对象类型属性-->
        <property name="dept.name" value="财务部"></property>
    </bean>
    <bean id="dept" class="com.atgw.spring5.bean.Dept">
        <property name="name" value="人力部"></property>
    </bean>

xml注入集合类型的属性

第一种

public class Stu {
    
    

    //数组类型属性
    private String[] courses;

    //list集合的属性
    private List<String> list;

    //map集合类型属性
    private Map<String,String> maps;

    //set集合
    private Set<String> sets;
}
   <bean id="stu" class="com.atgw.spring5.collectiontype.Stu">
        <!--数组类型属性注入-->
        <property name="courses">
            <array>
                <value>java课程</value>
                <value>数据库课程</value>
            </array>
        </property>
        <!--list类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>李四</value>
            </list>
        </property>
        <!--Map类型属性注入-->
        <property name="maps">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="PHP"></entry>
            </map>
        </property>
        <!--set集合类型属性注入-->
        <property name="sets">
            <set>
                <value>mysql</value>
                <value>redis</value>
            </set>
        </property>
    </bean>

第二种(在集合里面设置对象类型的值)

public class Course {
    
    

    private String cname;

    public void setCname(String cname) {
    
    
        this.cname = cname;
    }
}
    //List<Course>集合类型
    private List<Course> courseList;
        <!--配置List<Course>类型的属性-->
        <property name="courseList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>


 <!--创建多个Course对象-->
    <bean id="course1" class="com.atgw.spring5.collectiontype.Course">
        <property name="cname" value="Spring框架"></property>
    </bean>
    <bean id="course2" class="com.atgw.spring5.collectiontype.Course">
        <property name="cname" value="MyBatis框架"></property>
    </bean>

第三种(把集合注入部分提取出来)

public class Book {
    
    
    
    private List<String> list;
    public void setList(List<String> list) {
    
    
        this.list = list;
    }
}
    <!--提取list集合类型属性注入-->
    <util:list id="bookList">
        <value>计算机网络</value>
        <value>计算机组成原理</value>
        <value>数据结构</value>
    </util:list>

    <!--使用提取的list集合-->
    <bean id="book" class="com.atgw.spring5.collectiontype.Book">
        <property name="list" ref="bookList"></property>
    </bean>

FactoryBean

  • 普通bean:在配置文件中定义bean类型就是返回类型
  • 工厂bean:在配置文件定义bean类型可以和返回类型不一样(实现:定义一个来实现FactoryBean,并实现其中的方法)
public class MyBean implements FactoryBean<Course> {
    
    
    //获取哪种类型的返回值
    @Override
    public Course getObject() throws Exception {
    
    
        Course course = new Course();
        course.setCname("数据挖掘");
        return course;
    } 
}
    <bean id="myBean" class="com.atgw.spring5.factorybean.MyBean"></bean>
    @Test
    public void test3(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");
        Course course = context.getBean("myBean", Course.class);
        System.out.println(course);
    }

Bean的作用域

如何设置单实例还是多实例(就是每次获取的对象是否是同一个对象)

  • 在配置文件的bean标签有属性(scope)用于设置单实例还是多实例
  • scope:
    • 默认值singleton–单实例(在加载配置文件的时候就会创建对象)
    • prototype–多实例(在getBean()时才会创建对象)
    • request
    • session

Bean的生命周期

  1. 通过构造器创建bean实例(无参数构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)
  3. 调用bean的初始化方法(需要进行配置初始化的方法)
  4. bean可以使用
  5. 当关闭容器的时候,调用bean的销毁的方法(需要进行配置销毁的方法)

bean的后置处理器

在第三步的前后,会把bean实例传递bean后置处理器的方法

public class Order {
    
    

    private String oname;
    public Order() {
    
    
        System.out.println("第一步,执行初始化方法");
    }

    public void setOname(String oname) {
    
    
        this.oname = oname;
        System.out.println("第二步,调用set方法设置属性值");
    }

    public void initMethod(){
    
    
        System.out.println("第三步,执行初始化的方法");
    }
    public void destoryMethod(){
    
    
        System.out.println("第五步,执行销毁的方法");
    }
}
    <bean id="orders" class="com.atgw.spring5.collectiontype.Order" init-method="initMethod" destroy-method="destoryMethod">
        <property name="oname" value="手机"></property>
    </bean>
    @Test
    public void test4(){
    
    
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean4.xml");
        Order order = context.getBean("orders", Order.class);
        System.out.println("第四步,获取创建bean实例对象");
        System.out.println(order);

        //手动让bean实例销毁
        context.close();
    }

配置后置处理器

  • 创建一个类实现BeanPostProcessor接口,并重写其中的方法
  • 在配置文件中进行配置,会对所有的bean对象加上后置处理器
public class MyBeanPost implements BeanPostProcessor {
    
    

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("在初始化之前执行");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    
    
        System.out.println("在初始化之后执行");
        return bean;
    }
}
<!--配置后置处理器-->
    <bean id="myBeanPost" class="com.atgw.spring5.MyBeanPost"></bean>


xml自动装配

根据指定的装配规则(属性名称或属性类型),Spring自动将匹配的属性值进行注入

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

引入外部属性文件

连接数据库

jdbc.properties外部配置文件

prop.username=root
prop.password=000519
prop.url=jdbc:mysql://localhost:3306/book?useUnicode=true&characterEncoding=utf-8&useSSL=false&rewriteBatchedStatement=true
prop.driverClass=com.mysql.jdbc.Driver

bean6.xml文件

1.先引入context名称空间

       xmlns:context="http://www.springframework.org/schema/context"
                            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">

2.因为外部属性文件,动态获取

    <!--引入外部属性文件-->
    <context:property-placeholder location="classpath:jdbc.properties"/>

    <!--配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.username}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>

基于注解方式实现

使用注解目的:简化xml配置

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

  • @Component
  • @Service
  • @Controller
  • @Repository

上面的四个注解功能是一样的,都可以用来创建bean实例,一般分别用在不同的层面

导入jar包:spring-aop-5.2.6.RELEASE.jar


//注解里面的value值可以省略不写
//默认值是类名称,首字母小写
@Component(value = "userService")
public class UserService {
    
    
    public void add(){
    
    
        System.out.println("service add 方法");
    }
}

组件扫描

    <!--开启组件扫描
        1.如果扫描多个包,包之间可以用,隔开
        2.扫描包的下层目录
    -->
    <context:component-scan base-package="com.atgw.spring5"></context:component-scan>

组件扫描的作用就是在所添加的包路径下面寻找添加了注解的类,然后会在测试方法中的加载xml配置文件时自动创建类

    @Test
    public void test1(){
    
    
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }

组件扫描配置的细节

    <!--示例1-->
    <!--use-default-filters="false":表示不使用默认的filter,自己配置filter
    context:include-filter:设置扫描哪些内容
    下面的表示只扫描包下面的只带有Controller注解的类
    -->
    <context:component-scan base-package="com.atgw.spring5" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!--示例2-->
    <!--
    context:exclude-filter:与上面的相反,表示除了列出的类,其他的类都要扫描
    下面的表示不扫描带有Controller的类,其他的类都扫描-->
    <context:component-scan base-package="com.atgw.spring5" use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>

属性注入

@AutoWired:根据属性类型自动装配

步骤:

  1. 在service和dao类添加创建对象注解,实现service和dao对象创建
  2. 在service中添加dao类型属性,在属性上面使用注解
@Service
public class UserService {
     
     

   //定义dao类型属性
   //不需要添加set方法
   //添加注入属性注解
   @Autowired
   private UserDao userDao;

   public void add(){
     
     
       System.out.println("service add 方法");
       userDao.add();
   }
}
public interface UserDao {
     
     

   void add();
}
@Repository
public class UserDaoImpl implements UserDao {
     
     
   @Override
   public void add() {
     
     
       System.out.println("dao add方法");
   }
}

@Qualifier:根据属性名进行注入

  • 要和@AutoWired一起使用,因为@AutoWired只是根据类型进行注入,如果一个类型(接口)可能有多个实现类,那就不确定是要注入哪个实现类了
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
     
     }
@Autowired//根据类型进行注入
   @Qualifier(value = "userDaoImpl1")//根据名称进行注入
   private UserDao userDao;

@Resource:可以根据类型注入,也可以根据名称注入

@Resource注解并不是Springh中的注解,而是javax包下的

  • 根据类型注入,不要添加name属性的值
  • 根据名称进行注入,就要添加要注入属性的实现类(userDaoImpl)所添加注解的名称(userDaoImpl1)与name属性的值一致
@Repository(value = "userDaoImpl1")
public class UserDaoImpl implements UserDao {
     
     }
   //@Resource//根据类型注入
   @Resource(name = "userDaoImpl1")//根据名称进行注入
   private UserDao userDao;

@Value:注入普通类型属性

@Value(value = "liming")
   private String name;

完全注解开发

  1. 创建配置类,代替xml配置文件
@Configuration//作为配置类,替代xml配置文件
@ComponentScan(basePackages = {
    
    "com.atgw.spring5"})
public class SpringConfig {
    
    
    
}

没有配置文件,就不需要再加载配置文件,而是需要加载配置类

    @Test
    public void test2(){
    
    
        //加载配置类
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        System.out.println(userService);
        userService.add();
    }

AOP

概念

面向切面编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。

  • 不通过修改源代码,在主干功能里面添加新的功能

底层原理

底层使用动态代理

  1. 有接口的情况,使用JDK动态代理

    创建接口实现类代理对象,增强类的方法

  2. 没有接口的情况,使用CGLIB动态代理

    创建当前类的子类代理对象,增强类的方法

JDK动态代理实现

  1. 使用JDK动态代理,使用Proxy类里面的方法创建代理对象
  2. 调用 static Object newProxyInstance( , , )方法,返回指定接口的代理类的实例
    • 第一个参数:类加载器
    • 第二个参数:增强方法所在的类,这个类实现的接口,支持多个接口
    • 实现这个接口InvocationHandler,创建代理对象,写增强的方法

创建接口,定义方法

public interface UserDao {
     
     

    int add(int a, int b);
    String update(String id);
}

创建接口实现类,实现方法

public class UserDaoImpl implements UserDao{
     
     
    @Override
    public int add(int a, int b) {
     
     
        System.out.println("add方法执行了");
        return a+b;
    }

    @Override
    public String update(String id) {
     
     
        System.out.println("update方法执行了");
        return id;
    }
}

使用Proxy类创建接口代理对象

public class JDKProxy {
     
     

    public static void main(String[] args) {
     
     

        //创建接口实现类代理对象
        Class[] inter = {
     
     UserDao.class};
        UserDaoImpl userDao = new UserDaoImpl();
        UserDao dao = (UserDao)Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), inter, new UserDaoProxy(userDao));

        int result = dao.add(1, 4);
        System.out.println("result:"+result);
    }
}


//创建代理对象的代码
class UserDaoProxy implements InvocationHandler{
     
     

 //把代理对象传递过来(UserDaoImpl类)
    //有参数构造器
    private Object obj;
    public UserDaoProxy(Object obj){
     
     
        this.obj=obj;
    }
   
 //增强的逻辑
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     
     
   
     //被增强的方法(就是UserDaoImpl类中的方法)之前
        System.out.println("方法之前..下面要执行的方法是:"+method.getName()+";传递的参数:"+ Arrays.toString(args));
   
     //执行原被增强的方法
        Object res = method.invoke(obj,args);
   
     //被增强的方法之后
        System.out.println("方法之后..返回的对象"+obj);
   
     return res;
    }
   }

分析

我们的目的是将一个接口的实现类中的方法功能增强,要采取下面的方法

  1. 使用Proxy.newProxyInstance()方法来创建这个实现类的代理对象,意思就是这个方法会返回一个这个实现类的对象
  2. Proxy.newProxyInstance()需要三个参数
    • 第一个参数类加载器,就是创建代理对象所在的类(JDKProxy)
    • 第二个参数是这个实现类,数组形式传入
    • 第三个参数是实现了InvocationHandler接口的类,在这个类中有invoke()方法,它会实现要增强的方法,其中的method参数是被增强的实现类的哪一个被增强的方法,args参数是被增强方法的参数
  3. 当得到代理对象后,我们就会得到已经增强的方法

AOP操作术语

  1. 连接点

    类里面哪些方法可以被增强,这些方法称为连接点

  2. 切入点

    实际被真正增强的方法,称为切入点

  3. 通知(增强)

    实际增强的部分的称为通知

    通知有多种类型

    • 前置通知
    • 后置通知
    • 环绕通知(在被增强的方法前后)
    • 异常通知(有异常,才会执行)
    • 最终通知(有异常,不会执行)
  4. 切面

    把通知应用到切入点的过程


AOP操作

Spring框架一般都是基于AspectJ实现AOP操作

AsprctJ不是Spring组成部分,是独立的AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作

  1. 基于xml配置文件实现
  2. 基于注解方式实现

引入jar包依赖:

spring-aspects-5.2.6.RELEASE.jar

com.springsource.net.sf.cglib-2.2.0.jar

com.springsource.org.aopalliance-1.0.0.jar

com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar

切入点表达式

作用就是知道哪个类里面的哪个方法进行增强

表达式:execution ( [权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )

所有的(修饰符,类,方法)用 * 表示

注解方式实现

1 创建类,定义方法

public class User {
    
    

    public void add(){
    
    
        System.out.println("add方法.........");
    }
}

2 创建增强类(编写增强逻辑代码)

在增强类里面,创建方法,让不同方法代表不同的通知类型

//增强类
public class UserProxy {
    
    

    //前置通知
    public void before(){
    
    
        System.out.println("before.....");
    }
}

3 进行通知的配置

  1. 在spring配置文件中,开启注解扫描

    添加需要的命名空间

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:context="http://www.springframework.org/schema/context"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <!--开启注解扫描-->
        <context:component-scan base-package="com.atgw.spring5.aopanno"></context:component-scan>
    </beans>
    
  2. 使用注解创建User和 UserProxy对象

    @Component
    public class User {
          
          }
    
    //增强类
    @Component
    public class UserProxy {
          
          }
    
  3. 在增强类上面添加注解 @Aspect,生成代理对象

    //增强类
    @Component
    @Aspect//生成代理对象
    public class UserProxy {
          
          }
    
  4. 在spring 配置文件中开启生成代理对象

    <!--开启Aspect生成代理对象,
            会在上面的包中寻找有Aspect注解的类,生成代理对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    

4 配置不同类型的通知

在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置

    //前置通知
    //Before注解表示作为前置通知
    @Before(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void before(){
    
    
        System.out.println("before.....");
    }

测试1

    @Test
    public void testAopAnno(){
    
    
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean1.xml");
        User user = context.getBean("user", User.class);
        user.add();
    }
}
//before.....
//add方法.........

结果表明在add方法之前,before方法会先执行,完成前置通知

测试2

    //前置通知
    //Before注解表示作为前置通知
    @Before(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void before(){
    
    
        System.out.println("before前置.....");
    }

    //后置通知
    @After(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void after(){
    
    
        System.out.println("after后置.......");
    }

    //异常通知(有异常,才会执行)
    @AfterThrowing(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void afterThrowing(){
    
    
        System.out.println("afterThrowing异常.......");
    }

    //环绕通知
    @Around(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    
    
        System.out.println("around环绕之前.......");

        //被增强的方法执行
        proceedingJoinPoint.proceed();

        System.out.println("around环绕之后......");
    }

    //最终通知(有异常,不会执行)
    @AfterReturning(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void afterReturning(){
    
    
        System.out.println("afterReturning最终.......");
    }
//around环绕之前.......
//before前置.....
//add方法.........
//around环绕之后......
//after后置.......
//afterReturning最终.......

相同切入点提取

    //相同切入点抽取
    @Pointcut(value = "execution(* com.atgw.spring5.aopanno.User.add(..))")
    public void pointDemo(){
    
    

    }

    //前置通知
    //Before注解表示作为前置通知
    @Before(value = "pointDemo()")
    public void before(){
    
    
        System.out.println("before前置.....");
    }

多个增强类对同一个方法进行增强,设置增强类优先级

  • 在增强类上面添加注解@Order(数字类型值),数字越小,优先级越高
@Component
@Aspect
@Order(3)
public class PersonProxy {
    
    }
@Component
@Aspect//生成代理对象
@Order(1)
public class UserProxy {
    
    }

UserProxy的优先级要高于PersonProxy


配置文件实现

public class Book {
    
    

    public void buy(){
    
    
        System.out.println("buy......");
    }
}
public class BookProxy {
    
    

    public void before(){
    
    
        System.out.println("before......");
    }
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
            http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

<!--创建对象-->
    <bean id="book" class="com.atgw.spring5.aopxml.Book"></bean>
    <bean id="bookProxy" class="com.atgw.spring5.aopxml.BookProxy"></bean>


    <!--配置aop增强-->
    <aop:config>
        <!--切入点-->
        <aop:pointcut id="p" expression="execution(* com.atgw.spring5.aopxml.Book.buy(..))"/>

        <!--切面-->
        <aop:aspect ref="bookProxy">
            <!--增强作用在具体的方法上-->
            <!--下面的语句表示 其中的 前置通知 before方法作用在上面定义的 p 切入点上-->
            <aop:before method="before" pointcut-ref="p"/>
        </aop:aspect>
    </aop:config>
</beans>

测试

    @Test
    public void testAopXml(){
    
    
        ApplicationContext context =
                new ClassPathXmlApplicationContext("bean2.xml");
        Book book = context.getBean("book", Book.class);
        book.buy();
    }

全注解开发

需要创建配置类

@Configuration//配置类
@ComponentScan(basePackages = {
    
    "com.atgw.spring5"})//开启注解扫描
@EnableAspectJAutoProxy(proxyTargetClass = true)//开启Aspect生成代理对象
public class ConfigAop {
    
    

}

JdbcTemplate

概念

  • Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作

基本配置

引入jar包

  • mysql-connector-java-5.1.7-bin.jar
  • spring-jdbc-5.2.6.RELEASE.jar
  • spring-orm-5.2.6.RELEASE.jar
  • spring-tx-5.2.6.RELEASE.jar

配置连接池

    <!--直接配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/user_db"></property>
    <property name="username" value="root"></property>
    <property name="password" value="000519"></property>
    </bean>

配置JdbcTemplate对象,注入DateSource

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

开启组件扫描

<!--开启组件扫描-->
    <context:component-scan base-package="com.atgw.spring5"></context:component-scan>

创建Service和Dao对象

@Service
public class BookService {
    
    

    //注入Dao
    @Autowired
    private BookDao bookDao;
}
@Repository
public class BookDaoImpl implements BookDao{
    
    

    //注入JdbcTemplate
    @Autowired
    private JdbcTemplate jdbcTemplate;
}

添加操作

  • jdbcTemplate.update()

对应数据表t_book创建实体类

public class Book {
    
    

    private Integer userId;
    private String username;
    private String ustatus;
}

从Service层到Dao层的添加方法的实现

BookService程序

    //增加操作
    public void addBook(Book book){
     
     
        bookDao.add(book);
    }

BookDao接口

    //添加的操作
    void add(Book book);

BookDaoImpl实现类

    //添加方法
    @Override
    public void add(Book book) {
     
     

        String sql = "insert into t_book (`user_id`,`username`,`ustatus`) values(?,?,?)";
        //调用方法实现
        int update = jdbcTemplate.update(sql, book.getUserId(),book.getUsername(),book.getUstatus());
        System.out.println("操作了:"+ update+" 条数据");
    }

测试

    @Test
    public void testJdbcTemplate(){
     
     
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        BookService bookService = context.getBean("bookService", BookService.class);

        Book book = new Book();
        book.setUserId(1);
        book.setUsername("liming");
        book.setUstatus("A");
        bookService.addBook(book);
    }

修改和删除

  • jdbcTemplate.update()

BookService程序

    //修改操作
    public void updataBook(Book book){
     
     
        bookDao.updateBook(book);
    }

    //删除操作
    public void deleteBook(Integer id){
     
     
        bookDao.deleteBook(id);
    }

BookDao接口

    //修改
    void updateBook(Book book);

    //删除
    void deleteBook(Integer id);

BookDaoImpl实现类

    //修改
    @Override
    public void updateBook(Book book) {
     
     
        String sql = "update t_book set `username`=?,`ustatus`=? where `user_id`=?";
        int update = jdbcTemplate.update(sql, book.getUsername(), book.getUstatus(), book.getUserId());
        System.out.println("操作了:"+ update+" 条数据");
    }

    //删除
    @Override
    public void deleteBook(Integer id) {
     
     
        String sql = "delete from t_book where `user_id`=?";
        int update = jdbcTemplate.update(sql, id);
        System.out.println("操作了:"+ update+" 条数据");
    }

查询

1 查询记录数

  • jdbcTemplate.queryForObject()

BookService程序

    //查询表中的数量
    public int findCount(){
     
     
        return bookDao.selectCount();
    }

BookDao接口

    //查询表中数量
    int selectCount();

BookDaoImpl实现类

    //查询表中数量
    @Override
    public int selectCount() {
     
     
        String sql = "select count(*) from t_book";
        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);
        return count;
    }

2 查询指定对象(返回单个对象)

  • jdbcTemplate.queryForObject()
    //查询返回对象
    public Book findOne(Integer id){
     
     
        return bookDao.selectBookInfo(id);
    }
    Book selectBookInfo(Integer id);

BeanPropertyRowMapper是RowMapper的实现类,完成对相应类的数据封装

    @Override
    public Book selectBookInfo(Integer id) {
     
     
        String sql = "select * from t_book where `user_id`=?";
//      BeanPropertyRowMapper是RowMapper的实现类,完成对相应类的数据封装
        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);
        return book;
    }

2 查询指定对象(返回集合对象)

  • jdbcTemplate.query()
    //查询所有记录
    public List<Book> findAll(){
     
     
        return bookDao.selectAllBookInfo();
    }
    List<Book> selectAllBookInfo();
    @Override
    public List<Book> selectAllBookInfo() {
     
     
        String sql = "select * from t_book";
        List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));
        return bookList;
    }

批量操作

1 批量添加

  • jdbcTemplate.batchUpdate()
    //批量添加
    public void batchAdd(List<Object[]> listArgs){
     
     
        bookDao.batchAddBook(listArgs);
    }
    void batchAddBook(List<Object[]> listArgs);
    @Override
    public void batchAddBook(List<Object[]> listArgs) {
     
     
        String sql = "insert into t_book values(?,?,?)";
        int[] ints = jdbcTemplate.batchUpdate(sql, listArgs);
        System.out.println(Arrays.toString(ints));
    }

测试

//        批量添加
        List<Object[]> listBook = new ArrayList<>();
        Object[] o1 = {
     
     4,"java","A"};
        Object[] o2 = {
     
     5,"c++","B"};
        Object[] o3 = {
     
     6,"mysql","A"};
        listBook.add(o1);
        listBook.add(o2);
        listBook.add(o3);

        bookService.batchAdd(listBook);

2 批量修改

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

3 批量删除

  • jdbcTemplate.batchUpdate()
    //批量删除
    public void batchDelete(List<Object[]> listArgs){
     
     
        bookDao.batchDeleteBook(listArgs);
    }
    void batchDeleteBook(List<Object[]> listArgs);
    @Override
    public void batchDeleteBook(List<Object[]> listArgs) {
     
     
        String sql = "delete from t_book where `user_id`=?";
        int[] ints = jdbcTemplate.batchUpdate(sql, listArgs);
        System.out.println(Arrays.toString(ints));
    }

测试

        //批量删除
        List<Object[]> listBook = new ArrayList<>();
        Object[] o1 = {
     
     4};
        Object[] o2 = {
     
     5};
        Object[] o3 = {
     
     6};
        listBook.add(o1);
        listBook.add(o2);
        listBook.add(o3);
        bookService.batchDelete(listBook);

事务

四大特性(ACID)

  1. 原子性(atomicity):不可分割,一个事务中的操作要么都做,要么都不做
  2. 一致性(consistency):从一个一致性状态到另一个一致性状态
  3. 隔离性(isolation):一个事物的执行不能被另一个事务干扰
  4. 持续性(durability):一个事物一旦提交,它对数据库的数据改变就应该是永久性的

搭建事务操作环境案例

银行转账业务

创建数据库表

CREATE TABLE t_account(
	`id` VARCHAR(20) PRIMARY KEY NOT NULL,
	`username` VARCHAR(50),
	`money` INT
)

INSERT INTO t_account 
VALUES('1','lucy',1000),
	('2','mary',1000);

配置xml文件

    <!--开启组件扫描-->
    <context:component-scan base-package="com.atgw.spring5"></context:component-scan>
    
    <!--直接配置连接池-->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
    <property name="url" value="jdbc:mysql://localhost:3306/user_db"></property>
    <property name="username" value="root"></property>
    <property name="password" value="000519"></property>
    </bean>

    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <!--注入dataSource-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

创建Service,搭建dao,完成对象创建和注入关系

(service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource)

public interface UserDao {
     
     
        //少钱
    void reduceMoney();

    //多钱
    void addMoney();
}
//创建对象
@Repository
public class UserDaoImpl implements UserDao {
     
     

    //注入数据库连接池
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    @Override
    public void reduceMoney() {
     
     
        String sql = "update t_account set money=money-? where username=?";
        jdbcTemplate.update(sql,100,"lucy");
    }

    @Override
    public void addMoney() {
     
     
        String sql = "update t_account set money=money+? where username=?";
        jdbcTemplate.update(sql,100,"mary");
    }
}
//创建对象
@Service
public class UserService {
     
     

    //注入dao
    @Autowired
    private UserDao userDao;
    
        public void accountMoney(){
     
     
        userDao.reduceMoney();
        userDao.addMoney();
    }
}

测试

    @Test
    public void testAccount(){
     
     
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

使用事务来保证数据的可靠

Spring事务管理介绍

  1. 事务添加到Service层(业务逻辑层)
  2. 在Spring进行事务管理操作
    • 编程式(try-catch来捕获异常,没有异常就提交,有异常就回滚)
    • 声明式(使用)
  3. 声明式事务管理
    • 基于注解方式
    • 基于xml配置文件方式
  4. 在Spring进行声明式事务管理,底层使用AOP原理
  5. 提供了一个接口PlatformTransactionManager,代表事务管理器,实现类DataSourceTransactionManager

声明式(注解方式)

1 在Spring配置文件中配置事务管理器

    <!--创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

2 开启事务注解

   添加命名空间    xmlns:tx="http://www.springframework.org/schema/tx"
            http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
    <!--开启事务注解-->
    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>

3 在service层类上面(或者里面的方法上面)添加事务注解

@Transactional

  • 加在类上面,就对类里面的所有方法起作用
  • 加在方法上面,就对该方法起作用
@Transactional//添加事务
public class UserService{
     
     }

声明式参数配置

在@Transactional注解中有参数

  • propagation:事务传播行为
  • isolation:事务隔离级别
  • timeout:超时时间
  • readOnly:是否只读
  • rollbackFor:回滚
  • noRollBackFor:不回滚

事务传播行为

事务传播行为(方法之间的调用在有无事务时不同的处理)

@Transactional
public void add(){
     
     
    update();
}

public void update(){
     
     
    
}
  • REQUIRED
    • 如果add()方法有事务,则update()方法在add()的同一个事务中执行
    • 如果add()方法没有事务,则会创建新的事务
  • REQUIRED_NEW
    • 不论add方法是否有事务,都会创建新的事务
@Transactional(propagation = Propagation.REQUIRED)//添加事务
public class UserService{
     
     
    
}

隔离级别

不考虑隔离性,会出现三个问题

  • 脏读:一个未提交的事务读到了另一个未提交事务的数据

在这里插入图片描述


  • 不可重复读:一个未提交的事务读到了另一个事务修改前和修改后的数据,是不同的

在这里插入图片描述


  • 幻读

在这里插入图片描述


通过设置事务隔离性,解决上面三个问题

脏读 不可重复读 幻读
READ UNCOMMITTED(读未提交)
READ COMMITTED(读已提交)
REPEATABLE READ(可重复读)
SERIALIZABLE
@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)//添加事务

其他参数

超时时间timeout

  • 事务需要在一定时间内进行提交,如果不提交就进行回滚

  • 默认值是-1,表示不回滚


是否可读readOnly

  • 默认值false,表示可以查询,可以增删改
  • true,表示只能查询

回滚rollbackFor

  • 设置出现哪些异常时进行事务回滚

不回滚noRollbackFor

  • 设置出现哪些异常时不进行事务回滚

声明式(XML文件方式)

    <!--1 创建事务管理器-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <!--注入数据源-->
        <property name="dataSource" ref="dataSource"></property>
    </bean>

    <!--2 配置通知-->
    <tx:advice id="txadvice">
        <!--配置事务参数-->
        <tx:attributes>
            <tx:method name="accountMoney" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    
    <!--3 配置切入点,切面-->
    <aop:config>
        <!--配置切入点-->
        <aop:pointcut id="pt" expression="execution(* com.atgw.spring5.service.UserService.*(..))"/>
        <!--配置切面-->
        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"/>
    </aop:config>

完全注解方式

创建配置类

@Configuration//配置类
@ComponentScan(basePackages = "com.atgw")//组件扫描
@EnableTransactionManagement//开启事务
public class TxConfig {
     
     

    //创建数据库连接池
    @Bean
    public DruidDataSource getDruidDateSource(){
     
     
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName("com.mysql.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/user_db");
        dataSource.setUsername("root");
        dataSource.setPassword("000519");
        return dataSource;
    }


    //创建JdbcTemplate对象
    @Bean
    public JdbcTemplate getJdbcTemplate(DataSource dataSource){
     
     
        //到IOC容器中根据类型找到dataSource
        JdbcTemplate jdbcTemplate = new JdbcTemplate();
        //注入dataSource
        jdbcTemplate.setDataSource(dataSource);
        return jdbcTemplate;
    }


    //创建事务管理器
    @Bean
    public DataSourceTransactionManager getDataSourceTransactionManager(DataSource dataSource){
     
     
        DataSourceTransactionManager transactionManager = new DataSourceTransactionManager();
        transactionManager.setDataSource(dataSource);
        return transactionManager;
    }
}

测试

    @Test
    public void testAccount1(){
     
     
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);
        UserService userService = context.getBean("userService", UserService.class);
        userService.accountMoney();
    }

Spring5新功能

基于JDK8,兼容JDK9

  • 自带了通用的日志封装

引入log4j的jar包

  • log4j-api-2.11.2.jar
  • log4j-core-2.11.2.jar
  • log4j-slf4j-impl-2.11.2.jar
  • slf4j-api-1.7.30.jar

创建log4j2.xml文件(固定文件名)

<?xml version="1.0" encoding="UTF-8"?>
<!--日志级别以及优先级排序: OFF > FATAL > ERROR > WARN > INFO > DEBUG > TRACE > ALL -->
<!--Configuration后面的status用于设置log4j2自身内部的信息输出,可以不设置,当设置成trace时,可以看到log4j2内部各种详细输出-->
<configuration status="INFO">
    <!--先定义所有的appender-->
    <appenders>
        <!--输出日志信息到控制台-->
        <console name="Console" target="SYSTEM_OUT">
            <!--控制日志输出的格式-->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n"/>
        </console>
    </appenders>
    <!--然后定义logger,只有定义了logger并引入的appender,appender才会生效-->
    <!--root:用于指定项目的根日志,如果没有单独指定Logger,则会使用root作为默认的日志输出-->
    <loggers>
        <root level="info">
            <appender-ref ref="Console"/>
        </root>
    </loggers>
</configuration>
public class UserLog {
     
     

    private static final Logger log = LoggerFactory.getLogger(UserLog.class);

    public static void main(String[] args) {
     
     
        log.info("hello log4j2");
        log.warn("hello log4j2");
    }
}

猜你喜欢

转载自blog.csdn.net/weixin_46250447/article/details/112911135