spring

spring学习笔记

1、IoC思想的引入

IoC控制反转:是一种开发的设计模式,是一种思想。

​ 反转:1)将硬编码中对象new创建的权力交出去。2)将硬编码中对象间关系的维护权交出去。

IoC的作用:1)解决代码开闭原则。2)解决依赖倒置原则。

IoC实现的手段:DI 依赖注入 --

​ set方法注入属性值

​ 构造方法注入属性值

2、spring简介

核心思想:控制反转IoC 和 AOP切面编程

spring core 核心基石 spring aop 次级 其他组件都是建立在这2个基础上开发的。

3、spring入门程序

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

    <!--
        1.spring的配置文件,配置文件的模板
        2.文件名称可以随意,可以是其他的名称
        3.这个文件放在类路径下 resources目录下,方便后续项目移植
        4.配置bean,这样spring才可以帮助我们管理对象(一个类单独使用一个bean标签管理起来)
     -->


    <!-- bean 标签的重要属性:
          id: 标识这个bean对象的唯一属性值,不能重复
          class:必须填写类的全限定路径(带包名的路径)
     -->
    <bean id="user" class="com.cjb.spring.bean.User" />
    ......

</beans>

3.2 创建一个实体类对象(bean)

package com.cjb.spring.bean;

/**
 * 一个bean,封装用户信息
 * @author caijianbo
 * @ClassName User
 * @Date 2023/4/24 22:34
 * @version: 1.0
 **/
public class User {

}

3.3 创建测试类,测试spring创建对象

package com.cjb.spring.test;

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

/**
 * @author caijianbo
 * @ClassName FirstSpringTest
 * @Date 2023/4/24 22:43
 * @version: 1.0
 **/
public class FirstSpringTest {

    @Test
    public void testFirstSpring(){
        /*
         * 获取spring容器对象
         *
         * new ClassPathXmlApplicationContext("spring.xml");
         *      启动spring配置文件
         *      获取配置文件的上下文对象,并且实例化容器中的所有bean对象。
         *
         */
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        // 2.通过对象获取容器中的bean对象, user 就是配置文件中的 id 属性值
        Object user = ctx.getBean("user");
        System.out.println(user);
    }
}

image-20230424225451061

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in class path resource [spring.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.cjb.spring.bean.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.cjb.spring.bean.User.()

原因: 类声明的时候,缺少默认的无参构造方法,spring实例化对象的时候是通过反射机制,调用对象的无参构造方法创建对象的。如果类中没有无参构造方法,则创建实例失败。

org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'user' defined in class path resource [spring.xml]: Instantiation of bean failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.cjb.spring.bean.User]: No default constructor found; nested exception is java.lang.NoSuchMethodException: com.cjb.spring.bean.User.<init>()

	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateBean(AbstractAutowireCapableBeanFactory.java:1320)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1214)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:882)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)

4、spring整合Log4j2日志框架

4.1 引入jar包

		<dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-core</artifactId>
            <version>2.3</version>
        </dependency>

        <dependency>
            <groupId>org.apache.logging.log4j</groupId>
            <artifactId>log4j-to-slf4j</artifactId>
            <version>2.11.1</version>
        </dependency>

4.2 配置log4j2文件

配置文件的名称必须是log4j2.xml,且配置文件必须放置在根路径resources下

<?xml version="1.0" encoding="UTF-8"?>

<!-- status : 指定log4j本身的打印日志的级别.ALL< Trace < DEBUG < INFO < WARN < ERROR
< FATAL < OFF。 monitorInterval : 用于指定log4j自动重新配置的监测间隔时间,单位是s,最小是5s. -->
<Configuration>
    <!-- 配置记录器的日志输出级别   -->
    <loggers>
        <root level="DEBUG">
            <appender-ref ref="springLog"/>
        </root>
    </loggers>

    <appenders>
    <!--  日志输出到控制台      -->
        <console name="springLog" target="SYSTEM_OUT">
        <!--   控制台日志输出的格式         -->
            <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss SSS} [%t] %-3level %logger{1024} - %msg%n"/>
        </console>

    </appenders>

</Configuration>

4.3 如何在自己编写的代码中记录日志

// public static org.slf4j.Logger getLogger(java.lang.Class<?> clazz) { /* compiled code */ }
Logger logger = LoggerFactory.getLogger(FirstSpringTest.class);

image-20230425221633049

5、代理proxy

代理能解决的问题:

1.保护目标对象

2.实现目标对象方法增强

代理有三个重要角色:1.目标对象 ;2.代理对象;3.公共接口(目标对象与代理对象实现的公共接口)

代理分类:静态代理和动态代理

5.1静态代理

静态代理缺陷:

1.代理业务接口的时候,有多少个接口就需要写多少个代理类,会造成类爆炸现象

2.目标对象的增强代码,不能做到复用,需要重复编写。

示例

1.业务公共接口:目标对象与代理对象需要实现的接口

package service;


/**
	目标对象与代理对象共同实现的业务接口方法
*/
public interface OrderService {
    // 创建订单
    void createOrder();
    // 修改订单
    void modifyOrder();
    // 查看订单明细
    void detailOrder();
}

2.业务实现类-目标对象业务类:实现了公共业务接口方法

package service;


public class OrderServiceImpl implements OrderService{
    @Override
    public void createOrder() {
        // 模拟业务执行过程的延迟
        try {
            Thread.sleep(800);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已创建");
    }

    @Override
    public void modifyOrder() {
        // 模拟业务执行过程的延迟
        try {
            Thread.sleep(300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单已修改");
    }

    @Override
    public void detailOrder() {
        // 模拟业务执行过程的延迟
        try {
            Thread.sleep(200);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("请查看订单明细");
    }
}

3.业务实现类-代理实现类:实现了公共业务接口

package service;
/*
    代理对象类实现同一个接口
 */
public class OrderServiceProxy implements OrderService{

    // 目标对象,通过构造方式传入目标对象
    private OrderService target;

    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void createOrder() {
        long start = System.currentTimeMillis();
        //调用目标对象的目标方法
        target.createOrder();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end-start)+"毫秒");
    }

    @Override
    public void modifyOrder() {
        long start = System.currentTimeMillis();
        //调用目标对象的目标方法
        target.createOrder();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end-start)+"毫秒");

    }

    @Override
    public void detailOrder() {
        long start = System.currentTimeMillis();
        //调用目标对象的目标方法
        target.createOrder();
        long end = System.currentTimeMillis();
        System.out.println("耗时"+(end-start)+"毫秒");

    }
}

4.测试类

package client;

import service.OrderService;
import service.OrderServiceImpl;
import service.OrderServiceProxy;


public class Client {

    public static void main(String[] args) {
		// 创建目标对象
        OrderService service = new OrderServiceImpl();
        // 创建代理对象
        OrderService serviceProxy = new OrderServiceProxy(service);
        // 代理对象调用方法
        serviceProxy.createOrder();
        serviceProxy.modifyOrder();
        serviceProxy.detailOrder();
    }
}

5.2动态代理

动态代理可以解决的问题:解决静态代理带来的类爆炸问题,所有的代理类都是动态生成的,存储在内存中,在内存中动态生成需要的代理类。JDK动态代理只能代理接口,而CGLIB动态代理 既可以代理接口也可以代理类。

5.2.1-JDK动态代理

5.2.2动态代理-CGLIB动态代理

6、 切面变成的七大术语

6.1 连接点 JoinPoint

连接点,代表的是位置,程序执行流程中,切面织入的位置。

6.2 切点 pointCut(切入点)

实质就是方法,程序执行流程中,真正织入切面的方法。

6.3 通知 advice

通知又叫增强,就是具体要织入的代码

通知包括:

  • 前置通知
    • 在切点(方法)的前面织入
  • 后置通知
    • 切点(方法)的后面织入
  • 环绕通知
    • 切点(方法)的前后都织入
  • 异常通知
    • 出现异常的时候织入
  • 最终通知
    • 在finally语句块中织入

6.4 切面

切点+通知,组成了切面

6.5 织入 weaving

把通知运用到目标对象的过程,叫织入。

6.6 代理对象 proxy

一个目标对象被织入通知后产生的新对象,叫代理对象。

6.7 目标对象 target

被织入通知的对象,叫目标对象。

package service;

public class UserService {
    
    public void  do1(){
        System.out.println("do1");
    }

    public void  do2(){
        System.out.println("do2");
    }
    public void  do3(){
        System.out.println("do3");
    }
    public void  do4(){
        System.out.println("do4");
    }
    public void  do5(){
        System.out.println("do5");
    }
    
    
    public void service(){

        try {
            // joinPoint 连接点  这里可以织入前置通知
            do1();  //pointCut 切点
            // joinPoint 连接点
            do2(); //pointCut 切点
            // joinPoint 连接点
            do3(); //pointCut 切点
            // joinPoint 连接点
            do5(); //pointCut 切点
            // joinPoint 连接点
        } catch (Exception e) {
            // joinPoint 连接点
            // 这里可以织入 异常通知
            e.printStackTrace();
        } finally {
            // joinPoint 连接点
            // 这里可以织入最终通知
        }
    }
}

6.8 切点表达式

execution([访问控制权限修饰符] 返回值类型 [全限定类名] 方法名(形式参数列表) [异常])

[] 代表可选项。

6.8.1 访问控制权限类型

  • 可选项
  • 没写,就是全部权限都包括:public protected、private、default
  • 写public 表是只包含 public 这个类型。

6.8.2 返回值类型

  • 必填项
  • * 代表发回值类型任意。

6.8.3 全限定类名

  • 可选项
  • 两个..代表当前包以及子包下的所有类。
  • 省略时表示所有类。

6.8.4 方法名

  • 必填项。
  • *代表所有方法。
  • set*代表所有以set开头的方法。

6.8.5 形式参数列表

  • 必填项
  • ()表示没有参数。
  • (..)表示参数类型和个数任意。
  • (*)表示只有一个参数
  • (*,String)表示 第一个参数类型任意,第2个参数是String类型。

6.8.6 异常

  • 可选项
  • 省略时表示任意类型异常。

例子

service包以及子包下的所有类中以set开头的公开方法,参数任意

exceution(public * com.service.*.set*(..))

mall包下所有的类的所有方法

exceution(* com.mall..*(..))

所有类的所有方法

exceution(* *(..))

7、 Spring AOP

spring AOP 切面编程底层实质上也是采用动态代理的机制实现的。

7.1 依赖的jar包

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>com.cy</groupId>
    <artifactId>stage_spring_aop_aspects_anno</artifactId>
    <version>1.0.0</version>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
    </properties>


    <dependencies>
        <!-- spring 依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>6.0.9</version>
        </dependency>
		<!-- aop依赖 -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aop</artifactId>
            <version>6.0.9</version>
        </dependency>
		<!-- aspects -->
        <!-- https://mvnrepository.com/artifact/org.springframework/spring-aspects -->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.9</version>
        </dependency>
        <!-- 单元测试 -->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.13.2</version>
            <scope>test</scope>
        </dependency>

    </dependencies>

</project>

7.2 配置spring.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: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">
    <!--  配置spring 注解组件扫描      -->
    <context:component-scan base-package="com.cy.service"/>
    <!--
       aop:aspectj-autoproxy 代表开启aspectj的自动代理功能,spring容器在扫描类时,查看类上是否有@Aspect注解,如果有,则给个类生成代理对象。
       proxy-target-class  为true 代表强制使用CGLIB动态代理 为false,相当于默认,接口则使用JDK动态代理,类则使用CGLIB模式。
      -->
    <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>

7.3 编写一个目标对象类

package com.cy.service;

import org.springframework.stereotype.Service;

/**
 * 目标对象类
 */
@Service
public class UserService {


    public void login(){
        System.out.println("系统正在进行权限验证....");
    }


    public void logOut(){
        System.out.println("系统正在退出.....");
    }
}

7.4 编写一个切面类

package com.cy.service;

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

/**
 * 切面类:增强的日志代码
 * @Aspect 必须要这个注解,缺少就不能作为切面类
 */
@Aspect
@Component("logAspects")
public class LogAspects {


    /*
        切面 = 切点 + 通知
        前置通知
        @Before("execution(* com.cy.service.*(..))")
        execution(* com.cy.service.*(..)) 切点表达式
        public void doBefore(){} 增强代码
     */
    @Before("execution(* com.cy.service..*(..))")
    public void doBefore(){
        System.out.println("我是一个前置通知,我的代码增强了...");

    }

}

7.5 单元测试类

package com.cy;

import com.cy.service.UserService;
import org.junit.Test;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class ServiceTest {
    @Test
    public void test() {
        ApplicationContext ctx = new ClassPathXmlApplicationContext("spring.xml");
        UserService userService = ctx.getBean("userService", UserService.class);
        userService.login();
        userService.logOut();
    }
}

7.6 运行结果

"C:\Program Files\Java\jdk1.8.0_121\bin\java.exe"...
我是一个前置通知,我的代码增强了...
系统正在进行权限验证....
我是一个前置通知,我的代码增强了...
系统正在退出.....

Process finished with exit code 0
posted @ 2023-09-26 22:37  蔡剑波  阅读(26)  评论(0)    收藏  举报