Spring简单易懂(IOC-DI依赖注入-AOP-事务)

个人博客网站:点击这里哟

1. Spring


1.1简介

  • Spring:翻译的意思为春天---->给软件行业带来春天
  • 2002年,首次推出了Spring框架的雏形:interface21框架
  • Spring框架即以interface21框架为基础,经过重新设计,并不断丰富其内涵,于2004年3月24日
  • 创始人:Rod Johnson 这个人的专业不是计算机的,而是学音乐的
  • Spring的理念:使现有的技术更加容易使用,容易上手。
  • 官网 : http://spring.io/
  • 官方下载地址 : https://repo.spring.io/libs-release-local/org/springframework/spring
<!-- https://mvnrepository.com/artifact/org.springframework/spring-webmvc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-webmvc</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>


<!-- https://mvnrepository.com/artifact/org.springframework/spring-jdbc -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.6.RELEASE</version>
</dependency>


1.2 优点

  • spring是一个免费开源的框架(容器)
  • spring是一个轻量级的,非侵入型的框架
  • 控制反转(ioc)重点
  • 面向切面编程(aop)重点
  • 支持事务的处理,对框架的整合的支持

1.3 组成

在这里插入图片描述

2.IOC理论推导


1.UserDao 接口
2.UserDaoImpl 实现类
3.UserSerive 业务接口
4.UserSeriveImpl 业务实体类

在我们之前的业务中,用户的需求可能会影响到我们原来的代码,我们需要根据用户的需要去修改原代码(这样是非常麻烦,维护成本高)

我们使用一个Set接口实现,已经发生了很大的变化

 private UserDao userDao;

  //利用set方法进行动态实现值的注入
    public void setUserDao(UserDao userDao) {
        this.userDao = userDao;
    }
  • 之前,程序是主动创建对象,控制权在程序猿手上
  • 使用了set注入后,程序没有了主动性,而是是被动的接受对象(要用什么对象,直接通过set方法注入创建相应的对象

这种思想,从本质上解决了问题,我们程序猿就可以不用再去管理对象的创建了,系统耦合性降低了,可以更加专注在业务的实现上
在这里插入图片描述

IOC本质


控制反转IoC(Inversion of Control),是一种设计思想,DI(依赖注入)是实现IoC的一种方法,也有人认为DI只是IoC的另一种说法。没有IoC的程序中 , 我们使用面向对象编程 , 对象的创建与对象间的依赖关系完全硬编码在程序中,对象的创建由程序自己控制,控制反转后将对象的创建转移给第三方,个人认为所谓控制反转就是:获得依赖对象的方式反转了。

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

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

3.hellospring

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">


    <!--    使用spring来创建我们的对象,在spring这些都称为bean


            类型 变量名=new 类型();
            Hello  hello=new Hello();

            id=变量名
            class=new 的对象
            property=相当于给对象中的属性设置一个值

            bean=对象  new Hello();

    -->


    <bean id="hello" class="com.lwq.pojo.Hello">
        <property name="name" value="spring"></property>
    </bean>

</beans>

实体类

package com.lwq.pojo;

import java.io.Serializable;

/**
 * @author Hello
 * @since 2020/6/7 20:18
 */
public class Hello implements Serializable {
    private String name;
    public Hello(String name) {
        this.name = name;
    }

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

    public Hello() {
    }

    public String getName() {
        return name;
    }

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



}

测试

import com.lwq.pojo.Hello;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author
 * @since 2020/6/7 20:27
 */
public class MyTest {
    @Test
    public  void HelloTest(){
        //获取spring的上下文对象
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");

        //对象现在都换在spring中管理了,我们要使用,就直接去里面取出来就可以了(通过id)
        Hello hello = (Hello) context.getBean("hello");

        System.out.println(hello.toString());
    }
}

总结一下:到了现在我们已经可以彻底不要去程序中改动了,要实现不同的操作,只需要在xml文件中进行修改,IOC就是一句话:对象由spring来创建,管理,装配。

4.IOC创建对象的方式


1使用无参的构造方法创建对象,默认
2.假设我们要使用有参创建对象

第一种	
 <bean id="User" class="com.lwq.pojo.User">
        <!--    第一种方式下标赋值-->
        <constructor-arg index="0" value="小罗"></constructor-arg>
    </bean>
第二种这种方式不推荐使用	
 <bean id="User" class="com.lwq.pojo.User">
        <!--    第二种方式参数类型创建-->
        <constructor-arg type="java.lang.String" value="小罗啊"></constructor-arg>
  	  </bean>
第三种
  <bean id="User" class="com.lwq.pojo.User">
        <!--    第三种方式直接通过方法名参数-->
        <constructor-arg name="name" value="小罗啊"></constructor-arg>
    </bean>

5.Spring配置


5.1别名


    <bean id="User" class="com.lwq.pojo.User">
        <!--    第三种方式直接通过方法名参数-->
        <constructor-arg name="name" value="小罗啊"></constructor-arg>
    </bean>

    <!--    别名,如果添加了别名,我们也可以通过别名获取到这个对象-->
    <alias name="User" alias="ddddd"></alias>

在这里插入图片描述

5.2 Bean的配置

<!--    id:bean的唯一标识符,也就是相当于我们学的对象名
        class:bean 对象所对应的全限定名:包名+类型
        name:也是别名,name可以同时取多个别名

-->
    <bean id="user1" class="com.lwq.pojo.User" name="user2">
        <property name="name" value="难受"></property>
    </bean>

5.3 import

这个import,一般作用于团队开发,他可以将多个配置文件,导入合并为一个
假如,现在有多个人开发一个项目,这三个人负责不同的类的开发,不同的类需要注册在不同的bean中,我们可以利用import将所有人的beans.xml合并为一个总的xml,这个总的xml就拥有了所有的beans.xml的功能

  • 张三
  • 李四
  • 王五
  • applicationContext.xml
    在这里插入图片描述

6.DI依赖注入


6.1 构造器注入

前面已经说过的

6.2 set方法注入【重点】

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

环境搭建
1.复杂类型

package com.lwq.pojo;

/**
 * @author
 * @since 2020/6/8 20:27
 */
public class Address {
    private  String address;

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }
}

2.真实测试对象

package com.lwq.pojo;

import java.util.*;

/**
 * @author
 * @since 2020/6/8 20:27
 */
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;

    @Override
    public String toString() {
        return "Student{" +
                "name='" + name + '\'' +
                ", address=" + address +
                ", books=" + Arrays.toString(books) +
                ", hobbys=" + hobbys +
                ", card=" + card +
                ", games=" + games +
                ", wife='" + wife + '\'' +
                ", info=" + info +
                '}';
    }

    private String wife;

    public String getName() {
        return name;
    }

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

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

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

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

    public List<String> getHobbys() {
        return hobbys;
    }

    public void setHobbys(List<String> hobbys) {
        this.hobbys = hobbys;
    }

    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;
    }

    private Properties info;
}

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

    <bean id="student" class="com.lwq.pojo.Student">
<!--        第一种,普通值注入,直接使用value注入-->
        <property name="name" value="小罗"></property>

    </bean>

</beans>

4.测试类

import com.lwq.pojo.Student;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author
 * @since 2020/6/8 20:33
 */
public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.getName());

    }
}

完善过的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
        http://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="address" class="com.lwq.pojo.Address">
        <property name="address" value="重庆"></property>
    </bean>

    <bean id="student" class="com.lwq.pojo.Student">
        <!--        第一种,普通值注入,直接使用value注入-->
        <property name="name" value="小罗"></property>

        <!--           第二种,bean注入,ref-->
        <property name="address" ref="address"></property>

        <!--        数组注入-->
        <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>
            </list>
        </property>

        <!--        map注入-->

        <property name="card">
            <map>
                <entry key="身份证" value="12351142"></entry>
                <entry key="银行卡" value="1156874"></entry>
                <entry key="密码" value="2524411"></entry>
            </map>
        </property>

        <!--        set注入-->
        <property name="games">
            <set>
                <value>英雄联盟</value>
                <value>CSgo</value>
                <value>cf</value>
                <value>王者荣耀</value>
            </set>
        </property>

        <!--        null值注入-->
        <property name="wife">
            <null></null>
        </property>


        <property name="info">
            <props>
                <prop key="学号">2002022</prop>
                <prop key="性别"></prop>
            </props>
        </property>

    </bean>


</beans>

测试类

import com.lwq.pojo.Student;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author
 * @since 2020/6/8 20:33
 */
public class MyTest {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("beans.xml");
        Student student = (Student) context.getBean("student");
        System.out.println(student.toString());
        /* 运行结果
        Student{name='小罗',
        address=Address{address='重庆'},
        books=[西游记, 水浒传, 三国演义, 红楼梦],
        hobbys=[打游戏, 敲代码, 敲敲敲代码, 植发],
        card={身份证=12351142, 银行卡=1156874, 密码=2524411},
        games=[英雄联盟, CSgo, cf, 王者荣耀],
        wife='null',
        info={学号=2002022, 性别=男}}
         */

    }
}

6.3 其他方式

我们可以使用p命名空间和c命名空间进行注入

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


    <!--      p命名空间注入,可以直接注入属性的值:property-->
    <bean id="user" class="com.lwq.pojo.User" p:name="张三" p:age="15"></bean>


    <!--      c命名空间注入,可以通过构造器注入:construct-args-->
    <bean id="user2" class="com.lwq.pojo.User" c:age="15" c:name="lwq"></bean>


</beans>

测试

   @Test
    public  void  getUser(){
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = (User) context.getBean("user");
        System.out.println(user.toString());
    }

    @Test
    public  void  getUser2(){
        ApplicationContext context = new ClassPathXmlApplicationContext("userbeans.xml");
        User user = (User) context.getBean("user2");
        System.out.println(user.toString());
    }

注意点
p命名空间和c命名空间不能直接使用,需要导入xml约束

xmlns:p="http://www.springframework.org/schema/p"
xmlns:c="http://www.springframework.org/schema/c"

6.4 bean的作用域

在这里插入图片描述
1.单例模式(spring默认机制)
在这里插入图片描述

    <bean id="user2" class="com.lwq.pojo.User" c:age="15" c:name="lwq" scope="singleton"></bean>

2.原型模式:每次去容器中get的时候,都会创建一个新的对象
在这里插入图片描述

    <bean id="user2" class="com.lwq.pojo.User" c:age="15" c:name="lwq" scope="prototype"></bean>

3.其余的request -session-application ,这些只能在web开发中使用的到

7.Bean的自动装配


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

在spring中有3种装配的方式
1.在xml显示的配置
2.在java中显示的配置
3.隐式的自动装配bean【重要】

7.1 测试

环境搭建:一个人有两个宠物

7.2自动装配

byName和byType

<?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="cat" class="com.lwq.pojo.Cat"></bean>
    <bean id="dog" class="com.lwq.pojo.Dog"></bean>

<!--  byName:会自动去容器上下文中查找,和自己对象set方法后面相应的beanid
      byType:会自动去容器上下文中查找,和自己对象属性类型相同的bean

-->
    <bean id="people" class="com.lwq.pojo.People" autowire="byType">
        <property name="name" value="小罗"></property>
          <!--     
        原来:   
        <property name="dog" ref="dog"></property>
        <property name="cat" ref="cat"></property>
               -->
    </bean>
      <bean id="people" class="com.lwq.pojo.People" autowire="byName">
        <property name="name" value="小罗"></property>
          <!--     
        原来:   
        <property name="dog" ref="dog"></property>
        <property name="cat" ref="cat"></property>
               -->
</beans>

注意:
1.在byname的时候,必须保证所有bean的id唯一,并且这个bean需要和自动注入属性的set方法的值一致
2.在bytype,必须保证所有bean的class唯一,并且这个bean需要和自动注入属性的类型一致

7.3使用注解实现自动装配

要使用注解要知道的:

  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
        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/>

</beans>

@Autowired
直接在属性使用即可,还可以加在set方法上使用
使用了Autowired可以不用编写Set方法,前提是你这个自动装配的属性在IOC(Spring)容器中存在,且符合名字byname

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

测试代码

  //如果显示定义了Autowired的required属性为false,说明这个对象可以为null,否则不允许为null
    @Autowired(required = false)
    private  Cat cat;
    @Autowired
    private  Dog dog;
    private  String name;

如果在@Autowired自动装配的环境比较复杂,自动装配无法通过一个注解【@Autowired】完成的时候,这时候我们的 @Qualifier(value = “xxx”)出现了,他可以配合Autowired使用,可以为这个Autowired指定一个唯一的bean对象注入

    @Autowired
    @Qualifier(value = "cat111")
    private  Cat cat;
    @Autowired
    @Qualifier(value = "dog111")
    private  Dog dog;
    private  String name;

@Resource

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

@Resource和@Autowired的区别:

  • 都是用来自动装配的,都可以放在属性的字段上
  • @Autowired 通过byType的方式实现,而且必须要求这个对象必须存在
  • @Resource默认通过byname的方式实现,如果找不到名字,则通过byType(类型实现,如果都找不到就报错

7.8使用bean开发


要使用注解开发,必须要保证aop的包到导入了
在这里插入图片描述
使用注解需要导入context约束,增加注解的支持

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

    <!--    注解支持-->
    <context:annotation-config></context:annotation-config>
</beans>

1.bean
2.属性如何注入

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author
 * @since 2020/6/10 17:32
 */

//等价于<bean id="user" class="com.lwq.pojo.User"></bean>
//@Component 组件的意思
@Component
public class User {
    /*相当于
        <property name="name" value="lwq"></property>
    */
//    @Value("小罗")
    public String name;

    public String getName() {
        return name;
    }

    @Value("小罗")
    public void setName(String name) {
        this.name = name;
    }
}

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

  • dao【@Repository】
  • service【@Service】
  • controller 【@Controller】
    这四个注解的功能都是一样的,但是代表将某个类注入到spring中,装配bean,

4.自动装配

@Autowired //自动装配通过类型 名字
@Resource  //自动装配通过名字 类型

5.作用域
在这里插入图片描述
6.总结
xml于注解:

  • xml更加万能,适用于任何场合 维护简单方便
  • 注解 不是自己的类使用不了,有局限性,维护相对于xml更加复杂

分工:

  • xml用来管理bean,注解只负责完成属性的注入
  • 我们在使用的过程中,要注意一个问题,我们必须要让注解生效,就需要开启注解的支持
 <!--    注解支持-->
    <context:annotation-config></context:annotation-config>

    <!--    指定要扫描的包,这个包下的注解就会生效-->
    <context:component-scan base-package="com.lwq"></context:component-scan>

8.使用java的方式配置spring

我们可以完全不使用spring的xml配置了,全权交给java来做
JavaConfig是一个spring的一个子项目,在spring4之后,它成为了一个核心功能

在这里插入图片描述
核心配置类(相当于beans.xml)

package com.lwq.config;

import com.lwq.pojo.User;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;

/**
 * @author
 * @since 2020/6/11 19:39
 */

//这个也会被spring容器托管,注册到容器中 ,因为他本身也是一个@Component
//@Configuration代表这是一个配置类,就和我们之前看的beans.xml是一样的
@Configuration
@ComponentScan("com.lwq")
@Import(Haconfig.class)
public class Haconfig {
    //注册一个bean,就相当于我们之前写的
    // <bean id="这个方法的名字" class="这个方法的属性"></bean>
    @Bean
    public User getUser() {
        return new User();  //返回注入到bean的对象
    }
}

实体类

package com.lwq.pojo;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

/**
 * @author
 * @since 2020/6/11 19:33
 */

//这里这个注解的意思,就是说这个类已经被spring接管了,注册到了容器中。
@Component
public class User {
    @Value("小罗")   //属性注入值
    private  String name;

    public String getName() {
        return name;
    }

    public User() {
    }

    public User(String name) {
        this.name = name;
    }

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

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

测试类

import com.lwq.config.Haconfig;
import com.lwq.pojo.User;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author
 * @since 2020/6/11 19:27
 */
public class MyTest {
    public static void main(String[] args) {

        //完全使用注解的话,就需要通过AnnotationConfigApplicationContext上下文来获取容器,通过配置类的class对象加载
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(Haconfig.class);

        User getUser = (User) context.getBean("getUser");
        System.out.println( getUser.getName());
    }
}

9. AOP

9.1 什么是 AOP

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

9.2 aop在spring中的作用

提供声明式事务;允许用户自定义切面

以下名词需要了解下:

横切关注点:跨越应用程序多个模块的方法或功能。即是,与我们业务逻辑无关的,但是我们需要关注的部分,就是横切关注点。如日志 , 安全 , 缓存 , 事务等等 …

  • 切面(ASPECT):横切关注点 被模块化 的特殊对象。即,它是一个类。

  • 通知(Advice):切面必须要完成的工作。即,它是类中的一个方法。

  • 目标(Target):被通知对象。

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

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

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

在这里插入图片描述
SpringAOP中,通过Advice定义横切逻辑,Spring中支持5种类型的Advice:

在这里插入图片描述
即 Aop 在 不改变原有代码的情况下 , 去增加新的功能 .

9.3使用spring实现AOP

【重点】使用AOP织入,需要导入一个依赖包!

<!-- https://mvnrepository.com/artifact/org.aspectj/aspectjweaver -->
<dependency>
   <groupId>org.aspectj</groupId>
   <artifactId>aspectjweaver</artifactId>
   <version>1.9.4</version>
</dependency>

第一种方式

通过 Spring API 实现

编写我们的业务接口和实现类

package com.lwq.service;

/**
 * @author
 * @since 2020/6/14 19:56
 */
public interface UserService {
    public  void add();
    public  void delete();
    public  void update();
    public  void select();


}

package com.lwq.service;

/**
 * @author
 * @since 2020/6/15 16:05
 */
public class UserServiceImpl implements UserService {
    public void add() {
        System.out.println("增加了一个用户");
    }

    public void delete() {
        System.out.println("删除了一个用户");

    }

    public void update() {
        System.out.println("修改了一个用户");

    }

    public void select() {
        System.out.println("查询了一个用户");
    }
}

编写增强类,一个前置增强,一个后置增强

package com.lwq.log;

import org.springframework.aop.MethodBeforeAdvice;

import java.lang.reflect.Method;

/**
 * @author
 * @since 2020/6/15 16:15
 */
public class Log implements MethodBeforeAdvice {

    //method:要执行的目标对象的方法
    //objects:参数
    //target:目标对象
    public void before(Method method, Object[] objects, Object target) throws Throwable {
        System.out.println(target.getClass().getName()+"的"+method.getName()+"被执行了");

    }
}

package com.lwq.log;

import org.springframework.aop.AfterReturningAdvice;

import java.lang.reflect.Method;

/**
 * @author
 * @since 2020/6/15 16:38
 */
public class AfterLog implements AfterReturningAdvice {


    //returnValue;返回值
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {

        System.out.println("执行了"+method.getName()+"方法,返回结果为:"+returnValue);

    }
}

最后去spring的文件中注册,并实现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"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.lwq.service.UserServiceImpl"/>
    <bean id="log" class="com.lwq.log.Log"/>
    <bean id="afterLog" class="com.lwq.log.AfterLog"/>

   

<!--        方式一:使用原生Spring API接口-->
    <!--    aop的配置  需要导入aop的约束-->
        <aop:config>
            <!--切入点 expression:表达式匹配要执行的方法-->
            <aop:pointcut id="pointcut" expression="execution(* com.lwq.service.UserServiceImpl.*(..))"/>
            <!--执行环绕; advice-ref执行方法 . pointcut-ref切入点-->
            <aop:advisor advice-ref="log" pointcut-ref="pointcut"/>
            <aop:advisor advice-ref="afterLog" pointcut-ref="pointcut"/>
        </aop:config>


   
</beans>

测试

import com.lwq.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author
 * @since 2020/6/15 17:02
 */
public class MyTest {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //动态代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();

    }
}

第二种方式

自定义类来实现Aop

目标业务类不变依旧是userServiceImpl

第一步 : 写我们自己的一个切入类

package com.lwq.div;

/**
 * @author
 * @since 2020/6/15 19:31
 */
public class DivPointCut {


    public   void  front(){
        System.out.println("方法执行前----哒哒哒");
    }
    public  void  behind(){
        System.out.println("方法执行后----哒哒哒");
    }


}

去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: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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.lwq.service.UserServiceImpl"/>
    <bean id="log" class="com.lwq.log.Log"/>
    <bean id="afterLog" class="com.lwq.log.AfterLog"/>

<!--        方式二:自定义类-->

        <bean id="div" class="com.lwq.div.DivPointCut"></bean>
        <aop:config>
            <!--        自定义切面,ref要引用的类-->
            <aop:aspect ref="div">
                <!--        切入点-->
                <aop:pointcut id="pointcut" expression="execution(* com.lwq.service.UserServiceImpl.*(..))"/>
                <!--            配置 通知-->
                <aop:before method="front" pointcut-ref="pointcut"></aop:before>
                <aop:after method="behind" pointcut-ref="pointcut"></aop:after>
            </aop:aspect>
        </aop:config>

</beans>

测试

import com.lwq.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author
 * @since 2020/6/15 17:02
 */
public class MyTest {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //动态代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();

    }
}

第三种方式

使用注解实现

第一步:编写一个注解实现的增强类

package com.lwq.div;

import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

/**
 * @author
 * @since 2020/6/15 20:15
 */


//注解实现AOP的切入


@Aspect  //标注这个类为一个切面
public class AnnotationPointCut {


    @Before("execution(* com.lwq.service.UserServiceImpl.*(..))")
    public void start() {
        System.out.println("方法开始执行---哒哒哒");
    }

    @After("execution(* com.lwq.service.UserServiceImpl.*(..))")
    public void behind() {
        System.out.println("方法开始结束---哒哒哒");
    }

    //在环绕增强容器中,我们可以给定一个参数,代表我们要获取处理切入的点
    @Around("execution(* com.lwq.service.UserServiceImpl.*(..))")
    public void behind(ProceedingJoinPoint point) throws Throwable {
        System.out.println("环绕前");

        Signature signature = point.getSignature();//获取签名
        System.out.println("signature:"+signature);

       //执行方法
        point.proceed();

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


}

需要的注解

@Aspect  //标注这个类为一个切面
@Before("execution(* com.lwq.service.UserServiceImpl.*(..))") //在方法开始时切入
@After("execution(* com.lwq.service.UserServiceImpl.*(..))") //在方法结束时切入
//在环绕增强容器中,我们可以给定一个参数,代表我们要获取处理切入的点
@Around("execution(* com.lwq.service.UserServiceImpl.*(..))")

在spring注册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"
       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/aop
       http://www.springframework.org/schema/aop/spring-aop.xsd">

    <!--注册bean-->
    <bean id="userService" class="com.lwq.service.UserServiceImpl"/>
    <bean id="log" class="com.lwq.log.Log"/>
    <bean id="afterLog" class="com.lwq.log.AfterLog"/>

    <!--    方式三-->
    <bean id="apc" class="com.lwq.div.AnnotationPointCut"> </bean>

    <!--        开启注解支持-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
    
</beans>

测试

import com.lwq.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

/**
 * @author
 * @since 2020/6/15 17:02
 */
public class MyTest {
    public static void main(String[] args) {

        ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");

        //动态代理的是接口
        UserService userService = (UserService) context.getBean("userService");
        userService.add();

    }
}

大家可以看看这个执行的顺序
方法getSignature();获取签名 获取的是:
在这里插入图片描述

10.声明式事务

10.1 什么是事务

  • 把一组事务当成一个业务来做,要么都成功,要么都失败
  • 事务在项目开发中,十分的重要,涉及到数据的一致性问题,不能马虎
  • 确保完整性和一致性;

事务的ACID原则:

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

10.2 spring中的事务管理

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

1.编写接口

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<!--        namespace=绑定一个对应的Dao接口/Mapper接口-->
<mapper namespace="com.lwq.mapper.UserMapper">
    <!--    select查询语句-->
    <!--    resultType返回的结果集    -->
    <select id="selectUser" resultType="com.lwq.pojo.User">
    select * from mybatis.user
  </select>

    <insert id="addUser" parameterType="user">
        insert  into mybatis.user(id, name, pwd)  values (#{id},#{name},#{pwd});
    </insert>

    <delete id="deleteUser" parameterType="int">

        delete  from  mybatis.user where id=#{id};

    </delete>

</mapper>

2.编写实现类的方法,来模仿可能出现问题的代码

package com.lwq.mapper;

import com.lwq.pojo.User;
import org.mybatis.spring.SqlSessionTemplate;

import java.util.List;

/**
 * @author
 * @since 2020/6/17 14:43
 */
public class UserMapperImpl implements UserMapper {
    private SqlSessionTemplate sqlSessionTemplate;


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

    public List<User> selectUser() {
        User user = new User(7, "张三", "123456");
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        mapper.addUser(user);
        mapper.deleteUser(3);
        return mapper.selectUser();

    }

    public int addUser(User user) {
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        return mapper.addUser(user);

    }

    public int deleteUser(int id) {
        UserMapper mapper = sqlSessionTemplate.getMapper(UserMapper.class);
        return mapper.deleteUser(id);
    }
}

3.配置事务 配置要织入的事务的类,在通过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: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
         http://www.springframework.org/schema/beans/spring-beans.xsd
         http://www.springframework.org/schema/aop
         https://www.springframework.org/schema/aop/spring-aop.xsd
         http://www.springframework.org/schema/tx
         http://www.springframework.org/schema/tx/spring-tx.xsd">

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


    <!--    DataSource:使用spring的数据源替换mybatis的配置  c3p0 dbcp druid
            我们这里使用spring的JDBC
    -->
    <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
        <property name="url" value="jdbc:mysql://localhost:3306/mybatis?serverTimezone=GMT"></property>
        <property name="password" value="147258"></property>
        <property name="username" value="root"></property>
    </bean>


    <!--    SqlSessionTemplate就是我们使用的sqlSession-->
    <bean id="SqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">

        <!--        只能使用构造器注入sqlSessionFactory,因为他没有set方法-->
        <constructor-arg index="0" ref="sqlSessionFactory"></constructor-arg>
    </bean>

    <!--    配置声明式事务-->

    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"></property>
        <!--        <constructor-arg ref="dataSource" />-->
    </bean>

    <!--    结合AOP实现事务的织入-->
    <!--    配置事务的类-->
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
        <!--        给那些方法配置事务-->
        <!--        配置事务的传播特性 propagation-->
        <tx:attributes>
            <tx:method name="add" propagation="REQUIRED"/>
            <tx:method name="delete" propagation="REQUIRED"/>
            <tx:method name="update" propagation="REQUIRED"/>
            <tx:method name="select" read-only="true"/>
        </tx:attributes>
    </tx:advice>

<!--    配置事务切入-->
    <aop:config>
        <aop:pointcut id="txPointcut" expression="execution(* com.lwq.mapper.*.*(..))"/>
        <aop:advisor advice-ref="txAdvice" pointcut-ref="txPointcut"></aop:advisor>
    </aop:config>

</beans>

4.测试

import com.lwq.mapper.UserMapper;
import com.lwq.pojo.User;
import org.junit.Test;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import java.util.List;

/**
 * @author
 * @since 2020/6/17 14:46
 */
public class MyTest {
    @Test
    public  void selectUser(){
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
        UserMapper mapper = (UserMapper) context.getBean("userMapper");
        List<User> users = mapper.selectUser();
        for (User user : users) {
            System.out.println(user);
        }

    }

}

如果不配置事务,可以存在数据提交不一致的情况 如果我们不在spring中去配置声明式事务,我们就要在代码中手打配置事务
事务在项目开发中是十分重要的,涉及到数据的一致性和完整性问题,不容马虎

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值