注解开发
创建一个配置类:
@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 { @Bean public Teacher teacher(){ return new Teacher(); } @Bean public 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; @Autowired public void setTeacher(Teacher teacher) { this.teacher = teacher; } }
@Autowired默认采用byType的方式进行自动装配
发生歧义时配合@Qualifier进行名称匹配:
public class Student { @Autowired @Qualifier("a") //匹配名称为a的Teacher类型的Bean private 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> { @Override public Student getObject() { //生产的Bean对象 return new Student(); } @Override public Class<?> getObjectType() { //生产的Bean类型 return Student.class; } @Override public 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.xsd http://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(); } }
说明:
实体和代理类都得注册为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>
AOP 领域中的特性术语:
- 通知(Advice): AOP 框架中的增强处理,通知描述了切面何时执行以及如何执行增强处理,也就是我们上面编写的方法实现。
- 连接点(join point): 连接点表示应用执行过程中能够插入切面的一个点,这个点可以是方法的调用、异常的抛出,实际上就是我们在方法执行前或是执行后需要做的内容。
- 切点(PointCut): 可以插入增强处理的连接点,可以是方法执行之前也可以方法执行之后,还可以是抛出异常之类的。
- 切面(Aspect): 切面是通知和切点的结合,我们之前在xml中定义的就是切面,包括很多信息。
- 引入(Introduction):引入允许我们向现有的类添加新的方法或者属性。
- 织入(Weaving): 将增强处理添加到目标对象中,并创建一个被增强的对象,我们之前都是在将我们的增强处理添加到目标对象,也就是织入
接口实现AOP
将一个类实现Advice接口,只有实现此接口,才可以被通知
使用MethodBeforeAdvice
表示是一个在方法执行之前的动作;AfterReturningAdvice
就需要实现一个方法执行之后的操作
public class StudentAOP implements MethodBeforeAdvice, AfterReturningAdvice { @Override public void before(Method method, Object[] args, Object target) throws Throwable { System.out.println("before:"); } @Override public 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 { @Override public 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; }