Spring学习笔记一

B站视频教学

一·Spring框架概述

1. 概念

  • Spring是一个轻量级的开源的JavaEE框架。
  • Spring用来解决企业应用开发的复杂性。
  • Spring的两个核心部分:IOC(控制反转),AOP

2. 特点

  • 方便解耦,简化开发(借助Spring提供的IOC容器,将对象之间的依赖关系交给Spring管理)
  • AOP编程的支持,在不改变源代码的前提下增加新的功能
  • 便于程序测试
  • 便于集成其他框架
  • 降低JAVAEE API的使用难度
  • 便于进行事务的操作

3. 资源信息

spring官网
spring framework下载地址


spring框架如下:

在这里插入图片描述

4. 实践: Idea中创建Spring Project(使用Maven管理)

建议先查看后续章节的基础内容再返回实践该project

  1. 借助Aliyun Java Initializr生成demo框架
  2. 在pom.xml中添加相关的依赖
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-beans</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.2.6.RELEASE</version>
        </dependency>
  1. 创建Spring配置文件,在配置文件中配置创建的对象

在/src/main/resources中创建bean.xml配置文件

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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="person" class="com.alibaba.springExercise.Person">
<!--        <property name="name" value="spring"></property>-->
    </bean>
</beans>
  1. 在com.alibaba.springExercise下创建一个Person class
package com.alibaba.springExercise;

public class Person {
    public Integer age;
    public String sex;

    public void Person(){}
    public void Person(Integer _age, String _sex){
        this.age = _age;
        this.sex = _sex;
    }

    public void getProperties(){
        System.out.println("Class Person has age & sex properties.");
    }

    @Override
    public String toString() {
        return "Person{" +
                "age=" + age +
                ", sex='" + sex + '\'' +
                '}';
    }
}
  1. 编写main函数内容测试
    public static void main(String[] args) {
        //1.加载spring配置文件即bean.xml
        ApplicationContext context = new ClasspathXmlApplicationContext("bean.xml");

        //2.获取配置文件中所创建的对象
        Person person = context.getBean("person",Person.class);//这里的input string就是bean.xml文件自定义的bean id
        
        //测试
        System.out.println(person);
        person.getProperties();
        
    }

主体文件目录结构如下:

springExercise
├── HELP.md
├── README.md
├── mvnw
├── mvnw.cmd
├── pom.xml
├── springExercise.iml
├── src
│   ├── main
│   │   ├── java
│   │   │   └── com
│   │   │       └── alibaba
│   │   │           └── springExercise
│   │   │               ├── Person.java
│   │   │               └── SpringExerciseApplication.java
│   │   └── resources
│   │       ├── application.properties
│   │       └── bean.xml
│   └── test
│       └── java
│           └── com
│               └── alibaba
│                   └── springExercise
│                       └── SpringExerciseApplicationTests.java
└── target
    ├── ...
...
29 directories, 19 files

二·IOC容器

1. 简单理解IOC概念

ioc:控制反转,举例说明:

在现实生活中,人们要用到一样东西的时候,第一反应就是去找到这件东西,比如想喝新鲜橙汁,在没有饮品店的日子里,最直观的做法就是:买果汁机、买橙子,然后准备开水。值得注意的是:这些都是你自己**“主动”创造**的过程,也就是说一杯橙汁需要你自己创造。

在这里插入图片描述

然而到了今时今日,由于饮品店的盛行,当我们想喝橙汁时,第一想法就转换成了找到饮品店的联系方式,通过电话等渠道描述你的需要、地址、联系方式等,下订单等待,过一会儿就会有人送来橙汁了。
"图和例子来源于"知乎链接

比如传统的两个类之间的关系,class B想要调用class A中的方法,那么首先要有一个class A的对象才可以供 B对象调用,现在则把这些过程统统交给spring处理。可以很好的降低耦合度。

2. IOC底层原理

主要用到:xml文件的解析、工厂设计模式、反射

2.1 发展背景

比如类user中想要调用person类中的add方法,那么需要先创建person对象,然后调用方法

在这里插入图片描述


------------------------------------------------------------------------
常见的可以处理上述问题的方法:使用工厂设计模式进行解耦

在这里插入图片描述

工厂模式对上述问题的耦合度的降低体现在:
1.对产品类(比如上述提到的class Person)的屏蔽,**不需要知道创建对象的过程,**产品类的实现如何变化,调用者无需关心,只需要关心产品的接口,接口只要保持不变,上层模块就不会变化。
工厂设计模式的其他优点:
1.良好的封装性,代码结构清晰,调用者只需知道产品的类名即可,不需要知道创建对象的过程,降低代码间的耦合。
2.扩展性优秀,如果增加一个产品类,只需增加一个对应的工厂类。
3.工厂模式是典型的解耦框架,高层模块只需要知道产品的抽象类,其他的实现类都不需要关心,符合迪米特法则,符合依赖倒置原则,符合里氏替换原则。

使用工厂模式降低两个类之间的耦合度(class A调用class B中的方法),但并不是最优的。IOC应用而生。
------------------------------------------------------------------------

在这里插入图片描述

总的来说,IOC的过程有三步。1.通过xml配置文件来配置要创建的对象。2.通过xml解析得到对应的class;3.通过反射来创建对象 4. 套用工厂模式的流程模版

2.2 IOC接口

  • IOC思想基于IOC容器完成,IOC容器的底层实际上就是对象工厂。
  • Spring中提供IOC容器的两种实现方式(两个接口),注意两者的不同:

(1) beanfactory: 是IOC容器的基本实现,属于Spring内部的使用接口,一般不提供开发人员使用。
beanfactory加载配置文件时不会创建对象,仅仅是加载.xml配置文件,只有在获取/使用时才会创建对应的对象。
(2) ApplicationContext: 是beanfactory的子接口,相比beanfactory来说提供了更多更强大的功能,一般面向开发人员使用。
ApplicationContext加载配置文件时就会创建配置文件中的对象。

在这里插入图片描述



ApplicationContext其中的两个实现类,其区别在于FileSystemXmlApplicationContext对应的是os中文件系统下的完整文件路径;
ClasspathXmlApplicationContext则对应的是当前文件目录中src文件夹。

2.3 IOC操作–Bean管理

  1. Bean管理主要进行两个操作(1)Spring创建对象(2)Spring注入属性
  2. Bean管理操作有两种方式:(1)基于xml配置文件方式实现(2)基于注解方式进行实现
2.3.1 基于xml配置文件方式实现Bean管理
    <bean id="person" class="com.alibaba.springExercise.Person">
<!--        <property name="name" value="spring"></property>-->
    </bean>

id属性:给要创建的对象起一个别名或者其标识
class属性:要创建的对象所在类的全路径
name属性:与id属性一致,不过可以添加任意字符比如‘/’


利用上述xml文件创建对象时使用的是认的无参构造函数

**DI:依赖注入,实际上就是注入属性 ****DI是IOC中的一个具体实现。**可以通过set()方法注入属性;也可以通过有参构造函数注入属性

通过set()方法注入属性,首先在xml文件中的bean字段添加property属性,如下:

    <bean id="person" class="com.alibaba.springExercise.Person">
        <!--使用property来完成属性注入
        name:类中定义的属性名称
        value:该属性对应的值
        -->
        <property name="age" value="18"></property>
        <property name="sex" value="man"></property>
    </bean>

同时,在目标类中(Person class)中要添加set方法,否则会报错。

通过有参构造函数注入属性,在类中要添加有参构造器,其次就是在xml文件中的bean字段中添加constructor-args属性

    <bean id="person" class="com.alibaba.springExercise.Person">
        <constructor-arg index="0" value="28"></constructor-arg>
        <constructor-arg index="1" value="woman"></constructor-arg>
    </bean>

**

<!-- 首先加入限制 -->
xmlns:p="http://www.springframework.org/schema/p"

<bean id="person" class="com.alibaba.springExercise.Person"  p:age="12" p:sex="man">
</bean>

  • IOC操作Bean管理(在xml文件中注入其他类型属性)
  1. 字面量

(1)null值

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

(2)特殊符号

<!-- 使用CDATA结构 ![CDATA[]] -->

<property name="age">
	<value><![CDATA[<<999>>]]></value>
</property>
  1. 注入bean(内部bean和外部bean及级联赋值)

使用场景:class A中调用class B的方法,那么class A中就需要先创建class B的对象,然后调用方法

在xml文件中首先创建两个类的对象,比如user类和person类,然后在user类中注入person类对象属性,此时属性
使用的是ref属性,并不是value属性,且ref属性的值就是所创建的person类的别名。

<!--外部bean-->
		<bean id="user" class="com.alibaba.springExercise.User">
        <property name="object" ref="person"></property>
    </bean>

 		<bean id="person" class="com.alibaba.springExercise.Person"></bean>

<!--内部bean-->
		<bean id="user" class="com.alibaba.springExercise.User">
        <property name="object"> 
            <bean id="person" class="com.alibaba.springExercise.Person">
            </bean>
        </property>
    </bean>
  1. xml注入集合属性

注入数组类型属性; 注入List集合类型属性; 注入Map集合类型属性; 注入集合对象类型
首先创建类并创建对应属性字段,生成对应的set方法

public class Employee {

    private String[] courses;
    private List<String> projects;
    private Map<String, String> jobs;
    private List<Course> learn;

    public void setCourses(String[] courses) {
        this.courses = courses;
    }

    public void setProjects(List<String> projects) {
        this.projects = projects;
    }

    public void setJobs(Map<String, String> jobs) {
        this.jobs = jobs;
    }

    public void setLearn(List<Course> learn) {
        this.learn = learn;
    }

    public void test(){
        System.out.println(Arrays.toString(courses));
        System.out.println(projects);
        System.out.println(jobs);
        System.out.println(learn);
    }
}

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

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

然后在.xml文件中进行如下配置即可

        <bean id="employee" class="com.alibaba.springExercise.collectionType.Employee">
            <property name="courses">
                <array>
                    <value>java</value>
                    <value>spring</value>
                </array>
            </property>

            <property name="projects">
                <list>
                    <value>xiaomi</value>
                    <value>aliyun</value>
                </list>
            </property>

            <property name="jobs">
                <map>
                    <entry key="开发" value="java"></entry>
                    <entry key="AI" value="python"></entry>
                </map>
            </property>


            <property name="learn">
                <list>
                    <ref bean="course1"></ref>
                    <ref bean="course2"></ref>
                </list>
            </property>
        </bean>

        <bean id="course1" class="com.alibaba.springExercise.collectionType.Course">
            <property name="name" value="Spring框架"></property>
        </bean>
        <bean id="course2" class="com.alibaba.springExercise.collectionType.Course">
            <property name="name" value="MyBatis框架"></property>
        </bean>

最后进行测试

        ApplicationContext context = new ClasspathXmlApplicationContext("bean.xml");
        Employee employee = context.getBean("employee", Employee.class);
        employee.test();
  1. 使用util标签完成list集合注入提取

首先在spring配置文件添加util名称空间

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:util="http://www.springframework.org/schema/util"   <!--添加这句-->
       xsi:schemaLocation="http://www.springframework.org/schema/beans
       http://www.springframework.org/schema/beans/spring-beans.xsd
       http://www.springframework.org/schema/util 		<!--添加这句-->
       http://www.springframework.org/schema/util/spring-util.xsd		<!--添加这句-->
        ">

			<util:list id="booklistlist">
            <value>java</value>
            <value>c++</value>
            <value>python</value>
      </util:list>
      <bean id="book" class="com.alibaba.springExercise.collectionType.Book">
            <property name="bookName" ref="booklistlist"></property>
      </bean>
public class Book {
    private List<String> bookName;

    public void setBookName(List<String> bookName) {
        this.bookName = bookName;
    }

    @Override
    public String toString() {
        return "Book{" +
                "bookName=" + bookName +
                '}';
    }

    public void test(){
        System.out.println(bookName);
    }
}
  • IOC操作中Bean的方式

(1)普通Bean:就是上述所讲到的一些方式
(2)factorybean

两者区别:普通Bean在配置文件中定义什么样的bean类型,返回的就是对应的类型;factorybean配置文件中定义什么样的bean类型,返回的可以是和定义类型不一样。

如何定义一个factorybean
step 1: 定义一个类让这个类作为factorybean,实现接口factorybean
step 2: 实现接口里面的方法,在实现的方法中定义返回的bean类型

<bean id="myBean" class="com.alibaba.springExercise.factorybean.Factory">
</bean>
public class Factory implements factorybean<Course> {

    //定义返回bean
    @Override
    public Course getobject() throws Exception {
        Course cou = new Course();
        cou.setName("english");
        return cou;
    }
    
    @Override
    public Class<?> getobjectType() {
        return null;
    }

    @Override
    public boolean isSingleton() {
        return false;
    }
}


//test
        ApplicationContext context = new ClasspathXmlApplicationContext("bean1.xml");
        Course myBean = context.getBean("myBean");
        System.out.println(myBean);

  • IOC操作中Bean的作用域

Spring的配置文件中可以配置创建单实例对象还是多实例对象。

判断单实例对象和多实例对象的标准:在测试文件中new多个同一bean对象,然后查看这多个相同bean对象的地址,如果地址一致,那么就是单实例,如果不一致就是多实例。

Spring配置文件中bean字段认是创建单实例对象,可以通过bean字段中的scope属性来设置实例类型:
scope有三个值,第一个认不写,就是单实例子;第二个“singleton”单实例子 第三个“prototype” 多实例
**
singleton和prototype之间的区别:

  • 一个是创建单实例,一个是创建多实例;
  • 设置scope的值为singleton时,在加载spring配置文件时就会创建单实例对象;设置scope的值为prototype时,在加载spring配置文件时不会创建实例对象,而是在调用getBean方法时才会创建多实例对象。

  • IOC操作中Bean的生命周期

bean的生命周期:1⃣️通过构造器创建bean实例2⃣️为bean设置属性值(通过set方法)3⃣️调用bean的初始化方法(需配置初始化方法,使用init-method属性)4⃣️得到bean对象5⃣️容器关闭时,调用bean的销毁方法(需要进行配置销毁的方法,使用destroy-Method属性

还有一种是添加后置处理器的方式,其生命周期共有七步。

根据指定的装配规则(属性名称或者属性类型),Spring将自动的将匹配的属性值进行注入。

在bean字段中有autowrite属性,可以设置为byName和byType两种

(1)直接配置连接池方式, 借助druid包

        <!--直接配置连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="com.MysqL.jdbc.Driver"></property>
                <property name="url" value="jdbc:MysqL://localhost:3306/userDb"></property>
                <property name="username" value="root"></property>
                <property name="password" value="root"></property>
        </bean>

(2)创建外部属性文件,properties格式文件,写数据库信息:

prop.driverClassName=com.MysqL.jdbc.Driver
prop.url=jdbc:MysqL://localhost:3306/userDb
prop.username=root
prop.password=root

把外部properties属性文件引入到spring配置文件
首先在配置文件中声明一个context名称空间,然后在spring配置文件中使用标签引入外部属性文件

       xmlns:context="http://www.springframework.org/schema/context"
			 xsi:schemaLocation="http://www.springframework.org/schema/context
       http://www.springframework.org/schema/context/spring-context.xsd"

				<!--引入外部属性文件-->
        <context:property-placeholder location="classpath:jdbc.properties"/>
        <!--配置连接池-->
        <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
                <property name="driverClassName" value="${prop.driverClassName}"></property>
                <property name="url" value="${prop.url}"></property>
                <property name="username" value="${prop.username}"></property>
                <property name="password" value="${prop.password}"></property>
        </bean>

2.3.2 基于注解方式实现Bean管理

目的:简化xml配置

  • 基于注解方式创建对象

Spring中针对Bean管理中创建对象提供的注解有:@Component @Service @Controller @Repository
四个注解的功能一样,都可创建bean实例。

step1:引入AOP依赖(在pom.xml文件中)

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.8</version>
        </dependency>

step2:开启组件扫描,目的是为了让编译器知道该扫描哪个类从而找到对应的注解。

<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<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
        ">

        <!--
        开启组件扫描:
                1.如果扫描多个包,可以使用","隔开
                2.扫描包上层目录
        -->
        <context:component-scan base-package="com.alibaba.springExercise.annotation"></context:component-scan>
</beans>
    @Component(value = "createShop")
    public class Shop {
        public void add(){
            System.out.println("Annotation add .....");
        }
    }

    @Test
    public void test1(){
        ApplicationContext context = new ClasspathXmlApplicationContext("bean2.xml");
        Shop createShop = context.getBean("createShop",Shop.class);
        createShop.add();
    }


组件扫描配置

        <!--更加精细化的扫描
        只扫描annotation包下带注解@Component的类(先把use-default-filters置为false)
        -->
        <context:component-scan base-package="com.alibaba.springExercise.annotation" use-default-filters="false">
                <context:include-filter type="annotation" 
                                        expression="org.springframework.stereotype.Component"/>
        </context:component-scan>

        <!--设置不去扫描包下带有@Component的类-->
        <context:component-scan base-package="com.alibaba.springExercise.annotation">
                <context:exclude-filter type="annotation"
                                        expression="org.springframework.stereotype.Component"/>
        </context:component-scan>

**

  • 基于注解方式注入属性

基于注解注入属性无需在目标类中书写其属性对应的set方法

@AutoWired: 根据属性类型进行自动装配
@Qualifier:根据属性名称进行注入
@Qualifier注解的使用要和@AutoWired注解配合使用
@Resource:可以根据属性类型注入,也可以根据名称注入
@Value: 为类的属性注入值

@Controller(value = "userDaoImpltest")
public class UserDaoImpl implements UserDao{
    @Override
    public void sayHello() {
        System.out.println("UserDaoImpl running......");
    }
}
//---------------------------
//认值是类名称,首字母小写
@Component(value = "createShop")
public class Shop {

    @Autowired
    @Qualifier(value = "userDaoImpltest")
    private UserDao userDao;
    
    @Value(value = "tianmao")
    private String name;
    
    public void add(){
        System.out.println("Annotation add ....."+name);
        userDao.sayHello();
    }
}
//---------------------------
    @Test
    public void test2(){
        ApplicationContext context = new ClasspathXmlApplicationContext("bean2.xml");
        Shop shop = context.getBean("createShop",Shop.class);
        shop.add();
    }
  • ** 完全注解开发(不用xml配置文件进行配置)**
//创建配置类,替代xml配置文件
@Configuration
@ComponentScan(basePackages = {"com.alibaba.springExercise.annotation"})
public class SpringConfig {
}

//----------
//认值是类名称,首字母小写
@Component(value = "createShop")
public class Shop {

    @Autowired
    @Qualifier(value = "userDaoImpltest")
    private UserDao userDao;

    @Value(value = "tianmao")
    private String name;

    public void add(){
        System.out.println("Annotation add ....."+name);
        userDao.sayHello();
    }
}

//----------
    @Test
    public void test3(){
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
        Shop createShop = context.getBean("createShop", Shop.class);
        createShop.add();
    }

三. AOP

1.AOP基本概念

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

举例理解AOP:比如设计一个登陆功能模块,正常流程:先输入用户名和密码,然后进行数据库查询,接着进行判断,如果用户存在且密码正确则进入主页面,否则返回登陆界面。
假设现在需要添加一个新的功能,比如要判断一下用户权限,是普通用户还是管理员用户。原始方式:通过直接在源代码上进行修改添加ifelse判断来进行;AOP方式则是把权限判断功能作为一个独立的模块,通过进行某些配置从而使源代码具有权限判断的功能。也就是说AOP可以通过不修改代码的方式添加新的功能

2.AOP底层原理

2.1 AOP底层使用动态代理

(1)有两种情况进行动态代理
第一种:有接口情况,使用JDK动态代理

在这里插入图片描述



第二种:无接口情况,使用cglib动态代理

在这里插入图片描述

2.2 AOP(JDK动态代理方式源码实践)

  1. JDK动态代理,使用Proxy类中的方法创建代理对象,调用Proxy下的newProxyInstance()方法

在这里插入图片描述

参数一:类加载器来定义代理类
参数二:增强方法所在的类,这个类实现的接口,支持多个接口
参数三:实现这个接口InvocationHandler,创建代理对象,写增强的方法

//==========================Agent接口
public interface Agent {
    public int add(int a,int b);
    public void update(String str);
}

//==========================AgentImpl:Agent接口的实现类
public class AgentImpl implements Agent{
    @Override
    public int add(int a, int b) {
        System.out.println("正在执行add方法");
        return a+b;
    }

    @Override
    public void update(String str) {
        System.out.println("String已经更新");
    }
}

//==========================使用Proxy类创建接口代理对象
public class JDKProxyTest {

    public static void main(String[] args) {
        //创建接口类的代理对象
        Class[] interfaces = {Agent.class};
        AgentImpl agentImpl = new AgentImpl();

        Agent getProxy =(Agent) Proxy.newProxyInstance(JDKProxyTest.class.getClassLoader(), interfaces, new AgentProxy(agentImpl));

        int add = getProxy.add(1, 2);
        System.out.println("result:"+add);
        getProxy.update("updateStr");


    }
}

class AgentProxy implements InvocationHandler{

    //1.把要创建的目标接口/类的代理对象的目标/类先传递过来
    //有参数构造进行传递
    private Object object;
    public AgentProxy(Object obj){
        this.object = obj;
    }

    //2.增强/补充的逻辑
    //对被代理对象的每个方法执行前后均执行sout相关操作
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        System.out.println("方法之前执行:"+method.getName()+"传递的参数是:"+ Arrays.toString(args));

        Object res = method.invoke(object, args);

        System.out.println("方法后执行"+object);

        return res;
    }
}

2.3 AOP部分术语

  • 连接点:在一个类中,可以被增强的方法叫做连接点,比如上述AgentImpl类中的add方法就是一个连接点
  • 切入点:连接点是可以被增强的,而切入点指的是实际被增强的,比如上述AgentImpl类有两个方法,假设我们只增强update方法,那么update方法就是一个切入点。
  • 通知/增强:实际增强的逻辑部分叫做通知。比如上述demo中在方法的前后加了两句sout语句,这两句sout语句就叫做通知/增强。

通知有多种类型:前置通知方法前执行)/后置通知方法后执行)/环绕通知方法前后都执行)/异常通知方法异常才执行)/最终通知(类似于try-catch-finally中的finally)

  • 切面:把通知应用到切入点的动作/过程叫做切面

2.4 AOP操作

2.4.1 准备

Spring框架一般是基于AspectJ实现AOP操作。(AspectJ不是Spring组成部分,是一个独立的AOP框架,一般把AspectJ和Spring一起使用)。
基于AspectJ实现AOP操作有(1)基于xml配置文件实现(2)基于注解方式实现(一般使用这种方式)。
在项目工程中引入AOP相关依赖。
切入点表达式:其作用是知道对哪个类里面的哪个方法进行增强;其语法规则为:

execution([权限修饰符][返回类型][类全路径]方法名称)
eg: execution(*com.alibaba.springExercise.aopLearn.AgentImpl.update(…));
eg: execution(com.alibaba.springExercise.aopLearn.AgentImpl.(…));

加入一些依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

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


        <dependency>
            <groupId>aopalliance</groupId>
            <artifactId>aopalliance</artifactId>
            <version>1.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>5.3.8</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-expression</artifactId>
            <version>5.3.8</version>
        </dependency>

2.4.2 基于AspectJ注解方式实现AOP操作
//-----------------1.定义一个被增强类,定义相应方法
//被增强类
@Component(value = "school")
public class School {

    public void add(){
        System.out.println("School class add a new person.");
    }
}

//-----------------2.定义一个增强类,定义增强的方法/也就是通知的逻辑代码
@Component(value = "schoolimprove")
@Aspect  //在增强类上添加该注解,生成代理对象
public class SchoolImprove {

    //前置通知
    //@Before注解表示作为前置通知
    @Before(value = "execution(* com.alibaba.springExercise.aopExercise.School.add(..))")
    public void before(){
        System.out.println("before..........");
    }

    //@After @AfterReturning @AfterThrowing
    @Around(value = "execution(* com.alibaba.springExercise.aopExercise.School.add(..))")
    public void around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        System.out.println("before..........");
        proceedingJoinPoint.proceed();
        System.out.println("after..........");

    }

}


//-----------------3.进行通知的配置
//(1)首先在spring文件中开启注解扫描(通过xml方式/创建配置类的方式)
//(2)使用注解创建School和SchoolImprove对象(见上)
//(3)在增强类上面添加注解@Aspect (见上)
//(4)Spring配置文件中开启生成代理对象的功能

//创建配置类,替代xml配置文件
@Configuration
@ComponentScan(basePackages = {"com.alibaba.springExercise.aopExercise"})
@EnableAspectJAutoproxy(proxyTargetClass = true)//开启生成代理对象的功能
public class SpringConfig {
}
/*
//xml配置文件格式
<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
        ">

        <!--
        开启组件扫描:
                1⃣️如果扫描多个包,可以使用","隔开
                2⃣️扫描包上层目录
        -->
        <context:component-scan base-package="com.alibaba.springExercise.aopExercise"></context:component-scan>

        <!--开启Aspect生成代理对象-->
        <aop:aspectj-autoproxy></aop:aspectj-autoproxy>
</beans>
*/

//-----------------4.配置不同类型的通知
//在增强类的作为通知方法添加通知类型注解,并使用切入点表达式进行配置
//比如下述这样
/*
//前置通知
    //@Before注解表示作为前置通知
    @Before(value = "execution(* com.alibaba.springExercise.aopExercise.School.add(..))")
*/


//-----------------Test
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(SpringConfig.class);
//        ApplicationContext context = new ClasspathXmlApplicationContext("beanaop.xml");
        School school = (School) context.getBean("school");
        school.add();
    }
 

一些tricks:

  • 相同切入点的抽取,上述增强方法中的注解中都需要写增强表达式,实际上这部分是可以抽取出来的,方法如下:
    @pointcut(value = "execution(* com.alibaba.springExercise.aopExercise.School.add(..)")
    public void pointcutDemo(){
    }
  • 假设目前有多个增强类对目标类进行增强,此时就需要设置一下增强类的优先级

可在增强类的上方添加一个注解@Order(num) //num越小优先级越高

2.4.3 基于AspectJ的xml配置文件方式实现AOP操作

这里不赘述了,了解即可,需要用到再回顾即可。

相关文章

php输出xml格式字符串
J2ME Mobile 3D入门教程系列文章之一
XML轻松学习手册
XML入门的常见问题(一)
XML入门的常见问题(三)
XML轻松学习手册(2)XML概念