Spring5超详细

一、入门案例

步骤:

  1. 创建一个maven项目
  2. 导入spring基础依赖项:spring-context、spring-core、spring-beans、spring=expression
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m4OrnYzL-1632303419097)(https://note.youdao.com/yws/api/personal/file/WEB3ae479d039bffc6d6c543ca93ff7c032?method=download&shareKey=749991b114ded5d58ba5526b5e089dd9)]
  3. 创建一个对象,并写一个简单的方法
package com.zlf;

public class User {
    public void add() {
        System.out.println("add");
    }
}

  1. 创建spring配置文件,在配置文件配置创建的对象(xml格式的文件)
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-loyPULLR-1632303419099)(https://note.youdao.com/yws/api/personal/file/WEB6719efdae07428cf7254c0888cc5838f?method=download&shareKey=0fc94777fe23bfd199254f6ee891212d)]
<?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">
    <!-- 配置User对象创建 class:创建的对象的包名 id: 对象名的别名,用于找到对象 -->
    <bean id="user" class="com.zlf.User"></bean>
</beans>
  1. 使用
  • 加载spring配置文件
  • 获取配置文件中创建的对象
  • 附代码
public class UserTest {

   @Test
   public void add() {
//        1. 加载spring的配置文件
       ApplicationContext applicationContext = new ClassPathXmlApplicationContext("bean1.xml");
//        2. 获取配置文件中创建的对象
       User user = applicationContext.getBean("user", User.class);
       System.out.println(user);
       user.add();
   }
}

二、IOC 容器

2.1 IOC底层原理

2.1.1 什么是IOC:
- 控制反转,将对象创建和对象之间的调用过程,交给spring进行管理
- 使用IOC的目的是为了降低耦合度
2.1.2 底层原理
1. xml解析
2. 工厂模式
3. 反射
2.1.3 IOC过程
- 第一步创建xml配置文件,配置创建的对象
```xml
    <bean id="dao" class="com.zlf.UserDao"></bean>
```
- 第二步 有service和dao类,创建工厂类
```java
    class UserFactory{
        public static UserDao getDao(){
            String classValue = class属性值; // xml 解析获取
            Class clazz = Class.forName(classValue); // 通过反射创建对象
            return (UserDao) clazz.newInstence();
        }
    }
```
- 通过业务层调用工厂类方法

2.2 IOC接口(BeanFactory)

  1. IOC思想基于IOC容器完成,IOC容器底层就是对象工厂
  2. Spring提供IOC容器实现的两种方式(两个接口)
    1. BeanFactory : IOC 容器的基本实现,是Spring内部使用的接口,不提供给开发人员进行使用
    2. ApplicationContext : BeanFactory接口的子接口,提供更多更强大的功能,一般由开发人员进行使用

2.3 IOC bean管理

2.2.1 什Bean管理
1. Spring创建对象
2. Spring注入属性
2.2.2 Bean管理操作的两种方式
1. 基于xml配置文件方式实现
2. 基于注解方式实现
2.2.3 基于xml方式创建对象
  • 在spring配置文件中,使用bean标签,标签里面添加对应属性,就可以实现对象创建
  • 在bean标签中有很多属性
    1. id属性: 唯一标识
    2. class属性: 类全路径(包类路径)
  • 创建对象时候,默认执行无参构造方法
2.2.4 基于xml方式注入属性

DI:依赖注入,就是注入属性。(DI是IOC的一种具体实现,必须在创建对象的基础上进行)

  • set方法注入属性
<bean id="book" class="com.zlf.Book">
            <!--通过set方法注入属性-->
            <!--使用property完成属性注入
                name:类里面属性名称
                value:向属性注入的值
            -->
        <property name="bookName" value="易筋经"></property>
        <property name="author" value="周霖枫"></property>
    </bean>
  • 有参构造注入属性
    <bean id="order" class="com.zlf.Order">
        <constructor-arg name="name" value="华硕"></constructor-arg>
        <constructor-arg name="address" value="中国"></constructor-arg>
    </bean>
  • p名称空间注入(了解):使用p名称空间注入,可以简化基于xml配置方式
    • 添加p名称空间在配置文件
    • 进行属性注入,在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:p="http://www.springframework.org/schema/p"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
            
        <bean id="book" class="com.zlf.Book" p:bookName="降龙十八掌" p:author="无名氏"></bean>
    </beans>
  • 特殊值的注入

null

    <property name="address">
        <null></null>
    </property>

特殊值的注入

    <property name="address">-->
        <value><![CDATA[<<南京>>]]></value>
    </property>
  • 注入属性-外部bean

创建service层和dao层

    // service层
    package com.zlf.service;
    import com.zlf.dao.UserDao;
    public class UserService {
        private UserDao userDao;
    
        public void setUserDao(UserDao userDao) {
            this.userDao = userDao;
        }
    
        public void add() {
            System.out.println("userService add....");
            userDao.update();
        }
    }
    // dao层
    package com.zlf.dao;

    import com.zlf.User;
    
    public class UserDaoImp implements UserDao {
        @Override
        public void update() {
            System.out.println("userDapImp update.....");
        }
    }

配置文件

    <bean id="userService" class="com.zlf.service.UserService">
        <property name="userDao" ref="userDaoImp"></property>
    </bean>
    <bean id="userDaoImp" class="com.zlf.dao.UserDaoImp"></bean>

测试文件

    @Test
    public void test(){
        ApplicationContext context = new ClassPathXmlApplicationContext("bean1.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

输出

    userService add....
    userDapImp update.....
  • 注入属性 内部bean

对于部门和员工的对应关系

//部门类部分代码
    public class Department {
        private String depName;
    
        public String getDepName() {
            return depName;
        }
    
        public void setDepName(String depName) {
            this.depName = depName;
        }
    
        @Override
        public String toString() {
            return "Department{" +
                    "depName='" + depName + '\'' +
                    '}';
        }
    }
//员工类部分代码
    public class Emp {
        private String name;
        private String id;
        private Department department;
    
        public String getName() {
            return name;
        }
    
        public void setName(String name) {
            this.name = name;
        }
    
        public String getId() {
            return id;
        }
    
        public void setId(String id) {
            this.id = id;
        }
    
        @Override
        public String toString() {
            return "Emp{" +
                    "name='" + name + '\'' +
                    ", id='" + id + '\'' +
                    ", department=" + department.toString() +
                    '}';
        }
    
        public Department getDepartment() {
            return department;
        }
    
        public void setDepartment(Department department) {
            this.department = department;
        }
    }

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--        第一种写法-->
            <!--    <bean id="emp" class="com.zlf.Emp">-->
        <!--        <property name="name" value="张三"></property>-->
        <!--        <property name="id" value="123"></property>-->
        <!--        <property name="department">-->
        <!--            <bean id="department" class="com.zlf.Department">-->
        <!--                <property name="depName" value="安保部"></property>-->
        <!--            </bean>-->
        <!--        </property>-->
        <!--    </bean>-->
        
        <!-- 第二种写法-->
        <!--    <bean id="emp" class="com.zlf.Emp">-->
        <!--        <property name="name" value="张三"></property>-->
        <!--        <property name="id" value="123"></property>-->
        <!--        <property name="department" ref="department">-->
        <!--        </property>-->
        <!--    </bean>-->
        <!--    <bean id="department" class="com.zlf.Department">-->
        <!--        <property name="depName" value="安保部"></property>-->
        <!--    </bean>-->
        
        <!--    第三种写法-->
            <bean id="emp" class="com.zlf.Emp">
                <property name="name" value="张三"></property>
                <property name="id" value="123"></property>
                <property name="department" ref="department">
                </property>
                <property name="department.depName" value="技术部"></property>
            </bean>
            <bean id="department" class="com.zlf.Department">
            </bean>
        </beans>
  • 注入集合属性

创建一个类

    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Map;
    import java.util.Set;
    
    public class Student {
        private String[] courses;
        private List<String> list;
        private Map<String,String> map;
        private Set<String> set;
    
        public String[] getCourses() {
            return courses;
        }
    
        public void setCourses(String[] courses) {
            this.courses = courses;
        }
    
        public List<String> getList() {
            return list;
        }
    
        public void setList(List<String> list) {
            this.list = list;
        }
    
        public Map<String, String> getMap() {
            return map;
        }
    
        public void setMap(Map<String, String> map) {
            this.map = map;
        }
    
        public Set<String> getSet() {
            return set;
        }
    
        public void setSet(Set<String> set) {
            this.set = set;
        }
    
        @Override
        public String toString() {
            return "Student{" +
                    "courses=" + Arrays.toString(courses) +
                    ", list=" + list +
                    ", map=" + map +
                    ", set=" + set +
                    '}';
        }
    }

配置文件

<?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.zlf.Student">
<!--        数组类型属性注入-->
        <property name="courses">
            <array>
                <value>java课程</value>
                <value>高数</value>
            </array>
        </property>
<!--        list类型属性注入-->
        <property name="list">
            <list>
                <value>张三</value>
                <value>小明</value>
            </list>
        </property>
<!--        map类型属性注入-->
        <property name="map">
            <map>
                <entry key="JAVA" value="java"></entry>
                <entry key="PHP" value="php"></entry>
            </map>
        </property>
<!--        set集合-->
        <property name="set">
            <set>
                <value>Mysal</value>
                <value>redis</value>
            </set>
        </property>
<!--        注入list集合类型,值是对象-->
        <property name="coursesList">
            <list>
                <ref bean="course1"></ref>
                <ref bean="course2"></ref>
            </list>
        </property>
    </bean>
    <bean id="course1" class="com.zlf.Courses">
        <property name="courseName" value="spring5框架"></property>
    </bean>
    <bean id="course2" class="com.zlf.Courses">
        <property name="courseName" value="java学习"></property>
    </bean>
</beans>
  • 将集合注入部分提取出来

1). 在spring配置文件中引入名称空间util

    <?xml version="1.0" encoding="UTF-8"?>
        <beans xmlns="http://www.springframework.org/schema/beans"
               xmlns:util="http://www.springframework.org/schema/util"
               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
                                   http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        
        </beans>

2). 使用util标签完成list集合注入提取

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:util="http://www.springframework.org/schema/util"
           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
                               http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd">
        <util:list id="bookList">
            <value>易筋经</value>
            <value>九阳真经</value>
            <value>九阳神功</value>
        </util:list>
    
        <bean id="books" class="com.zlf.Books">
            <property name="bookList" ref="bookList"></property>
        </bean>
    </beans>

以上使用的books类

    package com.zlf;

    import java.util.List;
    
    public class Books {
        private List<String> bookList;
    
        public List<String> getBookList() {
            return bookList;
        }
    
        public void setBookList(List<String> bookList) {
            this.bookList = bookList;
        }
    
        @Override
        public String toString() {
            return "Books{" +
                    "bookList=" + bookList +
                    '}';
        }
    }
2.2.5 Bean管理(工厂Bean [FactoryBean] )
  1. Spring 有两种类型bean,一种普通bean,另外一种是工厂bean
  2. 普通bean:在配置文件中定义bean类型就是返回类型
  3. 工厂bean:在配置文件中定义bean可以和返回类型不一样
  4. 实现步骤:
    • 创建类:让这个类作为工厂bean,实现接口FactoryBean
    • 实现接口里面的方法,再实现方法中返回的bean类型
2.2.6 Bean管理(bean作用域)
  1. 在spring中默认情况下,bean是单实例对象
  2. 如何设置单例还是多实例对象
    • 在spring配置文件中bean标签里面有属性scope用于设置单实例还是多实例
    • scope属性值有两个:
      • 默认值: singleton
      • prototype,表示多实例对象
      • request
      • session
    • singleton和prototype区别
      • singleton时,加载spring配置文件时就会创建单实例对象
      • prototype时,在调用getBean方法时候创建多实例对象
2.2.7 Bean的生命周期
  1. 通过构造器创建bean实例(无参构造)
  2. 为bean的属性设置值和对其他bean引用(调用set方法)、
  3. 把bean实例传递给bean后置处理器方法postProcessBeforeInitialization
  4. 调用bean的初始化方法(需要进行配置初始化的方法)
  5. 将bean实例传递给bean 的后置处理器方法postProcessAfterInitialization
  6. bean可以使用了(对象获取到了)
  7. 当容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法)

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="lifecycle" class="com.zlf.LifeCycle" init-method="initMethod" destroy-method="destroyMethod">
        <property name="value" value="111"></property>
    </bean>
    <bean id="myBeanPostProcessor" class="com.zlf.MyBeanPostProcessor"></bean>
</beans>

创建类

package com.zlf;

public class LifeCycle {
    private String value;

    public LifeCycle() {
        System.out.println("第一步 通过构造器创建bean实例(无参构造)");
    }

    public String getValue() {
        return value;
    }

    public void setValue(String value) {
        this.value = value;
        System.out.println("第二步 为bean的属性设置值和对其他bean引用(调用set方法)、");
    }
    public void initMethod(){
        System.out.println("第四步 调用bean的初始化方法(需要进行配置初始化的方法)");
    }
    public void destroyMethod() {
        System.out.println("第七步 当容器关闭时,调用bean的销毁方法(需要进行pei'zhi销毁的方法)");
    }
}

后置处理器:实现接口BeanPostProcessor,创建后置处理器

package com.zlf;


import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        System.out.println("第三步把bean实例传递给bean后置处理器方法postProcessBeforeInitialization");
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) {
        System.out.println("第五步 将bean实例传递给bean 的后置处理器方法postProcessAfterInitialization");
        return bean;
    }
}
2.2.8 Bean的自动装配
  • 根据指定装配规则(属性名或者属性类型),spring自动将匹配的属性值进行注入。
  • 实现自动装配:
    1. bean标签属性autowire,配置自动装配
    2. autowire属性常用两个值:
      • byName根据属性名称注入,bean的id值要和类中的属性名称要一样
      • bytype根据属性类型注入,不能定义多个同样类型的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 http://www.springframework.org/schema/beans/spring-beans.xsd">

<!--        <bean id="emp" class="com.zlf.Emp" autowire="byName"></bean>-->
        <bean id="emp" class="com.zlf.Emp" autowire="byType"></bean>
        <bean id="dep" class="com.zlf.Dep"></bean>
</beans>
2.2.9 Bean管理 引入外部属性文件

以使用数据库连接池为例

  1. 创建一个外部属性文件()
prop.driverClass=com.mysql.jdbc.Driver
prop.url=jdbc:mysql://localhost:3306/user
prop.userName=root
prop.password=root
  1. 将外部properties属性文件引入到spring配置文件中(需要在配置文件中引入context名称空间)
  2. 在spring配置文件中使用标签引入外部属性文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       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/beans/spring-context.xsd">

    <context:property-placeholder location="classpath:jdbc.properties"></context:property-placeholder>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
        <property name="driverClassName" value="${prop.driverClass}"></property>
        <property name="url" value="${prop.url}"></property>
        <property name="username" value="${prop.userName}"></property>
        <property name="password" value="${prop.password}"></property>
    </bean>
</beans>
2.2.10 Bean管理(基于注解方式)

spring针对bean管理中创建对象提供注解

  1. @component
  2. @Service
  3. @Controller
  4. @Repository
    上面四个注解功能一样,都可以用来创建bean实例

** 基于注解创建对象流程**

  1. 开启组件扫描
<?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:component-scan base-package="com.zlf"></context:component-scan>
</beans>
  1. 使用注解
package com.zlf;

import org.springframework.stereotype.Component;

/*
注解中value属性值是可以省略的
* 默认值是类的名称,首字母小写
* */
@Component("userService")
public class UserService {
    public void add() {
        System.out.println("输出了内容");
    }
}

  1. 测试
 ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
UserService userService = context.getBean("userService", UserService.class);
userService.add();
2.2.11 注解实现bean管理(组件扫描配置)
<!--    use-default-filters表示不使用默认filter,自己配置filter
include-filter:设置扫描哪些扫描
该例子表示只扫描注解为Controller的对象
-->
    <context:component-scan base-package="com.zlf" use-default-filters="false">
        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
    <!--   
exclude-filter:设置不扫描哪些扫描
该例子表示只扫描注解不为Controller的对象
-->
    <context:component-scan base-package="com.zlf"  use-default-filters="false">
        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    </context:component-scan>
2.2.12 基于注解实现属性注入
  1. Autowired:根据属性类型进行自动装配
 @Autowired
 private UserDao userDao;
  1. Qualifier:根据属性名称进行注入
 @Qualifier(value = "userService1")
 private UserDao userDao;
  1. Resourse:可以根据类型注入,也可以根据名称注入
//  Resource不写是按照类型注入,写name按照名称注入
    @Resource(name = "userService1")
    private UserDao userDao;
  1. Value:注入普通类型属性
    @Value(value = "abc")
    private String name

代码示例

//dao层代码
package com.zlf.dao;

import org.springframework.stereotype.Repository;

@Repository(value = "userService1")
public class UserDaoImp implements UserDao{
    @Override
    public void add() {
        System.out.println("User Dao add...]");
    }
}
//service层代码
package com.zlf.service;

import com.zlf.dao.UserDao;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Service;

@Service
public class UserService {
    //根据类型注入
    @Autowired
    //根据名称注入
    @Qualifier(value = "userService1")
    //    Resource不写是按照类型注入,写name按照名称注入
    @Resource(name = "userService1")
    private UserDao userDao;
    public void add() {
        System.out.println("UserService add");
        userDao.add();
    }
}

//测试代码
package com.zlf.service;

import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import static org.junit.Assert.*;

public class UserServiceTest {
    @Test
    public void test() {
        ApplicationContext context = new ClassPathXmlApplicationContext("bean.xml");
        UserService userService = context.getBean("userService", UserService.class);
        userService.add();
    }

}

spring配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       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:component-scan base-package="com.zlf"></context:component-scan>
</beans>

使用Resource注解需要导入

  <dependency>
      <groupId>javax.annotation</groupId>
      <artifactId>javax.annotation-api</artifactId>
      <version>1.3.2</version>
    </dependency>
2.2.13 完全注解开发
  1. 创建配置类,替代xml配置文件
package com.zlf;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@Configuration // 作为配置类,替代xml配置文件
@ComponentScan(basePackages = {"com.zlf"}) // 扫描组件
public class SpringConfig {
}
  1. 编写测试类
@Test
public void test() {
    ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
    UserService userService = context.getBean("userService", UserService.class);
    userService.add();
}

三、AOP 面向切面编程

AOP底层原理

  1. AOP底层使用动态代理
    • 有两种情况的动态代理
      • 有接口的情况,使用JDK动态代理
      • 没有接口情况,使用CGLIB代理

2 AOP术语:

  • 连接点:类中哪些方法可以被增强,这些方法称为连接点
  • 切入点: 实际被真正增强的方法,成为切入点
  • 通知(增强):实际增强的逻辑部分被称为通知 ,通知有多种类型:
    • 前置通知(Before)
    • 后置通知(After)
    • 环绕通知(Around)
    • 异常通知(AfterThrowing)
    • 最终通知(AfterReturning )返回值之后进行执行
  • 切面:将通知应用到切入点的过程
3.1 AOP准备操作
  1. Spring框架内一般都是基于AspectJ实现AOP操作,AspectJ不是Spring组成部分,独立AOP框架,一般将AspectJ和Spring框架内一起使用,进行AOP操作
  2. 基于AspectJ实现AOP操作
    1. 基于xml配置文件实现
    2. 基于注解方式实现
  3. 在项目中引入AOP相关依赖
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aop -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aop</artifactId>
    <version>5.2.12.RELEASE</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.2.8.RELEASE</version>
</dependency>
  1. 切入点表达式
  • 切入点表达式作用:知道对哪个类里面的哪个方法进行增强
  • 语法结构:execution([权限修饰符][返回值类型][简单类名/全类名]方法名称)
  • 举例:
        1、execution(* com.zlf.spring.ArithmethicCalculator.*(..))
        含义:ArithmethicCalculator接口中生命的所有方法,第一个*代表任意修饰符及任意返回值,第二个*代表任意方法。".."匹配任意数量、任意类型的参数。若目标类、接口与该切面类在同一个包中可以省略包名
    
        2、execution(public double ArithmethicCalculator.*(double,..))
        含义:第一个参数为double类型的方法。"..."匹配任意数量、任意类型的参数
    
        3、execution(public double ArithmethicCalculator.*(double, double)
        含义:参数类型为double,double类型的方法
    
  • 在AspectJ中,切入点表达式可以通过"&&"、"||"、"!"等操作符结合起来
   // 任意类型中第一个参数为int类型的add方法或sub方法
   execution (* *.add(int,..)) || execution(* *.sub(int,..))
   // 匹配不是任意类中第一个参数为int类型的add方法
   !execution (* *.add(int,..))
  • 使用:
使用*通配符作为所有或者部分命名模式。
*类型匹配模式
   1. *:匹配任何数量字符;比如模式 (*,String) 匹配了一个接受两个参数的方法,第一个可以是任意类型,第二个则必须是String类型
   2. ..:匹配任何数量字符的重复,如在类型模式中匹配任何数量子包;而在方法参数模式中匹配任何数量参数,可以使零到多个。
   3. +:匹配指定类型的子类型;仅能作为后缀放在类型模式后边。
*参数匹配模式:
   1.()匹配了一个不接受任何参数的方法,
   2.(..)匹配了一个接受任意数量参数的方法(零或者更多)。 
   3.(*)匹配了一个接受一个任何类型的参数的方法。 
   4.(*,String)匹配了一个接受两个参数的方法,第一个可以是任意类型, 第二个则必须是String类型。

3.2 AspectJ 注解

3.2.1 AspectJ使用步骤

进行步骤:

1、创建类,在类里面定义方法

package aop;

public class User {
    public void add() {
        System.out.println("add....");
    }
}


2、创建增强类(编写增强逻辑)

package aop;

public class UserProxy {
//    前置通知
    public void before() {
        System.out.println("before...");
    }
}

3、进行通知的配置

  1. 在spring配置文件中,开启注解扫描
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--    开启注解扫描-->
    <context:component-scan base-package="aop"></context:component-scan>
</beans>
  1. 使用注解创建User和UserProxy
@Component
public class User {
    public void add() {
        System.out.println("add....");
    }
}

@Component
public class UserProxy {
//    前置通知
    public void before() {
        System.out.println("before...");
    }
}
  1. 在增强类上面添加注解@Aspect
@Component
@Aspect
public class UserProxy {
//    前置通知
    public void before() {
        System.out.println("before...");
    }
}
  1. 在spring配置文件中开启生成代理对象
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                            http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--    开启注解扫描-->
    <context:component-scan base-package="aop"></context:component-scan>
<!--    开启Aspect生成代理对象-->
    <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>

4、 配置不同类型的通知
在增强类里面,在作为通知方法上面添加通知类型注解,使用切入点表达式配置


@Component
@Aspect
public class UserProxy {
//    前置通知
    @Before("execution(* aop.User.add())")
    public void before() {
        System.out.println("before...");
    }
    @After("execution(* aop.User.add())")
    public void after() {
        System.out.println("after...");
    }
    @Around("execution(* aop.User.add())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around环绕前....");
        proceedingJoinPoint.proceed();
        System.out.println("around环绕后....");
    }
}

3.2.2 相同切入点抽取

可以看到,上面的每一个通知方法的切入点表达式相同,可以做一个抽取

//    相同切入点抽取
    @Pointcut("execution(* aop.User.add(..))")
    public void pointDemo(){}
//    使用相同切入点的抽取
    @Before("pointDemo()")
    public void beforeDemo() {
        System.out.println("before .....");
    }
3.2.3 有多个增强类对同一个方法进行增强,设置增强优先级

在增强类上面添加注解@Order(数字类型值),数字类型值越小优先级越高

// 被代理类
@Component("user")
public class User {
   public void add() {
       System.out.println("add....");
   }
}

// 代理类
@Component
@Aspect
@Order(1)
public class PersonProxy {
    @Before(value="execution(* aop.User.add(..))")
    public void before(){
        System.out.println("Person before");
    }
}
// 代理类
@Component
@Aspect
@Order(3)
public class UserProxy {
//    前置通知
    @Before("execution(* aop.User.add())")
    public void before() {
        System.out.println("before...");
    }
    @After("execution(* aop.User.add())")
    public void after() {
        System.out.println("after...");
    }
    @Around("execution(* aop.User.add())")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("around环绕前....");
        proceedingJoinPoint.proceed();
        System.out.println("around环绕后....");
    }

// 执行结果
Person before
around环绕前....
before...
add....
after...
around环绕后....
3.2.4 AspectJ配置文件
  1. 创建两个类,增强类和被增强类,创建方法
  2. 在spring配置文件中创建两个类对象
  3. 在spring配置文件中配置切入点
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:aop="http://www.springframework.org/schema/aop"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                           http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
                           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
<!--    创建对象-->
    <bean id="book" class="aop.Book"></bean>
    <bean id="bookProxy" class="aop.BookProxy"></bean>

<!--    配置aop增强-->
    <aop:config>
<!--        切入点-->
        <aop:pointcut id="p" expression="execution(* aop.Book.buy(..))"/>
<!--        配置切面-->
        <aop:aspect ref="bookProxy">
            <aop:before method="before" pointcut-ref="p"></aop:before>
        </aop:aspect>
    </aop:config>
</beans>
3.2.5 完全注解开发
package aop;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.EnableAspectJAutoProxy;

@Configuration
@ComponentScan(basePackages = {"aop"})
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class ConfigApp {
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值