spring学习


补Spring笔记,第一次学习总是没有什么印象,不断的重复学习以及使用才能更好的掌握知识点。

循环依赖:指的是多个Bean之间的相互调用,A对象创建时,需要构造B对象,构造B对象时需要构建C对象,构建C对象时又依赖A对象

1. Spring Start Learn

1.1 spring简介:

Spring框架以interface 21框架为基础,经过重新设计,并不断丰富其内涵,2004年发布1.0版本,由音乐学博士Rod Johnson创建,其专业是音乐学并不是计算机。

Spring是一个开源的框架,是一个轻量级、非侵入式的框架。

1.2 Spring理念

使得现有技术更容易使用,自身集成了各种组件

其核心为控制反转(IOC)、面向切面(AOP)

支持事务的处理

Spring体系结构

1.3 学习新的web框架

SSM:spring + springMVC+Mybatis

1.4 相关网址

github:托管源码网址 使用的是Gradlew

官网:官网介绍

官方网址下载地址:官方下载地址

1.5 构建项目,使用Maven

maven依赖

<!--SpringMVC,引入该依赖会将spring的相关依赖引入-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>
<!--引入数据库相关的连接组件-->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.0.RELEASE</version>
</dependency>

Spring整合太多的框架以及引进过多的组件,产生的弊端是“配置过多”

1.6 Spring好处

方便解耦、简化开发

AOP编程的支持

声明式事务的支持

便于单元测试

继承各种框架

降低 javaAPI的使用难度

2. IOC

Ioc是一种设计思想,称之为控制反转,可以分解为控制和反转,IOC容器控制着对象的创建或者叫做外部资源的获取方式; 一般主动的new对象是在程序中,程序主动创建对象,IOC容器管理着对象,因此程序中的对象是被动的接受依赖对象,因此称之为反转

2.1 传统的耦合开发

传统的开发,面向接口的开发,数据层接口Dao的实现类DaoImpl在Service中手动创建,然后调用底层的数据操作方法将业务层数据持久化。

项目的分层截图:

项目的目录结构

传统开发流程

dao层

//dao层的接口
public interface UserDao {
    void sayHello();
}
//dao层的实现类
public class UserDaoImpl implements UserDao {

    @Override
    public void sayHello() {
        System.out.println("Hello Spring");
    }
}

service层

//service接口
public interface UserService {
    void sayHello();
}
//service层实现类,依赖dao层,耦合程度较高
public class UserServiceImpl implements UserService {
    UserDao userdao = new UserDaoImpl();
    @Override
    public void sayHello() {
        userdao.sayHello();
    }
}

缺点是才层之间是一个高耦合程序,因此可以采用setter进行接耦合,对象的创建不在于程序本身而是在于用户传入的参数。

 //对象的创建在于传入的参数不再是直接new一个对象,耦合性降低
    private UserDao userDao;
    public void setUserDao(UserDao userDao){
        this.userDao = userDao;
    }
2.2 IOC将对象的创建交给spring

IOC:Inversion of Control 控制反转,将对象的创建交给Spring去管理,实现了程序之间的解耦合。

在dao层有三种数据库的连接方式,不修改程序只修改配置文件完成对dao实例的选取。

创建一个普通的对象类

public class Customer {
    String name;

    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                '}';
    }
}

spring中的配置文件是applicationContext.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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--普通的Bean使用spring管理,其字段使用value进行赋值-->
    <bean name="custom" class="com.itxing.pojo.Customer">
        <property name="name" value="xing"></property>
    </bean>

    <!--将dao层的对象注入到spring容器中-->
    <bean id="mysqlDao" class="com.itxing.dao.UserMysqlDaoImpl"></bean>
    <bean id="oracleDao" class="com.itxing.dao.UserOracleDaoImpl"></bean>
    <bean id="userService" class="com.itxing.service.UserServiceImpl">
        <!--使用ref将对象与前面的对象进行关联,可以进行切换不用修改配置文件-->
        <property name="userDao" ref="mysqlDao"></property>
        <!-- <property name="userDao" ref="oracleDao"></property>-->
    </bean>
</beans>

对普通的Bean进行测试,对象不再使用new进行创建将其交给spring

@Test
    public void Mytest2(){
        //获取spring上下文对象,根据上下文对象将spring中的bean创建
        ApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext.xml");
        Customer custom = (Customer)context.getBean("custom");
        System.out.println(custom.toString());
    }
2.3 IOC创建对象的方式

1.使用无参数的构造函数

 <bean name="custom" class="com.itxing.pojo.Customer">
        <property name="name" value="xing"></property>
    </bean>

2.自定义参数的构造

public class Customer {
    String name;
    //带参构造函数
    public Customer(String name){
        this.name = name;
    }
    public String getName() {
        return name;
    }

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

    @Override
    public String toString() {
        return "Customer{" +
                "name='" + name + '\'' +
                '}';
    }
}

自定义的带参数的构造函数有三种依赖注入的方式

<!--配置1:带参数的构造函数的spring配置,使用下标进行构造填充,从0开始-->  
<bean name="custom1" class="com.itxing.pojo.Customer">
        <constructor-arg index="0" value="dayangege"></constructor-arg>
    </bean>


<!--配置2(推荐使用):带参数的构造函数的spring配置,使用name属性进行属性注入--> 
<bean name="custom1" class="com.itxing.pojo.Customer">
        <constructor-arg name="name" value="dayangege"></constructor-arg>
    </bean>


<!--配置3(不推荐使用):带参数的构造函数的spring配置,类型匹配--> 
 <bean name="custom1" class="com.itxing.pojo.Customer">
        <constructor-arg type="java.lang.String" value="dage"></constructor-arg>
    </bean>

ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);
context.getBean(“userService”);

在getBean时,容器中管理的对象就已经初始化了。

3.spring的配置

3.1 配置别名
 <bean name="custom1" class="com.itxing.pojo.Customer">
        <constructor-arg name="name" value="dayangege"></constructor-arg>
    </bean>
    <alias name="custom1" alias="customer"></alias>
3.2 bean中属性介绍

bean的id:唯一标识符

bean的class属性:对象的权限定名

name:类似于别名,可以多个别名

<?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
 https://www.springframework.org/schema/beans/spring-beans.xsd">
     <!--将dao层的对象注入到spring容器中-->
    <bean id="mysqlDao" class="com.itxing.dao.UserMysqlDaoImpl"></bean>
    <bean id="oracleDao" class="com.itxing.dao.UserOracleDaoImpl"></bean>
    <bean id="userService" class="com.itxing.service.UserServiceImpl">
         <!--使用ref将对象与前面的对象进行关联-->
       <property name="userDao" ref="mysqlDao"></property>
    </bean>
</beans>
3.3 总配置中配置其余的bean
 <?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
    <!--将其余的有效配置放置在总的xml文件中-->
   <import resource="beans1.xml"></import>
</beans>

4. 依赖注入(DI)

官方网站介绍的依赖注入

4.1 依赖注入问题

动态的向某一个对象提供它所需要的其他对象

1.谁依赖于谁?:应用程序依赖于IOC容器

2.为什么需要依赖?:应用程序需要IOC容器来提供对象需要的外部资源

3.谁注入了谁?:IOC容器注入应用程序某个对象,应用程序依赖的对象

4.注入了什么?:注入某个对象所需要的外部资源(对象、数据)

4.2 构造器注入

(分为空参数的构造器和带参数的构造器),前面的介绍

构造器注入的缺点:参数过多时,xml中的property项过于繁多,有的人喜欢构造器注入

4.3 setting注入(推荐)

相关的环境准备

实体类准备
public class Address {
    String addr;

    public String getAddr() {
        return addr;
    }

    public void setAddr(String addr) {
        this.addr = addr;
    }

    @Override
    public String toString() {
        return "Address{" +
                "addr='" + addr + '\'' +
                '}';
    }
}
public class Student {
    private String name; //普通字段
    private Address addr;//引用类型字段
    private String[] books;//数组类型
    private List<String> hobby;//List集合类型
    private Map<String,String> card;//Map集合类型
    private Set<String> games;//Set集合类型
    private String wife;//空值注入
    private Properties info;//props注入

    public String getName() {
        return name;
    }

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

    public Address getAddr() {
        return addr;
    }

    public void setAddr(Address addr) {
        this.addr = addr;
    }

    public String[] getBooks() {
        return books;
    }

    public void setBooks(String[] books) {
        this.books = books;
    }

    public List<String> getHobby() {
        return hobby;
    }

    public void setHobby(List<String> hobby) {
        this.hobby = hobby;
    }

    public Map<String, String> getCard() {
        return card;
    }

    public void setCard(Map<String, String> card) {
        this.card = card;
    }

    public Set<String> getGames() {
        return games;
    }

    public void setGames(Set<String> games) {
        this.games = games;
    }

    public String getWife() {
        return wife;
    }

    public void setWife(String wife) {
        this.wife = wife;
    }

    public Properties getInfo() {
        return info;
    }

    public void setInfo(Properties info) {
        this.info = info;
    }

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", addr=" + addr.toString() +
                ", books=" + Arrays.toString(books) +
                ", hobby=" + hobby +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }
}
配置文件注入

8种不同的类型注入,一般的注入、引用类型、数组、List、Set、Map、Null、Properties

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="addr" class="com.itxing.pojo.Address">
        <property name="addr" value="西安"></property>
    </bean>

    <bean id="student" class="com.itxing.pojo.Student">
        <!--1.普通的属性注入-->
        <property name="name" value="星星"></property>
        <!--2.bean注入,当前bean的一个属性为另一个bean,即引用类型-->
        <property name="addr" ref="addr"/>
        <!--3.数组的注入方式-->
        <property name="books">
            <array>
                <value>java入门到放弃</value>
                <value>并发编程入门到放弃</value>
                <value>javaweb入门到放弃</value>
                <value>java网络编程入门到放弃</value>
            </array>
        </property>
        <!--4.List集合的注入方式-->
        <property name="hobby">
            <list>
                <value>听歌</value>
                <value>跳舞</value>
                <value>rap</value>
                <value>篮球</value>
            </list>
        </property>
        <!--5.Map集合的注入方式-->
        <property name="card">
            <map>
                <entry key="身份证号" value="123456"></entry>
                <entry key="银行卡号" value="789654"></entry>
            </map>
        </property>
        <!--6.Set集合的注入方式-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>cs</value>
                <value>cf</value>
            </set>
        </property>
        <!--7.空值注入-->
        <property name="wife">
            <null></null>
        </property>
        <!--8.配置注入-->
        <property name="info">
            <props>
                <prop key="学号">18062103101</prop>
                <prop key="姓名">itxing</prop>
                <prop key="年龄">25</prop>
            </props>
        </property>
    </bean>
</beans>
测试注入结果
  @Test
    public void mytest4(){
        ApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext.xml");
        //获取bean实例
        Student student = (Student) context.getBean("student");
       //输出bean
       System.out.println(student.toString());
    }

image-20200804170417979

4.4 其他方式

引入命名空间的原因在于使用bean的元素的属性进行元素的属性值设置,不再需要进行嵌套设置,主要在xml文件中引入p或者c命名空间。

p命名空间p命名空间注入讲解

c命名空间c命名空间注入讲解

引入命名空间

xml中的bean的命名空间中引入两个命名空间的url

<beansxmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-xmlns:p="http://www.springframework.org/schema/p"  xmlns:c="http://www.springframework.org/schema/c"
  xsi:schemaLocation="http://www.springframework.org/schema/beans    https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--p命名空间注入,可以直接注入属性的值:property  必须有无参数的构造函数-->
    <bean id="addr" class="com.itxing.pojo.Address" p:addr="西安"></bean>
    <bean id="stu01" class="com.itxing.pojo.Student" p:name="阿星" p:addr-ref="addr" ></bean>

    <!--c命名空间注入,c命名空间是使用构造器注入值的,不推荐使用,所提供的pojo必须有实参构造函数-->
    <bean id="addr1" class="com.itxing.pojo.Address" c:addr= "韩城"></bean>
    <bean id="stu02" class="com.itxing.pojo.Student" c:name="大雁" c:addr-ref="addr1"></bean>
</beans>

注意事项:引入p命名空间需要的是一个空参数的构造函数c命名空间是一个带参数的构造函数

4.5 Bean的作用域

官方文档中显示有6个作用域的范围

作用域 描述
singleton 默认,IOC容器中只有一个实例对象
prototype 原型模式,将单个bean定义的作用域限定为任意数量的实例对象
request 将单个bean定义的作用域限定为Http请求的生命周期,每一个http请求都有一个单个的bean实例对象
session 将单个bean定义的作用域限定为Http的整个session回话中,spring上下文中有效
application 将单个bean定义的作用域限定为servletContext
websocket 将单个bean定义的作用域限定为的生命周期WebSocket

自己设置对象的作用范围时,可以设置为singleton、prototype、request、session

spring中bean的作用域

作用范围:单例模式

<bean id="stu01" class="com.itxing.pojo.Student" p:name="阿星" p:addr-ref="addr" scope="singleton"></bean>
//将bean的scope属性设置成一个singleton进行测试
    @Test
    public void mytest5(){
        ApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student1 = (Student) context.getBean("stu02");
        Student student2 = (Student) context.getBean("stu02");
        System.out.println(student1==student2);
    }

结果为:True

作用范围:原型模式

    <bean id="stu01" class="com.itxing.pojo.Student" p:name="阿星" p:addr-ref="addr" scope="prototype"></bean>

其余的作用范围在web开发时会用到。

//将bean的scope设置为一个prototype进行测试
    @Test
    public void mytest6(){
        ApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student1 = (Student) context.getBean("stu02");
        Student student2 = (Student) context.getBean("stu02");
        System.out.println(student1==student2);
    }

结果为:false

5.Bean的自动装配

自动装配是spring满足bean依赖的一种方式,spring会在上下文中自动寻找,并给bean装配属性

1.在xml中显示配置

2.在java中显示配置

3.隐式的自动装配bean

一个People类中依赖两个pojo对象Cat、Dog以及一个String属性,之前的手动配置需要对引用类型的对象使用ref指向bean的id,使用value指定属性的属性值。

手动装配时:

 <bean id="dog" class="com.itxing.pojo.Dog"></bean>
<bean id="cat" class="com.itxing.pojo.Cat"></bean>

<bean id="people" class="com.itxing.pojo.People" >
      <property name="name" value="xingzai"></property>
      <property name="cat" ref="cat"></property>
      <property name="dog" ref="dog"></property>
</bean>
5.1 byName自动装配

使用autowire=”byName”进行自动配置,实体类中的属性命名与spring上下文中的beanid相同

  <bean id="dog" class="com.itxing.pojo.Dog"></bean>
    <bean id="cat" class="com.itxing.pojo.Cat"></bean>

    <!--autowire="byName" 自动装配,通过名字进行装配,寻找自己对象set方法后面对象的beanid,缺点:命名可能会与set中的命名不一致-->
    <bean id="people" class="com.itxing.pojo.People" autowire="byName">
      <property name="name" value="xingzai"></property>
    </bean>
5.2 byType自动装配

使用autowire=”byType”进行自动装配时,spring上下文有唯一的实体类型,如果有两个以上的bean指向同一个类,则匹配失败

<bean id="dog" class="com.itxing.pojo.Dog"></bean>
    <bean id="cat" class="com.itxing.pojo.Cat"></bean>

    <!--autowire="byType" 会在容器上下文中寻找与自己属性类型相同的beanid,如果有两个以上的类型bean会出错-->
    <bean id="people" class="com.itxing.pojo.People" autowire="byType">
      <property name="name" value="xingzai"></property>
    </bean>
5.3 使用注解自动装配

使用注解比XML更好吗?

答案:时情况而定,每种方法都有各自的优缺点,注释在声明中提供了很多上下文,使得配置更短,更简洁。xml擅长连接组件而不接触其源代码或重新编译他们。注解开发将配置分散,一定程度上难以管理。

需要在xml中添加context命名空间以及context约束

引入context约束

beans.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:context="http://www.springframework.org/schema/context"      xsi:schemaLocation="http://www.springframework.org/schema/beans      https://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context        https://www.springframework.org/schema/context/spring-context.xsd">
 <context:annotation-config/>
    <bean id="dog" class="com.itxing.pojo.Dog"/>
    <bean id="cat" class="com.itxing.pojo.Cat"/>
    <bean id="people" class="com.itxing.pojo.People"/>
</beans>

Autowried注解,在pojo属性中声明Autowried注解可以简化xml配置以及可以省略相关的set方法。

@Target({ElementType.CONSTRUCTOR, ElementType.METHOD, ElementType.PARAMETER, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Autowired {
    boolean required() default true;
}
public class People {
    //AutoWired注解的requored属性设置为false,说明该对象可以为null,否则不允许为空
   @Autowired(required = false)
    @Qualifier(value = "cat")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog")
    private Dog dog;

    public Cat getCat() {
        return cat;
    }

    public Dog getDog() {
        return dog;
    }
}

注解开发:(spring中的注解)

属性的注入时Autowired注解自动注入,使用Qualifier指定spring上下文中的beanid

@Autowired(required = false)
@Qualifier(value = “beanid”)

java中的注解也能够使用找到对应的bean,效率比spring的注解低。

public class People {
    @Resource(name = "cat")
    private Cat cat;
    @Resource
    private Dog dog;
}

Autowired和Resource的区别:

1.都是自动装配,都可以放在属性的字段上

2.Autowired通过byType的方式实现

3.Resource通过byname实现,如果没有匹配,则通过byType匹配,两个均不匹配时,异常。

6. 注解开发

spring4之后的注解开发必须导入aop的包,aop的约束以及aop的命名空间、context的命名空间以及context的约束。

6.1 bean

全部有spring管理的对象称之为一个bean

6.2 属性的注入
//组件注解
@Component
public class UserDemo {
    //spring的注解
    @Value("星仔")
    public String name;
}
6.3 衍生注解

@Component 组件注解,表名该类是spring中的一个bean,在web开发过程中,会按照mvc三层架构分层

​ dao[@Repository]

​ service[@Service]

​ controller[@Controller]

四种注解功能作用相同只是为了表示不同层之间的注解,都是将该类放入spring容器中进行装配

6.4 自动装配(之前使用过)

需要引入context约束、context命名空间

<!--开启扫描注解-->
<context:component-scan base-package="com.itxing"/>
    <context:annotation-config/>
6.5 作用域
@Component
//该注解表示该类是一个单例模式
@Scope("singleton")
public class UserDemo {
    @Value("星仔")
    public String name;
}
6.6 xml与注解比较

xml更加万能,适用的场景较多,维护简单方便

注解:维护相对复杂

一般使用:

xml用来管理bean;

注解只负责完成属性的注入;

注解需要生效必须开启注解

<context:component-scan basepackage=“”/>

<context:annotation-config/>

7. 不使用配置文件

采用java的一个配置类,配置类中有公共权限的方法向外暴露需要管理的对象。

@Configuration
//开启扫描注解
@ComponentScan("com.itxing.pojo")
@Import(SpringConfig2.class)
public class ItXingConfig {
    @Bean
    public Customer getCustomer(){
        return new Customer();
    }
}

另外一个配置类,使用@Import注解能够将两个配置类合并为一个配置类

@Configuration
public class SpringConfig2 {
}

测试类,创建上下文对象的时候不使用ClassPathXmlApplicationContext对象读取配置文件来获取。

@Test
public void test2(){
        //不适用配置文件去实现
        ApplicationContext context = new AnnotationConfigApplicationContext(ItXingConfig.class);
        Customer custumer = (Customer)context.getBean("getCustomer");
        System.out.println(custumer.getName());
 }

8. 代理模式

AOP底层使用的是代理模式,代理模式分为动态代理和静态代理

代理模式中的角色:代理、被代理对象、具体的事件

被代理对象专心的完成具体的事件,具体事件之外的事件由代理对象去完成

8.1 静态代理

代理模式的优缺点

好处:可以使得真实角色的操作更加纯粹,不需要关注公共的业务

公共业务交给代理对象完成,实现类业务分工

方便集中管理

缺点:一个真实角色有一个代理对象,代码量会加倍

代理模式示意图

1.公共接口

public interface Rent {
    public  void rent();
}

2.被代理对象

public class Host implements Rent{

    @Override
    public void rent() {
        System.out.println("出售房子");
    }
}

3.代理对象

public class ProxyHome implements Rent{

    private Host host;

    public ProxyHome(){}
    public ProxyHome(Host host){this.host = host;}

    @Override
    public void rent() {
        seeHouse();
        host.rent();
        signTong();
        getfare();
    }
    //代理对象能够处理的时间
    //代理对象 看房子
    public void seeHouse(){
        System.out.println("中介带客户看房");
    }
    //代理对象 收中介费
    public void getfare(){
        System.out.println("中介收费");
    }
    //代理对象 签合同
    public void signTong(){
        System.out.println("中介签合同");
    }
}

4.访问者

public class ClientDemo {
    public static void main(String[] args) {
        Host host = new Host();
        ProxyHome proxy = new ProxyHome(host);
        proxy.rent();
    }
}
8.2 动态代理

动态代理与静态代理的角色划分一致

动态代理的代理类是动态生成的,不是直接写好的

动态代理分为两大类:基于接口的动态代理、基于类的动态代理、字节码动态代理

​ 接口的JDK动态代理

​ 类:cglib动态代理

​ 字节码实现:javasist

公共代理类工具类

调用setObj设置被代理对象

使用getProxy获取一个代理对象

调用被代理对象接口中的方法

测试实例:给增、删、改、查每个方法添加日志信息。

1.接口

public interface UserService {
    public void add();
    public void delete();
    public void update();
    public void select();
}

2.具体实现类

public class UserserviceImpl implements UserService {
    @Override
    public void add() {
        System.out.println("新增用户");
    }

    @Override
    public void delete() {
        System.out.println("删除用户");
    }

    @Override
    public void update() {
        System.out.println("修改用户");
    }

    @Override
    public void select() {
        System.out.println("查询用户");
    }
}

3.公共的代理对象工具类

public class ProxyInvocationUtil implements InvocationHandler {
    //被代理对象接口
    private Object obj;

    public void setObj(Object obj) {
        this.obj = obj;
    }

    //使用Proxy获取一个代理独享
    public Object getProxy(){
        return Proxy.newProxyInstance(this.getClass().getClassLoader()
        ,obj.getClass().getInterfaces(),this);
    }

    //代理对象执行的方法
    @Override
    public Object invoke(Object o, Method method, Object[] objects) throws Throwable {
        log(method.getName());
        method.invoke(obj,objects);
        return null;
    }
    //添加日志
    public void log(String msg){
        System.out.println("执行的是"+msg+"方法。");
    }
}

4.客户端测试

public class Client {
    public static void main(String[] args) {
        //具体的被代理实例
        UserserviceImpl userservice = new UserserviceImpl();
        //创建代理对象工具类
        ProxyInvocationUtil proxyUtil = new ProxyInvocationUtil();
        //设置被代理对象
        proxyUtil.setObj(userservice);
        //获取代理对象
        UserService proxy = (UserService)proxyUtil.getProxy();
        proxy.add();
    }
}

动态代理的好处:

1.可以使真实角色的操作更加纯粹,专心做自己的事情

2.公共的事件交给代理对象,实现了业务分工

3.方便集中管理

4.一个动态代理类代理的是一个接口,一般就是应对的一类业务

5.一个动态代理类可以代理多个类,只要是实现了相同的接口

9. AOP

Aop中的相关名词,对于跨越应用程序多个模块的功能或方法,即与业务逻辑无关的部分,日志、安全、缓存、事务。

切面(Aspect):横切关注点,被模块化的特殊对象,一个类

通知(Advice):切面必须要完成的工作,一个类的方法

目标(Target):被通知的对象

代理(Proxy):向目标对象应用通知之后创建的对象

切入点(PointCut):切面通知执行”地点”的定义

连接点(JointPoint):与切入点匹配的执行点

引入依赖

 <dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
 </dependency>
方式一 使用springAPI

主要是springAPI的接口的实现

//MethodBeforeAdvice是spring的一个接口
public class Log implements MethodBeforeAdvice {
    /*
    * Method method   目标的方法
    * Object[] objects  参数
    * Object o  目标对象
    * */
    @Override
    public void before(Method method, Object[] objects, Object o) throws Throwable {
        System.out.println(o.getClass().getName()+"的"+method.getName()+"即将被执行");
    }
}

配置文件的主要关注点在于切入点的配置

<bean id="userService" class="com.itxing.service.UserServiceImpl"/>
<bean id="log" class="com.itxing.log.Log"/>
<!--配置aop-->
<aop:config>
    <!--切入点-->
    <aop:pointcut id="pointcut" expression="execution(* com.itxing.service.UserServiceImpl.*(..))"></aop:pointcut>
    <!--通知,该对应的bean实现了spring的核心api-->
    <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
</aop:config>
方式二 自定义类实现AOP

一个类中有业务前的逻辑方法与业务后执行的逻辑方法,从而将该方法横切进去

public class DiyLogDemo {
    public void before(){
        System.out.println("=========前置方法========");
    }
    public void after(){
        System.out.println("=========后置方法========");
    }
}

自定义类文件首先关注的是切面

  <!--业务方法前后执行的方法实现类-->
<bean id="diy" class="com.itxing.log.DiyLogDemo"/>
    <!--配置aop-->
    <aop:config>
        <!--在于切面的配置-->
        <aop:aspect ref="diy">
            <aop:pointcut id="point" expression="execution(* com.itxing.service.UserServiceImpl.*(..))"/>
            <aop:before method="before" pointcut-ref="point"/>
            <aop:after method="after" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>
方式三 使用注解实现AOP
//Aspect配置切面的注解
//Before前置通知
//After后置通知
//Around环绕通知
@Aspect
public class AnnotationPointCut {
    @Before("execution(* com.itxing.service.UserServiceImpl.*(..))")
    public void before(){
        System.out.println("=======执行前执行======");
    }
    @After("execution(* com.itxing.service.UserServiceImpl.*(..))")
    public void after(){
        System.out.println("=======执行后执行======");
    }
    @Around("execution(* com.itxing.service.UserServiceImpl.*(..))")
    public void around(ProceedingJoinPoint pjp){
        System.out.println("=======事前环绕======");
        try {
            pjp.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        System.out.println("=======事后环绕======");
    }
}

使用注解aop时在xml中只需要开启aop注解代理支持,默认使用的是jdk动态代理

 <!--Spring中加入切面以及切入点的bean-->
<bean id="annotationPointCut" class="com.itxing.log.AnnotationPointCut"/>
<!--开启aop的注解支持  JDK(默认)   CGLIB-->
<aop:aspectj-autoproxy/>

10 整合Mybatis

之前学习的mybatis笔记Mybatis笔记

步骤:

1.导入相关的依赖

​ junit

​ mybatis

​ mysql数据库

​ spring相关

​ aop织入

​ mybatis-spring

2.编写配置

3.测试

<dependencies>
    <!--junit-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
        </dependency>
        <!--mysql相关-->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.46</version>
        </dependency>
        <!--mybatis相关-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.2</version>
        </dependency>
        <!--spring相关-->
        <dependency>
  <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <dependency>
<groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.1.9.RELEASE</version>
        </dependency>
        <!--aop支持-->
        <dependency>
            <groupId>org.aspectj</groupId>
 <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!--spring与mybatis整合包-->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.0.2</version>
        </dependency>
    </dependencies>
方式一: SqlSessionTemplate

spring的配置文件中配置数据源信息(之前是在mybatis中配置)、配置SqlSessionFactory、配置SqlSessionTemplate(为了获取sqlsession)。

自己获取sqlsession的进行测试

  @Test
    public void test() throws IOException {
        String resource = "mybatis-config.xml";
        //输入流
        InputStream in = Resources.getResourceAsStream(resource);
        //获取SqlSessionFactory
        SqlSessionFactory build = new SqlSessionFactoryBuilder().build(in);
        //获取sqlSession
        SqlSession sqlSession = build.openSession();

        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> users = mapper.selectUser();
        for (User user:users){
            System.out.println(user);
        }
    }

将该对象的创建都交给Spring来管理

 <!--1.导入数据源,使用spring的数据源替换mybatis的配置 s3p0 dputils druid-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>
     </bean>
    <!--2.配置sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"></property>
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/itxing/mapper/*.xml"/>
    </bean>
    <!--3.配置SqlSessionTemplate只能使用构造器进行注入,-->
    <bean id="SqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

sqlSessionTemplate只能使用构造器注入

spring中sqlSessionTemplate源码

自己对接口进行实现并将其注入spring容器中,提供在创建时使用

实现类

public class UserMapperImpl implements UserMapper {

    public SqlSessionTemplate sqlSession;

    public void setSqlSession(SqlSessionTemplate sqlSession) {
        this.sqlSession = sqlSession;
    }

    @Override
    public List<User> selectUser() {
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        return mapper.selectUser();
    }
}

注入配置

 <bean id="userMapper" class="com.itxing.mapper.UserMapperImpl">
        <property name="SqlSession" ref="SqlSession"/>
    </bean>

spring管理后的测试类

 @Test
public void test2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapperImpl  userMapper = (UserMapperImpl) context.getBean("userMapper");
        List<User> users = userMapper.selectUser();
        users.forEach(System.out::println);
}
方式二 实现类继承SqlSessionDaoSupport

配置的不同

 <!--1.导入数据源,使用spring的数据源替换mybatis的配置 s3p0 dputils druid-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>
     </bean>
    <!--2.配置sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"></property>
        <!--添加mybatis配置-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/itxing/mapper/*.xml"/>
    </bean>

继承SqlSessionDaoSupport类,该类的方法指定返回sqlsession对象处理数据

public class UserMapperImpl2 extends SqlSessionDaoSupport implements UserMapper {
    @Override
    public List<User> selectUser() {
        return getSqlSession().getMapper(UserMapper.class).selectUser();
    }
}

注册接口的实现类

  <bean id="userMapper" class="com.itxing.mapper.UserMapperImpl2">
      <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

11.声明式事务

事务:一些任务同时成功或者同时失败,确保数据的完整性和一致性

事务的ACID:

原子性:操作的指令要么全部执行成功,要么全部不执行

一致性:事务的执行使数据从一个状态转换成另一个状态,但是对于整个数据的完整性保持稳定

隔离性:当多个用户并发访问数据库时,一个事务不能被其他事务所干扰,多个并发的事务之间相互隔离

持久性:当事务正确完成后,数据的修改是永久的

事务并发导致的问题(没有隔离级别)

第一类丢失更新:撤销一个事务时,把其他事务已提交的更新数据覆盖

脏读:指的是一个事务处理过程里读取了另一个未提交事务中的数据

幻读:一个事务两次查询,第二次结果包含第一次没有或已经删除的数据,造成两次读取不一致,指的是另一个事务在两次查询中修改了数据。(数据的整体)

不可重复读:一个事务两次读取同一行数据,结果不同即另一个事务在两次读取数据中更新了数据

第二类丢失更新:是不可重复读的一种特例,两个事务读取同一行数据后都进行写操作,并提交,第一个事务的改变会丢失

事务的隔离级别:从低到高

Read Uncommitted:读未提交,允许读取另一个事务的数据 (都不能保证)

Read committed:读已提交,读取已经提交的事务的数据(解决脏读问题)

Repeatable read:可重复读,读取时不允许修改数据(解决脏读、不可重复读)

Serializable:串行化,效率低下(解决幻读、脏读、不可重复读)

spring声明式事务:AOP的支持

编程式事务:需要在代码中进行事务的管理

spring中事务的七种传播特性

spring中事务的传播特性

特性 描述
REQUIRED 需要,如果没有事务就新建一个
SUPPORTS 支持,有事务就按事务提交,没有事务就非事务执行
MANDATORY 支持当前事务,当前没有事务就抛出异常
REQUIRES_NEW 新建事务,把当前的事务挂起
NEVER 非事务进行,有事务抛出异常
NESTED 支持当前事务,当前有事务嵌套事务,没有事务新建一个事务
NOT_SUPPORTED 非事务执行,当前事务挂起

spring与mybatis项目整合使用的xml管理的配置文件,其中涉及到context约束和命名空间、aop约束和命名空间、tx事务的约束的命名空间、数据源datasource、工厂sqlSessionFactory、sqlSession、事务、aop的相关配置

<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"
       xmlns:tx="http://www.springframework.org/schema/tx"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        https://www.springframework.org/schema/context/spring-context.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd
         http://www.springframework.org/schema/tx
        https://www.springframework.org/schema/tx/spring-tx.xsd
         ">

    <!--导入数据源,使用spring的数据源替换mybatis的配置 s3p0 dputils druid-->
    <bean id="datasource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?useSSL=true&amp;useUnicode=true&amp;characterEncoding=UTF-8"/>
        <property name="username" value="root"/>
        <property name="password" value="123"/>
    </bean>
    <!--配置sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="datasource"></property>
        <!--添加mybatis配置-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/itxing/mapper/*.xml"/>
    </bean>
    <!--配置sqlSession,可选配置,也可以使用继承类SqlSessionDaoSupport实现-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
        <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

    <!--声明式事务 使用数据库事务-->
    <bean id = "transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="datasource"/>
    </bean>

    <!--结合aop,执行数据库操纵时自动添加事务-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--指定的方法配置事务-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="query" propagation="REQUIRED"/>
            <tx:method name="*" propagation="REQUIRED"/>
        </tx:attributes>
    </tx:advice>
    <!--将事务织入-->
    <aop:config>
        <aop:pointcut id="pointcut" expression="execution(* com.itxing.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

mybatis中的相关配置,简洁了很多,大多数交给spring去管理和配置

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<!--configuration核心配置文件-->
<configuration>
    <!--别名管理-->
    <typeAliases>
        <package name="com.itxing.pojo"/>
    </typeAliases>

</configuration>

文章作者: it星
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 it星 !
 上一篇
springcloud实验 springcloud实验
it星
SpringCloud微服务入门 注:个人的学习是通过视频的讲解、阅读相关数据、进行实践、记录笔记进行的,希望与君共勉。个人的实验环境是Windows7,64位,内存16G,建议16G否则效果很差。 下面是我启动三个注册中心,一个路由网关一
下一篇 
Maven工具 Maven工具
Maven工具 前提是已经在自己本地有一个maven仓库,个人使用的是3.3.9版本的maven。 准备构建一个maven项目来进行spring的复习,发现之前学习的maven搭建项目的步骤突然忘记了,最近一直在做一些算法的题目和论文的
2020-05-14
  目录