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

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

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

浙公网安备 33010602011771号