Bootstrap

Spring注解-Spring-boot-SpingAOP & Spring事务

Spring各个模块

在这里插入图片描述

Test
对应spring-test.jar. Spring提供的测试工具, 可以整合JUnit测试, 简化测试环节.
Core Container
​ Spring的核心组件, 包含了Spring框架最基本的支撑.
​ Beans, 对应spring-beans.jar. Spring进行对象管理时依赖的jar包.
​ Core, 对应spring-core.jar, Spring核心jar包.
​ Context, 对应spring-context.jar, Spring容器上下文对象.
​ SpEL, 对应spring-expression.jar, Spring表达式语言.
AOP
​ 面向切面编程, 对应spring-aop.jar.
Aspects
​ AspectJ的具体实现,面向切面编程的另一种实现。对应spring-aspects.jar
Instrumentation
​ 服务器代理接口的具体实现。对应spring-instrument.jar
Messaging
​ 集成messaging api和消息协议提供支持。对应spring-messaging.jar
Data Access/Integration
​ Spring对数据访问层的封装
​ JDBC, 对应spring-jdbc.jar. Spring对jdbc的封装, 当需要使用spring连接数据库时使用. spring-jdbc.jar需要依赖spring-tx.jar.
​ Transactions, 对应spring-tx.jar. 事务管理
​ ORM, 对应spring-orm.jar. spring整合第三方orm框架需要使用的jar包, 例如Hibernate框架.
Web
​ Spring对javax下的接口或类做的扩展功能.
​ spring-web.jar, 对Servlet, filter, Listener等做的增强.
​ spring-webmvc.jar, 实际上就是SpringMVC框架. 需要依赖spring环境和spring-web.jar
学习Spring其实就是在分别学习Spring所有模块的功能。随着Spring框架的不断发展,在Spring官方压缩包中包含的模块对应的jar也越来越多。

IoC/DI

IoC(Inversion of Control)中文名称:控制反转。
DI(dependency injection )依赖注入。属于同一件事情的两个名称。

容器(Container):放置所有管理对象的对象。其本质是在容器对象里面有一个全局Map对象,map对象中放置所有被管理的对象。Spring中容器是指ApplicationContext接口及子接口或实现类。
beans:容器中所有被管理的对象称为beans。如果单说其中一个对象可称为bean。

Spring支持的注解

@Repository、@Service、@Controller、@Configuration都是@Component注解的子注解,作用相同。

注解名称解释
@Component实例化Bean,默认名称为类名收字母变小写。支持自定义名称
@Repository@Component子标签。作用和@Component一样。用在持久层
@Service@Component子标签。作用和@Component一样。用在业务层
@Controller@Component子标签。作用和@Component一样。用在控制器层
@Configuration@Component子标签。作用和@Component一样。用配置类
@Autowired自动注入。默认byType,如果多个同类型bean,使用byName(默认通过属性名查找是否有同名的bean,也可以通过@Qualifier(“bean名称”),执行需要注入的Bean名称)
@Resource非Spring注解。默认byName,如果没找到,使用byType。

创建Spring项目

Spring项目想要运行起来必须包含:

  • spring-context.jar。spring上下文依赖,它依赖了下面的四个jar。
  • spring-core.jar。Spring 核心jar包。它依赖了spring-jcl.jar
  • spring-aop.jar。Spring AOP基本支持。
  • spring-expression.jar。Spring的表达式语言支持。
  • spring-beans.jar。Spring容器的bean管理。
  • spring-jcl.jar。Spring 4版本时是common-logging.jar。从5开始Spring自己对日志进行了封装。
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>5.3.16</version>
        </dependency>
    </dependencies>

AOP基本概念

功能: 不修改源码的基础上就能添加新的业务逻辑
AspectJ实现AOP

概念:
Pointcut:切点。就是表达式,通过表达式说明哪些方法是join point
join point: 切入点。就是我们平时说的目标方法,或说对哪个方法做扩展,做增强。
Advice:通知,增强内容。
Weaving:织入。织入就是把Advice添加到join point的过程。
Aspect:切面。即join point + Advice

注解实现

1.导入jar包

<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2.明确切入点(要增强的方法)

package org.example.service;
//切入点
public class DemoServiceImpl {
    public void show(){
        System.out.println("abc");
    }
}

3.编写通知类,并在停止类上加上@Aspect注解表识这是一个通知类和@Component注解将这个类的实例对象放入spring容器中

@Component
@Aspect
public class MyAdvice{
	@Pointcut("execution(* org.example.service.DemoServiceImpl.show())")//切点
    public void pointCut(){
        //配置切点
    }

	//前置通知,在切入点执行前执行该方法
    @Before("pointCut()")
    public void before(){
        System.out.println("hello");
    }
	//后置通知,在切入点执行之后,执行该方法
    @After("pointCut()")
    public void after(){
        System.out.println("after");
    }
	//环绕通知
    @Around("pointCut()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    	//环绕前执行:在@Before之前执行
        System.out.println("around" + "before");
        
        //使用joinPoint.proceed()执行切入点,该方法可以拦截目标方法的返回值,可以对返回值处理后,在该方法的最后返回
        Object proceed = joinPoint.proceed();
        
        //环绕后执行:在@After之前执行,有异常不执行
        System.out.println("around" + "after");
        
        //返回拦截处理后的返回值
        return proceed;
    }
    //	在目标方法执行完成,并返回一个返回值后实施增强。
    @AfterReturning(value = "pointCut()")
    public void afterReturning() {
        System.out.println("AfterReturning advice...");
    }
	//目标方法执行时出现异常后执行
    @AfterThrowing(value = "pointCut()")
    public void afterThrowing() {
        System.out.println("AfterThrowing advice...");
    }
}

代理模式

代理模式是Java常见的设计模式之一。
简单来说: 想在A类中调用B类的实例方法,但是不直接调用,而是调用C类实例的方法a(),同时在a()中调用B类实例的方法并对其做一定程度的增强.这就是代理模式

代理设计模式包括:静态代理和动态代理。

静态代理: 代理对象由程序员自己编写

//1.创建接口
public interface MyInterface {
    void testMethod();
}
//2.创建实际要调用的对象
//真实对象
public class MyTrueClass implements MyInterface{
    @Override
    public void testMethod() {
        System.out.println("我是方法.....");
    }
}

//3.创建代理对象
public class MyStaticProxyClass implements MyInterface {
    //代理方法
    @Override
    public void testMethod() {
        //其他代码
        System.out.println("扩展代码上...");
        //调用真实对象的真实方法
        MyTrueClass myTrueClass=new MyTrueClass();
        myTrueClass.testMethod();
        //其他代码
        System.out.println("扩展代码下....");
    }
}
4.测试
public class Test {
    public static void main(String[] args) {
        //传统方式:直接调用
        MyInterface myInterface=new MyTrueClass();
        myInterface.testMethod();
        //代理方式: 间接调用,调用代理对象的代理方法
        MyInterface myInterface1=new MyStaticProxyClass();
        myInterface1.testMethod();
    }
}

1.JDK动态代理

//1.创建接口
interface MyInterface{
    void show();
}
//2.创建实际要调用的对象
//真实对象
class MyTrueObject implements  MyInterface{
    @Override
    public void show() {
        System.out.println("hello world");
    }
}
//2.创建代理对象
class MyjdkProxy{
    public static Object getProxyInstance(Object tag){
        return Proxy.newProxyInstance(MyjdkProxy.class.getClassLoader(),
                new Class[]{MyInterface.class},
                new InvocationHandler() {
                    //这个方法的左右就是将原方法暴露给用户,方便对其进行功能加强
                    /*
                    这个方法的作用就是暴露给用户进行功能扩展方法
                    proxy:代理类对象
                    method:需要调用的真实对象中方法对象
                    args:用户输入的参辫
                    */
                    @Override
                    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                        //扩展功能
                        System.out.println(new Date() + " " + "okk");
                        //通过反射调用
                        Object invoke = method.invoke(tag, args);
                        return invoke;
                    }
                });
    }
}
//测试获取代理对象
public class Teachher {
    public static void main(String[] args) {
        MyInterface proxyInstance = (MyInterface) MyjdkProxy.getProxyInstance(new MyTrueObject());
        proxyInstance.show();
    }
}

流程: Proxy.newProxyInstance(MyjdkProxy.class.getClassLoader(),new Class[]{MyInterface.class},new InvocationHandler() {}
第一个参数获取类加载器
第二个参数传入真实调用对象实现的接口,用来在内部创建一个和真实调用对象一样的实现这个接口的所有方法的类,在类中调用invoke()方法,这个invoke()调用的就是在第三个参数中重写的invoke()方法.

第三个参数: 重写invoke方法

2.Cglib动态代理
Cblig动态是第三方提供的技术,需要导入jar包,并且是基于继承的。

	<dependencies>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>
    </dependencies>
class ClassTrue{
    public void show(){
        System.out.println("hello world");
    }
}

public class Student  {
    public static Object getProxy(Object tag){
        //创建Enhancer对象
        Enhancer enhancer = new Enhancer();
        //设置父类class类型
        enhancer.setSuperclass(ClassTrue.class);
        //设置回调函数
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
                //扩展方法
                System.out.println(new Date());
                //调用真实目标中的方法
                Object invoke = method.invoke(tag, objects);
                //返回
                return invoke;
            }
        });
        //产生代理对象,并返回
        Object o = enhancer.create();
        return o;
    }
}

//测试
class Demo{
    public static void main(String[] args) {
        ClassTrue proxy = (ClassTrue)Student.getProxy(new ClassTrue());
        proxy.show();
    }
}

AOP底层代理模式: SpringAOP底层默认使用的JDK动态代理,但是同时也支持cglib动态代理。需要配置Cglib的依赖以及在Spring的配置文件中开启Cglib动态代理

Spring声明式事务

声明式事务是基于AOP实现的。程序员只需要编写调用持久层代码和业务逻辑代码。把开启事务的代码放在前置通知中,把事务回滚和事务提交的代码放在了后置通知中

springboot:
1.在启动类上加入@EnableTransactionManagement //开启事务管理注解模式 最新的版本可以省略
2.只需要在方法上增加注解@Transactional

事务传播行为

1.默认情况下都认为每个方法都是没有事务的。
​2.整个调用最终都是在调用者里面统一提交回滚。
3.在声明式事务中,如果是同一个类的多个方法相互调用,所有事务传播行为只生效第一个,其他跟随主要在NEVER和REQUIRED_NEW中出现。
​4.如果希望测试效果,必须把方法放入到多个不同的类中进行测试。

**REQUIRED:**默认值。如果当前有事务则加入到事务中。如果当前没有事务则新增事务。
**SUPPORTS:**如果当前有事务就在事务中执行,如果当前没有事务,就在非事务状态下执行.
**NEVER:**必须在非事务状态下执行,如果当前没有事务,正常执行,如果当前有事务,报错.
**NOT_SUPPORTED:**必须在非事务下执行,如果当前没有事务,正常执行,如果当前有事务,把当前事务挂起.
**MANDATORY:**必须在事务内部执行,如果当前有事务,就在事务中执行,如果没有事务,报错.(可以配置在入口方法)
**REQUIRES_NEW:**必须在事务中执行,如果当前没有事务,新建事务,如果当前有事务,把当前事务挂起. 在重新建一个事务。(调用者统一提交回滚),两个事务
NESTED:必须在事务状态下执行.如果没有事务,新建事务,如果当前有事务,创建一个嵌套事务.

事务四大特性(ACID):

原子性: 原子性是指事务包含的所有操作要么全部成功,要么全部失败回滚,
一致性: 事务开始前和结束后,数据库的完整性约束没有被破坏。比如A向B转账,不可能A扣了钱,B却没收到。
隔离性: 多个并发事务之间要相互隔离。
持久性: 持久性是指一个事务一旦被提交了,那么对数据库中的数据的改变就是永久性的.

事务隔离级别

出现的问题:
脏读:
事务A没有提交事务,事务B读取到事务A未提交的数据,这个过程称为脏读。读取到的数据叫做脏数据。
不可重复读:(针对数据的修改)
当事务A读取到表中一行数据时,同时另一个事务修改这行数据,事务A读取到的数据和表中真实数据不一致。
幻读:(针对数据的添加)
事务A对表做查询全部操作,事务B向表中新增一条数据。事务A查询出来的数据和表中数据不一致,称为幻读。

对应的解决问题的事务的隔离级别:
READ_UNCOMMITTED:读未提交(存在问题: 脏读,幻读,不可重复读)
READ_COMMITTED:读已提交(存在问题: 幻读,不可重复读)
REPEATABLE_READ:可重复读(存在问题: 幻读)
SERIALIZABLE: 串行读来通过牺牲性能解决脏读、不可重复度、幻读问题。

Bean的生命周期

实例化 -> 属性赋值 -> 初始化 -> 销毁

;