ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

2021-09-21

2021-09-21 21:30:44  阅读:107  来源: 互联网

标签:21 void 09 System class bean 2021 public String


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 使用

标签:21,void,09,System,class,bean,2021,public,String
来源: https://blog.csdn.net/qq_43333756/article/details/120405678

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有