Spring学习

目录

1、什么是Spring?

1.1、优点

1.2、7大组成

1.3、拓展

2、IOC理论推导

3、helloSpring

4、IOC创建对象方式

5、Spring配置

5.1、别名

5.2、bean的配置

5.3、import

6、DI依赖注入

6.1、构造器注入

6.2、Set方式注入(重点)

6.3其他方式注入

6.4、bean的作用域

7、bean的自动装配

7.1、byName自动装配

 7.2、byType自动装配

7.3、使用注解实现自动装配

8、Spring注解开发

 8.1、bean的注解

8.2、属性注入

8.3、衍生注解

8.4、作用域

8.5、小结

9、使用Java的方式配置spring

10、代理模式

10.1、静态代理

10.2、深入理解静态代理模式

10.3、动态代理

11、AOP

11.1、什么是AOP

11.2、使用Spring实现AOP

11.2.1、方式一:使用Spring的API接口

11.2.2、方式二:自定义实现AOP

11.2.3、方式三:使用注解实现AOP

 12、整合MyBatis

12.1、整合方式一:

12.2、整合方式二

13、声明式事物

13.1、回顾事务:

13.2、spring中的事务


1、什么是Spring?

Spring : 春天 —>给软件行业带来了春天

2002年,Rod Jahnson首次推出了Spring框架雏形interface21框架。

2004年3月24日,Spring框架以interface21框架为基础,经过重新设计,发布了1.0正式版。

很难想象Rod Johnson的学历 , 他是悉尼大学的博士,然而他的专业不是计算机,而是音乐学。

Spring理念 : 使现有技术更加实用 . 本身就是一个大杂烩 , 整合现有的框架技术

SSH:Struct2+Spring+Hibernate(全自动持久化层)

SSM:SpringMVC+Sprin+Mybatis(半自动持久化层)

1.1、优点

1、Spring是一个开源免费的框架 , 容器 .

2、Spring是一个轻量级的框架 , 非侵入式的 .

3、控制反转 IoC , 面向切面 Aop

4、对事物的处理 , 对框架整合的支持

一句话概括:

Spring是一个轻量级的控制反转(IoC)和面向切面(AOP)的容器(框架)。

1.2、7大组成

7模块内容: 

  • SpringCore(核心容器):核心容器提供Spring的基本功能。核心容器的主要组件是BeanFactory,它是工厂模式的实现。BeanFactory使用控制反转(IOC)模式将应用程序的配置和依赖性规范与实际的应用程序代码分开。
  • SpringContext上下文:Spring上下文是一个配置文件,向Spring框架提供上下文信息。Spring上下文包括企业服务,如JNDI、EJB、电子邮箱、国际化、校验和调度功能。
  • SpringAOP(面向切面编程):通过配置管理特性,SpringAOP模块直接将面向切面编程集成到了Spring框架中,所以,可以很容易地使Spring框架管理任何支持AOP的对象。SpringAOP模块为基于Spring的应用程序中的对象提供了事务管理服务。通过使用SpringAOP,不用依赖组件就可以将声明性的事务管理集成到应用程序中。
  • SpringDAO:JDBC DAO抽象层提供了有意义的异常管理层次结构,可用该结构来管理异常处理和不同的数据库供应商抛出的错误信息。异常层次结构简化了错误处理,并且极大地降低了需要编写的异常代码数量。SpringDAO的面向JDBC的 异常遵从通用的DAO异常层次结构。
  • SpringORM:Spring框架插入了若干个ORM框架,从而提供了ORM的对象关系工具,其中包括JDO、Hibernate和iBatis SQL Map。所有这些都遵从Spring的通用事务和DAO异常层次结构
  • SpringWeb模块:Web上下文模块建立在应用程序上下文模块之上,为基于web的应用程序提供上下文。所以,Spring框架支持与Jakarta Struts的集成。web模块还简化了处理多部分请求以及将请求参数帮定到域对象的工作。
  • SpringMVC框架:MVC框架是 一个全功能的构建web应用程序的MVC实现。通过策略接口,MVC框架变成高度可配置的,MVC容纳了大量的视图技术,其中包括JSP、Velocity、Tiles、iText和POI.

1.3、拓展

Spring Boot与Spring Cloud

  • Spring Boot是Spring的一套快速配置脚手架,可以基于Spring Boot快速开发单个微服务;
  • Spring Cloud是基于Spring Boot实现的
  • Spring Boot专注于快速、方便集成的单个微服务个体,Spring Cloud关注全局的服务治理框架
  • Spring Boot 使用了约束优于配置的理念,很多集成方案已经帮你选择好了,能不配置就不用配置,SpringCloud 很大一部分是基于Spring Boot 来实现的,Spring Boot可以离开Spring Cloud独立使用开发项目,但是Spring Cloud就需要依赖于Spring Boot
  • Spring Boot 在SpringCloud中起到承上启下的作用,如果想学习SpringCloud必须学习SpringBoot

2、IOC理论推导

IOC基础

1、UserDao接口

2、UserDaoImp实现类

3、UserService业务接口

4、UserService业务实现类

在之前的业务中,我们根据用户需求修改源代码,修i该成本大。

使用一个set接口:

public class UserServiceImp implements UserService{
    private UserDao userDao ;
    //使用set动态实现需求
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }

    @Override
    public void getUser() {
        userDao.getUser();
    }
}
  • 之前程序是主动创建对象,控制权在开发者
  • 使用set注入后,程序变得被动接收对象

这种思想使得系统的耦合性大大降低,我们可以更专注在业务的实现上,这就是IOC原型

        Ioc—Inversion of Control,即“控制反转”,不是什么技术,而是一种设计思想。在Java开发中,Ioc意味着将你设计好的对象交给容器控制,而不是传统的在你的对象内部直接控制。

         IOC是Spring框架的核心内容,使用多种方式完美的实现了IOC,可以用XML配置,也可以使用注解,新版本的Spring也可以零配置实现IOC。

        Spring容器在初始化时先读取配置文件,根据配置文件或源数据创建与组织对象存入容器中,程序使用时再从IOC容器中取出需要的对象。

         采用XML方式配置Bean的时候,Bean的定义信息是和实现分离的,而采用注解的方式可以把两者合为一体,Bean的定义信息直接以注解的形式定义在实体类中,从而达到零配置的目的。

控制反转是一种通过描述(XML或注解)并通过第三方去生产或获取特定对象的方式。在Spring中实现控制反转的是IOC容器,其实现方法是依赖注入

3、helloSpring

1、新建一个 实体类Hello

public class Hello {
    private String name;
    public void show(){
        System.out.println("hello:"+name);
    }

    public String getName() {
        return name;
    }

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

2、新建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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd">
<!--使用Spring创建对象,在spring中这些对象称为bean
    类型 变量名 = new 类型();
    Hello hello = new Hello();
    id = 变量名;
    class = new的对象
    property给属性赋值
-->
    <bean id="hello" class="com.zhang.pojo.Hello">
        <property name="name" value="哈喽Spring"/>
    </bean>
</beans>

3、测试

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        //getBean("hello")这里的hello是id
        Hello hello = (Hello) context.getBean("hello");
        hello.show();
    }
}
  • Hello对象是由Spring创建的
  • hello对象的属性是由spring容器设置的
  • 这个过程叫做控制反转
  • 控制:谁来控制对象的创建,传统应用程序的对象是由程序本身控制创建的,使用Spring后,对象由Spring创建
  • 反转:程序本身不创建对象,而变成被动的接收对像
  • 依赖注入:就是利用set方法进行注入

IOC的思想是主动的编程变成被动的接收

4、IOC创建对象方式

  1. 默认是通过无参构造函数创建对象。
  2. 可以使用有参构造函数创建

使用有参构造函数创建的三种方式:

1、下标赋值:

 <constructor-arg index="0" value="下标赋值"/>

2、通过类型赋值(不建议使用,参数只有一个时可以用)

如果多个构造参数都是同一个类型呢?

答:会根据标签的顺序对同一类型的参数进行从前到后赋值。

<constructor-arg type="java.lang.String" value="类型赋值"/>

3.、直接通过参数名字赋值(最常用)

 <constructor-arg name="name" value="直接用名字"/>

在配置文件加载的时候,容器中管理的对象就已经初始化了!

 IOC默认创建对象都是单例模式的,一个类只能拿到一个实例

5、Spring配置

5.1、别名

<!--如果添加了别名,我们也可以使用别名获取到这个对象-->
<alias name="user" alias="newUser"/>

5.2、bean的配置

    id:bean的唯一标识符,也就是对象名字
    class:bean对象所对应的全限定名:包名+类名
    name:也就是别名,可以取多个别名,多个别名之间使用空格或者“,”或者“;”隔开
<bean id="user2" class="com.zhang.pojo.UserT" name="u1 u2,u3;u4">
    <property name="name" value="user2"/>
</bean>

5.3、import

团队开发使用,可以将多个配置文件导入合并到一个

假如团队有bean2.xml,bean3.xml,beans.xml和applicationContext.xml,我们可以在applicationContext.xml里使用import将其他的三个bean合并在applicationContext.xml里

   <import resource="beans.xml"/>
   <import resource="bean2.xml"/>
   <import resource="bean3.xml"/>

使用时,直接用总成的就可以。

注:如果这些bean中有重复的对象名或对象名的别名,spring会自动合并不影响使用。

6、DI依赖注入

6.1、构造器注入

6.2、Set方式注入(重点)

  • 依赖:bean的创建依赖于容器
  • 注入:bean中的所有属性,由容器来注入

测试环境搭建:

1、创建两个实体类address、student

public class Student {
    private String name;
    private Address Address;
    private String[] books;
    private List<String> hobbys;
    private Map<String, String> card;
    private Set<String> games;
    private String wife;
    private Properties info;
}
public class Address {
    private String address;
}

2、创建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 id="address" class="com.zhang.pojo.Address">
        <property name="address" value="北京"/>
    </bean>

    <bean id="student" class="com.zhang.pojo.Student">
        <!--普通值注入,value-->
        <property name="name" value="张三"/>
        <!--bean注入,ref-->
        <property name="address" ref="address"/>
        <!--数组-->
        <property name="books">
            <array>
                <value>红楼梦</value>
                <value>西游记</value>
                <value>三国演义</value>
                <value>水浒传</value>
            </array>
        </property>
        <!--List-->
        <property name="hobbys">
            <list>
                <value>女</value>
                <value>电影</value>
                <value>游戏</value>
                <value>旅游</value>
                <value>听歌</value>
            </list>
        </property>
        <!--map-->
        <property name="card">
            <map>
                <entry key="ID" value="411503200301230716"/>
                <entry key="phone" value="123456789"/>
                <entry key="银行" value="41150685877230716"/>
            </map>
        </property>
        <!--set-->
        <property name="games">
            <set>
                <value>LOL</value>
                <value>CSGO</value>
                <value>王者荣耀</value>
            </set>
        </property>
        <!--null-->
        <property name="wife" value=""/>
        <!--properties-->
        <property name="info">
            <props>
                <prop key="学号">20220213</prop>
                <prop key="性别">男</prop>
                <prop key="班级">软工二班</prop>
            </props>
        </property>
    </bean>



</beans>

3、测试

 @Test
    public void t(){
        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student);
        /*
        * Student{
        *      name='张三',
        *      Address=Address{address='北京'},
        *      books=[红楼梦, 西游记, 三国演义, 水浒传],
        *      hobby's=[女, 电影, 游戏, 旅游, 听歌],
        *      card={
        *           ID=411503200301230716,
        *           phone=123456789,
        *           银行=41150685877230716},
        *      games=[LOL, CSGO, 王者荣耀],
        *      wife='',
        *      info={学号=20220213, 性别=男, 班级=软工二班}}
        * */
    }

6.3其他方式注入

P命名空间和C命名空间

想要使用先导入xml约束:

    xmlns:c="http://www.springframework.org/schema/c"
    xmlns:p="http://www.springframework.org/schema/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: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命名空间注入,可以直接写入属性值:properties-->
    <bean id="user" class="com.zhang.pojo.User" p:name="李四"/>
    <!--c命名空间注入,通过构造器注入:construct-args-->
    <bean id="user1" class="com.zhang.pojo.User" p:name="王五"/>

</beans>

测试:

 @Test
    public void t1(){
        ApplicationContext context = new ClassPathXmlApplicationContext("userBean.xml");
        User user = context.getBean("user1", User.class);
        System.out.println(user);
    }

6.4、bean的作用域

 1、单列模式(spring默认模式)

    <bean id="user" class="com.zhang.pojo.User" p:name="李四" scope="singleton"/>

2、原型模式

每次从容器里get的时候都会产生一个新对象。

    <bean id="user" class="com.zhang.pojo.User" p:name="李四" scope="prototype"/>

3、其他的request、session、application这些只能在web的开发中使用。

7、bean的自动装配

spring会自动在上下文中寻找,并自动给bean装配属性。

Spring中属性有三种装配方式

  1. 在xml中显示配置;
  2. 在Java中显示配置;
  3. 隐式的自动装配

测试:一个人俩宠物,猫和狗

7.1、byName自动装配

使用自动装配之前:

    <bean id="cat" class="com.zhang.pojo.Cat"/>
    <bean id="dog" class="com.zhang.pojo.Dog"/>
    <bean id="people" class="com.zhang.pojo.People">
        <property name="name" value="张三"/>
        <property name="dog" ref="dog"/>
        <property name="cat" ref="cat"/>
    <bean/>
 

使用自动装配后:

<bean id="cat" class="com.zhang.pojo.Cat"/>
    <bean id="dog" class="com.zhang.pojo.Dog"/>
    <!--
    byName:会自动在spring容器上下文中寻找和自己对象set方法后参数对应的beanID
    -->
    <bean id="people" class="com.zhang.pojo.People" autowire="byName">
        <property name="name" value="张三"/>
    </bean>

byName:会自动在spring容器上下文中寻找和自己对象set方法后参数对应的beanID.

如果把其中一个引用属性的beanid改掉就会抛空指针异常:

所以需要保证beanid的唯一性

 <bean id="dog1" class="com.zhang.pojo.Dog"/>

 7.2、byType自动装配

byType:会自动在spring容器上下文中寻找和自己对象属性类型相同的bean

使用byType自动装配不需要beanid,但要保证属性类型的唯一性。

    <bean class="com.zhang.pojo.Cat"/>
    <bean class="com.zhang.pojo.Dog"/>
    <!--
    byName:会自动在spring容器上下文中寻找和自己对象set方法后参数对应的beanID
    byType:会自动在spring容器上下文中寻找和自己对象属性类型相同的bean
    -->
    <bean id="people" class="com.zhang.pojo.People" autowire="byType">
        <property name="name" value="张三"/>
    </bean>

7.3、使用注解实现自动装配

基于注解的配置提供了 XML 设置的替代方法,该配置依赖字节码元数据来连接组件,而不是尖括号声明。通过使用相关类,方法或字段声明上的 注解,开发人员无需使用 XML 来描述 bean 的连接,而是将配置移入组件类本身。

使用注解开发的步骤:

  1. 导入约束:context约束
  2. 配置注解的支持:  <context:annotation-config/>(一定要记得这个)
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context.xsd">

    <context:annotation-config/>

</beans>
注解:@Autowired

在实体类中的属性或者set方法上使用(前提是这个属性在IOC容器中存在,且符合byName的规范),在属性上使用时set方法可以不用写,但get要保留。

@Nullable   标记了这个注解表示 这个字段可以为null

public @interface Autowired {
    boolean required() default true;
}

  //如果显性的定义了Autowired的required为false,说明这个对象可以为空,否者不能为空。 
    @Autowired(required = false)
    private Cat cat;
    @Autowired
    private Dog dog;
    private String name;

        如果@Autowired的装配环境比较复杂,即有多个相同类型的bean,自动装配不能通过一个注解(Autowired)完成时,我们可以添加限定符,使用@Qualifier(value = "XXX")去指定哪一个bean。

 @Autowired(required = false)
    @Qualifier(value = "cat1")
    private Cat cat;
    @Autowired
    @Qualifier(value = "dog2")
    private Dog dog;

 @Resource

   @Resource(name = "cat1")
    private Cat cat;
    @Resource
    private Dog dog;

小结:@Autowired 和@Resource的区别

  • 相同点:都用来自动装配,都放在属性字段上;
  • 区别:@Autowired是先通过byType方式实现,再通过byName 必须要求这个对象存在;
  • @Resource默认通过byName方式实现,如果找不到唯一的bean就使用byType方式找,如果还没有唯一的bean就报错;
  • 执行顺序不同:@Autowired是先通过byType方式,再通过byName;@Resource是先byName再byType;

8、Spring注解开发

使用Spring注解开发前需要保证aop包已经导入,还需要导入context约束,增加注解的支持

 8.1、bean的注解

 <!--指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.zhang.pojo"/>
    <!--开启注解支持-->
    <context:annotation-config/>
//Component组件,相当于<bean id="user" class="com.zhang.pojo.User"/>
@Component
public class User {
    public String name = "张三";
}

8.2、属性注入

@Component
public class User {
    //@Value相当于<property name="" value="zhangshan"/>
    @Value("zhangshan")
    private String name ;
    public String getName() {
        return name;
    }
}

8.3、衍生注解

Component有几个衍生注解,我们在web开发中会按照MVC三层架构分层。

  • dao(@Repository)
  • service(@Service)
  • controller(@controller)

这四个注解一样,都是将某个类注册到Spring中,装配bean

8.4、作用域

@Component
@Scope("prototype")
public class User {
    //@Value相当于<property name="" value="zhangshan"/>
    @Value("zhangshan")
    private String name ;
    public String getName() {
        return name;
    }
}

8.5、小结

xml与注解

  • xml更万能适合于任何场合,维护方便;
  • 注解不是自己的类不能使用,维护比较复杂;

xml与注解最佳实践

  • xml用来管理bean;
  • 注解只负责属性的注入;
  • 使用过程中,必须要让注解生效,就需要开启注解的支持;
  <!--指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.zhang"/>
    <!--开启注解支持-->
    <context:annotation-config/>

9、使用Java的方式配置spring

不使用spring的xml配置,全权交给Java。

JavaConfig原本是spring的一个子项目,spring4以后变成核心功能。 

实体类:


//@Component仍然代表这个类被spring托管了,注册到Spring容器中了
@Component
public class User {
    @Value("张三")
    private String name;

    public String getName() {
        return name;
    }

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

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

配置类:

//这个类也会被spring托管,注册到容器中,因为它本来就是@Component
//Configuration代表这是一个配置类,它对应的就相当于原来的ApplicationContext.xml(beans.xml)等价
@ComponentScan("com.zhang.pojo")
@Configuration
@Import(Config2.class)
public class Config {
    //注册一个bean,就相当于之前的bean标签
    //这个方法的名字就相当于bean标签中的id属性
    //这个方法的返回值就相当于bean标签的class属性
    @Bean
    public User getUser(){
        return new User();
    }
}

 @Bean 可以用于通过方法获取数据库连接池Connection这种对象

注意:1. 如果开启包扫描,加载配置类以后就可以通过反射拿到配置类中的对象了

           2. @Bean只写在方法上,返回的是一个对象,但一般不获取已经在容器中的对象

测试:

public class MyTest {
    @Test
    public void t1(){
        //如果完全使用了配置类的方法,我们就只能通过AnnotationConfig上下文来获取容器,通过配置类的class对象加载
       ApplicationContext context = new AnnotationConfigApplicationContext(Config.class);
        User user = (User) context.getBean("getUser");
        System.out.println(user.getName());
    }
}

10、代理模式

代理模式是SpringAOP的底层

代理模式的分类:

  • 静态代理
  • 动态代理

10.1、静态代理

角色:

  • 抽象角色:一般使用接口或抽象类来解决
  • 真实角色:被代理的对象
  • 代理角色:代理真实角色后,会做一些附属性操作
  • 客户:访问代理对象的人

测试:

        1、接口

public interface Rent {

   public void rent();
  /* public void rent1();
   public void rent2();*/

}

        2、真实角色

public class Landlord implements Rent {
    //真实对象,房东即被代理者
    @Override
    public void rent() {
        System.out.println("我是房东,我想出租房子!");
    }

    /*@Override
    public void rent1() {
        System.out.println("我是房东2,我也想出租房子");
    }

    @Override
    public void rent2() {
        System.out.println("我是房东3,我也想出租房子");
    }*/

}

        3、代理角色



public class Poxy implements Rent{
    private Landlord landlord;

    public Poxy(Landlord landlord) {
        this.landlord = landlord;
    }
    public Poxy(){

    }
    @Override
    public void rent() {
        this.visit();
        landlord.rent();
        this.contract();
        this.IntermediaryFees();
    }

    /*@Override
    public void rent1() {
        this.visit();
        landlord.rent1();
        this.contract();
        this.IntermediaryFees();
    }

    @Override
    public void rent2() {
        this.visit();
        landlord.rent2();
        this.contract();
        this.IntermediaryFees();
    }
*/
    //其他公共业务
    public void visit(){
        System.out.println("中介带我参观房子!");
    }
    public void contract(){
        System.out.println("中介让我签合同!");
    }
    public void IntermediaryFees(){
        System.out.println("支付中介费!");
    }
}

        4、客户访问代理角色

public class Client{
    public static void main(String[] args) {
        //房东想出租房子
        Landlord landlord = new Landlord();
        //进行代理,通过中介帮房东出租,然后还会有代理的附加操作
        Poxy poxy = new Poxy(landlord);
        //你不需要去找房东,直接去找中介就好
        poxy.rent();
       /* poxy.rent1();
        poxy.rent2();
*/
    }
}

代理模式的好处:

  • 可以使真实角色的操作更加纯粹,只需要做分内之事即可,不用操心公共业务;
  • 公共业务交给代理,实现了业务的分工;
  • 公共业务发生拓展的时候,方便集中管理;

缺点:一个真实角色就会产生一个代理角色 代码量翻倍效率低

10.2、深入理解静态代理模式

业务模块示例:
        1、接口:

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

        2、实现类:

public class UserServiceImp 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 query() {
        System.out.println("查询");
    }
}

        3、代理实现类:

//代理
public class UserPoxy implements UserService {
    //代理UserService
    private UserServiceImp userServiceImp;

    public UserPoxy(){}
    public UserPoxy(UserServiceImp userServiceImp) {
        this.userServiceImp = userServiceImp;
    }

    @Override
    public void add() {
        log("add");
        userServiceImp.add();
    }

    @Override
    public void delete() {
        log("delete");
        userServiceImp.delete();
    }

    @Override
    public void update() {
        log("update");
        userServiceImp.update();
    }

    @Override
    public void query() {
        log("query");
        userServiceImp.query();
    }
    //附加业务输出日志
    public void log(String str){
        System.out.println("方法"+str+"正在执行---");
    }

}

        4、客户调用代理:

public class Client {
    public static void main(String[] args) {
        UserServiceImp userServiceImp = new UserServiceImp();
        UserPoxy userPoxy = new UserPoxy(userServiceImp);
        userPoxy.add();
    }
}

AOP机制:

10.3、动态代理

  • 动态代理和静态代理角色是一样的
  • 动态代理的代理类是动态生成的,不是我们直接写好的
  • 动态代理分为两大类:基于接口的动态代理,基于类的动态代理。
    • 基于接口:JDK动态代理(使用的就是这个)
    • 基于类:cglib
    • Java字节码实现:Javasist

需要了解两个类:Proxy、InvocationHandler

Proxy:生成得到代理类

  //生成得到代理类  newProxyInstance代理实例
public Object getProxy() {

return 
Proxy.newProxyInstance(this.getClass().getClassLoader(),rent.getClass().getInterfaces(),this);
}

InvocationHandler:处理代理实例返回结果

    //处理代理实例并返回结果
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //动态代理机制的实现就是使用反射实现
        
        Object result = method.invoke(rent, args);
        
        return result;
    }

动态代理优点:  

  • 可以使真实角色的操作更加纯粹,只需要做分内之事即可,不用操心公共业务;
  • 公共业务交给代理,实现了业务的分工;
  • 公共业务发生拓展的时候,方便集中管理;
  • 一个动态代理类代理的是一个接口,一般对应着一个业务类;
  • 一个动态代理类可以动态代理多个真实类,只要是实现了同一个接口即可;

11、AOP

11.1、什么是AOP

        在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方
式和运行期动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个
热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑
的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高
了开发的效率。

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

(2)也就是不通过修改源代码方式,在主干功能里面添加新功能

(3)了解一些关键词:

  • 横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …
  • 切面(Aspect):横切关注点 被模块化 的特殊对象。即,它是一个类。
  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。
  • 目标(Target):被通知对象。
  • 代理(Proxy):向目标对象应用通知之后创建的对象。
  • 切入点(PointCut):切面通知 执行的 “地点”的定义。
  • 连接点(JointPoint):与切入点匹配的执行点。
  • Weaving(织入):将 Aspect 和其他对象连接起来, 并创建 Adviced object 的过程

11.2、使用Spring实现AOP

使用AOP织入需要导包:

<dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjtools</artifactId>
  <version>1.9.9</version>
</dependency>

11.2.1、方式一:使用Spring的API接口

日志包中建前置通知类和后置通知类:

前置通知:
 

//前置日志,方法前置通知
public class Log implements MethodBeforeAdvice {
    //method : 要执行的目标对象的方法
    //args:参数
    //target:目标对象

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"方法被执行了");
    }
}

后置通知:

//后置日志通知
public class AfterLog implements AfterReturningAdvice {
    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("执行了"+method.getName()+"方法。返回了"+returnValue);
    }
}

业务接口及实现类:

接口:

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

实现:

public class UserServiceImp 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("查询");
    }
}

      ApplicationContext.xml  注册bean及配置aop、设置切入点、执行环绕增加

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       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
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">
    <!--注册bean-->
    <bean id="log" class="com.zhang.log.Log"/>
    <bean id="userService" class="com.zhang.service.UserServiceImp"/>
    <bean id="afterLog" class="com.zhang.log.AfterLog"/>

    <!--方式一:使用原生Spring API接口-->
    <!--配置aop:  需要导入aop约束-->
    <aop:config>
    <!--切入点:pointcut id是给切入点起的名字
        expression:表达式
        execution(需要执行的位置 *修饰词 *返回值 *类名 *方法名 *参数)
        *(所有的修饰词) com.zhang.service.UserServiceImp(这个类).*(这个类下的所有方法)(..)(参数)
    -->
    <aop:pointcut id="pointcut" expression="execution(* com.zhang.service.UserServiceImp.*(..))"/>
    <!--执行环绕增加 advisor环绕-->
        <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
        <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
    </aop:config>
</beans>

测试:

public class MyTest {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("ApplicationContext.xml");
        //动态代理代理的是接口,不是实现类(注意)
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }
}

11.2.2、方式二:自定义实现AOP

新建DiyPointCut自定义切面类:

public class DiyPointCut {
    public void Before(){
        System.out.println("-----方法执行前-----");
    }
    public void After(){
        System.out.println("-----方法执行后-----");
    }
}

注册bean后:

<!--方式二:自定义实现AOP-->
    <aop:config>
    <!--自定义切面: ref要引用的类-->
        <aop:aspect ref="diy">
        <!--切入点-->
            <aop:pointcut id="point" expression="execution(* com.zhang.service.UserServiceImp.*(..))"/>
        <!--通知-->
            <aop:before method="Before" pointcut-ref="point"/>
            <aop:after method="After" pointcut-ref="point"/>
        </aop:aspect>
    </aop:config>

11.2.3、方式三:使用注解实现AOP

新建AnnotationPointCut类:

//方式三:使用注解方式实现AOP
@Aspect //标注这个类是一个切面
public class AnnotationPointCut {
    @Before("execution(* com.zhang.service.UserServiceImp.*(..))")
    public void before(){
        System.out.println("----执行方法前----");
    }
    @After("execution(* com.zhang.service.UserServiceImp.*(..))")
    public void after(){
        System.out.println("----执行方法后----");
    }

    //在环绕增加中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.zhang.service.UserServiceImp.*(..))")
    public void around(ProceedingJoinPoint jp) throws Throwable{
        System.out.println("环绕前");
        Signature signature = jp.getSignature();//获得签名
        System.out.println("签名:"+signature);
        Object proceed = jp.proceed();//执行方法
        System.out.println("环绕后");
    }

}

配置xml:

  <!--方式三:使用注解-->
    <!--注册bean-->
    <bean id="annotationPointCut" class="com.zhang.diy.AnnotationPointCut"/>
    <!--开启注解支持-->
    <aop:aspectj-autoproxy/>

顺序:

 12、整合MyBatis

步骤:

        1、导jar包:

                junit

                mybatis

                mysql数据库

                spring相关

                aop织入

                mybatis-spring

  <dependencies>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.29</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.5.9</version>
        </dependency>

    <!--spring操做数据库需要导:spring-jdbc-->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
            <version>5.3.18</version>
        </dependency>


        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.13</version>
        </dependency>

        <!-- https://mvnrepository.com/artifact/org.mybatis/mybatis-spring -->
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.22</version>
        </dependency>
    </dependencies>

        2、编写配置文件

        mybatis-config.xml:

<?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.zhang.pojo"/>
    </typeAliases>

    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.cj.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="123456"/>
            </dataSource>
        </environment>
    </environments>
    <mappers>
        <mapper class="com.zhang.mapper.UserMapper"/>
    </mappers>
</configuration>

       UserMapper.xml:

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.zhang.mapper.UserMapper">
    <select id="selectUser" resultType="user">
        select *from mybatis.user
    </select>

</mapper>

         3、测试

        提取工具类:

public class MybatisUtils {
    private static SqlSessionFactory sqlSessionFactory;
    static {
        try {
            //使用Mybatis获取sqlSession对象
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        } catch (IOException e) {
            e.printStackTrace();
        }

    }

    //自动提交事务设置
    public static SqlSession getSqlSession(){
        return sqlSessionFactory.openSession(true);
    }
}

test:

 @Test
    public void t1(){
        SqlSession sqlSession = MybatisUtils.getSqlSession();
        UserMapper mapper = sqlSession.getMapper(UserMapper.class);
        List<User> userList = mapper.selectUser();
        for (User user : userList) {
            System.out.println(user);
        }
        sqlSession.close();
    }

12.1、整合方式一:

1、编写配置

        mybatis-config.xml:

<?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.zhang.pojo"/>
    </typeAliases>

<!-- 设置放这里就好-->
</configuration>

spring-dao.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="
        http://www.springframework.org/schema/beans
        https://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/aop
        https://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--DataSource:使用spring的数据源替换掉mybatis的配置 c3p0 dbcp druid
        这里使用spring的jdbc:rg.springframework.jdbc.datasource
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="driverClassName" value="com.mysql.cj.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="123456"/>
    </bean>
    <!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    <!--绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations" value="classpath:com/zhang/mapper/UserMapper.xml"/>
    </bean>
    <!--SqlSessionTemplate:就是我们用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--只能使用构造器方法注入sqlSessionFactory-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

    <!--注册实现类-->
    <bean id="userMapper" class="com.zhang.mapper.UserMapperImp">
        <property name="sqlSessionTemplate" ref="sqlSession"/>
    </bean>
</beans>

2、sqlSessionFactory

<!--sqlSessionFactory-->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
    <!--绑定mybatis配置文件-->
        <property name="configLocation" value="classpath:mybatis-config.xml"/>
        <property name="mapperLocations"value="classpath:com/zhang/mapper/UserMapper.xml"/>
    </bean>

3、sqlSessionTmplate

 <!--SqlSessionTemplate:就是我们用的sqlSession-->
    <bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <!--只能使用构造器方法注入sqlSessionFactory-->
        <constructor-arg index="0" ref="sqlSessionFactory"/>
    </bean>

4、需要给接口加实现类

public class UserMapperImp implements UserMapper{
    //原来我们所有的操作都使用SqlSession执行,现在使用SqlSessionTemplate
    private SqlSessionTemplate sqlSessionTemplate;

    public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
    }

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

5、将实现类注入spring中

    <!--注册实现类-->
    <bean id="userMapper" class="com.zhang.mapper.UserMapperImp">
        <property name="sqlSessionTemplate" ref="sqlSession"/>
    </bean>

6、测试

    public void t1(){
       ApplicationContext context = new ClassPathXmlApplicationContext("spring-dao.xml");
        UserMapper userMapper = context.getBean("userMapper", UserMapper.class);
        for (User user : userMapper.selectUser()) {
            System.out.println(user);
        }

    }

12.2、整合方式二

让接口实现类去继承SqlSessionDaoSupport

SqlSessionDaoSupport 是一个抽象的支持类,用来为你提供 SqlSession。调用 getSqlSession() 方法你会得到一个 SqlSessionTemplate,之后可以用于执行 SQL 方法

例:

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

注册:给其父类属性注册sqlSessionFactory

    <bean id="userMapper2" class="com.zhang.mapper.UserMapperImpTwo">
        <property name="sqlSessionFactory" ref="sqlSessionFactory"/>
    </bean>

在这个类里面,通常更倾向于使用 MapperFactoryBean,因为它不需要额外的代码。但是,如果你需要在 DAO 中做其它非 MyBatis 的工作或需要一个非抽象的实现类,那么这个类就很有用了。

SqlSessionDaoSupport 需要通过属性设置一个 sqlSessionFactory 或 SqlSessionTemplate。如果两个属性都被设置了,那么 SqlSessionFactory 将被忽略。

13、声明式事物

13.1、回顾事务:

  • 要么都成功要么都失败
  • 事务在开发中涉及数据一致性,非常重要
  • 确保完整性与一致性

事务ACID原则:

  • 原子性Atomicity
  • 一致性Consistency
  • 隔离性Isolation   (多个业务可能操作同一个资源,防止数据损坏)
  • 持久性Durability   (事务一旦提交,无论系统发生什么问题,结果都不会受到影响,被持久化写入)

13.2、spring中的事务

  1. 声明式事务管理:AOP切面、不用改原有代码的。
  2. 编程式事务管理:需要在代码中修改,进行管理。

声明式事务配置:

 <!--配置声明式事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <!--结合AOP实现事务的织入-->
    <!--配置事务通知-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <!--给实现类里的方法配置事务-->
    <!--配置事务的传播特性:propagation默认是REQUIRED
        REQUIRED:支持当前事务,如果没有当前事务就新建一个事务,最常用也是默认的
    -->
     <tx:attributes>
         <tx:method name="addUser" propagation="REQUIRED"/>
         <tx:method name="deleteUser" propagation="REQUIRED"/>
         <tx:method name="query" propagation="REQUIRED"/>
         <tx:method name="update" propagation="REQUIRED"/>
         <tx:method name="*" propagation="REQUIRED"/>
     </tx:attributes>
    </tx:advice>
    <!--配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointCut" expression="execution(* com.zhang.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointCut"/>
    </aop:config>

接口再加两个方法:

public class UserMapperImpTwo extends SqlSessionDaoSupport implements UserMapper{
    @Override
    public List<User> selectUser() {
        UserMapper mapper = getSqlSession().getMapper(UserMapper.class);
        User user = new User(8, "老八", "88888");
        addUser(user);
        deleteUser(8);
        return mapper.selectUser();
    }
    @Override
    public int addUser(User user) {
        return getSqlSession().getMapper(UserMapper.class).addUser(user);
    }
    @Override
    public int deleteUser(int id) {
        return getSqlSession().getMapper(UserMapper.class).deleteUser(id);
    }
}

这时事务就开启了,出现错误就无法插入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Fiercezm

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值