反射,注解与Spring

本文详细介绍了Spring框架的启动过程、反射的原理与实现方式,以及注解的相关概念。讨论了Spring中的依赖注入,包括setter方法、构造函数、自动注入等方式,并分析了循环依赖的问题。此外,还探讨了Spring中的AOP概念,如代理模式、通知类型,以及事务处理的ACID原则和在Spring中的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Spring

Spring的启动过程

  1. 调用ContextLoader的初始化方法创建webApplicationContext上下文环境
  2. 加载Spring配置文件中的Bean对象【refresh方法】
  3. 将webApplicationContext放入ServletContext【java web全局变量】

在这里插入图片描述

反射

Reflection

反射机制允许程序执行期间获取类的任何内部信息。

反射的作用

在运行时可以知道任意一个类的所有属性,方法,并可以改变他的属性,应用在各种框架

反射的原理

java是一种编译型语言,在运行时,java文件先进行编译为class
这些编译好的class文件中包含类的属性方法信息,反射可以通过这些class文件来获取类的信息
通过不同方式获得的class对象是相同的。每个类只有一个class对象

实现反射的三种方式

  • 通过路径
    Class clazz = Class.forName("com.java.grandland.Old");
  • 通过对象
    Old o = new Old();
    Class clazz = o.getClass();
  • 通过类
    Class clazz1 = Old.class

注解

java.lang.annotation.Annotation

Java中常见的注解

@Override:重载,修饰方法
@Deprecated:废弃的,修饰方法,属性,类
@SuppressWarnings:抑制警告

元注解

用来注解自定义的注解
@Target: 表示自定义注解可以用在哪些地方
@Retenion:表示自定义注解在什么地方还有效【RUNTIME,CLASS,SOURCE】
@Documented:表示是否将我们的注解生成在javadoc中
@Inherited:子类可以继承父类注解

自定义注解

使用@interface声明一个注解
使用@Target和@Retenion定义定义作用位置和时间
只有一个value参数时,使用时可以默认不写

依赖注入

再注入时使用了反射机制生成注入对象

三种方法

  • setter方法
    主要方式
    构造方法声明为无参数的,使用setter注入为其设置对应的值
<bean id="role2" class="com.ssm.chapter9.pojo.Role">
    <property name="roleName" value="高级工程师"/>
    <property name="note" value="重要人员"/>
</bean>
  • 构造函数
    有参构造函数
<bean id="role1" class="com.ssm.chapter9.pojo.Role">
    <constructor-arg index="0" value="总经理"/>
    <constructor-arg index="1" value="公司管理者"/>
</bean>
  • 接口

    有些时候资源并非来自于自身系统,而是来自于外界,比如数据库连接资源完全可以在Tomcat下配置,然后通过JNDI的形式去获取它,这样数据库连接资源是属于开发工程外的资源,这个时候我们可以采用接口注入的形式来获取它

自动注入

  • @Autowired
    spring会在bean容器中寻找能够匹配的Bean,进行自动注入。如果存在多个能够匹配的bean就会出现问题。
    默认按照对象类型进行自动注入。
    自动注入的对象为空时,会抛出异常,使用required参数为false可以允许为null。
  • @Qualifier
    在自动注入时增加@Qualifier,用于缩小匹配范围,指定bean的名字
    在bean上使用@Qualifier,用来进行标注,方便自动注入时引用。
  • @Primary
    在bean上增加@Primary,表示在冲突时首选改bean对象
  • @Resource
    默认按照bean的name进行自动注入,两个参数name和type。也可指定根据类型匹配。

循环依赖

在进行依赖注入时,相互引用会发生循环依赖,导致抛出异常。发生在构造器注入和prototype类型setter注入时。

原因:

  • 因为spring使用构造器来实例化Bean对象,完成实例化之后设置对象属性,使用构造器注入方式会陷入死循环问题,改为使用setter单例注入方式,会先创建Baan对象,之后会以属性的方式注入其他Bean对象。

  • 使用setter注入的prototype方式注入,同样会抛出异常,因为singleton单例作用域时,会在spring初始化时初始化Bean对象,而prototype作用域是懒加载模式,spring容器不进行缓存,在进行注入时无法找到需要的Bean对象。

解决方式:

  • 使用setter注入的singleton模式
  • 使用@Autowired自动注入

AOP

代理模式

动态代理的代理类是动态生成的,不是直接写好的。

分为两类:

  • 基于接口 – JDK动态代理
  • 基于类 – cglib

JDK动态代理使用方法:

  • 实现InvocationHandle

  • 使用Proxy创建代理类

  • 实现invoke方法,对要代理的对象方法进行增强

class StarImpl implements Star{

    @Override
    public void sing() {
        System.out.println("sing");
    }
}

interface Star {
    void sing();
}

class InvocationImpl implements InvocationHandler {
    private Object target;

    public InvocationImpl(Object target) {
        this.target = target;
    }

    public Object getProxy() {
        return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("q3w2132");
        Object o = null;
        try{
            o= method.invoke(target,args);
        } catch(Exception e) {
            e.printStackTrace();
        }
        return o;
    }

    public static void main(String[] args) {
        Star o = (Star)(new InvocationImpl(new StarImpl()).getProxy());
        o.sing();
    }
}

CGLIB方式:

  • 通过字节码技术动态创建子类
  • 在子类中拦截父类方法的调用,织入横切逻辑
  • 采用继承模式,不能对final修饰的类进行代理

区别:

  • CGLIB创建动态代理对象比JDK性能更高,花费时间较长
  • CGLIB适用于单例对象,无需频繁创建
  • CGLIB无法对final修饰的类进行处理

spring中AOP的动态代理技术实现

如果目标对象实现了接口则使用JDK动态代理实现AOP

否则使用CGLIB

AOP

使用了代理模式

  • JoinPoint(连接点)
    目标对象中,所有可以增强的方法,就是spring允许你是通知(Advice)的地方,那可就真多了,基本每个方法的前、后(两者都有也行),或抛出异常是时都可以是连接点,spring只支持方法连接点。
  • Pointcut(切入点)
    目标对象中,已经被增强的方法。调用这几个方法之前、之后或者抛出异常时干点什么,那么就用切入点来定义这几个方法。
  • Advice(通知/增强)
    增强方法的代码、想要的功能。
  • Target(目标对象)
    被代理对象,被通知的对象,被增强的类对象。
  • Weaving(织入)
    将通知应用到连接点形成切入点的过程
  • Proxy(代理)
    将通知织入到目标对象之后形成的代理对象
  • aspect(切面)
    切入点+ 通知。说明了干什么的内容和什么时候干。

几种通知类型

1.前置通知———目标方法运行之前调用
2.后置通知———目标方法运行之后调用(如果出现异常不调用)
3.环绕通知———目标方法之前和之后都调用
4.异常拦截通知———如果出现异常,就会调用
5.后置通知———目标方法运行之后调用(无论是否出现异常都会调用)

事务

事务的ACID原则,要么都成功,要么都失败

  • 原子性

    ​ 原子性操作,一个整体,要么都成功要么都失败

  • 一致性

    ​ 多次执行结果一致

  • 隔离性

    ​ 多个业务操作同一个资源

  • 持久性

    ​ 事务一旦提交,结果不会改变

spring中事务处理

  • 声明式事务
  • 编程式事务
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值