【Spring】javaBean、依赖注入、面向切面AOP、使用注解开发

news2024/12/24 7:45:00

文章目录

  • JavaBean
    • IoC理论基础
      • 使用IoC容器
      • 使用Spring
    • 生命周期与继承
      • 生命周期
      • 继承
    • 依赖注入 Dependency Injection
      • 基本类型注入
      • 非基本类型注入
      • 集合注入
      • 自动装配注入
  • 面向切面AOP
      • 使用SpringAOP
        • 环绕方法
      • 使用接口实现AOP
  • 使用注解开发
      • 注解实现配置文件
      • 注解实现AOP操作
      • 其他注解配置

JavaBean

有一定规范的Java实体类,类内提供了一些公共方法以便外界对该对象的内部属性进行操作

所有属性都是private,所有的属性都可以通过get/set方法进行访问,同时还需要有一个无参构造(默认就有)

public class User{
	private String name;
	private int age;
	public String getName(){
		return name;
	}
	public String getAge(){
		return age;
	}
	public void setName(String name){
		this.name = name;
	}
	public void setAge(int age){
		this.age = age;
	}
}

IoC理论基础

高内聚,低耦合是现代软件的开发的设计模式

之前编写的图书管理系统具有高耦合性,虽然体系逻辑清晰,流程也快,但是当要对一个功能进行修改时,很容易需要大量的时间进行修改;因此如果需要改善这种情况,只能对各个模块进行解耦,降低依赖性,也就是说,所有的实现类对象全部交给程序来管理,对象之间的关系也由程序来动态决定

IOC是Inversion of Control的缩写,翻译为:“控制反转”,把复杂系统分解成相互合作的对象,这些对象类通过封装以后,内部实现对外部是透明的,从而降低了解决问题的复杂度,而且可以灵活地被重用和扩展

image-20230527215914137

可以将对象交给IoC容器进行管理,这时我们就只需要关心接口定义之类

使用IoC容器

使用Spring的首要目的是为了使得软件项目进行解耦,而不是简化代码

Spring并不是一个独立的框架,实际上有很多模块

image-20230527220218571

Spring是一个非入侵式的框架,就像一个工具库一样,因此,我们只需要直接导入其依赖就可以使用

使用Spring

先导入依赖

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.3.19</version>
</dependency>

在resources目录中创建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
        https://www.springframework.org/schema/beans/spring-beans.xsd">

</beans>

在主方法中编写

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("text");   
}

注册JavaBean

写一个类之后,在配置文件中添加bean

<bean name="Student" class="org.example.entity.Student"/>

之后在主方法中使用IoC容器生成

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Student student = context.getBean(Student.class); //直接输入name也行
        System.out.println(student);
    }

输出:说明是可行的

image-20230527221243181

这里得到的Student对象是由Spring通过反射机制进行创建

生命周期与继承

生命周期

<bean name="student" class="com.test.bean.Student"/>

name属性:也可以是id属性,全局唯一,不可出现重复的名称,我们发现,之前其实就是通过Bean的名称来向IoC容器索要对应的对象,也可以通过其他方式获取。

我们现在在主方法中连续获取两个对象:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Student student = context.getBean(Student.class);
        Student student1 = (Student) context.getBean("Student");
        System.out.println(student);
        System.out.println(student1);
    }
}

我们发现两次获取到的实际上是同一个对象

image-20230527221704555

也就是说,默认情况下,通过IoC容器进行管理的JavaBean是单例模式的,

那么如何进行修改:只需要修改其作用域即可,添加scope属性:

<bean name="student" class="com.test.bean.Student" scope="prototype"/>

通过将其设定为prototype(原型模式)来使得其每次都会创建一个新的对象

image-20230527221833758

观察一下这两种模式下Bean的生命周期,我们给构造方法添加一个输出:

public class Student {
    String name;
    int age;

    public Student(){
        System.out.println("我被构造了!");
    }
}

接着在mian方法中打上断点来查看对象分别是在什么时候被构造的

原型模式 prototype

每new一个对象,就构造一次

image-20230527222309492

单例模式 singleton

在一开始的时候就进行构造

image-20230527222512856

我们发现,当Bean的作用域为单例模式,那么它会在一开始就被创建,而处于原型模式下,只有在获取时才会被创建,也就是说,单例模式下,Bean会被IoC容器存储,只要容器没有被销毁,那么此对象将一直存在,而原型模式才是相当于直接new了一个对象,并不会被保存。

我们还可以通过配置文件,告诉创建一个对象需要执行此初始化方法,以及销毁一个对象的销毁方法:

public class Student {
    String name;
    int age;

    public Student() {
        System.out.println("我被构造了");
    }
    public void init() {
        System.out.println("我是初始化");
    }

    public void destroy() {
        System.out.println("我是销毁方法");
    }
}
public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
    Student student = (Student) context.getBean("student");
    System.out.println(student);
    context.close();  //手动销毁容器
}

最后在XML文件中编写配置:

<bean name="student" class="com.test.bean.Student" init-method="init" destroy-method="destroy"/>

看输出

image-20230527223010713

我们还可以手动指定Bean的加载顺序,若某个Bean需要保证一定在另一个Bean加载之前加载,那么就可以使用depend-on属性

添加一个类

public class teacher {
    String name;
    int age;
    public teacher() {
        System.out.println("老师被构造了");
    }
    public void init() {
        System.out.println("老师初始化");
    }

    public void destroy() {
        System.out.println("我是老师的销毁方法");
    }
}

再添加配置文件:此处注意depend-on后跟的是name

image-20230527223400741

最后输出

image-20230527223255279

继承

Bean之间也存在继承关系,是属性的继承

XML文件

虽然是继承但是也可以进行增删改,而不是完全继承

<bean name="Art" class="...">
	<property name="name" value="Alice"/>
</bean>
<bean name="Sports" class="...Sports" parent="Art">
	<property name="id" value="1"/>
</bean>

也可以将一个bean作为抽象的bean,此时就不能直接去获取,有点类似Java当中的抽象类

<bean name="Art" class="..." abstract="true">

依赖注入 Dependency Injection

将bean装配在一起的行为是基于依赖注入的模式实现的,此时,组件不会再去创建它所依赖的组件并管理它们的生命周期,使用依赖注入的应用依赖于单独的实体(容器)来创建和维护所有的组件,并将其注入到需要的bean中,通常通过构造器参数和属性访问(property accessor)方法来实现

基本类型注入

实现向Bean的成员属性进行赋值,使用set方法+property标签来实现

public class Student {
    String name;
    int age;

    public void setName(String name) {
        this.name = name;
    }

    public void say(){
        System.out.println("I'm "+name);
    }
}

在xml文件中设置

        <bean name="Student" class="org.example.entity.Student">
                <property name="name" value="Alice"/>
        </bean>

最后设置主方法

public static void main(String[] args) {
    ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
    Student student = (Student) context.getBean("student");
    student.say();
}

输出:发现属性被成功注入到对象当中

image-20230527225142284

非基本类型注入

如果成员类型是一个非基本类型的对象,应该这样注入

@ToString
public class teacher {
    String name;
    int age;
    public teacher() {
        System.out.println("老师被构造了");
    }

    public void setName(String name) {
        this.name = name;
    }
}

Student类

@ToString
public class Student {
    String name;
    int age;
    teacher t;

    public Student() {
        System.out.println("我被构造了");
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setT(teacher t) {
        this.t = t;
    }
    public void say() {
        System.out.println("I'm " + name);
    }
}

我们只需要将对应的类型也注册为bean即可,然后直接使用ref属性来进行引用:

image-20230527230719613

看输出

image-20230527230656825

集合注入

需要在property内部进行编写:

<bean name="student" class="com.test.bean.Student">
    <property name="list">
        <list>
            <value type="double">100.0</value>
            <value type="double">95.0</value>
            <value type="double">92.5</value>
        </list>
    </property>
</bean>

就可以直接以一个数组的方式将属性注入,注意如果是List类型的话,可以使用array数组。如果是一个Map类型,也可以使用entry来注入:

public class Student {
    String name;
    int age;
    Map<String, Double> map;

    public void setMap(Map<String, Double> map) {
        this.map = map;
    }

    public void say(){
        System.out.println("我的成绩:"+ map);
    }
}
<bean name="student" class="com.test.bean.Student">
    <property name="map">
        <map>
            <entry key="语文" value="100.0"/>
            <entry key="数学" value="80.0"/>
            <entry key="英语" value="92.5"/>
        </map>
    </property>
</bean>

自动装配注入

使用自动装配来实现属性值的注入:

<bean name="card" class="com.test.bean.Card"/>
<bean name="student" class="com.test.bean.Student" autowire="byType"/>

自动装配会根据set方法中需要的类型,自动在容器中查找是否存在对应类型或是对应名称以及对应构造方法的Bean,比如我们上面指定的为byType,那么其中的card属性就会被自动注入类型为Card的Bean

我们已经了解了如何使用set方法来创建对象,那么能否不使用默认的无参构造方法,而是指定一个有参构造进行对象的创建呢?我们可以指定构造方法:

<bean name="student" class="com.test.bean.Student">
        <constructor-arg name="name" value="小明"/>
        <constructor-arg index="1" value="18"/>
    </bean>
public class Student {
    String name;
    int age;

    public Student(String name, int age){
        this.name = name;
        this.age = age;
    }

    public void say(){
        System.out.println("我是:"+name+"今年"+age+"岁了!");
    }
}

通过手动指定构造方法参数,就可以直接告诉容器使用哪一个构造方法来创建对象

面向切面AOP

AOP思想实际上就是:在运行时,动态地将代码切入到类的指定方法、指定位置上的编程思想就是面向切面的编程。也就是说,我们可以使用AOP来帮助我们在方法执行前或执行之后,做一些额外的操作,实际上,就是代理!

通过AOP我们可以在保证原有业务不变的情况下,添加额外的动作,比如我们的某些方法执行完成之后,需要打印日志,那么这个时候,我们就可以使用AOP来帮助我们完成,它可以批量地为这些方法添加动作。可以说,它相当于将我们原有的方法,在不改变源代码的基础上进行了增强处理。

image-20230527231856953

相当于我们的整个业务流程,被直接斩断,并在断掉的位置添加了一个额外的操作,再连接起来,也就是在一个切点位置插入内容。它的原理实际上就是通过动态代理机制实现的,我们在JavaWeb阶段已经给大家讲解过动态代理了。不过Spring底层并不是使用的JDK提供的动态代理,而是使用的第三方库实现,它能够以父类的形式代理,而不是接口

使用SpringAOP

Spring是支持AOP编程的框架之一(实际上它整合了AspectJ框架的一部分),要使用AOP我们需要先导入一个依赖:

<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-aspects</artifactId>
    <version>5.3.13</version>
</dependency>

那么,如何使用AOP呢?首先我们要明确,要实现AOP操作,我们需要知道这些内容:

  1. 需要切入的类,类的哪个方法需要被切入
  2. 切入之后需要执行什么动作
  3. 是在方法执行前切入还是在方法执行后切入
  4. 如何告诉Spring需要进行切入

那么我们依次来看,首先需要解决的问题是,找到需要切入的类:

public class Student {
    String name;
    int age;

		//分别在test方法执行前后切入
    public int test(String str) {
        System.out.println("我是一个测试方法:"+str);
        return str.length();
    }
}

现在我们希望在test方法执行前后添加我们的额外执行的内容,接着,我们来看看如何为方法执行前和执行后添加切入动作。比如现在我们想在方法返回之后,再执行我们的动作,首先定义我们要执行的操作:

public class AopTest {

    //执行之后的方法
    public void after(){
        System.out.println("我是执行之后");
    }

    //执行之前的方法
    public void before(){
        System.out.println("我是执行之前");
    }
}

那么,现在如何告诉Spring我们需要在方法执行之前和之后插入其他逻辑呢?首先我们将要进行AOP操作的类注册为Bean:

<bean name="student" class="com.test.bean.Student"/>
<bean name="aopTest" class="com.test.aop.AopTest"/>

一个是Student类,还有一个就是包含我们要切入方法的AopTest类,注册为Bean后,他们就交给Spring进行管理,这样Spring才能帮助我们完成AOP操作。

接着,我们需要告诉Spring,我们需要添加切入点,首先将顶部修改为,引入aop相关标签:

<?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: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/aop http://www.springframework.org/schema/aop/spring-aop.xsd">

通过使用aop:config来添加一个新的AOP配置:

<aop:config>
    
</aop:config>

首先第一行,我们需要告诉Spring,我们要切入的是哪一个类的哪个或是哪些方法:

<aop:pointcut id="test" expression="execution(* com.test.bean.Student.say())"/>

其中,expression属性的execution填写格式如下:

修饰符 包名.类名.方法名称(方法参数)
  • 修饰符:public、protected、private、包括返回值类型、static等等(使用*代表任意修饰符)
  • 包名:如com.test(*代表全部,比如com.*代表com包下的全部包)
  • 类名:使用*也可以代表包下的所有类
  • 方法名称:可以使用*代表全部方法
  • 方法参数:填写对应的参数即可,比如(String, String),也可以使用*来代表任意一个参数,使用..代表所有参数

也可以使用其他属性来进行匹配,比如@annotation可以用于表示标记了哪些注解的方法被切入

接着,为此方法添加一个执行前动作和一个执行后动作:

<aop:aspect ref="aopTest">
    <aop:before method="before" pointcut-ref="test"/>
    <aop:after-returning method="after" pointcut-ref="test"/>
</aop:aspect>

最后总设置

image-20230531163450701

现在来实验一下吧:

public class Main {
    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring.xml");
        Student student = context.getBean(Student.class);
        student.say();
        context.close();
    }
}

输出

image-20230531163351379

我们发现,方法执行前后,分别调用了对应的方法。但是仅仅这样还是不能满足一些需求,在某些情况下,我们可以需求方法执行的一些参数,比如方法执行之后返回了什么,或是方法开始之前传入了什么参数等等。

这个时候,可以为切入的方法添加一个参数,通过此参数就可以快速获取切点位置的一些信息:

//执行之前的方法
public void before(JoinPoint point){
    System.out.println("我是执行之前");
    System.out.println(point.getTarget());  //获取执行方法的对象
    System.out.println(Arrays.toString(point.getArgs()));  //获取传入方法的实参
}

通过添加JoinPoint作为形参,Spring会自动给我们一个实现类对象,这样我们就能获取方法的一些信息了。

环绕方法

环绕方法相当于完全代理了此方法,它完全将此方法包含在中间,需要手动调用才可以执行此方法,并且可以直接获取更多的参数:

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("方法开始之前");
    Object value = joinPoint.proceed(); //如果没有此语句,则指定方法不会被调用
    System.out.println("方法执行完成,结果为:"+value);
    return value;
}

注意,如果代理方法存在返回值,那么环绕方法也需要有一个返回值,通过proceed方法来执行代理的方法,也可以修改参数之后调用proceed(Object[]),使用我们给定的参数再去执行:

public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("方法开始之前");
    String arg = joinPoint.getArgs()[0] + "lailou";
    Object value = joinPoint.proceed(new Object[]{arg});
    System.out.println("方法执行完成,结果为:"+value);
    return value;
}

使用接口实现AOP

如何使用Advice实现AOP

它与之前学习的动态代理更接近一些,比如在方法开始执行之前或是执行之后会去调用实现的接口,首先需要将一个类实现Advice接口,只有实现此接口,才可以被通知,比如这里使用MethodBeforeAdvice表示是一个在方法执行之前的动作:

public class AopTest implements MethodBeforeAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("通过Advice实现AOP");
    }
}

我们发现,方法中包括了很多的参数,其中args代表的是方法执行前得到的实参列表,还有target表示执行此方法的实例对象。运行之后,效果和之前是一样的,但是在这里我们就可以快速获取到更多信息。

<aop:config>
    <aop:pointcut id="stu" expression="execution(* com.test.bean.Student.say(String))"/>
    <aop:advisor advice-ref="before" pointcut-ref="stu"/>
</aop:config>

除了此接口以外,还有其他的接口,比如AfterReturningAdvice就需要实现一个方法执行之后的操作:

public class AopTest implements MethodBeforeAdvice, AfterReturningAdvice {
    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("我是方法执行之前!");
    }

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("我是方法执行之后!");
    }
}

AOP 领域中的特性术语:

  • 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理,也就是我们上面编写的方法实现。
  • 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出,实际上就是我们在方法执行前或是执行后需要做的内容。
  • 切点(PointCut): 可以插入增强处理的连接点,可以是方法执行之前也可以方法执行之后,还可以是抛出异常之类的。
  • 切面(Aspect): 切面是通知和切点的结合,我们之前在xml中定义的就是切面,包括很多信息。
  • 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
  • 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,我们之前都是在将我们的增强处理添加到目标对象,也就是织入(这名字挺有文艺范的)

使用注解开发

好处:更强的类型安全性和更好的重构能力

注解实现配置文件

那么,现在既然不使用XML文件了,那通过注解的方式就只能以实体类的形式进行配置了,给作为配置的类上添加@Configuration注解,先创建一个新的类MainConfiguration

@Configuration 注解会告知Spring这是一个配置类,会为Spring上下文提供bean

@Configuration
public class MainConfiguration {
    //没有配置任何Bean
}

可以看作:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
		<!-- 没有配置任何Bean -->
</beans>

Bean配置

之前我们是直接在配置文件中编写Bean的一些信息,现在在配置类中,我们只需要编写一个方法,并返回我们要创建的Bean的对象即可,并在其上方添加@Bean注解:

@Bean注解表明这些方法所返回的对象会以Bean的形式添加到 Spring的应用上下文,默认情况下,bean所对应的bean ID与定义它们的方法名称是相同的

@Bean //如果想设置别名,则使用 @Bean("card")
public Card card(){
    return new Card();
}

这样,等价于:

<?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
        https://www.springframework.org/schema/beans/spring-beans.xsd">
		<bean class="com.test.bean.Card"></bean>
</beans>

指定作用域

还可以继续添加@Scope注解来指定作用域,例如:

@Bean 
@Scope("prototype")
public Card card(){
    return new Card();
}

采用这种方式,可以更加方便地控制一个Bean对象的创建过程,相当于这个对象时由我们创建好了再交给Spring进行后续处理,我们可以在对象创建时做很多额外的操作,包括一些属性值的配置等

加载配置类

既然现在我们已经创建好了配置类,接着我们就可以在主方法中加载此配置类,并创建一个基于配置类的容器:

public class Main {
    public static void main(String[] args) {
      	//使用AnnotationConfigApplicationContext来实现注解配置
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class); 
        //这里需要告诉Spring哪个类作为配置类
        Card card = context.getBean(Card.class);  //容器用法和之前一样
        System.out.println(card);
    }
}

在配置的过程中,我们可以点击IDEA底部的Spring标签,打开后可以对当前向容器中注册的Bean进行集中查看,并且会标注Bean之间的依赖关系

Bean的默认名称实际上就是首字母小写的方法名称,可以手动指定:

@Bean("test")
@Scope("prototype")
public Card card(){
    return new Card();
}

还可以直接在类上添加@Component注解来将一个类进行注册**(现在最常用的方式)**,不过要实现这样的方式,我们需要添加一个自动扫描

在配置类上添加一个@ComponentScan注解,也可以使用@ComponentScans来批量添加。这里演示将bean包下的所有类进行扫描:

@ComponentScan("com.test.bean") //表示扫描bean目录中的所有
@Configuration
public class MainConfiguration {

}

现在删除类中的Bean定义,我们在Student类的上面添加@Component注解,来表示此类型需要作为Bean交给容器进行管理:

@Component
@Scope("prototype")
public class Student {
    String name;
    int age;
    Card card;
}

同样的,在类上也可以直接添加@Scope来限定作用域。

效果和刚刚实际上是相同的,我们可以来测试一下:

public class Main {
    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
        System.out.println(context.getBean(Student.class));
    }
}

我们可以看到IDEA的Spring板块中也显示了我们刚刚通过直接在类上添加@Component声明的Bean。

@Component同样效果的还有@Controller@Service@Repository

现在我们就有两种方式注册一个Bean了,那么如何实现像之前一样的自动注入呢,比如我们将Card也注册为Bean,我们希望Spring自动将其注入到Student的属性中:

@Component
public class Student {
    String name;
    int sid;
    Card card;
}

因此,我们可以将此类型,也通过这种方式注册为一个Bean:

@Component
@Scope("prototype")
public class Card {
}

在需要注入的位置,添加一个@Resource注解来实现自动装配:

@Component
public class Student {
    String name;
    int sid;
    
    @Resource
    Card card;
}

这样的好处是,完全不需要创建任何的set方法,只需要添加这样的一个注解就可以了,Spring会跟之前配置文件的自动注入一样,在整个容器中进行查找,并将对应的Bean实例对象注入到此属性中,当然,如果还是需要通过set方法来注入,可以将注解添加到方法上:

@Component
public class Student {
    String name;
    int sid;
    Card card;

    @Resource
    public void setCard(Card card) {
        System.out.println("通过方法");
        this.card = card;
    }
}

除了使用@Resource以外,我们还可以使用@Autowired(IDEA不推荐将其使用在字段上,会出现黄标,但是可以放在方法或是构造方法上),它们的效果是一样的,但是它们存在区别,虽然它们都是自动装配:

  • @Resource默认ByName如果找不到则ByType,可以添加到set方法、字段上。
  • @Autowired默认是byType,可以添加在构造方法、set方法、字段、方法参数上。

并且@Autowired可以配合@Qualifier使用,来指定一个名称的Bean进行注入:

@Autowired
@Qualifier("sxc")
public void setCard(Card card) {
    System.out.println("通过方法");
    this.card = card;
}

如果Bean是在配置文件中进行定义的,我们还可以在方法的参数中使用@Autowired来进行自动注入:

@ComponentScan("com.test.bean")
@Configuration
public class MainConfiguration {

    @Bean
    public Student student(@Autowired Card card){
        Student student = new Student();
        student.setCard(card);
        return student;
    }
}

我们还可以通过@PostConstruct注解来添加构造后执行的方法,它等价于之前讲解的init-method

@PostConstruct
public void init(){
    System.out.println("我是初始化方法!1");
}

注意它们的顺序:Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct

同样的,如果需要销毁方法,也可以使用@PreDestroy注解,这里就不做演示了。

这样,两种通过注解进行Bean声明的方式就讲解完毕了,那么什么时候该用什么方式去声明呢?

  • 如果要注册为Bean的类是由其他框架提供,我们无法修改其源代码,那么我们就使用第一种方式进行配置。
  • 如果要注册为Bean的类是我们自己编写的,我们就可以直接在类上添加注解,并在配置中添加扫描。

注解实现AOP操作

首先在主类添加@EnableAspectJAutoProxy注解,开启AOP注解支持:

@EnableAspectJAutoProxy
@ComponentScan("com.test.bean")
@Configuration
public class MainConfiguration {
}

接着在定义AOP增强操作的类上添加@Aspect注解和@Component将其注册为Bean,如同之前在配置文件中也要将其注册为Bean:

@Component
@Aspect
public class AopTest {

}

接着,在里面编写方法,并将此方法添加到一个切点中,比如我们希望在Student的test方法执行之前执行我们的方法:

public int test(String str){
    System.out.println("我被调用了:"+str);
    return str.length();
}

只需要添加@Before注解即可:

@Before("execution(* com.test.bean.Student.test(..))")
public void before(){
    System.out.println("我是之前执行的内容!");
}

同样的,为其添加JoinPoint参数来获取切入点信息:

@Before("execution(* com.test.bean.Student.test(..))")
public void before(JoinPoint point){
    System.out.println("参数列表:"+ Arrays.toString(point.getArgs()));
    System.out.println("我是之前执行的内容!");
}

使用@AfterReturning注解来指定方法返回后的操作:

@AfterReturning(value = "execution(* com.test.bean.Student.test(..))", returning = "returnVal")
public void after(Object returnVal){
    System.out.println("方法已返回,结果为:"+returnVal);
}

指定returning属性,并将其作为方法某个参数的实参。同样的,环绕也可以直接通过注解声明:

@Around("execution(* com.test.bean.Student.test(..))")
public Object around(ProceedingJoinPoint point) throws Throwable {
    System.out.println("方法执行之前!");
    Object val = point.proceed();
    System.out.println("方法执行之后!");
    return val;
}

其他注解配置

配置文件可能不止一个,我们有可能会根据模块划分,定义多个配置文件,这个时候,可能会出现很多个配置类,如果我们需要@Import注解来快速将某个类加入到容器中,比如我们现在创建一个新的配置文件,并将数据库Bean也搬过去:

public class Test2Configuration {
    @Bean
    public Connection getConnection() throws SQLException {
        System.out.println("创建新的连接!");
        return DriverManager.getConnection("jdbc:mysql://localhost:3306/study",
                "root",
                "root");
    }
}
@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.test")
@Import(Test2Configuration.class) //可以强制注册为一个Bean
public class TestConfiguration {

    @Resource
    Connection connection;

    @PostConstruct
    public void init(){
        System.out.println(connection);
    }
}

注意另一个配置类并没有添加任何注解,实际上,相当于导入的类被强制注册为了一个Bean,到现在,我们一共了解了三种注册为Bean的方式,利用这种特性,我们还可以将其他的类型也强制注册为Bean:

@EnableAspectJAutoProxy
@Configuration
@ComponentScan("com.test")
@Import({Test2Configuration.class, Date.class})
public class TestConfiguration {

    @Resource
    Connection connection;
    @Resource
    Date date;

    @PostConstruct
    public void init(){
        System.out.println(date+" -> "+connection);
    }
}

日期直接作为一个Bean放入到IoC容器中,并且时间永远都是被new的那个时间,也就是同一个对象(因为默认是单例模式)

通过@Import方式最主要为了实现的目标并不是创建Bean,而是为了方便一些框架的Registrar进行Bean定义

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/606285.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MongoDB6.0.6 副本集搭建(CentOs8)

本文只说如何操作配置副本集&#xff0c;历程艰难&#xff0c;官网文档看了半天也只说了怎么添加单个&#xff0c;没有给出来一个完整的流程。 1.第一步安装&#xff0c;参考前一篇安装即可。 配置三台虚拟机&#xff1a; 192.168.182.142 192.168.182.143 192.168.182.14…

钉钉小程序页面之间传递数据如何传递对象

今天写代码的时候&#xff0c;发现了一个问题&#xff0c;在钉钉小程序页面之间传递对象数据的时候&#xff0c;如果直接传递一个对象那个的话&#xff0c;接收的那个页面得到的是一个【object Object】&#xff0c;而并非里面的数据&#xff0c;所以针对上述问题&#xff0c;下…

基于Spring Boot的学生志愿者管理系统的设计与实现

摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对学生志愿者管理等问题&#xff0c;对学生…

项目计划软件 project安装包的下载和安装教程

目录 简介 安装配置过程 总结&#xff1a; 简介 Project是由微软公司开发的项目管理软件&#xff0c;旨在帮助个人和团队有效地管理项目进度、资源分配、协作和报告等工作&#xff0c;从而提高项目的质量和效率。Project维护项目的进程表、资源清单、成本预算、工作表和报告…

CSS-HTML知识点与高频考题解析

知识点梳理 选择器的权重和优先级 盒模型 盒子大小计算margin 的重叠计算 浮动 float浮动布局概念清理浮动 定位 position文档流概念定位分类fixed 定位特点绝对定位计算方式 flex布局 如何实现居中对齐&#xff1f; 理解语义化 CSS3 动画 重绘和回流 选择器的权重和优…

VTK 开发中遇到问题整理

1 Generic Warning VTK 开发 中是到 vtkOutputWindow 弹窗并提示Generic Warning&#xff1a;… vtkOutputWindow 弹窗 解决方法&#xff1a; 添加&#xff1a; #include <vtkOutputWindow.h> 在 main.cpp函数中添加&#xff1a; vtkOutputWindow::SetGlobalWarningD…

petalinux2022.2在ubantu20.04下的安装

1.Petalinux的下载路径 Downloads 这个是下载petalinux的官网路径。默认是2022.2版本&#xff0c;后期更新的均是以petalinux2022.2版本做的更新。 2.安装流程 在官网下载完成之后&#xff0c;会得到一个名为petalinux-v2022.2-10141622-installer.run的文件&#xff0c;这个文…

linux|磁盘管理工作|lvm逻辑管理卷的创建和使用总结(包括扩容,根目录扩容演示)

前言&#xff1a; 对于运维工作来说&#xff0c;磁盘管理是一个非常重要的工作。当然了&#xff0c;此类工作也是比较偏向底层的一项工作。 一个合理的磁盘分区设置&#xff0c;文件系统格式&#xff0c;以及准确的lvm逻辑管理会对我们的后期的扩展工作&#xff0c;管理工作带…

深入理解设计原则之单一职责原则(SRP)【软件架构设计】

系列文章目录 C高性能优化编程系列 深入理解软件架构设计系列 深入理解设计模式系列 高级C并发线程编程 SRP&#xff1a;单一职责原则 系列文章目录1、单一职责原则的定义和解读2、单一职责原则案例解读2.1、违背单一职责原则反面案例2.2、违背单一职责原则反面案例 - 解决方…

Openwrt_XiaoMiR3G路由器_刷入Breed固件

当我刷完Breed后&#xff0c;重启没有进入原来的小米路由器固件&#xff0c;但可以进入breed控制台。目前不清楚那个环节出错了。所以本过程会导致路由器无法再直接使用&#xff01;&#xff01;&#xff01;。 本过程只刷入Breed&#xff0c;接着编译OpenWrt和刷入OpenWrt请参…

git命令的使用

1. 查看文件 git cat-file -p 仓库路径下右键 Git Bash Here 打开git命令窗口&#xff1a; 复制某个文件的版本号&#xff1a; 粘贴到git命令窗口&#xff0c;会显示文件的提交信息&#xff1a; 查看 tree后面的版本号&#xff0c;则会看到详细提交信息&#xff1a; 查看hell…

第8章 泛型程序设计

文章目录 为什么要使用泛型程序设计类型参数的好处谁想成为泛型程序员 定义简单泛型类泛型方法类型变量的限定泛型代码和虚拟机类型擦除转换泛型表达式转换泛型方法类型擦除与多态会发生冲突桥方法实现多态桥方法与可协变的返回类型 调用遗留代码 限制与局限性泛型类型的继承规…

基于SpringBoot+Mybatis+Mysql+vue校园二手交易市场

基于SpringBootMybatisMysqlvue校园二手交易市场 一、系统介绍1、系统主要功能&#xff1a;2、环境配置 二、功能展示1.主页(客户)2.登陆、注册&#xff08;客户&#xff09;3.我的购物车(客户)4.我的商品详情(客户)5.我的商铺&#xff08;客户、商家&#xff09;6.我的信息&am…

zabbix5配置QQ邮件告警

1、服务端配置 编写邮件发送脚本 [fieldyangwww alertscripts]$ pwd /usr/lib/zabbix/alertscripts [fieldyangwww alertscripts]$ ll 总用量 8 -rwxr-xr-x 1 root root 136 5月 16 23:28 mail.sh -rwxr-xr-x 1 root root 751 5月 16 23:56 send_mail.py [fieldyangw…

信息与编码 SCUEC DDDD 期末考试整理(2)

1.求下面三种信道的信道容量 行列数量相等的情况 行比列多的情况 列比行多的情况 小贴士 2.客观世界三大基本要素&#xff1a;物质&#xff0c;能量&#xff0c;信息。 3.信息&#xff1a;是对事物运动状态和变化方式的表征&#xff0c;它存在于任何事物之中&#xff0c;可以…

机器学习算法系列(六)-- 朴素贝叶斯

.# 机器学习算法系列之 – 朴素贝叶斯 朴素贝叶斯法是基于概率统计&#xff0c;特征条件独立假设的分类方法&#xff0c;是一种非常常用的机器学习算法&#xff1b;通常用于处理文本分类和情感分析等自然语言处理任务中。相对于其他复杂的模型&#xff0c;朴素贝叶斯算法具有简…

提防利用 zip 域的新型网络钓鱼技术“浏览器中的文件归档器”

“浏览器中的文件存档器”是一种新的网络钓鱼技术,当受害者访问 .ZIP 域时,网络钓鱼者可以利用该技术。当受害者访问 .ZIP 域时,网络钓鱼者可以使用一种称为“浏览器中的文件存档器”的新型网络钓鱼技术在 Web 浏览器中“模拟”文件存档器软件。安全研究员 mr.d0x 详细介绍了…

A-可达鸭数学

题目链接 示例1 输入 9 1 -1 0 6 54 -8 520 1907 -2023输出 w m b wmb wmbbb mbw wmbwwmw wbmbmmbm mbwmbbwm备注: 请注意&#xff0c;在可达鸭数学里是没有负号的。 请注意&#xff0c;不要输出多余的前导b&#xff0c;否则会被判Wrong Answer。&#xff08;例如&#xff…

英文论文(sci)解读复现【NO.17】旋转至参加:卷积三重注意力模块

此前出了目标检测算法改进专栏&#xff0c;但是对于应用于什么场景&#xff0c;需要什么改进方法对应与自己的应用场景有效果&#xff0c;并且多少改进点能发什么水平的文章&#xff0c;为解决大家的困惑&#xff0c;此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…

Elasticsearch第一天学习笔记

目录 一、Elasticsearch概述 二、elasticsearch入门 一、Elasticsearch概述 &#xff08;一&#xff09;elasticsearch是什么&#xff1f; The Elastic Stack, 包括 Elasticsearch 、 Kibana 、 Beats 和 Logstash &#xff08;也称为 ELK Stack &#xff09;。 Elaticsear…