Spring学习笔记

Spring

个人总结:Spring框架的两个核心是IOC-Bean管理和AOP,它们都是用来减少耦合度,IOC把对象创建以及属性赋值交给spring处理以减少耦合度,AOP是增强功能不需要修改原来的代码。对于IOC-Bean管理有两种方式:基于xml、基于注解。可能最终注解用的更多,xml只是学习注解的铺垫?后面的webflux听的云里雾里如有需要再去补吧!

一、IOC容器
1.什么是IOC(控制反转)

a)把对象创建和对象之间的调用过程,交给spring进行管理

b)使用IOC目的:为了降低耦合度

2.IOC底层

xml解析、工厂模式、反射

3.Spring提供的IOC容器实现的两种方式(两个接口)

a)BeanFactory接口:IOC容器基本实现是Spring内部接口的使用接口,不提供给开发人员进行使用(加载配置文件时候不会创建对象,在获取对象时才会创建对象。)

b)ApplicationContext接口(推荐使用!):BeanFactory接口的子接口,提供更多更强大的功能,提供给开发人员使用(加载配置文件时候就会把在配置文件对象进行创建)

二、IOC容器-Bean管理(基于xml)

a)IOC操作Bean管理(基于xml)

b)IOC操作Bean管理(基于注解)

1、IOC操作Bean管理

a)Bean管理就是两个操作:(1)Spring创建对象;(2)Spring注入属性

2、基于XML配置文件创建对象并注入属性

IOC操作Bean管理(基于xml)包括简单来说包括三个部分:

  • 创建普通Bean类
  • xml配置:创建Bean类对象,并进行属性赋值(注入属性)
  • 代码测试类(即使用)
a)set方式注入
  • Book类
//(1)创建类,定义属性和对应的set方法
public class Book {
    private String bookName;
    private int bookPrice;
    public void setBookName(String bookName){
        this.bookName = bookName;
    }
    public void setBookPrice(int bookPrice){
        this.bookPrice = bookPrice;
    }
    public void sout(){
        System.out.println(bookName+"::"+bookPrice);
    }
}
  • xml配置:创建Book类对象,并进行属性赋值
<!--1 配置Book对象创建-->
<?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 name="book" class="com.liu.bean.Book">
        <property name="bookName" value="左耳"></property>
        <property name="bookPrice" value="30"></property>
    </bean>
</beans>
  • 代码测试类(具体使用)
<!--(3)代码测试-->
public class TestBook {
    @Test
    public void test(){
        //1 加载 spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        //2 获取配置创建的对象,相当于将xml中的name="book"和Book类绑定
        Book book = context.getBean("book", Book.class);
        book.sout();
    }
}
输出:左耳::30
b)有参构造函数注入
//(1)创建类,构建有参函数
public class Book {
    private String bookName;
    private int bookPrice;
    public Book(String bookName,int bookPrice){
        this.bookName = bookName;
        this.bookPrice = bookPrice;
    }
    public void sout(){
        System.out.println(bookName+"::"+bookPrice);
    }
}
<!--(2)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 name="book" class="com.liu.bean.Book">
        <constructor-arg name="bookName" value="左耳"></constructor-arg>
        <constructor-arg name="bookPrice" value="20"></constructor-arg>
    </bean>
</beans>
//(3)测试类
public class TestBook {
    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        Book book = context.getBean("book", Book.class);
        book.sout();
    }
}
输出:左耳::20
c)p名称空间注入(略)
d)注入空值和特殊符号

注意:整形属性需要包装成Integer才能==null

//1.创建类
public class Book {
    private String bookName;
    private String otherName;
    private Integer bookPrice;
    public void setBookName(String bookName) {
        this.bookName = bookName;
    }
    public void setOtherName(String otherName){
        this.otherName = otherName;
    }
    public void setBookPrice(Integer bookPrice) {
        this.bookPrice = bookPrice;
    }
    public void sout(){
        System.out.println(bookName+"or"+otherName+"::"+bookPrice);
    }
}
//2.配置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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean name="book" class="com.liu.bean.Book">
    <!--(1)特殊符号赋值-->
        <property name="bookName">
            <value><![CDATA[<<左耳>>]]></value>
        </property>
        <property name="otherName">
            <value>&lt;&lt;左耳2&gt;&gt;</value>
        </property>
    <!--(2)null值-->
        <property name="bookPrice">
            <null></null>
        </property>
    </bean>
</beans>
//3.测试类public class TestBook {    @Test    public void test(){        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");        Book book = context.getBean("book", Book.class);        book.sout();    }}输出:<<左耳>>or<<左耳2>>::null
2.1 注入属性-外部bean

简而言之,外部bean就是注入的可以是对象类型属性的方式

  • 创建两个类service和dao类
//dao类public interface UserDao {    public void update();}public class UserDaoImpl implements UserDao{    @Override    public void update() {        System.out.println("dao add.......");    }}
//service类public class UserService {    //创建UserDao类型(对象)属性,生成set方法    private UserDao userDao;    public void setUserDao(UserDao userDao){        this.userDao = userDao;    }    public void add(){        System.out.println("service add........");        userDao.update();        //原始方法:创建UserDao对象        //UserDao userDao = new UserDaoImpl();        //userDao.update();    }}
  • 配置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:util="http://www.springframework.org/schema/util"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/util https://www.springframework.org/schema/util/spring-util.xsd"><!--    1.创建service和dao对象-->    <bean id="userService" class="com.liu.service.UserService"><!--    2.注入userDao对象        name属性:类里面属性名称        ref属性:创建userDao对象bean标签id值,要对应-->        <property name="userDao" ref="userDaoImpl"></property>    </bean>    <bean id="userDaoImpl" class="com.liu.dao.UserDaoImpl"></bean></beans>
  • 测试类
public class TestServiceDao{    @Test    public void test(){        ApplicationContext context = new ClassPathXmlApplicationContext("bean2.xml");        UserService userService = context.getBean("userService", UserService.class);        userService.add();    }}输出:service add........     dao add.......
2.2注入属性-内部bean

内外部bean本质都是一个对象拥有对象类型属性,在xml中内部或者外部进行对象类型属性赋值

  • 两个类:员工和部门,部门是员工的属性之一
//员工类public class Emp {    private String ename;    private String gender;    //员工属于一个部门,使用对象属性表示    private Dept dept;    public void setEname(String ename){        this.ename = ename;    }    public void setGender(String gender){        this.gender = gender;    }    public void setDept(Dept dept){        this.dept = dept;    }    public void add(){        System.out.println(ename+":"+gender+":"+dept);    }}//部门类public class Dept {    private String dname;    public void setDname(String dname){        this.dname = dname;    }    @Override    public String toString() {        return "Dept{" +                "dname='" + dname + '\'' +                '}';    }}
  • 配置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"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="emp" class="com.liu.bean.Emp"><!--        两个普通属性-->        <property name="ename" value="Jon Snow"></property>        <property name="gender" value="boy"></property><!--        对象类型属性-->        <property name="dept">            <bean id="dept" class="com.liu.bean.Dept">                <property name="dname" value="临冬城"></property>            </bean>        </property>    </bean></beans>
  • 测试类
public class TestInner {    @Test    public void test(){        ApplicationContext context = new ClassPathXmlApplicationContext("bean3.xml");        Emp emp = context.getBean("emp", Emp.class);        emp.add();    }}输出:Jon Snow:boy:Dept{dname='临冬城'}
2.3注入属性-级联赋值

第一种方式是在外部bean的基础上,进行属性赋值

第二种方式必须要有dept的get方法

<!--方式一:级联赋值-->    <bean id="emp" class="com.atguigu.spring5.bean.Emp">        <!--设置两个普通属性-->        <property name="ename" value="Andy"></property>        <property name="gender" value="女"></property>        <!--级联赋值-->        <property name="dept" ref="dept"></property>    </bean>    <bean id="dept" class="com.atguigu.spring5.bean.Dept">        <property name="dname" value="公关部门"></property>    </bean>
<!--方式二:级联赋值-->    <bean id="emp" class="com.atguigu.spring5.bean.Emp">        <!--设置两个普通属性-->        <property name="ename" value="jams"></property>        <property name="gender" value="男"></property>        <!--级联赋值-->        <property name="dept.dname" value="技术部门"></property>    </bean>    <bean id="dept" class="com.atguigu.spring5.bean.Dept">    </bean>
2.4xml 注入集合属性
  • bean类
//注入:数组类型属性、List 集合类型属性、Map 集合类型属性、Set 集合类型属性public class Stu {    private String[] courses;    private List<String> list;    private Map<String,String> maps;    private Set<String> sets;    public void setCourses(String[] courses){        this.courses = courses;    }    public void setList(List<String> list) {        this.list = list;    }    public void setMaps(Map<String, String> maps) {        this.maps = maps;    }    public void setSets(Set<String> sets) {        this.sets = sets;    }    public void test(){        System.out.println(Arrays.toString(courses));        System.out.println(list+"|"+maps+"|"+sets);    }}
  • 配置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"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"><!--集合类型属性的注入-->    <bean id="stu" class="com.liu.bean.Stu">        <!--    数组类型属性注入:array或者list-->        <property name="courses">            <array>                <value>java课程</value>                <value>python课程</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></beans>
  • 测试类
@Testpublic void test1(){    ApplicationContext context = new ClassPathXmlApplicationContext("bean5.xml");    Stu stu = context.getBean("stu", Stu.class);    stu.test();}    输出:[java课程, python课程]		 [张三, 李四]|{JAVA=java, PHP=php}|[MySQL, Redis]
2.4.1、在集合里面设置对象类型值
  • bean类
  //学生所学多门课程    private List<Course> courseList;//创建集合    public void setCourseList(List<Course> courseList) {        this.courseList = courseList;    }
  • 配置xml
    <!--创建多个course对象-->    <bean id="course1" class="com.atguigu.spring5.collectiontype.Course">        <property name="cname" value="Spring5框架"></property>    </bean>    <bean id="course2" class="com.atguigu.spring5.collectiontype.Course">        <property name="cname" value="MyBatis框架"></property>    </bean>       	<!--注入list集合类型,值是对象-->       <property name="courseList">           <list>               <ref bean="course1"></ref>               <ref bean="course2"></ref>           </list>       </property>
2.4.2、把集合注入部分提取出来
  • bean类
public class Book {    private List<String> list;    public void setList(List<String> list) {        this.list = list;    }    public void test(){        System.out.println(list);    }}
  • 在spring配置文件xml中引入名称空间util(第一步与p命名空间类似)
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"       xmlns:p="http://www.springframework.org/schema/p"       xmlns:util="http://www.springframework.org/schema/util"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd                           http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd"><!--    1.提取list集合类型属性注入-->    <util:list id="bookList">        <value>易筋经</value>        <value>九阴真经</value>        <value>九阳神功</value>    </util:list><!--    2.提取list属性使用-->    <bean id="book" class="com.liu.bean.Book">        <property name="list" ref="bookList"></property>    </bean></beans>
  • 测试类
@Testpublic void test2(){    ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");    Book book = context.getBean("book", Book.class);    book.test();}输出:[易筋经, 九阴真经, 九阳神功]
3、工厂Bean
  1. Spring有两种类型bean,一种普通bean,另外一种工厂bean(FactoryBean)

  2. 普通bean:在配置文件中定义的bean类型就是测试类中返回的类型

  3. 工厂bean:在配置文件中定义bean类型可以和返回类型不一样

  • 第一步 创建类,让这个类作为工厂bean,实现接口FactoryBean

  • 第二步 实现接口里面的方法,在实现的方法中定义返回的bean类型

  • bean类

public class MyBean implements FactoryBean<Course> {    //定义返回类型    @Override    public Course getObject() throws Exception {        Course course = new Course();        course.setCname("abc");        return course;    }    @Override    public Class<?> getObjectType() {        return null;    }    @Override    public boolean isSingleton() {        return false;    }}
  • ​ 配置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"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="myBean" class="com.liu.factorybean.MyBean"></bean></beans>
  • 测试类
@Testpublic void test3(){    ApplicationContext context = new ClassPathXmlApplicationContext("bean7.xml");    Course course = context.getBean("myBean", Course.class);    System.out.println(course);}输出:Course{cname='abc'}
4、bean作用域
  1. 在Spring里面,默认情况下,bean是单实例对象
@Testpublic void test2(){    ApplicationContext context = new ClassPathXmlApplicationContext("bean6.xml");    Book book1 = context.getBean("book", Book.class);    Book book2 = context.getBean("book", Book.class);    System.out.println(book1);    System.out.println(book2);}输出:com.liu.bean.Book@548ad73b	 com.liu.bean.Book@548ad73b
  1. 在spring配置文件xml中bean标签有属性(scope)用于设置单实例还是多实例
 <bean id="book" class="com.liu.bean.Book" scope="prototype">
输出:com.liu.bean.Book@548ad73b	 com.liu.bean.Book@4c762604

scope属性值:

  • 默认值,singleton,表示是单实例对象

  • prototype,表示是多实例对象

  1. singleton和prototype区别
  • singleton单实例,prototype多实例
  • 设置scope值是singleton时,加载spring配置文件时就会创建单实例对象
  • 设置scope值是prototype时,调用getBean方法时创建多实例对象
5、bean生命周期
  • 通过构造器创建bean实例(无参数构造器)

  • 为bean的属性设置值和对其他bean引用(即外部bean)(调用set方法)

  • 调用bean的初始化的方法(需要进行配置初始化的方法)

  • bean可以使用了(对象获取到了)

  • 当容器关闭时,调用bean的销毁的方法(需要进行配置销毁的方法)

  • bean类

public class Orders {    //无参构造    public Orders(){        System.out.println("第一步 执行无参数构造创建bean实例");    }    private String oname;    public void setOname(String oname) {        this.oname = oname;        System.out.println("第二部 调用set方法设置属性值");    }    //创建执行的初始化的方法    public void initMethod() {        System.out.println("第三步 执行初始化的方法");    }    //创建执行的销毁的方法    public void destroyMethod() {        System.out.println("第五步 执行销毁的方法");    }}
  • 配置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"       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">    <bean id="orders" class="com.liu.bean.Orders" init-method="initMethod" destroy-method="destroyMethod">        <property name="oname" value="显示器"></property>    </bean></beans>
  • 测试类
    @Test    public void test4(){        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("bean8.xml");        Orders orders = context.getBean("orders", Orders.class);        System.out.println("第四步 获取创建bean实例对象");        System.out.println(orders);        //第五步 手动让bean实例销毁        context.close();    }输出:第一步 执行无参数构造创建bean实例第二部 调用set方法设置属性值第三步 执行初始化的方法第四步 获取创建bean实例对象com.liu.bean.Orders@e720b71第五步 执行销毁的方法
bean后置处理器

bean生命周期有七步:

(1)通过构造器创建 bean 实例(无参数构造)

(2)为 bean 的属性设置值和对其他 bean 引用(调用 set 方法)

(3)把 bean 实例传递 bean 后置处理器的方法 postProcessBeforeInitialization

(4)调用 bean 的初始化的方法(需要进行配置初始化的方法)

(5)把 bean 实例传递 bean 后置处理器的方法 postProcessAfterInitialization

(6)bean 可以使用了(对象获取到了)

(7)当容器关闭时候,调用 bean 的销毁的方法(需要进行配置销毁的方法)

  • 创建类,实现接口BeanPostProcessor,创建后置处理器
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;    }}
  • 在xml中配置后置处理器
<bean id="myBeanPost" class="com.liu.bean.MyBeanPost"></bean>输出:第一步 执行无参数构造创建bean实例第二部 调用set方法设置属性值在初始化之前执行的方法第三步 执行初始化的方法在初始化之后执行的方法第四步 获取创建bean实例对象com.liu.bean.Orders@74ad1f1f第五步 执行销毁的方法
6、xml自动装配

什么是自动装配?

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

实现自动装配:

  • bean标签属性autowire,配置自动装配
    autowire属性常用两个值:
    • byName根据属性名称注入,注入值bean的id值和类属性名称一样
    • byType根据属性类型注入
<!--        根据属性名称注入--><bean id="emp" class="com.liu.autowire.Emp" autowire="byName"></bean><bean id="dept" class="com.liu.autowire.Dept"></bean><!--        根据属性类型注入--><bean id="emp" class="com.liu.autowire.Emp" autowire="byType"></bean><bean id="dept" class="com.liu.autowire.Dept"></bean>
7、外部属性文件
  1. 直接配置德鲁伊连接池
<!--引入德鲁伊连接池依赖jar包--><!--直接配置连接池-->    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">        <property name="driverClassName" value="com.mysql.jdbc.Driver"></property>        <property name="url" value="jdbc:mysql://localhost:3306/userDb"></property>        <property name="username" value="root"></property>        <property name="password" value="root"></property>    </bean>
  1. 引入外部属性文件配置数据库连接池
  • 创建jdbc.properties格式文件,写数据库信息
prop.driverClass=com.mysql.jdbc.Driverprop.url=jdbc:mysql://localhost:3306/userDbprop.userName=rootprop.password=root
  • 把外部 properties 属性文件引入到 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:p="http://www.springframework.org/schema/p"       xmlns:util="http://www.springframework.org/schema/util"       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/util http://www.springframework.org/schema/util/spring-util.xsd">                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
  • 在 spring 配置文件使用标签引入外部属性文件
<!--引入外部属性文件--> <context:property-placeholder location="classpath:jdbc.properties"/><!--配置连接池--> <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"> <property name="driverClassName" value="${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>
三、IOC容器-Bean管理(基于注解)
1.什么是注解?

a)注解是代码特殊标记,格式:@注解名称(属性名称=属性值,属性名称=属性值…)

b)使用注解,注解作用在类上面,方法上面,属性上面

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

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

a)@Component

b)@Service

c)@Controller

d)@Repository

上面四个注解功能是一样的,都可以用来创建bean实例

3.基于注解方式实现对象创建
  • 第一步 引入依赖

  • 开启组件扫描

<!--    开启组件扫描        1.如果扫描多个包,多个包使用逗号隔开        2.扫描包上层目录--><context:component-scan base-package="com.liu"></context:component-scan>
  • 创建bean类,在类上面添加创建对象注解
//在注解里面value属性值可以省略不写,默认值是类名称,首字母小写。UserService--userService@Component(value = "userService")public class UserService {    public void add(){        System.out.println("service add....");    }}
  • 测试类
@Testpublic void testService(){    ApplicationContext context = new ClassPathXmlApplicationContext("bean11.xml");    UserService userService = context.getBean("userService", UserService.class);    userService.add();}输出:service add....
4.开启组件扫描细节配置
<!--示例 1 use-default-filters="false" 表示现在不使用默认 filter,自己配置 filter context:include-filter ,设置扫描哪些内容--> 	<context:component-scan base-package="com.atguigu" use-default-filters="false">	<context:include-filter type="annotation"							expression="org.springframework.stereotype.Controller"/>	</context:component-scan>	<!--示例 2 下面配置扫描包所有内容 context:exclude-filter: 设置哪些内容不进行扫描-->      <context:component-scan base-package="com.atguigu">     <context:exclude-filter type="annotation"                             expression="org.springframework.stereotype.Controller"/>     </context:component-scan>
5.基于注解方式实现属性注入

a)@Autowired:根据属性类型进行自动装配

  • 第一步 把service和dao对象创建,在service和dao类添加创建对象注解
  • 第二步 在service注入dao对象,在service类添加dao类型属性,在属性上面使用注解@Autowired
@Componentpublic class UserService {    //定义dao类型属性 不需要添加set方法 添加注入属性注解    @Autowired    private UserDao userDao;    public void add(){        System.out.println("service add....");        userDao.add();    }}

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

  • 这个@Qualifier注解的使用,和上面@Autowired一起使用
 //定义dao类型属性 不需要添加set方法 添加注入属性注解    @Autowired //根据类型进行注入    @Qualifier(value = "userDaoImpl")//根据名称进行注入    private UserDao userDao;

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

//    @Resource //根据类型进行注入    @Resource(name = "userDaoImpl") //根据名称进行注入    private UserDao userDao;

d)@Value:注入普通类型属性

@Value(value = "abc")private String name;
6.完全注解开发
  • 创建配置类,替代xml配置文件
@Configuration //作为配置类,替代xml配置文件@ComponentScan(basePackages = {"com.liu"})public class SpringConfig {}
  • 测试类
@Testpublic void testService2(){    //加载配置类    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);    UserService userService = context.getBean("userService", UserService.class);    userService.add();}
四、AOP(面向切面编程)
1.什么是AOP?
  • 面向切面(方面)编程,利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率

  • 通俗描述:不通过修改源代码方式,在主干功能里面添加新功能

  • 例如,完整的登录功能,额外添加权限控制模块

2.AOP底层原理
  • AOP底层使用动态代理,有两种情况动态代理:

    • 第一种 有接口情况,使用JDK动态代理:创建接口实现类代理对象,增强类的方法

    • 第二种 没有接口情况,使用CGLIB动态代理:创建子类的代理对象,增强类的方法

3.AOP底层原理(JDK动态代理)

使用JDK动态代理,使用Proxy类里面的方法创建代理对象(在java.lang.reflect.Proxy)

  • 调用newProxyInstance方法

    方法有三个参数: 第一参数,类加载器

    ​ 第二参数,增强方法所在的类,这个类实现的接口,支持多个接口

    ​ 第三参数,实现这个接口InvocationHandler,创建代理对象,写增强的方法

  • 编写JDK动态代理代码

    • 创建接口,定义方法
    public interface UserDao {    public int add(int a,int b);    public String update(String id);}
    
    • 创建接口实现类,实现方法
    public class UserDaoImpl implements UserDao {    @Override    public int add(int a, int b) {        return a+b;    }    @Override    public String update(String id) {        return id;    }}
    
    • 使用Proxy类创建接口代理对象
    public class JDKProxy {    public static void main(String[] args) {        //创建接口实现类代理对象        Class[] interfaces = {UserDao.class};        UserDaoImpl userDao = new UserDaoImpl();        UserDao dao = (UserDao) Proxy.newProxyInstance(JDKProxy.class.getClassLoader(), interfaces, new UserDaoProxy(userDao));        int result = dao.add(1,2);        System.out.println("result:"+result);    }}//创建代理对象代码class UserDaoProxy implements InvocationHandler{    //1.创建某个类的代理对象,需要把某个类也要传递过来    //有参数构造传递    private Object obj;    public UserDaoProxy(Object obj){        this.obj = obj;    }    //增强的逻辑    @Override    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {        //方法之前        System.out.println("方法之前执行........"+method.getName()+":传递的参数....."+ Arrays.toString(args));        //被增强的方法执行        Object res = method.invoke(obj, args);        //方法之后        System.out.println("方法之后执行"+obj);        return res;    }}
    
4.AOP(术语)
  • 连接点:类里面哪些方法可以被增强,这些方法称为连接点

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

  • 通知(增强):实际增强的逻辑部分称为通知(增强)

    通知有多种类型:前置通知、后置通知、环绕通知、异常通知、最终通知

  • 切面:是动作,把通知应用到切入点过程

5.AOP操作(准备)
  • Spring框架一般都是基于AspectJ实现AOP操作

  • 什么是AspectJ?

    • AspectJ不是Spring组成部分,独立AOP框架,一般把AspectJ和Spring框架一起使用,进行AOP操作
  • 基于AspectJ实现AOP操作

    • 基于xml配置文件实现
    • 基于注解方式实现(一般使用此方式)
  • 在项目工程里面引入AOP相关依赖

  • 切入点表达式

    • 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
    • 语法结构
      • execution([][][][][权限修饰符] [返回类型] [类全路径] [方法名称] ([参数列表]) )

    举例1:对com.atguigu.dao.BookDao类里面的add进行增强

    ​ execution(* com.atguigu.dao.BookDao.add(…))

    举例2:对com.atguigu.dao.BookDao类里面的所有的方法进行增强

    ​ execution(* com.atguigu.dao.BookDao.*(…))

    举例3:对com.atguigu.dao包里所有类,类里面所有方法进行增强

    ​ execution(* com.atguigu.dao. * .*(…))

6.AOP操作(AspectJ注解)
  1. 创建类,在类里面定义方法
public class User {    public void add(){        System.out.println("add......");    }}
  1. 创建增强类(编写增强逻辑)
  • 在增强类里面,创建方法,让不同方法代表不同通知类型
//增强的类public class UserProxy {    //前置通知    public void before(){        System.out.println("before.....");    }}
  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.atguigu.spring5.aopannotation"></context:component-scan></beans>
  • 使用注解创建User和UserProxy对象
//被增强类@Componentpublic class User {    public void add(){        System.out.println("add......");    }}//增强类@Componentpublic class UserProxy {    //前置通知    public void before(){        System.out.println("before.....");    }}
  • 在增强类上面添加注解@Aspect
//增强类@Component@Aspect  // 生成代理对象public class UserProxy {
  • 在spring配置文件中开启生成代理对象
<!--    开启Aspect生成代理对象-->    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
  1. 配置不同类型的通知
  • 在增强类的里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置
//增强类@Component@Aspect  // 生成代理对象public class UserProxy {    //前置通知    //@Before注解表示作为前置通知    @Before(value = "execution(* com.atguigu.spring5.aopannotation.User.add(..))")    public void before(){        System.out.println("before.....");    }    //最终通知:有异常也执行    @After(value = "execution(* com.atguigu.spring5.aopannotation.User.add(..))")    public void after(){        System.out.println("after.....");    }    //异常通知    @AfterThrowing(value = "execution(* com.atguigu.spring5.aopannotation.User.add(..))")    public void afterThrowing(){        System.out.println("afterThrowing.....");    }    //后置通知(返回通知):有异常不执行    @AfterReturning(value = "execution(* com.atguigu.spring5.aopannotation.User.add(..))")    public void afterReturning(){        System.out.println("afterReturning.....");    }    //环绕通知    @Around(value = "execution(* com.atguigu.spring5.aopannotation.User.add(..))")    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{        System.out.println("环绕之前..........");        //被增强方法执行        proceedingJoinPoint.proceed();        System.out.println("环绕之后..........");    }}
  1. 相同的切入点抽取
//相同切入点抽取@Pointcut(value = "execution(* com.atguigu.spring5.aopannotation.User.add(..))")public void pointdemo(){}//前置通知//@Before注解表示作为前置通知@Before(value = "pointdemo()")public void before(){	System.out.println("before.....");}
  1. 有多个增强类对同一个方法进行增强,设置增强类优先级
  • 在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高
@Component@Aspect@Order(1)public class PersonProxy {
  1. 完全使用注解开发
  • 创建配置类,不需要创建xml配置文件
@Configuration@ComponentScan(basePackages = {"com.atguigu"})@EnableAspectJAutoProxy(proxyTargetClass = true)public class ConfigAop {}
7.AOP操作(AspectJ配置文件)
  1. 创建两个类,增强类和被增强类,创建方法
//被增强类public class Book {    public void buy(){        System.out.println("buy........");    }}//增强类public class BookProxy {    public void before(){        System.out.println("before.........");    }}
  1. 在spring配置文件中创建两个类对象
<!--    创建对象-->    <bean id="book" class="com.atguigu.spring5.aopxml.Book"></bean>    <bean id="bookProxy" class="com.atguigu.spring5.aopxml.BookProxy"></bean>
  1. 在spring配置文件中配置切入点
<!--    配置aop增强-->    <aop:config><!--        切入点-->        <aop:pointcut id="p" expression="execution(* com.atguigu.spring5.aopxml.Book.buy(..))"/><!--        配置切面-->        <aop:aspect ref="bookProxy"><!--            增强作用在具体的方法上-->            <aop:before method="before" pointcut-ref="p"></aop:before>        </aop:aspect>    </aop:config>
五、JdbcTemplate(概念和准备)
1.什么是JdbcTemplate?
  • Spring框架对JDBC进行封装,使用JdbcTemplate方便实现对数据库操作
2.准备工作
  • 引入相关jar包

  • 在spring配置文件配置数据库连接池

        <!-- 数据库连接池 -->    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource"          destroy-method="close">        <property name="url" value="jdbc:mysql:///user_db" />        <property name="username" value="root" />        <property name="password" value="root" />        <property name="driverClassName" value="com.mysql.jdbc.Driver" />    </bean>
    
  • 配置JdbcTemplate对象,注入DataSource

    <!--    创建JdbcTemplate对象-->    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"><!--        注入dataSource-->        <property name="dataSource" ref="dataSource"></property>    </bean>
    
  • 创建service类,创建dao类,在dao注入jdbcTemplate对象

    • 开启组件扫描
    <!--    组件扫描-->    <context:component-scan base-package="com.atguigu.spring5"></context:component-scan>
    
    • Service类
    @Servicepublic class BookService {    //注入dao    @Autowired    private BookDao bookDao;}
    
    • Dao类
    @Repositorypublic class BookDaoImpl implements BookDao{    //注入jdbcTemplate    @Autowired    private JdbcTemplate jdbcTemplate;}
    
3.JdbcTemplate操作数据库(添加)
  • 对应数据库创建实体类

    public class User {    private String userId;    private String username;    private String ustatus;    public String getUserId() {        return userId;    }    public void setUserId(String userId) {        this.userId = userId;    }    public String getUsername() {        return username;    }    public void setUsername(String username) {        this.username = username;    }    public String getUstatus() {        return ustatus;    }    public void setUstatus(String ustatus) {        this.ustatus = ustatus;    }}
    
  • 编写service和dao

    • 在dao进行数据库添加操作

    • 调用jdbcTemplate对象里面update方法实现添加操作update(String sql,Object… args)

      • 有两个参数:sql语句,可变参数
      @Repositorypublic class BookDaoImpl implements BookDao{    //注入jdbcTemplate    @Autowired    private JdbcTemplate jdbcTemplate;    //添加的方法    @Override    public void add(Book book) {        //1.创建sql语句        String sql = "insert into t_book values(?,?,?)";        //2.调用方法实现        int update = jdbcTemplate.update(sql, book.getUserId(), book.getUsername(), book.getUstatus());        System.out.println(update);       }}
      
  • 测试类

public class TestBook {    @Test    public void testJdbcTemplate(){        ApplicationContext context = new ClassPathXmlApplicationContext("bean14.xml");        BookService bookService = context.getBean("bookService", BookService.class);        Book book = new Book();        book.setUserId("1");        book.setUsername("java");        book.setUstatus("a");        bookService.addBook(book);    }}
4.JdbcTemplate操作数据库(修改和删除)
    //修改    @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 delete(String id) {        String sql = "delete from t_book where user_id=?";        int update = jdbcTemplate.update(sql,id);        System.out.println(update);    }
5.JdbcTemplate操作数据库(查询返回某个值)
  • 查询表里面有多少条记录,返回是某个值
  • 使用JdbcTemplate实现查询返回某个值代码:jdbcTemplate.queryForObject(sql, Integer.class);
    • 有两个参数,sql语句,返回类型class
    @Override    public int selectCount() {        String sql = "select count(*) from t_book";        Integer count = jdbcTemplate.queryForObject(sql, Integer.class);        return count;    }
6.JdbcTemplate操作数据库(查询返回对象)
  • 场景:查询图书详情

  • JdbcTemplate实现查询返回对象:

    jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper(Book.class), id);

    • 有三个参数,第一个 sql语句,第三个 sql语句值,第二个 RowMapper是接口,针对返回不同类型数据,使用这个接口里面实现类完成数据封装
    //查询返回对象    @Override    public Book findBookInfo(String id) {        String sql = "select * from t_book where user_id=?";        //调用方法        Book book = jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<Book>(Book.class), id);        return book;    }
7.JdbcTemplate操作数据库(查询返回集合)
  • 场景:查询图书列表分页。。。

  • 调用JdbcTemplate方法实现查询返回集合:

    jdbcTemplate.query(sql, new BeanPropertyRowMapper(Book.class));

    • 同样有三个参数,参数意义与查询返回对象一样
@Overridepublic List<Book> findAllBook() {    String sql = "select * from t_book";    //调用方法    List<Book> bookList = jdbcTemplate.query(sql, new BeanPropertyRowMapper<Book>(Book.class));    return bookList;}
8.JdbcTemplate操作数据库(批量添加)
  • 批量操作:操作表里面多条数据
  • JdbcTemplate实现批量添加操作:jdbcTemplate.batchUpdate(sql, batchArgs);
    • 有两个参数,sql语句、List集合,添加多条记录数据
    //批量添加    @Override    public void batchAddBook(List<Object[]> batchArgs) {        String sql = "insert into t_book values(?,?,?)";        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);        System.out.println(Arrays.toString(ints));    }    //批量添加的测试(使用)    List<Object[]> batchArgs = new ArrayList<>();    Object[] o1 = {"3","java","a"};    Object[] o2 = {"4","c++","b"};    Object[] o3 = {"5","MySQL","c"};    batchArgs.add(o1);    batchArgs.add(o2);    batchArgs.add(o3);    bookService.batchAdd(batchArgs);
9.JdbcTemplate操作数据库(批量修改)
    //批量修改    @Override    public void batchUpdateBook(List<Object[]> batchArgs) {        String sql = "update t_book set username=?,ustatus=? where user_id=?";        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);        System.out.println(Arrays.toString(ints));    }    //批量修改    List<Object[]> batchArgs = new ArrayList<>();    Object[] o1 = {"java22","a2","3"};    Object[] o2 = {"c++123","b3","4"};    Object[] o3 = {"MySQL66","c4","5"};    batchArgs.add(o1);    batchArgs.add(o2);    batchArgs.add(o3);    bookService.batchUpdate(batchArgs);
10.JdbcTemplate操作数据库(批量删除)
    //批量删除    @Override    public void batchDeleteBook(List<Object[]> batchArgs) {        String sql = "delete from t_book where user_id=?";        int[] ints = jdbcTemplate.batchUpdate(sql, batchArgs);        System.out.println(Arrays.toString(ints));    }    //批量删除    List<Object[]> batchArgs = new ArrayList<>();    Object[] o1 = {"3"};    Object[] o2 = {"4"};    batchArgs.add(o1);    batchArgs.add(o2);    bookService.batchDelete(batchArgs);
六、事务概念
  • 什么是事务?

    • 事务是数据库操作最基本单元,逻辑上的一组操作,要么都成功,如果有一个失败所有操作都失败
    • 典型场景:银行转账:joy转账100元给lucy—>joy少100,lucy多100
  • 事务四个特性(ACID)

    • 原子性:要么都成功,要么失败都失败
    • 一致性:操作之前和操作之后总量不变
    • 隔离性:多事务操作时,它们之间不会产生影响
    • 持久性:操作之后,保持状态不变
1.事务操作(搭建事务操作环境)

dao用来写数据库操作,不写业务

service用来写业务操作

  • 创建数据库表,添加记录

    t_account表里lucy和mary各有1000元

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

    • service注入dao,在dao注入JdbcTemplate,在JdbcTemplate注入DataSource
    @Servicepublic class UserService {    //注入Dao    @Autowired    private UserDao userDao;}
    
    @Repositorypublic class UserDaoImpl implements UserDao {    @Autowired    private JdbcTemplate jdbcTemplate;}
    
  • 在dao创建两个方法:多钱和少钱的方法,在service创建方法(转账的方法)

    //dao类@Repositorypublic class UserDaoImpl implements UserDao {    @Autowired    private JdbcTemplate jdbcTemplate;    //多钱    @Override    public void addMoney() {        String sql = "update t_account set money=money+? where username=?";        jdbcTemplate.update(sql, 100,"mary");    }    //少钱    @Override    public void reduceMoney() {        String sql = "update t_account set money=money-? where username=?";        jdbcTemplate.update(sql, 100,"lucy");    }}
    
    //service类@Servicepublic class UserService {    //注入Dao    @Autowired    private UserDao userDao;    //转账的方法    public void accountMoney(){        //lucy少100        userDao.reduceMoney();        //mary多100        userDao.addMoney();    }}
    
2.事务场景的引入
  • 如果出现了以下异常,需要用事务解决

    @Servicepublic class UserService {    //注入Dao    @Autowired    private UserDao userDao;    //转账的方法    public void accountMoney(){        //lucy少100        userDao.reduceMoney();        //模拟异常        int i = 10/0;         //mary多100        userDao.addMoney();    }}
    
  • 事务如何解决?

    @Servicepublic class UserService {    //注入Dao    @Autowired    private UserDao userDao;    //转账的方法    public void accountMoney(){        try{            //第一步 开启事务            //第二部 进行业务操作            //lucy少100            userDao.reduceMoney();            //模拟异常            int i = 10/0;            //mary多100            userDao.addMoney();            //第三步 没有发生异常,提交事务        }catch (Exception e){            //第四步 出现异常,事务回滚        }      }}
    
3.事务操作(Spring事务管理介绍)
  1. 事务添加到JavaEE三层结构里面Service层(业务逻辑层)

  2. 在Spring进行事务管理操作

    • 有两种方式:编程式事务管理和声明式事务管理(一般使用)
  3. 声明式事务管理

    • 基于注解方式(一般使用)
    • 基于xml配置文件方式
  4. 在Spring进行声明式事务管理,底层使用AOP原理

  5. Spring事务管理API提供了一个接口PlatformTransactionManager,代表事务管理器,这个接口针对不同的框架提供不同的实现类。Spring和MyBatis都是使用DataSourceTransactionManager实现类。

4.事务操作(注解声明式事务管理)
  1. 在spring配置文件配置事务管理器

    <!--    创建事务管理器-->    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"><!--        注入数据源-->        <property name="dataSource" ref="dataSource"></property>    </bean>
    
  2. 在spring配置文件,开启事务注解

    • 在spring配置文件引入名称空间tx
    <?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"       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/tx http://www.springframework.org/schema/tx/spring-tx.xsd">
    
    • 开启事务注解
    <!--    开启事务注解-->    <tx:annotation-driven transaction-manager="transactionManager"></tx:annotation-driven>
    
  3. 在service类上面(或者service类里面方法上面)添加事务注解

    • @Transactional,这个注解添加到类上面,也可以添加方法上面
    • 如果把这个注解添加到类上面,这个类里面所有的方法都添加事务
    • 如果把这个注解添加方法上面,为这个方法添加事务
@Service@Transactionalpublic class UserService {
5.事务操作(声明式事务管理参数配置)
  1. 在service类上面添加注解@Transactional,在这个注解里面可以配置事务相关参数

    //事务方法:对数据库表数据进行变化的操作@Transactionalpublic void add(){	//调用update方法	update();}public void update(){}
    
  2. propagation:事务传播行为,Spring框架事务传播行为有7种(这里只记录最常用的两种)

    • REQUIRED:默认属性。如果add方法本身有事务,调用update方法之后,update使用当前add方法里面事务;如果add方法本身没有事务,调用update方法之后,创建新事务。

    • REQUIRED_NEW:使用add方法调用update方法,如果add无论是否有事务,都创建新的事务。

      @Service@Transactional(propagation = Propagation.REQUIRED)public class UserService {
      
  3. ioslation:事务隔离级别

    • 事务有特性称为隔离性,多事务操作之间不会产生影响。

    • 不考虑隔离性会产生三个读问题:脏读、不可重复读、虚(幻)读

    • 脏读:一个未提交事务读取到另一个未提交事务的数据。岳不群将余额从100改到200,如果此时事务回滚,又在此期间东方不败读取到了200,而实际岳不群事务回滚,实际数额100.

    • 不可重复读:一个未提交事务读取到另一提交事务修改的数据。岳不群将余额从100改到200,并提交,东方不败在岳不群提交前后两次查看,而两次数值不一样的现象。

    • 虚读:一个未提交事务读取到另一提交事务添加的数据

    @Service@Transactional(propagation = Propagation.REQUIRED,isolation = Isolation.REPEATABLE_READ)public class UserService {
    
  • 隔离级别 脏读 不可重复读 幻读

    READ UNCOMMITTED(读未提交) 有 有 有

    READ COMMITTED(读已提交) 无 有 有

    REPEATABLE READ(可重复读) 无 无 有

    SERIALIZABLE(串行化) 无 无 无

  • mysql数据库默认隔离级别是可重复读。

  1. timeout:超时时间

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

    • 默认值是-1(不超时),设置时间以秒单位进行计算

  2. readOnly:是否只读

    • readOnly默认值false,表示可以查询,可以添加修改删除操作;设置true,只能查询
  3. rollbackFor:回滚

    • 设置出现哪些异常进行事务回滚
  4. noRollbackFor:不回滚

    • 设置出现哪些异常不进行事务回滚
6.事务操作(XML声明式事务管理)

在spring配置文件中进行配置

  • 第一步 配置事务管理器
  • 第二步 配置通知
  • 第三步 配置切入点和切面
<!--    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:method name="account*" propagation="REQUIRED"/>-->        </tx:attributes>    </tx:advice>    <!--配置切入点和切面-->    <aop:config><!--        配置切入点-->        <aop:pointcut id="pt" expression="execution(* com.liuliu.service.UserService.*(..))"/><!--        配置切面-->        <aop:advisor advice-ref="txadvice" pointcut-ref="pt"></aop:advisor>    </aop:config>
7.事务操作(完全注解声明式事务管理)
  • 创建配置类,使用配置类替代xml配置文件

    @Configuration//配置类@ComponentScan(basePackages = "com.liuliu")//组件扫描@EnableTransactionManagement //开启事务public class TxConfig {    //创建数据库连接池    @Bean    public DruidDataSource getDruidDataSource(){        DruidDataSource dataSource = new DruidDataSource();        dataSource.setDriverClassName("com.mysql.jdbc.Driver");        dataSource.setUrl("jdbc:mysql:///user_db");        dataSource.setUsername("root");        dataSource.setPassword("123456");        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 dataSourceTransactionManager = new DataSourceTransactionManager();        dataSourceTransactionManager.setDataSource(dataSource);        return dataSourceTransactionManager;    }}
    
  • 测试类

        @org.junit.Test    public void testAccount2(){        ApplicationContext context = new AnnotationConfigApplicationContext(TxConfig.class);        UserService userService = context.getBean("userService", UserService.class);        userService.accountMoney();    }
    
七、Spring5框架新功能

整个Spring5框架的代码基于Java8,运行时兼容JDK9,许多不建议使用的类和方法在代码库中删除

1.Spring5框架自带了通用的日志封装
  • Spring5已经移除Log4jConfigListener,官方建议使用Log4j2

  • Spring5框架整合Log4j2

  • 第一步 引入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>
    
2.Spring5框架核心容器支持@Nullable注解
  • @Nullable注解可以使用在方法上面,属性上面,参数上面。表示方法返回可以为空,属性值可以为空,参数值可以为空。

    //注解用在方法上面,方法返回值可以为空@NullableString getId();//注解使用在方法参数里面,方法参数可以为空public <T> void registerBean(@Nullable String beanName, Class<T> beanClass, @Nullable Supplier<T> supplier, BeanDefinitionCustomizer... customizers){}//注解使用在属性上面,属性值可以为空@Nullableprivate String bookName;
    
3.Spring5核心容器支持函数式风格GenericApplicationContext
    //函数式风格创建对象,交给spring进行管理    @org.junit.Test    public void testGenericApplicationContext(){        //1.创建GenericApplicationContext对象        GenericApplicationContext context = new GenericApplicationContext();        //2.调用context对象的方法进行注册,用Lambda表达式        context.refresh();        context.registerBean("user1",User.class, () -> new User());        //3.获取在spring注册的对象//        User user = (User)context.getBean("com.liuliu.test.User");        User user = (User)context.getBean("user1");        System.out.println(user);    }
4.Spring5支持整合JUnit5
  1. 整合JUnit4

    • 第一步 引入Spring相关针对测试依赖

    • 第二步 创建测试类,使用注解方式完成

    @RunWith(SpringJUnit4ClassRunner.class)//单元测试框架@ContextConfiguration("classpath:bean15.xml")//加载配置文件public class JTest4 {    @Autowired    private UserService userService;    @Test    public void test1(){        userService.accountMoney();    }}
    
  2. Spring5整合JUnit5

    • 第一步 引入JUnit5的jar包

    • 第二步 创建测试类,使用注解完成

    @ExtendWith(SpringExtension.class)@ContextConfiguration("classpath:bean15.xml")public class JTest5 {    @Autowired    private UserService userService;    @Test    public void test1(){        userService.accountMoney();    }}
    
  • 使用一个复合注解替代上面两个注解完成整合

    @SpringJUnitConfig(locations = "classpath:bean15.xml")public class JTest5 {    @Autowired    private UserService userService;    @Test    public void test1(){        userService.accountMoney();    }}
    
5.Spring5框架新功能(Webflux)
  1. SpringWebflux介绍
  • 是Spring5添加新的模块,用于web开发的,功能和SpringMVC类似的,Webflux使用当前一种比较流行响应式编程出现的框架

  • 使用传统web框架,比如SpringMVC,这些基于Servlet容器,Webflux是一种异步非阻塞的框架,异步非阻塞的框架在Servlet3.1以后才支持,核心是基于Reactor的相关API实现的

  • 解释什么是异步非阻塞?

    • 异步和同步针对调用者,调用者发送请求,如果等着对方回应之后才去做其他事情就是同步,如果发送请求之后不等着对方回应就去做其他事情就是异步
    • 阻塞和非阻塞针对被调用者,被调用者收到请求之后,做完请求任务之后才给出反馈就是阻塞,收到请求之后马上给出反馈然后再去做其他事情就是非阻塞
  • Webflux特点:

    • 第一 非阻塞式:在有限资源下,提高系统吞吐量和伸缩性,以Reactor为基础实现响应式编程
    • 第二 函数式编程:Spring5框架基于java8,Webflux使用java8函数式编程方式实现路由请求
  • 比较SpringMVC

    • 第一 两个框架都可以使用注解方式,都运行在Tomcat等容器中
    • 第二 SpringMVC采用命令式编程,Webflux采用异步响应式编程
  1. 响应式编程
  • 什么是响应式编程?

    • 响应式编程是一种面向数据流和变化传播的编程范式。这意味着可以在编程语言中很方便地表达静态或动态的数据流,而相关的计算模型会自动将变化的值通过数据流进行传播。
    • 电子表格程序就是响应式编程的一个例子。单元格可以包含字面值或类似“=B1+C1”的公式,而包含公式的单元格的值会依据其他单元格的值的变化而变化。
  • Java8及其之前版本

    • 提供的观察者模式两个类Observer和Observable
    public class ObserverDemo extends Observable {    public static void main(String[] args) {        ObserverDemo observerDemo = new ObserverDemo();        //添加观察者        observerDemo.addObserver((o,arg)->{            System.out.println("发生变化!");        });        //添加观察者        observerDemo.addObserver((o,arg)->{            System.out.println("手动被观察者通知,准备改变!");        });        observerDemo.setChanged();//监测数据变化        observerDemo.notifyObservers();//通知    }}
    
  1. 响应式编程(Reactor实现)
  • 响应式编程操作中,Reactor是满足Reactive规范框架

  • Reactor有两个核心类,Mono和Flux,这两个类实现接口Publisher,提供丰富操作符。Flux对象实现发布者,返回N个元素;Mono实现翻发布者,返回0个或者1个元素

  • Flux和Mono都是数据流的发布者,使用Flux和Mono都可以发出三种数据信号:元素值、错误信号、完成信号。错误信号和完成信号都代表终止信号,终止信号用于告诉订阅者数据流结束了,错误信号终止数据流同时把错误信息传递给订阅者

  • 代码演示Flux和Mono

    • 第一步 引入依赖
    <dependency>    <groupId>io.projectreactor</groupId>    <artifactId>reactor-bom</artifactId>    <version>2020.0.10</version></dependency>
    
    • 第二步 编写代码
    public class TestReactor {    public static void main(String[] args) {        //just方法直接声明        Flux.just(1,2,3,4);        Mono.just(1);        //其他方法        Integer[] array = {1,2,3,4};        Flux.fromArray(array);        List<Integer> list = Arrays.asList(array);        Flux.fromIterable(list);        Stream<Integer> stream = list.stream();        Flux.fromStream(stream);    }}
    
  • 三种信号特点

    • 错误信号和完成信号都是终止信号,不能共存
    • 如果没有发送任何元素值,而是直接发送错误或者完成信号,表示是空数据流
    • 如果没有错误信号,没有完成信号,表示是无限数据流
  • 调用just或者其他方法只是声明数据流,数据流并没有发出,只有进行订阅之后才会触发数据流,不订阅什么都不会发生的

    Flux.just(1,2,3,4).subscribe(System.out::println);Mono.just(1).subscribe(System.out::println);
    
  • 操作符:对数据流进行一道道操作,成为操作符,比如工厂流水线

    • 第一 map元素映射为新元素
    • 第二 flatMap元素映射为流:把每个元素转换流,把转换之后多个流合并大的流
  1. SpringWebflux执行流程和核心API

    • SpringWebflux基于Reactor,默认使用容器是Netty,Netty是高性能的NIO框架,异步非阻塞的框架

    • SpringWebflux核心控制器DispatchHandler,实现接口WebHandler

    • WebHandler有一个方法

    public interface WebHandler{	Mono<Void> handle(ServerWebExchange var1);}
    

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mEAHLuqB-1632230589428)(C:\Users\The King\AppData\Roaming\Typora\typora-user-images\image-20210920212656883.png)]

    • SpringWebflux里面DispatchHandler,负责请求的处理

      • HandlerMapping:请求查询到处理的方法
      • HandlerAdapter:真正负责请求处理
      • HandlerResultHandler:响应结果处理
    • SpringWebflux实现函数式编程,两个接口:RouterFunction(路由处理)和HandlerFunction(处理函数)

  2. SpringWebflux(基于注解编码模型)

SpringWebflux实现方式有两种:注解编程模型和函数式编程模型。使用注解编程模型方式和之前SpringMVC使用相似的,只需要把相关依赖配置到项目中,SpringBoot自动配置相关运行容器,默认情况下使用Netty服务器

  • 第一步 创建SpringBoot工程,引入Webflux依赖

    <dependency>	<groupId>org.springframework.boot</groupId>	<artifactId>spring-boot-starter-webflux</artifactId></dependency>
    
  • 第二步 配置启动端口号(application.properties)

    server.port=8081
    
  • 第三步 创建包和相关类

    //实体类public class User {    private String name;    private String gender;    private Integer age;    public User(String name, String gender, Integer age) {        this.name = name;        this.gender = gender;        this.age = age;    }        public String getName() {return name;}    public void setName(String name) {this.name = name;}    public String getGender() {return gender;}    public void setGender(String gender) {this.gender = gender;}    public Integer getAge() {return age;}    public void setAge(Integer age) {this.age = age;}}
    
  • 创建接口定义操作的方法

    //用户操作接口public interface UserService {    //根据id查询用户    Mono<User> getUserById(int id);    //查询所有用户    Flux<User> getAllUser();    //添加用户    Mono<Void> saveUserInfo(Mono<User> user);}
    
  • 接口实现类

    @Repositorypublic class UserServiceImpl implements UserService{    //创建Map集合存储数据    private final Map<Integer,User> users = new HashMap<>();    public UserServiceImpl(){        this.users.put(1,new User("lucy","girl",20));        this.users.put(2,new User("snow","boy",30));        this.users.put(3,new User("jack","boy",50));    }    //根据id查询    @Override    public Mono<User> getUserById(int id) {        return Mono.justOrEmpty(this.users.get(id));    }    //查询多个用户    @Override    public Flux<User> getAllUser() {        return Flux.fromIterable(this.users.values());    }    //添加用户    @Override    public Mono<Void> saveUserInfo(Mono<User> userMono) {        return userMono.doOnNext(person -> {            //向map集合里面放值            int id = users.size() + 1;            users.put(id,person);        }).thenEmpty(Mono.empty());    }}
    
  • 创建controller

    @RestControllerpublic class UserController {    //注入service    @Autowired    private UserService userService;    //id查询    @GetMapping("/user/{id}")    public Mono<User> getUserId(@PathVariable int id){        return userService.getUserById(id);    }    //查询所有    @GetMapping("/user")    public Flux<User> getUsers(){        return userService.getAllUser();    }    //添加    @PostMapping("/saveuser")    public Mono<Void> saveUser(@RequestBody User user){        Mono<User> userMono = Mono.just(user);        return userService.saveUserInfo(userMono);    }}
    
  • 说明:SpringMVC方式实现,同步阻塞的方式,基于SpringMVC+Servlet+Tomcat。

    ​ SpringWebflux方式实现,异步非阻塞方式,基于SpringWebflux+Reactor+Netty

  1. SpringWebflux(基于函数式编程模型)
  • 在使用函数式编程模型操作时候,需要自己初始化服务器

  • 基于函数式编程模型时候,有两个核心接口:RouterFunction(实现路由功能,请求转发给对应的handler)和HandlerFunction(处理请求生成响应的函数)。核心任务定义两个函数式接口的实现并且启动需要的服务器。

  • SpringWebflux请求和响应不再是ServletRequest和ServletResponse,而是ServerRequest和ServerResponse。

  • 第一步 复制注解编程模型工程

  • 第二步 创建Handler(具体实现方法)

    public class UserHandler {    private final UserService userService;    public UserHandler(UserService userService) {        this.userService = userService;    }    //根据id查询    public Mono<ServerResponse> getUserById(ServerRequest request){        //获取id        int userId = Integer.valueOf(request.pathVariable("id"));        //空值处理        Mono<ServerResponse> notFound = ServerResponse.notFound().build();        //调用service方法得到数据        Mono<User> userMono = this.userService.getUserById(userId);        //把userMono进行转换返回        //使用Reactor操作符flatMap        return                userMono                        .flatMap(person -> ServerResponse.ok().contentType(MediaType.APPLICATION_JSON)                                .body(fromObject(person)))                                        .switchIfEmpty(notFound);    }    //查询所有    public Mono<ServerResponse> getAllUsers(){        //调用service得到结果        Flux<User> users = this.userService.getAllUser();        return ServerResponse.ok().contentType(MediaType.APPLICATION_JSON).body(users,User.class);    }    //添加    public Mono<ServerResponse> saveUser(ServerRequest request){        //得到user对象        Mono<User> userMono = request.bodyToMono(User.class);        return ServerResponse.ok().build(this.userService.saveUserInfo(userMono));    }}
    
  • 第三步 初始化服务器,编写Router

    • 创建路由的方法
    //1.创建Router路由public RouterFunction<ServerResponse> routerFunction(){    //创建handler对象    UserServiceImpl userService = new UserServiceImpl();    UserHandler handler = new UserHandler(userService);    //设置路由    return RouterFunctions.route(            GET("/users/{id}").and(accept(MediaType.APPLICATION_JSON)),handler::getUserById)            .andRoute(GET("/users").and(accept(MediaType.APPLICATION_JSON)),handler::getAllUsers);}
    
    • 创建服务器完成适配
    //2 创建服务器完成适配    public void createReactorServer() {        //路由和 handler 适配        RouterFunction<ServerResponse> route = routingFunction();        HttpHandler httpHandler = toHttpHandler(route);        ReactorHttpHandlerAdapter adapter = new                ReactorHttpHandlerAdapter(httpHandler);        //创建服务器        HttpServer httpServer = HttpServer.create();        httpServer.handle(adapter).bindNow();    }
    
    • 最终调用
    public static void main(String[] args) throws Exception{ Server server = new Server(); server.createReactorServer(); System.out.println("enter to exit"); System.in.read();}
    
  • 使用WebClient调用

public class Client {    public static void main(String[] args) {        //调用服务器地址        WebClient webClient = WebClient.create("http://127.0.0.1:5794");        //根据 id 查询        String id = "1";        User userresult = webClient.get().uri("/users/{id}", id)                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToMono(User                        .class)                .block();        System.out.println(userresult.getName());        //查询所有        Flux<User> results = webClient.get().uri("/users")                .accept(MediaType.APPLICATION_JSON).retrieve().bodyToFlux(User                        .class);        results.map(stu -> stu.getName())                .buffer().doOnNext(System.out::println).blockFirst();    } }
八、课程总结

1、Spring 框架概述

(1)轻量级开源 JavaEE 框架,为了解决企业复杂性,两个核心组成:IOC 和 AOP

(2)Spring5.2.6 版本

2、IOC 容器

(1)IOC 底层原理(工厂、反射等)

(2)IOC 接口(BeanFactory)

(3)IOC 操作 Bean 管理(基于 xml)

(4)IOC 操作 Bean 管理(基于注解)

3、Aop

(1)AOP 底层原理:动态代理,有接口(JDK 动态代理),没有接口(CGLIB 动态代理)

(2)术语:切入点、增强(通知)、切面

(3)基于 AspectJ 实现 AOP 操作

4、JdbcTemplate

(1)使用 JdbcTemplate 实现数据库 curd 操作

(2)使用 JdbcTemplate 实现数据库批量操作

5、事务管理

(1)事务概念

(2)重要概念(传播行为和隔离级别)

(3)基于注解实现声明式事务管理

(4)完全注解方式实现声明式事务管理

6、Spring5 新功能

(1)整合日志框架

(2)@Nullable 注解

(3)函数式注册对象

(4)整合 JUnit5 单元测试框架

(5)SpringWebflux 使用

Spring是一个开源的Java框架,用于构建企业级应用程序。它提供了一种轻量级的、非侵入式的开发方式,通过依赖注入和面向切面编程等特性,简化了Java应用程序的开发过程。 以下是关于Spring学习的一些笔记: 1. IoC(控制反转):Spring通过IoC容器管理对象的创建和依赖关系的注入。通过配置文件或注解,将对象的创建和依赖关系的维护交给Spring容器来管理,降低了组件之间的耦合度。 2. DI(依赖注入):Spring通过依赖注入将对象之间的依赖关系解耦。通过构造函数、Setter方法或注解,将依赖的对象注入到目标对象中,使得对象之间的关系更加灵活和可维护。 3. AOP(面向切面编程):Spring提供了AOP的支持,可以将与业务逻辑无关的横切关注点(如日志、事务管理等)从业务逻辑中分离出来,提高了代码的可重用性和可维护性。 4. MVC(模型-视图-控制器):Spring提供了一个MVC框架,用于构建Web应用程序。通过DispatcherServlet、Controller、ViewResolver等组件,实现了请求的分发和处理,将业务逻辑和视图展示进行了分离。 5. JDBC和ORM支持:Spring提供了对JDBC和ORM框架(如Hibernate、MyBatis)的集成支持,简化了数据库访问的操作,提高了开发效率。 6. 事务管理:Spring提供了对事务的支持,通过声明式事务管理和编程式事务管理,实现了对数据库事务的控制和管理。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值