Spring基础

文章目录

  • Spring基础
    • IoC容器基础
      • IoC理论
      • 第一个Spring程序
      • Bean注册与配置
      • 依赖注入
      • 自动装配
      • 生命周期与继承
      • 工厂模式和工厂Bean
      • 注解开发
    • AOP面向切片
      • 配置实现AOP
      • 接口实现AOP
      • 注解实现AOP

Spring基础

Spring是为了简化开发而生,它是轻量级的IoCAOP的容器框架,主要是针对Bean的生命周期进行管理的轻量级容器,并且它的生态已经发展得极为庞大。

IoC容器基础

IoC理论

之前的Web应用各层角色分工都明确,流水线上的一套操作必须环环相扣,这是一种高度耦合的体系。目前App更新频繁,对于高耦合代码出现问题,无法高效进行维护和更新。

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

将对象交给IoC容器进行管理,不用再关心我们要去使用哪一个实现类了,我们只需要关心,给到我的一定是一个可以正常使用的实现类,能用就完事了。

public static void main(String[] args) {A a = new A();a.test(IoC.getBean(Service.class));   //容器类//比如现在在IoC容器中管理的Service的实现是B,那么我们从里面拿到的Service实现就是B
}class A{private List<Service> list;   //一律使用Service,具体实现由IoC容器提供public Service test(Service b){return null;}
}interface Service{ }   //使用Service做一个顶层抽象class B implements Service{}  //B依然是具体实现类,并交给IoC容器管理

当具体实现类发生修改时,我们同样只需要将新的实现类交给IoC容器管理,这样我们无需修改之前的任何代码。

高内聚,低耦合,是现代软件的开发的设计目标,而Spring框架就给我们提供了这样的一个IoC容器进行对象的的管理,一个由Spring IoC容器实例化、组装和管理的对象,我们称其为Bean。

第一个Spring程序

创建实体类:

@ToString
public class Student {
}

注册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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean name="student" class="com.test.bean.Student"/>
</beans>

获取Bean对象:

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

这个对象不需要我们再去创建了,而是由IoC容器自动进行创建并提供,我们可以直接从上下文中获取到它为我们创建的对象,这里得到的Student对象是由Spring通过反射机制帮助我们创建的

Bean注册与配置

配置一个Bean,并指定对应的类:

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

据类型向容器索要Bean实例对象:

ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");
//getBean有多种形式,其中第一种就是根据类型获取对应的Bean
//容器中只要注册了对应类的Bean或是对应类型子类的Bean,都可以获取到
Student student = context.getBean(Student.class);

为Bean指定一个名称用于区分:

<bean name="a" class="com.test.bean.Student"/>
<bean name="b" class="com.test.bean.Student"/>
<bean name="a" class="com.test.bean.Student"/>
<alias name="a" alias="test"/>#为Bean创建别名

默认情况下,通过IoC容器进行管理的Bean都是单例模式的,这个对象只会被创建一次。

对象作用域配置:

singleton,默认单例,全局唯一实例; prototype,原型模式,每次获取新的对象

当Bean的作用域为单例模式时,那么容器加载配置时就被创建,之后拿到的都是这个对象,容器没有被销毁,对象将一直存在;而原型模式下,只有在获取时才会被创建

单例模式下的Bean懒加载(获取时再加载):

<bean class="com.test.bean.Student" lazy-init="true"/>

维护Bean的加载顺序:

<bean name="teacher" class="com.test.bean.Teacher"/>
<bean name="student" class="com.test.bean.Student" depends-on="teacher"/>

xml配置文件是可以相互导入的:

<?xml version="1.0" encoding="UTF-8"?>
<beans ...><import resource="test.xml"/>
</beans>

依赖注入

依赖注入(Dependency Injection, DI)是一种设计模式,也是Spring框架的核心概念之一。

实体类需要其他实体类,创建时保证其他实体类也能进行动态加载。

public class Student {private Teacher teacher;//要使用依赖注入,我们必须提供一个set方法(无论成员变量的访问权限是什么)命名规则依然是驼峰命名法public void setTeacher(Teacher teacher) {this.teacher = teacher;}...
public interface Teacher {void teach();
}
public class ArtTeacher implements Teacher{@Overridepublic void teach() {System.out.println("我是美术老师,我教你画画!");}
}
public class ProgramTeacher implements Teacher{@Overridepublic void teach() {System.out.println("我是编程老师,我教你学Golang!");}
}

有了依赖注入之后,Student中的Teacher成员变量,可以由IoC容器来选择一个合适的Teacher对象进行赋值,也就是说,IoC容器在创建对象时,需要将我们预先给定的属性注入到对象中

使用property标签:

<bean name="teacher" class="com.test.bean.ProgramTeacher"/>
<bean name="student" class="com.test.bean.Student"><property name="teacher" ref="teacher"/>
</bean>

name指定成员对象名,ref指定依赖的Bean名称

image-20231102214228170

依赖注入并不一定要注入其他的Bean,也可以是一个简单的值:

<bean name="student" class="com.test.bean.Student"><property name="name" value="卢本伟"/>
</bean>

在构造方法中去完成初始化:

public class Student {private final Teacher teacher;   //构造方法中完成,所以说是一个final变量public Student(Teacher teacher){   //Teacher属性是在构造方法中完成的初始化this.teacher = teacher;}...

IoC容器默认只会调用无参构造,需要指明一个可以用的构造方法

constructor-arg标签:

<bean name="teacher" class="com.test.bean.ArtTeacher"/>
<bean name="student" class="com.test.bean.Student"><constructor-arg name="teacher" ref="teacher"/>
</bean>

constructor-arg就是构造方法的一个参数,这个参数可以写很多个,会自动匹配符合里面参数数量的构造方法

type指定参数的类型

<constructor-arg value="1" type="int"/>

其他特殊类型:

<bean name="student" class="com.test.bean.Student"><!--  对于集合类型,我们可以直接使用标签编辑集合的默认值  --><property name="list"><list><value>AAA</value><value>BBB</value><value>CCC</value></list></property>
</bean>
<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>

自动装配

自动装配就是让IoC容器自己去寻找需要填入的值,只需要将set方法提供好就可以了

autowire属性有两个值普通,一个是byName,还有一个是byType,顾名思义,一个是根据类型去寻找合适的Bean自动装配,还有一个是根据名字去找,就不需要显式指定property

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

byName自动装配,依据的Name是setter方法的后缀和bean的name属性进行匹配:

public class Student {Teacher teacher;public void setArt(Teacher teacher) {this.teacher = teacher;}
}
<?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/beanshttps://www.springframework.org/schema/beans/spring-beans.xsd"><bean name="art" class="com.test.bean.ArtTeacher"/><bean name="student" class="com.test.bean.Student" autowire="byName"/>
</beans>

使用构造方法完成的依赖注入,也支持自动装配:

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

当自动装配发生歧义时:

Bean属性autowire-candidate设定false时,这个Bean将不再作为自动装配的候选Bean

Bean设定primary属性,当出现歧义时,也会优先选择

<bean name="teacher" class="com.test.bean.ArtTeacher" primary="true"/>
<bean name="teacher2" class="com.test.bean.ProgramTeacher" autowire-candidate="false"/>
<bean name="student" class="com.test.bean.Student" autowire="byType"/>

生命周期与继承

除了修改构造方法,我们也可以为Bean指定初始化方法和销毁方法,以便在对象创建和被销毁时执行一些其他的任务:

public void init(){System.out.println("我是对象初始化时要做的事情!");    
}public void destroy(){System.out.println("我是对象销毁时要做的事情!");
}

通过init-methoddestroy-method来指定:

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

如果Bean不是单例模式,而是采用的原型模式,那么就只会在获取时才创建,并调用init-method,而对应的销毁方法不会被调用(因此,对于原型模式下的Bean,Spring无法顾及其完整生命周期,而在单例模式下,Spring能够从Bean对象的创建一直管理到对象的销毁)

Bean之间也是继承属性的:

public class SportStudent {private String name;public void setName(String name) {this.name = name;}
}
public class ArtStudent {private String name;public void setName(String name) {this.name = name;}
}

配置Bean之间的继承关系了,我们可以让SportStudent这个Bean直接继承ArtStudent这个Bean配置的属性:

<bean name="artStudent" class="com.test.bean.ArtStudent"><property name="name" value="小明"/>
</bean>
<bean class="com.test.bean.SportStudent" parent="artStudent"/>

在ArtStudent Bean中配置的属性,会直接继承给SportStudent Bean(注意,所有配置的属性,在子Bean中必须也要存在,并且可以进行注入,否则会出现错误)

如果子类中某些属性比较特殊,也可以在继承的基础上单独配置:

<bean name="artStudent" class="com.test.bean.ArtStudent" abstract="true"><property name="name" value="小明"/><property name="id" value="1"/>
</bean>
<bean class="com.test.bean.SportStudent" parent="artStudent"><property name="id" value="2"/>
</bean>

只是希望某一个Bean仅作为一个配置模版供其他Bean继承使用,那么我们可以将其配置为abstract:

<bean name="artStudent" class="com.test.bean.ArtStudent" abstract="true"><property name="name" value="小明"/>
</bean>
<bean class="com.test.bean.SportStudent" parent="artStudent"/>

一旦声明为抽象Bean,那么就无法通过容器获取到其实例化对象了

全文Bean属性配置:

整个上下文中所有的Bean都采用某种配置,我们可以在最外层的beans标签中进行默认配置

即使Bean没有配置某项属性,但是只要在最外层编写了默认配置,那么同样会生效,除非Bean自己进行配置覆盖掉默认配置。

工厂模式和工厂Bean

正常情况下需要使用工厂才可以得到Student对象,现在我们希望Spring也这样做,不要直接去反射搞构造方法创建

public class Student {Student() {System.out.println("我被构造了");}
}
public class StudentFactory {public static Student getStudent(){System.out.println("欢迎光临电子厂");return new Student();}
}

通过factory-method进行指定:

<bean class="com.test.bean.StudentFactory" factory-method="getStudent"/>
public class Test {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");Student student = context.getBean(Student.class);System.out.println(student);}
}

Bean类型需要填写为Student类的工厂类,并且添加factory-method指定对应的工厂方法

某些工厂类需要构造出对象之后才能使用:

public class StudentFactory {public Student getStudent(){System.out.println("欢迎光临电子厂");return new Student();}
}

将某个工厂类直接注册为工厂Bean,再使用factory-bean来指定Bean的工厂Bean

<bean name="studentFactory" class="com.test.bean.StudentFactory"/>
<bean factory-bean="studentFactory" factory-method="getStudent"/>
Student bean = (Student) context.getBean("studentFactory");
StudentFactory bean = (StudentFactory) context.getBean("&studentFactory");

注解开发

创建一个配置类:

@Configuration
public class MainConfiguration {
}

配置Bean:

@Configuration
public class MainConfiguration {@Bean("student")public Student student(){return new Student();}
}

获取Bean对象:

ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);
Student student = context.getBean(Student.class);
System.out.println(student);

其他属性配置:

@Bean(name = "", initMethod = "", destroyMethod = "", autowireCandidate = false)
public Student student(){return new Student();
}
@Bean
@Lazy(true)     //对应lazy-init属性
@Scope("prototype")    //对应scope属性
@DependsOn("teacher")    //对应depends-on属性
public Student student(){return new Student();
}

通过构造方法或是Setter完成依赖注入的Bean:

@Configuration
public class MainConfiguration {@Beanpublic Teacher teacher(){return new Teacher();}@Beanpublic Student student(Teacher teacher){return new Student(teacher);
//        return new Student().setTeacher(teacher);}
}

直接到Bean对应的类中使用自动装配:

public class Student {@Autowired   //使用此注解来进行自动装配,由IoC容器自动为其赋值private Teacher teacher;
}

甚至连构造方法和Setter都不需要去编写了,就能直接完成自动装配

@Autowired并不是只能用于字段,对于构造方法或是Setter,它同样可以:

public class Student {private Teacher teacher;@Autowiredpublic void setTeacher(Teacher teacher) {this.teacher = teacher;}
}

@Autowired默认采用byType的方式进行自动装配

发生歧义时配合@Qualifier进行名称匹配:

public class Student {@Autowired@Qualifier("a")   //匹配名称为a的Teacher类型的Beanprivate Teacher teacher;
}

@Resource,它的作用与@Autowired时相同的,也可以实现自动装配,但是在IDEA中并不推荐使用@Autowired注解对成员字段进行自动装配,而是推荐使用@Resource,如果需要使用这个注解,还需要额外导入包:

<dependency><groupId>jakarta.annotation</groupId><artifactId>jakarta.annotation-api</artifactId><version>2.1.1</version>
</dependency>
  • @Resource默认ByName如果找不到则ByType,可以添加到set方法、字段上。
  • @Autowired默认是byType,只会根据类型寻找,可以添加在构造方法、set方法、字段、方法参数上。

@PostConstruct和@PreDestroy,它们效果和init-method和destroy-method是一样的:

@PostConstruct
public void init(){System.out.println("我是初始化方法");
}@PreDestroy
public void destroy(){System.out.println("我是销毁方法");
}

使用@Bean来注册Bean,只是简单将一个类作为Bean的话,就是单纯的new一个对象出来,不能像之前一样让容器自己反射获取构造方法去生成这个对象

@Component注册Bean:

@Component("lbwnb")   //同样可以自己起名字
public class Student {}

配置一下包扫描:

@Configuration
@ComponentScan("com.test.bean")   //包扫描,这样Spring就会去扫描对应包下所有的类
public class MainConfiguration {}

Spring在扫描对应包下所有的类时,会自动将那些添加了@Component的类注册为Bean

无论是通过@Bean还是@Component形式注册的Bean,Spring都会为其添加一个默认的name属性

@Component默认名称生产规则依然是类名并按照首字母小写的驼峰命名法来的

@Component
public class Student {
}
Student student = (Student) context.getBean("student");   //这样同样可以获取到
System.out.println(student);

@Bean注册的默认名称是对应的方法名称

@Bean
public Student artStudent(){return new Student();
}
Student student = (Student) context.getBean("artStudent");
System.out.println(student);

@Component注册的Bean,如果其构造方法不是默认无参构造,那么默认会对其每一个参数都进行自动注入:

Spring也提供了接口,我们可以直接实现接口表示这个Bean是一个工厂Bean:

@Component
public class StudentFactory implements FactoryBean<Student> {@Overridepublic Student getObject() {   //生产的Bean对象return new Student();}@Overridepublic Class<?> getObjectType() {   //生产的Bean类型return Student.class;}@Overridepublic boolean isSingleton() {   //生产的Bean是否采用单例模式return false;}
}

AOP面向切片

AOP(Aspect Oriented Programming)思想实际上就是:在运行时,动态地将代码切入到类的指定方法、指定位置上。

可以使用AOP来帮助我们在方法执行前或执行之后,做一些额外的操作,实际上,它就是代理。

通过AOP我们可以在保证原有业务不变的情况下,,在不改变源代码的基础上进行了增强处理。

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.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
</beans>
<dependency><groupId>org.springframework</groupId><artifactId>spring-aspects</artifactId><version>6.0.10</version>
</dependency>

配置实现AOP

实体类:

public class Student {public void study(){System.out.println("狠狠的学Java");}
}

代理类:

public class StudentAOP {public void afterStudy(){System.out.println("hahahaha");}
}

Bean注册:

    <bean class="com.test.entity.Student"/><bean id="studentAop" class="com.test.entity.StudentAOP"/><aop:config><aop:pointcut id="test" expression="execution(public void com.test.entity.Student.study())"/><aop:aspect ref="studentAop"><aop:after method="afterStudy" pointcut-ref="test"/></aop:aspect></aop:config>

调用效果:

public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");Student bean = context.getBean(Student.class);bean.study();}
}

image-20231104144818843

说明:

实体和代理类都得注册为Bean,然后进行aop配置

添加一个新的切点,首先填写ID,再通过后面的expression表达式来选择到我们需要切入的方法

Spring AOP支持以下AspectJ切点指示器(PCD)用于表达式:

  • execution:用于匹配方法执行连接点。这是使用Spring AOP时使用的主要点切割指示器。
  • within:限制匹配到某些类型的连接点(使用Spring AOP时在匹配类型中声明的方法的执行)。
  • this:限制与连接点匹配(使用Spring AOP时方法的执行),其中bean引用(Spring AOP代理)是给定类型的实例。
  • target:限制匹配连接点(使用Spring AOP时方法的执行),其中目标对象(正在代理的应用程序对象)是给定类型的实例。
  • args:限制与连接点匹配(使用Spring AOP时方法的执行),其中参数是给定类型的实例。
  • @target:限制匹配连接点(使用Spring AOP时方法的执行),其中执行对象的类具有给定类型的注释。
  • @args:限制匹配到连接点(使用Spring AOP时方法的执行),其中传递的实际参数的运行时类型具有给定类型的注释。
  • @within:限制与具有给定注释的类型中的连接点匹配(使用Spring AOP时在带有给定注释的类型中声明的方法的执行)。
  • @annotation:与连接点主体(在Spring AOP中运行的方法)具有给定注释的连接点匹配的限制。

execution填写格式如下:

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

添加aop:aspect标签,并使用ref属性将其指向我们刚刚注册的AOP类Bean

添加后续动作了,当然,官方支持的有多种多样的,比如执行前、执行后、抛出异常后、方法返回后等等

Spring通过CGLib为我们生成的动态代理类,使得调用方法会直接得到增强

JoinPoint信息获取:

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

public class Student {public void study(String str){System.out.println("狠狠的学"+str);}
}
public class StudentAOP {public void afterStudy(JoinPoint point){System.out.println("hahahaha"+point.getArgs()[0]+"无敌");}
}
public class Main {public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("test.xml");Student bean = context.getBean(Student.class);bean.study("java");}
}
<aop:pointcut id="test" expression="execution(public void com.test.entity.Student.study(String))"/>//指定具体参数类型
<aop:pointcut id="test" expression="execution(public void com.test.entity.Student.study(..))"/>//模糊代指

环绕方法:

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

通过proceed方法来执行代理的方法,也可以修改参数之后调用proceed(Object[]),在调用之后对返回值结果也进行处理

    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {System.out.println("before");String arg = joinPoint.getArgs()[0]+"666";Object ret = joinPoint.proceed(new Object[]{arg});System.out.println("after");return ret;}
        <aop:aspect ref="studentAop"><aop:around method="around" pointcut-ref="test"/></aop:aspect>

image-20231104151622259

AOP 领域中的特性术语:

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

接口实现AOP

将一个类实现Advice接口,只有实现此接口,才可以被通知

使用MethodBeforeAdvice表示是一个在方法执行之前的动作;AfterReturningAdvice就需要实现一个方法执行之后的操作

public class StudentAOP implements MethodBeforeAdvice, AfterReturningAdvice {@Overridepublic void before(Method method, Object[] args, Object target) throws Throwable {System.out.println("before:");}@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("after-return");}
}
        <aop:advisor advice-ref="studentAop" pointcut-ref="test"/>

使用MethodInterceptor(同样也是Advice的子接口)进行更加环绕那样的自定义的增强

public class StudentAOP implements MethodInterceptor {@Overridepublic Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("haha");Object ret = invocation.proceed();System.out.println("wuwu");return ret;}
}

注解实现AOP

实体类:

@Component//注册为Bean
public class Student {public void study(String str){System.out.println("狠狠的学"+str);}
}

代理类:

@Aspect//声明为切面
@Component//注册为Bean
public class StudentAOP {@Before("execution(* com.test.entity.Student.study(..))")public void before(JoinPoint point){System.out.println("before");System.out.println("args:"+Arrays.toString(point.getArgs()));}@After(value = "execution(* com.test.entity.Student.study(..)) && args(str)",argNames = "str")//命名绑定模式就是根据下面的方法参数列表进行匹配
//这里args指明参数,注意需要跟原方法保持一致,然后在argNames中指明public void after(String str){//获取指定参数System.out.println("get arg str:" + str);System.out.println("after");}
}

配置类:

@EnableAspectJAutoProxy//开启aop代理
@ComponentScan("com.test.entity")//扫描bean实体类
@Configuration//声明配置类
public class MainConfiguration {}

调用:

public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);Student bean = context.getBean(Student.class);bean.study("java");}
}

除了@Before,还有很多可以直接使用的注解,比如@AfterReturning、@AfterThrowing等

环绕注解:

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

void after(String str){//获取指定参数
System.out.println(“get arg str:” + str);
System.out.println(“after”);
}
}


配置类:~~~java
@EnableAspectJAutoProxy//开启aop代理
@ComponentScan("com.test.entity")//扫描bean实体类
@Configuration//声明配置类
public class MainConfiguration {}

调用:

public class Main {public static void main(String[] args) {ApplicationContext context = new AnnotationConfigApplicationContext(MainConfiguration.class);Student bean = context.getBean(Student.class);bean.study("java");}
}

除了@Before,还有很多可以直接使用的注解,比如@AfterReturning、@AfterThrowing等

环绕注解:

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

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

I/O多路转接之select

承接上文&#xff1a;I/O模型之非阻塞IO-CSDN博客 简介 select函数原型介绍使用 一个select简单的服务器的代码书写 select的缺点 初识select 系统提供select函数来实现多路复用输入/输出模型 select系统调用是用来让我们的程序监视多个文件描述符的状态变化的; 程序会停在s…

Vue3 实现 clipboard 复制功能

一个很小的交互功能&#xff0c;网上搜了一下有一个 vue3-clipboard 直接支持vue3&#xff0c;到github仓库看了下&#xff0c;原作者已经不维护这个项目了&#xff1a; 推荐使用 vueuse 自带的 useclipboard 功能&#xff0c;由 vue 团队维护&#xff0c;稳定性基本没问题 官…

数据结构构之顺序表

1.线性表 线性表&#xff08;linear list&#xff09;是n个具有相同特性的数据元素的有限序列。 线性表是一种在实际中广泛使用的数据结构&#xff0c;常见的线性表&#xff1a;顺序表、链表、栈、队列、字符串... 线性表在逻辑上是线性结构&#xff0c;也就说是连续的一条直线…

MySQL连接时出现Host ‘::1‘ is not allowed to connect to this MySQL server

报错原因 之前想着要提高一下连接速度&#xff0c;所以在my.ini中加入了&#xff1a;skip-name-resolve&#xff0c;当时的数据库root账号设置的登录权限是%&#xff0c;因此没有出现连接错误&#xff0c;这次因为是新建数据库&#xff0c;root账号的登录权限默认是localhost&…

园区网真实详细配置大全案例

实现要求&#xff1a; 1、只允许行政部电脑对全网telnet管理 2、所有dhcp都在核心 3、wifi用户只能上外网&#xff0c;不能访问局域网其它电脑 4、所有接入交换机上bpdu保护 5、只允许vlan 10-40上网 5、所有接入交换机开dhcp snoop 6、所有的交换机指定核心交换机为ntp时间服务…

解决Visual Studio Code 控制台中文乱码问题

C和CPP运行编码指定 "code-runner.executorMap": {"c": "cd $dir && gcc -fexec-charsetGBK $fileName -o $fileNameWithoutExt && $dir$fileNameWithoutExt","cpp": "cd $dir && g -fexec-charsetGBK $…

LV.12 D16 轮询与中断 学习笔记

一、CPU与硬件的交互方式 轮询 CPU执行程序时不断地询问硬件是否需要其服务&#xff0c;若需要则给予其服务&#xff0c;若不需要一段时间后再次询问&#xff0c;周而复始 中断 CPU执行程序时若硬件需要其服务&#xff0c;对应的硬件给CPU发送中断信号&#xff0c…

AD教程(六)现有元件模型的调用

AD教程&#xff08;六&#xff09;现有元件模型的调用 导入现有原理图 Altium Schematic Document (.SchDoc) 直接拖入AD即可 直接用现有原理图生成原理图库 点击设计&#xff0c;选择生成原理图库&#xff0c;进入归类设置界面&#xff08;用原理图直接生成原理图库&#xf…

如何实现多租户系统

shigen日更文章的博客写手&#xff0c;擅长Java、python、vue、shell等编程语言和各种应用程序、脚本的开发。记录成长&#xff0c;分享认知&#xff0c;留住感动。 不知道为什么&#xff0c;最近老是有一些失眠&#xff0c;凌晨睡&#xff0c;两点半还在醒着。脑子里想着自己生…

Linux flock和fcntl函数详解

文章目录 flock函数描述返回值和错误码笔记 fcntl函数描述复制文件描述符文件描述标志文件状态标志 咨询锁强制锁管理信号租赁文件和目录变更通知改变管道容量 返回值错误备注遗留问题 flock函数 主要功能是在已打开的文件应用或者删除共享锁或者独占锁。sys/file.h声明了这个…

使用Postman工具做接口测试 —— 环境变量与请求参数格式!

引言 在上一篇笔记我们主要介绍了接口测试的基础知识与基本功能&#xff0c;本章主要介绍如何使用postman做接口测试。 配置环境变量和全局变量 环境变量和全局变量 环境管理中还可以点击“Global”添加全局变量&#xff0c;环境变量只有当选择了该环境时才生效&#xff0c;…

centos9 stream 下 rabbitmq高可用集群搭建及使用

RabbitMQ是一种常用的消息队列系统&#xff0c;可以快速搭建一个高可用的集群环境&#xff0c;以提高系统的弹性和可靠性。下面是搭建RabbitMQ集群的步骤&#xff1a; 基于centos9 stream系统 1. 安装Erlang和RabbitMQ 首先需要在所有节点上安装Erlang和RabbitMQ。建议使用官…

VueJs各个版本— 判断当前是开发、生产环境

VueJs各个版本— 判断当前是开发、生产环境 文章目录 VueJs各个版本— 判断当前是开发、生产环境vue项目分类VueCLI21&#xff0c;判断样例2&#xff0c;判断原理 Vue CLI 3 和 Vue CLI 41&#xff0c;判断样例2, 判断原理手动设置-json文件手动设置- .env 文件单个 .env 文件多…

SpringBoot-SpringCache缓存

文章目录 Spring Cache 介绍常用注解 Spring Cache 介绍 Spring Cache 是一个框架&#xff0c;实现了基于注解的缓存功能&#xff0c;只需要简单地加一个注解&#xff0c;就能实现缓存功能。 Spring Cache 提供了一层抽象&#xff0c;底层可以切换不同的缓存实现&#xff0c;…

shell脚本代码混淆

文章目录 起因安装 Bashfuscator安装BashfuscatorBashfuscator的使用 起因 很多时候我并不希望自己的shell脚本被别人看到&#xff0c;于是我在想有没有什么玩意可以把代码加密而又正常执行&#xff0c;于是我想到了代码混淆&#xff0c;简单来看一下&#xff1a; 现在我的目…

JavaEE平台技术——预备知识(Maven、Docker)

JavaEE平台技术——预备知识&#xff08;Maven、Docker&#xff09; 1. Maven2. Docker 在观看这个之前&#xff0c;大家请查阅前序内容。 &#x1f600;JavaEE的渊源 &#x1f600;&#x1f600;JavaEE平台技术——预备知识&#xff08;Web、Sevlet、Tomcat&#xff09; 1. M…

Git 内容学习

一、Git 的理解 Git是一个分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;简称 DVCS&#xff09;&#xff0c;用于对项目源代码进行管理和跟踪变更。分为两种类型的仓库&#xff1a;本地仓库和远程仓库。 二、Git 的工作流程 详解如下&#x…

高性能渲染——详解Html Canvas的优势与性能

本文由葡萄城技术团队原创并首发。转载请注明出处&#xff1a;葡萄城官网&#xff0c;葡萄城为开发者提供专业的开发工具、解决方案和服务&#xff0c;赋能开发者。 一、什么是Canvas 想必学习前端的同学们对Canvas 都不陌生&#xff0c;它是 HTML5 新增的“画布”元素&#x…

GPT引发智能AI时代潮流

最近GPT概念爆火&#xff0c;许多行业开始竞相发展AI &#xff0c;工作就业也将面临跳转&#xff0c;目前测试就业形势就分为了两大类&#xff0c;一类是测试行业如功能、性能、自动化综合性人才就业技能需求&#xff0c;另一类便是AI测试行业的需求普遍增长&#xff0c;原本由…

一键混剪软件、根据模板批量剪辑、多种分割、多种合并、多种混剪、文案提取、文字转语音等

在这个短视频时代&#xff0c;视频剪辑已经成为了一个炙手可热的行业。但是&#xff0c;对于大多数人来说&#xff0c;视频剪辑是一项既复杂又繁琐的工作。不过&#xff0c;现在有了我们的新伙伴——视频闪闪&#xff0c;一键混剪软件&#xff0c;您将迎来全新的视频剪辑体验&a…