其他分享
首页 > 其他分享> > Spring笔记前四天

Spring笔记前四天

作者:互联网

文章目录

Spring学前基础

依赖

image-20220117101843939

1.依赖构成的3个要素

1.1组id

1.2项目名

1.3版本号

2.坐标和本地仓库的关系

根据坐标去本地仓库。查找jar文件

image-20220117102507418

这边都是路径,指向需要的文件

image-20220117104338062

3.如果依赖项报错,首先检查本地仓库的jar包是否正确

maven中jar包依赖的传递性

A依赖B,B依赖C,导入A时BC都会导入

image-20220117103228446

如何实现?

1.根据坐标查找本地仓库的jar包文件

2.查找jar包文件之后,查找**.pom文件**,之后查找里面的依赖项

3.之后根据新坐标查找jar包,查找**.pom文件**,循环操作

image-20220117104437733

pom文件中找dependency中的坐标,然后找jar文件

image-20220117104556103

SHA1算法

SHA-1(英语:Secure Hash Algorithm 1,中文名:安全散列算法1)是一种密码散列函数。SHA-1可以生成一个被称为消息摘要160(20字节)散列值,散列值通常的呈现形式为40个十六进制数。

Hash相关知识

问题1:对相同数据采用相同hash算法,结果是否相同?

答案:相同

hash本质是函数,变量相同,结果相同。如f(x)=x^2

image-20220117110454088

问题2:对不同数据采用相同hash算法,结果是否相同?

答案:可能相同

Hash碰撞

不同的数据结果相同的hash算法,得到了相同的结果,称为hash碰撞。

hash碰撞在数学界没办法规避

image-20220117112314403

数据传输安全保证

如果在jar包传输过程中对jar包做了手脚,如加入木马病毒。

对比前后消息摘要即可,不同则丢弃,重新下载

image-20220117113218571

build标签

  1. build中的插件缺一不可

  2. mainClass中的路径必须与主启动类一致

  3. 如果复制代码,copy dependencies内容即可

maven常用命令

image-20220117115356231

1. clean指令

删除项目中target目录,即删除编译后的文件

image-20220117115323332

2.compile指令

用maven工具编译.java文件–>.class

3.install指令

将开发完的项目打包部署,将项目形成xxx.jar包文件

作用:

​ 1.如果开发的是工具API/框架打成jar包,之后其他用户通过依赖的关系可以使用该jar包

​ 2. 可以将项目打成jar包直接部署,用户可以通过网址直接访问

存储位置:

  1. target目录下 会有jar包文件

    image-20220117134319453

  2. 根据maven坐标,会在本地仓库中生成jar包.

image-20220117134336002

项目发布命令

说明: 未来项目的发布环境都是Linux系统(纯dos命令)
命令: java -jar xxxxx.jar 回车

这边执行的就是上面install生成的jar包,重命名了一下

image-20220117142337896

image-20220117142308148

Spring-IOC

Spring框架是一个开放源代码的J2EE应用程序框架,由Rod Johnson发起,是针对bean的生命周期进行管理的轻量级容器

  1. J2SE java基础 J2EE java企业级开发 J2ME 移动端开发

  2. bean: spring容器管理的对象称之为bean

  3. 生命周期: 对象创建/对象的初始化/对象执行业务逻辑/对象销毁

  4. 轻量级容器: Spring在内存中申请了一块内存空间,用来存储其它对象.

面向接口编程

向上抽取,简化开发,这时接口用来定义行为规范准则

面向接口编程优势:解决了属性与方法名之间的耦合,以后几乎可以不修改属性名

面向接口编程的弊端:接口对象还是一一指向子类对象,后期维护还是要修改源码

Spring-IOC介绍

原理

image-20220117152827083

IOC-Inversion of Control,控制反转,一种思想,对象的创建权利由Spring框架完成,由容器管理对象的生命周期,需要的时候进行依赖注入(DI)即可。

1.XML开发

将Dog对象交给Spring容器管理。交给容器管理的对象也叫bean

说明: spring早期都使用配置文件的方式来管理对象.但是随着软件的升级当下注解的方式已经成为主流. 所以先完成xml配置文件的方式,之后完成注解的方式.
内容说明: xml文件 一般都会有固定的头标签

package demo2;

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

public class SpringGetDog {
    //从spring容器获取Dog对象
    //Dog对象创建好了,配置文件也进行了匹配
    public static void main(String[] args) {
        System.out.println("**********");
        //1.读取Spring配置文件路径
        String resource="spring.xml";
        //2.启动Spring容器
        ApplicationContext context =
                new ClassPathXmlApplicationContext(resource);
        //3.从容器中获取对象
        //这边会应用反射机制创建对象
//        Dog dog  = (Dog) context.getBean("dog");
        System.out.println("***************");
        Dog dog2 = context.getBean(Dog.class);
        System.out.println(dog2);
        dog2.hello();
        //反射机制获取对象
        getDog();
    }
    //实例化对象的核心原理,反射机制
    public static void getDog(){
        try {
            //反射创建对象
            //反射会调用对象的无参构造
            Dog dog = (Dog) Class.forName("demo2.Dog").newInstance();
            dog.hello();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
}
package demo2;

public class Dog {
    public void hello(){
        System.out.println("小狗,spring容器管理");
    }
    public Dog() {
        System.out.println("wucan");
    }
}

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">
     <!-- id是bean的唯一标识符,一般类名首字母小写-->
    <bean id="dog" class="demo2.Dog"></bean>
<!--    <bean id="cat" class="demo2.Cat"></bean>-->
</beans>

输出结果:

image-20220117173402016

2.Spring创建对象的步骤!!!重要!!!

  1. 指定Spring配置文件的地址。
  2. Spring容器启动时加载指定的配置文件。
  3. 当Spring扫描到bean标签时,加载属性id和class
  4. 根据反射机制 根据class的路径反射实例化对象
  5. Spring在内部维护了一个大型的Map<k,v>集合(容器),bean中的id当key,实例化的对象当value。形式:Map<id,实例化对象>
  6. 从Spring容器中通过id或者class类型获取对象
  7. 根据对象调用业务方法

3.注解开发

组成部分:

1.实体类:Spring容器管理的类(对象)

2.配置类:相当于早期的xml配置文件

3.测试代码:利用注解的方式启动Spring容器

package demo3;

public class Cat {
    public Cat(){
        System.out.println("cat的无参构造");
    }
    public void hello(){

        System.out.println("cat miaomiaomiao~");
    }

}
package demo3;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("demo3")//根据指定的包路径扫描注解,扫描当前包和其子包
@Configuration//标识当前类是配置类 其实就是配置文件
public class SpringCatConfigure {
    /*
    * 方法要求:
    *   1.必须为共有的 public
    *   2.必须添加返回值,返回值的对象就是容器管理的对象
    *   3.方法的名称就是bean的id
    *   4.方法必须使用@Bean注解标识,Spring才会执行该方法,标识该对象交给Spring容器
    * */
    @Bean
    public Cat cat(){
        return new Cat();
    }
}
package demo3;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class SpringAnno {
    public static void main(String[] args) {
        ApplicationContext context=
                new AnnotationConfigApplicationContext(SpringCatConfigure.class);
        Cat cat = context.getBean(Cat.class);
        cat.hello();

    }
}

Spring中多例模式@Scope

Spring中的对象默认是单例的

@Scope

单例模式的时候,注意创建对象的位置 是最长的那行代码

原因好像是因为饿加载

image-20220118154206861

image-20220118154217993

多例模式的时候,只要加一个Scope注解,然后还是执行上面的代码,

创建对象时执行的代码与单例模式有所不同,因为是懒加载模式

image-20220118154259341

image-20220118154356077

Spring中的懒加载

默认条件下,Spring启动则会创建对象(类比饿汉式)

如果开启了懒加载,则用户什么时候启动,对象就什么时候创建(类比懒汉式)

image-20220118162325587

懒加载之后就是在下面这行代码创建对象

image-20220118162422848

image-20220118162340224

因为多例模式默认是懒加载,所以懒加载只能控制单例模式

Spring的生命周期

阶段划分:对象创建、初始化、执行业务、销毁

注解总结

@ComponentScan("demo3")	//根据指定的包路径扫描注解
@Configuration   		//标识当前类是配置类
@Bean  					//标识该方法返回值交给Spring容器管理 
@Scope("prototype")		//控制单例多例模式
@Lazy					//懒加载
@PostConstruct			//初始化方法,构造方法后执行
@PreDestroy  			///销毁方法,close之后执行

Spring执行步骤!!!!

自己按照事例小结的

1.先写自己需要用的类,如cat dog等等

2.写配置文件,配置注解@Configureation@ComponentScan

​ 配置文件中,加入@Bean把上面写的类交给Spring容器管理

3.写测试类,用Spring容器执行想要执行的内容,先启动Spring容器,加载配置类(2),再用容器获取想要的对象,即获取(1)

Spring依赖注入(DI)

依赖注入案例

Dog类 子类,实现Pet接口

package demo4;

import org.springframework.stereotype.Component;

@Component//将该类交给Spring容器管理 key:dog value:反射机制创建对象
public class Dog implements Pet{
    @Override
    public void hello() {
        System.out.println("小皮狗,汪汪汪!!!");
    }
    public Dog(){
        System.out.println("dog的无参构造");
    }
}

Pet接口

package demo4;

public interface Pet {
    void hello();
}

User类,里面注入Pet接口

package demo4;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.stereotype.Component;
@Component //将User对象交给Spring容器管理
public class User {
    /**
     * 注入:将Spring容器中的对象进行引用!!!
     * @Autowired: 可以将容器中的对象进行注入
     *         1.按照类型注入
     *                 如果注入类型是接口,则自动查找其实现类对象进行注入,本例就可以直接找到 Dog类
     *                 注意:一般Spring框架内部接口都是单实现。(特殊条件下可以多实现)
     *         2.按照名称注入
     */

    @Autowired //Pet接口没有交给Spring,如果不注入,则Spring无法调用此pet对象
    private Pet pet;
    public void hello(){
        pet.hello();
    }
}

配置类

package demo4;

import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@ComponentScan("demo4")
@Configuration
public class SpringConfigure {

}

实现类

package demo4;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringDI {
    public static void main(String[] args) {
        ApplicationContext context=
                new AnnotationConfigApplicationContext(SpringConfigure.class);
        System.out.println("============================================");
        User user = context.getBean(User.class);
        user.hello();
    }
}

image-20220118195508044

注意

1.类交给Spring容器管理之后,就会放在Map里面,key:是类的id

value是用反射机制创建的对象,这个过程会执行类的无参构造

  1. 注入:将Spring容器中的对象进行引用!!!

    @Autowired: 可以将容器中的对象进行注入

    1.按照类型注入

    ​ 如果注入类型是接口,则自动查找其实现类对象进行注入,本例就可以直接找到 Dog类

    ​ 注意:一般Spring框架内部接口都是单实现。(特殊条件下可以多实现)

    2.按照名称注入:@Autowired + @Qualifier

  2. 注意看上面代码的注释,理解代码执行过程。

  3. 注入的优点:不用再去new对象,注解会直接帮助找到实现类的对象过

接口多实现案例@Qulifier

一般条件下,Spring中的接口都是单实现,如果遇到多实现,则会报错如图:

image-20220119091200941

image-20220119091213462

上面都是按照类型注入一个pet,那么这个时候可以按照名称注入,加注解即可:

image-20220119092850541

注解总结

  1. @ComponentScan(“demo3”) //根据指定的包路径扫描注解

  2. @Configuration //标识当前类是配置类

  3. @Bean //标识该方法返回值交给Spring容器管理

  4. @Scope(“prototype”) //控制单例多例模式

  5. @Lazy //懒加载

  6. @PostConstruct //初始化方法,构造方法后执行

  7. @PreDestroy ///销毁方法,close之后执行

  8. @Component //将当前类的未来对象交给Spring管理

  9. @Autowired //按照类型注入

  10. @Qualifier(“cat”) //按照名称进行注入

MVC设计思想

传统代码结构

如果将所有的业务代码都写到一个方法中。导致后期维护耦合度高。为了提高扩展性,将程序按照MVC设计思想进行管理。

MVC设计思想说明

M:Model 数据层

V:View 视图层

C:Control 控制层

MVC的主要目的就是降低代码的耦合性,提高扩展性,方便后续开发

image-20220119101406061

后端”三层“代码结构

基于MVC设计思想的启发,在后端为了提高代码的扩展性,一般将后端代码分为三层。

  1. Controller层:主要与页面进行交互 @Controller
  2. Service层:主要实现后端的业务逻辑 @Service
  3. Dao层/Mapper层:主要与数据库进行交互,也称为持久层 @Repository/@Mapper

代码结构说明

  1. 包名: mapper 类2个 一个接口UserMapper/一个实现类 UserMapperImpl

  2. 包名: service 类2个 一个接口UserService/ 一个实现类UserServiceImpl

  3. 包名: controller 一个类: UserController

    **知识说明: 被调用的一般会有接口和实现类,接口是为了方便被调用,解耦 **

image-20220119135547316

配置

package demo5.configure;

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

@Configuration
@ComponentScan("demo5")
public class SpringConfig {
}

控制层

package demo5.Controller;

import demo5.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class UserControl {
    @Autowired
    private UserService userService;

    public void addUser(){
        userService.addUser();
    }
}

Mapper层接口

package demo5.mapper;

public interface UserMapper {
    void addUser();
}

Mapper接口实现类

package demo5.mapper;

import org.springframework.stereotype.Repository;

@Repository //标识持久层,该类交给Spring容器管理,里面还是一个@Component。
            //key:userMapperImpl value:对象
public class UserMapperImpl implements UserMapper {
    @Override
    public void addUser() {
        System.out.println("增加用户!!!");
    }
}

Service层接口

package demo5.service;

public interface UserService {
    void addUser();
}

Service层接口实现类,里面注入Mapper层

package demo5.service;

import demo5.mapper.UserMapperImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService{

    @Autowired      //注入mapper层
    private UserMapperImpl userMapper;  //IOC+DI  解耦

    @Override
    public void addUser() {
        userMapper.addUser();
    }
}
package demo5;

import demo5.Controller.UserControl;
import demo5.configure.SpringConfig;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Spring_MVC {
    public static void main(String[] args) {
        ApplicationContext context=
                new AnnotationConfigApplicationContext(SpringConfig.class);
        UserControl userControl = context.getBean(UserControl.class);
        userControl.addUser();
    }
}

注解总结

  1. @ComponentScan(“demo3”) //根据指定的包路径扫描注解

  2. @Configuration //标识当前类是配置类

  3. @Bean //标识该方法返回值交给Spring容器管理

  4. @Scope(“prototype”) //控制单例多例模式

  5. @Lazy //懒加载

  6. @PostConstruct //初始化方法,构造方法后执行

  7. @PreDestroy ///销毁方法,close之后执行

  8. @Component //将当前类的未来对象交给Spring管理

  9. @Autowired //按照类型注入

  10. @Qualifier(“cat”) //按照名称进行注入

  11. @Service //标识Service层

  12. @Controller //标识Controller层

  13. @Repository //标识持久层

  14. @Value ("${key}") //给属性赋值

  15. @PropertySource(value = “classpath:/addUsers.properties”,encoding = “UTF-8”) //加载指定位置的配置文件

Properties文件赋值

#1.数据结构 key=value
#2.无需加引号
#3.注意多余的空格
#4.程序读取文件时,默认采用ISO-8859-1编码,中文必定乱码
name=王老五
package demo6.Mapper;

import com.sun.org.apache.bcel.internal.util.ClassPath;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Repository;

@Repository
@PropertySource(value = "classpath:/addUsers.properties",encoding = "UTF-8") //Spring加载配置文件 key:name value:...
public class UserMapperImpl implements UserMapper {

    //给变量赋值

    @Value("${name}")
    private String name;

    @Override
    public void addUser() {
        System.out.println("增加一个用户,这边是usermapperimpl"+name );
    }
}

IOC-DI小结

IOC:控制反转(Inversion of Control),将对象交给Spring容器 管理,由容器管理对象的生命周期

DI:依赖注入(Dependency Injection),类型/名称注入,为当前的对象注入属性(属性也是对象)

小结:使用IOC和DI可以极大程度上实现代码的松耦合(解耦)

Spring-AOP

AOP代码铺垫

事物特性:

  1. 原子性 2. 一致性 3. 隔离性 4. 持久性

事务说明:

在增删改时添加事务控制

image-20220119172634439

结论:

  1. 如果按照上述的代码进行编辑,则所有增/删除/修改操作的代码都必须按照上述的规则.那么代码冗余.
  2. UserService与事务控制代码紧紧的耦合在一起.不方便后期扩展. 以后尽可能保证业务的纯粹性.

代理思想

在业务层不方便做,但是又不得不做的事情,可以放在代理对象中。通过这样的设计就可以解决业务层耦合的问题、代理对象看起来和真实的对象一米一样。所以用户使用不会察觉。

image-20220119173556732

类比:1.外卖也是一种典型的代理思想2.游戏代练3.房屋中介

动态代理-JDK代理

配置文件

package com.jt.demo1.configre;

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

@Configuration
@ComponentScan("com.jt.demo1")
public class SpringConfigure {
}

生成代理

package com.jt.demo1.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class JDKProxy {

    /*
        获取代理对象
        参数说明:public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        1.ClassLoader loader:读取真实的类数据
        2.Class<?>[] interfaces:要求传递接口的信息
        注意事项:JDK代理要求被代理者要么有接口(本身就是接口) 要么实现接口(实现类)
        3.InvocationHandler h:当代理对象执行方法时执行
    */

    public static Object getProxy(Object target){
        //1/获取类加载器
        ClassLoader classLoader = target.getClass().getClassLoader();
        //2.获取接口,明确类一定有接口 不然数组会空
        Class<?>[] interfaces = target.getClass().getInterfaces();
        //获取代理对象
        return Proxy.newProxyInstance(classLoader,interfaces,getInvocationHandler(target));
    }
    //代理对象执行方法时调用
    public static InvocationHandler getInvocationHandler(Object target){
        //写死的代码
        return new InvocationHandler() {
            @Override
            public Object invoke(Object o, Method method, Object[] objects) throws Throwable {

                System.out.println("事务开始");
                //执行真实对象的方法
                Object result = method.invoke(target, objects);
                System.out.println("事务结束");
                return result;
            }
        };
    }

}

业务接口

package com.jt.demo1.service;

public interface UserService {
    void addUser();
}

业务实现类

package com.jt.demo1.service;

import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("增加用户");
    }

}
package com.jt.demo1;

import com.jt.demo1.configre.SpringConfigure;
import com.jt.demo1.proxy.JDKProxy;
import com.jt.demo1.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringTest {
    public static void main(String[] args) {
        ApplicationContext context=
                new AnnotationConfigApplicationContext(SpringConfigure.class);
        UserService userService = context.getBean(UserService.class);
        Class c = UserService.class;
        System.out.println(userService);
        //获取代理对象
        UserService proxy =(UserService) JDKProxy.getProxy(userService);
        System.out.println(proxy.getClass());//调出类 没有执行里面的方法
        System.out.println(proxy); //proxy.toString()  默认调了方法 所以执行事务
        proxy.addUser();
    }
}

代码输出结果:

image-20220119203539101

JDK动态代理特点

  1. 类型名称: class com.sun.proxy.$Proxy9
  2. 要求: 要求被代理者,必须是接口或者是实现类.
  3. JDK代理是java原生提供的API 无需导包.
  4. JDK动态代理在框架的源码中经常使用.

CGlib动态代理

CGLib特点说明
历史原因: JDK动态代理要求必须"有接口",但是某些类它没有接口,则无法使用JDK代理生成代理对象. 所以为了填补知识的空缺,则引入cglib代理.

问题说明: cglib动态代理 要求有无接口都可以创建代理对象. 问题? 如何保证和被代理者"相同"
答案(特点): 要求cglib动态代理继承被代理者.代理对象是被代理者的子类.

动态代理的作用

说明1: 一般我们将业务层中的耦合性高的代码,采用动态代理的方式进行解耦.使得程序更加具有扩展性. (业务逻辑的解耦)
说明2: Spring专门针对动态代理的规则.封装了一套API 起名 AOP

Spring中的AOP

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

总结: Spring中的AOP 利用代理对象在不修改源代码的条件下,对方法进行扩展.

AOP中专业术语(难点)

1).连接点: 用户可以被扩展的方法
2).切入点: 用户实际扩展的方法
3).通知: 扩展方法的具体实现
4).切面: 将通知应用到切入点的过程

AOP 入门案例

1.导入jar包



org.springframework.boot
​ spring-boot-starter-aop

2切入点表达式

image-20220120133031823

image-20220121091055651

*  切面 = 切入点表达式 + 通知方法
*   1.切入点:可以理解为一个if判断
*           判断条件:切入点表达式
*           规则:如果满足表达式 则判断为true,则执行通知方法
*                反之,false,不执行通知方法
*   2.切入点表达式:
*       2.1 bean("对象的Id")  每次拦截只拦截一个(匹配)
*       2.2 within(“包名.类名”)
*       说明:上面两个粗粒度 按类匹配
*       2.3 execution(返回值类型 包名.类名.方法名(参数列表))
*       2.4 @annotation(注解包路径)
*
*
*
*
* */

@Pointcut("bean(userServiceImpl)")        //只匹配id为userServiceImpl的类
@Pointcut("within(com.jt.demo2.service.*)") //匹配所有类
@Pointcut("execution(* com.jt.demo2.service..*.*(..))") //service下面所有包里的类,所有方法,任意参数。就是扫描了service里的所有
@Pointcut("execution(* com.jt.demo2.service..*.delete(..))") //delete开头的方法,这边没有完全满足表达式。所以没有前置通知,但是代理对象
@Pointcut("@annotation(com.jt.demo2.anno.Haixin2022)")

3.demo

自定义注解

package com.jt.demo2.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)//注解在方法上使用
@Retention(RetentionPolicy.RUNTIME)//运行期有效
public @interface Haixin2022 {  //注解起标记作用

}

Spring-AOP

package com.jt.demo2.aop;


import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component  //交给Spring容器管理
@Aspect  //一个切面类
public class SpringAOP {
    /*
    *  切面 = 切入点表达式 + 通知方法
    *   1.切入点:可以理解为一个if判断
    *           判断条件:切入点表达式
    *           规则:如果满足表达式 则判断为true,则执行通知方法
    *                反之,false,不执行通知方法
    *   2.切入点表达式:
    *       2.1 bean("对象的Id")  每次拦截只拦截一个(匹配)
    *       2.2 within(“包名.类名”)
    *       说明:上面两个粗粒度 按类匹配
    *       2.3 execution(返回值类型 包名.类名.方法名(参数列表))
    *       2.4 @annotation(注解包路径)
    *
    *
    *
    *
    * */

//    @Pointcut("bean(userServiceImpl)")        //只匹配id为userServiceImpl的类
//    @Pointcut("within(com.jt.demo2.service.*)") //匹配所有类
//    @Pointcut("execution(* com.jt.demo2.service..*.*(..))") //service下面所有包里的类,所有方法,任意参数。就是扫描了service里的所有
//    @Pointcut("execution(* com.jt.demo2.service..*.delete(..))") //delete开头的方法,这边没有完全满足表达式。所以没有前置通知,但是代理对象
    @Pointcut("@annotation(com.jt.demo2.anno.Haixin2022)")
    public void pointcut(){

    }
    /*
    * 定义通知方法:
    *   1.前置通知 在目标方法执行之前执行
    *   2.后置通知 在目标方法执行之后执行
    *   3.异常通知 在目标方法执行之后抛出异常时执行
    *   4.最终通知 都要执行的通知
    *   5.环绕通知 在目标方法执行前后都要执行的通知
    *
    * */
    @Before("pointcut()")
    public void before(){//前置通知
        System.out.println("你好~我是前置通知");
    }

}

配置文件

package com.jt.demo2.configre;

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

@Configuration
@ComponentScan("com.jt.demo2")
@EnableAspectJAutoProxy //让Spring中的AOP生效
public class SpringConfigure {
}

接口

package com.jt.demo2.service;

public interface UserService {
    void addUser();
    void deleteUser();
}

实现类

package com.jt.demo2.service;

import com.jt.demo2.anno.Haixin2022;
import org.springframework.stereotype.Service;

@Service
public class UserServiceImpl implements UserService {

    @Override
    public void addUser() {
        System.out.println("增加用户");
    }

    @Override
    @Haixin2022  //标记作用
    public void deleteUser(){
        System.out.println("删除用户");

    }

}

启动类

package com.jt.demo2;

import com.jt.demo2.configre.SpringConfigure;
import com.jt.demo2.service.UserService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringAOPDemo {
    public static void main(String[] args) {
        ApplicationContext context=
                new AnnotationConfigApplicationContext(SpringConfigure.class);
        //根据接口获取实现类对象,但与切入点表达式 匹配,为了后续扩展方便,apo内部动态生成代理
        UserService userService = context.getBean(UserService.class);
        //如果是实现类对象,则方法没有被扩展
        //如果是代理对象,则方法被扩展 aop有效
        System.out.println(userService.getClass());
        userService.addUser();

    }
}

AOP的形象化比喻

AOP是一种抽象性的概念

image-20220120133235755

动态获取注解参数

1.自定义注解

package com.jt.demo2.anno;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)//注解在方法上使用
@Retention(RetentionPolicy.RUNTIME)//运行期有效
public @interface Find {
    int id() default 0;  //int id(); 方法 默认值为0
}

2.使用注解

image-20220121092213922

3.需求说明

利用前置通知,打印注解中的id值

package com.jt.demo2.aop;


import com.jt.demo2.anno.Find;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component  //交给Spring容器管理
@Aspect  //一个切面类
public class SpringAOP {
    /*
    *  切面 = 切入点表达式 + 通知方法
    *   1.切入点:可以理解为一个if判断
    *           判断条件:切入点表达式
    *           规则:如果满足表达式 则判断为true,则执行通知方法
    *                反之,false,不执行通知方法
    *   2.切入点表达式:
    *       2.1 bean("对象的Id")  每次拦截只拦截一个(匹配)
    *       2.2 within(“包名.类名”)
    *       说明:上面两个粗粒度 按类匹配
    *       2.3 execution(返回值类型 包名.类名.方法名(参数列表))
    *       2.4 @annotation(注解包路径)
    * */

//    @Pointcut("bean(userServiceImpl)")        //只匹配id为userServiceImpl的类
//    @Pointcut("within(com.jt.demo2.service.*)") //匹配所有类
//    @Pointcut("execution(* com.jt.demo2.service..*.*(..))") //service下面所有包里的类,所有方法,任意参数。就是扫描了service里的所有
//    @Pointcut("execution(* com.jt.demo2.service..*.delete(..))") //delete开头的方法,这边没有完全满足表达式。所以没有前置通知,但是代理对象
    @Pointcut("@annotation(com.jt.demo2.anno.Haixin2022)")
    public void pointcut(){

    }
    /*
    * 定义通知方法:
    *   1.前置通知 在目标方法执行之前执行
    *   2.后置通知 在目标方法执行之后执行
    *   3.异常通知 在目标方法执行之后抛出异常时执行
    *   4.最终通知 都要执行的通知
    *   5.环绕通知 在目标方法执行前后都要执行的通知
    *
    * */
    @Before("pointcut()")
    public void before1(){//前置通知
        System.out.println("你好~我是前置通知");
    }
    /*
    *   知识点:
    *       1.如果切入点表达式只对当前通知有效,则可以按如下方式编辑
    *   要求:
    *       动态拦截find注解并获取id
    *   难点:
    *       动态获取注解对象
    *   解释:
    *       1.@annotation(find) 拦截find变量名称对应类型的注解
    *       2.当匹配该注解之后,将注解对象当做参数传递给find
    *   优势:
    *       可以一步到位获取注解内容,避免了反射代码
    *
    *
    *
    * */
    @Before("@annotation(find)")
    public void before2(Find find){//前置通知
        System.out.println("你好~我是前置通知 find's id"+ find.id());
    }

}

image-20220121100445244

结果:

image-20220121100526809

通知方法

* 定义通知方法:
*   1.前置通知 在目标方法执行之前执行
*   2.后置通知 在目标方法执行之后执行
*   3.异常通知 在目标方法执行之后抛出异常时执行
*   4.最终通知 都要执行的通知
*   

说明:
上述的四大通知一般用于记录程序的运行状态,只做记录

前置通知

package com.jt.demo2.aop;


import com.jt.demo2.anno.Find;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component  //交给Spring容器管理
@Aspect  //一个切面类
public class SpringAOP {
    /*
    *  切面 = 切入点表达式 + 通知方法
    *   1.切入点:可以理解为一个if判断
    *           判断条件:切入点表达式
    *           规则:如果满足表达式 则判断为true,则执行通知方法
    *                反之,false,不执行通知方法
    *   2.切入点表达式:
    *       2.1 bean("对象的Id")  每次拦截只拦截一个(匹配)
    *       2.2 within(“包名.类名”)
    *       说明:上面两个粗粒度 按类匹配
    *       2.3 execution(返回值类型 包名.类名.方法名(参数列表))
    *       2.4 @annotation(注解包路径)
    *

    * */

//    @Pointcut("bean(userServiceImpl)")        //只匹配id为userServiceImpl的类
//    @Pointcut("within(com.jt.demo2.service.*)") //匹配所有类
//    @Pointcut("execution(* com.jt.demo2.service..*.*(..))") //service下面所有包里的类,所有方法,任意参数。就是扫描了service里的所有
//    @Pointcut("execution(* com.jt.demo2.service..*.delete(..))") //delete开头的方法,这边没有完全满足表达式。所以没有前置通知,但是代理对象
    @Pointcut("@annotation(com.jt.demo2.anno.Haixin2022)")
    public void pointcut(){

    }
    /*
    * 定义通知方法:
    *   1.前置通知 在目标方法执行之前执行
    *   2.后置通知 在目标方法执行之后执行
    *   3.异常通知 在目标方法执行之后抛出异常时执行
    *   4.最终通知 都要执行的通知
    *   5.环绕通知 在目标方法执行前后都要执行的通知
    *        记录程序的状态:
    *       1.目标对象的类名 class/类路径
    *       2.目标对象的方法名
    *       3.目标对象的方法的参数信息
    *       4.获取目标对象方法的返回值
    *       5.获取目标对象执行报错的异常信息
    *
    * */
    @Before("pointcut()")
    public void before1(JoinPoint joinPoint){//前置通知
        //1、获取目标的类型
        Class targetClass = joinPoint.getTarget().getClass();
        //2.获取目标对象的路径
        String path = joinPoint.getSignature().getDeclaringTypeName();
        System.out.println("targetclass(类型)="+targetClass);
        System.out.println("path(路径)="+path);
        //3.获取方法名
        String methodName = joinPoint.getSignature().getName();
        System.out.println("metnodname(方法名)="+methodName);
        //3.获取方法参数
        Object[] args = joinPoint.getArgs();
        System.out.println("arg(参数)="+ Arrays.toString(args));
    }
    /*
    *   知识点:
    *       1.如果切入点表达式只对当前通知有效,则可以按如下方式编辑
    *   要求:
    *       动态拦截find注解并获取id
    *   难点:
    *       动态获取注解对象
    *   解释:
    *       1.@annotation(find) 拦截find变量名称对应类型的注解
    *       2.当匹配该注解之后,将注解对象当做参数传递给find
    *   优势:
    *       可以一步到位获取注解内容,避免了反射代码
    *
    *
    *
    * */
//    @Before("@annotation(find)")
    public void before2(Find find){//前置通知
        System.out.println("你好~我是前置通知 find's id"+ find.id());
    }

}

image-20220121103802155

image-20220121103823927

输出结果:

image-20220121103816870

**12月24号下午的视频 整个一个大懵逼,这注释 绕来绕去的 把我绕进去了

握 日 乐

环绕通知

在目标方法执行前后都要执行的通知 (使用率最高)

* */
@Before("pointcut()")
public void before1(JoinPoint joinPoint){//前置通知
    //1、获取目标的类型
    Class targetClass = joinPoint.getTarget().getClass();
    //2.获取目标对象的路径
    String path = joinPoint.getSignature().getDeclaringTypeName();
    System.out.println("targetclass(类型)="+targetClass);
    System.out.println("path(路径)="+path);
    //3.获取方法名
    String methodName = joinPoint.getSignature().getName();
    System.out.println("metnodname(方法名)="+methodName);
    //3.获取方法参数
    Object[] args = joinPoint.getArgs();
    System.out.println("arg(参数)="+ Arrays.toString(args));
}
/*
*   知识点:
*       1.如果切入点表达式只对当前通知有效,则可以按如下方式编辑
*   要求:
*       动态拦截find注解并获取id
*   难点:
*       动态获取注解对象
*   解释:
*       1.@annotation(find) 拦截find变量名称对应类型的注解
*       2.当匹配该注解之后,将注解对象当做参数传递给find
*   优势:
*       可以一步到位获取注解内容,避免了反射代码
*
*
*
* */

// @Before("@annotation(find)")
public void before2(Find find){//前置通知
System.out.println(“你好~我是前置通知 find’s id”+ find.id());
}

}


[外链图片转存中...(img-or0Fca4c-1642764066236)]

[外链图片转存中...(img-OWgSdWcx-1642764066236)]

输出结果:

[外链图片转存中...(img-csW7KBsI-1642764066237)]

**12月24号下午的视频 整个一个大懵逼,这注释 绕来绕去的 把我绕进去了

握 日 乐



### 环绕通知 

在目标方法执行前后都要执行的通知 (使用率最高)



标签:Spring,class,笔记,public,org,import,四天,annotation
来源: https://blog.csdn.net/straker/article/details/122627871